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