xref: /original-bsd/sys/ufs/ufs/ufs_quota.c (revision deff14a8)
1 /*
2  * Copyright (c) 1982, 1986, 1990, 1993
3  *	The Regents of the University of California.  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	8.3 (Berkeley) 08/19/94
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_vnodelist.lh_first; vp != NULL; vp = nextvp) {
387 		nextvp = vp->v_mntvnodes.le_next;
388 		if (vp->v_writecount == 0)
389 			continue;
390 		if (vget(vp, 1))
391 			goto again;
392 		if (error = getinoquota(VTOI(vp))) {
393 			vput(vp);
394 			break;
395 		}
396 		vput(vp);
397 		if (vp->v_mntvnodes.le_next != 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_vnodelist.lh_first; vp != NULL; vp = nextvp) {
434 		nextvp = vp->v_mntvnodes.le_next;
435 		if (vget(vp, 1))
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_mntvnodes.le_next != 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_vnodelist.lh_first; vp != NULL; vp = nextvp) {
614 		nextvp = vp->v_mntvnodes.le_next;
615 		if (VOP_ISLOCKED(vp))
616 			continue;
617 		if (vget(vp, 1))
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_mntvnodes.le_next != 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 #define DQHASH(dqvp, id) \
635 	(&dqhashtbl[((((int)(dqvp)) >> 8) + id) & dqhash])
636 LIST_HEAD(dqhash, dquot) *dqhashtbl;
637 u_long dqhash;
638 
639 /*
640  * Dquot free list.
641  */
642 #define	DQUOTINC	5	/* minimum free dquots desired */
643 TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
644 long numdquot, desireddquot = DQUOTINC;
645 
646 /*
647  * Initialize the quota system.
648  */
649 void
650 dqinit()
651 {
652 
653 	dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
654 	TAILQ_INIT(&dqfreelist);
655 }
656 
657 /*
658  * Obtain a dquot structure for the specified identifier and quota file
659  * reading the information from the file if necessary.
660  */
661 int
662 dqget(vp, id, ump, type, dqp)
663 	struct vnode *vp;
664 	u_long id;
665 	register struct ufsmount *ump;
666 	register int type;
667 	struct dquot **dqp;
668 {
669 	register struct dquot *dq;
670 	struct dqhash *dqh;
671 	register struct vnode *dqvp;
672 	struct iovec aiov;
673 	struct uio auio;
674 	int error;
675 
676 	dqvp = ump->um_quotas[type];
677 	if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
678 		*dqp = NODQUOT;
679 		return (EINVAL);
680 	}
681 	/*
682 	 * Check the cache first.
683 	 */
684 	dqh = DQHASH(dqvp, id);
685 	for (dq = dqh->lh_first; dq; dq = dq->dq_hash.le_next) {
686 		if (dq->dq_id != id ||
687 		    dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
688 			continue;
689 		/*
690 		 * Cache hit with no references.  Take
691 		 * the structure off the free list.
692 		 */
693 		if (dq->dq_cnt == 0)
694 			TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
695 		DQREF(dq);
696 		*dqp = dq;
697 		return (0);
698 	}
699 	/*
700 	 * Not in cache, allocate a new one.
701 	 */
702 	if (dqfreelist.tqh_first == NODQUOT &&
703 	    numdquot < MAXQUOTAS * desiredvnodes)
704 		desireddquot += DQUOTINC;
705 	if (numdquot < desireddquot) {
706 		dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK);
707 		bzero((char *)dq, sizeof *dq);
708 		numdquot++;
709 	} else {
710 		if ((dq = dqfreelist.tqh_first) == NULL) {
711 			tablefull("dquot");
712 			*dqp = NODQUOT;
713 			return (EUSERS);
714 		}
715 		if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
716 			panic("free dquot isn't");
717 		TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
718 		LIST_REMOVE(dq, dq_hash);
719 	}
720 	/*
721 	 * Initialize the contents of the dquot structure.
722 	 */
723 	if (vp != dqvp)
724 		VOP_LOCK(dqvp);
725 	LIST_INSERT_HEAD(dqh, dq, dq_hash);
726 	DQREF(dq);
727 	dq->dq_flags = DQ_LOCK;
728 	dq->dq_id = id;
729 	dq->dq_ump = ump;
730 	dq->dq_type = type;
731 	auio.uio_iov = &aiov;
732 	auio.uio_iovcnt = 1;
733 	aiov.iov_base = (caddr_t)&dq->dq_dqb;
734 	aiov.iov_len = sizeof (struct dqblk);
735 	auio.uio_resid = sizeof (struct dqblk);
736 	auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
737 	auio.uio_segflg = UIO_SYSSPACE;
738 	auio.uio_rw = UIO_READ;
739 	auio.uio_procp = (struct proc *)0;
740 	error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
741 	if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
742 		bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk));
743 	if (vp != dqvp)
744 		VOP_UNLOCK(dqvp);
745 	if (dq->dq_flags & DQ_WANT)
746 		wakeup((caddr_t)dq);
747 	dq->dq_flags = 0;
748 	/*
749 	 * I/O error in reading quota file, release
750 	 * quota structure and reflect problem to caller.
751 	 */
752 	if (error) {
753 		LIST_REMOVE(dq, dq_hash);
754 		dqrele(vp, dq);
755 		*dqp = NODQUOT;
756 		return (error);
757 	}
758 	/*
759 	 * Check for no limit to enforce.
760 	 * Initialize time values if necessary.
761 	 */
762 	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
763 	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
764 		dq->dq_flags |= DQ_FAKE;
765 	if (dq->dq_id != 0) {
766 		if (dq->dq_btime == 0)
767 			dq->dq_btime = time.tv_sec + ump->um_btime[type];
768 		if (dq->dq_itime == 0)
769 			dq->dq_itime = time.tv_sec + ump->um_itime[type];
770 	}
771 	*dqp = dq;
772 	return (0);
773 }
774 
775 /*
776  * Obtain a reference to a dquot.
777  */
778 void
779 dqref(dq)
780 	struct dquot *dq;
781 {
782 
783 	dq->dq_cnt++;
784 }
785 
786 /*
787  * Release a reference to a dquot.
788  */
789 void
790 dqrele(vp, dq)
791 	struct vnode *vp;
792 	register struct dquot *dq;
793 {
794 
795 	if (dq == NODQUOT)
796 		return;
797 	if (dq->dq_cnt > 1) {
798 		dq->dq_cnt--;
799 		return;
800 	}
801 	if (dq->dq_flags & DQ_MOD)
802 		(void) dqsync(vp, dq);
803 	if (--dq->dq_cnt > 0)
804 		return;
805 	TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
806 }
807 
808 /*
809  * Update the disk quota in the quota file.
810  */
811 int
812 dqsync(vp, dq)
813 	struct vnode *vp;
814 	register struct dquot *dq;
815 {
816 	struct vnode *dqvp;
817 	struct iovec aiov;
818 	struct uio auio;
819 	int error;
820 
821 	if (dq == NODQUOT)
822 		panic("dqsync: dquot");
823 	if ((dq->dq_flags & DQ_MOD) == 0)
824 		return (0);
825 	if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
826 		panic("dqsync: file");
827 	if (vp != dqvp)
828 		VOP_LOCK(dqvp);
829 	while (dq->dq_flags & DQ_LOCK) {
830 		dq->dq_flags |= DQ_WANT;
831 		sleep((caddr_t)dq, PINOD+2);
832 		if ((dq->dq_flags & DQ_MOD) == 0) {
833 			if (vp != dqvp)
834 				VOP_UNLOCK(dqvp);
835 			return (0);
836 		}
837 	}
838 	dq->dq_flags |= DQ_LOCK;
839 	auio.uio_iov = &aiov;
840 	auio.uio_iovcnt = 1;
841 	aiov.iov_base = (caddr_t)&dq->dq_dqb;
842 	aiov.iov_len = sizeof (struct dqblk);
843 	auio.uio_resid = sizeof (struct dqblk);
844 	auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
845 	auio.uio_segflg = UIO_SYSSPACE;
846 	auio.uio_rw = UIO_WRITE;
847 	auio.uio_procp = (struct proc *)0;
848 	error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
849 	if (auio.uio_resid && error == 0)
850 		error = EIO;
851 	if (dq->dq_flags & DQ_WANT)
852 		wakeup((caddr_t)dq);
853 	dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
854 	if (vp != dqvp)
855 		VOP_UNLOCK(dqvp);
856 	return (error);
857 }
858 
859 /*
860  * Flush all entries from the cache for a particular vnode.
861  */
862 void
863 dqflush(vp)
864 	register struct vnode *vp;
865 {
866 	register struct dquot *dq, *nextdq;
867 	struct dqhash *dqh;
868 
869 	/*
870 	 * Move all dquot's that used to refer to this quota
871 	 * file off their hash chains (they will eventually
872 	 * fall off the head of the free list and be re-used).
873 	 */
874 	for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
875 		for (dq = dqh->lh_first; dq; dq = nextdq) {
876 			nextdq = dq->dq_hash.le_next;
877 			if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
878 				continue;
879 			if (dq->dq_cnt)
880 				panic("dqflush: stray dquot");
881 			LIST_REMOVE(dq, dq_hash);
882 			dq->dq_ump = (struct ufsmount *)0;
883 		}
884 	}
885 }
886