]>
git.saurik.com Git - apple/xnu.git/blob - bsd/vfs/vfs_quota.c
2 * Copyright (c) 2002-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * Copyright (c) 1982, 1986, 1990, 1993, 1995
24 * The Regents of the University of California. All rights reserved.
26 * This code is derived from software contributed to Berkeley by
27 * Robert Elz at The University of Melbourne.
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
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.
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
58 * derived from @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
61 #include <sys/param.h>
62 #include <sys/kernel.h>
63 #include <sys/systm.h>
64 #include <sys/malloc.h>
65 #include <sys/file_internal.h>
66 #include <sys/proc_internal.h>
67 #include <sys/vnode_internal.h>
68 #include <sys/mount_internal.h>
69 #include <sys/quota.h>
70 #include <sys/uio_internal.h>
72 #include <libkern/OSByteOrder.h>
75 /* vars for quota file lock */
76 lck_grp_t
* qf_lck_grp
;
77 lck_grp_attr_t
* qf_lck_grp_attr
;
78 lck_attr_t
* qf_lck_attr
;
80 /* vars for quota list lock */
81 lck_grp_t
* quota_list_lck_grp
;
82 lck_grp_attr_t
* quota_list_lck_grp_attr
;
83 lck_attr_t
* quota_list_lck_attr
;
84 lck_mtx_t
* quota_list_mtx_lock
;
86 /* Routines to lock and unlock the quota global data */
87 static int dq_list_lock(void);
88 static void dq_list_unlock(void);
90 static void dq_lock_internal(struct dquot
*dq
);
91 static void dq_unlock_internal(struct dquot
*dq
);
93 static u_int32_t quotamagic
[MAXQUOTAS
] = INITQMAGICS
;
97 * Code pertaining to management of the in-core dquot data structures.
99 #define DQHASH(dqvp, id) \
100 (&dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash])
101 LIST_HEAD(dqhash
, dquot
) *dqhashtbl
;
104 #define DQUOTINC 5 /* minimum free dquots desired */
105 long numdquot
, desireddquot
= DQUOTINC
;
110 TAILQ_HEAD(dqfreelist
, dquot
) dqfreelist
;
112 * Dquot dirty orphans list
114 TAILQ_HEAD(dqdirtylist
, dquot
) dqdirtylist
;
117 static int dqlookup(struct quotafile
*, u_long
, struct dqblk
*, u_int32_t
*);
118 static int dqsync_locked(struct dquot
*dq
);
120 static void qf_lock(struct quotafile
*);
121 static void qf_unlock(struct quotafile
*);
122 static int qf_ref(struct quotafile
*);
123 static void qf_rele(struct quotafile
*);
127 * Initialize the quota system.
133 dqhashtbl
= hashinit(desiredvnodes
, M_DQUOT
, &dqhash
);
134 TAILQ_INIT(&dqfreelist
);
135 TAILQ_INIT(&dqdirtylist
);
138 * Allocate quota list lock group attribute and group
140 quota_list_lck_grp_attr
= lck_grp_attr_alloc_init();
141 quota_list_lck_grp
= lck_grp_alloc_init("quota list", quota_list_lck_grp_attr
);
144 * Allocate qouta list lock attribute
146 quota_list_lck_attr
= lck_attr_alloc_init();
149 * Allocate quota list lock
151 quota_list_mtx_lock
= lck_mtx_alloc_init(quota_list_lck_grp
, quota_list_lck_attr
);
155 * allocate quota file lock group attribute and group
157 qf_lck_grp_attr
= lck_grp_attr_alloc_init();
158 qf_lck_grp
= lck_grp_alloc_init("quota file", qf_lck_grp_attr
);
161 * Allocate quota file lock attribute
163 qf_lck_attr
= lck_attr_alloc_init();
167 static volatile int dq_list_lock_cnt
= 0;
172 lck_mtx_lock(quota_list_mtx_lock
);
173 return ++dq_list_lock_cnt
;
177 dq_list_lock_changed(int oldval
) {
178 return (dq_list_lock_cnt
!= oldval
);
182 dq_list_lock_val(void) {
183 return dq_list_lock_cnt
;
189 lck_mtx_unlock(quota_list_mtx_lock
);
194 * must be called with the quota_list_lock held
197 dq_lock_internal(struct dquot
*dq
)
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);
203 dq
->dq_lflags
|= DQ_LLOCK
;
207 * must be called with the quota_list_lock held
210 dq_unlock_internal(struct dquot
*dq
)
212 int wanted
= dq
->dq_lflags
& DQ_LWANT
;
214 dq
->dq_lflags
&= ~(DQ_LLOCK
| DQ_LWANT
);
217 wakeup(&dq
->dq_lflags
);
221 dqlock(struct dquot
*dq
) {
223 lck_mtx_lock(quota_list_mtx_lock
);
225 dq_lock_internal(dq
);
227 lck_mtx_unlock(quota_list_mtx_lock
);
231 dqunlock(struct dquot
*dq
) {
233 lck_mtx_lock(quota_list_mtx_lock
);
235 dq_unlock_internal(dq
);
237 lck_mtx_unlock(quota_list_mtx_lock
);
243 qf_get(struct quotafile
*qfp
, int type
)
252 while ( (qfp
->qf_qflags
& (QTF_OPENING
| QTF_CLOSING
)) ) {
253 if ( (qfp
->qf_qflags
& QTF_OPENING
) ) {
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);
262 if (qfp
->qf_vp
!= NULLVP
)
265 qfp
->qf_qflags
|= QTF_OPENING
;
269 if ( (qfp
->qf_qflags
& QTF_CLOSING
) ) {
273 qfp
->qf_qflags
|= QTF_CLOSING
;
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);
279 if (qfp
->qf_vp
== NULLVP
) {
280 qfp
->qf_qflags
&= ~QTF_CLOSING
;
291 qf_put(struct quotafile
*qfp
, int type
)
300 qfp
->qf_qflags
&= ~type
;
303 if ( (qfp
->qf_qflags
& QTF_WANTED
) ) {
304 qfp
->qf_qflags
&= ~QTF_WANTED
;
305 wakeup(&qfp
->qf_qflags
);
312 qf_lock(struct quotafile
*qfp
)
314 lck_mtx_lock(&qfp
->qf_lock
);
318 qf_unlock(struct quotafile
*qfp
)
320 lck_mtx_unlock(&qfp
->qf_lock
);
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
336 * quota list lock must be held on entry
339 qf_ref(struct quotafile
*qfp
)
343 if ( (qfp
->qf_qflags
& (QTF_OPENING
| QTF_CLOSING
)) || (qfp
->qf_vp
== NULLVP
) )
352 * drop our reference and wakeup any waiters if
353 * we were the last one holding a ref
355 * quota list lock must be held on entry
358 qf_rele(struct quotafile
*qfp
)
362 if ( (qfp
->qf_qflags
& QTF_WANTED
) && qfp
->qf_refcnt
== 0) {
363 qfp
->qf_qflags
&= ~QTF_WANTED
;
364 wakeup(&qfp
->qf_qflags
);
370 dqfileinit(struct quotafile
*qfp
)
375 lck_mtx_init(&qfp
->qf_lock
, qf_lck_grp
, qf_lck_attr
);
380 * Initialize a quota file
382 * must be called with the quota file lock held
385 dqfileopen(qfp
, type
)
386 struct quotafile
*qfp
;
389 struct dqfilehdr header
;
390 struct vfs_context context
;
394 char uio_buf
[ UIO_SIZEOF(1) ];
396 context
.vc_proc
= current_proc();
397 context
.vc_ucred
= qfp
->qf_cred
;
399 /* Obtain the file size */
400 if ((error
= vnode_size(qfp
->qf_vp
, &file_size
, &context
)) != 0)
403 /* Read the file header */
404 auio
= uio_createwithbuffer(1, 0, UIO_SYSSPACE
, UIO_READ
,
405 &uio_buf
[0], sizeof(uio_buf
));
406 uio_addiov(auio
, CAST_USER_ADDR_T(&header
), sizeof (header
));
407 error
= VNOP_READ(qfp
->qf_vp
, auio
, 0, &context
);
410 else if (uio_resid(auio
)) {
414 /* Sanity check the quota file header. */
415 if ((OSSwapBigToHostInt32(header
.dqh_magic
) != quotamagic
[type
]) ||
416 (OSSwapBigToHostInt32(header
.dqh_version
) > QF_VERSION
) ||
417 (!powerof2(OSSwapBigToHostInt32(header
.dqh_maxentries
))) ||
418 (OSSwapBigToHostInt32(header
.dqh_maxentries
) > (file_size
/ sizeof(struct dqblk
)))) {
422 /* Set up the time limits for this quota. */
423 if (header
.dqh_btime
!= 0)
424 qfp
->qf_btime
= OSSwapBigToHostInt32(header
.dqh_btime
);
426 qfp
->qf_btime
= MAX_DQ_TIME
;
427 if (header
.dqh_itime
!= 0)
428 qfp
->qf_itime
= OSSwapBigToHostInt32(header
.dqh_itime
);
430 qfp
->qf_itime
= MAX_IQ_TIME
;
432 /* Calculate the hash table constants. */
433 qfp
->qf_maxentries
= OSSwapBigToHostInt32(header
.dqh_maxentries
);
434 qfp
->qf_entrycnt
= OSSwapBigToHostInt32(header
.dqh_entrycnt
);
435 qfp
->qf_shift
= dqhashshift(qfp
->qf_maxentries
);
441 * Close down a quota file
444 dqfileclose(struct quotafile
*qfp
, __unused
int type
)
446 struct dqfilehdr header
;
447 struct vfs_context context
;
449 char uio_buf
[ UIO_SIZEOF(1) ];
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
));
455 context
.vc_proc
= current_proc();
456 context
.vc_ucred
= qfp
->qf_cred
;
458 if (VNOP_READ(qfp
->qf_vp
, auio
, 0, &context
) == 0) {
459 header
.dqh_entrycnt
= OSSwapHostToBigInt32(qfp
->qf_entrycnt
);
460 uio_reset(auio
, 0, UIO_SYSSPACE
, UIO_WRITE
);
461 uio_addiov(auio
, CAST_USER_ADDR_T(&header
), sizeof (header
));
462 (void) VNOP_WRITE(qfp
->qf_vp
, auio
, 0, &context
);
468 * Obtain a dquot structure for the specified identifier and quota file
469 * reading the information from the file if necessary.
472 dqget(id
, qfp
, type
, dqp
)
474 struct quotafile
*qfp
;
479 struct dquot
*ndq
= NULL
;
480 struct dquot
*fdq
= NULL
;
486 if ( id
== 0 || qfp
->qf_vp
== NULLVP
) {
492 if ( (qf_ref(qfp
)) ) {
498 if ( (dqvp
= qfp
->qf_vp
) == NULLVP
) {
505 dqh
= DQHASH(dqvp
, id
);
508 listlockval
= dq_list_lock_val();
511 * Check the cache first.
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
)
518 dq_lock_internal(dq
);
519 if (dq_list_lock_changed(listlockval
)) {
520 dq_unlock_internal(dq
);
525 * dq_lock_internal may drop the quota_list_lock to msleep, so
526 * we need to re-evaluate the identity of this dq
528 if (dq
->dq_id
!= id
|| dq
->dq_qfile
== NULL
||
529 dq
->dq_qfile
->qf_vp
!= dqvp
) {
530 dq_unlock_internal(dq
);
534 * Cache hit with no references. Take
535 * the structure off the free list.
537 if (dq
->dq_cnt
++ == 0) {
538 if (dq
->dq_flags
& DQ_MOD
)
539 TAILQ_REMOVE(&dqdirtylist
, dq
, dq_freelist
);
541 TAILQ_REMOVE(&dqfreelist
, dq
, dq_freelist
);
542 } else if (dq
->dq_cnt
== 0) {
543 /* We've overflowed */
545 dq_unlock_internal(dq
);
550 dq_unlock_internal(dq
);
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
559 TAILQ_INSERT_HEAD(&dqfreelist
, fdq
, dq_freelist
);
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
577 * Not in cache, allocate a new one.
579 if (TAILQ_EMPTY(&dqfreelist
) &&
580 numdquot
< MAXQUOTAS
* desiredvnodes
)
581 desireddquot
+= DQUOTINC
;
585 * we captured this from the free list
586 * in the first pass through, so go
591 } else if (numdquot
< desireddquot
) {
594 * drop the quota list lock since MALLOC may block
598 ndq
= (struct dquot
*)_MALLOC(sizeof *dq
, M_DQUOT
, M_WAITOK
);
599 bzero((char *)ndq
, sizeof *dq
);
601 listlockval
= dq_list_lock();
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
610 * we allocated this in the first pass through
611 * and we're still under out target, so go
619 if (TAILQ_EMPTY(&dqfreelist
)) {
625 * we allocated this in the first pass through
626 * but we're now at the limit of our cache size
635 dq
= TAILQ_FIRST(&dqfreelist
);
637 dq_lock_internal(dq
);
639 if (dq_list_lock_changed(listlockval
) || dq
->dq_cnt
|| (dq
->dq_flags
& DQ_MOD
)) {
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
646 dq_unlock_internal(dq
);
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
655 TAILQ_REMOVE(&dqfreelist
, dq
, dq_freelist
);
657 if (dq
->dq_qfile
!= NULL
) {
658 LIST_REMOVE(dq
, dq_hash
);
662 dq_unlock_internal(dq
);
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
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
681 dq_lock_internal(dq
);
682 if (dq_list_lock_changed(listlockval
)) {
683 dq_unlock_internal(dq
);
688 * Initialize the contents of the dquot structure.
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
703 LIST_INSERT_HEAD(dqh
, dq
, dq_hash
);
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
715 error
= dqlookup(qfp
, id
, &dq
->dq_dqb
, &dq
->dq_index
);
718 * I/O error in reading quota file, release
719 * quota structure and reflect problem to caller.
726 LIST_REMOVE(dq
, dq_hash
);
728 dq_unlock_internal(dq
);
738 * Check for no limit to enforce.
739 * Initialize time values if necessary.
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) {
748 if (dq
->dq_btime
== 0)
749 dq
->dq_btime
= tv
.tv_sec
+ qfp
->qf_btime
;
750 if (dq
->dq_itime
== 0)
751 dq
->dq_itime
= tv
.tv_sec
+ qfp
->qf_itime
;
754 dq_unlock_internal(dq
);
763 * Lookup a dqblk structure for the specified identifier and
764 * quota file. If there is no entry for this identifier then
765 * one is inserted. The actual hash table index is returned.
768 dqlookup(qfp
, id
, dqb
, index
)
769 struct quotafile
*qfp
;
775 struct vfs_context context
;
780 char uio_buf
[ UIO_SIZEOF(1) ];
787 context
.vc_proc
= current_proc();
788 context
.vc_ucred
= qfp
->qf_cred
;
790 mask
= qfp
->qf_maxentries
- 1;
791 i
= dqhash1(id
, qfp
->qf_shift
, mask
);
792 skip
= dqhash2(id
, mask
);
794 for (last
= (i
+ (qfp
->qf_maxentries
-1) * skip
) & mask
;
796 i
= (i
+ skip
) & mask
) {
797 auio
= uio_createwithbuffer(1, dqoffset(i
), UIO_SYSSPACE
, UIO_READ
,
798 &uio_buf
[0], sizeof(uio_buf
));
799 uio_addiov(auio
, CAST_USER_ADDR_T(dqb
), sizeof (struct dqblk
));
800 error
= VNOP_READ(dqvp
, auio
, 0, &context
);
802 printf("dqlookup: error %d looking up id %d at index %d\n", error
, id
, i
);
804 } else if (uio_resid(auio
)) {
806 printf("dqlookup: error looking up id %d at index %d\n", id
, i
);
810 * An empty entry means there is no entry
811 * with that id. In this case a new dqb
812 * record will be inserted.
814 if (dqb
->dqb_id
== 0) {
815 bzero(dqb
, sizeof(struct dqblk
));
816 dqb
->dqb_id
= OSSwapHostToBigInt32(id
);
818 * Write back to reserve entry for this id
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)
830 /* An id match means an entry was found. */
831 if (OSSwapBigToHostInt32(dqb
->dqb_id
) == id
) {
832 dqb
->dqb_bhardlimit
= OSSwapBigToHostInt64(dqb
->dqb_bhardlimit
);
833 dqb
->dqb_bsoftlimit
= OSSwapBigToHostInt64(dqb
->dqb_bsoftlimit
);
834 dqb
->dqb_curbytes
= OSSwapBigToHostInt64(dqb
->dqb_curbytes
);
835 dqb
->dqb_ihardlimit
= OSSwapBigToHostInt32(dqb
->dqb_ihardlimit
);
836 dqb
->dqb_isoftlimit
= OSSwapBigToHostInt32(dqb
->dqb_isoftlimit
);
837 dqb
->dqb_curinodes
= OSSwapBigToHostInt32(dqb
->dqb_curinodes
);
838 dqb
->dqb_btime
= OSSwapBigToHostInt32(dqb
->dqb_btime
);
839 dqb
->dqb_itime
= OSSwapBigToHostInt32(dqb
->dqb_itime
);
840 dqb
->dqb_id
= OSSwapBigToHostInt32(dqb
->dqb_id
);
846 *index
= i
; /* remember index so we don't have to recompute it later */
853 * Release a reference to a dquot.
856 dqrele(struct dquot
*dq
)
863 if (dq
->dq_cnt
> 1) {
869 if (dq
->dq_flags
& DQ_MOD
)
870 (void) dqsync_locked(dq
);
874 TAILQ_INSERT_TAIL(&dqfreelist
, dq
, dq_freelist
);
875 dq_unlock_internal(dq
);
880 * Release a reference to a dquot but don't do any I/O.
883 dqreclaim(register struct dquot
*dq
)
890 dq_lock_internal(dq
);
892 if (--dq
->dq_cnt
> 0) {
893 dq_unlock_internal(dq
);
897 if (dq
->dq_flags
& DQ_MOD
)
898 TAILQ_INSERT_TAIL(&dqdirtylist
, dq
, dq_freelist
);
900 TAILQ_INSERT_TAIL(&dqfreelist
, dq
, dq_freelist
);
902 dq_unlock_internal(dq
);
907 * Update a quota file's orphaned disk quotas.
911 struct quotafile
*qfp
;
918 listlockval
= dq_list_lock_val();
920 TAILQ_FOREACH(dq
, &dqdirtylist
, dq_freelist
) {
921 if (dq
->dq_qfile
!= qfp
)
924 dq_lock_internal(dq
);
925 if (dq_list_lock_changed(listlockval
)) {
926 dq_unlock_internal(dq
);
930 if (dq
->dq_qfile
!= qfp
) {
932 * the identity of this dq changed while
933 * the quota_list_lock was dropped
934 * dq_lock_internal can drop it to msleep
936 dq_unlock_internal(dq
);
939 if ((dq
->dq_flags
& DQ_MOD
) == 0) {
941 * someone cleaned and removed this from
942 * the dq from the dirty list while the
943 * quota_list_lock was dropped
945 dq_unlock_internal(dq
);
949 panic("dqsync_orphans: dquot in use");
951 TAILQ_REMOVE(&dqdirtylist
, dq
, dq_freelist
);
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
960 (void) dqsync_locked(dq
);
964 TAILQ_INSERT_TAIL(&dqfreelist
, dq
, dq_freelist
);
966 dq_unlock_internal(dq
);
973 dqsync(struct dquot
*dq
)
980 if ( (dq
->dq_flags
& DQ_MOD
) )
981 error
= dqsync_locked(dq
);
990 * Update the disk quota in the quota file.
993 dqsync_locked(struct dquot
*dq
)
995 struct proc
*p
= current_proc(); /* XXX */
996 struct vfs_context context
;
998 struct dqblk dqb
, *dqblkp
;
1001 char uio_buf
[ UIO_SIZEOF(1) ];
1003 if (dq
->dq_id
== 0) {
1004 dq
->dq_flags
&= ~DQ_MOD
;
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");
1012 auio
= uio_createwithbuffer(1, dqoffset(dq
->dq_index
), UIO_SYSSPACE
,
1013 UIO_WRITE
, &uio_buf
[0], sizeof(uio_buf
));
1014 uio_addiov(auio
, CAST_USER_ADDR_T(&dqb
), sizeof (struct dqblk
));
1016 context
.vc_proc
= p
;
1017 context
.vc_ucred
= dq
->dq_qfile
->qf_cred
;
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;
1034 error
= VNOP_WRITE(dqvp
, auio
, 0, &context
);
1035 if (uio_resid(auio
) && error
== 0)
1037 dq
->dq_flags
&= ~DQ_MOD
;
1043 * Flush all entries from the cache for a particular vnode.
1047 register struct vnode
*vp
;
1049 register struct dquot
*dq
, *nextdq
;
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).
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
)
1065 panic("dqflush: stray dquot");
1066 LIST_REMOVE(dq
, dq_hash
);
1067 dq
->dq_qfile
= NULL
;
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.
1078 __private_extern__
void
1079 munge_dqblk(struct dqblk
*dqblkp
, struct user_dqblk
*user_dqblkp
, boolean_t 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
;
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 */