]>
git.saurik.com Git - apple/xnu.git/blob - bsd/vfs/vfs_quota.c
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 * Dquot dirty orphans list.
93 TAILQ_HEAD(dqdirtylist
, dquot
) dqdirtylist
;
96 static int dqlookup(struct quotafile
*, u_long
, struct dqblk
*, u_int32_t
*);
100 * Initialize the quota system.
106 dqhashtbl
= hashinit(desiredvnodes
, M_DQUOT
, &dqhash
);
107 TAILQ_INIT(&dqfreelist
);
108 TAILQ_INIT(&dqdirtylist
);
113 * Initialize a quota file
116 dqfileopen(qfp
, type
)
117 struct quotafile
*qfp
;
120 struct dqfilehdr header
;
126 /* Obtain the file size */
127 error
= VOP_GETATTR(qfp
->qf_vp
, &vattr
, qfp
->qf_cred
, current_proc());
131 /* Read the file header */
132 auio
.uio_iov
= &aiov
;
134 aiov
.iov_base
= (caddr_t
)&header
;
135 aiov
.iov_len
= sizeof (header
);
136 auio
.uio_resid
= sizeof (header
);
137 auio
.uio_offset
= (off_t
)(0);
138 auio
.uio_segflg
= UIO_SYSSPACE
;
139 auio
.uio_rw
= UIO_READ
;
140 auio
.uio_procp
= (struct proc
*)0;
141 error
= VOP_READ(qfp
->qf_vp
, &auio
, 0, qfp
->qf_cred
);
144 else if (auio
.uio_resid
)
147 /* Sanity check the quota file header. */
148 if ((header
.dqh_magic
!= quotamagic
[type
]) ||
149 (header
.dqh_version
> QF_VERSION
) ||
150 (!powerof2(header
.dqh_maxentries
)) ||
151 (header
.dqh_maxentries
> (vattr
.va_size
/ sizeof(struct dqblk
))))
154 /* Set up the time limits for this quota. */
155 if (header
.dqh_btime
> 0)
156 qfp
->qf_btime
= header
.dqh_btime
;
158 qfp
->qf_btime
= MAX_DQ_TIME
;
159 if (header
.dqh_itime
> 0)
160 qfp
->qf_itime
= header
.dqh_itime
;
162 qfp
->qf_itime
= MAX_IQ_TIME
;
164 /* Calculate the hash table constants. */
165 qfp
->qf_maxentries
= header
.dqh_maxentries
;
166 qfp
->qf_entrycnt
= header
.dqh_entrycnt
;
167 qfp
->qf_shift
= dqhashshift(header
.dqh_maxentries
);
173 * Close down a quota file
176 dqfileclose(qfp
, type
)
177 struct quotafile
*qfp
;
180 struct dqfilehdr header
;
184 auio
.uio_iov
= &aiov
;
186 aiov
.iov_base
= (caddr_t
)&header
;
187 aiov
.iov_len
= sizeof (header
);
188 auio
.uio_resid
= sizeof (header
);
189 auio
.uio_offset
= (off_t
)(0);
190 auio
.uio_segflg
= UIO_SYSSPACE
;
191 auio
.uio_rw
= UIO_READ
;
192 auio
.uio_procp
= (struct proc
*)0;
193 if (VOP_READ(qfp
->qf_vp
, &auio
, 0, qfp
->qf_cred
) == 0) {
194 header
.dqh_entrycnt
= qfp
->qf_entrycnt
;
196 auio
.uio_iov
= &aiov
;
198 aiov
.iov_base
= (caddr_t
)&header
;
199 aiov
.iov_len
= sizeof (header
);
200 auio
.uio_resid
= sizeof (header
);
201 auio
.uio_offset
= (off_t
)(0);
202 auio
.uio_segflg
= UIO_SYSSPACE
;
203 auio
.uio_rw
= UIO_WRITE
;
204 auio
.uio_procp
= (struct proc
*)0;
205 (void) VOP_WRITE(qfp
->qf_vp
, &auio
, 0, qfp
->qf_cred
);
211 * Obtain a dquot structure for the specified identifier and quota file
212 * reading the information from the file if necessary.
215 dqget(vp
, id
, qfp
, type
, dqp
)
218 struct quotafile
*qfp
;
222 struct proc
*p
= current_proc(); /* XXX */
229 if (id
== 0 || dqvp
== NULLVP
|| (qfp
->qf_qflags
& QTF_CLOSING
)) {
234 * Check the cache first.
236 dqh
= DQHASH(dqvp
, id
);
237 for (dq
= dqh
->lh_first
; dq
; dq
= dq
->dq_hash
.le_next
) {
238 if (dq
->dq_id
!= id
||
239 dq
->dq_qfile
->qf_vp
!= dqvp
)
242 * Cache hit with no references. Take
243 * the structure off the free list.
245 if (dq
->dq_cnt
== 0) {
246 if (dq
->dq_flags
& DQ_MOD
)
247 TAILQ_REMOVE(&dqdirtylist
, dq
, dq_freelist
);
249 TAILQ_REMOVE(&dqfreelist
, dq
, dq_freelist
);
256 * Not in cache, allocate a new one.
258 if (dqfreelist
.tqh_first
== NODQUOT
&&
259 numdquot
< MAXQUOTAS
* desiredvnodes
)
260 desireddquot
+= DQUOTINC
;
261 if (numdquot
< desireddquot
) {
262 dq
= (struct dquot
*)_MALLOC(sizeof *dq
, M_DQUOT
, M_WAITOK
);
263 bzero((char *)dq
, sizeof *dq
);
266 if ((dq
= dqfreelist
.tqh_first
) == NULL
) {
271 if (dq
->dq_cnt
|| (dq
->dq_flags
& DQ_MOD
))
272 panic("free dquot isn't");
273 TAILQ_REMOVE(&dqfreelist
, dq
, dq_freelist
);
274 LIST_REMOVE(dq
, dq_hash
);
277 * Initialize the contents of the dquot structure.
280 vn_lock(dqvp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
281 LIST_INSERT_HEAD(dqh
, dq
, dq_hash
);
283 dq
->dq_flags
= DQ_LOCK
;
287 error
= dqlookup(qfp
, id
, &dq
->dq_dqb
, &dq
->dq_index
);
290 VOP_UNLOCK(dqvp
, 0, p
);
291 if (dq
->dq_flags
& DQ_WANT
)
295 * I/O error in reading quota file, release
296 * quota structure and reflect problem to caller.
299 LIST_REMOVE(dq
, dq_hash
);
305 * Check for no limit to enforce.
306 * Initialize time values if necessary.
308 if (dq
->dq_isoftlimit
== 0 && dq
->dq_bsoftlimit
== 0 &&
309 dq
->dq_ihardlimit
== 0 && dq
->dq_bhardlimit
== 0)
310 dq
->dq_flags
|= DQ_FAKE
;
311 if (dq
->dq_id
!= 0) {
312 if (dq
->dq_btime
== 0)
313 dq
->dq_btime
= time
.tv_sec
+ qfp
->qf_btime
;
314 if (dq
->dq_itime
== 0)
315 dq
->dq_itime
= time
.tv_sec
+ qfp
->qf_itime
;
322 * Lookup a dqblk structure for the specified identifier and
323 * quota file. If there is no enetry for this identifier then
324 * one is inserted. The actual hash table index is returned.
327 dqlookup(qfp
, id
, dqb
, index
)
328 struct quotafile
*qfp
;
346 auio
.uio_iov
= &aiov
;
348 auio
.uio_segflg
= UIO_SYSSPACE
;
349 auio
.uio_procp
= (struct proc
*)0;
351 mask
= qfp
->qf_maxentries
- 1;
352 i
= dqhash1(id
, qfp
->qf_shift
, mask
);
353 skip
= dqhash2(id
, mask
);
355 for (last
= (i
+ (qfp
->qf_maxentries
-1) * skip
) & mask
;
357 i
= (i
+ skip
) & mask
) {
359 aiov
.iov_base
= (caddr_t
)dqb
;
360 aiov
.iov_len
= sizeof (struct dqblk
);
361 auio
.uio_resid
= sizeof (struct dqblk
);
362 auio
.uio_offset
= (off_t
)dqoffset(i
);
363 auio
.uio_rw
= UIO_READ
;
364 error
= VOP_READ(dqvp
, &auio
, 0, cred
);
366 printf("dqlookup: error %d looking up id %d at index %d\n", error
, id
, i
);
368 } else if (auio
.uio_resid
) {
370 printf("dqlookup: error looking up id %d at index %d\n", id
, i
);
374 * An empty entry means there is no entry
375 * with that id. In this case a new dqb
376 * record will be inserted.
378 if (dqb
->dqb_id
== 0) {
379 bzero(dqb
, sizeof(struct dqblk
));
382 * Write back to reserve entry for this id
384 aiov
.iov_base
= (caddr_t
)dqb
;
385 aiov
.iov_len
= sizeof (struct dqblk
);
386 auio
.uio_resid
= sizeof (struct dqblk
);
387 auio
.uio_offset
= (off_t
)dqoffset(i
);
388 auio
.uio_rw
= UIO_WRITE
;
389 error
= VOP_WRITE(dqvp
, &auio
, 0, cred
);
390 if (auio
.uio_resid
&& error
== 0)
396 /* An id match means an entry was found. */
397 if (dqb
->dqb_id
== id
)
401 *index
= i
; /* remember index so we don't have to recompute it later */
406 * Obtain a reference to a dquot.
417 * Release a reference to a dquot.
422 register struct dquot
*dq
;
427 if (dq
->dq_cnt
> 1) {
431 if (dq
->dq_flags
& DQ_MOD
)
432 (void) dqsync(vp
, dq
);
433 if (--dq
->dq_cnt
> 0)
435 TAILQ_INSERT_TAIL(&dqfreelist
, dq
, dq_freelist
);
439 * Release a reference to a dquot but don't do any I/O.
444 register struct dquot
*dq
;
449 if (--dq
->dq_cnt
> 0)
452 if (dq
->dq_flags
& DQ_MOD
)
453 TAILQ_INSERT_TAIL(&dqdirtylist
, dq
, dq_freelist
);
455 TAILQ_INSERT_TAIL(&dqfreelist
, dq
, dq_freelist
);
459 * Update a quota file's orphaned disk quotas.
463 struct quotafile
*qfp
;
468 TAILQ_FOREACH(dq
, &dqdirtylist
, dq_freelist
) {
469 if ((dq
->dq_flags
& DQ_MOD
) == 0)
470 panic("dqsync_orphans: dirty dquot isn't");
472 panic("dqsync_orphans: dquot in use");
474 if (dq
->dq_qfile
== qfp
) {
475 TAILQ_REMOVE(&dqdirtylist
, dq
, dq_freelist
);
478 (void) dqsync(NULLVP
, dq
);
481 if ((dq
->dq_cnt
== 0) && (dq
->dq_flags
& DQ_MOD
) == 0)
482 TAILQ_INSERT_TAIL(&dqfreelist
, dq
, dq_freelist
);
490 * Update the disk quota in the quota file.
497 struct proc
*p
= current_proc(); /* XXX */
504 panic("dqsync: dquot");
505 if ((dq
->dq_flags
& DQ_MOD
) == 0)
509 if ((dqvp
= dq
->dq_qfile
->qf_vp
) == NULLVP
)
510 panic("dqsync: file");
512 vn_lock(dqvp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
513 while (dq
->dq_flags
& DQ_LOCK
) {
514 dq
->dq_flags
|= DQ_WANT
;
515 sleep((caddr_t
)dq
, PINOD
+2);
516 if ((dq
->dq_flags
& DQ_MOD
) == 0) {
518 VOP_UNLOCK(dqvp
, 0, p
);
522 dq
->dq_flags
|= DQ_LOCK
;
523 auio
.uio_iov
= &aiov
;
525 aiov
.iov_base
= (caddr_t
)&dq
->dq_dqb
;
526 aiov
.iov_len
= sizeof (struct dqblk
);
527 auio
.uio_resid
= sizeof (struct dqblk
);
528 auio
.uio_offset
= (off_t
)dqoffset(dq
->dq_index
);
529 auio
.uio_segflg
= UIO_SYSSPACE
;
530 auio
.uio_rw
= UIO_WRITE
;
531 auio
.uio_procp
= (struct proc
*)0;
532 error
= VOP_WRITE(dqvp
, &auio
, 0, dq
->dq_qfile
->qf_cred
);
533 if (auio
.uio_resid
&& error
== 0)
535 if (dq
->dq_flags
& DQ_WANT
)
537 dq
->dq_flags
&= ~(DQ_MOD
|DQ_LOCK
|DQ_WANT
);
539 VOP_UNLOCK(dqvp
, 0, p
);
544 * Flush all entries from the cache for a particular vnode.
548 register struct vnode
*vp
;
550 register struct dquot
*dq
, *nextdq
;
554 * Move all dquot's that used to refer to this quota
555 * file off their hash chains (they will eventually
556 * fall off the head of the free list and be re-used).
558 for (dqh
= &dqhashtbl
[dqhash
]; dqh
>= dqhashtbl
; dqh
--) {
559 for (dq
= dqh
->lh_first
; dq
; dq
= nextdq
) {
560 nextdq
= dq
->dq_hash
.le_next
;
561 if (dq
->dq_qfile
->qf_vp
!= vp
)
564 panic("dqflush: stray dquot");
565 LIST_REMOVE(dq
, dq_hash
);
566 dq
->dq_qfile
= (struct quotafile
*)0;