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