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