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: releng/10.3/sys/ufs/ufs/ufs_quota.c 278150 2015-02-03 11:54:33Z kib $");
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;
310167543Skib	int i, error, warn, do_check;
3111541Srgrimes
3121541Srgrimes#ifdef DIAGNOSTIC
3131541Srgrimes	if ((flags & CHOWN) == 0)
3141541Srgrimes		chkdquot(ip);
3151541Srgrimes#endif
3161541Srgrimes	if (change == 0)
3171541Srgrimes		return (0);
3181541Srgrimes	if (change < 0) {
3191541Srgrimes		for (i = 0; i < MAXQUOTAS; i++) {
3201541Srgrimes			if ((dq = ip->i_dquot[i]) == NODQUOT)
3211541Srgrimes				continue;
322167543Skib			DQI_LOCK(dq);
323167543Skib			DQI_WAIT(dq, PINOD+1, "chkiq1");
324260827Smckusick			if (dq->dq_curinodes >= -change)
325260827Smckusick				dq->dq_curinodes += change;
3261541Srgrimes			else
3271541Srgrimes				dq->dq_curinodes = 0;
3281541Srgrimes			dq->dq_flags &= ~DQ_INODS;
3291541Srgrimes			dq->dq_flags |= DQ_MOD;
330167543Skib			DQI_UNLOCK(dq);
3311541Srgrimes		}
3321541Srgrimes		return (0);
3331541Srgrimes	}
334167543Skib	if ((flags & FORCE) == 0 &&
335167543Skib	    priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
336167543Skib		do_check = 1;
337167543Skib	else
338167543Skib		do_check = 0;
3391541Srgrimes	for (i = 0; i < MAXQUOTAS; i++) {
3401541Srgrimes		if ((dq = ip->i_dquot[i]) == NODQUOT)
3411541Srgrimes			continue;
342167543Skib		warn = 0;
343167543Skib		DQI_LOCK(dq);
344167543Skib		DQI_WAIT(dq, PINOD+1, "chkiq2");
345167543Skib		if (do_check) {
346167543Skib			error = chkiqchg(ip, change, cred, i, &warn);
347167543Skib			if (error) {
348167543Skib				/*
349167543Skib				 * Roll back user quota changes when
350167543Skib				 * group quota failed.
351167543Skib				 */
352167543Skib				while (i > 0) {
353167543Skib					--i;
354167543Skib					dq = ip->i_dquot[i];
355167543Skib					if (dq == NODQUOT)
356167543Skib						continue;
357167543Skib					DQI_LOCK(dq);
358167543Skib					DQI_WAIT(dq, PINOD+1, "chkiq3");
359260827Smckusick					if (dq->dq_curinodes >= change)
360260827Smckusick						dq->dq_curinodes -= change;
361167543Skib					else
362167543Skib						dq->dq_curinodes = 0;
363167543Skib					dq->dq_flags &= ~DQ_INODS;
364167543Skib					dq->dq_flags |= DQ_MOD;
365167543Skib					DQI_UNLOCK(dq);
366167543Skib				}
367167543Skib				return (error);
368167543Skib			}
3691541Srgrimes		}
37059721Smckusick		/* Reset timer when crossing soft limit */
37159721Smckusick		if (dq->dq_curinodes + change >= dq->dq_isoftlimit &&
37259721Smckusick		    dq->dq_curinodes < dq->dq_isoftlimit)
373219388Skib			dq->dq_itime = time_second + ip->i_ump->um_itime[i];
3741541Srgrimes		dq->dq_curinodes += change;
3751541Srgrimes		dq->dq_flags |= DQ_MOD;
376167543Skib		DQI_UNLOCK(dq);
377167543Skib		if (warn)
378217357Spluknet			uprintf("\n%s: warning, %s inode quota exceeded\n",
379217357Spluknet			    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
380217357Spluknet			    quotatypes[i]);
3811541Srgrimes	}
3821541Srgrimes	return (0);
3831541Srgrimes}
3841541Srgrimes
3851541Srgrimes/*
3861541Srgrimes * Check for a valid change to a users allocation.
3871541Srgrimes * Issue an error message if appropriate.
3881541Srgrimes */
38912971Sphkstatic int
390181327Sdeschkiqchg(struct inode *ip, int change, struct ucred *cred, int type, int *warn)
3911541Srgrimes{
39296506Sphk	struct dquot *dq = ip->i_dquot[type];
39398542Smckusick	ino_t ncurinodes = dq->dq_curinodes + change;
3941541Srgrimes
3951541Srgrimes	/*
3961541Srgrimes	 * If user would exceed their hard limit, disallow inode allocation.
3971541Srgrimes	 */
3981541Srgrimes	if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
3991541Srgrimes		if ((dq->dq_flags & DQ_INODS) == 0 &&
4001541Srgrimes		    ip->i_uid == cred->cr_uid) {
401167543Skib			dq->dq_flags |= DQ_INODS;
402167543Skib			DQI_UNLOCK(dq);
4031541Srgrimes			uprintf("\n%s: write failed, %s inode limit reached\n",
4041541Srgrimes			    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
4051541Srgrimes			    quotatypes[type]);
406167543Skib			return (EDQUOT);
4071541Srgrimes		}
408167543Skib		DQI_UNLOCK(dq);
4091541Srgrimes		return (EDQUOT);
4101541Srgrimes	}
4111541Srgrimes	/*
4121541Srgrimes	 * If user is over their soft limit for too long, disallow inode
4131541Srgrimes	 * allocation. Reset time limit as they cross their soft limit.
4141541Srgrimes	 */
4151541Srgrimes	if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
4161541Srgrimes		if (dq->dq_curinodes < dq->dq_isoftlimit) {
417219388Skib			dq->dq_itime = time_second + ip->i_ump->um_itime[type];
4181541Srgrimes			if (ip->i_uid == cred->cr_uid)
419167543Skib				*warn = 1;
4201541Srgrimes			return (0);
4211541Srgrimes		}
42234961Sphk		if (time_second > dq->dq_itime) {
4231541Srgrimes			if ((dq->dq_flags & DQ_INODS) == 0 &&
4241541Srgrimes			    ip->i_uid == cred->cr_uid) {
425167543Skib				dq->dq_flags |= DQ_INODS;
426167543Skib				DQI_UNLOCK(dq);
427217357Spluknet				uprintf("\n%s: write failed, %s "
428217357Spluknet				    "inode quota exceeded for too long\n",
429217357Spluknet				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
430217357Spluknet				    quotatypes[type]);
431167543Skib				return (EDQUOT);
4321541Srgrimes			}
433167543Skib			DQI_UNLOCK(dq);
4341541Srgrimes			return (EDQUOT);
4351541Srgrimes		}
4361541Srgrimes	}
4371541Srgrimes	return (0);
4381541Srgrimes}
4391541Srgrimes
4401541Srgrimes#ifdef DIAGNOSTIC
4411541Srgrimes/*
4421541Srgrimes * On filesystems with quotas enabled, it is an error for a file to change
4431541Srgrimes * size and not to have a dquot structure associated with it.
4441541Srgrimes */
44512971Sphkstatic void
446181327Sdeschkdquot(struct inode *ip)
4471541Srgrimes{
448219388Skib	struct ufsmount *ump = ip->i_ump;
449166142Smpp	struct vnode *vp = ITOV(ip);
45096506Sphk	int i;
4511541Srgrimes
452158322Stegge	/*
453166142Smpp	 * Disk quotas must be turned off for system files.  Currently
454166142Smpp	 * these are snapshots and quota files.
455158322Stegge	 */
456166142Smpp	if ((vp->v_vflag & VV_SYSTEM) != 0)
457158322Stegge		return;
458166142Smpp	/*
459166142Smpp	 * XXX: Turn off quotas for files with a negative UID or GID.
460166142Smpp	 * This prevents the creation of 100GB+ quota files.
461166142Smpp	 */
462166142Smpp	if ((int)ip->i_uid < 0 || (int)ip->i_gid < 0)
463166146Sdelphij		return;
464167543Skib
465167543Skib	UFS_LOCK(ump);
4661541Srgrimes	for (i = 0; i < MAXQUOTAS; i++) {
4671541Srgrimes		if (ump->um_quotas[i] == NULLVP ||
4681541Srgrimes		    (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING)))
4691541Srgrimes			continue;
4701541Srgrimes		if (ip->i_dquot[i] == NODQUOT) {
471167543Skib			UFS_UNLOCK(ump);
4721541Srgrimes			vprint("chkdquot: missing dquot", ITOV(ip));
47323562Smpp			panic("chkdquot: missing dquot");
4741541Srgrimes		}
4751541Srgrimes	}
476167543Skib	UFS_UNLOCK(ump);
4771541Srgrimes}
4781541Srgrimes#endif
4791541Srgrimes
4801541Srgrimes/*
4811541Srgrimes * Code to process quotactl commands.
4821541Srgrimes */
4831541Srgrimes
4841541Srgrimes/*
48596755Strhodes * Q_QUOTAON - set up a quota file for a particular filesystem.
4861541Srgrimes */
4871541Srgrimesint
488181327Sdesquotaon(struct thread *td, struct mount *mp, int type, void *fname)
4891541Srgrimes{
490166743Skib	struct ufsmount *ump;
49122521Sdyson	struct vnode *vp, **vpp;
492154152Stegge	struct vnode *mvp;
4931541Srgrimes	struct dquot *dq;
494241896Skib	int error, flags;
4951541Srgrimes	struct nameidata nd;
4961541Srgrimes
497170587Srwatson	error = priv_check(td, PRIV_UFS_QUOTAON);
498278150Skib	if (error != 0) {
499278150Skib		vfs_unbusy(mp);
500116384Srwatson		return (error);
501278150Skib	}
502116384Srwatson
503278150Skib	if ((mp->mnt_flag & MNT_RDONLY) != 0) {
504278150Skib		vfs_unbusy(mp);
505207736Smckusick		return (EROFS);
506278150Skib	}
507207736Smckusick
508166743Skib	ump = VFSTOUFS(mp);
509167543Skib	dq = NODQUOT;
510167543Skib
511241896Skib	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, td);
51262550Smckusick	flags = FREAD | FWRITE;
513229828Skib	vfs_ref(mp);
514229828Skib	vfs_unbusy(mp);
515170152Skib	error = vn_open(&nd, &flags, 0, NULL);
516229828Skib	if (error != 0) {
517229828Skib		vfs_rel(mp);
5181541Srgrimes		return (error);
519229828Skib	}
52054655Seivind	NDFREE(&nd, NDF_ONLY_PNBUF);
5211541Srgrimes	vp = nd.ni_vp;
522229828Skib	error = vfs_busy(mp, MBF_NOWAIT);
523229828Skib	vfs_rel(mp);
524229828Skib	if (error == 0) {
525229828Skib		if (vp->v_type != VREG) {
526229828Skib			error = EACCES;
527229828Skib			vfs_unbusy(mp);
528229828Skib		}
529229828Skib	}
530229828Skib	if (error != 0) {
531208774Skib		VOP_UNLOCK(vp, 0);
53291406Sjhb		(void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
533229828Skib		return (error);
5341541Srgrimes	}
535167543Skib
536167543Skib	UFS_LOCK(ump);
537167543Skib	if ((ump->um_qflags[type] & (QTF_OPENING|QTF_CLOSING)) != 0) {
538167543Skib		UFS_UNLOCK(ump);
539208774Skib		VOP_UNLOCK(vp, 0);
540167543Skib		(void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
541229828Skib		vfs_unbusy(mp);
542167543Skib		return (EALREADY);
543167543Skib	}
544167543Skib	ump->um_qflags[type] |= QTF_OPENING|QTF_CLOSING;
545207736Smckusick	UFS_UNLOCK(ump);
546207736Smckusick	if ((error = dqopen(vp, ump, type)) != 0) {
547208774Skib		VOP_UNLOCK(vp, 0);
548207736Smckusick		UFS_LOCK(ump);
549207736Smckusick		ump->um_qflags[type] &= ~(QTF_OPENING|QTF_CLOSING);
550207736Smckusick		UFS_UNLOCK(ump);
551207736Smckusick		(void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
552229828Skib		vfs_unbusy(mp);
553207736Smckusick		return (error);
554207736Smckusick	}
555208774Skib	VOP_UNLOCK(vp, 0);
556162647Stegge	MNT_ILOCK(mp);
5571541Srgrimes	mp->mnt_flag |= MNT_QUOTA;
558162647Stegge	MNT_IUNLOCK(mp);
559167543Skib
560167543Skib	vpp = &ump->um_quotas[type];
561167543Skib	if (*vpp != vp)
562167543Skib		quotaoff1(td, mp, type);
563167543Skib
564271162Skib	/*
565271162Skib	 * When the directory vnode containing the quota file is
566271162Skib	 * inactivated, due to the shared lookup of the quota file
567271162Skib	 * vput()ing the dvp, the qsyncvp() call for the containing
568271162Skib	 * directory would try to acquire the quota lock exclusive.
569271162Skib	 * At the same time, lookup already locked the quota vnode
570271162Skib	 * shared.  Mark the quota vnode lock as allowing recursion
571271162Skib	 * and automatically converting shared locks to exclusive.
572271162Skib	 *
573271162Skib	 * Also mark quota vnode as system.
574271162Skib	 */
575175202Sattilio	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
576101308Sjeff	vp->v_vflag |= VV_SYSTEM;
577271162Skib	VN_LOCK_AREC(vp);
578271162Skib	VN_LOCK_DSHARE(vp);
579175294Sattilio	VOP_UNLOCK(vp, 0);
5801541Srgrimes	*vpp = vp;
5811541Srgrimes	/*
5821541Srgrimes	 * Save the credential of the process that turned on quotas.
5831541Srgrimes	 * Set up the time limits for this quota.
5841541Srgrimes	 */
58591406Sjhb	ump->um_cred[type] = crhold(td->td_ucred);
5861541Srgrimes	ump->um_btime[type] = MAX_DQ_TIME;
5871541Srgrimes	ump->um_itime[type] = MAX_IQ_TIME;
5881541Srgrimes	if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
5891541Srgrimes		if (dq->dq_btime > 0)
5901541Srgrimes			ump->um_btime[type] = dq->dq_btime;
5911541Srgrimes		if (dq->dq_itime > 0)
5921541Srgrimes			ump->um_itime[type] = dq->dq_itime;
5931541Srgrimes		dqrele(NULLVP, dq);
5941541Srgrimes	}
5951541Srgrimes	/*
596167543Skib	 * Allow the getdq from getinoquota below to read the quota
597167543Skib	 * from file.
598167543Skib	 */
599167543Skib	UFS_LOCK(ump);
600167543Skib	ump->um_qflags[type] &= ~QTF_CLOSING;
601167543Skib	UFS_UNLOCK(ump);
602167543Skib	/*
6031541Srgrimes	 * Search vnodes associated with this mount point,
6041541Srgrimes	 * adding references to quota file being opened.
6051541Srgrimes	 * NB: only need to add dquot's for inodes being modified.
6061541Srgrimes	 */
6071541Srgrimesagain:
608234386Smckusick	MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
609120737Sjeff		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
610234386Smckusick			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
611103943Sjeff			goto again;
612103943Sjeff		}
61378912Sjhb		if (vp->v_type == VNON || vp->v_writecount == 0) {
614175294Sattilio			VOP_UNLOCK(vp, 0);
615121874Skan			vrele(vp);
6161541Srgrimes			continue;
61778912Sjhb		}
6183427Sphk		error = getinoquota(VTOI(vp));
619175294Sattilio		VOP_UNLOCK(vp, 0);
620121874Skan		vrele(vp);
621154152Stegge		if (error) {
622234386Smckusick			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
6231541Srgrimes			break;
624154152Stegge		}
6251541Srgrimes	}
626167543Skib
627167543Skib        if (error)
628167543Skib		quotaoff_inchange(td, mp, type);
629167543Skib	UFS_LOCK(ump);
6301541Srgrimes	ump->um_qflags[type] &= ~QTF_OPENING;
631167543Skib	KASSERT((ump->um_qflags[type] & QTF_CLOSING) == 0,
632167543Skib		("quotaon: leaking flags"));
633167543Skib	UFS_UNLOCK(ump);
634167543Skib
635229828Skib	vfs_unbusy(mp);
6361541Srgrimes	return (error);
6371541Srgrimes}
6381541Srgrimes
6391541Srgrimes/*
640167543Skib * Main code to turn off disk quotas for a filesystem. Does not change
641167543Skib * flags.
6421541Srgrimes */
643167543Skibstatic int
644181327Sdesquotaoff1(struct thread *td, struct mount *mp, int type)
6451541Srgrimes{
64622521Sdyson	struct vnode *vp;
647154152Stegge	struct vnode *qvp, *mvp;
648166743Skib	struct ufsmount *ump;
64922521Sdyson	struct dquot *dq;
65022521Sdyson	struct inode *ip;
651167543Skib	struct ucred *cr;
6521541Srgrimes	int error;
6538876Srgrimes
654167543Skib	ump = VFSTOUFS(mp);
655116384Srwatson
656167543Skib	UFS_LOCK(ump);
657167543Skib	KASSERT((ump->um_qflags[type] & QTF_CLOSING) != 0,
658167543Skib		("quotaoff1: flags are invalid"));
659167543Skib	if ((qvp = ump->um_quotas[type]) == NULLVP) {
660167543Skib		UFS_UNLOCK(ump);
6611541Srgrimes		return (0);
662167543Skib	}
663167543Skib	cr = ump->um_cred[type];
664167543Skib	UFS_UNLOCK(ump);
665181327Sdes
6661541Srgrimes	/*
6671541Srgrimes	 * Search vnodes associated with this mount point,
6681541Srgrimes	 * deleting any references to quota file being closed.
6691541Srgrimes	 */
6701541Srgrimesagain:
671234386Smckusick	MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
67278912Sjhb		if (vp->v_type == VNON) {
673120737Sjeff			VI_UNLOCK(vp);
67434266Sjulian			continue;
67578912Sjhb		}
67683366Sjulian		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
677234386Smckusick			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
6781541Srgrimes			goto again;
67978912Sjhb		}
6801541Srgrimes		ip = VTOI(vp);
6811541Srgrimes		dq = ip->i_dquot[type];
6821541Srgrimes		ip->i_dquot[type] = NODQUOT;
6831541Srgrimes		dqrele(vp, dq);
684175294Sattilio		VOP_UNLOCK(vp, 0);
685121874Skan		vrele(vp);
6861541Srgrimes	}
687167543Skib
688247388Skib	error = dqflush(qvp);
689247388Skib	if (error != 0)
690247388Skib		return (error);
691247388Skib
692247388Skib	/*
693247388Skib	 * Clear um_quotas before closing the quota vnode to prevent
694167543Skib	 * access to the closed vnode from dqget/dqsync
695167543Skib	 */
696167543Skib	UFS_LOCK(ump);
697167543Skib	ump->um_quotas[type] = NULLVP;
698167543Skib	ump->um_cred[type] = NOCRED;
699167543Skib	UFS_UNLOCK(ump);
700167543Skib
701175202Sattilio	vn_lock(qvp, LK_EXCLUSIVE | LK_RETRY);
702101308Sjeff	qvp->v_vflag &= ~VV_SYSTEM;
703175294Sattilio	VOP_UNLOCK(qvp, 0);
70491406Sjhb	error = vn_close(qvp, FREAD|FWRITE, td->td_ucred, td);
705167543Skib	crfree(cr);
706167543Skib
707167543Skib	return (error);
708167543Skib}
709167543Skib
710167543Skib/*
711167543Skib * Turns off quotas, assumes that ump->um_qflags are already checked
712167543Skib * and QTF_CLOSING is set to indicate operation in progress. Fixes
713167543Skib * ump->um_qflags and mp->mnt_flag after.
714167543Skib */
715167543Skibint
716181327Sdesquotaoff_inchange(struct thread *td, struct mount *mp, int type)
717167543Skib{
718167543Skib	struct ufsmount *ump;
719167543Skib	int i;
720167543Skib	int error;
721167543Skib
722167543Skib	error = quotaoff1(td, mp, type);
723167543Skib
724167543Skib	ump = VFSTOUFS(mp);
725167543Skib	UFS_LOCK(ump);
7261541Srgrimes	ump->um_qflags[type] &= ~QTF_CLOSING;
727167543Skib	for (i = 0; i < MAXQUOTAS; i++)
728167543Skib		if (ump->um_quotas[i] != NULLVP)
7291541Srgrimes			break;
730167543Skib	if (i == MAXQUOTAS) {
731162647Stegge		MNT_ILOCK(mp);
7321541Srgrimes		mp->mnt_flag &= ~MNT_QUOTA;
733162647Stegge		MNT_IUNLOCK(mp);
734162647Stegge	}
735167543Skib	UFS_UNLOCK(ump);
7361541Srgrimes	return (error);
7371541Srgrimes}
7381541Srgrimes
7391541Srgrimes/*
740167543Skib * Q_QUOTAOFF - turn off disk quotas for a filesystem.
741167543Skib */
742167543Skibint
743181327Sdesquotaoff(struct thread *td, struct mount *mp, int type)
744167543Skib{
745167543Skib	struct ufsmount *ump;
746167543Skib	int error;
747167543Skib
748170587Srwatson	error = priv_check(td, PRIV_UFS_QUOTAOFF);
749167543Skib	if (error)
750167543Skib		return (error);
751167543Skib
752167543Skib	ump = VFSTOUFS(mp);
753167543Skib	UFS_LOCK(ump);
754167543Skib	if ((ump->um_qflags[type] & (QTF_OPENING|QTF_CLOSING)) != 0) {
755167543Skib		UFS_UNLOCK(ump);
756167543Skib		return (EALREADY);
757167543Skib	}
758167543Skib	ump->um_qflags[type] |= QTF_CLOSING;
759167543Skib	UFS_UNLOCK(ump);
760167543Skib
761167543Skib	return (quotaoff_inchange(td, mp, type));
762167543Skib}
763167543Skib
764167543Skib/*
7651541Srgrimes * Q_GETQUOTA - return current values in a dqblk structure.
7661541Srgrimes */
767207736Smckusickstatic int
768207736Smckusick_getquota(struct thread *td, struct mount *mp, u_long id, int type,
769207736Smckusick    struct dqblk64 *dqb)
7701541Srgrimes{
7711541Srgrimes	struct dquot *dq;
7721541Srgrimes	int error;
7731541Srgrimes
774116384Srwatson	switch (type) {
775116384Srwatson	case USRQUOTA:
776116384Srwatson		if ((td->td_ucred->cr_uid != id) && !unprivileged_get_quota) {
777170587Srwatson			error = priv_check(td, PRIV_VFS_GETQUOTA);
778116384Srwatson			if (error)
779116384Srwatson				return (error);
780116384Srwatson		}
781181327Sdes		break;
782116384Srwatson
783116384Srwatson	case GRPQUOTA:
784164033Srwatson		if (!groupmember(id, td->td_ucred) &&
785164033Srwatson		    !unprivileged_get_quota) {
786170587Srwatson			error = priv_check(td, PRIV_VFS_GETQUOTA);
787116384Srwatson			if (error)
788116384Srwatson				return (error);
789116384Srwatson		}
790116384Srwatson		break;
791116384Srwatson
792116384Srwatson	default:
793116384Srwatson		return (EINVAL);
794116384Srwatson	}
795116384Srwatson
796167543Skib	dq = NODQUOT;
7973427Sphk	error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq);
7983427Sphk	if (error)
7991541Srgrimes		return (error);
800207736Smckusick	*dqb = dq->dq_dqb;
8011541Srgrimes	dqrele(NULLVP, dq);
8021541Srgrimes	return (error);
8031541Srgrimes}
8041541Srgrimes
8051541Srgrimes/*
8061541Srgrimes * Q_SETQUOTA - assign an entire dqblk structure.
8071541Srgrimes */
808207736Smckusickstatic int
809207736Smckusick_setquota(struct thread *td, struct mount *mp, u_long id, int type,
810207736Smckusick    struct dqblk64 *dqb)
8111541Srgrimes{
81296506Sphk	struct dquot *dq;
8131541Srgrimes	struct dquot *ndq;
814166743Skib	struct ufsmount *ump;
815207736Smckusick	struct dqblk64 newlim;
8161541Srgrimes	int error;
8171541Srgrimes
818170587Srwatson	error = priv_check(td, PRIV_VFS_SETQUOTA);
819116384Srwatson	if (error)
820116384Srwatson		return (error);
821116384Srwatson
822207736Smckusick	newlim = *dqb;
823167543Skib
824167543Skib	ndq = NODQUOT;
825167543Skib	ump = VFSTOUFS(mp);
826167543Skib
8273427Sphk	error = dqget(NULLVP, id, ump, type, &ndq);
8283427Sphk	if (error)
8291541Srgrimes		return (error);
8301541Srgrimes	dq = ndq;
831167543Skib	DQI_LOCK(dq);
832167543Skib	DQI_WAIT(dq, PINOD+1, "setqta");
8331541Srgrimes	/*
8341541Srgrimes	 * Copy all but the current values.
8351541Srgrimes	 * Reset time limit if previously had no soft limit or were
8361541Srgrimes	 * under it, but now have a soft limit and are over it.
8371541Srgrimes	 */
8381541Srgrimes	newlim.dqb_curblocks = dq->dq_curblocks;
8391541Srgrimes	newlim.dqb_curinodes = dq->dq_curinodes;
8401541Srgrimes	if (dq->dq_id != 0) {
8411541Srgrimes		newlim.dqb_btime = dq->dq_btime;
8421541Srgrimes		newlim.dqb_itime = dq->dq_itime;
8431541Srgrimes	}
8441541Srgrimes	if (newlim.dqb_bsoftlimit &&
8451541Srgrimes	    dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
8461541Srgrimes	    (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
84734961Sphk		newlim.dqb_btime = time_second + ump->um_btime[type];
8481541Srgrimes	if (newlim.dqb_isoftlimit &&
8491541Srgrimes	    dq->dq_curinodes >= newlim.dqb_isoftlimit &&
8501541Srgrimes	    (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
85134961Sphk		newlim.dqb_itime = time_second + ump->um_itime[type];
8521541Srgrimes	dq->dq_dqb = newlim;
8531541Srgrimes	if (dq->dq_curblocks < dq->dq_bsoftlimit)
8541541Srgrimes		dq->dq_flags &= ~DQ_BLKS;
8551541Srgrimes	if (dq->dq_curinodes < dq->dq_isoftlimit)
8561541Srgrimes		dq->dq_flags &= ~DQ_INODS;
8571541Srgrimes	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
8581541Srgrimes	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
8591541Srgrimes		dq->dq_flags |= DQ_FAKE;
8601541Srgrimes	else
8611541Srgrimes		dq->dq_flags &= ~DQ_FAKE;
8621541Srgrimes	dq->dq_flags |= DQ_MOD;
863167543Skib	DQI_UNLOCK(dq);
8641541Srgrimes	dqrele(NULLVP, dq);
8651541Srgrimes	return (0);
8661541Srgrimes}
8671541Srgrimes
8681541Srgrimes/*
8691541Srgrimes * Q_SETUSE - set current inode and block usage.
8701541Srgrimes */
871207736Smckusickstatic int
872207736Smckusick_setuse(struct thread *td, struct mount *mp, u_long id, int type,
873207736Smckusick    struct dqblk64 *dqb)
8741541Srgrimes{
87596506Sphk	struct dquot *dq;
876166743Skib	struct ufsmount *ump;
8771541Srgrimes	struct dquot *ndq;
878207736Smckusick	struct dqblk64 usage;
8791541Srgrimes	int error;
8801541Srgrimes
881170587Srwatson	error = priv_check(td, PRIV_UFS_SETUSE);
882116384Srwatson	if (error)
883116384Srwatson		return (error);
884116384Srwatson
885207736Smckusick	usage = *dqb;
886167543Skib
887167543Skib	ump = VFSTOUFS(mp);
888167543Skib	ndq = NODQUOT;
889167543Skib
8903427Sphk	error = dqget(NULLVP, id, ump, type, &ndq);
8913427Sphk	if (error)
8921541Srgrimes		return (error);
8931541Srgrimes	dq = ndq;
894167543Skib	DQI_LOCK(dq);
895167543Skib	DQI_WAIT(dq, PINOD+1, "setuse");
8961541Srgrimes	/*
8971541Srgrimes	 * Reset time limit if have a soft limit and were
8981541Srgrimes	 * previously under it, but are now over it.
8991541Srgrimes	 */
9001541Srgrimes	if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
9011541Srgrimes	    usage.dqb_curblocks >= dq->dq_bsoftlimit)
90234961Sphk		dq->dq_btime = time_second + ump->um_btime[type];
9031541Srgrimes	if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
9041541Srgrimes	    usage.dqb_curinodes >= dq->dq_isoftlimit)
90534961Sphk		dq->dq_itime = time_second + ump->um_itime[type];
9061541Srgrimes	dq->dq_curblocks = usage.dqb_curblocks;
9071541Srgrimes	dq->dq_curinodes = usage.dqb_curinodes;
9081541Srgrimes	if (dq->dq_curblocks < dq->dq_bsoftlimit)
9091541Srgrimes		dq->dq_flags &= ~DQ_BLKS;
9101541Srgrimes	if (dq->dq_curinodes < dq->dq_isoftlimit)
9111541Srgrimes		dq->dq_flags &= ~DQ_INODS;
9121541Srgrimes	dq->dq_flags |= DQ_MOD;
913167543Skib	DQI_UNLOCK(dq);
9141541Srgrimes	dqrele(NULLVP, dq);
9151541Srgrimes	return (0);
9161541Srgrimes}
9171541Srgrimes
918207736Smckusickint
919207736Smckusickgetquota32(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
920207736Smckusick{
921207736Smckusick	struct dqblk32 dqb32;
922207736Smckusick	struct dqblk64 dqb64;
923207736Smckusick	int error;
924207736Smckusick
925207736Smckusick	error = _getquota(td, mp, id, type, &dqb64);
926207736Smckusick	if (error)
927207736Smckusick		return (error);
928207736Smckusick	dqb64_dqb32(&dqb64, &dqb32);
929207736Smckusick	error = copyout(&dqb32, addr, sizeof(dqb32));
930207736Smckusick	return (error);
931207736Smckusick}
932207736Smckusick
933207736Smckusickint
934207736Smckusicksetquota32(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
935207736Smckusick{
936207736Smckusick	struct dqblk32 dqb32;
937207736Smckusick	struct dqblk64 dqb64;
938207736Smckusick	int error;
939207736Smckusick
940207736Smckusick	error = copyin(addr, &dqb32, sizeof(dqb32));
941207736Smckusick	if (error)
942207736Smckusick		return (error);
943207736Smckusick	dqb32_dqb64(&dqb32, &dqb64);
944207736Smckusick	error = _setquota(td, mp, id, type, &dqb64);
945207736Smckusick	return (error);
946207736Smckusick}
947207736Smckusick
948207736Smckusickint
949207736Smckusicksetuse32(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
950207736Smckusick{
951207736Smckusick	struct dqblk32 dqb32;
952207736Smckusick	struct dqblk64 dqb64;
953207736Smckusick	int error;
954207736Smckusick
955207736Smckusick	error = copyin(addr, &dqb32, sizeof(dqb32));
956207736Smckusick	if (error)
957207736Smckusick		return (error);
958207736Smckusick	dqb32_dqb64(&dqb32, &dqb64);
959207736Smckusick	error = _setuse(td, mp, id, type, &dqb64);
960207736Smckusick	return (error);
961207736Smckusick}
962207736Smckusick
963207736Smckusickint
964207736Smckusickgetquota(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
965207736Smckusick{
966207736Smckusick	struct dqblk64 dqb64;
967207736Smckusick	int error;
968207736Smckusick
969207736Smckusick	error = _getquota(td, mp, id, type, &dqb64);
970207736Smckusick	if (error)
971207736Smckusick		return (error);
972207736Smckusick	error = copyout(&dqb64, addr, sizeof(dqb64));
973207736Smckusick	return (error);
974207736Smckusick}
975207736Smckusick
976207736Smckusickint
977207736Smckusicksetquota(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
978207736Smckusick{
979207736Smckusick	struct dqblk64 dqb64;
980207736Smckusick	int error;
981207736Smckusick
982207736Smckusick	error = copyin(addr, &dqb64, sizeof(dqb64));
983207736Smckusick	if (error)
984207736Smckusick		return (error);
985207736Smckusick	error = _setquota(td, mp, id, type, &dqb64);
986207736Smckusick	return (error);
987207736Smckusick}
988207736Smckusick
989207736Smckusickint
990207736Smckusicksetuse(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
991207736Smckusick{
992207736Smckusick	struct dqblk64 dqb64;
993207736Smckusick	int error;
994207736Smckusick
995207736Smckusick	error = copyin(addr, &dqb64, sizeof(dqb64));
996207736Smckusick	if (error)
997207736Smckusick		return (error);
998207736Smckusick	error = _setuse(td, mp, id, type, &dqb64);
999207736Smckusick	return (error);
1000207736Smckusick}
1001207736Smckusick
10021541Srgrimes/*
1003207736Smckusick * Q_GETQUOTASIZE - get bit-size of quota file fields
1004207736Smckusick */
1005207736Smckusickint
1006207736Smckusickgetquotasize(struct thread *td, struct mount *mp, u_long id, int type,
1007207736Smckusick    void *sizep)
1008207736Smckusick{
1009207736Smckusick	struct ufsmount *ump = VFSTOUFS(mp);
1010207736Smckusick	int bitsize;
1011207736Smckusick
1012207736Smckusick	UFS_LOCK(ump);
1013207736Smckusick	if (ump->um_quotas[type] == NULLVP ||
1014207736Smckusick	    (ump->um_qflags[type] & QTF_CLOSING)) {
1015207736Smckusick		UFS_UNLOCK(ump);
1016207736Smckusick		return (EINVAL);
1017207736Smckusick	}
1018207736Smckusick	if ((ump->um_qflags[type] & QTF_64BIT) != 0)
1019207736Smckusick		bitsize = 64;
1020207736Smckusick	else
1021207736Smckusick		bitsize = 32;
1022207736Smckusick	UFS_UNLOCK(ump);
1023207736Smckusick	return (copyout(&bitsize, sizep, sizeof(int)));
1024207736Smckusick}
1025207736Smckusick
1026207736Smckusick/*
10271541Srgrimes * Q_SYNC - sync quota files to disk.
10281541Srgrimes */
10291541Srgrimesint
1030181327Sdesqsync(struct mount *mp)
10311541Srgrimes{
10321541Srgrimes	struct ufsmount *ump = VFSTOUFS(mp);
103383366Sjulian	struct thread *td = curthread;		/* XXX */
1034154152Stegge	struct vnode *vp, *mvp;
103522521Sdyson	struct dquot *dq;
103622521Sdyson	int i, error;
10371541Srgrimes
10381541Srgrimes	/*
10391541Srgrimes	 * Check if the mount point has any quotas.
10401541Srgrimes	 * If not, simply return.
10411541Srgrimes	 */
1042167543Skib	UFS_LOCK(ump);
10431541Srgrimes	for (i = 0; i < MAXQUOTAS; i++)
10441541Srgrimes		if (ump->um_quotas[i] != NULLVP)
10451541Srgrimes			break;
1046167543Skib	UFS_UNLOCK(ump);
10471541Srgrimes	if (i == MAXQUOTAS)
10481541Srgrimes		return (0);
10491541Srgrimes	/*
10501541Srgrimes	 * Search vnodes associated with this mount point,
10511541Srgrimes	 * synchronizing any modified dquot structures.
10521541Srgrimes	 */
10531541Srgrimesagain:
1054234483Smckusick	MNT_VNODE_FOREACH_ACTIVE(vp, mp, mvp) {
105578912Sjhb		if (vp->v_type == VNON) {
1056120737Sjeff			VI_UNLOCK(vp);
105734266Sjulian			continue;
105878912Sjhb		}
1059155897Sjeff		error = vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td);
106022521Sdyson		if (error) {
1061154152Stegge			if (error == ENOENT) {
1062244239Skib				MNT_VNODE_FOREACH_ACTIVE_ABORT(mp, mvp);
106322521Sdyson				goto again;
1064154152Stegge			}
10651541Srgrimes			continue;
106622521Sdyson		}
10671541Srgrimes		for (i = 0; i < MAXQUOTAS; i++) {
10681541Srgrimes			dq = VTOI(vp)->i_dquot[i];
1069167543Skib			if (dq != NODQUOT)
10701541Srgrimes				dqsync(vp, dq);
10711541Srgrimes		}
1072121847Skan		vput(vp);
10731541Srgrimes	}
10741541Srgrimes	return (0);
10751541Srgrimes}
10761541Srgrimes
10771541Srgrimes/*
1078234483Smckusick * Sync quota file for given vnode to disk.
1079234483Smckusick */
1080234483Smckusickint
1081234483Smckusickqsyncvp(struct vnode *vp)
1082234483Smckusick{
1083234483Smckusick	struct ufsmount *ump = VFSTOUFS(vp->v_mount);
1084234483Smckusick	struct dquot *dq;
1085234483Smckusick	int i;
1086234483Smckusick
1087234483Smckusick	/*
1088234483Smckusick	 * Check if the mount point has any quotas.
1089234483Smckusick	 * If not, simply return.
1090234483Smckusick	 */
1091234483Smckusick	UFS_LOCK(ump);
1092234483Smckusick	for (i = 0; i < MAXQUOTAS; i++)
1093234483Smckusick		if (ump->um_quotas[i] != NULLVP)
1094234483Smckusick			break;
1095234483Smckusick	UFS_UNLOCK(ump);
1096234483Smckusick	if (i == MAXQUOTAS)
1097234483Smckusick		return (0);
1098234483Smckusick	/*
1099234483Smckusick	 * Search quotas associated with this vnode
1100234483Smckusick	 * synchronizing any modified dquot structures.
1101234483Smckusick	 */
1102234483Smckusick	for (i = 0; i < MAXQUOTAS; i++) {
1103234483Smckusick		dq = VTOI(vp)->i_dquot[i];
1104234483Smckusick		if (dq != NODQUOT)
1105234483Smckusick			dqsync(vp, dq);
1106234483Smckusick	}
1107234483Smckusick	return (0);
1108234483Smckusick}
1109234483Smckusick
1110234483Smckusick/*
11111541Srgrimes * Code pertaining to management of the in-core dquot data structures.
11121541Srgrimes */
111322521Sdyson#define DQHASH(dqvp, id) \
111437649Sbde	(&dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & dqhash])
111560938Sjakestatic LIST_HEAD(dqhash, dquot) *dqhashtbl;
111612971Sphkstatic u_long dqhash;
11171541Srgrimes
11181541Srgrimes/*
11191541Srgrimes * Dquot free list.
11201541Srgrimes */
11211541Srgrimes#define	DQUOTINC	5	/* minimum free dquots desired */
112260938Sjakestatic TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
112312971Sphkstatic long numdquot, desireddquot = DQUOTINC;
11241541Srgrimes
1125181327Sdes/*
1126167543Skib * Lock to protect quota hash, dq free list and dq_cnt ref counters of
1127167543Skib * _all_ dqs.
1128167543Skib */
1129167543Skibstruct mtx dqhlock;
1130167543Skib
1131167543Skib#define	DQH_LOCK()	mtx_lock(&dqhlock)
1132167543Skib#define	DQH_UNLOCK()	mtx_unlock(&dqhlock)
1133167543Skib
1134167543Skibstatic struct dquot *dqhashfind(struct dqhash *dqh, u_long id,
1135167543Skib	struct vnode *dqvp);
1136167543Skib
11371541Srgrimes/*
11381541Srgrimes * Initialize the quota system.
11391541Srgrimes */
11401541Srgrimesvoid
1141181327Sdesdqinit(void)
11421541Srgrimes{
11431541Srgrimes
1144167543Skib	mtx_init(&dqhlock, "dqhlock", NULL, MTX_DEF);
11451541Srgrimes	dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
114622521Sdyson	TAILQ_INIT(&dqfreelist);
11471541Srgrimes}
11481541Srgrimes
11491541Srgrimes/*
115099101Siedowse * Shut down the quota system.
115199101Siedowse */
115299101Siedowsevoid
1153181327Sdesdquninit(void)
115499101Siedowse{
115599101Siedowse	struct dquot *dq;
115699101Siedowse
115799101Siedowse	hashdestroy(dqhashtbl, M_DQUOT, dqhash);
115899101Siedowse	while ((dq = TAILQ_FIRST(&dqfreelist)) != NULL) {
115999101Siedowse		TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
1160167543Skib		mtx_destroy(&dq->dq_lock);
116199101Siedowse		free(dq, M_DQUOT);
116299101Siedowse	}
1163167543Skib	mtx_destroy(&dqhlock);
116499101Siedowse}
116599101Siedowse
1166167543Skibstatic struct dquot *
1167181327Sdesdqhashfind(struct dqhash *dqh, u_long id, struct vnode *dqvp)
1168167543Skib{
1169167543Skib	struct dquot *dq;
1170167543Skib
1171167543Skib	mtx_assert(&dqhlock, MA_OWNED);
1172167543Skib	LIST_FOREACH(dq, dqh, dq_hash) {
1173167543Skib		if (dq->dq_id != id ||
1174167543Skib		    dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
1175167543Skib			continue;
1176167543Skib		/*
1177167543Skib		 * Cache hit with no references.  Take
1178167543Skib		 * the structure off the free list.
1179167543Skib		 */
1180167543Skib		if (dq->dq_cnt == 0)
1181167543Skib			TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
1182167543Skib		DQREF(dq);
1183167543Skib		return (dq);
1184167543Skib	}
1185167543Skib	return (NODQUOT);
1186167543Skib}
1187167543Skib
118899101Siedowse/*
1189207736Smckusick * Determine the quota file type.
1190207736Smckusick *
1191207736Smckusick * A 32-bit quota file is simply an array of struct dqblk32.
1192207736Smckusick *
1193207736Smckusick * A 64-bit quota file is a struct dqhdr64 followed by an array of struct
1194207736Smckusick * dqblk64.  The header contains various magic bits which allow us to be
1195207736Smckusick * reasonably confident that it is indeeda 64-bit quota file and not just
1196207736Smckusick * a 32-bit quota file that just happens to "look right".
1197207736Smckusick *
1198207736Smckusick */
1199207736Smckusickstatic int
1200207736Smckusickdqopen(struct vnode *vp, struct ufsmount *ump, int type)
1201207736Smckusick{
1202207736Smckusick	struct dqhdr64 dqh;
1203207736Smckusick	struct iovec aiov;
1204207736Smckusick	struct uio auio;
1205208774Skib	int error;
1206207736Smckusick
1207208774Skib	ASSERT_VOP_LOCKED(vp, "dqopen");
1208207736Smckusick	auio.uio_iov = &aiov;
1209207736Smckusick	auio.uio_iovcnt = 1;
1210207736Smckusick	aiov.iov_base = &dqh;
1211207736Smckusick	aiov.iov_len = sizeof(dqh);
1212207736Smckusick	auio.uio_resid = sizeof(dqh);
1213207736Smckusick	auio.uio_offset = 0;
1214207736Smckusick	auio.uio_segflg = UIO_SYSSPACE;
1215207736Smckusick	auio.uio_rw = UIO_READ;
1216207736Smckusick	auio.uio_td = (struct thread *)0;
1217207736Smckusick	error = VOP_READ(vp, &auio, 0, ump->um_cred[type]);
1218207736Smckusick
1219207736Smckusick	if (error != 0)
1220207736Smckusick		return (error);
1221207736Smckusick	if (auio.uio_resid > 0) {
1222207736Smckusick		/* assume 32 bits */
1223207736Smckusick		return (0);
1224207736Smckusick	}
1225207736Smckusick
1226207736Smckusick	UFS_LOCK(ump);
1227207736Smckusick	if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) == 0 &&
1228207736Smckusick	    be32toh(dqh.dqh_version) == Q_DQHDR64_VERSION &&
1229207736Smckusick	    be32toh(dqh.dqh_hdrlen) == (uint32_t)sizeof(struct dqhdr64) &&
1230207736Smckusick	    be32toh(dqh.dqh_reclen) == (uint32_t)sizeof(struct dqblk64)) {
1231207736Smckusick		/* XXX: what if the magic matches, but the sizes are wrong? */
1232207736Smckusick		ump->um_qflags[type] |= QTF_64BIT;
1233207736Smckusick	} else {
1234207736Smckusick		ump->um_qflags[type] &= ~QTF_64BIT;
1235207736Smckusick	}
1236207736Smckusick	UFS_UNLOCK(ump);
1237207736Smckusick
1238207736Smckusick	return (0);
1239207736Smckusick}
1240207736Smckusick
1241207736Smckusick/*
12421541Srgrimes * Obtain a dquot structure for the specified identifier and quota file
12431541Srgrimes * reading the information from the file if necessary.
12441541Srgrimes */
124512971Sphkstatic int
1246181327Sdesdqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type,
1247181327Sdes    struct dquot **dqp)
12481541Srgrimes{
1249207736Smckusick	uint8_t buf[sizeof(struct dqblk64)];
1250207736Smckusick	off_t base, recsize;
1251167543Skib	struct dquot *dq, *dq1;
125222521Sdyson	struct dqhash *dqh;
125322521Sdyson	struct vnode *dqvp;
12541541Srgrimes	struct iovec aiov;
12551541Srgrimes	struct uio auio;
1256241896Skib	int dqvplocked, error;
12571541Srgrimes
1258167543Skib#ifdef DEBUG_VFS_LOCKS
1259167543Skib	if (vp != NULLVP)
1260167543Skib		ASSERT_VOP_ELOCKED(vp, "dqget");
1261167543Skib#endif
1262167543Skib
1263167543Skib	if (vp != NULLVP && *dqp != NODQUOT) {
1264167543Skib		return (0);
1265167543Skib	}
1266167543Skib
1267166380Smpp	/* XXX: Disallow negative id values to prevent the
1268166380Smpp	* creation of 100GB+ quota data files.
1269166380Smpp	*/
1270166380Smpp	if ((int)id < 0)
1271166380Smpp		return (EINVAL);
1272167543Skib
1273167543Skib	UFS_LOCK(ump);
12741541Srgrimes	dqvp = ump->um_quotas[type];
12751541Srgrimes	if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
12761541Srgrimes		*dqp = NODQUOT;
1277167543Skib		UFS_UNLOCK(ump);
12781541Srgrimes		return (EINVAL);
12791541Srgrimes	}
1280167543Skib	vref(dqvp);
1281167543Skib	UFS_UNLOCK(ump);
1282167543Skib	error = 0;
1283167543Skib	dqvplocked = 0;
1284167543Skib
12851541Srgrimes	/*
12861541Srgrimes	 * Check the cache first.
12871541Srgrimes	 */
128822521Sdyson	dqh = DQHASH(dqvp, id);
1289167543Skib	DQH_LOCK();
1290167543Skib	dq = dqhashfind(dqh, id, dqvp);
1291167543Skib	if (dq != NULL) {
1292167543Skib		DQH_UNLOCK();
1293167543Skibhfound:		DQI_LOCK(dq);
1294167543Skib		DQI_WAIT(dq, PINOD+1, "dqget");
1295167543Skib		DQI_UNLOCK(dq);
1296167543Skib		if (dq->dq_ump == NULL) {
1297167543Skib			dqrele(vp, dq);
1298167543Skib			dq = NODQUOT;
1299167543Skib			error = EIO;
1300167543Skib		}
1301167543Skib		*dqp = dq;
1302167543Skib		if (dqvplocked)
1303167543Skib			vput(dqvp);
1304167543Skib		else
1305167543Skib			vrele(dqvp);
1306167543Skib		return (error);
1307167543Skib	}
1308167543Skib
1309167543Skib	/*
1310167543Skib	 * Quota vnode lock is before DQ_LOCK. Acquire dqvp lock there
1311167543Skib	 * since new dq will appear on the hash chain DQ_LOCKed.
1312167543Skib	 */
1313167543Skib	if (vp != dqvp) {
1314167543Skib		DQH_UNLOCK();
1315175202Sattilio		vn_lock(dqvp, LK_SHARED | LK_RETRY);
1316167543Skib		dqvplocked = 1;
1317167543Skib		DQH_LOCK();
13181541Srgrimes		/*
1319167543Skib		 * Recheck the cache after sleep for quota vnode lock.
13201541Srgrimes		 */
1321167543Skib		dq = dqhashfind(dqh, id, dqvp);
1322167543Skib		if (dq != NULL) {
1323167543Skib			DQH_UNLOCK();
1324167543Skib			goto hfound;
1325167543Skib		}
13261541Srgrimes	}
1327167543Skib
13281541Srgrimes	/*
1329167543Skib	 * Not in cache, allocate a new one or take it from the
1330167543Skib	 * free list.
13311541Srgrimes	 */
133271999Sphk	if (TAILQ_FIRST(&dqfreelist) == NODQUOT &&
133322521Sdyson	    numdquot < MAXQUOTAS * desiredvnodes)
13341541Srgrimes		desireddquot += DQUOTINC;
13351541Srgrimes	if (numdquot < desireddquot) {
1336167543Skib		numdquot++;
1337167543Skib		DQH_UNLOCK();
1338207736Smckusick		dq1 = malloc(sizeof *dq1, M_DQUOT, M_WAITOK | M_ZERO);
1339167543Skib		mtx_init(&dq1->dq_lock, "dqlock", NULL, MTX_DEF);
1340167543Skib		DQH_LOCK();
1341167543Skib		/*
1342167543Skib		 * Recheck the cache after sleep for memory.
1343167543Skib		 */
1344167543Skib		dq = dqhashfind(dqh, id, dqvp);
1345167543Skib		if (dq != NULL) {
1346167543Skib			numdquot--;
1347167543Skib			DQH_UNLOCK();
1348167543Skib			mtx_destroy(&dq1->dq_lock);
1349167543Skib			free(dq1, M_DQUOT);
1350167543Skib			goto hfound;
1351167543Skib		}
1352167543Skib		dq = dq1;
13531541Srgrimes	} else {
135471999Sphk		if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) {
1355167543Skib			DQH_UNLOCK();
13561541Srgrimes			tablefull("dquot");
13571541Srgrimes			*dqp = NODQUOT;
1358167543Skib			if (dqvplocked)
1359167543Skib				vput(dqvp);
1360167543Skib			else
1361167543Skib				vrele(dqvp);
13621541Srgrimes			return (EUSERS);
13631541Srgrimes		}
13641541Srgrimes		if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
1365185739Skib			panic("dqget: free dquot isn't %p", dq);
136622521Sdyson		TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
136789213Sphk		if (dq->dq_ump != NULL)
136889213Sphk			LIST_REMOVE(dq, dq_hash);
13691541Srgrimes	}
1370167543Skib
13711541Srgrimes	/*
1372167543Skib	 * Dq is put into hash already locked to prevent parallel
1373167543Skib	 * usage while it is being read from file.
13741541Srgrimes	 */
13751541Srgrimes	dq->dq_flags = DQ_LOCK;
13761541Srgrimes	dq->dq_id = id;
1377167543Skib	dq->dq_type = type;
13781541Srgrimes	dq->dq_ump = ump;
1379167543Skib	LIST_INSERT_HEAD(dqh, dq, dq_hash);
1380167543Skib	DQREF(dq);
1381167543Skib	DQH_UNLOCK();
1382167543Skib
1383207736Smckusick	/*
1384207736Smckusick	 * Read the requested quota record from the quota file, performing
1385207736Smckusick	 * any necessary conversions.
1386207736Smckusick	 */
1387207736Smckusick	if (ump->um_qflags[type] & QTF_64BIT) {
1388207736Smckusick		recsize = sizeof(struct dqblk64);
1389207736Smckusick		base = sizeof(struct dqhdr64);
1390207736Smckusick	} else {
1391207736Smckusick		recsize = sizeof(struct dqblk32);
1392207736Smckusick		base = 0;
1393207736Smckusick	}
13941541Srgrimes	auio.uio_iov = &aiov;
13951541Srgrimes	auio.uio_iovcnt = 1;
1396207736Smckusick	aiov.iov_base = buf;
1397207736Smckusick	aiov.iov_len = recsize;
1398207736Smckusick	auio.uio_resid = recsize;
1399207736Smckusick	auio.uio_offset = base + id * recsize;
14001541Srgrimes	auio.uio_segflg = UIO_SYSSPACE;
14011541Srgrimes	auio.uio_rw = UIO_READ;
140283366Sjulian	auio.uio_td = (struct thread *)0;
1403167543Skib
14041541Srgrimes	error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
1405207736Smckusick	if (auio.uio_resid == recsize && error == 0) {
1406207736Smckusick		bzero(&dq->dq_dqb, sizeof(dq->dq_dqb));
1407207736Smckusick	} else {
1408207736Smckusick		if (ump->um_qflags[type] & QTF_64BIT)
1409207736Smckusick			dqb64_dq((struct dqblk64 *)buf, dq);
1410207736Smckusick		else
1411207736Smckusick			dqb32_dq((struct dqblk32 *)buf, dq);
1412207736Smckusick	}
1413167543Skib	if (dqvplocked)
1414167543Skib		vput(dqvp);
1415167543Skib	else
1416167543Skib		vrele(dqvp);
14171541Srgrimes	/*
14181541Srgrimes	 * I/O error in reading quota file, release
14191541Srgrimes	 * quota structure and reflect problem to caller.
14201541Srgrimes	 */
14211541Srgrimes	if (error) {
1422167543Skib		DQH_LOCK();
1423167543Skib		dq->dq_ump = NULL;
142422521Sdyson		LIST_REMOVE(dq, dq_hash);
1425167543Skib		DQH_UNLOCK();
1426167543Skib		DQI_LOCK(dq);
1427167543Skib		if (dq->dq_flags & DQ_WANT)
1428167543Skib			wakeup(dq);
1429167543Skib		dq->dq_flags = 0;
1430167543Skib		DQI_UNLOCK(dq);
14311541Srgrimes		dqrele(vp, dq);
14321541Srgrimes		*dqp = NODQUOT;
14331541Srgrimes		return (error);
14341541Srgrimes	}
1435167543Skib	DQI_LOCK(dq);
14361541Srgrimes	/*
14371541Srgrimes	 * Check for no limit to enforce.
14381541Srgrimes	 * Initialize time values if necessary.
14391541Srgrimes	 */
14401541Srgrimes	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
14411541Srgrimes	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
14421541Srgrimes		dq->dq_flags |= DQ_FAKE;
14431541Srgrimes	if (dq->dq_id != 0) {
1444166487Smpp		if (dq->dq_btime == 0) {
144534961Sphk			dq->dq_btime = time_second + ump->um_btime[type];
1446166487Smpp			if (dq->dq_bsoftlimit &&
1447166487Smpp			    dq->dq_curblocks >= dq->dq_bsoftlimit)
1448166487Smpp				dq->dq_flags |= DQ_MOD;
1449166487Smpp		}
1450166487Smpp		if (dq->dq_itime == 0) {
145134961Sphk			dq->dq_itime = time_second + ump->um_itime[type];
1452166487Smpp			if (dq->dq_isoftlimit &&
1453166487Smpp			    dq->dq_curinodes >= dq->dq_isoftlimit)
1454166487Smpp				dq->dq_flags |= DQ_MOD;
1455166487Smpp		}
14561541Srgrimes	}
1457167543Skib	DQI_WAKEUP(dq);
1458167543Skib	DQI_UNLOCK(dq);
14591541Srgrimes	*dqp = dq;
14601541Srgrimes	return (0);
14611541Srgrimes}
14621541Srgrimes
146317040Swollman#ifdef DIAGNOSTIC
14641541Srgrimes/*
14651541Srgrimes * Obtain a reference to a dquot.
14661541Srgrimes */
146712971Sphkstatic void
1468181327Sdesdqref(struct dquot *dq)
14691541Srgrimes{
14701541Srgrimes
14711541Srgrimes	dq->dq_cnt++;
14721541Srgrimes}
147317040Swollman#endif
14741541Srgrimes
14751541Srgrimes/*
14761541Srgrimes * Release a reference to a dquot.
14771541Srgrimes */
14781541Srgrimesvoid
1479181327Sdesdqrele(struct vnode *vp, struct dquot *dq)
14801541Srgrimes{
14811541Srgrimes
14821541Srgrimes	if (dq == NODQUOT)
14831541Srgrimes		return;
1484167543Skib	DQH_LOCK();
1485232003Skib	KASSERT(dq->dq_cnt > 0, ("Lost dq %p reference 1", dq));
14861541Srgrimes	if (dq->dq_cnt > 1) {
14871541Srgrimes		dq->dq_cnt--;
1488167543Skib		DQH_UNLOCK();
14891541Srgrimes		return;
14901541Srgrimes	}
1491167543Skib	DQH_UNLOCK();
1492185761Skibsync:
1493167543Skib	(void) dqsync(vp, dq);
1494167543Skib
1495167543Skib	DQH_LOCK();
1496232003Skib	KASSERT(dq->dq_cnt > 0, ("Lost dq %p reference 2", dq));
14971541Srgrimes	if (--dq->dq_cnt > 0)
1498167543Skib	{
1499167543Skib		DQH_UNLOCK();
15001541Srgrimes		return;
1501167543Skib	}
1502185761Skib
1503185761Skib	/*
1504185761Skib	 * The dq may become dirty after it is synced but before it is
1505185761Skib	 * put to the free list. Checking the DQ_MOD there without
1506185761Skib	 * locking dq should be safe since no other references to the
1507185761Skib	 * dq exist.
1508185761Skib	 */
1509185761Skib	if ((dq->dq_flags & DQ_MOD) != 0) {
1510185761Skib		dq->dq_cnt++;
1511185761Skib		DQH_UNLOCK();
1512185761Skib		goto sync;
1513185761Skib	}
151422521Sdyson	TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
1515167543Skib	DQH_UNLOCK();
15161541Srgrimes}
15171541Srgrimes
15181541Srgrimes/*
15191541Srgrimes * Update the disk quota in the quota file.
15201541Srgrimes */
152112971Sphkstatic int
1522181327Sdesdqsync(struct vnode *vp, struct dquot *dq)
15231541Srgrimes{
1524207736Smckusick	uint8_t buf[sizeof(struct dqblk64)];
1525207736Smckusick	off_t base, recsize;
15261541Srgrimes	struct vnode *dqvp;
15271541Srgrimes	struct iovec aiov;
15281541Srgrimes	struct uio auio;
1529241896Skib	int error;
1530156451Stegge	struct mount *mp;
1531167543Skib	struct ufsmount *ump;
15321541Srgrimes
1533167543Skib#ifdef DEBUG_VFS_LOCKS
1534167543Skib	if (vp != NULL)
1535167543Skib		ASSERT_VOP_ELOCKED(vp, "dqsync");
1536167543Skib#endif
1537167543Skib
1538156451Stegge	mp = NULL;
1539167543Skib	error = 0;
15401541Srgrimes	if (dq == NODQUOT)
15411541Srgrimes		panic("dqsync: dquot");
1542167543Skib	if ((ump = dq->dq_ump) == NULL)
15431541Srgrimes		return (0);
1544167543Skib	UFS_LOCK(ump);
1545167543Skib	if ((dqvp = ump->um_quotas[dq->dq_type]) == NULLVP)
15461541Srgrimes		panic("dqsync: file");
1547167543Skib	vref(dqvp);
1548167543Skib	UFS_UNLOCK(ump);
1549167543Skib
1550167543Skib	DQI_LOCK(dq);
1551167543Skib	if ((dq->dq_flags & DQ_MOD) == 0) {
1552167543Skib		DQI_UNLOCK(dq);
1553167543Skib		vrele(dqvp);
1554167543Skib		return (0);
1555167543Skib	}
1556167543Skib	DQI_UNLOCK(dq);
1557167543Skib
1558156451Stegge	(void) vn_start_secondary_write(dqvp, &mp, V_WAIT);
15591541Srgrimes	if (vp != dqvp)
1560175202Sattilio		vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
1561167543Skib
1562167543Skib	DQI_LOCK(dq);
1563167543Skib	DQI_WAIT(dq, PINOD+2, "dqsync");
1564167543Skib	if ((dq->dq_flags & DQ_MOD) == 0)
1565167543Skib		goto out;
15661541Srgrimes	dq->dq_flags |= DQ_LOCK;
1567167543Skib	DQI_UNLOCK(dq);
1568167543Skib
1569207736Smckusick	/*
1570207736Smckusick	 * Write the quota record to the quota file, performing any
1571207736Smckusick	 * necessary conversions.  See dqget() for additional details.
1572207736Smckusick	 */
1573207736Smckusick	if (ump->um_qflags[dq->dq_type] & QTF_64BIT) {
1574207736Smckusick		dq_dqb64(dq, (struct dqblk64 *)buf);
1575207736Smckusick		recsize = sizeof(struct dqblk64);
1576207736Smckusick		base = sizeof(struct dqhdr64);
1577207736Smckusick	} else {
1578207736Smckusick		dq_dqb32(dq, (struct dqblk32 *)buf);
1579207736Smckusick		recsize = sizeof(struct dqblk32);
1580207736Smckusick		base = 0;
1581207736Smckusick	}
1582207736Smckusick
15831541Srgrimes	auio.uio_iov = &aiov;
15841541Srgrimes	auio.uio_iovcnt = 1;
1585207736Smckusick	aiov.iov_base = buf;
1586207736Smckusick	aiov.iov_len = recsize;
1587207736Smckusick	auio.uio_resid = recsize;
1588207736Smckusick	auio.uio_offset = base + dq->dq_id * recsize;
15891541Srgrimes	auio.uio_segflg = UIO_SYSSPACE;
15901541Srgrimes	auio.uio_rw = UIO_WRITE;
159183366Sjulian	auio.uio_td = (struct thread *)0;
15921541Srgrimes	error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
15931541Srgrimes	if (auio.uio_resid && error == 0)
15941541Srgrimes		error = EIO;
1595167543Skib
1596167543Skib	DQI_LOCK(dq);
1597167543Skib	DQI_WAKEUP(dq);
1598167543Skib	dq->dq_flags &= ~DQ_MOD;
1599207736Smckusickout:
1600207736Smckusick	DQI_UNLOCK(dq);
16011541Srgrimes	if (vp != dqvp)
1602167543Skib		vput(dqvp);
1603167543Skib	else
1604167543Skib		vrele(dqvp);
1605156451Stegge	vn_finished_secondary_write(mp);
16061541Srgrimes	return (error);
16071541Srgrimes}
16081541Srgrimes
16091541Srgrimes/*
16101541Srgrimes * Flush all entries from the cache for a particular vnode.
16111541Srgrimes */
1612247388Skibstatic int
1613181327Sdesdqflush(struct vnode *vp)
16141541Srgrimes{
161596506Sphk	struct dquot *dq, *nextdq;
161622521Sdyson	struct dqhash *dqh;
1617247388Skib	int error;
16181541Srgrimes
16191541Srgrimes	/*
16201541Srgrimes	 * Move all dquot's that used to refer to this quota
16211541Srgrimes	 * file off their hash chains (they will eventually
16221541Srgrimes	 * fall off the head of the free list and be re-used).
16231541Srgrimes	 */
1624247388Skib	error = 0;
1625167543Skib	DQH_LOCK();
162622521Sdyson	for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
162771999Sphk		for (dq = LIST_FIRST(dqh); dq; dq = nextdq) {
162871999Sphk			nextdq = LIST_NEXT(dq, dq_hash);
16291541Srgrimes			if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
16301541Srgrimes				continue;
16311541Srgrimes			if (dq->dq_cnt)
1632247388Skib				error = EBUSY;
1633247388Skib			else {
1634247388Skib				LIST_REMOVE(dq, dq_hash);
1635247388Skib				dq->dq_ump = NULL;
1636247388Skib			}
16371541Srgrimes		}
16381541Srgrimes	}
1639167543Skib	DQH_UNLOCK();
1640247388Skib	return (error);
16411541Srgrimes}
1642207736Smckusick
1643207736Smckusick/*
1644222955Sjeff * The following three functions are provided for the adjustment of
1645222955Sjeff * quotas by the soft updates code.
1646222955Sjeff */
1647222955Sjeff#ifdef SOFTUPDATES
1648222955Sjeff/*
1649222955Sjeff * Acquire a reference to the quota structures associated with a vnode.
1650222955Sjeff * Return count of number of quota structures found.
1651222955Sjeff */
1652222955Sjeffint
1653222955Sjeffquotaref(vp, qrp)
1654222955Sjeff	struct vnode *vp;
1655222955Sjeff	struct dquot **qrp;
1656222955Sjeff{
1657222955Sjeff	struct inode *ip;
1658222955Sjeff	struct dquot *dq;
1659222955Sjeff	int i, found;
1660222955Sjeff
1661222955Sjeff	for (i = 0; i < MAXQUOTAS; i++)
1662222955Sjeff		qrp[i] = NODQUOT;
1663222955Sjeff	/*
1664222955Sjeff	 * Disk quotas must be turned off for system files.  Currently
1665222955Sjeff	 * snapshot and quota files.
1666222955Sjeff	 */
1667222955Sjeff	if ((vp->v_vflag & VV_SYSTEM) != 0)
1668222955Sjeff		return (0);
1669222955Sjeff	/*
1670222955Sjeff	 * Iterate through and copy active quotas.
1671222955Sjeff	 */
1672222955Sjeff	found = 0;
1673222955Sjeff	ip = VTOI(vp);
1674232003Skib	mtx_lock(&dqhlock);
1675222955Sjeff	for (i = 0; i < MAXQUOTAS; i++) {
1676222955Sjeff		if ((dq = ip->i_dquot[i]) == NODQUOT)
1677222955Sjeff			continue;
1678222955Sjeff		DQREF(dq);
1679222955Sjeff		qrp[i] = dq;
1680222955Sjeff		found++;
1681222955Sjeff	}
1682232003Skib	mtx_unlock(&dqhlock);
1683222955Sjeff	return (found);
1684222955Sjeff}
1685222955Sjeff
1686222955Sjeff/*
1687222955Sjeff * Release a set of quota structures obtained from a vnode.
1688222955Sjeff */
1689222955Sjeffvoid
1690222955Sjeffquotarele(qrp)
1691222955Sjeff	struct dquot **qrp;
1692222955Sjeff{
1693222955Sjeff	struct dquot *dq;
1694222955Sjeff	int i;
1695222955Sjeff
1696222955Sjeff	for (i = 0; i < MAXQUOTAS; i++) {
1697222955Sjeff		if ((dq = qrp[i]) == NODQUOT)
1698222955Sjeff			continue;
1699222955Sjeff		dqrele(NULL, dq);
1700222955Sjeff	}
1701222955Sjeff}
1702222955Sjeff
1703222955Sjeff/*
1704222955Sjeff * Adjust the number of blocks associated with a quota.
1705222955Sjeff * Positive numbers when adding blocks; negative numbers when freeing blocks.
1706222955Sjeff */
1707222955Sjeffvoid
1708222955Sjeffquotaadj(qrp, ump, blkcount)
1709222955Sjeff	struct dquot **qrp;
1710222955Sjeff	struct ufsmount *ump;
1711222955Sjeff	int64_t blkcount;
1712222955Sjeff{
1713222955Sjeff	struct dquot *dq;
1714222955Sjeff	ufs2_daddr_t ncurblocks;
1715222955Sjeff	int i;
1716222955Sjeff
1717222955Sjeff	if (blkcount == 0)
1718222955Sjeff		return;
1719222955Sjeff	for (i = 0; i < MAXQUOTAS; i++) {
1720222955Sjeff		if ((dq = qrp[i]) == NODQUOT)
1721222955Sjeff			continue;
1722222955Sjeff		DQI_LOCK(dq);
1723222955Sjeff		DQI_WAIT(dq, PINOD+1, "adjqta");
1724222955Sjeff		ncurblocks = dq->dq_curblocks + blkcount;
1725222955Sjeff		if (ncurblocks >= 0)
1726222955Sjeff			dq->dq_curblocks = ncurblocks;
1727222955Sjeff		else
1728222955Sjeff			dq->dq_curblocks = 0;
1729222955Sjeff		if (blkcount < 0)
1730222955Sjeff			dq->dq_flags &= ~DQ_BLKS;
1731222955Sjeff		else if (dq->dq_curblocks + blkcount >= dq->dq_bsoftlimit &&
1732222955Sjeff			 dq->dq_curblocks < dq->dq_bsoftlimit)
1733222955Sjeff			dq->dq_btime = time_second + ump->um_btime[i];
1734222955Sjeff		dq->dq_flags |= DQ_MOD;
1735222955Sjeff		DQI_UNLOCK(dq);
1736222955Sjeff	}
1737222955Sjeff}
1738222955Sjeff#endif /* SOFTUPDATES */
1739222955Sjeff
1740222955Sjeff/*
1741207736Smckusick * 32-bit / 64-bit conversion functions.
1742207736Smckusick *
1743207736Smckusick * 32-bit quota records are stored in native byte order.  Attention must
1744207736Smckusick * be paid to overflow issues.
1745207736Smckusick *
1746207736Smckusick * 64-bit quota records are stored in network byte order.
1747207736Smckusick */
1748207736Smckusick
1749207736Smckusick#define CLIP32(u64) (u64 > UINT32_MAX ? UINT32_MAX : (uint32_t)u64)
1750207736Smckusick
1751207736Smckusick/*
1752207736Smckusick * Convert 32-bit host-order structure to dquot.
1753207736Smckusick */
1754207736Smckusickstatic void
1755207736Smckusickdqb32_dq(const struct dqblk32 *dqb32, struct dquot *dq)
1756207736Smckusick{
1757207736Smckusick
1758207736Smckusick	dq->dq_bhardlimit = dqb32->dqb_bhardlimit;
1759207736Smckusick	dq->dq_bsoftlimit = dqb32->dqb_bsoftlimit;
1760207736Smckusick	dq->dq_curblocks = dqb32->dqb_curblocks;
1761207736Smckusick	dq->dq_ihardlimit = dqb32->dqb_ihardlimit;
1762207736Smckusick	dq->dq_isoftlimit = dqb32->dqb_isoftlimit;
1763207736Smckusick	dq->dq_curinodes = dqb32->dqb_curinodes;
1764207736Smckusick	dq->dq_btime = dqb32->dqb_btime;
1765207736Smckusick	dq->dq_itime = dqb32->dqb_itime;
1766207736Smckusick}
1767207736Smckusick
1768207736Smckusick/*
1769207736Smckusick * Convert 64-bit network-order structure to dquot.
1770207736Smckusick */
1771207736Smckusickstatic void
1772207736Smckusickdqb64_dq(const struct dqblk64 *dqb64, struct dquot *dq)
1773207736Smckusick{
1774207736Smckusick
1775207736Smckusick	dq->dq_bhardlimit = be64toh(dqb64->dqb_bhardlimit);
1776207736Smckusick	dq->dq_bsoftlimit = be64toh(dqb64->dqb_bsoftlimit);
1777207736Smckusick	dq->dq_curblocks = be64toh(dqb64->dqb_curblocks);
1778207736Smckusick	dq->dq_ihardlimit = be64toh(dqb64->dqb_ihardlimit);
1779207736Smckusick	dq->dq_isoftlimit = be64toh(dqb64->dqb_isoftlimit);
1780207736Smckusick	dq->dq_curinodes = be64toh(dqb64->dqb_curinodes);
1781207736Smckusick	dq->dq_btime = be64toh(dqb64->dqb_btime);
1782207736Smckusick	dq->dq_itime = be64toh(dqb64->dqb_itime);
1783207736Smckusick}
1784207736Smckusick
1785207736Smckusick/*
1786207736Smckusick * Convert dquot to 32-bit host-order structure.
1787207736Smckusick */
1788207736Smckusickstatic void
1789207736Smckusickdq_dqb32(const struct dquot *dq, struct dqblk32 *dqb32)
1790207736Smckusick{
1791207736Smckusick
1792207736Smckusick	dqb32->dqb_bhardlimit = CLIP32(dq->dq_bhardlimit);
1793207736Smckusick	dqb32->dqb_bsoftlimit = CLIP32(dq->dq_bsoftlimit);
1794207736Smckusick	dqb32->dqb_curblocks = CLIP32(dq->dq_curblocks);
1795207736Smckusick	dqb32->dqb_ihardlimit = CLIP32(dq->dq_ihardlimit);
1796207736Smckusick	dqb32->dqb_isoftlimit = CLIP32(dq->dq_isoftlimit);
1797207736Smckusick	dqb32->dqb_curinodes = CLIP32(dq->dq_curinodes);
1798207736Smckusick	dqb32->dqb_btime = CLIP32(dq->dq_btime);
1799207736Smckusick	dqb32->dqb_itime = CLIP32(dq->dq_itime);
1800207736Smckusick}
1801207736Smckusick
1802207736Smckusick/*
1803207736Smckusick * Convert dquot to 64-bit network-order structure.
1804207736Smckusick */
1805207736Smckusickstatic void
1806207736Smckusickdq_dqb64(const struct dquot *dq, struct dqblk64 *dqb64)
1807207736Smckusick{
1808207736Smckusick
1809207736Smckusick	dqb64->dqb_bhardlimit = htobe64(dq->dq_bhardlimit);
1810207736Smckusick	dqb64->dqb_bsoftlimit = htobe64(dq->dq_bsoftlimit);
1811207736Smckusick	dqb64->dqb_curblocks = htobe64(dq->dq_curblocks);
1812207736Smckusick	dqb64->dqb_ihardlimit = htobe64(dq->dq_ihardlimit);
1813207736Smckusick	dqb64->dqb_isoftlimit = htobe64(dq->dq_isoftlimit);
1814207736Smckusick	dqb64->dqb_curinodes = htobe64(dq->dq_curinodes);
1815207736Smckusick	dqb64->dqb_btime = htobe64(dq->dq_btime);
1816207736Smckusick	dqb64->dqb_itime = htobe64(dq->dq_itime);
1817207736Smckusick}
1818207736Smckusick
1819207736Smckusick/*
1820207736Smckusick * Convert 64-bit host-order structure to 32-bit host-order structure.
1821207736Smckusick */
1822207736Smckusickstatic void
1823207736Smckusickdqb64_dqb32(const struct dqblk64 *dqb64, struct dqblk32 *dqb32)
1824207736Smckusick{
1825207736Smckusick
1826207736Smckusick	dqb32->dqb_bhardlimit = CLIP32(dqb64->dqb_bhardlimit);
1827207736Smckusick	dqb32->dqb_bsoftlimit = CLIP32(dqb64->dqb_bsoftlimit);
1828207736Smckusick	dqb32->dqb_curblocks = CLIP32(dqb64->dqb_curblocks);
1829207736Smckusick	dqb32->dqb_ihardlimit = CLIP32(dqb64->dqb_ihardlimit);
1830207736Smckusick	dqb32->dqb_isoftlimit = CLIP32(dqb64->dqb_isoftlimit);
1831207736Smckusick	dqb32->dqb_curinodes = CLIP32(dqb64->dqb_curinodes);
1832207736Smckusick	dqb32->dqb_btime = CLIP32(dqb64->dqb_btime);
1833207736Smckusick	dqb32->dqb_itime = CLIP32(dqb64->dqb_itime);
1834207736Smckusick}
1835207736Smckusick
1836207736Smckusick/*
1837207736Smckusick * Convert 32-bit host-order structure to 64-bit host-order structure.
1838207736Smckusick */
1839207736Smckusickstatic void
1840207736Smckusickdqb32_dqb64(const struct dqblk32 *dqb32, struct dqblk64 *dqb64)
1841207736Smckusick{
1842207736Smckusick
1843207736Smckusick	dqb64->dqb_bhardlimit = dqb32->dqb_bhardlimit;
1844207736Smckusick	dqb64->dqb_bsoftlimit = dqb32->dqb_bsoftlimit;
1845207736Smckusick	dqb64->dqb_curblocks = dqb32->dqb_curblocks;
1846207736Smckusick	dqb64->dqb_ihardlimit = dqb32->dqb_ihardlimit;
1847207736Smckusick	dqb64->dqb_isoftlimit = dqb32->dqb_isoftlimit;
1848207736Smckusick	dqb64->dqb_curinodes = dqb32->dqb_curinodes;
1849207736Smckusick	dqb64->dqb_btime = dqb32->dqb_btime;
1850207736Smckusick	dqb64->dqb_itime = dqb32->dqb_itime;
1851207736Smckusick}
1852