]> git.saurik.com Git - apple/xnu.git/blob - bsd/vfs/vfs_quota.c
xnu-792.25.20.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_quota.c
1 /*
2 * Copyright (c) 2002-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * Copyright (c) 1982, 1986, 1990, 1993, 1995
24 * The Regents of the University of California. All rights reserved.
25 *
26 * This code is derived from software contributed to Berkeley by
27 * Robert Elz at The University of Melbourne.
28 *
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
31 * are met:
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright
35 * notice, this list of conditions and the following disclaimer in the
36 * documentation and/or other materials provided with the distribution.
37 * 3. All advertising materials mentioning features or use of this software
38 * must display the following acknowledgement:
39 * This product includes software developed by the University of
40 * California, Berkeley and its contributors.
41 * 4. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 *
57 * @(#)vfs_quota.c
58 * derived from @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
59 */
60
61 #include <sys/param.h>
62 #include <sys/kernel.h>
63 #include <sys/systm.h>
64 #include <sys/malloc.h>
65 #include <sys/file_internal.h>
66 #include <sys/proc_internal.h>
67 #include <sys/vnode_internal.h>
68 #include <sys/mount_internal.h>
69 #include <sys/quota.h>
70 #include <sys/uio_internal.h>
71
72 #include <libkern/OSByteOrder.h>
73
74
75 /* vars for quota file lock */
76 lck_grp_t * qf_lck_grp;
77 lck_grp_attr_t * qf_lck_grp_attr;
78 lck_attr_t * qf_lck_attr;
79
80 /* vars for quota list lock */
81 lck_grp_t * quota_list_lck_grp;
82 lck_grp_attr_t * quota_list_lck_grp_attr;
83 lck_attr_t * quota_list_lck_attr;
84 lck_mtx_t * quota_list_mtx_lock;
85
86 /* Routines to lock and unlock the quota global data */
87 static int dq_list_lock(void);
88 static void dq_list_unlock(void);
89
90 static void dq_lock_internal(struct dquot *dq);
91 static void dq_unlock_internal(struct dquot *dq);
92
93 static u_int32_t quotamagic[MAXQUOTAS] = INITQMAGICS;
94
95
96 /*
97 * Code pertaining to management of the in-core dquot data structures.
98 */
99 #define DQHASH(dqvp, id) \
100 (&dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash])
101 LIST_HEAD(dqhash, dquot) *dqhashtbl;
102 u_long dqhash;
103
104 #define DQUOTINC 5 /* minimum free dquots desired */
105 long numdquot, desireddquot = DQUOTINC;
106
107 /*
108 * Dquot free list.
109 */
110 TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
111 /*
112 * Dquot dirty orphans list
113 */
114 TAILQ_HEAD(dqdirtylist, dquot) dqdirtylist;
115
116
117 static int dqlookup(struct quotafile *, u_long, struct dqblk *, u_int32_t *);
118 static int dqsync_locked(struct dquot *dq);
119
120 static void qf_lock(struct quotafile *);
121 static void qf_unlock(struct quotafile *);
122 static int qf_ref(struct quotafile *);
123 static void qf_rele(struct quotafile *);
124
125
126 /*
127 * Initialize the quota system.
128 */
129 void
130 dqinit()
131 {
132
133 dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
134 TAILQ_INIT(&dqfreelist);
135 TAILQ_INIT(&dqdirtylist);
136
137 /*
138 * Allocate quota list lock group attribute and group
139 */
140 quota_list_lck_grp_attr= lck_grp_attr_alloc_init();
141 quota_list_lck_grp = lck_grp_alloc_init("quota list", quota_list_lck_grp_attr);
142
143 /*
144 * Allocate qouta list lock attribute
145 */
146 quota_list_lck_attr = lck_attr_alloc_init();
147
148 /*
149 * Allocate quota list lock
150 */
151 quota_list_mtx_lock = lck_mtx_alloc_init(quota_list_lck_grp, quota_list_lck_attr);
152
153
154 /*
155 * allocate quota file lock group attribute and group
156 */
157 qf_lck_grp_attr= lck_grp_attr_alloc_init();
158 qf_lck_grp = lck_grp_alloc_init("quota file", qf_lck_grp_attr);
159
160 /*
161 * Allocate quota file lock attribute
162 */
163 qf_lck_attr = lck_attr_alloc_init();
164 }
165
166
167 static volatile int dq_list_lock_cnt = 0;
168
169 static int
170 dq_list_lock(void)
171 {
172 lck_mtx_lock(quota_list_mtx_lock);
173 return ++dq_list_lock_cnt;
174 }
175
176 static int
177 dq_list_lock_changed(int oldval) {
178 return (dq_list_lock_cnt != oldval);
179 }
180
181 static int
182 dq_list_lock_val(void) {
183 return dq_list_lock_cnt;
184 }
185
186 void
187 dq_list_unlock(void)
188 {
189 lck_mtx_unlock(quota_list_mtx_lock);
190 }
191
192
193 /*
194 * must be called with the quota_list_lock held
195 */
196 void
197 dq_lock_internal(struct dquot *dq)
198 {
199 while (dq->dq_lflags & DQ_LLOCK) {
200 dq->dq_lflags |= DQ_LWANT;
201 msleep(&dq->dq_lflags, quota_list_mtx_lock, PVFS, "dq_lock_internal", 0);
202 }
203 dq->dq_lflags |= DQ_LLOCK;
204 }
205
206 /*
207 * must be called with the quota_list_lock held
208 */
209 void
210 dq_unlock_internal(struct dquot *dq)
211 {
212 int wanted = dq->dq_lflags & DQ_LWANT;
213
214 dq->dq_lflags &= ~(DQ_LLOCK | DQ_LWANT);
215
216 if (wanted)
217 wakeup(&dq->dq_lflags);
218 }
219
220 void
221 dqlock(struct dquot *dq) {
222
223 lck_mtx_lock(quota_list_mtx_lock);
224
225 dq_lock_internal(dq);
226
227 lck_mtx_unlock(quota_list_mtx_lock);
228 }
229
230 void
231 dqunlock(struct dquot *dq) {
232
233 lck_mtx_lock(quota_list_mtx_lock);
234
235 dq_unlock_internal(dq);
236
237 lck_mtx_unlock(quota_list_mtx_lock);
238 }
239
240
241
242 int
243 qf_get(struct quotafile *qfp, int type)
244 {
245 int error = 0;
246
247 dq_list_lock();
248
249 switch (type) {
250
251 case QTF_OPENING:
252 while ( (qfp->qf_qflags & (QTF_OPENING | QTF_CLOSING)) ) {
253 if ( (qfp->qf_qflags & QTF_OPENING) ) {
254 error = EBUSY;
255 break;
256 }
257 if ( (qfp->qf_qflags & QTF_CLOSING) ) {
258 qfp->qf_qflags |= QTF_WANTED;
259 msleep(&qfp->qf_qflags, quota_list_mtx_lock, PVFS, "qf_get", 0);
260 }
261 }
262 if (qfp->qf_vp != NULLVP)
263 error = EBUSY;
264 if (error == 0)
265 qfp->qf_qflags |= QTF_OPENING;
266 break;
267
268 case QTF_CLOSING:
269 if ( (qfp->qf_qflags & QTF_CLOSING) ) {
270 error = EBUSY;
271 break;
272 }
273 qfp->qf_qflags |= QTF_CLOSING;
274
275 while ( (qfp->qf_qflags & QTF_OPENING) || qfp->qf_refcnt ) {
276 qfp->qf_qflags |= QTF_WANTED;
277 msleep(&qfp->qf_qflags, quota_list_mtx_lock, PVFS, "qf_get", 0);
278 }
279 if (qfp->qf_vp == NULLVP) {
280 qfp->qf_qflags &= ~QTF_CLOSING;
281 error = EBUSY;
282 }
283 break;
284 }
285 dq_list_unlock();
286
287 return (error);
288 }
289
290 void
291 qf_put(struct quotafile *qfp, int type)
292 {
293
294 dq_list_lock();
295
296 switch (type) {
297
298 case QTF_OPENING:
299 case QTF_CLOSING:
300 qfp->qf_qflags &= ~type;
301 break;
302 }
303 if ( (qfp->qf_qflags & QTF_WANTED) ) {
304 qfp->qf_qflags &= ~QTF_WANTED;
305 wakeup(&qfp->qf_qflags);
306 }
307 dq_list_unlock();
308 }
309
310
311 static void
312 qf_lock(struct quotafile *qfp)
313 {
314 lck_mtx_lock(&qfp->qf_lock);
315 }
316
317 static void
318 qf_unlock(struct quotafile *qfp)
319 {
320 lck_mtx_unlock(&qfp->qf_lock);
321 }
322
323
324 /*
325 * take a reference on the quota file while we're
326 * in dqget... this will prevent a quota_off from
327 * occurring while we're potentially playing with
328 * the quota file... the quota_off will stall until
329 * all the current references 'die'... once we start
330 * into quoto_off, all new references will be rejected
331 * we also don't want any dqgets being processed while
332 * we're in the middle of the quota_on... once we've
333 * actually got the quota file open and the associated
334 * struct quotafile inited, we can let them come through
335 *
336 * quota list lock must be held on entry
337 */
338 static int
339 qf_ref(struct quotafile *qfp)
340 {
341 int error = 0;
342
343 if ( (qfp->qf_qflags & (QTF_OPENING | QTF_CLOSING)) || (qfp->qf_vp == NULLVP) )
344 error = EINVAL;
345 else
346 qfp->qf_refcnt++;
347
348 return (error);
349 }
350
351 /*
352 * drop our reference and wakeup any waiters if
353 * we were the last one holding a ref
354 *
355 * quota list lock must be held on entry
356 */
357 static void
358 qf_rele(struct quotafile *qfp)
359 {
360 qfp->qf_refcnt--;
361
362 if ( (qfp->qf_qflags & QTF_WANTED) && qfp->qf_refcnt == 0) {
363 qfp->qf_qflags &= ~QTF_WANTED;
364 wakeup(&qfp->qf_qflags);
365 }
366 }
367
368
369 void
370 dqfileinit(struct quotafile *qfp)
371 {
372 qfp->qf_vp = NULLVP;
373 qfp->qf_qflags = 0;
374
375 lck_mtx_init(&qfp->qf_lock, qf_lck_grp, qf_lck_attr);
376 }
377
378
379 /*
380 * Initialize a quota file
381 *
382 * must be called with the quota file lock held
383 */
384 int
385 dqfileopen(qfp, type)
386 struct quotafile *qfp;
387 int type;
388 {
389 struct dqfilehdr header;
390 struct vfs_context context;
391 off_t file_size;
392 uio_t auio;
393 int error = 0;
394 char uio_buf[ UIO_SIZEOF(1) ];
395
396 context.vc_proc = current_proc();
397 context.vc_ucred = qfp->qf_cred;
398
399 /* Obtain the file size */
400 if ((error = vnode_size(qfp->qf_vp, &file_size, &context)) != 0)
401 goto out;
402
403 /* Read the file header */
404 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
405 &uio_buf[0], sizeof(uio_buf));
406 uio_addiov(auio, CAST_USER_ADDR_T(&header), sizeof (header));
407 error = VNOP_READ(qfp->qf_vp, auio, 0, &context);
408 if (error)
409 goto out;
410 else if (uio_resid(auio)) {
411 error = EINVAL;
412 goto out;
413 }
414 /* Sanity check the quota file header. */
415 if ((OSSwapBigToHostInt32(header.dqh_magic) != quotamagic[type]) ||
416 (OSSwapBigToHostInt32(header.dqh_version) > QF_VERSION) ||
417 (!powerof2(OSSwapBigToHostInt32(header.dqh_maxentries))) ||
418 (OSSwapBigToHostInt32(header.dqh_maxentries) > (file_size / sizeof(struct dqblk)))) {
419 error = EINVAL;
420 goto out;
421 }
422 /* Set up the time limits for this quota. */
423 if (header.dqh_btime != 0)
424 qfp->qf_btime = OSSwapBigToHostInt32(header.dqh_btime);
425 else
426 qfp->qf_btime = MAX_DQ_TIME;
427 if (header.dqh_itime != 0)
428 qfp->qf_itime = OSSwapBigToHostInt32(header.dqh_itime);
429 else
430 qfp->qf_itime = MAX_IQ_TIME;
431
432 /* Calculate the hash table constants. */
433 qfp->qf_maxentries = OSSwapBigToHostInt32(header.dqh_maxentries);
434 qfp->qf_entrycnt = OSSwapBigToHostInt32(header.dqh_entrycnt);
435 qfp->qf_shift = dqhashshift(qfp->qf_maxentries);
436 out:
437 return (error);
438 }
439
440 /*
441 * Close down a quota file
442 */
443 void
444 dqfileclose(struct quotafile *qfp, __unused int type)
445 {
446 struct dqfilehdr header;
447 struct vfs_context context;
448 uio_t auio;
449 char uio_buf[ UIO_SIZEOF(1) ];
450
451 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_READ,
452 &uio_buf[0], sizeof(uio_buf));
453 uio_addiov(auio, CAST_USER_ADDR_T(&header), sizeof (header));
454
455 context.vc_proc = current_proc();
456 context.vc_ucred = qfp->qf_cred;
457
458 if (VNOP_READ(qfp->qf_vp, auio, 0, &context) == 0) {
459 header.dqh_entrycnt = OSSwapHostToBigInt32(qfp->qf_entrycnt);
460 uio_reset(auio, 0, UIO_SYSSPACE, UIO_WRITE);
461 uio_addiov(auio, CAST_USER_ADDR_T(&header), sizeof (header));
462 (void) VNOP_WRITE(qfp->qf_vp, auio, 0, &context);
463 }
464 }
465
466
467 /*
468 * Obtain a dquot structure for the specified identifier and quota file
469 * reading the information from the file if necessary.
470 */
471 int
472 dqget(id, qfp, type, dqp)
473 u_long id;
474 struct quotafile *qfp;
475 register int type;
476 struct dquot **dqp;
477 {
478 struct dquot *dq;
479 struct dquot *ndq = NULL;
480 struct dquot *fdq = NULL;
481 struct dqhash *dqh;
482 struct vnode *dqvp;
483 int error = 0;
484 int listlockval = 0;
485
486 if ( id == 0 || qfp->qf_vp == NULLVP ) {
487 *dqp = NODQUOT;
488 return (EINVAL);
489 }
490 dq_list_lock();
491
492 if ( (qf_ref(qfp)) ) {
493 dq_list_unlock();
494
495 *dqp = NODQUOT;
496 return (EINVAL);
497 }
498 if ( (dqvp = qfp->qf_vp) == NULLVP ) {
499 qf_rele(qfp);
500 dq_list_unlock();
501
502 *dqp = NODQUOT;
503 return (EINVAL);
504 }
505 dqh = DQHASH(dqvp, id);
506
507 relookup:
508 listlockval = dq_list_lock_val();
509
510 /*
511 * Check the cache first.
512 */
513 for (dq = dqh->lh_first; dq; dq = dq->dq_hash.le_next) {
514 if (dq->dq_id != id ||
515 dq->dq_qfile->qf_vp != dqvp)
516 continue;
517
518 dq_lock_internal(dq);
519 if (dq_list_lock_changed(listlockval)) {
520 dq_unlock_internal(dq);
521 goto relookup;
522 }
523
524 /*
525 * dq_lock_internal may drop the quota_list_lock to msleep, so
526 * we need to re-evaluate the identity of this dq
527 */
528 if (dq->dq_id != id || dq->dq_qfile == NULL ||
529 dq->dq_qfile->qf_vp != dqvp) {
530 dq_unlock_internal(dq);
531 goto relookup;
532 }
533 /*
534 * Cache hit with no references. Take
535 * the structure off the free list.
536 */
537 if (dq->dq_cnt++ == 0) {
538 if (dq->dq_flags & DQ_MOD)
539 TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist);
540 else
541 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
542 } else if (dq->dq_cnt == 0) {
543 /* We've overflowed */
544 --dq->dq_cnt;
545 dq_unlock_internal(dq);
546 dq_list_unlock();
547 *dqp = NODQUOT;
548 return (EINVAL);
549 }
550 dq_unlock_internal(dq);
551
552 if (fdq != NULL) {
553 /*
554 * we grabbed this from the free list in the first pass
555 * but we found the dq we were looking for in
556 * the cache the 2nd time through
557 * so stick it back on the free list and return the cached entry
558 */
559 TAILQ_INSERT_HEAD(&dqfreelist, fdq, dq_freelist);
560 }
561 qf_rele(qfp);
562 dq_list_unlock();
563
564 if (ndq != NULL) {
565 /*
566 * we allocated this in the first pass
567 * but we found the dq we were looking for in
568 * the cache the 2nd time through so free it
569 */
570 _FREE(ndq, M_DQUOT);
571 }
572 *dqp = dq;
573
574 return (0);
575 }
576 /*
577 * Not in cache, allocate a new one.
578 */
579 if (TAILQ_EMPTY(&dqfreelist) &&
580 numdquot < MAXQUOTAS * desiredvnodes)
581 desireddquot += DQUOTINC;
582
583 if (fdq != NULL) {
584 /*
585 * we captured this from the free list
586 * in the first pass through, so go
587 * ahead and use it
588 */
589 dq = fdq;
590 fdq = NULL;
591 } else if (numdquot < desireddquot) {
592 if (ndq == NULL) {
593 /*
594 * drop the quota list lock since MALLOC may block
595 */
596 dq_list_unlock();
597
598 ndq = (struct dquot *)_MALLOC(sizeof *dq, M_DQUOT, M_WAITOK);
599 bzero((char *)ndq, sizeof *dq);
600
601 listlockval = dq_list_lock();
602 /*
603 * need to look for the entry again in the cache
604 * since we dropped the quota list lock and
605 * someone else may have beaten us to creating it
606 */
607 goto relookup;
608 } else {
609 /*
610 * we allocated this in the first pass through
611 * and we're still under out target, so go
612 * ahead and use it
613 */
614 dq = ndq;
615 ndq = NULL;
616 numdquot++;
617 }
618 } else {
619 if (TAILQ_EMPTY(&dqfreelist)) {
620 qf_rele(qfp);
621 dq_list_unlock();
622
623 if (ndq) {
624 /*
625 * we allocated this in the first pass through
626 * but we're now at the limit of our cache size
627 * so free it
628 */
629 _FREE(ndq, M_DQUOT);
630 }
631 tablefull("dquot");
632 *dqp = NODQUOT;
633 return (EUSERS);
634 }
635 dq = TAILQ_FIRST(&dqfreelist);
636
637 dq_lock_internal(dq);
638
639 if (dq_list_lock_changed(listlockval) || dq->dq_cnt || (dq->dq_flags & DQ_MOD)) {
640 /*
641 * we lost the race while we weren't holding
642 * the quota list lock... dq_lock_internal
643 * will drop it to msleep... this dq has been
644 * reclaimed... go find another
645 */
646 dq_unlock_internal(dq);
647
648 /*
649 * need to look for the entry again in the cache
650 * since we dropped the quota list lock and
651 * someone else may have beaten us to creating it
652 */
653 goto relookup;
654 }
655 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
656
657 if (dq->dq_qfile != NULL) {
658 LIST_REMOVE(dq, dq_hash);
659 dq->dq_qfile = NULL;
660 dq->dq_id = 0;
661 }
662 dq_unlock_internal(dq);
663
664 /*
665 * because we may have dropped the quota list lock
666 * in the call to dq_lock_internal, we need to
667 * relookup in the hash in case someone else
668 * caused a dq with this identity to be created...
669 * if we don't find it, we'll use this one
670 */
671 fdq = dq;
672 goto relookup;
673 }
674 /*
675 * we've either freshly allocated a dq
676 * or we've atomically pulled it out of
677 * the hash and freelists... no one else
678 * can have a reference, which means no
679 * one else can be trying to use this dq
680 */
681 dq_lock_internal(dq);
682 if (dq_list_lock_changed(listlockval)) {
683 dq_unlock_internal(dq);
684 goto relookup;
685 }
686
687 /*
688 * Initialize the contents of the dquot structure.
689 */
690 dq->dq_cnt = 1;
691 dq->dq_flags = 0;
692 dq->dq_id = id;
693 dq->dq_qfile = qfp;
694 dq->dq_type = type;
695 /*
696 * once we insert it in the hash and
697 * drop the quota_list_lock, it can be
698 * 'found'... however, we're still holding
699 * the dq_lock which will keep us from doing
700 * anything with it until we've finished
701 * initializing it...
702 */
703 LIST_INSERT_HEAD(dqh, dq, dq_hash);
704 dq_list_unlock();
705
706 if (ndq) {
707 /*
708 * we allocated this in the first pass through
709 * but we didn't need it, so free it after
710 * we've droped the quota list lock
711 */
712 _FREE(ndq, M_DQUOT);
713 }
714
715 error = dqlookup(qfp, id, &dq->dq_dqb, &dq->dq_index);
716
717 /*
718 * I/O error in reading quota file, release
719 * quota structure and reflect problem to caller.
720 */
721 if (error) {
722 dq_list_lock();
723
724 dq->dq_id = 0;
725 dq->dq_qfile = NULL;
726 LIST_REMOVE(dq, dq_hash);
727
728 dq_unlock_internal(dq);
729 qf_rele(qfp);
730 dq_list_unlock();
731
732 dqrele(dq);
733
734 *dqp = NODQUOT;
735 return (error);
736 }
737 /*
738 * Check for no limit to enforce.
739 * Initialize time values if necessary.
740 */
741 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
742 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
743 dq->dq_flags |= DQ_FAKE;
744 if (dq->dq_id != 0) {
745 struct timeval tv;
746
747 microtime(&tv);
748 if (dq->dq_btime == 0)
749 dq->dq_btime = tv.tv_sec + qfp->qf_btime;
750 if (dq->dq_itime == 0)
751 dq->dq_itime = tv.tv_sec + qfp->qf_itime;
752 }
753 dq_list_lock();
754 dq_unlock_internal(dq);
755 qf_rele(qfp);
756 dq_list_unlock();
757
758 *dqp = dq;
759 return (0);
760 }
761
762 /*
763 * Lookup a dqblk structure for the specified identifier and
764 * quota file. If there is no entry for this identifier then
765 * one is inserted. The actual hash table index is returned.
766 */
767 static int
768 dqlookup(qfp, id, dqb, index)
769 struct quotafile *qfp;
770 u_long id;
771 struct dqblk *dqb;
772 u_int32_t *index;
773 {
774 struct vnode *dqvp;
775 struct vfs_context context;
776 uio_t auio;
777 int i, skip, last;
778 u_long mask;
779 int error = 0;
780 char uio_buf[ UIO_SIZEOF(1) ];
781
782
783 qf_lock(qfp);
784
785 dqvp = qfp->qf_vp;
786
787 context.vc_proc = current_proc();
788 context.vc_ucred = qfp->qf_cred;
789
790 mask = qfp->qf_maxentries - 1;
791 i = dqhash1(id, qfp->qf_shift, mask);
792 skip = dqhash2(id, mask);
793
794 for (last = (i + (qfp->qf_maxentries-1) * skip) & mask;
795 i != last;
796 i = (i + skip) & mask) {
797 auio = uio_createwithbuffer(1, dqoffset(i), UIO_SYSSPACE, UIO_READ,
798 &uio_buf[0], sizeof(uio_buf));
799 uio_addiov(auio, CAST_USER_ADDR_T(dqb), sizeof (struct dqblk));
800 error = VNOP_READ(dqvp, auio, 0, &context);
801 if (error) {
802 printf("dqlookup: error %d looking up id %d at index %d\n", error, id, i);
803 break;
804 } else if (uio_resid(auio)) {
805 error = EIO;
806 printf("dqlookup: error looking up id %d at index %d\n", id, i);
807 break;
808 }
809 /*
810 * An empty entry means there is no entry
811 * with that id. In this case a new dqb
812 * record will be inserted.
813 */
814 if (dqb->dqb_id == 0) {
815 bzero(dqb, sizeof(struct dqblk));
816 dqb->dqb_id = OSSwapHostToBigInt32(id);
817 /*
818 * Write back to reserve entry for this id
819 */
820 uio_reset(auio, dqoffset(i), UIO_SYSSPACE, UIO_WRITE);
821 uio_addiov(auio, CAST_USER_ADDR_T(dqb), sizeof (struct dqblk));
822 error = VNOP_WRITE(dqvp, auio, 0, &context);
823 if (uio_resid(auio) && error == 0)
824 error = EIO;
825 if (error == 0)
826 ++qfp->qf_entrycnt;
827 dqb->dqb_id = id;
828 break;
829 }
830 /* An id match means an entry was found. */
831 if (OSSwapBigToHostInt32(dqb->dqb_id) == id) {
832 dqb->dqb_bhardlimit = OSSwapBigToHostInt64(dqb->dqb_bhardlimit);
833 dqb->dqb_bsoftlimit = OSSwapBigToHostInt64(dqb->dqb_bsoftlimit);
834 dqb->dqb_curbytes = OSSwapBigToHostInt64(dqb->dqb_curbytes);
835 dqb->dqb_ihardlimit = OSSwapBigToHostInt32(dqb->dqb_ihardlimit);
836 dqb->dqb_isoftlimit = OSSwapBigToHostInt32(dqb->dqb_isoftlimit);
837 dqb->dqb_curinodes = OSSwapBigToHostInt32(dqb->dqb_curinodes);
838 dqb->dqb_btime = OSSwapBigToHostInt32(dqb->dqb_btime);
839 dqb->dqb_itime = OSSwapBigToHostInt32(dqb->dqb_itime);
840 dqb->dqb_id = OSSwapBigToHostInt32(dqb->dqb_id);
841 break;
842 }
843 }
844 qf_unlock(qfp);
845
846 *index = i; /* remember index so we don't have to recompute it later */
847
848 return (error);
849 }
850
851
852 /*
853 * Release a reference to a dquot.
854 */
855 void
856 dqrele(struct dquot *dq)
857 {
858
859 if (dq == NODQUOT)
860 return;
861 dqlock(dq);
862
863 if (dq->dq_cnt > 1) {
864 dq->dq_cnt--;
865
866 dqunlock(dq);
867 return;
868 }
869 if (dq->dq_flags & DQ_MOD)
870 (void) dqsync_locked(dq);
871 dq->dq_cnt--;
872
873 dq_list_lock();
874 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
875 dq_unlock_internal(dq);
876 dq_list_unlock();
877 }
878
879 /*
880 * Release a reference to a dquot but don't do any I/O.
881 */
882 void
883 dqreclaim(register struct dquot *dq)
884 {
885
886 if (dq == NODQUOT)
887 return;
888
889 dq_list_lock();
890 dq_lock_internal(dq);
891
892 if (--dq->dq_cnt > 0) {
893 dq_unlock_internal(dq);
894 dq_list_unlock();
895 return;
896 }
897 if (dq->dq_flags & DQ_MOD)
898 TAILQ_INSERT_TAIL(&dqdirtylist, dq, dq_freelist);
899 else
900 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
901
902 dq_unlock_internal(dq);
903 dq_list_unlock();
904 }
905
906 /*
907 * Update a quota file's orphaned disk quotas.
908 */
909 void
910 dqsync_orphans(qfp)
911 struct quotafile *qfp;
912 {
913 struct dquot *dq;
914 int listlockval = 0;
915
916 dq_list_lock();
917 loop:
918 listlockval = dq_list_lock_val();
919
920 TAILQ_FOREACH(dq, &dqdirtylist, dq_freelist) {
921 if (dq->dq_qfile != qfp)
922 continue;
923
924 dq_lock_internal(dq);
925 if (dq_list_lock_changed(listlockval)) {
926 dq_unlock_internal(dq);
927 goto loop;
928 }
929
930 if (dq->dq_qfile != qfp) {
931 /*
932 * the identity of this dq changed while
933 * the quota_list_lock was dropped
934 * dq_lock_internal can drop it to msleep
935 */
936 dq_unlock_internal(dq);
937 goto loop;
938 }
939 if ((dq->dq_flags & DQ_MOD) == 0) {
940 /*
941 * someone cleaned and removed this from
942 * the dq from the dirty list while the
943 * quota_list_lock was dropped
944 */
945 dq_unlock_internal(dq);
946 goto loop;
947 }
948 if (dq->dq_cnt != 0)
949 panic("dqsync_orphans: dquot in use");
950
951 TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist);
952
953 dq_list_unlock();
954 /*
955 * we're still holding the dqlock at this point
956 * with the reference count == 0
957 * we shouldn't be able
958 * to pick up another one since we hold dqlock
959 */
960 (void) dqsync_locked(dq);
961
962 dq_list_lock();
963
964 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
965
966 dq_unlock_internal(dq);
967 goto loop;
968 }
969 dq_list_unlock();
970 }
971
972 int
973 dqsync(struct dquot *dq)
974 {
975 int error = 0;
976
977 if (dq != NODQUOT) {
978 dqlock(dq);
979
980 if ( (dq->dq_flags & DQ_MOD) )
981 error = dqsync_locked(dq);
982
983 dqunlock(dq);
984 }
985 return (error);
986 }
987
988
989 /*
990 * Update the disk quota in the quota file.
991 */
992 int
993 dqsync_locked(struct dquot *dq)
994 {
995 struct proc *p = current_proc(); /* XXX */
996 struct vfs_context context;
997 struct vnode *dqvp;
998 struct dqblk dqb, *dqblkp;
999 uio_t auio;
1000 int error;
1001 char uio_buf[ UIO_SIZEOF(1) ];
1002
1003 if (dq->dq_id == 0) {
1004 dq->dq_flags &= ~DQ_MOD;
1005 return (0);
1006 }
1007 if (dq->dq_qfile == NULL)
1008 panic("dqsync: NULL dq_qfile");
1009 if ((dqvp = dq->dq_qfile->qf_vp) == NULLVP)
1010 panic("dqsync: NULL qf_vp");
1011
1012 auio = uio_createwithbuffer(1, dqoffset(dq->dq_index), UIO_SYSSPACE,
1013 UIO_WRITE, &uio_buf[0], sizeof(uio_buf));
1014 uio_addiov(auio, CAST_USER_ADDR_T(&dqb), sizeof (struct dqblk));
1015
1016 context.vc_proc = p;
1017 context.vc_ucred = dq->dq_qfile->qf_cred;
1018
1019 dqblkp = &dq->dq_dqb;
1020 dqb.dqb_bhardlimit = OSSwapHostToBigInt64(dqblkp->dqb_bhardlimit);
1021 dqb.dqb_bsoftlimit = OSSwapHostToBigInt64(dqblkp->dqb_bsoftlimit);
1022 dqb.dqb_curbytes = OSSwapHostToBigInt64(dqblkp->dqb_curbytes);
1023 dqb.dqb_ihardlimit = OSSwapHostToBigInt32(dqblkp->dqb_ihardlimit);
1024 dqb.dqb_isoftlimit = OSSwapHostToBigInt32(dqblkp->dqb_isoftlimit);
1025 dqb.dqb_curinodes = OSSwapHostToBigInt32(dqblkp->dqb_curinodes);
1026 dqb.dqb_btime = OSSwapHostToBigInt32(dqblkp->dqb_btime);
1027 dqb.dqb_itime = OSSwapHostToBigInt32(dqblkp->dqb_itime);
1028 dqb.dqb_id = OSSwapHostToBigInt32(dqblkp->dqb_id);
1029 dqb.dqb_spare[0] = 0;
1030 dqb.dqb_spare[1] = 0;
1031 dqb.dqb_spare[2] = 0;
1032 dqb.dqb_spare[3] = 0;
1033
1034 error = VNOP_WRITE(dqvp, auio, 0, &context);
1035 if (uio_resid(auio) && error == 0)
1036 error = EIO;
1037 dq->dq_flags &= ~DQ_MOD;
1038
1039 return (error);
1040 }
1041
1042 /*
1043 * Flush all entries from the cache for a particular vnode.
1044 */
1045 void
1046 dqflush(vp)
1047 register struct vnode *vp;
1048 {
1049 register struct dquot *dq, *nextdq;
1050 struct dqhash *dqh;
1051
1052 /*
1053 * Move all dquot's that used to refer to this quota
1054 * file off their hash chains (they will eventually
1055 * fall off the head of the free list and be re-used).
1056 */
1057 dq_list_lock();
1058
1059 for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
1060 for (dq = dqh->lh_first; dq; dq = nextdq) {
1061 nextdq = dq->dq_hash.le_next;
1062 if (dq->dq_qfile->qf_vp != vp)
1063 continue;
1064 if (dq->dq_cnt)
1065 panic("dqflush: stray dquot");
1066 LIST_REMOVE(dq, dq_hash);
1067 dq->dq_qfile = NULL;
1068 }
1069 }
1070 dq_list_unlock();
1071 }
1072
1073 /*
1074 * LP64 support for munging dqblk structure.
1075 * XXX conversion of user_time_t to time_t loses precision; not an issue for
1076 * XXX us now, since we are only ever setting 32 bits worth of time into it.
1077 */
1078 __private_extern__ void
1079 munge_dqblk(struct dqblk *dqblkp, struct user_dqblk *user_dqblkp, boolean_t to64)
1080 {
1081 if (to64) {
1082 /* munge kernel (32 bit) dqblk into user (64 bit) dqblk */
1083 bcopy((caddr_t)dqblkp, (caddr_t)user_dqblkp, offsetof(struct dqblk, dqb_btime));
1084 user_dqblkp->dqb_id = dqblkp->dqb_id;
1085 user_dqblkp->dqb_itime = dqblkp->dqb_itime;
1086 user_dqblkp->dqb_btime = dqblkp->dqb_btime;
1087 }
1088 else {
1089 /* munge user (64 bit) dqblk into kernel (32 bit) dqblk */
1090 bcopy((caddr_t)user_dqblkp, (caddr_t)dqblkp, offsetof(struct dqblk, dqb_btime));
1091 dqblkp->dqb_id = user_dqblkp->dqb_id;
1092 dqblkp->dqb_itime = user_dqblkp->dqb_itime; /* XXX - lose precision */
1093 dqblkp->dqb_btime = user_dqblkp->dqb_btime; /* XXX - lose precision */
1094 }
1095 }