]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/vfs/vfs_quota.c
xnu-344.tar.gz
[apple/xnu.git] / bsd / vfs / vfs_quota.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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.
11 *
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
18 * under the License.
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
90
91static int dqlookup(struct quotafile *, u_long, struct dqblk *, u_int32_t *);
92
93
94/*
95 * Initialize the quota system.
96 */
97void
98dqinit()
99{
100
101 dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
102 TAILQ_INIT(&dqfreelist);
103}
104
105
106/*
107 * Initialize a quota file
108 */
109int
110dqfileopen(qfp, type)
111 struct quotafile *qfp;
112 int type;
113{
114 struct dqfilehdr header;
115 struct vattr vattr;
116 struct iovec aiov;
117 struct uio auio;
118 int error;
119
120 /* Obtain the file size */
121 error = VOP_GETATTR(qfp->qf_vp, &vattr, qfp->qf_cred, current_proc());
122 if (error)
123 return (error);
124
125 /* Read the file header */
126 auio.uio_iov = &aiov;
127 auio.uio_iovcnt = 1;
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);
136 if (error)
137 return (error);
138 else if (auio.uio_resid)
139 return (EINVAL);
140
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))))
146 return (EINVAL);
147
148 /* Set up the time limits for this quota. */
149 if (header.dqh_btime > 0)
150 qfp->qf_btime = header.dqh_btime;
151 else
152 qfp->qf_btime = MAX_DQ_TIME;
153 if (header.dqh_itime > 0)
154 qfp->qf_itime = header.dqh_itime;
155 else
156 qfp->qf_itime = MAX_IQ_TIME;
157
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);
162
163 return (0);
164}
165
166/*
167 * Close down a quota file
168 */
169void
170dqfileclose(qfp, type)
171 struct quotafile *qfp;
172 int type;
173{
174 struct dqfilehdr header;
175 struct iovec aiov;
176 struct uio auio;
177
178 auio.uio_iov = &aiov;
179 auio.uio_iovcnt = 1;
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;
189
190 auio.uio_iov = &aiov;
191 auio.uio_iovcnt = 1;
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);
200 }
201}
202
203
204/*
205 * Obtain a dquot structure for the specified identifier and quota file
206 * reading the information from the file if necessary.
207 */
208int
209dqget(vp, id, qfp, type, dqp)
210 struct vnode *vp;
211 u_long id;
212 struct quotafile *qfp;
213 register int type;
214 struct dquot **dqp;
215{
216 struct proc *p = current_proc(); /* XXX */
217 struct dquot *dq;
218 struct dqhash *dqh;
219 struct vnode *dqvp;
220 int error = 0;
221
222 dqvp = qfp->qf_vp;
223 if (id == 0 || dqvp == NULLVP || (qfp->qf_qflags & QTF_CLOSING)) {
224 *dqp = NODQUOT;
225 return (EINVAL);
226 }
227 /*
228 * Check the cache first.
229 */
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)
234 continue;
235 /*
236 * Cache hit with no references. Take
237 * the structure off the free list.
238 */
239 if (dq->dq_cnt == 0)
240 TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
241 DQREF(dq);
242 *dqp = dq;
243 return (0);
244 }
245 /*
246 * Not in cache, allocate a new one.
247 */
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);
254 numdquot++;
255 } else {
256 if ((dq = dqfreelist.tqh_first) == NULL) {
257 tablefull("dquot");
258 *dqp = NODQUOT;
259 return (EUSERS);
260 }
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);
265 }
266 /*
267 * Initialize the contents of the dquot structure.
268 */
269 if (vp != dqvp)
270 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p);
271 LIST_INSERT_HEAD(dqh, dq, dq_hash);
272 DQREF(dq);
273 dq->dq_flags = DQ_LOCK;
274 dq->dq_id = id;
275 dq->dq_qfile = qfp;
276 dq->dq_type = type;
277 error = dqlookup(qfp, id, &dq->dq_dqb, &dq->dq_index);
278
279 if (vp != dqvp)
280 VOP_UNLOCK(dqvp, 0, p);
281 if (dq->dq_flags & DQ_WANT)
282 wakeup((caddr_t)dq);
283 dq->dq_flags = 0;
284 /*
285 * I/O error in reading quota file, release
286 * quota structure and reflect problem to caller.
287 */
288 if (error) {
289 LIST_REMOVE(dq, dq_hash);
290 dqrele(vp, dq);
291 *dqp = NODQUOT;
292 return (error);
293 }
294 /*
295 * Check for no limit to enforce.
296 * Initialize time values if necessary.
297 */
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;
306 }
307 *dqp = dq;
308 return (0);
309}
310
311/*
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.
315 */
316static int
317dqlookup(qfp, id, dqb, index)
318 struct quotafile *qfp;
319 u_long id;
320 struct dqblk *dqb;
321 u_int32_t *index;
322{
323 struct vnode *dqvp;
324 struct ucred *cred;
325 struct iovec aiov;
326 struct uio auio;
327 int i, skip, last;
328 u_long mask;
329 int error = 0;
330
331 if (id == 0)
332 return (EINVAL);
333 dqvp = qfp->qf_vp;
334 cred = qfp->qf_cred;
335
336 auio.uio_iov = &aiov;
337 auio.uio_iovcnt = 1;
338 auio.uio_segflg = UIO_SYSSPACE;
339 auio.uio_procp = (struct proc *)0;
340
341 mask = qfp->qf_maxentries - 1;
342 i = dqhash1(id, qfp->qf_shift, mask);
343 skip = dqhash2(id, mask);
344
345 for (last = (i + (qfp->qf_maxentries-1) * skip) & mask;
346 i != last;
347 i = (i + skip) & mask) {
348
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);
355 if (error) {
356 printf("dqlookup: error %d looking up id %d at index %d\n", error, id, i);
357 break;
358 } else if (auio.uio_resid) {
359 error = EIO;
360 printf("dqlookup: error looking up id %d at index %d\n", id, i);
361 break;
362 }
363 /*
364 * An empty entry means there is no entry
365 * with that id. In this case a new dqb
366 * record will be inserted.
367 */
368 if (dqb->dqb_id == 0) {
369 bzero(dqb, sizeof(struct dqblk));
370 dqb->dqb_id = id;
371 /*
372 * Write back to reserve entry for this id
373 */
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)
381 error = EIO;
382 if (error == 0)
383 ++qfp->qf_entrycnt;
384 break;
385 }
386 /* An id match means an entry was found. */
387 if (dqb->dqb_id == id)
388 break;
389 }
390
391 *index = i; /* remember index so we don't have to recompute it later */
392 return (error);
393}
394
395/*
396 * Obtain a reference to a dquot.
397 */
398void
399dqref(dq)
400 struct dquot *dq;
401{
402
403 dq->dq_cnt++;
404}
405
406/*
407 * Release a reference to a dquot.
408 */
409void
410dqrele(vp, dq)
411 struct vnode *vp;
412 register struct dquot *dq;
413{
414
415 if (dq == NODQUOT)
416 return;
417 if (dq->dq_cnt > 1) {
418 dq->dq_cnt--;
419 return;
420 }
421 if (dq->dq_flags & DQ_MOD)
422 (void) dqsync(vp, dq);
423 if (--dq->dq_cnt > 0)
424 return;
425 TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
426}
427
428/*
429 * Update the disk quota in the quota file.
430 */
431int
432dqsync(vp, dq)
433 struct vnode *vp;
434 struct dquot *dq;
435{
436 struct proc *p = current_proc(); /* XXX */
437 struct vnode *dqvp;
438 struct iovec aiov;
439 struct uio auio;
440 int error;
441
442 if (dq == NODQUOT)
443 panic("dqsync: dquot");
444 if ((dq->dq_flags & DQ_MOD) == 0)
445 return (0);
446 if (dq->dq_id == 0)
447 return(0);
448 if ((dqvp = dq->dq_qfile->qf_vp) == NULLVP)
449 panic("dqsync: file");
450 if (vp != dqvp)
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) {
456 if (vp != dqvp)
457 VOP_UNLOCK(dqvp, 0, p);
458 return (0);
459 }
460 }
461 dq->dq_flags |= DQ_LOCK;
462 auio.uio_iov = &aiov;
463 auio.uio_iovcnt = 1;
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)
473 error = EIO;
474 if (dq->dq_flags & DQ_WANT)
475 wakeup((caddr_t)dq);
476 dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
477 if (vp != dqvp)
478 VOP_UNLOCK(dqvp, 0, p);
479 return (error);
480}
481
482/*
483 * Flush all entries from the cache for a particular vnode.
484 */
485void
486dqflush(vp)
487 register struct vnode *vp;
488{
489 register struct dquot *dq, *nextdq;
490 struct dqhash *dqh;
491
492 /*
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).
496 */
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)
501 continue;
502 if (dq->dq_cnt)
503 panic("dqflush: stray dquot");
504 LIST_REMOVE(dq, dq_hash);
505 dq->dq_qfile = (struct quotafile *)0;
506 }
507 }
508}