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