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