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