xref: /original-bsd/sys/ufs/ffs/ufs_quota.c (revision d54be081)
1 /*
2  * Copyright (c) 1982, 1986, 1990 Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Robert Elz at The University of Melbourne.
7  *
8  * %sccs.include.redist.c%
9  *
10  *	@(#)ufs_quota.c	7.11 (Berkeley) 06/21/91
11  */
12 #include "param.h"
13 #include "kernel.h"
14 #include "systm.h"
15 #include "namei.h"
16 #include "malloc.h"
17 #include "file.h"
18 #include "proc.h"
19 #include "vnode.h"
20 #include "mount.h"
21 
22 #include "fs.h"
23 #include "quota.h"
24 #include "inode.h"
25 #include "ufsmount.h"
26 
27 /*
28  * Quota name to error message mapping.
29  */
30 static char *quotatypes[] = INITQFNAMES;
31 
32 /*
33  * Set up the quotas for an inode.
34  *
35  * This routine completely defines the semantics of quotas.
36  * If other criterion want to be used to establish quotas, the
37  * MAXQUOTAS value in quotas.h should be increased, and the
38  * additional dquots set up here.
39  */
40 getinoquota(ip)
41 	register struct inode *ip;
42 {
43 	struct ufsmount *ump;
44 	struct vnode *vp = ITOV(ip);
45 	int error;
46 
47 	ump = VFSTOUFS(vp->v_mount);
48 	/*
49 	 * Set up the user quota based on file uid.
50 	 * EINVAL means that quotas are not enabled.
51 	 */
52 	if (ip->i_dquot[USRQUOTA] == NODQUOT &&
53 	    (error =
54 		dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) &&
55 	    error != EINVAL)
56 		return (error);
57 	/*
58 	 * Set up the group quota based on file gid.
59 	 * EINVAL means that quotas are not enabled.
60 	 */
61 	if (ip->i_dquot[GRPQUOTA] == NODQUOT &&
62 	    (error =
63 		dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&
64 	    error != EINVAL)
65 		return (error);
66 	return (0);
67 }
68 
69 /*
70  * Update disk usage, and take corrective action.
71  */
72 chkdq(ip, change, cred, flags)
73 	register struct inode *ip;
74 	long change;
75 	struct ucred *cred;
76 	int flags;
77 {
78 	register struct dquot *dq;
79 	register int i;
80 	int ncurblocks, error;
81 
82 #ifdef DIAGNOSTIC
83 	if ((flags & CHOWN) == 0)
84 		chkdquot(ip);
85 #endif
86 	if (change == 0)
87 		return (0);
88 	if (change < 0) {
89 		for (i = 0; i < MAXQUOTAS; i++) {
90 			if ((dq = ip->i_dquot[i]) == NODQUOT)
91 				continue;
92 			while (dq->dq_flags & DQ_LOCK) {
93 				dq->dq_flags |= DQ_WANT;
94 				sleep((caddr_t)dq, PINOD+1);
95 			}
96 			ncurblocks = dq->dq_curblocks + change;
97 			if (ncurblocks >= 0)
98 				dq->dq_curblocks = ncurblocks;
99 			else
100 				dq->dq_curblocks = 0;
101 			dq->dq_flags &= ~DQ_BLKS;
102 			dq->dq_flags |= DQ_MOD;
103 		}
104 		return (0);
105 	}
106 	if ((flags & FORCE) == 0 && cred->cr_uid != 0) {
107 		for (i = 0; i < MAXQUOTAS; i++) {
108 			if ((dq = ip->i_dquot[i]) == NODQUOT)
109 				continue;
110 			if (error = chkdqchg(ip, change, cred, i))
111 				return (error);
112 		}
113 	}
114 	for (i = 0; i < MAXQUOTAS; i++) {
115 		if ((dq = ip->i_dquot[i]) == NODQUOT)
116 			continue;
117 		while (dq->dq_flags & DQ_LOCK) {
118 			dq->dq_flags |= DQ_WANT;
119 			sleep((caddr_t)dq, PINOD+1);
120 		}
121 		dq->dq_curblocks += change;
122 		dq->dq_flags |= DQ_MOD;
123 	}
124 	return (0);
125 }
126 
127 /*
128  * Check for a valid change to a users allocation.
129  * Issue an error message if appropriate.
130  */
131 chkdqchg(ip, change, cred, type)
132 	struct inode *ip;
133 	long change;
134 	struct ucred *cred;
135 	int type;
136 {
137 	register struct dquot *dq = ip->i_dquot[type];
138 	long ncurblocks = dq->dq_curblocks + change;
139 
140 	/*
141 	 * If user would exceed their hard limit, disallow space allocation.
142 	 */
143 	if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
144 		if ((dq->dq_flags & DQ_BLKS) == 0 &&
145 		    ip->i_uid == cred->cr_uid) {
146 			uprintf("\n%s: write failed, %s disk limit reached\n",
147 			    ip->i_fs->fs_fsmnt, quotatypes[type]);
148 			dq->dq_flags |= DQ_BLKS;
149 		}
150 		return (EDQUOT);
151 	}
152 	/*
153 	 * If user is over their soft limit for too long, disallow space
154 	 * allocation. Reset time limit as they cross their soft limit.
155 	 */
156 	if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
157 		if (dq->dq_curblocks < dq->dq_bsoftlimit) {
158 			dq->dq_btime = time.tv_sec +
159 			    VFSTOUFS(ITOV(ip)->v_mount)->um_btime[type];
160 			if (ip->i_uid == cred->cr_uid)
161 				uprintf("\n%s: warning, %s %s\n",
162 				    ip->i_fs->fs_fsmnt, quotatypes[type],
163 				    "disk quota exceeded");
164 			return (0);
165 		}
166 		if (time.tv_sec > dq->dq_btime) {
167 			if ((dq->dq_flags & DQ_BLKS) == 0 &&
168 			    ip->i_uid == cred->cr_uid) {
169 				uprintf("\n%s: write failed, %s %s\n",
170 				    ip->i_fs->fs_fsmnt, quotatypes[type],
171 				    "disk quota exceeded too long");
172 				dq->dq_flags |= DQ_BLKS;
173 			}
174 			return (EDQUOT);
175 		}
176 	}
177 	return (0);
178 }
179 
180 /*
181  * Check the inode limit, applying corrective action.
182  */
183 chkiq(ip, change, cred, flags)
184 	register struct inode *ip;
185 	long change;
186 	struct ucred *cred;
187 	int flags;
188 {
189 	register struct dquot *dq;
190 	register int i;
191 	int ncurinodes, error;
192 
193 #ifdef DIAGNOSTIC
194 	if ((flags & CHOWN) == 0)
195 		chkdquot(ip);
196 #endif
197 	if (change == 0)
198 		return (0);
199 	if (change < 0) {
200 		for (i = 0; i < MAXQUOTAS; i++) {
201 			if ((dq = ip->i_dquot[i]) == NODQUOT)
202 				continue;
203 			while (dq->dq_flags & DQ_LOCK) {
204 				dq->dq_flags |= DQ_WANT;
205 				sleep((caddr_t)dq, PINOD+1);
206 			}
207 			ncurinodes = dq->dq_curinodes + change;
208 			if (ncurinodes >= 0)
209 				dq->dq_curinodes = ncurinodes;
210 			else
211 				dq->dq_curinodes = 0;
212 			dq->dq_flags &= ~DQ_INODS;
213 			dq->dq_flags |= DQ_MOD;
214 		}
215 		return (0);
216 	}
217 	if ((flags & FORCE) == 0 && cred->cr_uid != 0) {
218 		for (i = 0; i < MAXQUOTAS; i++) {
219 			if ((dq = ip->i_dquot[i]) == NODQUOT)
220 				continue;
221 			if (error = chkiqchg(ip, change, cred, i))
222 				return (error);
223 		}
224 	}
225 	for (i = 0; i < MAXQUOTAS; i++) {
226 		if ((dq = ip->i_dquot[i]) == NODQUOT)
227 			continue;
228 		while (dq->dq_flags & DQ_LOCK) {
229 			dq->dq_flags |= DQ_WANT;
230 			sleep((caddr_t)dq, PINOD+1);
231 		}
232 		dq->dq_curinodes += change;
233 		dq->dq_flags |= DQ_MOD;
234 	}
235 	return (0);
236 }
237 
238 /*
239  * Check for a valid change to a users allocation.
240  * Issue an error message if appropriate.
241  */
242 chkiqchg(ip, change, cred, type)
243 	struct inode *ip;
244 	long change;
245 	struct ucred *cred;
246 	int type;
247 {
248 	register struct dquot *dq = ip->i_dquot[type];
249 	long ncurinodes = dq->dq_curinodes + change;
250 
251 	/*
252 	 * If user would exceed their hard limit, disallow inode allocation.
253 	 */
254 	if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
255 		if ((dq->dq_flags & DQ_INODS) == 0 &&
256 		    ip->i_uid == cred->cr_uid) {
257 			uprintf("\n%s: write failed, %s inode limit reached\n",
258 			    ip->i_fs->fs_fsmnt, quotatypes[type]);
259 			dq->dq_flags |= DQ_INODS;
260 		}
261 		return (EDQUOT);
262 	}
263 	/*
264 	 * If user is over their soft limit for too long, disallow inode
265 	 * allocation. Reset time limit as they cross their soft limit.
266 	 */
267 	if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
268 		if (dq->dq_curinodes < dq->dq_isoftlimit) {
269 			dq->dq_itime = time.tv_sec +
270 			    VFSTOUFS(ITOV(ip)->v_mount)->um_itime[type];
271 			if (ip->i_uid == cred->cr_uid)
272 				uprintf("\n%s: warning, %s %s\n",
273 				    ip->i_fs->fs_fsmnt, quotatypes[type],
274 				    "inode quota exceeded");
275 			return (0);
276 		}
277 		if (time.tv_sec > dq->dq_itime) {
278 			if ((dq->dq_flags & DQ_INODS) == 0 &&
279 			    ip->i_uid == cred->cr_uid) {
280 				uprintf("\n%s: write failed, %s %s\n",
281 				    ip->i_fs->fs_fsmnt, quotatypes[type],
282 				    "inode quota exceeded too long");
283 				dq->dq_flags |= DQ_INODS;
284 			}
285 			return (EDQUOT);
286 		}
287 	}
288 	return (0);
289 }
290 
291 #ifdef DIAGNOSTIC
292 /*
293  * On filesystems with quotas enabled,
294  * it is an error for a file to change size and not
295  * to have a dquot structure associated with it.
296  */
297 chkdquot(ip)
298 	register struct inode *ip;
299 {
300 	struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount);
301 	register int i;
302 
303 	for (i = 0; i < MAXQUOTAS; i++) {
304 		if (ump->um_quotas[i] == NULLVP ||
305 		    (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING)))
306 			continue;
307 		if (ip->i_dquot[i] == NODQUOT) {
308 			vprint("chkdquot: missing dquot", ITOV(ip));
309 			panic("missing dquot");
310 		}
311 	}
312 }
313 #endif /* DIAGNOSTIC */
314 
315 /*
316  * Code to process quotactl commands.
317  */
318 
319 /*
320  * Q_QUOTAON - set up a quota file for a particular file system.
321  */
322 quotaon(p, mp, type, fname)
323 	struct proc *p;
324 	struct mount *mp;
325 	register int type;
326 	caddr_t fname;
327 {
328 	register struct ufsmount *ump = VFSTOUFS(mp);
329 	register struct vnode *vp, **vpp;
330 	struct vnode *nextvp;
331 	struct dquot *dq;
332 	int error;
333 	struct nameidata nd;
334 
335 	vpp = &ump->um_quotas[type];
336 	nd.ni_segflg = UIO_USERSPACE;
337 	nd.ni_dirp = fname;
338 	if (error = vn_open(&nd, p, FREAD|FWRITE, 0))
339 		return (error);
340 	vp = nd.ni_vp;
341 	VOP_UNLOCK(vp);
342 	if (vp->v_type != VREG) {
343 		(void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
344 		return (EACCES);
345 	}
346 	if (vfs_busy(mp)) {
347 		(void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
348 		return (EBUSY);
349 	}
350 	if (*vpp != vp)
351 		quotaoff(p, mp, type);
352 	ump->um_qflags[type] |= QTF_OPENING;
353 	mp->mnt_flag |= MNT_QUOTA;
354 	vp->v_flag |= VSYSTEM;
355 	*vpp = vp;
356 	/*
357 	 * Save the credential of the process that turned on quotas.
358 	 * Set up the time limits for this quota.
359 	 */
360 	crhold(p->p_ucred);
361 	ump->um_cred[type] = p->p_ucred;
362 	ump->um_btime[type] = MAX_DQ_TIME;
363 	ump->um_itime[type] = MAX_IQ_TIME;
364 	if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
365 		if (dq->dq_btime > 0)
366 			ump->um_btime[type] = dq->dq_btime;
367 		if (dq->dq_itime > 0)
368 			ump->um_itime[type] = dq->dq_itime;
369 		dqrele(NULLVP, dq);
370 	}
371 	/*
372 	 * Search vnodes associated with this mount point,
373 	 * adding references to quota file being opened.
374 	 * NB: only need to add dquot's for inodes being modified.
375 	 */
376 again:
377 	for (vp = mp->mnt_mounth; vp; vp = nextvp) {
378 		nextvp = vp->v_mountf;
379 		if (vp->v_writecount == 0)
380 			continue;
381 		if (vget(vp))
382 			goto again;
383 		if (error = getinoquota(VTOI(vp))) {
384 			vput(vp);
385 			break;
386 		}
387 		vput(vp);
388 		if (vp->v_mountf != nextvp || vp->v_mount != mp)
389 			goto again;
390 	}
391 	ump->um_qflags[type] &= ~QTF_OPENING;
392 	if (error)
393 		quotaoff(p, mp, type);
394 	vfs_unbusy(mp);
395 	return (error);
396 }
397 
398 /*
399  * Q_QUOTAOFF - turn off disk quotas for a filesystem.
400  */
401 quotaoff(p, mp, type)
402 	struct proc *p;
403 	struct mount *mp;
404 	register int type;
405 {
406 	register struct vnode *vp;
407 	struct vnode *qvp, *nextvp;
408 	struct ufsmount *ump = VFSTOUFS(mp);
409 	register struct dquot *dq;
410 	register struct inode *ip;
411 	int error;
412 
413 	if ((mp->mnt_flag & MNT_MPBUSY) == 0)
414 		panic("quotaoff: not busy");
415 	if ((qvp = ump->um_quotas[type]) == NULLVP)
416 		return (0);
417 	ump->um_qflags[type] |= QTF_CLOSING;
418 	/*
419 	 * Search vnodes associated with this mount point,
420 	 * deleting any references to quota file being closed.
421 	 */
422 again:
423 	for (vp = mp->mnt_mounth; vp; vp = nextvp) {
424 		nextvp = vp->v_mountf;
425 		if (vget(vp))
426 			goto again;
427 		ip = VTOI(vp);
428 		dq = ip->i_dquot[type];
429 		ip->i_dquot[type] = NODQUOT;
430 		dqrele(vp, dq);
431 		vput(vp);
432 		if (vp->v_mountf != nextvp || vp->v_mount != mp)
433 			goto again;
434 	}
435 	dqflush(qvp);
436 	qvp->v_flag &= ~VSYSTEM;
437 	error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p);
438 	ump->um_quotas[type] = NULLVP;
439 	crfree(ump->um_cred[type]);
440 	ump->um_cred[type] = NOCRED;
441 	ump->um_qflags[type] &= ~QTF_CLOSING;
442 	for (type = 0; type < MAXQUOTAS; type++)
443 		if (ump->um_quotas[type] != NULLVP)
444 			break;
445 	if (type == MAXQUOTAS)
446 		mp->mnt_flag &= ~MNT_QUOTA;
447 	return (error);
448 }
449 
450 /*
451  * Q_GETQUOTA - return current values in a dqblk structure.
452  */
453 getquota(mp, id, type, addr)
454 	struct mount *mp;
455 	u_long id;
456 	int type;
457 	caddr_t addr;
458 {
459 	struct dquot *dq;
460 	int error;
461 
462 	if (error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq))
463 		return (error);
464 	error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk));
465 	dqrele(NULLVP, dq);
466 	return (error);
467 }
468 
469 /*
470  * Q_SETQUOTA - assign an entire dqblk structure.
471  */
472 setquota(mp, id, type, addr)
473 	struct mount *mp;
474 	u_long id;
475 	int type;
476 	caddr_t addr;
477 {
478 	register struct dquot *dq;
479 	struct dquot *ndq;
480 	struct ufsmount *ump = VFSTOUFS(mp);
481 	struct dqblk newlim;
482 	int error;
483 
484 	if (error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk)))
485 		return (error);
486 	if (error = dqget(NULLVP, id, ump, type, &ndq))
487 		return (error);
488 	dq = ndq;
489 	while (dq->dq_flags & DQ_LOCK) {
490 		dq->dq_flags |= DQ_WANT;
491 		sleep((caddr_t)dq, PINOD+1);
492 	}
493 	/*
494 	 * Copy all but the current values.
495 	 * Reset time limit if previously had no soft limit or were
496 	 * under it, but now have a soft limit and are over it.
497 	 */
498 	newlim.dqb_curblocks = dq->dq_curblocks;
499 	newlim.dqb_curinodes = dq->dq_curinodes;
500 	if (dq->dq_id != 0) {
501 		newlim.dqb_btime = dq->dq_btime;
502 		newlim.dqb_itime = dq->dq_itime;
503 	}
504 	if (newlim.dqb_bsoftlimit &&
505 	    dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
506 	    (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
507 		newlim.dqb_btime = time.tv_sec + ump->um_btime[type];
508 	if (newlim.dqb_isoftlimit &&
509 	    dq->dq_curinodes >= newlim.dqb_isoftlimit &&
510 	    (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
511 		newlim.dqb_itime = time.tv_sec + ump->um_itime[type];
512 	dq->dq_dqb = newlim;
513 	if (dq->dq_curblocks < dq->dq_bsoftlimit)
514 		dq->dq_flags &= ~DQ_BLKS;
515 	if (dq->dq_curinodes < dq->dq_isoftlimit)
516 		dq->dq_flags &= ~DQ_INODS;
517 	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
518 	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
519 		dq->dq_flags |= DQ_FAKE;
520 	else
521 		dq->dq_flags &= ~DQ_FAKE;
522 	dq->dq_flags |= DQ_MOD;
523 	dqrele(NULLVP, dq);
524 	return (0);
525 }
526 
527 /*
528  * Q_SETUSE - set current inode and block usage.
529  */
530 setuse(mp, id, type, addr)
531 	struct mount *mp;
532 	u_long id;
533 	int type;
534 	caddr_t addr;
535 {
536 	register struct dquot *dq;
537 	struct ufsmount *ump = VFSTOUFS(mp);
538 	struct dquot *ndq;
539 	struct dqblk usage;
540 	int error;
541 
542 	if (error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk)))
543 		return (error);
544 	if (error = dqget(NULLVP, id, ump, type, &ndq))
545 		return (error);
546 	dq = ndq;
547 	while (dq->dq_flags & DQ_LOCK) {
548 		dq->dq_flags |= DQ_WANT;
549 		sleep((caddr_t)dq, PINOD+1);
550 	}
551 	/*
552 	 * Reset time limit if have a soft limit and were
553 	 * previously under it, but are now over it.
554 	 */
555 	if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
556 	    usage.dqb_curblocks >= dq->dq_bsoftlimit)
557 		dq->dq_btime = time.tv_sec + ump->um_btime[type];
558 	if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
559 	    usage.dqb_curinodes >= dq->dq_isoftlimit)
560 		dq->dq_itime = time.tv_sec + ump->um_itime[type];
561 	dq->dq_curblocks = usage.dqb_curblocks;
562 	dq->dq_curinodes = usage.dqb_curinodes;
563 	if (dq->dq_curblocks < dq->dq_bsoftlimit)
564 		dq->dq_flags &= ~DQ_BLKS;
565 	if (dq->dq_curinodes < dq->dq_isoftlimit)
566 		dq->dq_flags &= ~DQ_INODS;
567 	dq->dq_flags |= DQ_MOD;
568 	dqrele(NULLVP, dq);
569 	return (0);
570 }
571 
572 /*
573  * Q_SYNC - sync quota files to disk.
574  */
575 qsync(mp)
576 	struct mount *mp;
577 {
578 	struct ufsmount *ump = VFSTOUFS(mp);
579 	register struct vnode *vp, *nextvp;
580 	register struct dquot *dq;
581 	register int i;
582 
583 	/*
584 	 * Check if the mount point has any quotas.
585 	 * If not, simply return.
586 	 */
587 	if ((mp->mnt_flag & MNT_MPBUSY) == 0)
588 		panic("qsync: not busy");
589 	for (i = 0; i < MAXQUOTAS; i++)
590 		if (ump->um_quotas[i] != NULLVP)
591 			break;
592 	if (i == MAXQUOTAS)
593 		return (0);
594 	/*
595 	 * Search vnodes associated with this mount point,
596 	 * synchronizing any modified dquot structures.
597 	 */
598 again:
599 	for (vp = mp->mnt_mounth; vp; vp = nextvp) {
600 		nextvp = vp->v_mountf;
601 		if (VOP_ISLOCKED(vp))
602 			continue;
603 		if (vget(vp))
604 			goto again;
605 		for (i = 0; i < MAXQUOTAS; i++) {
606 			dq = VTOI(vp)->i_dquot[i];
607 			if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
608 				dqsync(vp, dq);
609 		}
610 		vput(vp);
611 		if (vp->v_mountf != nextvp || vp->v_mount != mp)
612 			goto again;
613 	}
614 	return (0);
615 }
616 
617 /*
618  * Code pertaining to management of the in-core dquot data structures.
619  */
620 
621 /*
622  * Dquot cache - hash chain headers.
623  */
624 union	dqhead	{
625 	union	dqhead	*dqh_head[2];
626 	struct	dquot	*dqh_chain[2];
627 };
628 #define	dqh_forw	dqh_chain[0]
629 #define	dqh_back	dqh_chain[1]
630 
631 union dqhead *dqhashtbl;
632 long dqhash;
633 
634 /*
635  * Dquot free list.
636  */
637 #define	DQUOTINC	5	/* minimum free dquots desired */
638 struct dquot *dqfreel, **dqback = &dqfreel;
639 long numdquot, desireddquot = DQUOTINC;
640 
641 /*
642  * Initialize the quota system.
643  */
644 dqinit()
645 {
646 	register union dqhead *dhp;
647 	register long dqhashsize;
648 
649 	dqhashsize = roundup((desiredvnodes + 1) * sizeof *dhp / 2,
650 		NBPG * CLSIZE);
651 	dqhashtbl = (union dqhead *)malloc(dqhashsize, M_DQUOT, M_WAITOK);
652 	for (dqhash = 1; dqhash <= dqhashsize / sizeof *dhp; dqhash <<= 1)
653 		/* void */;
654 	dqhash = (dqhash >> 1) - 1;
655 	for (dhp = &dqhashtbl[dqhash]; dhp >= dqhashtbl; dhp--) {
656 		dhp->dqh_head[0] = dhp;
657 		dhp->dqh_head[1] = dhp;
658 	}
659 }
660 
661 /*
662  * Obtain a dquot structure for the specified identifier and quota file
663  * reading the information from the file if necessary.
664  */
665 dqget(vp, id, ump, type, dqp)
666 	struct vnode *vp;
667 	u_long id;
668 	register struct ufsmount *ump;
669 	register int type;
670 	struct dquot **dqp;
671 {
672 	register struct dquot *dq;
673 	register union dqhead *dh;
674 	register struct dquot *dp;
675 	register struct vnode *dqvp;
676 	struct iovec aiov;
677 	struct uio auio;
678 	int error;
679 
680 	dqvp = ump->um_quotas[type];
681 	if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
682 		*dqp = NODQUOT;
683 		return (EINVAL);
684 	}
685 	/*
686 	 * Check the cache first.
687 	 */
688 	dh = &dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash];
689 	for (dq = dh->dqh_forw; dq != (struct dquot *)dh; dq = dq->dq_forw) {
690 		if (dq->dq_id != id ||
691 		    dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
692 			continue;
693 		/*
694 		 * Cache hit with no references.  Take
695 		 * the structure off the free list.
696 		 */
697 		if (dq->dq_cnt == 0) {
698 			dp = dq->dq_freef;
699 			if (dp != NODQUOT)
700 				dp->dq_freeb = dq->dq_freeb;
701 			else
702 				dqback = dq->dq_freeb;
703 			*dq->dq_freeb = dp;
704 		}
705 		DQREF(dq);
706 		*dqp = dq;
707 		return (0);
708 	}
709 	/*
710 	 * Not in cache, allocate a new one.
711 	 */
712 	if (dqfreel == NODQUOT && numdquot < MAXQUOTAS * desiredvnodes)
713 		desireddquot += DQUOTINC;
714 	if (numdquot < desireddquot) {
715 		dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK);
716 		bzero((char *)dq, sizeof *dq);
717 		numdquot++;
718 	} else {
719 		if ((dq = dqfreel) == NULL) {
720 			tablefull("dquot");
721 			*dqp = NODQUOT;
722 			return (EUSERS);
723 		}
724 		if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
725 			panic("free dquot isn't");
726 		if ((dp = dq->dq_freef) != NODQUOT)
727 			dp->dq_freeb = &dqfreel;
728 		else
729 			dqback = &dqfreel;
730 		dqfreel = dp;
731 		dq->dq_freef = NULL;
732 		dq->dq_freeb = NULL;
733 		remque(dq);
734 	}
735 	/*
736 	 * Initialize the contents of the dquot structure.
737 	 */
738 	if (vp != dqvp)
739 		VOP_LOCK(dqvp);
740 	insque(dq, dh);
741 	DQREF(dq);
742 	dq->dq_flags = DQ_LOCK;
743 	dq->dq_id = id;
744 	dq->dq_ump = ump;
745 	dq->dq_type = type;
746 	auio.uio_iov = &aiov;
747 	auio.uio_iovcnt = 1;
748 	aiov.iov_base = (caddr_t)&dq->dq_dqb;
749 	aiov.iov_len = sizeof (struct dqblk);
750 	auio.uio_resid = sizeof (struct dqblk);
751 	auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
752 	auio.uio_segflg = UIO_SYSSPACE;
753 	auio.uio_rw = UIO_READ;
754 	auio.uio_procp = (struct proc *)0;
755 	error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
756 	if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
757 		bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk));
758 	if (vp != dqvp)
759 		VOP_UNLOCK(dqvp);
760 	if (dq->dq_flags & DQ_WANT)
761 		wakeup((caddr_t)dq);
762 	dq->dq_flags = 0;
763 	/*
764 	 * I/O error in reading quota file, release
765 	 * quota structure and reflect problem to caller.
766 	 */
767 	if (error) {
768 		remque(dq);
769 		dq->dq_forw = dq;	/* on a private, unfindable hash list */
770 		dq->dq_back = dq;
771 		dqrele(vp, dq);
772 		*dqp = NODQUOT;
773 		return (error);
774 	}
775 	/*
776 	 * Check for no limit to enforce.
777 	 * Initialize time values if necessary.
778 	 */
779 	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
780 	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
781 		dq->dq_flags |= DQ_FAKE;
782 	if (dq->dq_id != 0) {
783 		if (dq->dq_btime == 0)
784 			dq->dq_btime = time.tv_sec + ump->um_btime[type];
785 		if (dq->dq_itime == 0)
786 			dq->dq_itime = time.tv_sec + ump->um_itime[type];
787 	}
788 	*dqp = dq;
789 	return (0);
790 }
791 
792 /*
793  * Obtain a reference to a dquot.
794  */
795 dqref(dq)
796 	struct dquot *dq;
797 {
798 
799 	dq->dq_cnt++;
800 }
801 
802 /*
803  * Release a reference to a dquot.
804  */
805 dqrele(vp, dq)
806 	struct vnode *vp;
807 	register struct dquot *dq;
808 {
809 
810 	if (dq == NODQUOT)
811 		return;
812 	if (dq->dq_cnt > 1) {
813 		dq->dq_cnt--;
814 		return;
815 	}
816 	if (dq->dq_flags & DQ_MOD)
817 		(void) dqsync(vp, dq);
818 	if (--dq->dq_cnt > 0)
819 		return;
820 	if (dqfreel != NODQUOT) {
821 		*dqback = dq;
822 		dq->dq_freeb = dqback;
823 	} else {
824 		dqfreel = dq;
825 		dq->dq_freeb = &dqfreel;
826 	}
827 	dq->dq_freef = NODQUOT;
828 	dqback = &dq->dq_freef;
829 }
830 
831 /*
832  * Update the disk quota in the quota file.
833  */
834 dqsync(vp, dq)
835 	struct vnode *vp;
836 	register struct dquot *dq;
837 {
838 	struct vnode *dqvp;
839 	struct iovec aiov;
840 	struct uio auio;
841 	int error;
842 
843 	if (dq == NODQUOT)
844 		panic("dqsync: dquot");
845 	if ((dq->dq_flags & DQ_MOD) == 0)
846 		return (0);
847 	if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
848 		panic("dqsync: file");
849 	if (vp != dqvp)
850 		VOP_LOCK(dqvp);
851 	while (dq->dq_flags & DQ_LOCK) {
852 		dq->dq_flags |= DQ_WANT;
853 		sleep((caddr_t)dq, PINOD+2);
854 		if ((dq->dq_flags & DQ_MOD) == 0) {
855 			if (vp != dqvp)
856 				VOP_UNLOCK(dqvp);
857 			return (0);
858 		}
859 	}
860 	dq->dq_flags |= DQ_LOCK;
861 	auio.uio_iov = &aiov;
862 	auio.uio_iovcnt = 1;
863 	aiov.iov_base = (caddr_t)&dq->dq_dqb;
864 	aiov.iov_len = sizeof (struct dqblk);
865 	auio.uio_resid = sizeof (struct dqblk);
866 	auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
867 	auio.uio_segflg = UIO_SYSSPACE;
868 	auio.uio_rw = UIO_WRITE;
869 	auio.uio_procp = (struct proc *)0;
870 	error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
871 	if (auio.uio_resid && error == 0)
872 		error = EIO;
873 	if (dq->dq_flags & DQ_WANT)
874 		wakeup((caddr_t)dq);
875 	dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
876 	if (vp != dqvp)
877 		VOP_UNLOCK(dqvp);
878 	return (error);
879 }
880 
881 /*
882  * Flush all entries from the cache for a particular vnode.
883  */
884 dqflush(vp)
885 	register struct vnode *vp;
886 {
887 	register union dqhead *dh;
888 	register struct dquot *dq, *nextdq;
889 
890 	/*
891 	 * Move all dquot's that used to refer to this quota
892 	 * file off their hash chains (they will eventually
893 	 * fall off the head of the free list and be re-used).
894 	 */
895 	for (dh = &dqhashtbl[dqhash]; dh >= dqhashtbl; dh--) {
896 		for (dq = dh->dqh_forw; dq != (struct dquot *)dh; dq = nextdq) {
897 			nextdq = dq->dq_forw;
898 			if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
899 				continue;
900 			if (dq->dq_cnt)
901 				panic("dqflush: stray dquot");
902 			remque(dq);
903 			dq->dq_forw = dq;
904 			dq->dq_back = dq;
905 			dq->dq_ump = (struct ufsmount *)0;
906 		}
907 	}
908 }
909