xref: /original-bsd/sys/ufs/ufs/ufs_quota.c (revision e59fb703)
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.12 (Berkeley) 11/01/91
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 	nd.ni_segflg = UIO_USERSPACE;
347 	nd.ni_dirp = fname;
348 	if (error = vn_open(&nd, p, FREAD|FWRITE, 0))
349 		return (error);
350 	vp = nd.ni_vp;
351 	VOP_UNLOCK(vp);
352 	if (vp->v_type != VREG) {
353 		(void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
354 		return (EACCES);
355 	}
356 	if (vfs_busy(mp)) {
357 		(void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
358 		return (EBUSY);
359 	}
360 	if (*vpp != vp)
361 		quotaoff(p, mp, type);
362 	ump->um_qflags[type] |= QTF_OPENING;
363 	mp->mnt_flag |= MNT_QUOTA;
364 	vp->v_flag |= VSYSTEM;
365 	*vpp = vp;
366 	/*
367 	 * Save the credential of the process that turned on quotas.
368 	 * Set up the time limits for this quota.
369 	 */
370 	crhold(p->p_ucred);
371 	ump->um_cred[type] = p->p_ucred;
372 	ump->um_btime[type] = MAX_DQ_TIME;
373 	ump->um_itime[type] = MAX_IQ_TIME;
374 	if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
375 		if (dq->dq_btime > 0)
376 			ump->um_btime[type] = dq->dq_btime;
377 		if (dq->dq_itime > 0)
378 			ump->um_itime[type] = dq->dq_itime;
379 		dqrele(NULLVP, dq);
380 	}
381 	/*
382 	 * Search vnodes associated with this mount point,
383 	 * adding references to quota file being opened.
384 	 * NB: only need to add dquot's for inodes being modified.
385 	 */
386 again:
387 	for (vp = mp->mnt_mounth; vp; vp = nextvp) {
388 		nextvp = vp->v_mountf;
389 		if (vp->v_writecount == 0)
390 			continue;
391 		if (vget(vp))
392 			goto again;
393 		if (error = getinoquota(VTOI(vp))) {
394 			vput(vp);
395 			break;
396 		}
397 		vput(vp);
398 		if (vp->v_mountf != nextvp || vp->v_mount != mp)
399 			goto again;
400 	}
401 	ump->um_qflags[type] &= ~QTF_OPENING;
402 	if (error)
403 		quotaoff(p, mp, type);
404 	vfs_unbusy(mp);
405 	return (error);
406 }
407 
408 /*
409  * Q_QUOTAOFF - turn off disk quotas for a filesystem.
410  */
411 int
412 quotaoff(p, mp, type)
413 	struct proc *p;
414 	struct mount *mp;
415 	register int type;
416 {
417 	register struct vnode *vp;
418 	struct vnode *qvp, *nextvp;
419 	struct ufsmount *ump = VFSTOUFS(mp);
420 	register struct dquot *dq;
421 	register struct inode *ip;
422 	int error;
423 
424 	if ((mp->mnt_flag & MNT_MPBUSY) == 0)
425 		panic("quotaoff: not busy");
426 	if ((qvp = ump->um_quotas[type]) == NULLVP)
427 		return (0);
428 	ump->um_qflags[type] |= QTF_CLOSING;
429 	/*
430 	 * Search vnodes associated with this mount point,
431 	 * deleting any references to quota file being closed.
432 	 */
433 again:
434 	for (vp = mp->mnt_mounth; vp; vp = nextvp) {
435 		nextvp = vp->v_mountf;
436 		if (vget(vp))
437 			goto again;
438 		ip = VTOI(vp);
439 		dq = ip->i_dquot[type];
440 		ip->i_dquot[type] = NODQUOT;
441 		dqrele(vp, dq);
442 		vput(vp);
443 		if (vp->v_mountf != nextvp || vp->v_mount != mp)
444 			goto again;
445 	}
446 	dqflush(qvp);
447 	qvp->v_flag &= ~VSYSTEM;
448 	error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p);
449 	ump->um_quotas[type] = NULLVP;
450 	crfree(ump->um_cred[type]);
451 	ump->um_cred[type] = NOCRED;
452 	ump->um_qflags[type] &= ~QTF_CLOSING;
453 	for (type = 0; type < MAXQUOTAS; type++)
454 		if (ump->um_quotas[type] != NULLVP)
455 			break;
456 	if (type == MAXQUOTAS)
457 		mp->mnt_flag &= ~MNT_QUOTA;
458 	return (error);
459 }
460 
461 /*
462  * Q_GETQUOTA - return current values in a dqblk structure.
463  */
464 int
465 getquota(mp, id, type, addr)
466 	struct mount *mp;
467 	u_long id;
468 	int type;
469 	caddr_t addr;
470 {
471 	struct dquot *dq;
472 	int error;
473 
474 	if (error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq))
475 		return (error);
476 	error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk));
477 	dqrele(NULLVP, dq);
478 	return (error);
479 }
480 
481 /*
482  * Q_SETQUOTA - assign an entire dqblk structure.
483  */
484 int
485 setquota(mp, id, type, addr)
486 	struct mount *mp;
487 	u_long id;
488 	int type;
489 	caddr_t addr;
490 {
491 	register struct dquot *dq;
492 	struct dquot *ndq;
493 	struct ufsmount *ump = VFSTOUFS(mp);
494 	struct dqblk newlim;
495 	int error;
496 
497 	if (error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk)))
498 		return (error);
499 	if (error = dqget(NULLVP, id, ump, type, &ndq))
500 		return (error);
501 	dq = ndq;
502 	while (dq->dq_flags & DQ_LOCK) {
503 		dq->dq_flags |= DQ_WANT;
504 		sleep((caddr_t)dq, PINOD+1);
505 	}
506 	/*
507 	 * Copy all but the current values.
508 	 * Reset time limit if previously had no soft limit or were
509 	 * under it, but now have a soft limit and are over it.
510 	 */
511 	newlim.dqb_curblocks = dq->dq_curblocks;
512 	newlim.dqb_curinodes = dq->dq_curinodes;
513 	if (dq->dq_id != 0) {
514 		newlim.dqb_btime = dq->dq_btime;
515 		newlim.dqb_itime = dq->dq_itime;
516 	}
517 	if (newlim.dqb_bsoftlimit &&
518 	    dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
519 	    (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
520 		newlim.dqb_btime = time.tv_sec + ump->um_btime[type];
521 	if (newlim.dqb_isoftlimit &&
522 	    dq->dq_curinodes >= newlim.dqb_isoftlimit &&
523 	    (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
524 		newlim.dqb_itime = time.tv_sec + ump->um_itime[type];
525 	dq->dq_dqb = newlim;
526 	if (dq->dq_curblocks < dq->dq_bsoftlimit)
527 		dq->dq_flags &= ~DQ_BLKS;
528 	if (dq->dq_curinodes < dq->dq_isoftlimit)
529 		dq->dq_flags &= ~DQ_INODS;
530 	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
531 	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
532 		dq->dq_flags |= DQ_FAKE;
533 	else
534 		dq->dq_flags &= ~DQ_FAKE;
535 	dq->dq_flags |= DQ_MOD;
536 	dqrele(NULLVP, dq);
537 	return (0);
538 }
539 
540 /*
541  * Q_SETUSE - set current inode and block usage.
542  */
543 int
544 setuse(mp, id, type, addr)
545 	struct mount *mp;
546 	u_long id;
547 	int type;
548 	caddr_t addr;
549 {
550 	register struct dquot *dq;
551 	struct ufsmount *ump = VFSTOUFS(mp);
552 	struct dquot *ndq;
553 	struct dqblk usage;
554 	int error;
555 
556 	if (error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk)))
557 		return (error);
558 	if (error = dqget(NULLVP, id, ump, type, &ndq))
559 		return (error);
560 	dq = ndq;
561 	while (dq->dq_flags & DQ_LOCK) {
562 		dq->dq_flags |= DQ_WANT;
563 		sleep((caddr_t)dq, PINOD+1);
564 	}
565 	/*
566 	 * Reset time limit if have a soft limit and were
567 	 * previously under it, but are now over it.
568 	 */
569 	if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
570 	    usage.dqb_curblocks >= dq->dq_bsoftlimit)
571 		dq->dq_btime = time.tv_sec + ump->um_btime[type];
572 	if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
573 	    usage.dqb_curinodes >= dq->dq_isoftlimit)
574 		dq->dq_itime = time.tv_sec + ump->um_itime[type];
575 	dq->dq_curblocks = usage.dqb_curblocks;
576 	dq->dq_curinodes = usage.dqb_curinodes;
577 	if (dq->dq_curblocks < dq->dq_bsoftlimit)
578 		dq->dq_flags &= ~DQ_BLKS;
579 	if (dq->dq_curinodes < dq->dq_isoftlimit)
580 		dq->dq_flags &= ~DQ_INODS;
581 	dq->dq_flags |= DQ_MOD;
582 	dqrele(NULLVP, dq);
583 	return (0);
584 }
585 
586 /*
587  * Q_SYNC - sync quota files to disk.
588  */
589 int
590 qsync(mp)
591 	struct mount *mp;
592 {
593 	struct ufsmount *ump = VFSTOUFS(mp);
594 	register struct vnode *vp, *nextvp;
595 	register struct dquot *dq;
596 	register int i;
597 
598 	/*
599 	 * Check if the mount point has any quotas.
600 	 * If not, simply return.
601 	 */
602 	if ((mp->mnt_flag & MNT_MPBUSY) == 0)
603 		panic("qsync: not busy");
604 	for (i = 0; i < MAXQUOTAS; i++)
605 		if (ump->um_quotas[i] != NULLVP)
606 			break;
607 	if (i == MAXQUOTAS)
608 		return (0);
609 	/*
610 	 * Search vnodes associated with this mount point,
611 	 * synchronizing any modified dquot structures.
612 	 */
613 again:
614 	for (vp = mp->mnt_mounth; vp; vp = nextvp) {
615 		nextvp = vp->v_mountf;
616 		if (VOP_ISLOCKED(vp))
617 			continue;
618 		if (vget(vp))
619 			goto again;
620 		for (i = 0; i < MAXQUOTAS; i++) {
621 			dq = VTOI(vp)->i_dquot[i];
622 			if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
623 				dqsync(vp, dq);
624 		}
625 		vput(vp);
626 		if (vp->v_mountf != nextvp || vp->v_mount != mp)
627 			goto again;
628 	}
629 	return (0);
630 }
631 
632 /*
633  * Code pertaining to management of the in-core dquot data structures.
634  */
635 
636 /*
637  * Dquot cache - hash chain headers.
638  */
639 union	dqhead	{
640 	union	dqhead	*dqh_head[2];
641 	struct	dquot	*dqh_chain[2];
642 };
643 #define	dqh_forw	dqh_chain[0]
644 #define	dqh_back	dqh_chain[1]
645 
646 union dqhead *dqhashtbl;
647 long dqhash;
648 
649 /*
650  * Dquot free list.
651  */
652 #define	DQUOTINC	5	/* minimum free dquots desired */
653 struct dquot *dqfreel, **dqback = &dqfreel;
654 long numdquot, desireddquot = DQUOTINC;
655 
656 /*
657  * Initialize the quota system.
658  */
659 void
660 dqinit()
661 {
662 	register union dqhead *dhp;
663 	register long dqhashsize;
664 
665 	dqhashsize = roundup((desiredvnodes + 1) * sizeof *dhp / 2,
666 		NBPG * CLSIZE);
667 	dqhashtbl = (union dqhead *)malloc(dqhashsize, M_DQUOT, M_WAITOK);
668 	for (dqhash = 1; dqhash <= dqhashsize / sizeof *dhp; dqhash <<= 1)
669 		/* void */;
670 	dqhash = (dqhash >> 1) - 1;
671 	for (dhp = &dqhashtbl[dqhash]; dhp >= dqhashtbl; dhp--) {
672 		dhp->dqh_head[0] = dhp;
673 		dhp->dqh_head[1] = dhp;
674 	}
675 }
676 
677 /*
678  * Obtain a dquot structure for the specified identifier and quota file
679  * reading the information from the file if necessary.
680  */
681 int
682 dqget(vp, id, ump, type, dqp)
683 	struct vnode *vp;
684 	u_long id;
685 	register struct ufsmount *ump;
686 	register int type;
687 	struct dquot **dqp;
688 {
689 	register struct dquot *dq;
690 	register union dqhead *dh;
691 	register struct dquot *dp;
692 	register struct vnode *dqvp;
693 	struct iovec aiov;
694 	struct uio auio;
695 	int error;
696 
697 	dqvp = ump->um_quotas[type];
698 	if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
699 		*dqp = NODQUOT;
700 		return (EINVAL);
701 	}
702 	/*
703 	 * Check the cache first.
704 	 */
705 	dh = &dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash];
706 	for (dq = dh->dqh_forw; dq != (struct dquot *)dh; dq = dq->dq_forw) {
707 		if (dq->dq_id != id ||
708 		    dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
709 			continue;
710 		/*
711 		 * Cache hit with no references.  Take
712 		 * the structure off the free list.
713 		 */
714 		if (dq->dq_cnt == 0) {
715 			dp = dq->dq_freef;
716 			if (dp != NODQUOT)
717 				dp->dq_freeb = dq->dq_freeb;
718 			else
719 				dqback = dq->dq_freeb;
720 			*dq->dq_freeb = dp;
721 		}
722 		DQREF(dq);
723 		*dqp = dq;
724 		return (0);
725 	}
726 	/*
727 	 * Not in cache, allocate a new one.
728 	 */
729 	if (dqfreel == NODQUOT && numdquot < MAXQUOTAS * desiredvnodes)
730 		desireddquot += DQUOTINC;
731 	if (numdquot < desireddquot) {
732 		dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK);
733 		bzero((char *)dq, sizeof *dq);
734 		numdquot++;
735 	} else {
736 		if ((dq = dqfreel) == NULL) {
737 			tablefull("dquot");
738 			*dqp = NODQUOT;
739 			return (EUSERS);
740 		}
741 		if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
742 			panic("free dquot isn't");
743 		if ((dp = dq->dq_freef) != NODQUOT)
744 			dp->dq_freeb = &dqfreel;
745 		else
746 			dqback = &dqfreel;
747 		dqfreel = dp;
748 		dq->dq_freef = NULL;
749 		dq->dq_freeb = NULL;
750 		remque(dq);
751 	}
752 	/*
753 	 * Initialize the contents of the dquot structure.
754 	 */
755 	if (vp != dqvp)
756 		VOP_LOCK(dqvp);
757 	insque(dq, dh);
758 	DQREF(dq);
759 	dq->dq_flags = DQ_LOCK;
760 	dq->dq_id = id;
761 	dq->dq_ump = ump;
762 	dq->dq_type = type;
763 	auio.uio_iov = &aiov;
764 	auio.uio_iovcnt = 1;
765 	aiov.iov_base = (caddr_t)&dq->dq_dqb;
766 	aiov.iov_len = sizeof (struct dqblk);
767 	auio.uio_resid = sizeof (struct dqblk);
768 	auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
769 	auio.uio_segflg = UIO_SYSSPACE;
770 	auio.uio_rw = UIO_READ;
771 	auio.uio_procp = (struct proc *)0;
772 	error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
773 	if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
774 		bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk));
775 	if (vp != dqvp)
776 		VOP_UNLOCK(dqvp);
777 	if (dq->dq_flags & DQ_WANT)
778 		wakeup((caddr_t)dq);
779 	dq->dq_flags = 0;
780 	/*
781 	 * I/O error in reading quota file, release
782 	 * quota structure and reflect problem to caller.
783 	 */
784 	if (error) {
785 		remque(dq);
786 		dq->dq_forw = dq;	/* on a private, unfindable hash list */
787 		dq->dq_back = dq;
788 		dqrele(vp, dq);
789 		*dqp = NODQUOT;
790 		return (error);
791 	}
792 	/*
793 	 * Check for no limit to enforce.
794 	 * Initialize time values if necessary.
795 	 */
796 	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
797 	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
798 		dq->dq_flags |= DQ_FAKE;
799 	if (dq->dq_id != 0) {
800 		if (dq->dq_btime == 0)
801 			dq->dq_btime = time.tv_sec + ump->um_btime[type];
802 		if (dq->dq_itime == 0)
803 			dq->dq_itime = time.tv_sec + ump->um_itime[type];
804 	}
805 	*dqp = dq;
806 	return (0);
807 }
808 
809 /*
810  * Obtain a reference to a dquot.
811  */
812 void
813 dqref(dq)
814 	struct dquot *dq;
815 {
816 
817 	dq->dq_cnt++;
818 }
819 
820 /*
821  * Release a reference to a dquot.
822  */
823 void
824 dqrele(vp, dq)
825 	struct vnode *vp;
826 	register struct dquot *dq;
827 {
828 
829 	if (dq == NODQUOT)
830 		return;
831 	if (dq->dq_cnt > 1) {
832 		dq->dq_cnt--;
833 		return;
834 	}
835 	if (dq->dq_flags & DQ_MOD)
836 		(void) dqsync(vp, dq);
837 	if (--dq->dq_cnt > 0)
838 		return;
839 	if (dqfreel != NODQUOT) {
840 		*dqback = dq;
841 		dq->dq_freeb = dqback;
842 	} else {
843 		dqfreel = dq;
844 		dq->dq_freeb = &dqfreel;
845 	}
846 	dq->dq_freef = NODQUOT;
847 	dqback = &dq->dq_freef;
848 }
849 
850 /*
851  * Update the disk quota in the quota file.
852  */
853 int
854 dqsync(vp, dq)
855 	struct vnode *vp;
856 	register struct dquot *dq;
857 {
858 	struct vnode *dqvp;
859 	struct iovec aiov;
860 	struct uio auio;
861 	int error;
862 
863 	if (dq == NODQUOT)
864 		panic("dqsync: dquot");
865 	if ((dq->dq_flags & DQ_MOD) == 0)
866 		return (0);
867 	if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
868 		panic("dqsync: file");
869 	if (vp != dqvp)
870 		VOP_LOCK(dqvp);
871 	while (dq->dq_flags & DQ_LOCK) {
872 		dq->dq_flags |= DQ_WANT;
873 		sleep((caddr_t)dq, PINOD+2);
874 		if ((dq->dq_flags & DQ_MOD) == 0) {
875 			if (vp != dqvp)
876 				VOP_UNLOCK(dqvp);
877 			return (0);
878 		}
879 	}
880 	dq->dq_flags |= DQ_LOCK;
881 	auio.uio_iov = &aiov;
882 	auio.uio_iovcnt = 1;
883 	aiov.iov_base = (caddr_t)&dq->dq_dqb;
884 	aiov.iov_len = sizeof (struct dqblk);
885 	auio.uio_resid = sizeof (struct dqblk);
886 	auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
887 	auio.uio_segflg = UIO_SYSSPACE;
888 	auio.uio_rw = UIO_WRITE;
889 	auio.uio_procp = (struct proc *)0;
890 	error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
891 	if (auio.uio_resid && error == 0)
892 		error = EIO;
893 	if (dq->dq_flags & DQ_WANT)
894 		wakeup((caddr_t)dq);
895 	dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
896 	if (vp != dqvp)
897 		VOP_UNLOCK(dqvp);
898 	return (error);
899 }
900 
901 /*
902  * Flush all entries from the cache for a particular vnode.
903  */
904 void
905 dqflush(vp)
906 	register struct vnode *vp;
907 {
908 	register union dqhead *dh;
909 	register struct dquot *dq, *nextdq;
910 
911 	/*
912 	 * Move all dquot's that used to refer to this quota
913 	 * file off their hash chains (they will eventually
914 	 * fall off the head of the free list and be re-used).
915 	 */
916 	for (dh = &dqhashtbl[dqhash]; dh >= dqhashtbl; dh--) {
917 		for (dq = dh->dqh_forw; dq != (struct dquot *)dh; dq = nextdq) {
918 			nextdq = dq->dq_forw;
919 			if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
920 				continue;
921 			if (dq->dq_cnt)
922 				panic("dqflush: stray dquot");
923 			remque(dq);
924 			dq->dq_forw = dq;
925 			dq->dq_back = dq;
926 			dq->dq_ump = (struct ufsmount *)0;
927 		}
928 	}
929 }
930