]> git.saurik.com Git - apple/xnu.git/blame - bsd/vfs/vfs_quota.c
xnu-517.9.4.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_quota.c
CommitLineData
9bccf70c
A
1/*
2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
e5568f75
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.
9bccf70c 11 *
e5568f75
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
9bccf70c
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
e5568f75
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.
9bccf70c
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
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>
65#include <sys/file.h>
66#include <sys/proc.h>
67#include <sys/vnode.h>
68#include <sys/mount.h>
69#include <sys/quota.h>
70
71
72static u_int32_t quotamagic[MAXQUOTAS] = INITQMAGICS;
73
74
75/*
76 * Code pertaining to management of the in-core dquot data structures.
77 */
78#define DQHASH(dqvp, id) \
79 (&dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash])
80LIST_HEAD(dqhash, dquot) *dqhashtbl;
81u_long dqhash;
82
83/*
84 * Dquot free list.
85 */
86#define DQUOTINC 5 /* minimum free dquots desired */
87TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
88long numdquot, desireddquot = DQUOTINC;
89
d7e50217
A
90/*
91 * Dquot dirty orphans list.
92 */
93TAILQ_HEAD(dqdirtylist, dquot) dqdirtylist;
94
9bccf70c
A
95
96static int dqlookup(struct quotafile *, u_long, struct dqblk *, u_int32_t *);
97
98
99/*
100 * Initialize the quota system.
101 */
102void
103dqinit()
104{
105
106 dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
107 TAILQ_INIT(&dqfreelist);
d7e50217 108 TAILQ_INIT(&dqdirtylist);
9bccf70c
A
109}
110
111
112/*
113 * Initialize a quota file
114 */
115int
116dqfileopen(qfp, type)
117 struct quotafile *qfp;
118 int type;
119{
120 struct dqfilehdr header;
121 struct vattr vattr;
122 struct iovec aiov;
123 struct uio auio;
124 int error;
125
126 /* Obtain the file size */
127 error = VOP_GETATTR(qfp->qf_vp, &vattr, qfp->qf_cred, current_proc());
128 if (error)
129 return (error);
130
131 /* Read the file header */
132 auio.uio_iov = &aiov;
133 auio.uio_iovcnt = 1;
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);
142 if (error)
143 return (error);
144 else if (auio.uio_resid)
145 return (EINVAL);
146
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))))
152 return (EINVAL);
153
154 /* Set up the time limits for this quota. */
155 if (header.dqh_btime > 0)
156 qfp->qf_btime = header.dqh_btime;
157 else
158 qfp->qf_btime = MAX_DQ_TIME;
159 if (header.dqh_itime > 0)
160 qfp->qf_itime = header.dqh_itime;
161 else
162 qfp->qf_itime = MAX_IQ_TIME;
163
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);
168
169 return (0);
170}
171
172/*
173 * Close down a quota file
174 */
175void
176dqfileclose(qfp, type)
177 struct quotafile *qfp;
178 int type;
179{
180 struct dqfilehdr header;
181 struct iovec aiov;
182 struct uio auio;
183
184 auio.uio_iov = &aiov;
185 auio.uio_iovcnt = 1;
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;
195
196 auio.uio_iov = &aiov;
197 auio.uio_iovcnt = 1;
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);
206 }
207}
208
209
210/*
211 * Obtain a dquot structure for the specified identifier and quota file
212 * reading the information from the file if necessary.
213 */
214int
215dqget(vp, id, qfp, type, dqp)
216 struct vnode *vp;
217 u_long id;
218 struct quotafile *qfp;
219 register int type;
220 struct dquot **dqp;
221{
222 struct proc *p = current_proc(); /* XXX */
223 struct dquot *dq;
224 struct dqhash *dqh;
225 struct vnode *dqvp;
226 int error = 0;
227
228 dqvp = qfp->qf_vp;
229 if (id == 0 || dqvp == NULLVP || (qfp->qf_qflags & QTF_CLOSING)) {
230 *dqp = NODQUOT;
231 return (EINVAL);
232 }
233 /*
234 * Check the cache first.
235 */
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)
240 continue;
241 /*
242 * Cache hit with no references. Take
243 * the structure off the free list.
244 */
d7e50217
A
245 if (dq->dq_cnt == 0) {
246 if (dq->dq_flags & DQ_MOD)
247 TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist);
248 else
249 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
250 }
9bccf70c
A
251 DQREF(dq);
252 *dqp = dq;
253 return (0);
254 }
255 /*
256 * Not in cache, allocate a new one.
257 */
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);
264 numdquot++;
265 } else {
266 if ((dq = dqfreelist.tqh_first) == NULL) {
267 tablefull("dquot");
268 *dqp = NODQUOT;
269 return (EUSERS);
270 }
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);
275 }
276 /*
277 * Initialize the contents of the dquot structure.
278 */
279 if (vp != dqvp)
280 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p);
281 LIST_INSERT_HEAD(dqh, dq, dq_hash);
282 DQREF(dq);
283 dq->dq_flags = DQ_LOCK;
284 dq->dq_id = id;
285 dq->dq_qfile = qfp;
286 dq->dq_type = type;
287 error = dqlookup(qfp, id, &dq->dq_dqb, &dq->dq_index);
288
289 if (vp != dqvp)
290 VOP_UNLOCK(dqvp, 0, p);
291 if (dq->dq_flags & DQ_WANT)
292 wakeup((caddr_t)dq);
293 dq->dq_flags = 0;
294 /*
295 * I/O error in reading quota file, release
296 * quota structure and reflect problem to caller.
297 */
298 if (error) {
299 LIST_REMOVE(dq, dq_hash);
300 dqrele(vp, dq);
301 *dqp = NODQUOT;
302 return (error);
303 }
304 /*
305 * Check for no limit to enforce.
306 * Initialize time values if necessary.
307 */
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;
316 }
317 *dqp = dq;
318 return (0);
319}
320
321/*
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.
325 */
326static int
327dqlookup(qfp, id, dqb, index)
328 struct quotafile *qfp;
329 u_long id;
330 struct dqblk *dqb;
331 u_int32_t *index;
332{
333 struct vnode *dqvp;
334 struct ucred *cred;
335 struct iovec aiov;
336 struct uio auio;
337 int i, skip, last;
338 u_long mask;
339 int error = 0;
340
341 if (id == 0)
342 return (EINVAL);
343 dqvp = qfp->qf_vp;
344 cred = qfp->qf_cred;
345
346 auio.uio_iov = &aiov;
347 auio.uio_iovcnt = 1;
348 auio.uio_segflg = UIO_SYSSPACE;
349 auio.uio_procp = (struct proc *)0;
350
351 mask = qfp->qf_maxentries - 1;
352 i = dqhash1(id, qfp->qf_shift, mask);
353 skip = dqhash2(id, mask);
354
355 for (last = (i + (qfp->qf_maxentries-1) * skip) & mask;
356 i != last;
357 i = (i + skip) & mask) {
358
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);
365 if (error) {
366 printf("dqlookup: error %d looking up id %d at index %d\n", error, id, i);
367 break;
368 } else if (auio.uio_resid) {
369 error = EIO;
370 printf("dqlookup: error looking up id %d at index %d\n", id, i);
371 break;
372 }
373 /*
374 * An empty entry means there is no entry
375 * with that id. In this case a new dqb
376 * record will be inserted.
377 */
378 if (dqb->dqb_id == 0) {
379 bzero(dqb, sizeof(struct dqblk));
380 dqb->dqb_id = id;
381 /*
382 * Write back to reserve entry for this id
383 */
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)
391 error = EIO;
392 if (error == 0)
393 ++qfp->qf_entrycnt;
394 break;
395 }
396 /* An id match means an entry was found. */
397 if (dqb->dqb_id == id)
398 break;
399 }
400
401 *index = i; /* remember index so we don't have to recompute it later */
402 return (error);
403}
404
405/*
406 * Obtain a reference to a dquot.
407 */
408void
409dqref(dq)
410 struct dquot *dq;
411{
412
413 dq->dq_cnt++;
414}
415
416/*
417 * Release a reference to a dquot.
418 */
419void
420dqrele(vp, dq)
421 struct vnode *vp;
422 register struct dquot *dq;
423{
424
425 if (dq == NODQUOT)
426 return;
427 if (dq->dq_cnt > 1) {
428 dq->dq_cnt--;
429 return;
430 }
431 if (dq->dq_flags & DQ_MOD)
432 (void) dqsync(vp, dq);
433 if (--dq->dq_cnt > 0)
434 return;
435 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
436}
437
d7e50217
A
438/*
439 * Release a reference to a dquot but don't do any I/O.
440 */
441void
442dqreclaim(vp, dq)
443 struct vnode *vp;
444 register struct dquot *dq;
445{
446 if (dq == NODQUOT)
447 return;
448
449 if (--dq->dq_cnt > 0)
450 return;
451
452 if (dq->dq_flags & DQ_MOD)
453 TAILQ_INSERT_TAIL(&dqdirtylist, dq, dq_freelist);
454 else
455 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
456}
457
458/*
459 * Update a quota file's orphaned disk quotas.
460 */
461void
462dqsync_orphans(qfp)
463 struct quotafile *qfp;
464{
465 struct dquot *dq;
466
467 loop:
468 TAILQ_FOREACH(dq, &dqdirtylist, dq_freelist) {
469 if ((dq->dq_flags & DQ_MOD) == 0)
470 panic("dqsync_orphans: dirty dquot isn't");
471 if (dq->dq_cnt != 0)
472 panic("dqsync_orphans: dquot in use");
473
474 if (dq->dq_qfile == qfp) {
475 TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist);
476
477 dq->dq_cnt++;
478 (void) dqsync(NULLVP, dq);
479 dq->dq_cnt--;
480
481 if ((dq->dq_cnt == 0) && (dq->dq_flags & DQ_MOD) == 0)
482 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
483
484 goto loop;
485 }
486 }
487}
488
9bccf70c
A
489/*
490 * Update the disk quota in the quota file.
491 */
492int
493dqsync(vp, dq)
494 struct vnode *vp;
495 struct dquot *dq;
496{
497 struct proc *p = current_proc(); /* XXX */
498 struct vnode *dqvp;
499 struct iovec aiov;
500 struct uio auio;
501 int error;
502
503 if (dq == NODQUOT)
504 panic("dqsync: dquot");
505 if ((dq->dq_flags & DQ_MOD) == 0)
506 return (0);
507 if (dq->dq_id == 0)
508 return(0);
509 if ((dqvp = dq->dq_qfile->qf_vp) == NULLVP)
510 panic("dqsync: file");
511 if (vp != dqvp)
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) {
517 if (vp != dqvp)
518 VOP_UNLOCK(dqvp, 0, p);
519 return (0);
520 }
521 }
522 dq->dq_flags |= DQ_LOCK;
523 auio.uio_iov = &aiov;
524 auio.uio_iovcnt = 1;
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)
534 error = EIO;
535 if (dq->dq_flags & DQ_WANT)
536 wakeup((caddr_t)dq);
537 dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
538 if (vp != dqvp)
539 VOP_UNLOCK(dqvp, 0, p);
540 return (error);
541}
542
543/*
544 * Flush all entries from the cache for a particular vnode.
545 */
546void
547dqflush(vp)
548 register struct vnode *vp;
549{
550 register struct dquot *dq, *nextdq;
551 struct dqhash *dqh;
552
553 /*
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).
557 */
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)
562 continue;
563 if (dq->dq_cnt)
564 panic("dqflush: stray dquot");
565 LIST_REMOVE(dq, dq_hash);
566 dq->dq_qfile = (struct quotafile *)0;
567 }
568 }
569}