]> git.saurik.com Git - apple/xnu.git/blob - bsd/vfs/vfs_quota.c
c60bb78ed3aa53f11302014e4bf5b78e5cc61c53
[apple/xnu.git] / bsd / vfs / vfs_quota.c
1 /*
2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
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
13 * file.
14 *
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.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25 /*
26 * Copyright (c) 1982, 1986, 1990, 1993, 1995
27 * The Regents of the University of California. All rights reserved.
28 *
29 * This code is derived from software contributed to Berkeley by
30 * Robert Elz at The University of Melbourne.
31 *
32 * Redistribution and use in source and binary forms, with or without
33 * modification, are permitted provided that the following conditions
34 * are met:
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.
47 *
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
58 * SUCH DAMAGE.
59 *
60 * @(#)vfs_quota.c
61 * derived from @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
62 */
63
64 #include <sys/param.h>
65 #include <sys/kernel.h>
66 #include <sys/systm.h>
67 #include <sys/malloc.h>
68 #include <sys/file.h>
69 #include <sys/proc.h>
70 #include <sys/vnode.h>
71 #include <sys/mount.h>
72 #include <sys/quota.h>
73
74
75 static u_int32_t quotamagic[MAXQUOTAS] = INITQMAGICS;
76
77
78 /*
79 * Code pertaining to management of the in-core dquot data structures.
80 */
81 #define DQHASH(dqvp, id) \
82 (&dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash])
83 LIST_HEAD(dqhash, dquot) *dqhashtbl;
84 u_long dqhash;
85
86 /*
87 * Dquot free list.
88 */
89 #define DQUOTINC 5 /* minimum free dquots desired */
90 TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
91 long numdquot, desireddquot = DQUOTINC;
92
93 /*
94 * Dquot dirty orphans list.
95 */
96 TAILQ_HEAD(dqdirtylist, dquot) dqdirtylist;
97
98
99 static int dqlookup(struct quotafile *, u_long, struct dqblk *, u_int32_t *);
100
101
102 /*
103 * Initialize the quota system.
104 */
105 void
106 dqinit()
107 {
108
109 dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
110 TAILQ_INIT(&dqfreelist);
111 TAILQ_INIT(&dqdirtylist);
112 }
113
114
115 /*
116 * Initialize a quota file
117 */
118 int
119 dqfileopen(qfp, type)
120 struct quotafile *qfp;
121 int type;
122 {
123 struct dqfilehdr header;
124 struct vattr vattr;
125 struct iovec aiov;
126 struct uio auio;
127 int error;
128
129 /* Obtain the file size */
130 error = VOP_GETATTR(qfp->qf_vp, &vattr, qfp->qf_cred, current_proc());
131 if (error)
132 return (error);
133
134 /* Read the file header */
135 auio.uio_iov = &aiov;
136 auio.uio_iovcnt = 1;
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);
145 if (error)
146 return (error);
147 else if (auio.uio_resid)
148 return (EINVAL);
149
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))))
155 return (EINVAL);
156
157 /* Set up the time limits for this quota. */
158 if (header.dqh_btime > 0)
159 qfp->qf_btime = header.dqh_btime;
160 else
161 qfp->qf_btime = MAX_DQ_TIME;
162 if (header.dqh_itime > 0)
163 qfp->qf_itime = header.dqh_itime;
164 else
165 qfp->qf_itime = MAX_IQ_TIME;
166
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);
171
172 return (0);
173 }
174
175 /*
176 * Close down a quota file
177 */
178 void
179 dqfileclose(qfp, type)
180 struct quotafile *qfp;
181 int type;
182 {
183 struct dqfilehdr header;
184 struct iovec aiov;
185 struct uio auio;
186
187 auio.uio_iov = &aiov;
188 auio.uio_iovcnt = 1;
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;
198
199 auio.uio_iov = &aiov;
200 auio.uio_iovcnt = 1;
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);
209 }
210 }
211
212
213 /*
214 * Obtain a dquot structure for the specified identifier and quota file
215 * reading the information from the file if necessary.
216 */
217 int
218 dqget(vp, id, qfp, type, dqp)
219 struct vnode *vp;
220 u_long id;
221 struct quotafile *qfp;
222 register int type;
223 struct dquot **dqp;
224 {
225 struct proc *p = current_proc(); /* XXX */
226 struct dquot *dq;
227 struct dqhash *dqh;
228 struct vnode *dqvp;
229 int error = 0;
230
231 dqvp = qfp->qf_vp;
232 if (id == 0 || dqvp == NULLVP || (qfp->qf_qflags & QTF_CLOSING)) {
233 *dqp = NODQUOT;
234 return (EINVAL);
235 }
236 /*
237 * Check the cache first.
238 */
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)
243 continue;
244 /*
245 * Cache hit with no references. Take
246 * the structure off the free list.
247 */
248 if (dq->dq_cnt == 0) {
249 if (dq->dq_flags & DQ_MOD)
250 TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist);
251 else
252 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
253 }
254 DQREF(dq);
255 *dqp = dq;
256 return (0);
257 }
258 /*
259 * Not in cache, allocate a new one.
260 */
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);
267 numdquot++;
268 } else {
269 if ((dq = dqfreelist.tqh_first) == NULL) {
270 tablefull("dquot");
271 *dqp = NODQUOT;
272 return (EUSERS);
273 }
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);
278 }
279 /*
280 * Initialize the contents of the dquot structure.
281 */
282 if (vp != dqvp)
283 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p);
284 LIST_INSERT_HEAD(dqh, dq, dq_hash);
285 DQREF(dq);
286 dq->dq_flags = DQ_LOCK;
287 dq->dq_id = id;
288 dq->dq_qfile = qfp;
289 dq->dq_type = type;
290 error = dqlookup(qfp, id, &dq->dq_dqb, &dq->dq_index);
291
292 if (vp != dqvp)
293 VOP_UNLOCK(dqvp, 0, p);
294 if (dq->dq_flags & DQ_WANT)
295 wakeup((caddr_t)dq);
296 dq->dq_flags = 0;
297 /*
298 * I/O error in reading quota file, release
299 * quota structure and reflect problem to caller.
300 */
301 if (error) {
302 LIST_REMOVE(dq, dq_hash);
303 dqrele(vp, dq);
304 *dqp = NODQUOT;
305 return (error);
306 }
307 /*
308 * Check for no limit to enforce.
309 * Initialize time values if necessary.
310 */
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;
319 }
320 *dqp = dq;
321 return (0);
322 }
323
324 /*
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.
328 */
329 static int
330 dqlookup(qfp, id, dqb, index)
331 struct quotafile *qfp;
332 u_long id;
333 struct dqblk *dqb;
334 u_int32_t *index;
335 {
336 struct vnode *dqvp;
337 struct ucred *cred;
338 struct iovec aiov;
339 struct uio auio;
340 int i, skip, last;
341 u_long mask;
342 int error = 0;
343
344 if (id == 0)
345 return (EINVAL);
346 dqvp = qfp->qf_vp;
347 cred = qfp->qf_cred;
348
349 auio.uio_iov = &aiov;
350 auio.uio_iovcnt = 1;
351 auio.uio_segflg = UIO_SYSSPACE;
352 auio.uio_procp = (struct proc *)0;
353
354 mask = qfp->qf_maxentries - 1;
355 i = dqhash1(id, qfp->qf_shift, mask);
356 skip = dqhash2(id, mask);
357
358 for (last = (i + (qfp->qf_maxentries-1) * skip) & mask;
359 i != last;
360 i = (i + skip) & mask) {
361
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);
368 if (error) {
369 printf("dqlookup: error %d looking up id %d at index %d\n", error, id, i);
370 break;
371 } else if (auio.uio_resid) {
372 error = EIO;
373 printf("dqlookup: error looking up id %d at index %d\n", id, i);
374 break;
375 }
376 /*
377 * An empty entry means there is no entry
378 * with that id. In this case a new dqb
379 * record will be inserted.
380 */
381 if (dqb->dqb_id == 0) {
382 bzero(dqb, sizeof(struct dqblk));
383 dqb->dqb_id = id;
384 /*
385 * Write back to reserve entry for this id
386 */
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)
394 error = EIO;
395 if (error == 0)
396 ++qfp->qf_entrycnt;
397 break;
398 }
399 /* An id match means an entry was found. */
400 if (dqb->dqb_id == id)
401 break;
402 }
403
404 *index = i; /* remember index so we don't have to recompute it later */
405 return (error);
406 }
407
408 /*
409 * Obtain a reference to a dquot.
410 */
411 void
412 dqref(dq)
413 struct dquot *dq;
414 {
415
416 dq->dq_cnt++;
417 }
418
419 /*
420 * Release a reference to a dquot.
421 */
422 void
423 dqrele(vp, dq)
424 struct vnode *vp;
425 register struct dquot *dq;
426 {
427
428 if (dq == NODQUOT)
429 return;
430 if (dq->dq_cnt > 1) {
431 dq->dq_cnt--;
432 return;
433 }
434 if (dq->dq_flags & DQ_MOD)
435 (void) dqsync(vp, dq);
436 if (--dq->dq_cnt > 0)
437 return;
438 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
439 }
440
441 /*
442 * Release a reference to a dquot but don't do any I/O.
443 */
444 void
445 dqreclaim(vp, dq)
446 struct vnode *vp;
447 register struct dquot *dq;
448 {
449 if (dq == NODQUOT)
450 return;
451
452 if (--dq->dq_cnt > 0)
453 return;
454
455 if (dq->dq_flags & DQ_MOD)
456 TAILQ_INSERT_TAIL(&dqdirtylist, dq, dq_freelist);
457 else
458 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
459 }
460
461 /*
462 * Update a quota file's orphaned disk quotas.
463 */
464 void
465 dqsync_orphans(qfp)
466 struct quotafile *qfp;
467 {
468 struct dquot *dq;
469
470 loop:
471 TAILQ_FOREACH(dq, &dqdirtylist, dq_freelist) {
472 if ((dq->dq_flags & DQ_MOD) == 0)
473 panic("dqsync_orphans: dirty dquot isn't");
474 if (dq->dq_cnt != 0)
475 panic("dqsync_orphans: dquot in use");
476
477 if (dq->dq_qfile == qfp) {
478 TAILQ_REMOVE(&dqdirtylist, dq, dq_freelist);
479
480 dq->dq_cnt++;
481 (void) dqsync(NULLVP, dq);
482 dq->dq_cnt--;
483
484 if ((dq->dq_cnt == 0) && (dq->dq_flags & DQ_MOD) == 0)
485 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
486
487 goto loop;
488 }
489 }
490 }
491
492 /*
493 * Update the disk quota in the quota file.
494 */
495 int
496 dqsync(vp, dq)
497 struct vnode *vp;
498 struct dquot *dq;
499 {
500 struct proc *p = current_proc(); /* XXX */
501 struct vnode *dqvp;
502 struct iovec aiov;
503 struct uio auio;
504 int error;
505
506 if (dq == NODQUOT)
507 panic("dqsync: dquot");
508 if ((dq->dq_flags & DQ_MOD) == 0)
509 return (0);
510 if (dq->dq_id == 0)
511 return(0);
512 if ((dqvp = dq->dq_qfile->qf_vp) == NULLVP)
513 panic("dqsync: file");
514 if (vp != dqvp)
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) {
520 if (vp != dqvp)
521 VOP_UNLOCK(dqvp, 0, p);
522 return (0);
523 }
524 }
525 dq->dq_flags |= DQ_LOCK;
526 auio.uio_iov = &aiov;
527 auio.uio_iovcnt = 1;
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)
537 error = EIO;
538 if (dq->dq_flags & DQ_WANT)
539 wakeup((caddr_t)dq);
540 dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
541 if (vp != dqvp)
542 VOP_UNLOCK(dqvp, 0, p);
543 return (error);
544 }
545
546 /*
547 * Flush all entries from the cache for a particular vnode.
548 */
549 void
550 dqflush(vp)
551 register struct vnode *vp;
552 {
553 register struct dquot *dq, *nextdq;
554 struct dqhash *dqh;
555
556 /*
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).
560 */
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)
565 continue;
566 if (dq->dq_cnt)
567 panic("dqflush: stray dquot");
568 LIST_REMOVE(dq, dq_hash);
569 dq->dq_qfile = (struct quotafile *)0;
570 }
571 }
572 }