]> git.saurik.com Git - apple/xnu.git/blame - bsd/vfs/vfs_quota.c
xnu-792.25.20.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_quota.c
CommitLineData
9bccf70c 1/*
0c530ab8 2 * Copyright (c) 2002-2005 Apple Computer, Inc. All rights reserved.
9bccf70c 3 *
6601e61a 4 * @APPLE_LICENSE_HEADER_START@
9bccf70c 5 *
6601e61a
A
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.
8f6c56a5 11 *
6601e61a
A
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
8f6c56a5
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
6601e61a
A
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.
8f6c56a5 19 *
6601e61a 20 * @APPLE_LICENSE_HEADER_END@
9bccf70c
A
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>
91447636
A
65#include <sys/file_internal.h>
66#include <sys/proc_internal.h>
67#include <sys/vnode_internal.h>
68#include <sys/mount_internal.h>
9bccf70c 69#include <sys/quota.h>
91447636 70#include <sys/uio_internal.h>
9bccf70c 71
0c530ab8
A
72#include <libkern/OSByteOrder.h>
73
9bccf70c 74
91447636
A
75/* vars for quota file lock */
76lck_grp_t * qf_lck_grp;
77lck_grp_attr_t * qf_lck_grp_attr;
78lck_attr_t * qf_lck_attr;
79
80/* vars for quota list lock */
81lck_grp_t * quota_list_lck_grp;
82lck_grp_attr_t * quota_list_lck_grp_attr;
83lck_attr_t * quota_list_lck_attr;
84lck_mtx_t * quota_list_mtx_lock;
85
86/* Routines to lock and unlock the quota global data */
0c530ab8 87static int dq_list_lock(void);
91447636
A
88static void dq_list_unlock(void);
89
90static void dq_lock_internal(struct dquot *dq);
91static void dq_unlock_internal(struct dquot *dq);
92
9bccf70c
A
93static 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])
101LIST_HEAD(dqhash, dquot) *dqhashtbl;
102u_long dqhash;
103
91447636
A
104#define DQUOTINC 5 /* minimum free dquots desired */
105long numdquot, desireddquot = DQUOTINC;
106
9bccf70c
A
107/*
108 * Dquot free list.
109 */
9bccf70c 110TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
d7e50217 111/*
91447636 112 * Dquot dirty orphans list
d7e50217
A
113 */
114TAILQ_HEAD(dqdirtylist, dquot) dqdirtylist;
115
9bccf70c 116
91447636
A
117static int dqlookup(struct quotafile *, u_long, struct dqblk *, u_int32_t *);
118static int dqsync_locked(struct dquot *dq);
119
120static void qf_lock(struct quotafile *);
121static void qf_unlock(struct quotafile *);
122static int qf_ref(struct quotafile *);
123static void qf_rele(struct quotafile *);
9bccf70c
A
124
125
126/*
127 * Initialize the quota system.
128 */
129void
130dqinit()
131{
132
133 dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
134 TAILQ_INIT(&dqfreelist);
d7e50217 135 TAILQ_INIT(&dqdirtylist);
91447636
A
136
137 /*
138 * Allocate quota list lock group attribute and group
139 */
140 quota_list_lck_grp_attr= lck_grp_attr_alloc_init();
91447636
A
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();
91447636
A
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();
91447636
A
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();
91447636
A
164}
165
166
0c530ab8 167static volatile int dq_list_lock_cnt = 0;
91447636 168
0c530ab8 169static int
91447636
A
170dq_list_lock(void)
171{
172 lck_mtx_lock(quota_list_mtx_lock);
0c530ab8
A
173 return ++dq_list_lock_cnt;
174}
175
176static int
177dq_list_lock_changed(int oldval) {
178 return (dq_list_lock_cnt != oldval);
179}
180
181static int
182dq_list_lock_val(void) {
183 return dq_list_lock_cnt;
91447636
A
184}
185
186void
187dq_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 */
196void
197dq_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 */
209void
210dq_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
220void
221dqlock(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
230void
231dqunlock(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
242int
243qf_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
290void
291qf_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
311static void
312qf_lock(struct quotafile *qfp)
313{
314 lck_mtx_lock(&qfp->qf_lock);
315}
316
317static void
318qf_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 */
338static int
339qf_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 */
357static void
358qf_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
369void
370dqfileinit(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);
9bccf70c
A
376}
377
378
379/*
380 * Initialize a quota file
91447636
A
381 *
382 * must be called with the quota file lock held
9bccf70c
A
383 */
384int
385dqfileopen(qfp, type)
386 struct quotafile *qfp;
387 int type;
388{
389 struct dqfilehdr header;
91447636
A
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) ];
9bccf70c 395
91447636
A
396 context.vc_proc = current_proc();
397 context.vc_ucred = qfp->qf_cred;
398
9bccf70c 399 /* Obtain the file size */
91447636
A
400 if ((error = vnode_size(qfp->qf_vp, &file_size, &context)) != 0)
401 goto out;
9bccf70c
A
402
403 /* Read the file header */
91447636
A
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);
9bccf70c 408 if (error)
91447636
A
409 goto out;
410 else if (uio_resid(auio)) {
411 error = EINVAL;
412 goto out;
413 }
9bccf70c 414 /* Sanity check the quota file header. */
0c530ab8
A
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)))) {
91447636
A
419 error = EINVAL;
420 goto out;
421 }
9bccf70c 422 /* Set up the time limits for this quota. */
0c530ab8
A
423 if (header.dqh_btime != 0)
424 qfp->qf_btime = OSSwapBigToHostInt32(header.dqh_btime);
9bccf70c
A
425 else
426 qfp->qf_btime = MAX_DQ_TIME;
0c530ab8
A
427 if (header.dqh_itime != 0)
428 qfp->qf_itime = OSSwapBigToHostInt32(header.dqh_itime);
9bccf70c
A
429 else
430 qfp->qf_itime = MAX_IQ_TIME;
431
432 /* Calculate the hash table constants. */
0c530ab8
A
433 qfp->qf_maxentries = OSSwapBigToHostInt32(header.dqh_maxentries);
434 qfp->qf_entrycnt = OSSwapBigToHostInt32(header.dqh_entrycnt);
435 qfp->qf_shift = dqhashshift(qfp->qf_maxentries);
91447636
A
436out:
437 return (error);
9bccf70c
A
438}
439
440/*
441 * Close down a quota file
442 */
443void
91447636 444dqfileclose(struct quotafile *qfp, __unused int type)
9bccf70c
A
445{
446 struct dqfilehdr header;
91447636
A
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));
9bccf70c 454
91447636
A
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) {
0c530ab8 459 header.dqh_entrycnt = OSSwapHostToBigInt32(qfp->qf_entrycnt);
91447636
A
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);
9bccf70c
A
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 */
471int
91447636 472dqget(id, qfp, type, dqp)
9bccf70c
A
473 u_long id;
474 struct quotafile *qfp;
475 register int type;
476 struct dquot **dqp;
477{
9bccf70c 478 struct dquot *dq;
91447636
A
479 struct dquot *ndq = NULL;
480 struct dquot *fdq = NULL;
9bccf70c
A
481 struct dqhash *dqh;
482 struct vnode *dqvp;
483 int error = 0;
0c530ab8 484 int listlockval = 0;
9bccf70c 485
91447636
A
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
9bccf70c
A
502 *dqp = NODQUOT;
503 return (EINVAL);
504 }
91447636
A
505 dqh = DQHASH(dqvp, id);
506
507relookup:
0c530ab8
A
508 listlockval = dq_list_lock_val();
509
9bccf70c
A
510 /*
511 * Check the cache first.
512 */
9bccf70c
A
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;
91447636
A
517
518 dq_lock_internal(dq);
0c530ab8
A
519 if (dq_list_lock_changed(listlockval)) {
520 dq_unlock_internal(dq);
521 goto relookup;
522 }
523
91447636
A
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 }
9bccf70c
A
533 /*
534 * Cache hit with no references. Take
535 * the structure off the free list.
536 */
91447636 537 if (dq->dq_cnt++ == 0) {
d7e50217
A
538 if (dq->dq_flags & DQ_MOD)
539 TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist);
540 else
541 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
0c530ab8
A
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);
d7e50217 549 }
91447636
A
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 }
9bccf70c 572 *dqp = dq;
91447636 573
9bccf70c
A
574 return (0);
575 }
576 /*
577 * Not in cache, allocate a new one.
578 */
91447636 579 if (TAILQ_EMPTY(&dqfreelist) &&
9bccf70c
A
580 numdquot < MAXQUOTAS * desiredvnodes)
581 desireddquot += DQUOTINC;
91447636
A
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
0c530ab8 601 listlockval = dq_list_lock();
91447636
A
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 }
9bccf70c 618 } else {
91447636
A
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 }
9bccf70c
A
631 tablefull("dquot");
632 *dqp = NODQUOT;
633 return (EUSERS);
634 }
91447636
A
635 dq = TAILQ_FIRST(&dqfreelist);
636
637 dq_lock_internal(dq);
638
0c530ab8 639 if (dq_list_lock_changed(listlockval) || dq->dq_cnt || (dq->dq_flags & DQ_MOD)) {
91447636
A
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 }
9bccf70c 655 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
91447636
A
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;
9bccf70c 673 }
91447636
A
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);
0c530ab8
A
682 if (dq_list_lock_changed(listlockval)) {
683 dq_unlock_internal(dq);
684 goto relookup;
685 }
91447636 686
9bccf70c
A
687 /*
688 * Initialize the contents of the dquot structure.
689 */
91447636
A
690 dq->dq_cnt = 1;
691 dq->dq_flags = 0;
9bccf70c
A
692 dq->dq_id = id;
693 dq->dq_qfile = qfp;
694 dq->dq_type = type;
91447636
A
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
9bccf70c
A
715 error = dqlookup(qfp, id, &dq->dq_dqb, &dq->dq_index);
716
9bccf70c
A
717 /*
718 * I/O error in reading quota file, release
719 * quota structure and reflect problem to caller.
720 */
721 if (error) {
91447636
A
722 dq_list_lock();
723
724 dq->dq_id = 0;
725 dq->dq_qfile = NULL;
9bccf70c 726 LIST_REMOVE(dq, dq_hash);
91447636
A
727
728 dq_unlock_internal(dq);
729 qf_rele(qfp);
730 dq_list_unlock();
731
732 dqrele(dq);
733
9bccf70c
A
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) {
91447636
A
745 struct timeval tv;
746
747 microtime(&tv);
9bccf70c 748 if (dq->dq_btime == 0)
91447636 749 dq->dq_btime = tv.tv_sec + qfp->qf_btime;
9bccf70c 750 if (dq->dq_itime == 0)
91447636 751 dq->dq_itime = tv.tv_sec + qfp->qf_itime;
9bccf70c 752 }
91447636
A
753 dq_list_lock();
754 dq_unlock_internal(dq);
755 qf_rele(qfp);
756 dq_list_unlock();
757
9bccf70c
A
758 *dqp = dq;
759 return (0);
760}
761
762/*
763 * Lookup a dqblk structure for the specified identifier and
91447636 764 * quota file. If there is no entry for this identifier then
9bccf70c
A
765 * one is inserted. The actual hash table index is returned.
766 */
767static int
768dqlookup(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;
91447636
A
775 struct vfs_context context;
776 uio_t auio;
9bccf70c
A
777 int i, skip, last;
778 u_long mask;
779 int error = 0;
91447636
A
780 char uio_buf[ UIO_SIZEOF(1) ];
781
782
783 qf_lock(qfp);
9bccf70c 784
9bccf70c 785 dqvp = qfp->qf_vp;
9bccf70c 786
91447636
A
787 context.vc_proc = current_proc();
788 context.vc_ucred = qfp->qf_cred;
9bccf70c
A
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) {
91447636
A
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);
9bccf70c
A
801 if (error) {
802 printf("dqlookup: error %d looking up id %d at index %d\n", error, id, i);
803 break;
91447636 804 } else if (uio_resid(auio)) {
9bccf70c
A
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));
0c530ab8 816 dqb->dqb_id = OSSwapHostToBigInt32(id);
9bccf70c
A
817 /*
818 * Write back to reserve entry for this id
819 */
91447636
A
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)
9bccf70c
A
824 error = EIO;
825 if (error == 0)
826 ++qfp->qf_entrycnt;
0c530ab8 827 dqb->dqb_id = id;
9bccf70c
A
828 break;
829 }
830 /* An id match means an entry was found. */
0c530ab8
A
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);
9bccf70c 841 break;
0c530ab8 842 }
9bccf70c 843 }
91447636
A
844 qf_unlock(qfp);
845
9bccf70c 846 *index = i; /* remember index so we don't have to recompute it later */
91447636 847
9bccf70c
A
848 return (error);
849}
850
9bccf70c
A
851
852/*
853 * Release a reference to a dquot.
854 */
855void
91447636 856dqrele(struct dquot *dq)
9bccf70c
A
857{
858
859 if (dq == NODQUOT)
860 return;
91447636
A
861 dqlock(dq);
862
9bccf70c
A
863 if (dq->dq_cnt > 1) {
864 dq->dq_cnt--;
91447636
A
865
866 dqunlock(dq);
9bccf70c
A
867 return;
868 }
869 if (dq->dq_flags & DQ_MOD)
91447636
A
870 (void) dqsync_locked(dq);
871 dq->dq_cnt--;
872
873 dq_list_lock();
9bccf70c 874 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
91447636
A
875 dq_unlock_internal(dq);
876 dq_list_unlock();
9bccf70c
A
877}
878
d7e50217
A
879/*
880 * Release a reference to a dquot but don't do any I/O.
881 */
882void
91447636 883dqreclaim(register struct dquot *dq)
d7e50217 884{
91447636 885
d7e50217
A
886 if (dq == NODQUOT)
887 return;
888
91447636
A
889 dq_list_lock();
890 dq_lock_internal(dq);
d7e50217 891
91447636
A
892 if (--dq->dq_cnt > 0) {
893 dq_unlock_internal(dq);
894 dq_list_unlock();
895 return;
896 }
d7e50217
A
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);
91447636
A
901
902 dq_unlock_internal(dq);
903 dq_list_unlock();
d7e50217
A
904}
905
906/*
907 * Update a quota file's orphaned disk quotas.
908 */
909void
910dqsync_orphans(qfp)
911 struct quotafile *qfp;
912{
913 struct dquot *dq;
0c530ab8
A
914 int listlockval = 0;
915
91447636 916 dq_list_lock();
d7e50217 917 loop:
0c530ab8
A
918 listlockval = dq_list_lock_val();
919
d7e50217 920 TAILQ_FOREACH(dq, &dqdirtylist, dq_freelist) {
91447636
A
921 if (dq->dq_qfile != qfp)
922 continue;
923
924 dq_lock_internal(dq);
0c530ab8
A
925 if (dq_list_lock_changed(listlockval)) {
926 dq_unlock_internal(dq);
927 goto loop;
928 }
91447636
A
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 }
d7e50217
A
948 if (dq->dq_cnt != 0)
949 panic("dqsync_orphans: dquot in use");
950
91447636 951 TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist);
d7e50217 952
91447636
A
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();
d7e50217 963
91447636 964 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
d7e50217 965
91447636
A
966 dq_unlock_internal(dq);
967 goto loop;
968 }
969 dq_list_unlock();
970}
971
972int
973dqsync(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);
d7e50217 984 }
91447636 985 return (error);
d7e50217
A
986}
987
91447636 988
9bccf70c
A
989/*
990 * Update the disk quota in the quota file.
991 */
992int
91447636 993dqsync_locked(struct dquot *dq)
9bccf70c
A
994{
995 struct proc *p = current_proc(); /* XXX */
91447636 996 struct vfs_context context;
9bccf70c 997 struct vnode *dqvp;
0c530ab8 998 struct dqblk dqb, *dqblkp;
91447636 999 uio_t auio;
9bccf70c 1000 int error;
91447636 1001 char uio_buf[ UIO_SIZEOF(1) ];
9bccf70c 1002
91447636
A
1003 if (dq->dq_id == 0) {
1004 dq->dq_flags &= ~DQ_MOD;
9bccf70c 1005 return (0);
9bccf70c 1006 }
91447636
A
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));
0c530ab8 1014 uio_addiov(auio, CAST_USER_ADDR_T(&dqb), sizeof (struct dqblk));
91447636
A
1015
1016 context.vc_proc = p;
1017 context.vc_ucred = dq->dq_qfile->qf_cred;
1018
0c530ab8
A
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
91447636
A
1034 error = VNOP_WRITE(dqvp, auio, 0, &context);
1035 if (uio_resid(auio) && error == 0)
9bccf70c 1036 error = EIO;
91447636
A
1037 dq->dq_flags &= ~DQ_MOD;
1038
9bccf70c
A
1039 return (error);
1040}
1041
1042/*
1043 * Flush all entries from the cache for a particular vnode.
1044 */
1045void
1046dqflush(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 */
91447636
A
1057 dq_list_lock();
1058
9bccf70c
A
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);
91447636 1067 dq->dq_qfile = NULL;
9bccf70c
A
1068 }
1069 }
91447636
A
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
1079munge_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 }
9bccf70c 1095}