ufs_quota.c revision 59794
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 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	@(#)ufs_quota.c	8.5 (Berkeley) 5/20/95
37 * $FreeBSD: head/sys/ufs/ufs/ufs_quota.c 59794 2000-04-30 18:52:11Z phk $
38 */
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/kernel.h>
43#include <sys/namei.h>
44#include <sys/malloc.h>
45#include <sys/fcntl.h>
46#include <sys/proc.h>
47#include <sys/vnode.h>
48#include <sys/mount.h>
49
50#include <ufs/ufs/extattr.h>
51#include <ufs/ufs/quota.h>
52#include <ufs/ufs/inode.h>
53#include <ufs/ufs/ufsmount.h>
54
55static MALLOC_DEFINE(M_DQUOT, "UFS quota", "UFS quota entries");
56
57/*
58 * Quota name to error message mapping.
59 */
60static char *quotatypes[] = INITQFNAMES;
61
62static int chkdqchg __P((struct inode *, long, struct ucred *, int));
63static int chkiqchg __P((struct inode *, long, struct ucred *, int));
64static int dqget __P((struct vnode *,
65		u_long, struct ufsmount *, int, struct dquot **));
66static int dqsync __P((struct vnode *, struct dquot *));
67static void dqflush __P((struct vnode *));
68
69#ifdef DIAGNOSTIC
70static void dqref __P((struct dquot *));
71static void chkdquot __P((struct inode *));
72#endif
73
74/*
75 * Set up the quotas for an inode.
76 *
77 * This routine completely defines the semantics of quotas.
78 * If other criterion want to be used to establish quotas, the
79 * MAXQUOTAS value in quotas.h should be increased, and the
80 * additional dquots set up here.
81 */
82int
83getinoquota(ip)
84	register struct inode *ip;
85{
86	struct ufsmount *ump;
87	struct vnode *vp = ITOV(ip);
88	int error;
89
90	ump = VFSTOUFS(vp->v_mount);
91	/*
92	 * Set up the user quota based on file uid.
93	 * EINVAL means that quotas are not enabled.
94	 */
95	if (ip->i_dquot[USRQUOTA] == NODQUOT &&
96	    (error =
97		dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) &&
98	    error != EINVAL)
99		return (error);
100	/*
101	 * Set up the group quota based on file gid.
102	 * EINVAL means that quotas are not enabled.
103	 */
104	if (ip->i_dquot[GRPQUOTA] == NODQUOT &&
105	    (error =
106		dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&
107	    error != EINVAL)
108		return (error);
109	return (0);
110}
111
112/*
113 * Update disk usage, and take corrective action.
114 */
115int
116chkdq(ip, change, cred, flags)
117	register struct inode *ip;
118	long change;
119	struct ucred *cred;
120	int flags;
121{
122	register struct dquot *dq;
123	register int i;
124	int ncurblocks, error;
125
126#ifdef DIAGNOSTIC
127	if ((flags & CHOWN) == 0)
128		chkdquot(ip);
129#endif
130	if (change == 0)
131		return (0);
132	if (change < 0) {
133		for (i = 0; i < MAXQUOTAS; i++) {
134			if ((dq = ip->i_dquot[i]) == NODQUOT)
135				continue;
136			while (dq->dq_flags & DQ_LOCK) {
137				dq->dq_flags |= DQ_WANT;
138				(void) tsleep((caddr_t)dq, PINOD+1, "chkdq1", 0);
139			}
140			ncurblocks = dq->dq_curblocks + change;
141			if (ncurblocks >= 0)
142				dq->dq_curblocks = ncurblocks;
143			else
144				dq->dq_curblocks = 0;
145			dq->dq_flags &= ~DQ_BLKS;
146			dq->dq_flags |= DQ_MOD;
147		}
148		return (0);
149	}
150	if ((flags & FORCE) == 0 && cred->cr_uid != 0) {
151		for (i = 0; i < MAXQUOTAS; i++) {
152			if ((dq = ip->i_dquot[i]) == NODQUOT)
153				continue;
154			error = chkdqchg(ip, change, cred, i);
155			if (error)
156				return (error);
157		}
158	}
159	for (i = 0; i < MAXQUOTAS; i++) {
160		if ((dq = ip->i_dquot[i]) == NODQUOT)
161			continue;
162		while (dq->dq_flags & DQ_LOCK) {
163			dq->dq_flags |= DQ_WANT;
164			(void) tsleep((caddr_t)dq, PINOD+1, "chkdq2", 0);
165		}
166		/* Reset timer when crossing soft limit */
167		if (dq->dq_curblocks + change >= dq->dq_bsoftlimit &&
168		    dq->dq_curblocks < dq->dq_bsoftlimit)
169			dq->dq_btime = time_second +
170			    VFSTOUFS(ITOV(ip)->v_mount)->um_btime[i];
171		dq->dq_curblocks += change;
172		dq->dq_flags |= DQ_MOD;
173	}
174	return (0);
175}
176
177/*
178 * Check for a valid change to a users allocation.
179 * Issue an error message if appropriate.
180 */
181static int
182chkdqchg(ip, change, cred, type)
183	struct inode *ip;
184	long change;
185	struct ucred *cred;
186	int type;
187{
188	register struct dquot *dq = ip->i_dquot[type];
189	long ncurblocks = dq->dq_curblocks + change;
190
191	/*
192	 * If user would exceed their hard limit, disallow space allocation.
193	 */
194	if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
195		if ((dq->dq_flags & DQ_BLKS) == 0 &&
196		    ip->i_uid == cred->cr_uid) {
197			uprintf("\n%s: write failed, %s disk limit reached\n",
198			    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
199			    quotatypes[type]);
200			dq->dq_flags |= DQ_BLKS;
201		}
202		return (EDQUOT);
203	}
204	/*
205	 * If user is over their soft limit for too long, disallow space
206	 * allocation. Reset time limit as they cross their soft limit.
207	 */
208	if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
209		if (dq->dq_curblocks < dq->dq_bsoftlimit) {
210			dq->dq_btime = time_second +
211			    VFSTOUFS(ITOV(ip)->v_mount)->um_btime[type];
212			if (ip->i_uid == cred->cr_uid)
213				uprintf("\n%s: warning, %s %s\n",
214				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
215				    quotatypes[type], "disk quota exceeded");
216			return (0);
217		}
218		if (time_second > dq->dq_btime) {
219			if ((dq->dq_flags & DQ_BLKS) == 0 &&
220			    ip->i_uid == cred->cr_uid) {
221				uprintf("\n%s: write failed, %s %s\n",
222				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
223				    quotatypes[type],
224				    "disk quota exceeded for too long");
225				dq->dq_flags |= DQ_BLKS;
226			}
227			return (EDQUOT);
228		}
229	}
230	return (0);
231}
232
233/*
234 * Check the inode limit, applying corrective action.
235 */
236int
237chkiq(ip, change, cred, flags)
238	register struct inode *ip;
239	long change;
240	struct ucred *cred;
241	int flags;
242{
243	register struct dquot *dq;
244	register int i;
245	int ncurinodes, error;
246
247#ifdef DIAGNOSTIC
248	if ((flags & CHOWN) == 0)
249		chkdquot(ip);
250#endif
251	if (change == 0)
252		return (0);
253	if (change < 0) {
254		for (i = 0; i < MAXQUOTAS; i++) {
255			if ((dq = ip->i_dquot[i]) == NODQUOT)
256				continue;
257			while (dq->dq_flags & DQ_LOCK) {
258				dq->dq_flags |= DQ_WANT;
259				(void) tsleep((caddr_t)dq, PINOD+1, "chkiq1", 0);
260			}
261			ncurinodes = dq->dq_curinodes + change;
262			if (ncurinodes >= 0)
263				dq->dq_curinodes = ncurinodes;
264			else
265				dq->dq_curinodes = 0;
266			dq->dq_flags &= ~DQ_INODS;
267			dq->dq_flags |= DQ_MOD;
268		}
269		return (0);
270	}
271	if ((flags & FORCE) == 0 && cred->cr_uid != 0) {
272		for (i = 0; i < MAXQUOTAS; i++) {
273			if ((dq = ip->i_dquot[i]) == NODQUOT)
274				continue;
275			error = chkiqchg(ip, change, cred, i);
276			if (error)
277				return (error);
278		}
279	}
280	for (i = 0; i < MAXQUOTAS; i++) {
281		if ((dq = ip->i_dquot[i]) == NODQUOT)
282			continue;
283		while (dq->dq_flags & DQ_LOCK) {
284			dq->dq_flags |= DQ_WANT;
285			(void) tsleep((caddr_t)dq, PINOD+1, "chkiq2", 0);
286		}
287		/* Reset timer when crossing soft limit */
288		if (dq->dq_curinodes + change >= dq->dq_isoftlimit &&
289		    dq->dq_curinodes < dq->dq_isoftlimit)
290			dq->dq_itime = time_second +
291			    VFSTOUFS(ITOV(ip)->v_mount)->um_itime[i];
292		dq->dq_curinodes += change;
293		dq->dq_flags |= DQ_MOD;
294	}
295	return (0);
296}
297
298/*
299 * Check for a valid change to a users allocation.
300 * Issue an error message if appropriate.
301 */
302static int
303chkiqchg(ip, change, cred, type)
304	struct inode *ip;
305	long change;
306	struct ucred *cred;
307	int type;
308{
309	register struct dquot *dq = ip->i_dquot[type];
310	long ncurinodes = dq->dq_curinodes + change;
311
312	/*
313	 * If user would exceed their hard limit, disallow inode allocation.
314	 */
315	if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
316		if ((dq->dq_flags & DQ_INODS) == 0 &&
317		    ip->i_uid == cred->cr_uid) {
318			uprintf("\n%s: write failed, %s inode limit reached\n",
319			    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
320			    quotatypes[type]);
321			dq->dq_flags |= DQ_INODS;
322		}
323		return (EDQUOT);
324	}
325	/*
326	 * If user is over their soft limit for too long, disallow inode
327	 * allocation. Reset time limit as they cross their soft limit.
328	 */
329	if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
330		if (dq->dq_curinodes < dq->dq_isoftlimit) {
331			dq->dq_itime = time_second +
332			    VFSTOUFS(ITOV(ip)->v_mount)->um_itime[type];
333			if (ip->i_uid == cred->cr_uid)
334				uprintf("\n%s: warning, %s %s\n",
335				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
336				    quotatypes[type], "inode quota exceeded");
337			return (0);
338		}
339		if (time_second > dq->dq_itime) {
340			if ((dq->dq_flags & DQ_INODS) == 0 &&
341			    ip->i_uid == cred->cr_uid) {
342				uprintf("\n%s: write failed, %s %s\n",
343				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
344				    quotatypes[type],
345				    "inode quota exceeded for too long");
346				dq->dq_flags |= DQ_INODS;
347			}
348			return (EDQUOT);
349		}
350	}
351	return (0);
352}
353
354#ifdef DIAGNOSTIC
355/*
356 * On filesystems with quotas enabled, it is an error for a file to change
357 * size and not to have a dquot structure associated with it.
358 */
359static void
360chkdquot(ip)
361	register struct inode *ip;
362{
363	struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount);
364	register int i;
365
366	for (i = 0; i < MAXQUOTAS; i++) {
367		if (ump->um_quotas[i] == NULLVP ||
368		    (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING)))
369			continue;
370		if (ip->i_dquot[i] == NODQUOT) {
371			vprint("chkdquot: missing dquot", ITOV(ip));
372			panic("chkdquot: missing dquot");
373		}
374	}
375}
376#endif
377
378/*
379 * Code to process quotactl commands.
380 */
381
382/*
383 * Q_QUOTAON - set up a quota file for a particular file system.
384 */
385int
386quotaon(p, mp, type, fname)
387	struct proc *p;
388	struct mount *mp;
389	register int type;
390	caddr_t fname;
391{
392	struct ufsmount *ump = VFSTOUFS(mp);
393	struct vnode *vp, **vpp;
394	struct vnode *nextvp;
395	struct dquot *dq;
396	int error;
397	struct nameidata nd;
398
399	vpp = &ump->um_quotas[type];
400	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, p);
401	error = vn_open(&nd, FREAD|FWRITE, 0);
402	if (error)
403		return (error);
404	NDFREE(&nd, NDF_ONLY_PNBUF);
405	vp = nd.ni_vp;
406	VOP_UNLOCK(vp, 0, p);
407	if (vp->v_type != VREG) {
408		(void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
409		return (EACCES);
410	}
411	if (*vpp != vp)
412		quotaoff(p, mp, type);
413	ump->um_qflags[type] |= QTF_OPENING;
414	mp->mnt_flag |= MNT_QUOTA;
415	vp->v_flag |= VSYSTEM;
416	*vpp = vp;
417	/*
418	 * Save the credential of the process that turned on quotas.
419	 * Set up the time limits for this quota.
420	 */
421	crhold(p->p_ucred);
422	ump->um_cred[type] = p->p_ucred;
423	ump->um_btime[type] = MAX_DQ_TIME;
424	ump->um_itime[type] = MAX_IQ_TIME;
425	if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
426		if (dq->dq_btime > 0)
427			ump->um_btime[type] = dq->dq_btime;
428		if (dq->dq_itime > 0)
429			ump->um_itime[type] = dq->dq_itime;
430		dqrele(NULLVP, dq);
431	}
432	/*
433	 * Search vnodes associated with this mount point,
434	 * adding references to quota file being opened.
435	 * NB: only need to add dquot's for inodes being modified.
436	 */
437again:
438	for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {
439		nextvp = vp->v_mntvnodes.le_next;
440		if (vp->v_type == VNON || vp->v_writecount == 0)
441			continue;
442		if (vget(vp, LK_EXCLUSIVE, p))
443			goto again;
444		error = getinoquota(VTOI(vp));
445		if (error) {
446			vput(vp);
447			break;
448		}
449		vput(vp);
450		if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp)
451			goto again;
452	}
453	ump->um_qflags[type] &= ~QTF_OPENING;
454	if (error)
455		quotaoff(p, mp, type);
456	return (error);
457}
458
459/*
460 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
461 */
462int
463quotaoff(p, mp, type)
464	struct proc *p;
465	struct mount *mp;
466	register int type;
467{
468	struct vnode *vp;
469	struct vnode *qvp, *nextvp;
470	struct ufsmount *ump = VFSTOUFS(mp);
471	struct dquot *dq;
472	struct inode *ip;
473	int error;
474
475	if ((qvp = ump->um_quotas[type]) == NULLVP)
476		return (0);
477	ump->um_qflags[type] |= QTF_CLOSING;
478	/*
479	 * Search vnodes associated with this mount point,
480	 * deleting any references to quota file being closed.
481	 */
482again:
483	for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {
484		nextvp = vp->v_mntvnodes.le_next;
485		if (vp->v_type == VNON)
486			continue;
487		if (vget(vp, LK_EXCLUSIVE, p))
488			goto again;
489		ip = VTOI(vp);
490		dq = ip->i_dquot[type];
491		ip->i_dquot[type] = NODQUOT;
492		dqrele(vp, dq);
493		vput(vp);
494		if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp)
495			goto again;
496	}
497	dqflush(qvp);
498	qvp->v_flag &= ~VSYSTEM;
499	error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p);
500	ump->um_quotas[type] = NULLVP;
501	crfree(ump->um_cred[type]);
502	ump->um_cred[type] = NOCRED;
503	ump->um_qflags[type] &= ~QTF_CLOSING;
504	for (type = 0; type < MAXQUOTAS; type++)
505		if (ump->um_quotas[type] != NULLVP)
506			break;
507	if (type == MAXQUOTAS)
508		mp->mnt_flag &= ~MNT_QUOTA;
509	return (error);
510}
511
512/*
513 * Q_GETQUOTA - return current values in a dqblk structure.
514 */
515int
516getquota(mp, id, type, addr)
517	struct mount *mp;
518	u_long id;
519	int type;
520	caddr_t addr;
521{
522	struct dquot *dq;
523	int error;
524
525	error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq);
526	if (error)
527		return (error);
528	error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk));
529	dqrele(NULLVP, dq);
530	return (error);
531}
532
533/*
534 * Q_SETQUOTA - assign an entire dqblk structure.
535 */
536int
537setquota(mp, id, type, addr)
538	struct mount *mp;
539	u_long id;
540	int type;
541	caddr_t addr;
542{
543	register struct dquot *dq;
544	struct dquot *ndq;
545	struct ufsmount *ump = VFSTOUFS(mp);
546	struct dqblk newlim;
547	int error;
548
549	error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk));
550	if (error)
551		return (error);
552	error = dqget(NULLVP, id, ump, type, &ndq);
553	if (error)
554		return (error);
555	dq = ndq;
556	while (dq->dq_flags & DQ_LOCK) {
557		dq->dq_flags |= DQ_WANT;
558		(void) tsleep((caddr_t)dq, PINOD+1, "setqta", 0);
559	}
560	/*
561	 * Copy all but the current values.
562	 * Reset time limit if previously had no soft limit or were
563	 * under it, but now have a soft limit and are over it.
564	 */
565	newlim.dqb_curblocks = dq->dq_curblocks;
566	newlim.dqb_curinodes = dq->dq_curinodes;
567	if (dq->dq_id != 0) {
568		newlim.dqb_btime = dq->dq_btime;
569		newlim.dqb_itime = dq->dq_itime;
570	}
571	if (newlim.dqb_bsoftlimit &&
572	    dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
573	    (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
574		newlim.dqb_btime = time_second + ump->um_btime[type];
575	if (newlim.dqb_isoftlimit &&
576	    dq->dq_curinodes >= newlim.dqb_isoftlimit &&
577	    (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
578		newlim.dqb_itime = time_second + ump->um_itime[type];
579	dq->dq_dqb = newlim;
580	if (dq->dq_curblocks < dq->dq_bsoftlimit)
581		dq->dq_flags &= ~DQ_BLKS;
582	if (dq->dq_curinodes < dq->dq_isoftlimit)
583		dq->dq_flags &= ~DQ_INODS;
584	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
585	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
586		dq->dq_flags |= DQ_FAKE;
587	else
588		dq->dq_flags &= ~DQ_FAKE;
589	dq->dq_flags |= DQ_MOD;
590	dqrele(NULLVP, dq);
591	return (0);
592}
593
594/*
595 * Q_SETUSE - set current inode and block usage.
596 */
597int
598setuse(mp, id, type, addr)
599	struct mount *mp;
600	u_long id;
601	int type;
602	caddr_t addr;
603{
604	register struct dquot *dq;
605	struct ufsmount *ump = VFSTOUFS(mp);
606	struct dquot *ndq;
607	struct dqblk usage;
608	int error;
609
610	error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk));
611	if (error)
612		return (error);
613	error = dqget(NULLVP, id, ump, type, &ndq);
614	if (error)
615		return (error);
616	dq = ndq;
617	while (dq->dq_flags & DQ_LOCK) {
618		dq->dq_flags |= DQ_WANT;
619		(void) tsleep((caddr_t)dq, PINOD+1, "setuse", 0);
620	}
621	/*
622	 * Reset time limit if have a soft limit and were
623	 * previously under it, but are now over it.
624	 */
625	if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
626	    usage.dqb_curblocks >= dq->dq_bsoftlimit)
627		dq->dq_btime = time_second + ump->um_btime[type];
628	if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
629	    usage.dqb_curinodes >= dq->dq_isoftlimit)
630		dq->dq_itime = time_second + ump->um_itime[type];
631	dq->dq_curblocks = usage.dqb_curblocks;
632	dq->dq_curinodes = usage.dqb_curinodes;
633	if (dq->dq_curblocks < dq->dq_bsoftlimit)
634		dq->dq_flags &= ~DQ_BLKS;
635	if (dq->dq_curinodes < dq->dq_isoftlimit)
636		dq->dq_flags &= ~DQ_INODS;
637	dq->dq_flags |= DQ_MOD;
638	dqrele(NULLVP, dq);
639	return (0);
640}
641
642/*
643 * Q_SYNC - sync quota files to disk.
644 */
645int
646qsync(mp)
647	struct mount *mp;
648{
649	struct ufsmount *ump = VFSTOUFS(mp);
650	struct proc *p = curproc;		/* XXX */
651	struct vnode *vp, *nextvp;
652	struct dquot *dq;
653	int i, error;
654
655	/*
656	 * Check if the mount point has any quotas.
657	 * If not, simply return.
658	 */
659	for (i = 0; i < MAXQUOTAS; i++)
660		if (ump->um_quotas[i] != NULLVP)
661			break;
662	if (i == MAXQUOTAS)
663		return (0);
664	/*
665	 * Search vnodes associated with this mount point,
666	 * synchronizing any modified dquot structures.
667	 */
668	simple_lock(&mntvnode_slock);
669again:
670	for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {
671		if (vp->v_mount != mp)
672			goto again;
673		nextvp = vp->v_mntvnodes.le_next;
674		if (vp->v_type == VNON)
675			continue;
676		simple_lock(&vp->v_interlock);
677		simple_unlock(&mntvnode_slock);
678		error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK, p);
679		if (error) {
680			simple_lock(&mntvnode_slock);
681			if (error == ENOENT)
682				goto again;
683			continue;
684		}
685		for (i = 0; i < MAXQUOTAS; i++) {
686			dq = VTOI(vp)->i_dquot[i];
687			if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
688				dqsync(vp, dq);
689		}
690		vput(vp);
691		simple_lock(&mntvnode_slock);
692		if (vp->v_mntvnodes.le_next != nextvp)
693			goto again;
694	}
695	simple_unlock(&mntvnode_slock);
696	return (0);
697}
698
699/*
700 * Code pertaining to management of the in-core dquot data structures.
701 */
702#define DQHASH(dqvp, id) \
703	(&dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & dqhash])
704static LIST_HEAD(dqhash, dquot) *dqhashtbl;
705static u_long dqhash;
706
707/*
708 * Dquot free list.
709 */
710#define	DQUOTINC	5	/* minimum free dquots desired */
711static TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
712static long numdquot, desireddquot = DQUOTINC;
713
714/*
715 * Initialize the quota system.
716 */
717void
718dqinit()
719{
720
721	dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
722	TAILQ_INIT(&dqfreelist);
723}
724
725/*
726 * Obtain a dquot structure for the specified identifier and quota file
727 * reading the information from the file if necessary.
728 */
729static int
730dqget(vp, id, ump, type, dqp)
731	struct vnode *vp;
732	u_long id;
733	register struct ufsmount *ump;
734	register int type;
735	struct dquot **dqp;
736{
737	struct proc *p = curproc;		/* XXX */
738	struct dquot *dq;
739	struct dqhash *dqh;
740	struct vnode *dqvp;
741	struct iovec aiov;
742	struct uio auio;
743	int error;
744
745	dqvp = ump->um_quotas[type];
746	if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
747		*dqp = NODQUOT;
748		return (EINVAL);
749	}
750	/*
751	 * Check the cache first.
752	 */
753	dqh = DQHASH(dqvp, id);
754	for (dq = dqh->lh_first; dq; dq = dq->dq_hash.le_next) {
755		if (dq->dq_id != id ||
756		    dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
757			continue;
758		/*
759		 * Cache hit with no references.  Take
760		 * the structure off the free list.
761		 */
762		if (dq->dq_cnt == 0)
763			TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
764		DQREF(dq);
765		*dqp = dq;
766		return (0);
767	}
768	/*
769	 * Not in cache, allocate a new one.
770	 */
771	if (dqfreelist.tqh_first == NODQUOT &&
772	    numdquot < MAXQUOTAS * desiredvnodes)
773		desireddquot += DQUOTINC;
774	if (numdquot < desireddquot) {
775		dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK);
776		bzero((char *)dq, sizeof *dq);
777		numdquot++;
778	} else {
779		if ((dq = dqfreelist.tqh_first) == NULL) {
780			tablefull("dquot");
781			*dqp = NODQUOT;
782			return (EUSERS);
783		}
784		if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
785			panic("dqget: free dquot isn't");
786		TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
787		LIST_REMOVE(dq, dq_hash);
788	}
789	/*
790	 * Initialize the contents of the dquot structure.
791	 */
792	if (vp != dqvp)
793		vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p);
794	LIST_INSERT_HEAD(dqh, dq, dq_hash);
795	DQREF(dq);
796	dq->dq_flags = DQ_LOCK;
797	dq->dq_id = id;
798	dq->dq_ump = ump;
799	dq->dq_type = type;
800	auio.uio_iov = &aiov;
801	auio.uio_iovcnt = 1;
802	aiov.iov_base = (caddr_t)&dq->dq_dqb;
803	aiov.iov_len = sizeof (struct dqblk);
804	auio.uio_resid = sizeof (struct dqblk);
805	auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
806	auio.uio_segflg = UIO_SYSSPACE;
807	auio.uio_rw = UIO_READ;
808	auio.uio_procp = (struct proc *)0;
809	error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
810	if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
811		bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk));
812	if (vp != dqvp)
813		VOP_UNLOCK(dqvp, 0, p);
814	if (dq->dq_flags & DQ_WANT)
815		wakeup((caddr_t)dq);
816	dq->dq_flags = 0;
817	/*
818	 * I/O error in reading quota file, release
819	 * quota structure and reflect problem to caller.
820	 */
821	if (error) {
822		LIST_REMOVE(dq, dq_hash);
823		dqrele(vp, dq);
824		*dqp = NODQUOT;
825		return (error);
826	}
827	/*
828	 * Check for no limit to enforce.
829	 * Initialize time values if necessary.
830	 */
831	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
832	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
833		dq->dq_flags |= DQ_FAKE;
834	if (dq->dq_id != 0) {
835		if (dq->dq_btime == 0)
836			dq->dq_btime = time_second + ump->um_btime[type];
837		if (dq->dq_itime == 0)
838			dq->dq_itime = time_second + ump->um_itime[type];
839	}
840	*dqp = dq;
841	return (0);
842}
843
844#ifdef DIAGNOSTIC
845/*
846 * Obtain a reference to a dquot.
847 */
848static void
849dqref(dq)
850	struct dquot *dq;
851{
852
853	dq->dq_cnt++;
854}
855#endif
856
857/*
858 * Release a reference to a dquot.
859 */
860void
861dqrele(vp, dq)
862	struct vnode *vp;
863	register struct dquot *dq;
864{
865
866	if (dq == NODQUOT)
867		return;
868	if (dq->dq_cnt > 1) {
869		dq->dq_cnt--;
870		return;
871	}
872	if (dq->dq_flags & DQ_MOD)
873		(void) dqsync(vp, dq);
874	if (--dq->dq_cnt > 0)
875		return;
876	TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
877}
878
879/*
880 * Update the disk quota in the quota file.
881 */
882static int
883dqsync(vp, dq)
884	struct vnode *vp;
885	struct dquot *dq;
886{
887	struct proc *p = curproc;		/* XXX */
888	struct vnode *dqvp;
889	struct iovec aiov;
890	struct uio auio;
891	int error;
892
893	if (dq == NODQUOT)
894		panic("dqsync: dquot");
895	if ((dq->dq_flags & DQ_MOD) == 0)
896		return (0);
897	if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
898		panic("dqsync: file");
899	if (vp != dqvp)
900		vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p);
901	while (dq->dq_flags & DQ_LOCK) {
902		dq->dq_flags |= DQ_WANT;
903		(void) tsleep((caddr_t)dq, PINOD+2, "dqsync", 0);
904		if ((dq->dq_flags & DQ_MOD) == 0) {
905			if (vp != dqvp)
906				VOP_UNLOCK(dqvp, 0, p);
907			return (0);
908		}
909	}
910	dq->dq_flags |= DQ_LOCK;
911	auio.uio_iov = &aiov;
912	auio.uio_iovcnt = 1;
913	aiov.iov_base = (caddr_t)&dq->dq_dqb;
914	aiov.iov_len = sizeof (struct dqblk);
915	auio.uio_resid = sizeof (struct dqblk);
916	auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
917	auio.uio_segflg = UIO_SYSSPACE;
918	auio.uio_rw = UIO_WRITE;
919	auio.uio_procp = (struct proc *)0;
920	error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
921	if (auio.uio_resid && error == 0)
922		error = EIO;
923	if (dq->dq_flags & DQ_WANT)
924		wakeup((caddr_t)dq);
925	dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
926	if (vp != dqvp)
927		VOP_UNLOCK(dqvp, 0, p);
928	return (error);
929}
930
931/*
932 * Flush all entries from the cache for a particular vnode.
933 */
934static void
935dqflush(vp)
936	register struct vnode *vp;
937{
938	register struct dquot *dq, *nextdq;
939	struct dqhash *dqh;
940
941	/*
942	 * Move all dquot's that used to refer to this quota
943	 * file off their hash chains (they will eventually
944	 * fall off the head of the free list and be re-used).
945	 */
946	for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
947		for (dq = dqh->lh_first; dq; dq = nextdq) {
948			nextdq = dq->dq_hash.le_next;
949			if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
950				continue;
951			if (dq->dq_cnt)
952				panic("dqflush: stray dquot");
953			LIST_REMOVE(dq, dq_hash);
954			dq->dq_ump = (struct ufsmount *)0;
955		}
956	}
957}
958