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