1139825Simp/*-
222521Sdyson * Copyright (c) 1982, 1986, 1990, 1993, 1995
31541Srgrimes *	The Regents of the University of California.  All rights reserved.
41541Srgrimes *
51541Srgrimes * This code is derived from software contributed to Berkeley by
61541Srgrimes * Robert Elz at The University of Melbourne.
71541Srgrimes *
81541Srgrimes * Redistribution and use in source and binary forms, with or without
91541Srgrimes * modification, are permitted provided that the following conditions
101541Srgrimes * are met:
111541Srgrimes * 1. Redistributions of source code must retain the above copyright
121541Srgrimes *    notice, this list of conditions and the following disclaimer.
131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141541Srgrimes *    notice, this list of conditions and the following disclaimer in the
151541Srgrimes *    documentation and/or other materials provided with the distribution.
161541Srgrimes * 4. Neither the name of the University nor the names of its contributors
171541Srgrimes *    may be used to endorse or promote products derived from this software
181541Srgrimes *    without specific prior written permission.
191541Srgrimes *
201541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301541Srgrimes * SUCH DAMAGE.
311541Srgrimes *
3222521Sdyson *	@(#)ufs_quota.c	8.5 (Berkeley) 5/20/95
331541Srgrimes */
3413260Swollman
35116192Sobrien#include <sys/cdefs.h>
36116192Sobrien__FBSDID("$FreeBSD$");
37116192Sobrien
38158322Stegge#include "opt_ffs.h"
39158322Stegge
401541Srgrimes#include <sys/param.h>
411541Srgrimes#include <sys/systm.h>
42207736Smckusick#include <sys/endian.h>
4376166Smarkm#include <sys/fcntl.h>
4441059Speter#include <sys/kernel.h>
4576166Smarkm#include <sys/lock.h>
4676166Smarkm#include <sys/malloc.h>
4776166Smarkm#include <sys/mount.h>
4876166Smarkm#include <sys/mutex.h>
491541Srgrimes#include <sys/namei.h>
50164033Srwatson#include <sys/priv.h>
511541Srgrimes#include <sys/proc.h>
5276166Smarkm#include <sys/socket.h>
53158322Stegge#include <sys/stat.h>
54116384Srwatson#include <sys/sysctl.h>
551541Srgrimes#include <sys/vnode.h>
561541Srgrimes
5759241Srwatson#include <ufs/ufs/extattr.h>
581541Srgrimes#include <ufs/ufs/quota.h>
591541Srgrimes#include <ufs/ufs/inode.h>
601541Srgrimes#include <ufs/ufs/ufsmount.h>
6198542Smckusick#include <ufs/ufs/ufs_extern.h>
621541Srgrimes
63207736SmckusickCTASSERT(sizeof(struct dqblk64) == sizeof(struct dqhdr64));
64207736Smckusick
65116384Srwatsonstatic int unprivileged_get_quota = 0;
66116384SrwatsonSYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_get_quota, CTLFLAG_RW,
67116384Srwatson    &unprivileged_get_quota, 0,
68116384Srwatson    "Unprivileged processes may retrieve quotas for other uids and gids");
69116384Srwatson
70151897Srwatsonstatic MALLOC_DEFINE(M_DQUOT, "ufs_quota", "UFS quota entries");
7130309Sphk
721541Srgrimes/*
731541Srgrimes * Quota name to error message mapping.
741541Srgrimes */
751541Srgrimesstatic char *quotatypes[] = INITQFNAMES;
761541Srgrimes
77167543Skibstatic int chkdqchg(struct inode *, ufs2_daddr_t, struct ucred *, int, int *);
78167543Skibstatic int chkiqchg(struct inode *, int, struct ucred *, int, int *);
79207736Smckusickstatic int dqopen(struct vnode *, struct ufsmount *, int);
8092728Salfredstatic int dqget(struct vnode *,
81167543Skib	u_long, struct ufsmount *, int, struct dquot **);
8292728Salfredstatic int dqsync(struct vnode *, struct dquot *);
83247388Skibstatic int dqflush(struct vnode *);
84167543Skibstatic int quotaoff1(struct thread *td, struct mount *mp, int type);
85167543Skibstatic int quotaoff_inchange(struct thread *td, struct mount *mp, int type);
8612971Sphk
87207736Smckusick/* conversion functions - from_to() */
88207736Smckusickstatic void dqb32_dq(const struct dqblk32 *, struct dquot *);
89207736Smckusickstatic void dqb64_dq(const struct dqblk64 *, struct dquot *);
90207736Smckusickstatic void dq_dqb32(const struct dquot *, struct dqblk32 *);
91207736Smckusickstatic void dq_dqb64(const struct dquot *, struct dqblk64 *);
92207736Smckusickstatic void dqb32_dqb64(const struct dqblk32 *, struct dqblk64 *);
93207736Smckusickstatic void dqb64_dqb32(const struct dqblk64 *, struct dqblk32 *);
94207736Smckusick
9512971Sphk#ifdef DIAGNOSTIC
9692728Salfredstatic void dqref(struct dquot *);
9792728Salfredstatic void chkdquot(struct inode *);
9812971Sphk#endif
9912971Sphk
1001541Srgrimes/*
1011541Srgrimes * Set up the quotas for an inode.
1021541Srgrimes *
1031541Srgrimes * This routine completely defines the semantics of quotas.
1041541Srgrimes * If other criterion want to be used to establish quotas, the
105207736Smckusick * MAXQUOTAS value in quota.h should be increased, and the
1061541Srgrimes * additional dquots set up here.
1071541Srgrimes */
1081541Srgrimesint
109181327Sdesgetinoquota(struct inode *ip)
1101541Srgrimes{
1111541Srgrimes	struct ufsmount *ump;
112166743Skib	struct vnode *vp;
1131541Srgrimes	int error;
1141541Srgrimes
115166743Skib	vp = ITOV(ip);
116166743Skib
117158322Stegge	/*
118166142Smpp	 * Disk quotas must be turned off for system files.  Currently
119166142Smpp	 * snapshot and quota files.
120158322Stegge	 */
121166142Smpp	if ((vp->v_vflag & VV_SYSTEM) != 0)
122158322Stegge		return (0);
123166142Smpp	/*
124166142Smpp	 * XXX: Turn off quotas for files with a negative UID or GID.
125166142Smpp	 * This prevents the creation of 100GB+ quota files.
126166142Smpp	 */
127166142Smpp	if ((int)ip->i_uid < 0 || (int)ip->i_gid < 0)
128166142Smpp		return (0);
1291541Srgrimes	ump = VFSTOUFS(vp->v_mount);
1301541Srgrimes	/*
1311541Srgrimes	 * Set up the user quota based on file uid.
1321541Srgrimes	 * EINVAL means that quotas are not enabled.
1331541Srgrimes	 */
134167543Skib	if ((error =
1351541Srgrimes		dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) &&
1361541Srgrimes	    error != EINVAL)
1371541Srgrimes		return (error);
1381541Srgrimes	/*
1391541Srgrimes	 * Set up the group quota based on file gid.
1401541Srgrimes	 * EINVAL means that quotas are not enabled.
1411541Srgrimes	 */
142167543Skib	if ((error =
1431541Srgrimes		dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&
1441541Srgrimes	    error != EINVAL)
1451541Srgrimes		return (error);
1461541Srgrimes	return (0);
1471541Srgrimes}
1481541Srgrimes
1491541Srgrimes/*
1501541Srgrimes * Update disk usage, and take corrective action.
1511541Srgrimes */
1521541Srgrimesint
153181327Sdeschkdq(struct inode *ip, ufs2_daddr_t change, struct ucred *cred, int flags)
1541541Srgrimes{
15596506Sphk	struct dquot *dq;
15698542Smckusick	ufs2_daddr_t ncurblocks;
157166142Smpp	struct vnode *vp = ITOV(ip);
158167543Skib	int i, error, warn, do_check;
1591541Srgrimes
160166142Smpp	/*
161166142Smpp	 * Disk quotas must be turned off for system files.  Currently
162166142Smpp	 * snapshot and quota files.
163166142Smpp	 */
164166142Smpp	if ((vp->v_vflag & VV_SYSTEM) != 0)
165166142Smpp		return (0);
166166142Smpp	/*
167166142Smpp	 * XXX: Turn off quotas for files with a negative UID or GID.
168166142Smpp	 * This prevents the creation of 100GB+ quota files.
169166142Smpp	 */
170166142Smpp	if ((int)ip->i_uid < 0 || (int)ip->i_gid < 0)
171166142Smpp		return (0);
1721541Srgrimes#ifdef DIAGNOSTIC
1731541Srgrimes	if ((flags & CHOWN) == 0)
1741541Srgrimes		chkdquot(ip);
1751541Srgrimes#endif
1761541Srgrimes	if (change == 0)
1771541Srgrimes		return (0);
1781541Srgrimes	if (change < 0) {
1791541Srgrimes		for (i = 0; i < MAXQUOTAS; i++) {
1801541Srgrimes			if ((dq = ip->i_dquot[i]) == NODQUOT)
1811541Srgrimes				continue;
182167543Skib			DQI_LOCK(dq);
183167543Skib			DQI_WAIT(dq, PINOD+1, "chkdq1");
1841541Srgrimes			ncurblocks = dq->dq_curblocks + change;
1851541Srgrimes			if (ncurblocks >= 0)
1861541Srgrimes				dq->dq_curblocks = ncurblocks;
1871541Srgrimes			else
1881541Srgrimes				dq->dq_curblocks = 0;
1891541Srgrimes			dq->dq_flags &= ~DQ_BLKS;
1901541Srgrimes			dq->dq_flags |= DQ_MOD;
191167543Skib			DQI_UNLOCK(dq);
1921541Srgrimes		}
1931541Srgrimes		return (0);
1941541Srgrimes	}
195167543Skib	if ((flags & FORCE) == 0 &&
196167543Skib	    priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
197167543Skib		do_check = 1;
198167543Skib	else
199167543Skib		do_check = 0;
2001541Srgrimes	for (i = 0; i < MAXQUOTAS; i++) {
2011541Srgrimes		if ((dq = ip->i_dquot[i]) == NODQUOT)
2021541Srgrimes			continue;
203167543Skib		warn = 0;
204167543Skib		DQI_LOCK(dq);
205167543Skib		DQI_WAIT(dq, PINOD+1, "chkdq2");
206167543Skib		if (do_check) {
207167543Skib			error = chkdqchg(ip, change, cred, i, &warn);
208167543Skib			if (error) {
209167543Skib				/*
210167543Skib				 * Roll back user quota changes when
211167543Skib				 * group quota failed.
212167543Skib				 */
213167543Skib				while (i > 0) {
214167543Skib					--i;
215167543Skib					dq = ip->i_dquot[i];
216167543Skib					if (dq == NODQUOT)
217167543Skib						continue;
218167543Skib					DQI_LOCK(dq);
219167543Skib					DQI_WAIT(dq, PINOD+1, "chkdq3");
220167543Skib					ncurblocks = dq->dq_curblocks - change;
221167543Skib					if (ncurblocks >= 0)
222167543Skib						dq->dq_curblocks = ncurblocks;
223167543Skib					else
224167543Skib						dq->dq_curblocks = 0;
225167543Skib					dq->dq_flags &= ~DQ_BLKS;
226167543Skib					dq->dq_flags |= DQ_MOD;
227167543Skib					DQI_UNLOCK(dq);
228167543Skib				}
229167543Skib				return (error);
230167543Skib			}
2311541Srgrimes		}
23259721Smckusick		/* Reset timer when crossing soft limit */
23359721Smckusick		if (dq->dq_curblocks + change >= dq->dq_bsoftlimit &&
23459721Smckusick		    dq->dq_curblocks < dq->dq_bsoftlimit)
235219388Skib			dq->dq_btime = time_second + ip->i_ump->um_btime[i];
2361541Srgrimes		dq->dq_curblocks += change;
2371541Srgrimes		dq->dq_flags |= DQ_MOD;
238167543Skib		DQI_UNLOCK(dq);
239167543Skib		if (warn)
240217357Spluknet			uprintf("\n%s: warning, %s disk quota exceeded\n",
241217357Spluknet			    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
242217357Spluknet			    quotatypes[i]);
2431541Srgrimes	}
2441541Srgrimes	return (0);
2451541Srgrimes}
2461541Srgrimes
2471541Srgrimes/*
2481541Srgrimes * Check for a valid change to a users allocation.
2491541Srgrimes * Issue an error message if appropriate.
2501541Srgrimes */
25112971Sphkstatic int
252181327Sdeschkdqchg(struct inode *ip, ufs2_daddr_t change, struct ucred *cred,
253181327Sdes    int type, int *warn)
2541541Srgrimes{
25596506Sphk	struct dquot *dq = ip->i_dquot[type];
25698542Smckusick	ufs2_daddr_t ncurblocks = dq->dq_curblocks + change;
2571541Srgrimes
2581541Srgrimes	/*
2591541Srgrimes	 * If user would exceed their hard limit, disallow space allocation.
2601541Srgrimes	 */
2611541Srgrimes	if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
2621541Srgrimes		if ((dq->dq_flags & DQ_BLKS) == 0 &&
2631541Srgrimes		    ip->i_uid == cred->cr_uid) {
264167543Skib			dq->dq_flags |= DQ_BLKS;
265167543Skib			DQI_UNLOCK(dq);
2661541Srgrimes			uprintf("\n%s: write failed, %s disk limit reached\n",
2671541Srgrimes			    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
2681541Srgrimes			    quotatypes[type]);
269167543Skib			return (EDQUOT);
2701541Srgrimes		}
271167543Skib		DQI_UNLOCK(dq);
2721541Srgrimes		return (EDQUOT);
2731541Srgrimes	}
2741541Srgrimes	/*
2751541Srgrimes	 * If user is over their soft limit for too long, disallow space
2761541Srgrimes	 * allocation. Reset time limit as they cross their soft limit.
2771541Srgrimes	 */
2781541Srgrimes	if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
2791541Srgrimes		if (dq->dq_curblocks < dq->dq_bsoftlimit) {
280219388Skib			dq->dq_btime = time_second + ip->i_ump->um_btime[type];
2811541Srgrimes			if (ip->i_uid == cred->cr_uid)
282167543Skib				*warn = 1;
2831541Srgrimes			return (0);
2841541Srgrimes		}
28534961Sphk		if (time_second > dq->dq_btime) {
2861541Srgrimes			if ((dq->dq_flags & DQ_BLKS) == 0 &&
2871541Srgrimes			    ip->i_uid == cred->cr_uid) {
288167543Skib				dq->dq_flags |= DQ_BLKS;
289167543Skib				DQI_UNLOCK(dq);
290217357Spluknet				uprintf("\n%s: write failed, %s "
291217357Spluknet				    "disk quota exceeded for too long\n",
2921541Srgrimes				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
293217357Spluknet				    quotatypes[type]);
294167543Skib				return (EDQUOT);
2951541Srgrimes			}
296167543Skib			DQI_UNLOCK(dq);
2971541Srgrimes			return (EDQUOT);
2981541Srgrimes		}
2991541Srgrimes	}
3001541Srgrimes	return (0);
3011541Srgrimes}
3021541Srgrimes
3031541Srgrimes/*
3041541Srgrimes * Check the inode limit, applying corrective action.
3051541Srgrimes */
3061541Srgrimesint
307181327Sdeschkiq(struct inode *ip, int change, struct ucred *cred, int flags)
3081541Srgrimes{
30996506Sphk	struct dquot *dq;
31098542Smckusick	ino_t ncurinodes;
311167543Skib	int i, error, warn, do_check;
3121541Srgrimes
3131541Srgrimes#ifdef DIAGNOSTIC
3141541Srgrimes	if ((flags & CHOWN) == 0)
3151541Srgrimes		chkdquot(ip);
3161541Srgrimes#endif
3171541Srgrimes	if (change == 0)
3181541Srgrimes		return (0);
3191541Srgrimes	if (change < 0) {
3201541Srgrimes		for (i = 0; i < MAXQUOTAS; i++) {
3211541Srgrimes			if ((dq = ip->i_dquot[i]) == NODQUOT)
3221541Srgrimes				continue;
323167543Skib			DQI_LOCK(dq);
324167543Skib			DQI_WAIT(dq, PINOD+1, "chkiq1");
3251541Srgrimes			ncurinodes = dq->dq_curinodes + change;
326104364Sphk			/* XXX: ncurinodes is unsigned */
327166142Smpp			if (dq->dq_curinodes != 0 && ncurinodes >= 0)
3281541Srgrimes				dq->dq_curinodes = ncurinodes;
3291541Srgrimes			else
3301541Srgrimes				dq->dq_curinodes = 0;
3311541Srgrimes			dq->dq_flags &= ~DQ_INODS;
3321541Srgrimes			dq->dq_flags |= DQ_MOD;
333167543Skib			DQI_UNLOCK(dq);
3341541Srgrimes		}
3351541Srgrimes		return (0);
3361541Srgrimes	}
337167543Skib	if ((flags & FORCE) == 0 &&
338167543Skib	    priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
339167543Skib		do_check = 1;
340167543Skib	else
341167543Skib		do_check = 0;
3421541Srgrimes	for (i = 0; i < MAXQUOTAS; i++) {
3431541Srgrimes		if ((dq = ip->i_dquot[i]) == NODQUOT)
3441541Srgrimes			continue;
345167543Skib		warn = 0;
346167543Skib		DQI_LOCK(dq);
347167543Skib		DQI_WAIT(dq, PINOD+1, "chkiq2");
348167543Skib		if (do_check) {
349167543Skib			error = chkiqchg(ip, change, cred, i, &warn);
350167543Skib			if (error) {
351167543Skib				/*
352167543Skib				 * Roll back user quota changes when
353167543Skib				 * group quota failed.
354167543Skib				 */
355167543Skib				while (i > 0) {
356167543Skib					--i;
357167543Skib					dq = ip->i_dquot[i];
358167543Skib					if (dq == NODQUOT)
359167543Skib						continue;
360167543Skib					DQI_LOCK(dq);
361167543Skib					DQI_WAIT(dq, PINOD+1, "chkiq3");
362167543Skib					ncurinodes = dq->dq_curinodes - change;
363167543Skib					/* XXX: ncurinodes is unsigned */
364167543Skib					if (dq->dq_curinodes != 0 &&
365167543Skib					    ncurinodes >= 0)
366167543Skib						dq->dq_curinodes = ncurinodes;
367167543Skib					else
368167543Skib						dq->dq_curinodes = 0;
369167543Skib					dq->dq_flags &= ~DQ_INODS;
370167543Skib					dq->dq_flags |= DQ_MOD;
371167543Skib					DQI_UNLOCK(dq);
372167543Skib				}
373167543Skib				return (error);
374167543Skib			}
3751541Srgrimes		}
37659721Smckusick		/* Reset timer when crossing soft limit */
37759721Smckusick		if (dq->dq_curinodes + change >= dq->dq_isoftlimit &&
37859721Smckusick		    dq->dq_curinodes < dq->dq_isoftlimit)
379219388Skib			dq->dq_itime = time_second + ip->i_ump->um_itime[i];
3801541Srgrimes		dq->dq_curinodes += change;
3811541Srgrimes		dq->dq_flags |= DQ_MOD;
382167543Skib		DQI_UNLOCK(dq);
383167543Skib		if (warn)
384217357Spluknet			uprintf("\n%s: warning, %s inode quota exceeded\n",
385217357Spluknet			    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
386217357Spluknet			    quotatypes[i]);
3871541Srgrimes	}
3881541Srgrimes	return (0);
3891541Srgrimes}
3901541Srgrimes
3911541Srgrimes/*
3921541Srgrimes * Check for a valid change to a users allocation.
3931541Srgrimes * Issue an error message if appropriate.
3941541Srgrimes */
39512971Sphkstatic int
396181327Sdeschkiqchg(struct inode *ip, int change, struct ucred *cred, int type, int *warn)
3971541Srgrimes{
39896506Sphk	struct dquot *dq = ip->i_dquot[type];
39998542Smckusick	ino_t ncurinodes = dq->dq_curinodes + change;
4001541Srgrimes
4011541Srgrimes	/*
4021541Srgrimes	 * If user would exceed their hard limit, disallow inode allocation.
4031541Srgrimes	 */
4041541Srgrimes	if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
4051541Srgrimes		if ((dq->dq_flags & DQ_INODS) == 0 &&
4061541Srgrimes		    ip->i_uid == cred->cr_uid) {
407167543Skib			dq->dq_flags |= DQ_INODS;
408167543Skib			DQI_UNLOCK(dq);
4091541Srgrimes			uprintf("\n%s: write failed, %s inode limit reached\n",
4101541Srgrimes			    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
4111541Srgrimes			    quotatypes[type]);
412167543Skib			return (EDQUOT);
4131541Srgrimes		}
414167543Skib		DQI_UNLOCK(dq);
4151541Srgrimes		return (EDQUOT);
4161541Srgrimes	}
4171541Srgrimes	/*
4181541Srgrimes	 * If user is over their soft limit for too long, disallow inode
4191541Srgrimes	 * allocation. Reset time limit as they cross their soft limit.
4201541Srgrimes	 */
4211541Srgrimes	if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
4221541Srgrimes		if (dq->dq_curinodes < dq->dq_isoftlimit) {
423219388Skib			dq->dq_itime = time_second + ip->i_ump->um_itime[type];
4241541Srgrimes			if (ip->i_uid == cred->cr_uid)
425167543Skib				*warn = 1;
4261541Srgrimes			return (0);
4271541Srgrimes		}
42834961Sphk		if (time_second > dq->dq_itime) {
4291541Srgrimes			if ((dq->dq_flags & DQ_INODS) == 0 &&
4301541Srgrimes			    ip->i_uid == cred->cr_uid) {
431167543Skib				dq->dq_flags |= DQ_INODS;
432167543Skib				DQI_UNLOCK(dq);
433217357Spluknet				uprintf("\n%s: write failed, %s "
434217357Spluknet				    "inode quota exceeded for too long\n",
435217357Spluknet				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
436217357Spluknet				    quotatypes[type]);
437167543Skib				return (EDQUOT);
4381541Srgrimes			}
439167543Skib			DQI_UNLOCK(dq);
4401541Srgrimes			return (EDQUOT);
4411541Srgrimes		}
4421541Srgrimes	}
4431541Srgrimes	return (0);
4441541Srgrimes}
4451541Srgrimes
4461541Srgrimes#ifdef DIAGNOSTIC
4471541Srgrimes/*
4481541Srgrimes * On filesystems with quotas enabled, it is an error for a file to change
4491541Srgrimes * size and not to have a dquot structure associated with it.
4501541Srgrimes */
45112971Sphkstatic void
452181327Sdeschkdquot(struct inode *ip)
4531541Srgrimes{
454219388Skib	struct ufsmount *ump = ip->i_ump;
455166142Smpp	struct vnode *vp = ITOV(ip);
45696506Sphk	int i;
4571541Srgrimes
458158322Stegge	/*
459166142Smpp	 * Disk quotas must be turned off for system files.  Currently
460166142Smpp	 * these are snapshots and quota files.
461158322Stegge	 */
462166142Smpp	if ((vp->v_vflag & VV_SYSTEM) != 0)
463158322Stegge		return;
464166142Smpp	/*
465166142Smpp	 * XXX: Turn off quotas for files with a negative UID or GID.
466166142Smpp	 * This prevents the creation of 100GB+ quota files.
467166142Smpp	 */
468166142Smpp	if ((int)ip->i_uid < 0 || (int)ip->i_gid < 0)
469166146Sdelphij		return;
470167543Skib
471167543Skib	UFS_LOCK(ump);
4721541Srgrimes	for (i = 0; i < MAXQUOTAS; i++) {
4731541Srgrimes		if (ump->um_quotas[i] == NULLVP ||
4741541Srgrimes		    (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING)))
4751541Srgrimes			continue;
4761541Srgrimes		if (ip->i_dquot[i] == NODQUOT) {
477167543Skib			UFS_UNLOCK(ump);
4781541Srgrimes			vprint("chkdquot: missing dquot", ITOV(ip));
47923562Smpp			panic("chkdquot: missing dquot");
4801541Srgrimes		}
4811541Srgrimes	}
482167543Skib	UFS_UNLOCK(ump);
4831541Srgrimes}
4841541Srgrimes#endif
4851541Srgrimes
4861541Srgrimes/*
4871541Srgrimes * Code to process quotactl commands.
4881541Srgrimes */
4891541Srgrimes
4901541Srgrimes/*
49196755Strhodes * Q_QUOTAON - set up a quota file for a particular filesystem.
4921541Srgrimes */
4931541Srgrimesint
494181327Sdesquotaon(struct thread *td, struct mount *mp, int type, void *fname)
4951541Srgrimes{
496166743Skib	struct ufsmount *ump;
49722521Sdyson	struct vnode *vp, **vpp;
498154152Stegge	struct vnode *mvp;
4991541Srgrimes	struct dquot *dq;
500241896Skib	int error, flags;
5011541Srgrimes	struct nameidata nd;
5021541Srgrimes
503170587Srwatson	error = priv_check(td, PRIV_UFS_QUOTAON);
504116384Srwatson	if (error)
505116384Srwatson		return (error);
506116384Srwatson
507207736Smckusick	if (mp->mnt_flag & MNT_RDONLY)
508207736Smckusick		return (EROFS);
509207736Smckusick
510166743Skib	ump = VFSTOUFS(mp);
511167543Skib	dq = NODQUOT;
512167543Skib
513241896Skib	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, td);
51462550Smckusick	flags = FREAD | FWRITE;
515229828Skib	vfs_ref(mp);
516229828Skib	vfs_unbusy(mp);
517170152Skib	error = vn_open(&nd, &flags, 0, NULL);
518229828Skib	if (error != 0) {
519229828Skib		vfs_rel(mp);
5201541Srgrimes		return (error);
521229828Skib	}
52254655Seivind	NDFREE(&nd, NDF_ONLY_PNBUF);
5231541Srgrimes	vp = nd.ni_vp;
524229828Skib	error = vfs_busy(mp, MBF_NOWAIT);
525229828Skib	vfs_rel(mp);
526229828Skib	if (error == 0) {
527229828Skib		if (vp->v_type != VREG) {
528229828Skib			error = EACCES;
529229828Skib			vfs_unbusy(mp);
530229828Skib		}
531229828Skib	}
532229828Skib	if (error != 0) {
533208774Skib		VOP_UNLOCK(vp, 0);
53491406Sjhb		(void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
535229828Skib		return (error);
5361541Srgrimes	}
537167543Skib
538167543Skib	UFS_LOCK(ump);
539167543Skib	if ((ump->um_qflags[type] & (QTF_OPENING|QTF_CLOSING)) != 0) {
540167543Skib		UFS_UNLOCK(ump);
541208774Skib		VOP_UNLOCK(vp, 0);
542167543Skib		(void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
543229828Skib		vfs_unbusy(mp);
544167543Skib		return (EALREADY);
545167543Skib	}
546167543Skib	ump->um_qflags[type] |= QTF_OPENING|QTF_CLOSING;
547207736Smckusick	UFS_UNLOCK(ump);
548207736Smckusick	if ((error = dqopen(vp, ump, type)) != 0) {
549208774Skib		VOP_UNLOCK(vp, 0);
550207736Smckusick		UFS_LOCK(ump);
551207736Smckusick		ump->um_qflags[type] &= ~(QTF_OPENING|QTF_CLOSING);
552207736Smckusick		UFS_UNLOCK(ump);
553207736Smckusick		(void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
554229828Skib		vfs_unbusy(mp);
555207736Smckusick		return (error);
556207736Smckusick	}
557208774Skib	VOP_UNLOCK(vp, 0);
558162647Stegge	MNT_ILOCK(mp);
5591541Srgrimes	mp->mnt_flag |= MNT_QUOTA;
560162647Stegge	MNT_IUNLOCK(mp);
561167543Skib
562167543Skib	vpp = &ump->um_quotas[type];
563167543Skib	if (*vpp != vp)
564167543Skib		quotaoff1(td, mp, type);
565167543Skib
566175202Sattilio	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
567101308Sjeff	vp->v_vflag |= VV_SYSTEM;
568175294Sattilio	VOP_UNLOCK(vp, 0);
5691541Srgrimes	*vpp = vp;
5701541Srgrimes	/*
5711541Srgrimes	 * Save the credential of the process that turned on quotas.
5721541Srgrimes	 * Set up the time limits for this quota.
5731541Srgrimes	 */
57491406Sjhb	ump->um_cred[type] = crhold(td->td_ucred);
5751541Srgrimes	ump->um_btime[type] = MAX_DQ_TIME;
5761541Srgrimes	ump->um_itime[type] = MAX_IQ_TIME;
5771541Srgrimes	if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
5781541Srgrimes		if (dq->dq_btime > 0)
5791541Srgrimes			ump->um_btime[type] = dq->dq_btime;
5801541Srgrimes		if (dq->dq_itime > 0)
5811541Srgrimes			ump->um_itime[type] = dq->dq_itime;
5821541Srgrimes		dqrele(NULLVP, dq);
5831541Srgrimes	}
5841541Srgrimes	/*
585167543Skib	 * Allow the getdq from getinoquota below to read the quota
586167543Skib	 * from file.
587167543Skib	 */
588167543Skib	UFS_LOCK(ump);
589167543Skib	ump->um_qflags[type] &= ~QTF_CLOSING;
590167543Skib	UFS_UNLOCK(ump);
591167543Skib	/*
5921541Srgrimes	 * Search vnodes associated with this mount point,
5931541Srgrimes	 * adding references to quota file being opened.
5941541Srgrimes	 * NB: only need to add dquot's for inodes being modified.
5951541Srgrimes	 */
5961541Srgrimesagain:
597234386Smckusick	MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
598120737Sjeff		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
599234386Smckusick			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
600103943Sjeff			goto again;
601103943Sjeff		}
60278912Sjhb		if (vp->v_type == VNON || vp->v_writecount == 0) {
603175294Sattilio			VOP_UNLOCK(vp, 0);
604121874Skan			vrele(vp);
6051541Srgrimes			continue;
60678912Sjhb		}
6073427Sphk		error = getinoquota(VTOI(vp));
608175294Sattilio		VOP_UNLOCK(vp, 0);
609121874Skan		vrele(vp);
610154152Stegge		if (error) {
611234386Smckusick			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
6121541Srgrimes			break;
613154152Stegge		}
6141541Srgrimes	}
615167543Skib
616167543Skib        if (error)
617167543Skib		quotaoff_inchange(td, mp, type);
618167543Skib	UFS_LOCK(ump);
6191541Srgrimes	ump->um_qflags[type] &= ~QTF_OPENING;
620167543Skib	KASSERT((ump->um_qflags[type] & QTF_CLOSING) == 0,
621167543Skib		("quotaon: leaking flags"));
622167543Skib	UFS_UNLOCK(ump);
623167543Skib
624229828Skib	vfs_unbusy(mp);
6251541Srgrimes	return (error);
6261541Srgrimes}
6271541Srgrimes
6281541Srgrimes/*
629167543Skib * Main code to turn off disk quotas for a filesystem. Does not change
630167543Skib * flags.
6311541Srgrimes */
632167543Skibstatic int
633181327Sdesquotaoff1(struct thread *td, struct mount *mp, int type)
6341541Srgrimes{
63522521Sdyson	struct vnode *vp;
636154152Stegge	struct vnode *qvp, *mvp;
637166743Skib	struct ufsmount *ump;
63822521Sdyson	struct dquot *dq;
63922521Sdyson	struct inode *ip;
640167543Skib	struct ucred *cr;
6411541Srgrimes	int error;
6428876Srgrimes
643167543Skib	ump = VFSTOUFS(mp);
644116384Srwatson
645167543Skib	UFS_LOCK(ump);
646167543Skib	KASSERT((ump->um_qflags[type] & QTF_CLOSING) != 0,
647167543Skib		("quotaoff1: flags are invalid"));
648167543Skib	if ((qvp = ump->um_quotas[type]) == NULLVP) {
649167543Skib		UFS_UNLOCK(ump);
6501541Srgrimes		return (0);
651167543Skib	}
652167543Skib	cr = ump->um_cred[type];
653167543Skib	UFS_UNLOCK(ump);
654181327Sdes
6551541Srgrimes	/*
6561541Srgrimes	 * Search vnodes associated with this mount point,
6571541Srgrimes	 * deleting any references to quota file being closed.
6581541Srgrimes	 */
6591541Srgrimesagain:
660234386Smckusick	MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
66178912Sjhb		if (vp->v_type == VNON) {
662120737Sjeff			VI_UNLOCK(vp);
66334266Sjulian			continue;
66478912Sjhb		}
66583366Sjulian		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
666234386Smckusick			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
6671541Srgrimes			goto again;
66878912Sjhb		}
6691541Srgrimes		ip = VTOI(vp);
6701541Srgrimes		dq = ip->i_dquot[type];
6711541Srgrimes		ip->i_dquot[type] = NODQUOT;
6721541Srgrimes		dqrele(vp, dq);
673175294Sattilio		VOP_UNLOCK(vp, 0);
674121874Skan		vrele(vp);
6751541Srgrimes	}
676167543Skib
677247388Skib	error = dqflush(qvp);
678247388Skib	if (error != 0)
679247388Skib		return (error);
680247388Skib
681247388Skib	/*
682247388Skib	 * Clear um_quotas before closing the quota vnode to prevent
683167543Skib	 * access to the closed vnode from dqget/dqsync
684167543Skib	 */
685167543Skib	UFS_LOCK(ump);
686167543Skib	ump->um_quotas[type] = NULLVP;
687167543Skib	ump->um_cred[type] = NOCRED;
688167543Skib	UFS_UNLOCK(ump);
689167543Skib
690175202Sattilio	vn_lock(qvp, LK_EXCLUSIVE | LK_RETRY);
691101308Sjeff	qvp->v_vflag &= ~VV_SYSTEM;
692175294Sattilio	VOP_UNLOCK(qvp, 0);
69391406Sjhb	error = vn_close(qvp, FREAD|FWRITE, td->td_ucred, td);
694167543Skib	crfree(cr);
695167543Skib
696167543Skib	return (error);
697167543Skib}
698167543Skib
699167543Skib/*
700167543Skib * Turns off quotas, assumes that ump->um_qflags are already checked
701167543Skib * and QTF_CLOSING is set to indicate operation in progress. Fixes
702167543Skib * ump->um_qflags and mp->mnt_flag after.
703167543Skib */
704167543Skibint
705181327Sdesquotaoff_inchange(struct thread *td, struct mount *mp, int type)
706167543Skib{
707167543Skib	struct ufsmount *ump;
708167543Skib	int i;
709167543Skib	int error;
710167543Skib
711167543Skib	error = quotaoff1(td, mp, type);
712167543Skib
713167543Skib	ump = VFSTOUFS(mp);
714167543Skib	UFS_LOCK(ump);
7151541Srgrimes	ump->um_qflags[type] &= ~QTF_CLOSING;
716167543Skib	for (i = 0; i < MAXQUOTAS; i++)
717167543Skib		if (ump->um_quotas[i] != NULLVP)
7181541Srgrimes			break;
719167543Skib	if (i == MAXQUOTAS) {
720162647Stegge		MNT_ILOCK(mp);
7211541Srgrimes		mp->mnt_flag &= ~MNT_QUOTA;
722162647Stegge		MNT_IUNLOCK(mp);
723162647Stegge	}
724167543Skib	UFS_UNLOCK(ump);
7251541Srgrimes	return (error);
7261541Srgrimes}
7271541Srgrimes
7281541Srgrimes/*
729167543Skib * Q_QUOTAOFF - turn off disk quotas for a filesystem.
730167543Skib */
731167543Skibint
732181327Sdesquotaoff(struct thread *td, struct mount *mp, int type)
733167543Skib{
734167543Skib	struct ufsmount *ump;
735167543Skib	int error;
736167543Skib
737170587Srwatson	error = priv_check(td, PRIV_UFS_QUOTAOFF);
738167543Skib	if (error)
739167543Skib		return (error);
740167543Skib
741167543Skib	ump = VFSTOUFS(mp);
742167543Skib	UFS_LOCK(ump);
743167543Skib	if ((ump->um_qflags[type] & (QTF_OPENING|QTF_CLOSING)) != 0) {
744167543Skib		UFS_UNLOCK(ump);
745167543Skib		return (EALREADY);
746167543Skib	}
747167543Skib	ump->um_qflags[type] |= QTF_CLOSING;
748167543Skib	UFS_UNLOCK(ump);
749167543Skib
750167543Skib	return (quotaoff_inchange(td, mp, type));
751167543Skib}
752167543Skib
753167543Skib/*
7541541Srgrimes * Q_GETQUOTA - return current values in a dqblk structure.
7551541Srgrimes */
756207736Smckusickstatic int
757207736Smckusick_getquota(struct thread *td, struct mount *mp, u_long id, int type,
758207736Smckusick    struct dqblk64 *dqb)
7591541Srgrimes{
7601541Srgrimes	struct dquot *dq;
7611541Srgrimes	int error;
7621541Srgrimes
763116384Srwatson	switch (type) {
764116384Srwatson	case USRQUOTA:
765116384Srwatson		if ((td->td_ucred->cr_uid != id) && !unprivileged_get_quota) {
766170587Srwatson			error = priv_check(td, PRIV_VFS_GETQUOTA);
767116384Srwatson			if (error)
768116384Srwatson				return (error);
769116384Srwatson		}
770181327Sdes		break;
771116384Srwatson
772116384Srwatson	case GRPQUOTA:
773164033Srwatson		if (!groupmember(id, td->td_ucred) &&
774164033Srwatson		    !unprivileged_get_quota) {
775170587Srwatson			error = priv_check(td, PRIV_VFS_GETQUOTA);
776116384Srwatson			if (error)
777116384Srwatson				return (error);
778116384Srwatson		}
779116384Srwatson		break;
780116384Srwatson
781116384Srwatson	default:
782116384Srwatson		return (EINVAL);
783116384Srwatson	}
784116384Srwatson
785167543Skib	dq = NODQUOT;
7863427Sphk	error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq);
7873427Sphk	if (error)
7881541Srgrimes		return (error);
789207736Smckusick	*dqb = dq->dq_dqb;
7901541Srgrimes	dqrele(NULLVP, dq);
7911541Srgrimes	return (error);
7921541Srgrimes}
7931541Srgrimes
7941541Srgrimes/*
7951541Srgrimes * Q_SETQUOTA - assign an entire dqblk structure.
7961541Srgrimes */
797207736Smckusickstatic int
798207736Smckusick_setquota(struct thread *td, struct mount *mp, u_long id, int type,
799207736Smckusick    struct dqblk64 *dqb)
8001541Srgrimes{
80196506Sphk	struct dquot *dq;
8021541Srgrimes	struct dquot *ndq;
803166743Skib	struct ufsmount *ump;
804207736Smckusick	struct dqblk64 newlim;
8051541Srgrimes	int error;
8061541Srgrimes
807170587Srwatson	error = priv_check(td, PRIV_VFS_SETQUOTA);
808116384Srwatson	if (error)
809116384Srwatson		return (error);
810116384Srwatson
811207736Smckusick	newlim = *dqb;
812167543Skib
813167543Skib	ndq = NODQUOT;
814167543Skib	ump = VFSTOUFS(mp);
815167543Skib
8163427Sphk	error = dqget(NULLVP, id, ump, type, &ndq);
8173427Sphk	if (error)
8181541Srgrimes		return (error);
8191541Srgrimes	dq = ndq;
820167543Skib	DQI_LOCK(dq);
821167543Skib	DQI_WAIT(dq, PINOD+1, "setqta");
8221541Srgrimes	/*
8231541Srgrimes	 * Copy all but the current values.
8241541Srgrimes	 * Reset time limit if previously had no soft limit or were
8251541Srgrimes	 * under it, but now have a soft limit and are over it.
8261541Srgrimes	 */
8271541Srgrimes	newlim.dqb_curblocks = dq->dq_curblocks;
8281541Srgrimes	newlim.dqb_curinodes = dq->dq_curinodes;
8291541Srgrimes	if (dq->dq_id != 0) {
8301541Srgrimes		newlim.dqb_btime = dq->dq_btime;
8311541Srgrimes		newlim.dqb_itime = dq->dq_itime;
8321541Srgrimes	}
8331541Srgrimes	if (newlim.dqb_bsoftlimit &&
8341541Srgrimes	    dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
8351541Srgrimes	    (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
83634961Sphk		newlim.dqb_btime = time_second + ump->um_btime[type];
8371541Srgrimes	if (newlim.dqb_isoftlimit &&
8381541Srgrimes	    dq->dq_curinodes >= newlim.dqb_isoftlimit &&
8391541Srgrimes	    (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
84034961Sphk		newlim.dqb_itime = time_second + ump->um_itime[type];
8411541Srgrimes	dq->dq_dqb = newlim;
8421541Srgrimes	if (dq->dq_curblocks < dq->dq_bsoftlimit)
8431541Srgrimes		dq->dq_flags &= ~DQ_BLKS;
8441541Srgrimes	if (dq->dq_curinodes < dq->dq_isoftlimit)
8451541Srgrimes		dq->dq_flags &= ~DQ_INODS;
8461541Srgrimes	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
8471541Srgrimes	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
8481541Srgrimes		dq->dq_flags |= DQ_FAKE;
8491541Srgrimes	else
8501541Srgrimes		dq->dq_flags &= ~DQ_FAKE;
8511541Srgrimes	dq->dq_flags |= DQ_MOD;
852167543Skib	DQI_UNLOCK(dq);
8531541Srgrimes	dqrele(NULLVP, dq);
8541541Srgrimes	return (0);
8551541Srgrimes}
8561541Srgrimes
8571541Srgrimes/*
8581541Srgrimes * Q_SETUSE - set current inode and block usage.
8591541Srgrimes */
860207736Smckusickstatic int
861207736Smckusick_setuse(struct thread *td, struct mount *mp, u_long id, int type,
862207736Smckusick    struct dqblk64 *dqb)
8631541Srgrimes{
86496506Sphk	struct dquot *dq;
865166743Skib	struct ufsmount *ump;
8661541Srgrimes	struct dquot *ndq;
867207736Smckusick	struct dqblk64 usage;
8681541Srgrimes	int error;
8691541Srgrimes
870170587Srwatson	error = priv_check(td, PRIV_UFS_SETUSE);
871116384Srwatson	if (error)
872116384Srwatson		return (error);
873116384Srwatson
874207736Smckusick	usage = *dqb;
875167543Skib
876167543Skib	ump = VFSTOUFS(mp);
877167543Skib	ndq = NODQUOT;
878167543Skib
8793427Sphk	error = dqget(NULLVP, id, ump, type, &ndq);
8803427Sphk	if (error)
8811541Srgrimes		return (error);
8821541Srgrimes	dq = ndq;
883167543Skib	DQI_LOCK(dq);
884167543Skib	DQI_WAIT(dq, PINOD+1, "setuse");
8851541Srgrimes	/*
8861541Srgrimes	 * Reset time limit if have a soft limit and were
8871541Srgrimes	 * previously under it, but are now over it.
8881541Srgrimes	 */
8891541Srgrimes	if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
8901541Srgrimes	    usage.dqb_curblocks >= dq->dq_bsoftlimit)
89134961Sphk		dq->dq_btime = time_second + ump->um_btime[type];
8921541Srgrimes	if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
8931541Srgrimes	    usage.dqb_curinodes >= dq->dq_isoftlimit)
89434961Sphk		dq->dq_itime = time_second + ump->um_itime[type];
8951541Srgrimes	dq->dq_curblocks = usage.dqb_curblocks;
8961541Srgrimes	dq->dq_curinodes = usage.dqb_curinodes;
8971541Srgrimes	if (dq->dq_curblocks < dq->dq_bsoftlimit)
8981541Srgrimes		dq->dq_flags &= ~DQ_BLKS;
8991541Srgrimes	if (dq->dq_curinodes < dq->dq_isoftlimit)
9001541Srgrimes		dq->dq_flags &= ~DQ_INODS;
9011541Srgrimes	dq->dq_flags |= DQ_MOD;
902167543Skib	DQI_UNLOCK(dq);
9031541Srgrimes	dqrele(NULLVP, dq);
9041541Srgrimes	return (0);
9051541Srgrimes}
9061541Srgrimes
907207736Smckusickint
908207736Smckusickgetquota32(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
909207736Smckusick{
910207736Smckusick	struct dqblk32 dqb32;
911207736Smckusick	struct dqblk64 dqb64;
912207736Smckusick	int error;
913207736Smckusick
914207736Smckusick	error = _getquota(td, mp, id, type, &dqb64);
915207736Smckusick	if (error)
916207736Smckusick		return (error);
917207736Smckusick	dqb64_dqb32(&dqb64, &dqb32);
918207736Smckusick	error = copyout(&dqb32, addr, sizeof(dqb32));
919207736Smckusick	return (error);
920207736Smckusick}
921207736Smckusick
922207736Smckusickint
923207736Smckusicksetquota32(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
924207736Smckusick{
925207736Smckusick	struct dqblk32 dqb32;
926207736Smckusick	struct dqblk64 dqb64;
927207736Smckusick	int error;
928207736Smckusick
929207736Smckusick	error = copyin(addr, &dqb32, sizeof(dqb32));
930207736Smckusick	if (error)
931207736Smckusick		return (error);
932207736Smckusick	dqb32_dqb64(&dqb32, &dqb64);
933207736Smckusick	error = _setquota(td, mp, id, type, &dqb64);
934207736Smckusick	return (error);
935207736Smckusick}
936207736Smckusick
937207736Smckusickint
938207736Smckusicksetuse32(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
939207736Smckusick{
940207736Smckusick	struct dqblk32 dqb32;
941207736Smckusick	struct dqblk64 dqb64;
942207736Smckusick	int error;
943207736Smckusick
944207736Smckusick	error = copyin(addr, &dqb32, sizeof(dqb32));
945207736Smckusick	if (error)
946207736Smckusick		return (error);
947207736Smckusick	dqb32_dqb64(&dqb32, &dqb64);
948207736Smckusick	error = _setuse(td, mp, id, type, &dqb64);
949207736Smckusick	return (error);
950207736Smckusick}
951207736Smckusick
952207736Smckusickint
953207736Smckusickgetquota(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
954207736Smckusick{
955207736Smckusick	struct dqblk64 dqb64;
956207736Smckusick	int error;
957207736Smckusick
958207736Smckusick	error = _getquota(td, mp, id, type, &dqb64);
959207736Smckusick	if (error)
960207736Smckusick		return (error);
961207736Smckusick	error = copyout(&dqb64, addr, sizeof(dqb64));
962207736Smckusick	return (error);
963207736Smckusick}
964207736Smckusick
965207736Smckusickint
966207736Smckusicksetquota(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
967207736Smckusick{
968207736Smckusick	struct dqblk64 dqb64;
969207736Smckusick	int error;
970207736Smckusick
971207736Smckusick	error = copyin(addr, &dqb64, sizeof(dqb64));
972207736Smckusick	if (error)
973207736Smckusick		return (error);
974207736Smckusick	error = _setquota(td, mp, id, type, &dqb64);
975207736Smckusick	return (error);
976207736Smckusick}
977207736Smckusick
978207736Smckusickint
979207736Smckusicksetuse(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
980207736Smckusick{
981207736Smckusick	struct dqblk64 dqb64;
982207736Smckusick	int error;
983207736Smckusick
984207736Smckusick	error = copyin(addr, &dqb64, sizeof(dqb64));
985207736Smckusick	if (error)
986207736Smckusick		return (error);
987207736Smckusick	error = _setuse(td, mp, id, type, &dqb64);
988207736Smckusick	return (error);
989207736Smckusick}
990207736Smckusick
9911541Srgrimes/*
992207736Smckusick * Q_GETQUOTASIZE - get bit-size of quota file fields
993207736Smckusick */
994207736Smckusickint
995207736Smckusickgetquotasize(struct thread *td, struct mount *mp, u_long id, int type,
996207736Smckusick    void *sizep)
997207736Smckusick{
998207736Smckusick	struct ufsmount *ump = VFSTOUFS(mp);
999207736Smckusick	int bitsize;
1000207736Smckusick
1001207736Smckusick	UFS_LOCK(ump);
1002207736Smckusick	if (ump->um_quotas[type] == NULLVP ||
1003207736Smckusick	    (ump->um_qflags[type] & QTF_CLOSING)) {
1004207736Smckusick		UFS_UNLOCK(ump);
1005207736Smckusick		return (EINVAL);
1006207736Smckusick	}
1007207736Smckusick	if ((ump->um_qflags[type] & QTF_64BIT) != 0)
1008207736Smckusick		bitsize = 64;
1009207736Smckusick	else
1010207736Smckusick		bitsize = 32;
1011207736Smckusick	UFS_UNLOCK(ump);
1012207736Smckusick	return (copyout(&bitsize, sizep, sizeof(int)));
1013207736Smckusick}
1014207736Smckusick
1015207736Smckusick/*
10161541Srgrimes * Q_SYNC - sync quota files to disk.
10171541Srgrimes */
10181541Srgrimesint
1019181327Sdesqsync(struct mount *mp)
10201541Srgrimes{
10211541Srgrimes	struct ufsmount *ump = VFSTOUFS(mp);
102283366Sjulian	struct thread *td = curthread;		/* XXX */
1023154152Stegge	struct vnode *vp, *mvp;
102422521Sdyson	struct dquot *dq;
102522521Sdyson	int i, error;
10261541Srgrimes
10271541Srgrimes	/*
10281541Srgrimes	 * Check if the mount point has any quotas.
10291541Srgrimes	 * If not, simply return.
10301541Srgrimes	 */
1031167543Skib	UFS_LOCK(ump);
10321541Srgrimes	for (i = 0; i < MAXQUOTAS; i++)
10331541Srgrimes		if (ump->um_quotas[i] != NULLVP)
10341541Srgrimes			break;
1035167543Skib	UFS_UNLOCK(ump);
10361541Srgrimes	if (i == MAXQUOTAS)
10371541Srgrimes		return (0);
10381541Srgrimes	/*
10391541Srgrimes	 * Search vnodes associated with this mount point,
10401541Srgrimes	 * synchronizing any modified dquot structures.
10411541Srgrimes	 */
10421541Srgrimesagain:
1043234483Smckusick	MNT_VNODE_FOREACH_ACTIVE(vp, mp, mvp) {
104478912Sjhb		if (vp->v_type == VNON) {
1045120737Sjeff			VI_UNLOCK(vp);
104634266Sjulian			continue;
104778912Sjhb		}
1048155897Sjeff		error = vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td);
104922521Sdyson		if (error) {
1050154152Stegge			if (error == ENOENT) {
1051244239Skib				MNT_VNODE_FOREACH_ACTIVE_ABORT(mp, mvp);
105222521Sdyson				goto again;
1053154152Stegge			}
10541541Srgrimes			continue;
105522521Sdyson		}
10561541Srgrimes		for (i = 0; i < MAXQUOTAS; i++) {
10571541Srgrimes			dq = VTOI(vp)->i_dquot[i];
1058167543Skib			if (dq != NODQUOT)
10591541Srgrimes				dqsync(vp, dq);
10601541Srgrimes		}
1061121847Skan		vput(vp);
10621541Srgrimes	}
10631541Srgrimes	return (0);
10641541Srgrimes}
10651541Srgrimes
10661541Srgrimes/*
1067234483Smckusick * Sync quota file for given vnode to disk.
1068234483Smckusick */
1069234483Smckusickint
1070234483Smckusickqsyncvp(struct vnode *vp)
1071234483Smckusick{
1072234483Smckusick	struct ufsmount *ump = VFSTOUFS(vp->v_mount);
1073234483Smckusick	struct dquot *dq;
1074234483Smckusick	int i;
1075234483Smckusick
1076234483Smckusick	/*
1077234483Smckusick	 * Check if the mount point has any quotas.
1078234483Smckusick	 * If not, simply return.
1079234483Smckusick	 */
1080234483Smckusick	UFS_LOCK(ump);
1081234483Smckusick	for (i = 0; i < MAXQUOTAS; i++)
1082234483Smckusick		if (ump->um_quotas[i] != NULLVP)
1083234483Smckusick			break;
1084234483Smckusick	UFS_UNLOCK(ump);
1085234483Smckusick	if (i == MAXQUOTAS)
1086234483Smckusick		return (0);
1087234483Smckusick	/*
1088234483Smckusick	 * Search quotas associated with this vnode
1089234483Smckusick	 * synchronizing any modified dquot structures.
1090234483Smckusick	 */
1091234483Smckusick	for (i = 0; i < MAXQUOTAS; i++) {
1092234483Smckusick		dq = VTOI(vp)->i_dquot[i];
1093234483Smckusick		if (dq != NODQUOT)
1094234483Smckusick			dqsync(vp, dq);
1095234483Smckusick	}
1096234483Smckusick	return (0);
1097234483Smckusick}
1098234483Smckusick
1099234483Smckusick/*
11001541Srgrimes * Code pertaining to management of the in-core dquot data structures.
11011541Srgrimes */
110222521Sdyson#define DQHASH(dqvp, id) \
110337649Sbde	(&dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & dqhash])
110460938Sjakestatic LIST_HEAD(dqhash, dquot) *dqhashtbl;
110512971Sphkstatic u_long dqhash;
11061541Srgrimes
11071541Srgrimes/*
11081541Srgrimes * Dquot free list.
11091541Srgrimes */
11101541Srgrimes#define	DQUOTINC	5	/* minimum free dquots desired */
111160938Sjakestatic TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
111212971Sphkstatic long numdquot, desireddquot = DQUOTINC;
11131541Srgrimes
1114181327Sdes/*
1115167543Skib * Lock to protect quota hash, dq free list and dq_cnt ref counters of
1116167543Skib * _all_ dqs.
1117167543Skib */
1118167543Skibstruct mtx dqhlock;
1119167543Skib
1120167543Skib#define	DQH_LOCK()	mtx_lock(&dqhlock)
1121167543Skib#define	DQH_UNLOCK()	mtx_unlock(&dqhlock)
1122167543Skib
1123167543Skibstatic struct dquot *dqhashfind(struct dqhash *dqh, u_long id,
1124167543Skib	struct vnode *dqvp);
1125167543Skib
11261541Srgrimes/*
11271541Srgrimes * Initialize the quota system.
11281541Srgrimes */
11291541Srgrimesvoid
1130181327Sdesdqinit(void)
11311541Srgrimes{
11321541Srgrimes
1133167543Skib	mtx_init(&dqhlock, "dqhlock", NULL, MTX_DEF);
11341541Srgrimes	dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
113522521Sdyson	TAILQ_INIT(&dqfreelist);
11361541Srgrimes}
11371541Srgrimes
11381541Srgrimes/*
113999101Siedowse * Shut down the quota system.
114099101Siedowse */
114199101Siedowsevoid
1142181327Sdesdquninit(void)
114399101Siedowse{
114499101Siedowse	struct dquot *dq;
114599101Siedowse
114699101Siedowse	hashdestroy(dqhashtbl, M_DQUOT, dqhash);
114799101Siedowse	while ((dq = TAILQ_FIRST(&dqfreelist)) != NULL) {
114899101Siedowse		TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
1149167543Skib		mtx_destroy(&dq->dq_lock);
115099101Siedowse		free(dq, M_DQUOT);
115199101Siedowse	}
1152167543Skib	mtx_destroy(&dqhlock);
115399101Siedowse}
115499101Siedowse
1155167543Skibstatic struct dquot *
1156181327Sdesdqhashfind(struct dqhash *dqh, u_long id, struct vnode *dqvp)
1157167543Skib{
1158167543Skib	struct dquot *dq;
1159167543Skib
1160167543Skib	mtx_assert(&dqhlock, MA_OWNED);
1161167543Skib	LIST_FOREACH(dq, dqh, dq_hash) {
1162167543Skib		if (dq->dq_id != id ||
1163167543Skib		    dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
1164167543Skib			continue;
1165167543Skib		/*
1166167543Skib		 * Cache hit with no references.  Take
1167167543Skib		 * the structure off the free list.
1168167543Skib		 */
1169167543Skib		if (dq->dq_cnt == 0)
1170167543Skib			TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
1171167543Skib		DQREF(dq);
1172167543Skib		return (dq);
1173167543Skib	}
1174167543Skib	return (NODQUOT);
1175167543Skib}
1176167543Skib
117799101Siedowse/*
1178207736Smckusick * Determine the quota file type.
1179207736Smckusick *
1180207736Smckusick * A 32-bit quota file is simply an array of struct dqblk32.
1181207736Smckusick *
1182207736Smckusick * A 64-bit quota file is a struct dqhdr64 followed by an array of struct
1183207736Smckusick * dqblk64.  The header contains various magic bits which allow us to be
1184207736Smckusick * reasonably confident that it is indeeda 64-bit quota file and not just
1185207736Smckusick * a 32-bit quota file that just happens to "look right".
1186207736Smckusick *
1187207736Smckusick */
1188207736Smckusickstatic int
1189207736Smckusickdqopen(struct vnode *vp, struct ufsmount *ump, int type)
1190207736Smckusick{
1191207736Smckusick	struct dqhdr64 dqh;
1192207736Smckusick	struct iovec aiov;
1193207736Smckusick	struct uio auio;
1194208774Skib	int error;
1195207736Smckusick
1196208774Skib	ASSERT_VOP_LOCKED(vp, "dqopen");
1197207736Smckusick	auio.uio_iov = &aiov;
1198207736Smckusick	auio.uio_iovcnt = 1;
1199207736Smckusick	aiov.iov_base = &dqh;
1200207736Smckusick	aiov.iov_len = sizeof(dqh);
1201207736Smckusick	auio.uio_resid = sizeof(dqh);
1202207736Smckusick	auio.uio_offset = 0;
1203207736Smckusick	auio.uio_segflg = UIO_SYSSPACE;
1204207736Smckusick	auio.uio_rw = UIO_READ;
1205207736Smckusick	auio.uio_td = (struct thread *)0;
1206207736Smckusick	error = VOP_READ(vp, &auio, 0, ump->um_cred[type]);
1207207736Smckusick
1208207736Smckusick	if (error != 0)
1209207736Smckusick		return (error);
1210207736Smckusick	if (auio.uio_resid > 0) {
1211207736Smckusick		/* assume 32 bits */
1212207736Smckusick		return (0);
1213207736Smckusick	}
1214207736Smckusick
1215207736Smckusick	UFS_LOCK(ump);
1216207736Smckusick	if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) == 0 &&
1217207736Smckusick	    be32toh(dqh.dqh_version) == Q_DQHDR64_VERSION &&
1218207736Smckusick	    be32toh(dqh.dqh_hdrlen) == (uint32_t)sizeof(struct dqhdr64) &&
1219207736Smckusick	    be32toh(dqh.dqh_reclen) == (uint32_t)sizeof(struct dqblk64)) {
1220207736Smckusick		/* XXX: what if the magic matches, but the sizes are wrong? */
1221207736Smckusick		ump->um_qflags[type] |= QTF_64BIT;
1222207736Smckusick	} else {
1223207736Smckusick		ump->um_qflags[type] &= ~QTF_64BIT;
1224207736Smckusick	}
1225207736Smckusick	UFS_UNLOCK(ump);
1226207736Smckusick
1227207736Smckusick	return (0);
1228207736Smckusick}
1229207736Smckusick
1230207736Smckusick/*
12311541Srgrimes * Obtain a dquot structure for the specified identifier and quota file
12321541Srgrimes * reading the information from the file if necessary.
12331541Srgrimes */
123412971Sphkstatic int
1235181327Sdesdqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type,
1236181327Sdes    struct dquot **dqp)
12371541Srgrimes{
1238207736Smckusick	uint8_t buf[sizeof(struct dqblk64)];
1239207736Smckusick	off_t base, recsize;
1240167543Skib	struct dquot *dq, *dq1;
124122521Sdyson	struct dqhash *dqh;
124222521Sdyson	struct vnode *dqvp;
12431541Srgrimes	struct iovec aiov;
12441541Srgrimes	struct uio auio;
1245241896Skib	int dqvplocked, error;
12461541Srgrimes
1247167543Skib#ifdef DEBUG_VFS_LOCKS
1248167543Skib	if (vp != NULLVP)
1249167543Skib		ASSERT_VOP_ELOCKED(vp, "dqget");
1250167543Skib#endif
1251167543Skib
1252167543Skib	if (vp != NULLVP && *dqp != NODQUOT) {
1253167543Skib		return (0);
1254167543Skib	}
1255167543Skib
1256166380Smpp	/* XXX: Disallow negative id values to prevent the
1257166380Smpp	* creation of 100GB+ quota data files.
1258166380Smpp	*/
1259166380Smpp	if ((int)id < 0)
1260166380Smpp		return (EINVAL);
1261167543Skib
1262167543Skib	UFS_LOCK(ump);
12631541Srgrimes	dqvp = ump->um_quotas[type];
12641541Srgrimes	if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
12651541Srgrimes		*dqp = NODQUOT;
1266167543Skib		UFS_UNLOCK(ump);
12671541Srgrimes		return (EINVAL);
12681541Srgrimes	}
1269167543Skib	vref(dqvp);
1270167543Skib	UFS_UNLOCK(ump);
1271167543Skib	error = 0;
1272167543Skib	dqvplocked = 0;
1273167543Skib
12741541Srgrimes	/*
12751541Srgrimes	 * Check the cache first.
12761541Srgrimes	 */
127722521Sdyson	dqh = DQHASH(dqvp, id);
1278167543Skib	DQH_LOCK();
1279167543Skib	dq = dqhashfind(dqh, id, dqvp);
1280167543Skib	if (dq != NULL) {
1281167543Skib		DQH_UNLOCK();
1282167543Skibhfound:		DQI_LOCK(dq);
1283167543Skib		DQI_WAIT(dq, PINOD+1, "dqget");
1284167543Skib		DQI_UNLOCK(dq);
1285167543Skib		if (dq->dq_ump == NULL) {
1286167543Skib			dqrele(vp, dq);
1287167543Skib			dq = NODQUOT;
1288167543Skib			error = EIO;
1289167543Skib		}
1290167543Skib		*dqp = dq;
1291167543Skib		if (dqvplocked)
1292167543Skib			vput(dqvp);
1293167543Skib		else
1294167543Skib			vrele(dqvp);
1295167543Skib		return (error);
1296167543Skib	}
1297167543Skib
1298167543Skib	/*
1299167543Skib	 * Quota vnode lock is before DQ_LOCK. Acquire dqvp lock there
1300167543Skib	 * since new dq will appear on the hash chain DQ_LOCKed.
1301167543Skib	 */
1302167543Skib	if (vp != dqvp) {
1303167543Skib		DQH_UNLOCK();
1304175202Sattilio		vn_lock(dqvp, LK_SHARED | LK_RETRY);
1305167543Skib		dqvplocked = 1;
1306167543Skib		DQH_LOCK();
13071541Srgrimes		/*
1308167543Skib		 * Recheck the cache after sleep for quota vnode lock.
13091541Srgrimes		 */
1310167543Skib		dq = dqhashfind(dqh, id, dqvp);
1311167543Skib		if (dq != NULL) {
1312167543Skib			DQH_UNLOCK();
1313167543Skib			goto hfound;
1314167543Skib		}
13151541Srgrimes	}
1316167543Skib
13171541Srgrimes	/*
1318167543Skib	 * Not in cache, allocate a new one or take it from the
1319167543Skib	 * free list.
13201541Srgrimes	 */
132171999Sphk	if (TAILQ_FIRST(&dqfreelist) == NODQUOT &&
132222521Sdyson	    numdquot < MAXQUOTAS * desiredvnodes)
13231541Srgrimes		desireddquot += DQUOTINC;
13241541Srgrimes	if (numdquot < desireddquot) {
1325167543Skib		numdquot++;
1326167543Skib		DQH_UNLOCK();
1327207736Smckusick		dq1 = malloc(sizeof *dq1, M_DQUOT, M_WAITOK | M_ZERO);
1328167543Skib		mtx_init(&dq1->dq_lock, "dqlock", NULL, MTX_DEF);
1329167543Skib		DQH_LOCK();
1330167543Skib		/*
1331167543Skib		 * Recheck the cache after sleep for memory.
1332167543Skib		 */
1333167543Skib		dq = dqhashfind(dqh, id, dqvp);
1334167543Skib		if (dq != NULL) {
1335167543Skib			numdquot--;
1336167543Skib			DQH_UNLOCK();
1337167543Skib			mtx_destroy(&dq1->dq_lock);
1338167543Skib			free(dq1, M_DQUOT);
1339167543Skib			goto hfound;
1340167543Skib		}
1341167543Skib		dq = dq1;
13421541Srgrimes	} else {
134371999Sphk		if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) {
1344167543Skib			DQH_UNLOCK();
13451541Srgrimes			tablefull("dquot");
13461541Srgrimes			*dqp = NODQUOT;
1347167543Skib			if (dqvplocked)
1348167543Skib				vput(dqvp);
1349167543Skib			else
1350167543Skib				vrele(dqvp);
13511541Srgrimes			return (EUSERS);
13521541Srgrimes		}
13531541Srgrimes		if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
1354185739Skib			panic("dqget: free dquot isn't %p", dq);
135522521Sdyson		TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
135689213Sphk		if (dq->dq_ump != NULL)
135789213Sphk			LIST_REMOVE(dq, dq_hash);
13581541Srgrimes	}
1359167543Skib
13601541Srgrimes	/*
1361167543Skib	 * Dq is put into hash already locked to prevent parallel
1362167543Skib	 * usage while it is being read from file.
13631541Srgrimes	 */
13641541Srgrimes	dq->dq_flags = DQ_LOCK;
13651541Srgrimes	dq->dq_id = id;
1366167543Skib	dq->dq_type = type;
13671541Srgrimes	dq->dq_ump = ump;
1368167543Skib	LIST_INSERT_HEAD(dqh, dq, dq_hash);
1369167543Skib	DQREF(dq);
1370167543Skib	DQH_UNLOCK();
1371167543Skib
1372207736Smckusick	/*
1373207736Smckusick	 * Read the requested quota record from the quota file, performing
1374207736Smckusick	 * any necessary conversions.
1375207736Smckusick	 */
1376207736Smckusick	if (ump->um_qflags[type] & QTF_64BIT) {
1377207736Smckusick		recsize = sizeof(struct dqblk64);
1378207736Smckusick		base = sizeof(struct dqhdr64);
1379207736Smckusick	} else {
1380207736Smckusick		recsize = sizeof(struct dqblk32);
1381207736Smckusick		base = 0;
1382207736Smckusick	}
13831541Srgrimes	auio.uio_iov = &aiov;
13841541Srgrimes	auio.uio_iovcnt = 1;
1385207736Smckusick	aiov.iov_base = buf;
1386207736Smckusick	aiov.iov_len = recsize;
1387207736Smckusick	auio.uio_resid = recsize;
1388207736Smckusick	auio.uio_offset = base + id * recsize;
13891541Srgrimes	auio.uio_segflg = UIO_SYSSPACE;
13901541Srgrimes	auio.uio_rw = UIO_READ;
139183366Sjulian	auio.uio_td = (struct thread *)0;
1392167543Skib
13931541Srgrimes	error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
1394207736Smckusick	if (auio.uio_resid == recsize && error == 0) {
1395207736Smckusick		bzero(&dq->dq_dqb, sizeof(dq->dq_dqb));
1396207736Smckusick	} else {
1397207736Smckusick		if (ump->um_qflags[type] & QTF_64BIT)
1398207736Smckusick			dqb64_dq((struct dqblk64 *)buf, dq);
1399207736Smckusick		else
1400207736Smckusick			dqb32_dq((struct dqblk32 *)buf, dq);
1401207736Smckusick	}
1402167543Skib	if (dqvplocked)
1403167543Skib		vput(dqvp);
1404167543Skib	else
1405167543Skib		vrele(dqvp);
14061541Srgrimes	/*
14071541Srgrimes	 * I/O error in reading quota file, release
14081541Srgrimes	 * quota structure and reflect problem to caller.
14091541Srgrimes	 */
14101541Srgrimes	if (error) {
1411167543Skib		DQH_LOCK();
1412167543Skib		dq->dq_ump = NULL;
141322521Sdyson		LIST_REMOVE(dq, dq_hash);
1414167543Skib		DQH_UNLOCK();
1415167543Skib		DQI_LOCK(dq);
1416167543Skib		if (dq->dq_flags & DQ_WANT)
1417167543Skib			wakeup(dq);
1418167543Skib		dq->dq_flags = 0;
1419167543Skib		DQI_UNLOCK(dq);
14201541Srgrimes		dqrele(vp, dq);
14211541Srgrimes		*dqp = NODQUOT;
14221541Srgrimes		return (error);
14231541Srgrimes	}
1424167543Skib	DQI_LOCK(dq);
14251541Srgrimes	/*
14261541Srgrimes	 * Check for no limit to enforce.
14271541Srgrimes	 * Initialize time values if necessary.
14281541Srgrimes	 */
14291541Srgrimes	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
14301541Srgrimes	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
14311541Srgrimes		dq->dq_flags |= DQ_FAKE;
14321541Srgrimes	if (dq->dq_id != 0) {
1433166487Smpp		if (dq->dq_btime == 0) {
143434961Sphk			dq->dq_btime = time_second + ump->um_btime[type];
1435166487Smpp			if (dq->dq_bsoftlimit &&
1436166487Smpp			    dq->dq_curblocks >= dq->dq_bsoftlimit)
1437166487Smpp				dq->dq_flags |= DQ_MOD;
1438166487Smpp		}
1439166487Smpp		if (dq->dq_itime == 0) {
144034961Sphk			dq->dq_itime = time_second + ump->um_itime[type];
1441166487Smpp			if (dq->dq_isoftlimit &&
1442166487Smpp			    dq->dq_curinodes >= dq->dq_isoftlimit)
1443166487Smpp				dq->dq_flags |= DQ_MOD;
1444166487Smpp		}
14451541Srgrimes	}
1446167543Skib	DQI_WAKEUP(dq);
1447167543Skib	DQI_UNLOCK(dq);
14481541Srgrimes	*dqp = dq;
14491541Srgrimes	return (0);
14501541Srgrimes}
14511541Srgrimes
145217040Swollman#ifdef DIAGNOSTIC
14531541Srgrimes/*
14541541Srgrimes * Obtain a reference to a dquot.
14551541Srgrimes */
145612971Sphkstatic void
1457181327Sdesdqref(struct dquot *dq)
14581541Srgrimes{
14591541Srgrimes
14601541Srgrimes	dq->dq_cnt++;
14611541Srgrimes}
146217040Swollman#endif
14631541Srgrimes
14641541Srgrimes/*
14651541Srgrimes * Release a reference to a dquot.
14661541Srgrimes */
14671541Srgrimesvoid
1468181327Sdesdqrele(struct vnode *vp, struct dquot *dq)
14691541Srgrimes{
14701541Srgrimes
14711541Srgrimes	if (dq == NODQUOT)
14721541Srgrimes		return;
1473167543Skib	DQH_LOCK();
1474232003Skib	KASSERT(dq->dq_cnt > 0, ("Lost dq %p reference 1", dq));
14751541Srgrimes	if (dq->dq_cnt > 1) {
14761541Srgrimes		dq->dq_cnt--;
1477167543Skib		DQH_UNLOCK();
14781541Srgrimes		return;
14791541Srgrimes	}
1480167543Skib	DQH_UNLOCK();
1481185761Skibsync:
1482167543Skib	(void) dqsync(vp, dq);
1483167543Skib
1484167543Skib	DQH_LOCK();
1485232003Skib	KASSERT(dq->dq_cnt > 0, ("Lost dq %p reference 2", dq));
14861541Srgrimes	if (--dq->dq_cnt > 0)
1487167543Skib	{
1488167543Skib		DQH_UNLOCK();
14891541Srgrimes		return;
1490167543Skib	}
1491185761Skib
1492185761Skib	/*
1493185761Skib	 * The dq may become dirty after it is synced but before it is
1494185761Skib	 * put to the free list. Checking the DQ_MOD there without
1495185761Skib	 * locking dq should be safe since no other references to the
1496185761Skib	 * dq exist.
1497185761Skib	 */
1498185761Skib	if ((dq->dq_flags & DQ_MOD) != 0) {
1499185761Skib		dq->dq_cnt++;
1500185761Skib		DQH_UNLOCK();
1501185761Skib		goto sync;
1502185761Skib	}
150322521Sdyson	TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
1504167543Skib	DQH_UNLOCK();
15051541Srgrimes}
15061541Srgrimes
15071541Srgrimes/*
15081541Srgrimes * Update the disk quota in the quota file.
15091541Srgrimes */
151012971Sphkstatic int
1511181327Sdesdqsync(struct vnode *vp, struct dquot *dq)
15121541Srgrimes{
1513207736Smckusick	uint8_t buf[sizeof(struct dqblk64)];
1514207736Smckusick	off_t base, recsize;
15151541Srgrimes	struct vnode *dqvp;
15161541Srgrimes	struct iovec aiov;
15171541Srgrimes	struct uio auio;
1518241896Skib	int error;
1519156451Stegge	struct mount *mp;
1520167543Skib	struct ufsmount *ump;
15211541Srgrimes
1522167543Skib#ifdef DEBUG_VFS_LOCKS
1523167543Skib	if (vp != NULL)
1524167543Skib		ASSERT_VOP_ELOCKED(vp, "dqsync");
1525167543Skib#endif
1526167543Skib
1527156451Stegge	mp = NULL;
1528167543Skib	error = 0;
15291541Srgrimes	if (dq == NODQUOT)
15301541Srgrimes		panic("dqsync: dquot");
1531167543Skib	if ((ump = dq->dq_ump) == NULL)
15321541Srgrimes		return (0);
1533167543Skib	UFS_LOCK(ump);
1534167543Skib	if ((dqvp = ump->um_quotas[dq->dq_type]) == NULLVP)
15351541Srgrimes		panic("dqsync: file");
1536167543Skib	vref(dqvp);
1537167543Skib	UFS_UNLOCK(ump);
1538167543Skib
1539167543Skib	DQI_LOCK(dq);
1540167543Skib	if ((dq->dq_flags & DQ_MOD) == 0) {
1541167543Skib		DQI_UNLOCK(dq);
1542167543Skib		vrele(dqvp);
1543167543Skib		return (0);
1544167543Skib	}
1545167543Skib	DQI_UNLOCK(dq);
1546167543Skib
1547156451Stegge	(void) vn_start_secondary_write(dqvp, &mp, V_WAIT);
15481541Srgrimes	if (vp != dqvp)
1549175202Sattilio		vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
1550167543Skib
1551167543Skib	DQI_LOCK(dq);
1552167543Skib	DQI_WAIT(dq, PINOD+2, "dqsync");
1553167543Skib	if ((dq->dq_flags & DQ_MOD) == 0)
1554167543Skib		goto out;
15551541Srgrimes	dq->dq_flags |= DQ_LOCK;
1556167543Skib	DQI_UNLOCK(dq);
1557167543Skib
1558207736Smckusick	/*
1559207736Smckusick	 * Write the quota record to the quota file, performing any
1560207736Smckusick	 * necessary conversions.  See dqget() for additional details.
1561207736Smckusick	 */
1562207736Smckusick	if (ump->um_qflags[dq->dq_type] & QTF_64BIT) {
1563207736Smckusick		dq_dqb64(dq, (struct dqblk64 *)buf);
1564207736Smckusick		recsize = sizeof(struct dqblk64);
1565207736Smckusick		base = sizeof(struct dqhdr64);
1566207736Smckusick	} else {
1567207736Smckusick		dq_dqb32(dq, (struct dqblk32 *)buf);
1568207736Smckusick		recsize = sizeof(struct dqblk32);
1569207736Smckusick		base = 0;
1570207736Smckusick	}
1571207736Smckusick
15721541Srgrimes	auio.uio_iov = &aiov;
15731541Srgrimes	auio.uio_iovcnt = 1;
1574207736Smckusick	aiov.iov_base = buf;
1575207736Smckusick	aiov.iov_len = recsize;
1576207736Smckusick	auio.uio_resid = recsize;
1577207736Smckusick	auio.uio_offset = base + dq->dq_id * recsize;
15781541Srgrimes	auio.uio_segflg = UIO_SYSSPACE;
15791541Srgrimes	auio.uio_rw = UIO_WRITE;
158083366Sjulian	auio.uio_td = (struct thread *)0;
15811541Srgrimes	error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
15821541Srgrimes	if (auio.uio_resid && error == 0)
15831541Srgrimes		error = EIO;
1584167543Skib
1585167543Skib	DQI_LOCK(dq);
1586167543Skib	DQI_WAKEUP(dq);
1587167543Skib	dq->dq_flags &= ~DQ_MOD;
1588207736Smckusickout:
1589207736Smckusick	DQI_UNLOCK(dq);
15901541Srgrimes	if (vp != dqvp)
1591167543Skib		vput(dqvp);
1592167543Skib	else
1593167543Skib		vrele(dqvp);
1594156451Stegge	vn_finished_secondary_write(mp);
15951541Srgrimes	return (error);
15961541Srgrimes}
15971541Srgrimes
15981541Srgrimes/*
15991541Srgrimes * Flush all entries from the cache for a particular vnode.
16001541Srgrimes */
1601247388Skibstatic int
1602181327Sdesdqflush(struct vnode *vp)
16031541Srgrimes{
160496506Sphk	struct dquot *dq, *nextdq;
160522521Sdyson	struct dqhash *dqh;
1606247388Skib	int error;
16071541Srgrimes
16081541Srgrimes	/*
16091541Srgrimes	 * Move all dquot's that used to refer to this quota
16101541Srgrimes	 * file off their hash chains (they will eventually
16111541Srgrimes	 * fall off the head of the free list and be re-used).
16121541Srgrimes	 */
1613247388Skib	error = 0;
1614167543Skib	DQH_LOCK();
161522521Sdyson	for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
161671999Sphk		for (dq = LIST_FIRST(dqh); dq; dq = nextdq) {
161771999Sphk			nextdq = LIST_NEXT(dq, dq_hash);
16181541Srgrimes			if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
16191541Srgrimes				continue;
16201541Srgrimes			if (dq->dq_cnt)
1621247388Skib				error = EBUSY;
1622247388Skib			else {
1623247388Skib				LIST_REMOVE(dq, dq_hash);
1624247388Skib				dq->dq_ump = NULL;
1625247388Skib			}
16261541Srgrimes		}
16271541Srgrimes	}
1628167543Skib	DQH_UNLOCK();
1629247388Skib	return (error);
16301541Srgrimes}
1631207736Smckusick
1632207736Smckusick/*
1633222955Sjeff * The following three functions are provided for the adjustment of
1634222955Sjeff * quotas by the soft updates code.
1635222955Sjeff */
1636222955Sjeff#ifdef SOFTUPDATES
1637222955Sjeff/*
1638222955Sjeff * Acquire a reference to the quota structures associated with a vnode.
1639222955Sjeff * Return count of number of quota structures found.
1640222955Sjeff */
1641222955Sjeffint
1642222955Sjeffquotaref(vp, qrp)
1643222955Sjeff	struct vnode *vp;
1644222955Sjeff	struct dquot **qrp;
1645222955Sjeff{
1646222955Sjeff	struct inode *ip;
1647222955Sjeff	struct dquot *dq;
1648222955Sjeff	int i, found;
1649222955Sjeff
1650222955Sjeff	for (i = 0; i < MAXQUOTAS; i++)
1651222955Sjeff		qrp[i] = NODQUOT;
1652222955Sjeff	/*
1653222955Sjeff	 * Disk quotas must be turned off for system files.  Currently
1654222955Sjeff	 * snapshot and quota files.
1655222955Sjeff	 */
1656222955Sjeff	if ((vp->v_vflag & VV_SYSTEM) != 0)
1657222955Sjeff		return (0);
1658222955Sjeff	/*
1659222955Sjeff	 * Iterate through and copy active quotas.
1660222955Sjeff	 */
1661222955Sjeff	found = 0;
1662222955Sjeff	ip = VTOI(vp);
1663232003Skib	mtx_lock(&dqhlock);
1664222955Sjeff	for (i = 0; i < MAXQUOTAS; i++) {
1665222955Sjeff		if ((dq = ip->i_dquot[i]) == NODQUOT)
1666222955Sjeff			continue;
1667222955Sjeff		DQREF(dq);
1668222955Sjeff		qrp[i] = dq;
1669222955Sjeff		found++;
1670222955Sjeff	}
1671232003Skib	mtx_unlock(&dqhlock);
1672222955Sjeff	return (found);
1673222955Sjeff}
1674222955Sjeff
1675222955Sjeff/*
1676222955Sjeff * Release a set of quota structures obtained from a vnode.
1677222955Sjeff */
1678222955Sjeffvoid
1679222955Sjeffquotarele(qrp)
1680222955Sjeff	struct dquot **qrp;
1681222955Sjeff{
1682222955Sjeff	struct dquot *dq;
1683222955Sjeff	int i;
1684222955Sjeff
1685222955Sjeff	for (i = 0; i < MAXQUOTAS; i++) {
1686222955Sjeff		if ((dq = qrp[i]) == NODQUOT)
1687222955Sjeff			continue;
1688222955Sjeff		dqrele(NULL, dq);
1689222955Sjeff	}
1690222955Sjeff}
1691222955Sjeff
1692222955Sjeff/*
1693222955Sjeff * Adjust the number of blocks associated with a quota.
1694222955Sjeff * Positive numbers when adding blocks; negative numbers when freeing blocks.
1695222955Sjeff */
1696222955Sjeffvoid
1697222955Sjeffquotaadj(qrp, ump, blkcount)
1698222955Sjeff	struct dquot **qrp;
1699222955Sjeff	struct ufsmount *ump;
1700222955Sjeff	int64_t blkcount;
1701222955Sjeff{
1702222955Sjeff	struct dquot *dq;
1703222955Sjeff	ufs2_daddr_t ncurblocks;
1704222955Sjeff	int i;
1705222955Sjeff
1706222955Sjeff	if (blkcount == 0)
1707222955Sjeff		return;
1708222955Sjeff	for (i = 0; i < MAXQUOTAS; i++) {
1709222955Sjeff		if ((dq = qrp[i]) == NODQUOT)
1710222955Sjeff			continue;
1711222955Sjeff		DQI_LOCK(dq);
1712222955Sjeff		DQI_WAIT(dq, PINOD+1, "adjqta");
1713222955Sjeff		ncurblocks = dq->dq_curblocks + blkcount;
1714222955Sjeff		if (ncurblocks >= 0)
1715222955Sjeff			dq->dq_curblocks = ncurblocks;
1716222955Sjeff		else
1717222955Sjeff			dq->dq_curblocks = 0;
1718222955Sjeff		if (blkcount < 0)
1719222955Sjeff			dq->dq_flags &= ~DQ_BLKS;
1720222955Sjeff		else if (dq->dq_curblocks + blkcount >= dq->dq_bsoftlimit &&
1721222955Sjeff			 dq->dq_curblocks < dq->dq_bsoftlimit)
1722222955Sjeff			dq->dq_btime = time_second + ump->um_btime[i];
1723222955Sjeff		dq->dq_flags |= DQ_MOD;
1724222955Sjeff		DQI_UNLOCK(dq);
1725222955Sjeff	}
1726222955Sjeff}
1727222955Sjeff#endif /* SOFTUPDATES */
1728222955Sjeff
1729222955Sjeff/*
1730207736Smckusick * 32-bit / 64-bit conversion functions.
1731207736Smckusick *
1732207736Smckusick * 32-bit quota records are stored in native byte order.  Attention must
1733207736Smckusick * be paid to overflow issues.
1734207736Smckusick *
1735207736Smckusick * 64-bit quota records are stored in network byte order.
1736207736Smckusick */
1737207736Smckusick
1738207736Smckusick#define CLIP32(u64) (u64 > UINT32_MAX ? UINT32_MAX : (uint32_t)u64)
1739207736Smckusick
1740207736Smckusick/*
1741207736Smckusick * Convert 32-bit host-order structure to dquot.
1742207736Smckusick */
1743207736Smckusickstatic void
1744207736Smckusickdqb32_dq(const struct dqblk32 *dqb32, struct dquot *dq)
1745207736Smckusick{
1746207736Smckusick
1747207736Smckusick	dq->dq_bhardlimit = dqb32->dqb_bhardlimit;
1748207736Smckusick	dq->dq_bsoftlimit = dqb32->dqb_bsoftlimit;
1749207736Smckusick	dq->dq_curblocks = dqb32->dqb_curblocks;
1750207736Smckusick	dq->dq_ihardlimit = dqb32->dqb_ihardlimit;
1751207736Smckusick	dq->dq_isoftlimit = dqb32->dqb_isoftlimit;
1752207736Smckusick	dq->dq_curinodes = dqb32->dqb_curinodes;
1753207736Smckusick	dq->dq_btime = dqb32->dqb_btime;
1754207736Smckusick	dq->dq_itime = dqb32->dqb_itime;
1755207736Smckusick}
1756207736Smckusick
1757207736Smckusick/*
1758207736Smckusick * Convert 64-bit network-order structure to dquot.
1759207736Smckusick */
1760207736Smckusickstatic void
1761207736Smckusickdqb64_dq(const struct dqblk64 *dqb64, struct dquot *dq)
1762207736Smckusick{
1763207736Smckusick
1764207736Smckusick	dq->dq_bhardlimit = be64toh(dqb64->dqb_bhardlimit);
1765207736Smckusick	dq->dq_bsoftlimit = be64toh(dqb64->dqb_bsoftlimit);
1766207736Smckusick	dq->dq_curblocks = be64toh(dqb64->dqb_curblocks);
1767207736Smckusick	dq->dq_ihardlimit = be64toh(dqb64->dqb_ihardlimit);
1768207736Smckusick	dq->dq_isoftlimit = be64toh(dqb64->dqb_isoftlimit);
1769207736Smckusick	dq->dq_curinodes = be64toh(dqb64->dqb_curinodes);
1770207736Smckusick	dq->dq_btime = be64toh(dqb64->dqb_btime);
1771207736Smckusick	dq->dq_itime = be64toh(dqb64->dqb_itime);
1772207736Smckusick}
1773207736Smckusick
1774207736Smckusick/*
1775207736Smckusick * Convert dquot to 32-bit host-order structure.
1776207736Smckusick */
1777207736Smckusickstatic void
1778207736Smckusickdq_dqb32(const struct dquot *dq, struct dqblk32 *dqb32)
1779207736Smckusick{
1780207736Smckusick
1781207736Smckusick	dqb32->dqb_bhardlimit = CLIP32(dq->dq_bhardlimit);
1782207736Smckusick	dqb32->dqb_bsoftlimit = CLIP32(dq->dq_bsoftlimit);
1783207736Smckusick	dqb32->dqb_curblocks = CLIP32(dq->dq_curblocks);
1784207736Smckusick	dqb32->dqb_ihardlimit = CLIP32(dq->dq_ihardlimit);
1785207736Smckusick	dqb32->dqb_isoftlimit = CLIP32(dq->dq_isoftlimit);
1786207736Smckusick	dqb32->dqb_curinodes = CLIP32(dq->dq_curinodes);
1787207736Smckusick	dqb32->dqb_btime = CLIP32(dq->dq_btime);
1788207736Smckusick	dqb32->dqb_itime = CLIP32(dq->dq_itime);
1789207736Smckusick}
1790207736Smckusick
1791207736Smckusick/*
1792207736Smckusick * Convert dquot to 64-bit network-order structure.
1793207736Smckusick */
1794207736Smckusickstatic void
1795207736Smckusickdq_dqb64(const struct dquot *dq, struct dqblk64 *dqb64)
1796207736Smckusick{
1797207736Smckusick
1798207736Smckusick	dqb64->dqb_bhardlimit = htobe64(dq->dq_bhardlimit);
1799207736Smckusick	dqb64->dqb_bsoftlimit = htobe64(dq->dq_bsoftlimit);
1800207736Smckusick	dqb64->dqb_curblocks = htobe64(dq->dq_curblocks);
1801207736Smckusick	dqb64->dqb_ihardlimit = htobe64(dq->dq_ihardlimit);
1802207736Smckusick	dqb64->dqb_isoftlimit = htobe64(dq->dq_isoftlimit);
1803207736Smckusick	dqb64->dqb_curinodes = htobe64(dq->dq_curinodes);
1804207736Smckusick	dqb64->dqb_btime = htobe64(dq->dq_btime);
1805207736Smckusick	dqb64->dqb_itime = htobe64(dq->dq_itime);
1806207736Smckusick}
1807207736Smckusick
1808207736Smckusick/*
1809207736Smckusick * Convert 64-bit host-order structure to 32-bit host-order structure.
1810207736Smckusick */
1811207736Smckusickstatic void
1812207736Smckusickdqb64_dqb32(const struct dqblk64 *dqb64, struct dqblk32 *dqb32)
1813207736Smckusick{
1814207736Smckusick
1815207736Smckusick	dqb32->dqb_bhardlimit = CLIP32(dqb64->dqb_bhardlimit);
1816207736Smckusick	dqb32->dqb_bsoftlimit = CLIP32(dqb64->dqb_bsoftlimit);
1817207736Smckusick	dqb32->dqb_curblocks = CLIP32(dqb64->dqb_curblocks);
1818207736Smckusick	dqb32->dqb_ihardlimit = CLIP32(dqb64->dqb_ihardlimit);
1819207736Smckusick	dqb32->dqb_isoftlimit = CLIP32(dqb64->dqb_isoftlimit);
1820207736Smckusick	dqb32->dqb_curinodes = CLIP32(dqb64->dqb_curinodes);
1821207736Smckusick	dqb32->dqb_btime = CLIP32(dqb64->dqb_btime);
1822207736Smckusick	dqb32->dqb_itime = CLIP32(dqb64->dqb_itime);
1823207736Smckusick}
1824207736Smckusick
1825207736Smckusick/*
1826207736Smckusick * Convert 32-bit host-order structure to 64-bit host-order structure.
1827207736Smckusick */
1828207736Smckusickstatic void
1829207736Smckusickdqb32_dqb64(const struct dqblk32 *dqb32, struct dqblk64 *dqb64)
1830207736Smckusick{
1831207736Smckusick
1832207736Smckusick	dqb64->dqb_bhardlimit = dqb32->dqb_bhardlimit;
1833207736Smckusick	dqb64->dqb_bsoftlimit = dqb32->dqb_bsoftlimit;
1834207736Smckusick	dqb64->dqb_curblocks = dqb32->dqb_curblocks;
1835207736Smckusick	dqb64->dqb_ihardlimit = dqb32->dqb_ihardlimit;
1836207736Smckusick	dqb64->dqb_isoftlimit = dqb32->dqb_isoftlimit;
1837207736Smckusick	dqb64->dqb_curinodes = dqb32->dqb_curinodes;
1838207736Smckusick	dqb64->dqb_btime = dqb32->dqb_btime;
1839207736Smckusick	dqb64->dqb_itime = dqb32->dqb_itime;
1840207736Smckusick}
1841