]>
git.saurik.com Git - apple/xnu.git/blob - bsd/vfs/vfs_quota.c
c60bb78ed3aa53f11302014e4bf5b78e5cc61c53
2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 * Copyright (c) 1982, 1986, 1990, 1993, 1995
27 * The Regents of the University of California. All rights reserved.
29 * This code is derived from software contributed to Berkeley by
30 * Robert Elz at The University of Melbourne.
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
35 * 1. Redistributions of source code must retain the above copyright
36 * notice, this list of conditions and the following disclaimer.
37 * 2. Redistributions in binary form must reproduce the above copyright
38 * notice, this list of conditions and the following disclaimer in the
39 * documentation and/or other materials provided with the distribution.
40 * 3. All advertising materials mentioning features or use of this software
41 * must display the following acknowledgement:
42 * This product includes software developed by the University of
43 * California, Berkeley and its contributors.
44 * 4. Neither the name of the University nor the names of its contributors
45 * may be used to endorse or promote products derived from this software
46 * without specific prior written permission.
48 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61 * derived from @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
64 #include <sys/param.h>
65 #include <sys/kernel.h>
66 #include <sys/systm.h>
67 #include <sys/malloc.h>
70 #include <sys/vnode.h>
71 #include <sys/mount.h>
72 #include <sys/quota.h>
75 static u_int32_t quotamagic
[MAXQUOTAS
] = INITQMAGICS
;
79 * Code pertaining to management of the in-core dquot data structures.
81 #define DQHASH(dqvp, id) \
82 (&dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash])
83 LIST_HEAD(dqhash
, dquot
) *dqhashtbl
;
89 #define DQUOTINC 5 /* minimum free dquots desired */
90 TAILQ_HEAD(dqfreelist
, dquot
) dqfreelist
;
91 long numdquot
, desireddquot
= DQUOTINC
;
94 * Dquot dirty orphans list.
96 TAILQ_HEAD(dqdirtylist
, dquot
) dqdirtylist
;
99 static int dqlookup(struct quotafile
*, u_long
, struct dqblk
*, u_int32_t
*);
103 * Initialize the quota system.
109 dqhashtbl
= hashinit(desiredvnodes
, M_DQUOT
, &dqhash
);
110 TAILQ_INIT(&dqfreelist
);
111 TAILQ_INIT(&dqdirtylist
);
116 * Initialize a quota file
119 dqfileopen(qfp
, type
)
120 struct quotafile
*qfp
;
123 struct dqfilehdr header
;
129 /* Obtain the file size */
130 error
= VOP_GETATTR(qfp
->qf_vp
, &vattr
, qfp
->qf_cred
, current_proc());
134 /* Read the file header */
135 auio
.uio_iov
= &aiov
;
137 aiov
.iov_base
= (caddr_t
)&header
;
138 aiov
.iov_len
= sizeof (header
);
139 auio
.uio_resid
= sizeof (header
);
140 auio
.uio_offset
= (off_t
)(0);
141 auio
.uio_segflg
= UIO_SYSSPACE
;
142 auio
.uio_rw
= UIO_READ
;
143 auio
.uio_procp
= (struct proc
*)0;
144 error
= VOP_READ(qfp
->qf_vp
, &auio
, 0, qfp
->qf_cred
);
147 else if (auio
.uio_resid
)
150 /* Sanity check the quota file header. */
151 if ((header
.dqh_magic
!= quotamagic
[type
]) ||
152 (header
.dqh_version
> QF_VERSION
) ||
153 (!powerof2(header
.dqh_maxentries
)) ||
154 (header
.dqh_maxentries
> (vattr
.va_size
/ sizeof(struct dqblk
))))
157 /* Set up the time limits for this quota. */
158 if (header
.dqh_btime
> 0)
159 qfp
->qf_btime
= header
.dqh_btime
;
161 qfp
->qf_btime
= MAX_DQ_TIME
;
162 if (header
.dqh_itime
> 0)
163 qfp
->qf_itime
= header
.dqh_itime
;
165 qfp
->qf_itime
= MAX_IQ_TIME
;
167 /* Calculate the hash table constants. */
168 qfp
->qf_maxentries
= header
.dqh_maxentries
;
169 qfp
->qf_entrycnt
= header
.dqh_entrycnt
;
170 qfp
->qf_shift
= dqhashshift(header
.dqh_maxentries
);
176 * Close down a quota file
179 dqfileclose(qfp
, type
)
180 struct quotafile
*qfp
;
183 struct dqfilehdr header
;
187 auio
.uio_iov
= &aiov
;
189 aiov
.iov_base
= (caddr_t
)&header
;
190 aiov
.iov_len
= sizeof (header
);
191 auio
.uio_resid
= sizeof (header
);
192 auio
.uio_offset
= (off_t
)(0);
193 auio
.uio_segflg
= UIO_SYSSPACE
;
194 auio
.uio_rw
= UIO_READ
;
195 auio
.uio_procp
= (struct proc
*)0;
196 if (VOP_READ(qfp
->qf_vp
, &auio
, 0, qfp
->qf_cred
) == 0) {
197 header
.dqh_entrycnt
= qfp
->qf_entrycnt
;
199 auio
.uio_iov
= &aiov
;
201 aiov
.iov_base
= (caddr_t
)&header
;
202 aiov
.iov_len
= sizeof (header
);
203 auio
.uio_resid
= sizeof (header
);
204 auio
.uio_offset
= (off_t
)(0);
205 auio
.uio_segflg
= UIO_SYSSPACE
;
206 auio
.uio_rw
= UIO_WRITE
;
207 auio
.uio_procp
= (struct proc
*)0;
208 (void) VOP_WRITE(qfp
->qf_vp
, &auio
, 0, qfp
->qf_cred
);
214 * Obtain a dquot structure for the specified identifier and quota file
215 * reading the information from the file if necessary.
218 dqget(vp
, id
, qfp
, type
, dqp
)
221 struct quotafile
*qfp
;
225 struct proc
*p
= current_proc(); /* XXX */
232 if (id
== 0 || dqvp
== NULLVP
|| (qfp
->qf_qflags
& QTF_CLOSING
)) {
237 * Check the cache first.
239 dqh
= DQHASH(dqvp
, id
);
240 for (dq
= dqh
->lh_first
; dq
; dq
= dq
->dq_hash
.le_next
) {
241 if (dq
->dq_id
!= id
||
242 dq
->dq_qfile
->qf_vp
!= dqvp
)
245 * Cache hit with no references. Take
246 * the structure off the free list.
248 if (dq
->dq_cnt
== 0) {
249 if (dq
->dq_flags
& DQ_MOD
)
250 TAILQ_REMOVE(&dqdirtylist
, dq
, dq_freelist
);
252 TAILQ_REMOVE(&dqfreelist
, dq
, dq_freelist
);
259 * Not in cache, allocate a new one.
261 if (dqfreelist
.tqh_first
== NODQUOT
&&
262 numdquot
< MAXQUOTAS
* desiredvnodes
)
263 desireddquot
+= DQUOTINC
;
264 if (numdquot
< desireddquot
) {
265 dq
= (struct dquot
*)_MALLOC(sizeof *dq
, M_DQUOT
, M_WAITOK
);
266 bzero((char *)dq
, sizeof *dq
);
269 if ((dq
= dqfreelist
.tqh_first
) == NULL
) {
274 if (dq
->dq_cnt
|| (dq
->dq_flags
& DQ_MOD
))
275 panic("free dquot isn't");
276 TAILQ_REMOVE(&dqfreelist
, dq
, dq_freelist
);
277 LIST_REMOVE(dq
, dq_hash
);
280 * Initialize the contents of the dquot structure.
283 vn_lock(dqvp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
284 LIST_INSERT_HEAD(dqh
, dq
, dq_hash
);
286 dq
->dq_flags
= DQ_LOCK
;
290 error
= dqlookup(qfp
, id
, &dq
->dq_dqb
, &dq
->dq_index
);
293 VOP_UNLOCK(dqvp
, 0, p
);
294 if (dq
->dq_flags
& DQ_WANT
)
298 * I/O error in reading quota file, release
299 * quota structure and reflect problem to caller.
302 LIST_REMOVE(dq
, dq_hash
);
308 * Check for no limit to enforce.
309 * Initialize time values if necessary.
311 if (dq
->dq_isoftlimit
== 0 && dq
->dq_bsoftlimit
== 0 &&
312 dq
->dq_ihardlimit
== 0 && dq
->dq_bhardlimit
== 0)
313 dq
->dq_flags
|= DQ_FAKE
;
314 if (dq
->dq_id
!= 0) {
315 if (dq
->dq_btime
== 0)
316 dq
->dq_btime
= time
.tv_sec
+ qfp
->qf_btime
;
317 if (dq
->dq_itime
== 0)
318 dq
->dq_itime
= time
.tv_sec
+ qfp
->qf_itime
;
325 * Lookup a dqblk structure for the specified identifier and
326 * quota file. If there is no enetry for this identifier then
327 * one is inserted. The actual hash table index is returned.
330 dqlookup(qfp
, id
, dqb
, index
)
331 struct quotafile
*qfp
;
349 auio
.uio_iov
= &aiov
;
351 auio
.uio_segflg
= UIO_SYSSPACE
;
352 auio
.uio_procp
= (struct proc
*)0;
354 mask
= qfp
->qf_maxentries
- 1;
355 i
= dqhash1(id
, qfp
->qf_shift
, mask
);
356 skip
= dqhash2(id
, mask
);
358 for (last
= (i
+ (qfp
->qf_maxentries
-1) * skip
) & mask
;
360 i
= (i
+ skip
) & mask
) {
362 aiov
.iov_base
= (caddr_t
)dqb
;
363 aiov
.iov_len
= sizeof (struct dqblk
);
364 auio
.uio_resid
= sizeof (struct dqblk
);
365 auio
.uio_offset
= (off_t
)dqoffset(i
);
366 auio
.uio_rw
= UIO_READ
;
367 error
= VOP_READ(dqvp
, &auio
, 0, cred
);
369 printf("dqlookup: error %d looking up id %d at index %d\n", error
, id
, i
);
371 } else if (auio
.uio_resid
) {
373 printf("dqlookup: error looking up id %d at index %d\n", id
, i
);
377 * An empty entry means there is no entry
378 * with that id. In this case a new dqb
379 * record will be inserted.
381 if (dqb
->dqb_id
== 0) {
382 bzero(dqb
, sizeof(struct dqblk
));
385 * Write back to reserve entry for this id
387 aiov
.iov_base
= (caddr_t
)dqb
;
388 aiov
.iov_len
= sizeof (struct dqblk
);
389 auio
.uio_resid
= sizeof (struct dqblk
);
390 auio
.uio_offset
= (off_t
)dqoffset(i
);
391 auio
.uio_rw
= UIO_WRITE
;
392 error
= VOP_WRITE(dqvp
, &auio
, 0, cred
);
393 if (auio
.uio_resid
&& error
== 0)
399 /* An id match means an entry was found. */
400 if (dqb
->dqb_id
== id
)
404 *index
= i
; /* remember index so we don't have to recompute it later */
409 * Obtain a reference to a dquot.
420 * Release a reference to a dquot.
425 register struct dquot
*dq
;
430 if (dq
->dq_cnt
> 1) {
434 if (dq
->dq_flags
& DQ_MOD
)
435 (void) dqsync(vp
, dq
);
436 if (--dq
->dq_cnt
> 0)
438 TAILQ_INSERT_TAIL(&dqfreelist
, dq
, dq_freelist
);
442 * Release a reference to a dquot but don't do any I/O.
447 register struct dquot
*dq
;
452 if (--dq
->dq_cnt
> 0)
455 if (dq
->dq_flags
& DQ_MOD
)
456 TAILQ_INSERT_TAIL(&dqdirtylist
, dq
, dq_freelist
);
458 TAILQ_INSERT_TAIL(&dqfreelist
, dq
, dq_freelist
);
462 * Update a quota file's orphaned disk quotas.
466 struct quotafile
*qfp
;
471 TAILQ_FOREACH(dq
, &dqdirtylist
, dq_freelist
) {
472 if ((dq
->dq_flags
& DQ_MOD
) == 0)
473 panic("dqsync_orphans: dirty dquot isn't");
475 panic("dqsync_orphans: dquot in use");
477 if (dq
->dq_qfile
== qfp
) {
478 TAILQ_REMOVE(&dqdirtylist
, dq
, dq_freelist
);
481 (void) dqsync(NULLVP
, dq
);
484 if ((dq
->dq_cnt
== 0) && (dq
->dq_flags
& DQ_MOD
) == 0)
485 TAILQ_INSERT_TAIL(&dqfreelist
, dq
, dq_freelist
);
493 * Update the disk quota in the quota file.
500 struct proc
*p
= current_proc(); /* XXX */
507 panic("dqsync: dquot");
508 if ((dq
->dq_flags
& DQ_MOD
) == 0)
512 if ((dqvp
= dq
->dq_qfile
->qf_vp
) == NULLVP
)
513 panic("dqsync: file");
515 vn_lock(dqvp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
516 while (dq
->dq_flags
& DQ_LOCK
) {
517 dq
->dq_flags
|= DQ_WANT
;
518 sleep((caddr_t
)dq
, PINOD
+2);
519 if ((dq
->dq_flags
& DQ_MOD
) == 0) {
521 VOP_UNLOCK(dqvp
, 0, p
);
525 dq
->dq_flags
|= DQ_LOCK
;
526 auio
.uio_iov
= &aiov
;
528 aiov
.iov_base
= (caddr_t
)&dq
->dq_dqb
;
529 aiov
.iov_len
= sizeof (struct dqblk
);
530 auio
.uio_resid
= sizeof (struct dqblk
);
531 auio
.uio_offset
= (off_t
)dqoffset(dq
->dq_index
);
532 auio
.uio_segflg
= UIO_SYSSPACE
;
533 auio
.uio_rw
= UIO_WRITE
;
534 auio
.uio_procp
= (struct proc
*)0;
535 error
= VOP_WRITE(dqvp
, &auio
, 0, dq
->dq_qfile
->qf_cred
);
536 if (auio
.uio_resid
&& error
== 0)
538 if (dq
->dq_flags
& DQ_WANT
)
540 dq
->dq_flags
&= ~(DQ_MOD
|DQ_LOCK
|DQ_WANT
);
542 VOP_UNLOCK(dqvp
, 0, p
);
547 * Flush all entries from the cache for a particular vnode.
551 register struct vnode
*vp
;
553 register struct dquot
*dq
, *nextdq
;
557 * Move all dquot's that used to refer to this quota
558 * file off their hash chains (they will eventually
559 * fall off the head of the free list and be re-used).
561 for (dqh
= &dqhashtbl
[dqhash
]; dqh
>= dqhashtbl
; dqh
--) {
562 for (dq
= dqh
->lh_first
; dq
; dq
= nextdq
) {
563 nextdq
= dq
->dq_hash
.le_next
;
564 if (dq
->dq_qfile
->qf_vp
!= vp
)
567 panic("dqflush: stray dquot");
568 LIST_REMOVE(dq
, dq_hash
);
569 dq
->dq_qfile
= (struct quotafile
*)0;