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