]>
git.saurik.com Git - apple/xnu.git/blob - bsd/vfs/vfs_quota.c
36f4fb89aa2705a4fa61d1a6bf85d44de5b10228
2 * Copyright (c) 2002 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>
67 #include <sys/vnode.h>
68 #include <sys/mount.h>
69 #include <sys/quota.h>
72 static u_int32_t quotamagic
[MAXQUOTAS
] = INITQMAGICS
;
76 * Code pertaining to management of the in-core dquot data structures.
78 #define DQHASH(dqvp, id) \
79 (&dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash])
80 LIST_HEAD(dqhash
, dquot
) *dqhashtbl
;
86 #define DQUOTINC 5 /* minimum free dquots desired */
87 TAILQ_HEAD(dqfreelist
, dquot
) dqfreelist
;
88 long numdquot
, desireddquot
= DQUOTINC
;
91 static int dqlookup(struct quotafile
*, u_long
, struct dqblk
*, u_int32_t
*);
95 * Initialize the quota system.
101 dqhashtbl
= hashinit(desiredvnodes
, M_DQUOT
, &dqhash
);
102 TAILQ_INIT(&dqfreelist
);
107 * Initialize a quota file
110 dqfileopen(qfp
, type
)
111 struct quotafile
*qfp
;
114 struct dqfilehdr header
;
120 /* Obtain the file size */
121 error
= VOP_GETATTR(qfp
->qf_vp
, &vattr
, qfp
->qf_cred
, current_proc());
125 /* Read the file header */
126 auio
.uio_iov
= &aiov
;
128 aiov
.iov_base
= (caddr_t
)&header
;
129 aiov
.iov_len
= sizeof (header
);
130 auio
.uio_resid
= sizeof (header
);
131 auio
.uio_offset
= (off_t
)(0);
132 auio
.uio_segflg
= UIO_SYSSPACE
;
133 auio
.uio_rw
= UIO_READ
;
134 auio
.uio_procp
= (struct proc
*)0;
135 error
= VOP_READ(qfp
->qf_vp
, &auio
, 0, qfp
->qf_cred
);
138 else if (auio
.uio_resid
)
141 /* Sanity check the quota file header. */
142 if ((header
.dqh_magic
!= quotamagic
[type
]) ||
143 (header
.dqh_version
> QF_VERSION
) ||
144 (!powerof2(header
.dqh_maxentries
)) ||
145 (header
.dqh_maxentries
> (vattr
.va_size
/ sizeof(struct dqblk
))))
148 /* Set up the time limits for this quota. */
149 if (header
.dqh_btime
> 0)
150 qfp
->qf_btime
= header
.dqh_btime
;
152 qfp
->qf_btime
= MAX_DQ_TIME
;
153 if (header
.dqh_itime
> 0)
154 qfp
->qf_itime
= header
.dqh_itime
;
156 qfp
->qf_itime
= MAX_IQ_TIME
;
158 /* Calculate the hash table constants. */
159 qfp
->qf_maxentries
= header
.dqh_maxentries
;
160 qfp
->qf_entrycnt
= header
.dqh_entrycnt
;
161 qfp
->qf_shift
= dqhashshift(header
.dqh_maxentries
);
167 * Close down a quota file
170 dqfileclose(qfp
, type
)
171 struct quotafile
*qfp
;
174 struct dqfilehdr header
;
178 auio
.uio_iov
= &aiov
;
180 aiov
.iov_base
= (caddr_t
)&header
;
181 aiov
.iov_len
= sizeof (header
);
182 auio
.uio_resid
= sizeof (header
);
183 auio
.uio_offset
= (off_t
)(0);
184 auio
.uio_segflg
= UIO_SYSSPACE
;
185 auio
.uio_rw
= UIO_READ
;
186 auio
.uio_procp
= (struct proc
*)0;
187 if (VOP_READ(qfp
->qf_vp
, &auio
, 0, qfp
->qf_cred
) == 0) {
188 header
.dqh_entrycnt
= qfp
->qf_entrycnt
;
190 auio
.uio_iov
= &aiov
;
192 aiov
.iov_base
= (caddr_t
)&header
;
193 aiov
.iov_len
= sizeof (header
);
194 auio
.uio_resid
= sizeof (header
);
195 auio
.uio_offset
= (off_t
)(0);
196 auio
.uio_segflg
= UIO_SYSSPACE
;
197 auio
.uio_rw
= UIO_WRITE
;
198 auio
.uio_procp
= (struct proc
*)0;
199 (void) VOP_WRITE(qfp
->qf_vp
, &auio
, 0, qfp
->qf_cred
);
205 * Obtain a dquot structure for the specified identifier and quota file
206 * reading the information from the file if necessary.
209 dqget(vp
, id
, qfp
, type
, dqp
)
212 struct quotafile
*qfp
;
216 struct proc
*p
= current_proc(); /* XXX */
223 if (id
== 0 || dqvp
== NULLVP
|| (qfp
->qf_qflags
& QTF_CLOSING
)) {
228 * Check the cache first.
230 dqh
= DQHASH(dqvp
, id
);
231 for (dq
= dqh
->lh_first
; dq
; dq
= dq
->dq_hash
.le_next
) {
232 if (dq
->dq_id
!= id
||
233 dq
->dq_qfile
->qf_vp
!= dqvp
)
236 * Cache hit with no references. Take
237 * the structure off the free list.
240 TAILQ_REMOVE(&dqfreelist
, dq
, dq_freelist
);
246 * Not in cache, allocate a new one.
248 if (dqfreelist
.tqh_first
== NODQUOT
&&
249 numdquot
< MAXQUOTAS
* desiredvnodes
)
250 desireddquot
+= DQUOTINC
;
251 if (numdquot
< desireddquot
) {
252 dq
= (struct dquot
*)_MALLOC(sizeof *dq
, M_DQUOT
, M_WAITOK
);
253 bzero((char *)dq
, sizeof *dq
);
256 if ((dq
= dqfreelist
.tqh_first
) == NULL
) {
261 if (dq
->dq_cnt
|| (dq
->dq_flags
& DQ_MOD
))
262 panic("free dquot isn't");
263 TAILQ_REMOVE(&dqfreelist
, dq
, dq_freelist
);
264 LIST_REMOVE(dq
, dq_hash
);
267 * Initialize the contents of the dquot structure.
270 vn_lock(dqvp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
271 LIST_INSERT_HEAD(dqh
, dq
, dq_hash
);
273 dq
->dq_flags
= DQ_LOCK
;
277 error
= dqlookup(qfp
, id
, &dq
->dq_dqb
, &dq
->dq_index
);
280 VOP_UNLOCK(dqvp
, 0, p
);
281 if (dq
->dq_flags
& DQ_WANT
)
285 * I/O error in reading quota file, release
286 * quota structure and reflect problem to caller.
289 LIST_REMOVE(dq
, dq_hash
);
295 * Check for no limit to enforce.
296 * Initialize time values if necessary.
298 if (dq
->dq_isoftlimit
== 0 && dq
->dq_bsoftlimit
== 0 &&
299 dq
->dq_ihardlimit
== 0 && dq
->dq_bhardlimit
== 0)
300 dq
->dq_flags
|= DQ_FAKE
;
301 if (dq
->dq_id
!= 0) {
302 if (dq
->dq_btime
== 0)
303 dq
->dq_btime
= time
.tv_sec
+ qfp
->qf_btime
;
304 if (dq
->dq_itime
== 0)
305 dq
->dq_itime
= time
.tv_sec
+ qfp
->qf_itime
;
312 * Lookup a dqblk structure for the specified identifier and
313 * quota file. If there is no enetry for this identifier then
314 * one is inserted. The actual hash table index is returned.
317 dqlookup(qfp
, id
, dqb
, index
)
318 struct quotafile
*qfp
;
336 auio
.uio_iov
= &aiov
;
338 auio
.uio_segflg
= UIO_SYSSPACE
;
339 auio
.uio_procp
= (struct proc
*)0;
341 mask
= qfp
->qf_maxentries
- 1;
342 i
= dqhash1(id
, qfp
->qf_shift
, mask
);
343 skip
= dqhash2(id
, mask
);
345 for (last
= (i
+ (qfp
->qf_maxentries
-1) * skip
) & mask
;
347 i
= (i
+ skip
) & mask
) {
349 aiov
.iov_base
= (caddr_t
)dqb
;
350 aiov
.iov_len
= sizeof (struct dqblk
);
351 auio
.uio_resid
= sizeof (struct dqblk
);
352 auio
.uio_offset
= (off_t
)dqoffset(i
);
353 auio
.uio_rw
= UIO_READ
;
354 error
= VOP_READ(dqvp
, &auio
, 0, cred
);
356 printf("dqlookup: error %d looking up id %d at index %d\n", error
, id
, i
);
358 } else if (auio
.uio_resid
) {
360 printf("dqlookup: error looking up id %d at index %d\n", id
, i
);
364 * An empty entry means there is no entry
365 * with that id. In this case a new dqb
366 * record will be inserted.
368 if (dqb
->dqb_id
== 0) {
369 bzero(dqb
, sizeof(struct dqblk
));
372 * Write back to reserve entry for this id
374 aiov
.iov_base
= (caddr_t
)dqb
;
375 aiov
.iov_len
= sizeof (struct dqblk
);
376 auio
.uio_resid
= sizeof (struct dqblk
);
377 auio
.uio_offset
= (off_t
)dqoffset(i
);
378 auio
.uio_rw
= UIO_WRITE
;
379 error
= VOP_WRITE(dqvp
, &auio
, 0, cred
);
380 if (auio
.uio_resid
&& error
== 0)
386 /* An id match means an entry was found. */
387 if (dqb
->dqb_id
== id
)
391 *index
= i
; /* remember index so we don't have to recompute it later */
396 * Obtain a reference to a dquot.
407 * Release a reference to a dquot.
412 register struct dquot
*dq
;
417 if (dq
->dq_cnt
> 1) {
421 if (dq
->dq_flags
& DQ_MOD
)
422 (void) dqsync(vp
, dq
);
423 if (--dq
->dq_cnt
> 0)
425 TAILQ_INSERT_TAIL(&dqfreelist
, dq
, dq_freelist
);
429 * Update the disk quota in the quota file.
436 struct proc
*p
= current_proc(); /* XXX */
443 panic("dqsync: dquot");
444 if ((dq
->dq_flags
& DQ_MOD
) == 0)
448 if ((dqvp
= dq
->dq_qfile
->qf_vp
) == NULLVP
)
449 panic("dqsync: file");
451 vn_lock(dqvp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
452 while (dq
->dq_flags
& DQ_LOCK
) {
453 dq
->dq_flags
|= DQ_WANT
;
454 sleep((caddr_t
)dq
, PINOD
+2);
455 if ((dq
->dq_flags
& DQ_MOD
) == 0) {
457 VOP_UNLOCK(dqvp
, 0, p
);
461 dq
->dq_flags
|= DQ_LOCK
;
462 auio
.uio_iov
= &aiov
;
464 aiov
.iov_base
= (caddr_t
)&dq
->dq_dqb
;
465 aiov
.iov_len
= sizeof (struct dqblk
);
466 auio
.uio_resid
= sizeof (struct dqblk
);
467 auio
.uio_offset
= (off_t
)dqoffset(dq
->dq_index
);
468 auio
.uio_segflg
= UIO_SYSSPACE
;
469 auio
.uio_rw
= UIO_WRITE
;
470 auio
.uio_procp
= (struct proc
*)0;
471 error
= VOP_WRITE(dqvp
, &auio
, 0, dq
->dq_qfile
->qf_cred
);
472 if (auio
.uio_resid
&& error
== 0)
474 if (dq
->dq_flags
& DQ_WANT
)
476 dq
->dq_flags
&= ~(DQ_MOD
|DQ_LOCK
|DQ_WANT
);
478 VOP_UNLOCK(dqvp
, 0, p
);
483 * Flush all entries from the cache for a particular vnode.
487 register struct vnode
*vp
;
489 register struct dquot
*dq
, *nextdq
;
493 * Move all dquot's that used to refer to this quota
494 * file off their hash chains (they will eventually
495 * fall off the head of the free list and be re-used).
497 for (dqh
= &dqhashtbl
[dqhash
]; dqh
>= dqhashtbl
; dqh
--) {
498 for (dq
= dqh
->lh_first
; dq
; dq
= nextdq
) {
499 nextdq
= dq
->dq_hash
.le_next
;
500 if (dq
->dq_qfile
->qf_vp
!= vp
)
503 panic("dqflush: stray dquot");
504 LIST_REMOVE(dq
, dq_hash
);
505 dq
->dq_qfile
= (struct quotafile
*)0;