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 *);
83248233Skibstatic 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;
500167543Skib	int error, flags, vfslocked;
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
513167543Skib	NDINIT(&nd, LOOKUP, FOLLOW | MPSAFE, UIO_USERSPACE, fname, td);
51462550Smckusick	flags = FREAD | FWRITE;
515230124Skib	vfs_ref(mp);
516230124Skib	vfs_unbusy(mp);
517170152Skib	error = vn_open(&nd, &flags, 0, NULL);
518230124Skib	if (error != 0) {
519230124Skib		vfs_rel(mp);
5201541Srgrimes		return (error);
521230124Skib	}
522167543Skib	vfslocked = NDHASGIANT(&nd);
52354655Seivind	NDFREE(&nd, NDF_ONLY_PNBUF);
5241541Srgrimes	vp = nd.ni_vp;
525230124Skib	error = vfs_busy(mp, MBF_NOWAIT);
526230124Skib	vfs_rel(mp);
527230124Skib	if (error == 0) {
528230124Skib		if (vp->v_type != VREG) {
529230124Skib			error = EACCES;
530230124Skib			vfs_unbusy(mp);
531230124Skib		}
532230124Skib	}
533230124Skib	if (error != 0) {
534208774Skib		VOP_UNLOCK(vp, 0);
53591406Sjhb		(void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
536167543Skib		VFS_UNLOCK_GIANT(vfslocked);
537230124Skib		return (error);
5381541Srgrimes	}
539167543Skib
540167543Skib	UFS_LOCK(ump);
541167543Skib	if ((ump->um_qflags[type] & (QTF_OPENING|QTF_CLOSING)) != 0) {
542167543Skib		UFS_UNLOCK(ump);
543208774Skib		VOP_UNLOCK(vp, 0);
544167543Skib		(void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
545167543Skib		VFS_UNLOCK_GIANT(vfslocked);
546230124Skib		vfs_unbusy(mp);
547167543Skib		return (EALREADY);
548167543Skib	}
549167543Skib	ump->um_qflags[type] |= QTF_OPENING|QTF_CLOSING;
550207736Smckusick	UFS_UNLOCK(ump);
551207736Smckusick	if ((error = dqopen(vp, ump, type)) != 0) {
552208774Skib		VOP_UNLOCK(vp, 0);
553207736Smckusick		UFS_LOCK(ump);
554207736Smckusick		ump->um_qflags[type] &= ~(QTF_OPENING|QTF_CLOSING);
555207736Smckusick		UFS_UNLOCK(ump);
556207736Smckusick		(void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
557207736Smckusick		VFS_UNLOCK_GIANT(vfslocked);
558230124Skib		vfs_unbusy(mp);
559207736Smckusick		return (error);
560207736Smckusick	}
561208774Skib	VOP_UNLOCK(vp, 0);
562162647Stegge	MNT_ILOCK(mp);
5631541Srgrimes	mp->mnt_flag |= MNT_QUOTA;
564162647Stegge	MNT_IUNLOCK(mp);
565167543Skib
566167543Skib	vpp = &ump->um_quotas[type];
567167543Skib	if (*vpp != vp)
568167543Skib		quotaoff1(td, mp, type);
569167543Skib
570175202Sattilio	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
571101308Sjeff	vp->v_vflag |= VV_SYSTEM;
572175294Sattilio	VOP_UNLOCK(vp, 0);
5731541Srgrimes	*vpp = vp;
574167543Skib	VFS_UNLOCK_GIANT(vfslocked);
5751541Srgrimes	/*
5761541Srgrimes	 * Save the credential of the process that turned on quotas.
5771541Srgrimes	 * Set up the time limits for this quota.
5781541Srgrimes	 */
57991406Sjhb	ump->um_cred[type] = crhold(td->td_ucred);
5801541Srgrimes	ump->um_btime[type] = MAX_DQ_TIME;
5811541Srgrimes	ump->um_itime[type] = MAX_IQ_TIME;
5821541Srgrimes	if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
5831541Srgrimes		if (dq->dq_btime > 0)
5841541Srgrimes			ump->um_btime[type] = dq->dq_btime;
5851541Srgrimes		if (dq->dq_itime > 0)
5861541Srgrimes			ump->um_itime[type] = dq->dq_itime;
5871541Srgrimes		dqrele(NULLVP, dq);
5881541Srgrimes	}
5891541Srgrimes	/*
590167543Skib	 * Allow the getdq from getinoquota below to read the quota
591167543Skib	 * from file.
592167543Skib	 */
593167543Skib	UFS_LOCK(ump);
594167543Skib	ump->um_qflags[type] &= ~QTF_CLOSING;
595167543Skib	UFS_UNLOCK(ump);
596167543Skib	/*
5971541Srgrimes	 * Search vnodes associated with this mount point,
5981541Srgrimes	 * adding references to quota file being opened.
5991541Srgrimes	 * NB: only need to add dquot's for inodes being modified.
6001541Srgrimes	 */
6011541Srgrimesagain:
602235626Smckusick	MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
603120737Sjeff		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
604235626Smckusick			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
605103943Sjeff			goto again;
606103943Sjeff		}
60778912Sjhb		if (vp->v_type == VNON || vp->v_writecount == 0) {
608175294Sattilio			VOP_UNLOCK(vp, 0);
609121874Skan			vrele(vp);
6101541Srgrimes			continue;
61178912Sjhb		}
6123427Sphk		error = getinoquota(VTOI(vp));
613175294Sattilio		VOP_UNLOCK(vp, 0);
614121874Skan		vrele(vp);
615154152Stegge		if (error) {
616235626Smckusick			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
6171541Srgrimes			break;
618154152Stegge		}
6191541Srgrimes	}
620167543Skib
621167543Skib        if (error)
622167543Skib		quotaoff_inchange(td, mp, type);
623167543Skib	UFS_LOCK(ump);
6241541Srgrimes	ump->um_qflags[type] &= ~QTF_OPENING;
625167543Skib	KASSERT((ump->um_qflags[type] & QTF_CLOSING) == 0,
626167543Skib		("quotaon: leaking flags"));
627167543Skib	UFS_UNLOCK(ump);
628167543Skib
629230124Skib	vfs_unbusy(mp);
6301541Srgrimes	return (error);
6311541Srgrimes}
6321541Srgrimes
6331541Srgrimes/*
634167543Skib * Main code to turn off disk quotas for a filesystem. Does not change
635167543Skib * flags.
6361541Srgrimes */
637167543Skibstatic int
638181327Sdesquotaoff1(struct thread *td, struct mount *mp, int type)
6391541Srgrimes{
64022521Sdyson	struct vnode *vp;
641154152Stegge	struct vnode *qvp, *mvp;
642166743Skib	struct ufsmount *ump;
64322521Sdyson	struct dquot *dq;
64422521Sdyson	struct inode *ip;
645167543Skib	struct ucred *cr;
646167543Skib	int vfslocked;
6471541Srgrimes	int error;
6488876Srgrimes
649167543Skib	ump = VFSTOUFS(mp);
650116384Srwatson
651167543Skib	UFS_LOCK(ump);
652167543Skib	KASSERT((ump->um_qflags[type] & QTF_CLOSING) != 0,
653167543Skib		("quotaoff1: flags are invalid"));
654167543Skib	if ((qvp = ump->um_quotas[type]) == NULLVP) {
655167543Skib		UFS_UNLOCK(ump);
6561541Srgrimes		return (0);
657167543Skib	}
658167543Skib	cr = ump->um_cred[type];
659167543Skib	UFS_UNLOCK(ump);
660181327Sdes
6611541Srgrimes	/*
6621541Srgrimes	 * Search vnodes associated with this mount point,
6631541Srgrimes	 * deleting any references to quota file being closed.
6641541Srgrimes	 */
6651541Srgrimesagain:
666235626Smckusick	MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
66778912Sjhb		if (vp->v_type == VNON) {
668120737Sjeff			VI_UNLOCK(vp);
66934266Sjulian			continue;
67078912Sjhb		}
67183366Sjulian		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
672235626Smckusick			MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
6731541Srgrimes			goto again;
67478912Sjhb		}
6751541Srgrimes		ip = VTOI(vp);
6761541Srgrimes		dq = ip->i_dquot[type];
6771541Srgrimes		ip->i_dquot[type] = NODQUOT;
6781541Srgrimes		dqrele(vp, dq);
679175294Sattilio		VOP_UNLOCK(vp, 0);
680121874Skan		vrele(vp);
6811541Srgrimes	}
682167543Skib
683248233Skib	error = dqflush(qvp);
684248233Skib	if (error != 0)
685248233Skib		return (error);
686248233Skib
687248233Skib	/*
688248233Skib	 * Clear um_quotas before closing the quota vnode to prevent
689167543Skib	 * access to the closed vnode from dqget/dqsync
690167543Skib	 */
691167543Skib	UFS_LOCK(ump);
692167543Skib	ump->um_quotas[type] = NULLVP;
693167543Skib	ump->um_cred[type] = NOCRED;
694167543Skib	UFS_UNLOCK(ump);
695167543Skib
696167543Skib	vfslocked = VFS_LOCK_GIANT(qvp->v_mount);
697175202Sattilio	vn_lock(qvp, LK_EXCLUSIVE | LK_RETRY);
698101308Sjeff	qvp->v_vflag &= ~VV_SYSTEM;
699175294Sattilio	VOP_UNLOCK(qvp, 0);
70091406Sjhb	error = vn_close(qvp, FREAD|FWRITE, td->td_ucred, td);
701167543Skib	VFS_UNLOCK_GIANT(vfslocked);
702167543Skib	crfree(cr);
703167543Skib
704167543Skib	return (error);
705167543Skib}
706167543Skib
707167543Skib/*
708167543Skib * Turns off quotas, assumes that ump->um_qflags are already checked
709167543Skib * and QTF_CLOSING is set to indicate operation in progress. Fixes
710167543Skib * ump->um_qflags and mp->mnt_flag after.
711167543Skib */
712167543Skibint
713181327Sdesquotaoff_inchange(struct thread *td, struct mount *mp, int type)
714167543Skib{
715167543Skib	struct ufsmount *ump;
716167543Skib	int i;
717167543Skib	int error;
718167543Skib
719167543Skib	error = quotaoff1(td, mp, type);
720167543Skib
721167543Skib	ump = VFSTOUFS(mp);
722167543Skib	UFS_LOCK(ump);
7231541Srgrimes	ump->um_qflags[type] &= ~QTF_CLOSING;
724167543Skib	for (i = 0; i < MAXQUOTAS; i++)
725167543Skib		if (ump->um_quotas[i] != NULLVP)
7261541Srgrimes			break;
727167543Skib	if (i == MAXQUOTAS) {
728162647Stegge		MNT_ILOCK(mp);
7291541Srgrimes		mp->mnt_flag &= ~MNT_QUOTA;
730162647Stegge		MNT_IUNLOCK(mp);
731162647Stegge	}
732167543Skib	UFS_UNLOCK(ump);
7331541Srgrimes	return (error);
7341541Srgrimes}
7351541Srgrimes
7361541Srgrimes/*
737167543Skib * Q_QUOTAOFF - turn off disk quotas for a filesystem.
738167543Skib */
739167543Skibint
740181327Sdesquotaoff(struct thread *td, struct mount *mp, int type)
741167543Skib{
742167543Skib	struct ufsmount *ump;
743167543Skib	int error;
744167543Skib
745170587Srwatson	error = priv_check(td, PRIV_UFS_QUOTAOFF);
746167543Skib	if (error)
747167543Skib		return (error);
748167543Skib
749167543Skib	ump = VFSTOUFS(mp);
750167543Skib	UFS_LOCK(ump);
751167543Skib	if ((ump->um_qflags[type] & (QTF_OPENING|QTF_CLOSING)) != 0) {
752167543Skib		UFS_UNLOCK(ump);
753167543Skib		return (EALREADY);
754167543Skib	}
755167543Skib	ump->um_qflags[type] |= QTF_CLOSING;
756167543Skib	UFS_UNLOCK(ump);
757167543Skib
758167543Skib	return (quotaoff_inchange(td, mp, type));
759167543Skib}
760167543Skib
761167543Skib/*
7621541Srgrimes * Q_GETQUOTA - return current values in a dqblk structure.
7631541Srgrimes */
764207736Smckusickstatic int
765207736Smckusick_getquota(struct thread *td, struct mount *mp, u_long id, int type,
766207736Smckusick    struct dqblk64 *dqb)
7671541Srgrimes{
7681541Srgrimes	struct dquot *dq;
7691541Srgrimes	int error;
7701541Srgrimes
771116384Srwatson	switch (type) {
772116384Srwatson	case USRQUOTA:
773116384Srwatson		if ((td->td_ucred->cr_uid != id) && !unprivileged_get_quota) {
774170587Srwatson			error = priv_check(td, PRIV_VFS_GETQUOTA);
775116384Srwatson			if (error)
776116384Srwatson				return (error);
777116384Srwatson		}
778181327Sdes		break;
779116384Srwatson
780116384Srwatson	case GRPQUOTA:
781164033Srwatson		if (!groupmember(id, td->td_ucred) &&
782164033Srwatson		    !unprivileged_get_quota) {
783170587Srwatson			error = priv_check(td, PRIV_VFS_GETQUOTA);
784116384Srwatson			if (error)
785116384Srwatson				return (error);
786116384Srwatson		}
787116384Srwatson		break;
788116384Srwatson
789116384Srwatson	default:
790116384Srwatson		return (EINVAL);
791116384Srwatson	}
792116384Srwatson
793167543Skib	dq = NODQUOT;
7943427Sphk	error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq);
7953427Sphk	if (error)
7961541Srgrimes		return (error);
797207736Smckusick	*dqb = dq->dq_dqb;
7981541Srgrimes	dqrele(NULLVP, dq);
7991541Srgrimes	return (error);
8001541Srgrimes}
8011541Srgrimes
8021541Srgrimes/*
8031541Srgrimes * Q_SETQUOTA - assign an entire dqblk structure.
8041541Srgrimes */
805207736Smckusickstatic int
806207736Smckusick_setquota(struct thread *td, struct mount *mp, u_long id, int type,
807207736Smckusick    struct dqblk64 *dqb)
8081541Srgrimes{
80996506Sphk	struct dquot *dq;
8101541Srgrimes	struct dquot *ndq;
811166743Skib	struct ufsmount *ump;
812207736Smckusick	struct dqblk64 newlim;
8131541Srgrimes	int error;
8141541Srgrimes
815170587Srwatson	error = priv_check(td, PRIV_VFS_SETQUOTA);
816116384Srwatson	if (error)
817116384Srwatson		return (error);
818116384Srwatson
819207736Smckusick	newlim = *dqb;
820167543Skib
821167543Skib	ndq = NODQUOT;
822167543Skib	ump = VFSTOUFS(mp);
823167543Skib
8243427Sphk	error = dqget(NULLVP, id, ump, type, &ndq);
8253427Sphk	if (error)
8261541Srgrimes		return (error);
8271541Srgrimes	dq = ndq;
828167543Skib	DQI_LOCK(dq);
829167543Skib	DQI_WAIT(dq, PINOD+1, "setqta");
8301541Srgrimes	/*
8311541Srgrimes	 * Copy all but the current values.
8321541Srgrimes	 * Reset time limit if previously had no soft limit or were
8331541Srgrimes	 * under it, but now have a soft limit and are over it.
8341541Srgrimes	 */
8351541Srgrimes	newlim.dqb_curblocks = dq->dq_curblocks;
8361541Srgrimes	newlim.dqb_curinodes = dq->dq_curinodes;
8371541Srgrimes	if (dq->dq_id != 0) {
8381541Srgrimes		newlim.dqb_btime = dq->dq_btime;
8391541Srgrimes		newlim.dqb_itime = dq->dq_itime;
8401541Srgrimes	}
8411541Srgrimes	if (newlim.dqb_bsoftlimit &&
8421541Srgrimes	    dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
8431541Srgrimes	    (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
84434961Sphk		newlim.dqb_btime = time_second + ump->um_btime[type];
8451541Srgrimes	if (newlim.dqb_isoftlimit &&
8461541Srgrimes	    dq->dq_curinodes >= newlim.dqb_isoftlimit &&
8471541Srgrimes	    (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
84834961Sphk		newlim.dqb_itime = time_second + ump->um_itime[type];
8491541Srgrimes	dq->dq_dqb = newlim;
8501541Srgrimes	if (dq->dq_curblocks < dq->dq_bsoftlimit)
8511541Srgrimes		dq->dq_flags &= ~DQ_BLKS;
8521541Srgrimes	if (dq->dq_curinodes < dq->dq_isoftlimit)
8531541Srgrimes		dq->dq_flags &= ~DQ_INODS;
8541541Srgrimes	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
8551541Srgrimes	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
8561541Srgrimes		dq->dq_flags |= DQ_FAKE;
8571541Srgrimes	else
8581541Srgrimes		dq->dq_flags &= ~DQ_FAKE;
8591541Srgrimes	dq->dq_flags |= DQ_MOD;
860167543Skib	DQI_UNLOCK(dq);
8611541Srgrimes	dqrele(NULLVP, dq);
8621541Srgrimes	return (0);
8631541Srgrimes}
8641541Srgrimes
8651541Srgrimes/*
8661541Srgrimes * Q_SETUSE - set current inode and block usage.
8671541Srgrimes */
868207736Smckusickstatic int
869207736Smckusick_setuse(struct thread *td, struct mount *mp, u_long id, int type,
870207736Smckusick    struct dqblk64 *dqb)
8711541Srgrimes{
87296506Sphk	struct dquot *dq;
873166743Skib	struct ufsmount *ump;
8741541Srgrimes	struct dquot *ndq;
875207736Smckusick	struct dqblk64 usage;
8761541Srgrimes	int error;
8771541Srgrimes
878170587Srwatson	error = priv_check(td, PRIV_UFS_SETUSE);
879116384Srwatson	if (error)
880116384Srwatson		return (error);
881116384Srwatson
882207736Smckusick	usage = *dqb;
883167543Skib
884167543Skib	ump = VFSTOUFS(mp);
885167543Skib	ndq = NODQUOT;
886167543Skib
8873427Sphk	error = dqget(NULLVP, id, ump, type, &ndq);
8883427Sphk	if (error)
8891541Srgrimes		return (error);
8901541Srgrimes	dq = ndq;
891167543Skib	DQI_LOCK(dq);
892167543Skib	DQI_WAIT(dq, PINOD+1, "setuse");
8931541Srgrimes	/*
8941541Srgrimes	 * Reset time limit if have a soft limit and were
8951541Srgrimes	 * previously under it, but are now over it.
8961541Srgrimes	 */
8971541Srgrimes	if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
8981541Srgrimes	    usage.dqb_curblocks >= dq->dq_bsoftlimit)
89934961Sphk		dq->dq_btime = time_second + ump->um_btime[type];
9001541Srgrimes	if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
9011541Srgrimes	    usage.dqb_curinodes >= dq->dq_isoftlimit)
90234961Sphk		dq->dq_itime = time_second + ump->um_itime[type];
9031541Srgrimes	dq->dq_curblocks = usage.dqb_curblocks;
9041541Srgrimes	dq->dq_curinodes = usage.dqb_curinodes;
9051541Srgrimes	if (dq->dq_curblocks < dq->dq_bsoftlimit)
9061541Srgrimes		dq->dq_flags &= ~DQ_BLKS;
9071541Srgrimes	if (dq->dq_curinodes < dq->dq_isoftlimit)
9081541Srgrimes		dq->dq_flags &= ~DQ_INODS;
9091541Srgrimes	dq->dq_flags |= DQ_MOD;
910167543Skib	DQI_UNLOCK(dq);
9111541Srgrimes	dqrele(NULLVP, dq);
9121541Srgrimes	return (0);
9131541Srgrimes}
9141541Srgrimes
915207736Smckusickint
916207736Smckusickgetquota32(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
917207736Smckusick{
918207736Smckusick	struct dqblk32 dqb32;
919207736Smckusick	struct dqblk64 dqb64;
920207736Smckusick	int error;
921207736Smckusick
922207736Smckusick	error = _getquota(td, mp, id, type, &dqb64);
923207736Smckusick	if (error)
924207736Smckusick		return (error);
925207736Smckusick	dqb64_dqb32(&dqb64, &dqb32);
926207736Smckusick	error = copyout(&dqb32, addr, sizeof(dqb32));
927207736Smckusick	return (error);
928207736Smckusick}
929207736Smckusick
930207736Smckusickint
931207736Smckusicksetquota32(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
932207736Smckusick{
933207736Smckusick	struct dqblk32 dqb32;
934207736Smckusick	struct dqblk64 dqb64;
935207736Smckusick	int error;
936207736Smckusick
937207736Smckusick	error = copyin(addr, &dqb32, sizeof(dqb32));
938207736Smckusick	if (error)
939207736Smckusick		return (error);
940207736Smckusick	dqb32_dqb64(&dqb32, &dqb64);
941207736Smckusick	error = _setquota(td, mp, id, type, &dqb64);
942207736Smckusick	return (error);
943207736Smckusick}
944207736Smckusick
945207736Smckusickint
946207736Smckusicksetuse32(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
947207736Smckusick{
948207736Smckusick	struct dqblk32 dqb32;
949207736Smckusick	struct dqblk64 dqb64;
950207736Smckusick	int error;
951207736Smckusick
952207736Smckusick	error = copyin(addr, &dqb32, sizeof(dqb32));
953207736Smckusick	if (error)
954207736Smckusick		return (error);
955207736Smckusick	dqb32_dqb64(&dqb32, &dqb64);
956207736Smckusick	error = _setuse(td, mp, id, type, &dqb64);
957207736Smckusick	return (error);
958207736Smckusick}
959207736Smckusick
960207736Smckusickint
961207736Smckusickgetquota(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
962207736Smckusick{
963207736Smckusick	struct dqblk64 dqb64;
964207736Smckusick	int error;
965207736Smckusick
966207736Smckusick	error = _getquota(td, mp, id, type, &dqb64);
967207736Smckusick	if (error)
968207736Smckusick		return (error);
969207736Smckusick	error = copyout(&dqb64, addr, sizeof(dqb64));
970207736Smckusick	return (error);
971207736Smckusick}
972207736Smckusick
973207736Smckusickint
974207736Smckusicksetquota(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
975207736Smckusick{
976207736Smckusick	struct dqblk64 dqb64;
977207736Smckusick	int error;
978207736Smckusick
979207736Smckusick	error = copyin(addr, &dqb64, sizeof(dqb64));
980207736Smckusick	if (error)
981207736Smckusick		return (error);
982207736Smckusick	error = _setquota(td, mp, id, type, &dqb64);
983207736Smckusick	return (error);
984207736Smckusick}
985207736Smckusick
986207736Smckusickint
987207736Smckusicksetuse(struct thread *td, struct mount *mp, u_long id, int type, void *addr)
988207736Smckusick{
989207736Smckusick	struct dqblk64 dqb64;
990207736Smckusick	int error;
991207736Smckusick
992207736Smckusick	error = copyin(addr, &dqb64, sizeof(dqb64));
993207736Smckusick	if (error)
994207736Smckusick		return (error);
995207736Smckusick	error = _setuse(td, mp, id, type, &dqb64);
996207736Smckusick	return (error);
997207736Smckusick}
998207736Smckusick
9991541Srgrimes/*
1000207736Smckusick * Q_GETQUOTASIZE - get bit-size of quota file fields
1001207736Smckusick */
1002207736Smckusickint
1003207736Smckusickgetquotasize(struct thread *td, struct mount *mp, u_long id, int type,
1004207736Smckusick    void *sizep)
1005207736Smckusick{
1006207736Smckusick	struct ufsmount *ump = VFSTOUFS(mp);
1007207736Smckusick	int bitsize;
1008207736Smckusick
1009207736Smckusick	UFS_LOCK(ump);
1010207736Smckusick	if (ump->um_quotas[type] == NULLVP ||
1011207736Smckusick	    (ump->um_qflags[type] & QTF_CLOSING)) {
1012207736Smckusick		UFS_UNLOCK(ump);
1013207736Smckusick		return (EINVAL);
1014207736Smckusick	}
1015207736Smckusick	if ((ump->um_qflags[type] & QTF_64BIT) != 0)
1016207736Smckusick		bitsize = 64;
1017207736Smckusick	else
1018207736Smckusick		bitsize = 32;
1019207736Smckusick	UFS_UNLOCK(ump);
1020207736Smckusick	return (copyout(&bitsize, sizep, sizeof(int)));
1021207736Smckusick}
1022207736Smckusick
1023207736Smckusick/*
10241541Srgrimes * Q_SYNC - sync quota files to disk.
10251541Srgrimes */
10261541Srgrimesint
1027181327Sdesqsync(struct mount *mp)
10281541Srgrimes{
10291541Srgrimes	struct ufsmount *ump = VFSTOUFS(mp);
103083366Sjulian	struct thread *td = curthread;		/* XXX */
1031154152Stegge	struct vnode *vp, *mvp;
103222521Sdyson	struct dquot *dq;
103322521Sdyson	int i, error;
10341541Srgrimes
10351541Srgrimes	/*
10361541Srgrimes	 * Check if the mount point has any quotas.
10371541Srgrimes	 * If not, simply return.
10381541Srgrimes	 */
1039167543Skib	UFS_LOCK(ump);
10401541Srgrimes	for (i = 0; i < MAXQUOTAS; i++)
10411541Srgrimes		if (ump->um_quotas[i] != NULLVP)
10421541Srgrimes			break;
1043167543Skib	UFS_UNLOCK(ump);
10441541Srgrimes	if (i == MAXQUOTAS)
10451541Srgrimes		return (0);
10461541Srgrimes	/*
10471541Srgrimes	 * Search vnodes associated with this mount point,
10481541Srgrimes	 * synchronizing any modified dquot structures.
10491541Srgrimes	 */
10501541Srgrimesagain:
1051235626Smckusick	MNT_VNODE_FOREACH_ACTIVE(vp, mp, mvp) {
105278912Sjhb		if (vp->v_type == VNON) {
1053120737Sjeff			VI_UNLOCK(vp);
105434266Sjulian			continue;
105578912Sjhb		}
1056155897Sjeff		error = vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td);
105722521Sdyson		if (error) {
1058154152Stegge			if (error == ENOENT) {
1059244375Skib				MNT_VNODE_FOREACH_ACTIVE_ABORT(mp, mvp);
106022521Sdyson				goto again;
1061154152Stegge			}
10621541Srgrimes			continue;
106322521Sdyson		}
10641541Srgrimes		for (i = 0; i < MAXQUOTAS; i++) {
10651541Srgrimes			dq = VTOI(vp)->i_dquot[i];
1066167543Skib			if (dq != NODQUOT)
10671541Srgrimes				dqsync(vp, dq);
10681541Srgrimes		}
1069121847Skan		vput(vp);
10701541Srgrimes	}
10711541Srgrimes	return (0);
10721541Srgrimes}
10731541Srgrimes
10741541Srgrimes/*
1075235626Smckusick * Sync quota file for given vnode to disk.
1076235626Smckusick */
1077235626Smckusickint
1078235626Smckusickqsyncvp(struct vnode *vp)
1079235626Smckusick{
1080235626Smckusick	struct ufsmount *ump = VFSTOUFS(vp->v_mount);
1081235626Smckusick	struct dquot *dq;
1082235626Smckusick	int i;
1083235626Smckusick
1084235626Smckusick	/*
1085235626Smckusick	 * Check if the mount point has any quotas.
1086235626Smckusick	 * If not, simply return.
1087235626Smckusick	 */
1088235626Smckusick	UFS_LOCK(ump);
1089235626Smckusick	for (i = 0; i < MAXQUOTAS; i++)
1090235626Smckusick		if (ump->um_quotas[i] != NULLVP)
1091235626Smckusick			break;
1092235626Smckusick	UFS_UNLOCK(ump);
1093235626Smckusick	if (i == MAXQUOTAS)
1094235626Smckusick		return (0);
1095235626Smckusick	/*
1096235626Smckusick	 * Search quotas associated with this vnode
1097235626Smckusick	 * synchronizing any modified dquot structures.
1098235626Smckusick	 */
1099235626Smckusick	for (i = 0; i < MAXQUOTAS; i++) {
1100235626Smckusick		dq = VTOI(vp)->i_dquot[i];
1101235626Smckusick		if (dq != NODQUOT)
1102235626Smckusick			dqsync(vp, dq);
1103235626Smckusick	}
1104235626Smckusick	return (0);
1105235626Smckusick}
1106235626Smckusick
1107235626Smckusick/*
11081541Srgrimes * Code pertaining to management of the in-core dquot data structures.
11091541Srgrimes */
111022521Sdyson#define DQHASH(dqvp, id) \
111137649Sbde	(&dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & dqhash])
111260938Sjakestatic LIST_HEAD(dqhash, dquot) *dqhashtbl;
111312971Sphkstatic u_long dqhash;
11141541Srgrimes
11151541Srgrimes/*
11161541Srgrimes * Dquot free list.
11171541Srgrimes */
11181541Srgrimes#define	DQUOTINC	5	/* minimum free dquots desired */
111960938Sjakestatic TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
112012971Sphkstatic long numdquot, desireddquot = DQUOTINC;
11211541Srgrimes
1122181327Sdes/*
1123167543Skib * Lock to protect quota hash, dq free list and dq_cnt ref counters of
1124167543Skib * _all_ dqs.
1125167543Skib */
1126167543Skibstruct mtx dqhlock;
1127167543Skib
1128167543Skib#define	DQH_LOCK()	mtx_lock(&dqhlock)
1129167543Skib#define	DQH_UNLOCK()	mtx_unlock(&dqhlock)
1130167543Skib
1131167543Skibstatic struct dquot *dqhashfind(struct dqhash *dqh, u_long id,
1132167543Skib	struct vnode *dqvp);
1133167543Skib
11341541Srgrimes/*
11351541Srgrimes * Initialize the quota system.
11361541Srgrimes */
11371541Srgrimesvoid
1138181327Sdesdqinit(void)
11391541Srgrimes{
11401541Srgrimes
1141167543Skib	mtx_init(&dqhlock, "dqhlock", NULL, MTX_DEF);
11421541Srgrimes	dqhashtbl = hashinit(desiredvnodes, M_DQUOT, &dqhash);
114322521Sdyson	TAILQ_INIT(&dqfreelist);
11441541Srgrimes}
11451541Srgrimes
11461541Srgrimes/*
114799101Siedowse * Shut down the quota system.
114899101Siedowse */
114999101Siedowsevoid
1150181327Sdesdquninit(void)
115199101Siedowse{
115299101Siedowse	struct dquot *dq;
115399101Siedowse
115499101Siedowse	hashdestroy(dqhashtbl, M_DQUOT, dqhash);
115599101Siedowse	while ((dq = TAILQ_FIRST(&dqfreelist)) != NULL) {
115699101Siedowse		TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
1157167543Skib		mtx_destroy(&dq->dq_lock);
115899101Siedowse		free(dq, M_DQUOT);
115999101Siedowse	}
1160167543Skib	mtx_destroy(&dqhlock);
116199101Siedowse}
116299101Siedowse
1163167543Skibstatic struct dquot *
1164181327Sdesdqhashfind(struct dqhash *dqh, u_long id, struct vnode *dqvp)
1165167543Skib{
1166167543Skib	struct dquot *dq;
1167167543Skib
1168167543Skib	mtx_assert(&dqhlock, MA_OWNED);
1169167543Skib	LIST_FOREACH(dq, dqh, dq_hash) {
1170167543Skib		if (dq->dq_id != id ||
1171167543Skib		    dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
1172167543Skib			continue;
1173167543Skib		/*
1174167543Skib		 * Cache hit with no references.  Take
1175167543Skib		 * the structure off the free list.
1176167543Skib		 */
1177167543Skib		if (dq->dq_cnt == 0)
1178167543Skib			TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
1179167543Skib		DQREF(dq);
1180167543Skib		return (dq);
1181167543Skib	}
1182167543Skib	return (NODQUOT);
1183167543Skib}
1184167543Skib
118599101Siedowse/*
1186207736Smckusick * Determine the quota file type.
1187207736Smckusick *
1188207736Smckusick * A 32-bit quota file is simply an array of struct dqblk32.
1189207736Smckusick *
1190207736Smckusick * A 64-bit quota file is a struct dqhdr64 followed by an array of struct
1191207736Smckusick * dqblk64.  The header contains various magic bits which allow us to be
1192207736Smckusick * reasonably confident that it is indeeda 64-bit quota file and not just
1193207736Smckusick * a 32-bit quota file that just happens to "look right".
1194207736Smckusick *
1195207736Smckusick */
1196207736Smckusickstatic int
1197207736Smckusickdqopen(struct vnode *vp, struct ufsmount *ump, int type)
1198207736Smckusick{
1199207736Smckusick	struct dqhdr64 dqh;
1200207736Smckusick	struct iovec aiov;
1201207736Smckusick	struct uio auio;
1202208774Skib	int error;
1203207736Smckusick
1204208774Skib	ASSERT_VOP_LOCKED(vp, "dqopen");
1205207736Smckusick	auio.uio_iov = &aiov;
1206207736Smckusick	auio.uio_iovcnt = 1;
1207207736Smckusick	aiov.iov_base = &dqh;
1208207736Smckusick	aiov.iov_len = sizeof(dqh);
1209207736Smckusick	auio.uio_resid = sizeof(dqh);
1210207736Smckusick	auio.uio_offset = 0;
1211207736Smckusick	auio.uio_segflg = UIO_SYSSPACE;
1212207736Smckusick	auio.uio_rw = UIO_READ;
1213207736Smckusick	auio.uio_td = (struct thread *)0;
1214207736Smckusick	error = VOP_READ(vp, &auio, 0, ump->um_cred[type]);
1215207736Smckusick
1216207736Smckusick	if (error != 0)
1217207736Smckusick		return (error);
1218207736Smckusick	if (auio.uio_resid > 0) {
1219207736Smckusick		/* assume 32 bits */
1220207736Smckusick		return (0);
1221207736Smckusick	}
1222207736Smckusick
1223207736Smckusick	UFS_LOCK(ump);
1224207736Smckusick	if (strcmp(dqh.dqh_magic, Q_DQHDR64_MAGIC) == 0 &&
1225207736Smckusick	    be32toh(dqh.dqh_version) == Q_DQHDR64_VERSION &&
1226207736Smckusick	    be32toh(dqh.dqh_hdrlen) == (uint32_t)sizeof(struct dqhdr64) &&
1227207736Smckusick	    be32toh(dqh.dqh_reclen) == (uint32_t)sizeof(struct dqblk64)) {
1228207736Smckusick		/* XXX: what if the magic matches, but the sizes are wrong? */
1229207736Smckusick		ump->um_qflags[type] |= QTF_64BIT;
1230207736Smckusick	} else {
1231207736Smckusick		ump->um_qflags[type] &= ~QTF_64BIT;
1232207736Smckusick	}
1233207736Smckusick	UFS_UNLOCK(ump);
1234207736Smckusick
1235207736Smckusick	return (0);
1236207736Smckusick}
1237207736Smckusick
1238207736Smckusick/*
12391541Srgrimes * Obtain a dquot structure for the specified identifier and quota file
12401541Srgrimes * reading the information from the file if necessary.
12411541Srgrimes */
124212971Sphkstatic int
1243181327Sdesdqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type,
1244181327Sdes    struct dquot **dqp)
12451541Srgrimes{
1246207736Smckusick	uint8_t buf[sizeof(struct dqblk64)];
1247207736Smckusick	off_t base, recsize;
1248167543Skib	struct dquot *dq, *dq1;
124922521Sdyson	struct dqhash *dqh;
125022521Sdyson	struct vnode *dqvp;
12511541Srgrimes	struct iovec aiov;
12521541Srgrimes	struct uio auio;
1253167543Skib	int vfslocked, dqvplocked, error;
12541541Srgrimes
1255167543Skib#ifdef DEBUG_VFS_LOCKS
1256167543Skib	if (vp != NULLVP)
1257167543Skib		ASSERT_VOP_ELOCKED(vp, "dqget");
1258167543Skib#endif
1259167543Skib
1260167543Skib	if (vp != NULLVP && *dqp != NODQUOT) {
1261167543Skib		return (0);
1262167543Skib	}
1263167543Skib
1264166380Smpp	/* XXX: Disallow negative id values to prevent the
1265166380Smpp	* creation of 100GB+ quota data files.
1266166380Smpp	*/
1267166380Smpp	if ((int)id < 0)
1268166380Smpp		return (EINVAL);
1269167543Skib
1270167543Skib	UFS_LOCK(ump);
12711541Srgrimes	dqvp = ump->um_quotas[type];
12721541Srgrimes	if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
12731541Srgrimes		*dqp = NODQUOT;
1274167543Skib		UFS_UNLOCK(ump);
12751541Srgrimes		return (EINVAL);
12761541Srgrimes	}
1277167543Skib	vref(dqvp);
1278167543Skib	UFS_UNLOCK(ump);
1279167543Skib	error = 0;
1280167543Skib	dqvplocked = 0;
1281167543Skib
12821541Srgrimes	/*
12831541Srgrimes	 * Check the cache first.
12841541Srgrimes	 */
128522521Sdyson	dqh = DQHASH(dqvp, id);
1286167543Skib	DQH_LOCK();
1287167543Skib	dq = dqhashfind(dqh, id, dqvp);
1288167543Skib	if (dq != NULL) {
1289167543Skib		DQH_UNLOCK();
1290167543Skibhfound:		DQI_LOCK(dq);
1291167543Skib		DQI_WAIT(dq, PINOD+1, "dqget");
1292167543Skib		DQI_UNLOCK(dq);
1293167543Skib		if (dq->dq_ump == NULL) {
1294167543Skib			dqrele(vp, dq);
1295167543Skib			dq = NODQUOT;
1296167543Skib			error = EIO;
1297167543Skib		}
1298167543Skib		*dqp = dq;
1299167543Skib		vfslocked = VFS_LOCK_GIANT(dqvp->v_mount);
1300167543Skib		if (dqvplocked)
1301167543Skib			vput(dqvp);
1302167543Skib		else
1303167543Skib			vrele(dqvp);
1304167543Skib		VFS_UNLOCK_GIANT(vfslocked);
1305167543Skib		return (error);
1306167543Skib	}
1307167543Skib
1308167543Skib	/*
1309167543Skib	 * Quota vnode lock is before DQ_LOCK. Acquire dqvp lock there
1310167543Skib	 * since new dq will appear on the hash chain DQ_LOCKed.
1311167543Skib	 */
1312167543Skib	if (vp != dqvp) {
1313167543Skib		DQH_UNLOCK();
1314175202Sattilio		vn_lock(dqvp, LK_SHARED | LK_RETRY);
1315167543Skib		dqvplocked = 1;
1316167543Skib		DQH_LOCK();
13171541Srgrimes		/*
1318167543Skib		 * Recheck the cache after sleep for quota vnode lock.
13191541Srgrimes		 */
1320167543Skib		dq = dqhashfind(dqh, id, dqvp);
1321167543Skib		if (dq != NULL) {
1322167543Skib			DQH_UNLOCK();
1323167543Skib			goto hfound;
1324167543Skib		}
13251541Srgrimes	}
1326167543Skib
13271541Srgrimes	/*
1328167543Skib	 * Not in cache, allocate a new one or take it from the
1329167543Skib	 * free list.
13301541Srgrimes	 */
133171999Sphk	if (TAILQ_FIRST(&dqfreelist) == NODQUOT &&
133222521Sdyson	    numdquot < MAXQUOTAS * desiredvnodes)
13331541Srgrimes		desireddquot += DQUOTINC;
13341541Srgrimes	if (numdquot < desireddquot) {
1335167543Skib		numdquot++;
1336167543Skib		DQH_UNLOCK();
1337207736Smckusick		dq1 = malloc(sizeof *dq1, M_DQUOT, M_WAITOK | M_ZERO);
1338167543Skib		mtx_init(&dq1->dq_lock, "dqlock", NULL, MTX_DEF);
1339167543Skib		DQH_LOCK();
1340167543Skib		/*
1341167543Skib		 * Recheck the cache after sleep for memory.
1342167543Skib		 */
1343167543Skib		dq = dqhashfind(dqh, id, dqvp);
1344167543Skib		if (dq != NULL) {
1345167543Skib			numdquot--;
1346167543Skib			DQH_UNLOCK();
1347167543Skib			mtx_destroy(&dq1->dq_lock);
1348167543Skib			free(dq1, M_DQUOT);
1349167543Skib			goto hfound;
1350167543Skib		}
1351167543Skib		dq = dq1;
13521541Srgrimes	} else {
135371999Sphk		if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) {
1354167543Skib			DQH_UNLOCK();
13551541Srgrimes			tablefull("dquot");
13561541Srgrimes			*dqp = NODQUOT;
1357167543Skib			vfslocked = VFS_LOCK_GIANT(dqvp->v_mount);
1358167543Skib			if (dqvplocked)
1359167543Skib				vput(dqvp);
1360167543Skib			else
1361167543Skib				vrele(dqvp);
1362167543Skib			VFS_UNLOCK_GIANT(vfslocked);
13631541Srgrimes			return (EUSERS);
13641541Srgrimes		}
13651541Srgrimes		if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
1366185739Skib			panic("dqget: free dquot isn't %p", dq);
136722521Sdyson		TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
136889213Sphk		if (dq->dq_ump != NULL)
136989213Sphk			LIST_REMOVE(dq, dq_hash);
13701541Srgrimes	}
1371167543Skib
13721541Srgrimes	/*
1373167543Skib	 * Dq is put into hash already locked to prevent parallel
1374167543Skib	 * usage while it is being read from file.
13751541Srgrimes	 */
13761541Srgrimes	dq->dq_flags = DQ_LOCK;
13771541Srgrimes	dq->dq_id = id;
1378167543Skib	dq->dq_type = type;
13791541Srgrimes	dq->dq_ump = ump;
1380167543Skib	LIST_INSERT_HEAD(dqh, dq, dq_hash);
1381167543Skib	DQREF(dq);
1382167543Skib	DQH_UNLOCK();
1383167543Skib
1384207736Smckusick	/*
1385207736Smckusick	 * Read the requested quota record from the quota file, performing
1386207736Smckusick	 * any necessary conversions.
1387207736Smckusick	 */
1388207736Smckusick	if (ump->um_qflags[type] & QTF_64BIT) {
1389207736Smckusick		recsize = sizeof(struct dqblk64);
1390207736Smckusick		base = sizeof(struct dqhdr64);
1391207736Smckusick	} else {
1392207736Smckusick		recsize = sizeof(struct dqblk32);
1393207736Smckusick		base = 0;
1394207736Smckusick	}
13951541Srgrimes	auio.uio_iov = &aiov;
13961541Srgrimes	auio.uio_iovcnt = 1;
1397207736Smckusick	aiov.iov_base = buf;
1398207736Smckusick	aiov.iov_len = recsize;
1399207736Smckusick	auio.uio_resid = recsize;
1400207736Smckusick	auio.uio_offset = base + id * recsize;
14011541Srgrimes	auio.uio_segflg = UIO_SYSSPACE;
14021541Srgrimes	auio.uio_rw = UIO_READ;
140383366Sjulian	auio.uio_td = (struct thread *)0;
1404167543Skib
1405167543Skib	vfslocked = VFS_LOCK_GIANT(dqvp->v_mount);
14061541Srgrimes	error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
1407207736Smckusick	if (auio.uio_resid == recsize && error == 0) {
1408207736Smckusick		bzero(&dq->dq_dqb, sizeof(dq->dq_dqb));
1409207736Smckusick	} else {
1410207736Smckusick		if (ump->um_qflags[type] & QTF_64BIT)
1411207736Smckusick			dqb64_dq((struct dqblk64 *)buf, dq);
1412207736Smckusick		else
1413207736Smckusick			dqb32_dq((struct dqblk32 *)buf, dq);
1414207736Smckusick	}
1415167543Skib	if (dqvplocked)
1416167543Skib		vput(dqvp);
1417167543Skib	else
1418167543Skib		vrele(dqvp);
1419167543Skib	VFS_UNLOCK_GIANT(vfslocked);
14201541Srgrimes	/*
14211541Srgrimes	 * I/O error in reading quota file, release
14221541Srgrimes	 * quota structure and reflect problem to caller.
14231541Srgrimes	 */
14241541Srgrimes	if (error) {
1425167543Skib		DQH_LOCK();
1426167543Skib		dq->dq_ump = NULL;
142722521Sdyson		LIST_REMOVE(dq, dq_hash);
1428167543Skib		DQH_UNLOCK();
1429167543Skib		DQI_LOCK(dq);
1430167543Skib		if (dq->dq_flags & DQ_WANT)
1431167543Skib			wakeup(dq);
1432167543Skib		dq->dq_flags = 0;
1433167543Skib		DQI_UNLOCK(dq);
14341541Srgrimes		dqrele(vp, dq);
14351541Srgrimes		*dqp = NODQUOT;
14361541Srgrimes		return (error);
14371541Srgrimes	}
1438167543Skib	DQI_LOCK(dq);
14391541Srgrimes	/*
14401541Srgrimes	 * Check for no limit to enforce.
14411541Srgrimes	 * Initialize time values if necessary.
14421541Srgrimes	 */
14431541Srgrimes	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
14441541Srgrimes	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
14451541Srgrimes		dq->dq_flags |= DQ_FAKE;
14461541Srgrimes	if (dq->dq_id != 0) {
1447166487Smpp		if (dq->dq_btime == 0) {
144834961Sphk			dq->dq_btime = time_second + ump->um_btime[type];
1449166487Smpp			if (dq->dq_bsoftlimit &&
1450166487Smpp			    dq->dq_curblocks >= dq->dq_bsoftlimit)
1451166487Smpp				dq->dq_flags |= DQ_MOD;
1452166487Smpp		}
1453166487Smpp		if (dq->dq_itime == 0) {
145434961Sphk			dq->dq_itime = time_second + ump->um_itime[type];
1455166487Smpp			if (dq->dq_isoftlimit &&
1456166487Smpp			    dq->dq_curinodes >= dq->dq_isoftlimit)
1457166487Smpp				dq->dq_flags |= DQ_MOD;
1458166487Smpp		}
14591541Srgrimes	}
1460167543Skib	DQI_WAKEUP(dq);
1461167543Skib	DQI_UNLOCK(dq);
14621541Srgrimes	*dqp = dq;
14631541Srgrimes	return (0);
14641541Srgrimes}
14651541Srgrimes
146617040Swollman#ifdef DIAGNOSTIC
14671541Srgrimes/*
14681541Srgrimes * Obtain a reference to a dquot.
14691541Srgrimes */
147012971Sphkstatic void
1471181327Sdesdqref(struct dquot *dq)
14721541Srgrimes{
14731541Srgrimes
14741541Srgrimes	dq->dq_cnt++;
14751541Srgrimes}
147617040Swollman#endif
14771541Srgrimes
14781541Srgrimes/*
14791541Srgrimes * Release a reference to a dquot.
14801541Srgrimes */
14811541Srgrimesvoid
1482181327Sdesdqrele(struct vnode *vp, struct dquot *dq)
14831541Srgrimes{
14841541Srgrimes
14851541Srgrimes	if (dq == NODQUOT)
14861541Srgrimes		return;
1487167543Skib	DQH_LOCK();
1488232285Skib	KASSERT(dq->dq_cnt > 0, ("Lost dq %p reference 1", dq));
14891541Srgrimes	if (dq->dq_cnt > 1) {
14901541Srgrimes		dq->dq_cnt--;
1491167543Skib		DQH_UNLOCK();
14921541Srgrimes		return;
14931541Srgrimes	}
1494167543Skib	DQH_UNLOCK();
1495185761Skibsync:
1496167543Skib	(void) dqsync(vp, dq);
1497167543Skib
1498167543Skib	DQH_LOCK();
1499232285Skib	KASSERT(dq->dq_cnt > 0, ("Lost dq %p reference 2", dq));
15001541Srgrimes	if (--dq->dq_cnt > 0)
1501167543Skib	{
1502167543Skib		DQH_UNLOCK();
15031541Srgrimes		return;
1504167543Skib	}
1505185761Skib
1506185761Skib	/*
1507185761Skib	 * The dq may become dirty after it is synced but before it is
1508185761Skib	 * put to the free list. Checking the DQ_MOD there without
1509185761Skib	 * locking dq should be safe since no other references to the
1510185761Skib	 * dq exist.
1511185761Skib	 */
1512185761Skib	if ((dq->dq_flags & DQ_MOD) != 0) {
1513185761Skib		dq->dq_cnt++;
1514185761Skib		DQH_UNLOCK();
1515185761Skib		goto sync;
1516185761Skib	}
151722521Sdyson	TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
1518167543Skib	DQH_UNLOCK();
15191541Srgrimes}
15201541Srgrimes
15211541Srgrimes/*
15221541Srgrimes * Update the disk quota in the quota file.
15231541Srgrimes */
152412971Sphkstatic int
1525181327Sdesdqsync(struct vnode *vp, struct dquot *dq)
15261541Srgrimes{
1527207736Smckusick	uint8_t buf[sizeof(struct dqblk64)];
1528207736Smckusick	off_t base, recsize;
15291541Srgrimes	struct vnode *dqvp;
15301541Srgrimes	struct iovec aiov;
15311541Srgrimes	struct uio auio;
1532167543Skib	int vfslocked, error;
1533156451Stegge	struct mount *mp;
1534167543Skib	struct ufsmount *ump;
15351541Srgrimes
1536167543Skib#ifdef DEBUG_VFS_LOCKS
1537167543Skib	if (vp != NULL)
1538167543Skib		ASSERT_VOP_ELOCKED(vp, "dqsync");
1539167543Skib#endif
1540167543Skib
1541156451Stegge	mp = NULL;
1542167543Skib	error = 0;
15431541Srgrimes	if (dq == NODQUOT)
15441541Srgrimes		panic("dqsync: dquot");
1545167543Skib	if ((ump = dq->dq_ump) == NULL)
15461541Srgrimes		return (0);
1547167543Skib	UFS_LOCK(ump);
1548167543Skib	if ((dqvp = ump->um_quotas[dq->dq_type]) == NULLVP)
15491541Srgrimes		panic("dqsync: file");
1550167543Skib	vref(dqvp);
1551167543Skib	UFS_UNLOCK(ump);
1552167543Skib
1553167543Skib	vfslocked = VFS_LOCK_GIANT(dqvp->v_mount);
1554167543Skib	DQI_LOCK(dq);
1555167543Skib	if ((dq->dq_flags & DQ_MOD) == 0) {
1556167543Skib		DQI_UNLOCK(dq);
1557167543Skib		vrele(dqvp);
1558167543Skib		VFS_UNLOCK_GIANT(vfslocked);
1559167543Skib		return (0);
1560167543Skib	}
1561167543Skib	DQI_UNLOCK(dq);
1562167543Skib
1563156451Stegge	(void) vn_start_secondary_write(dqvp, &mp, V_WAIT);
15641541Srgrimes	if (vp != dqvp)
1565175202Sattilio		vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
1566167543Skib
1567167543Skib	VFS_UNLOCK_GIANT(vfslocked);
1568167543Skib	DQI_LOCK(dq);
1569167543Skib	DQI_WAIT(dq, PINOD+2, "dqsync");
1570167543Skib	if ((dq->dq_flags & DQ_MOD) == 0)
1571167543Skib		goto out;
15721541Srgrimes	dq->dq_flags |= DQ_LOCK;
1573167543Skib	DQI_UNLOCK(dq);
1574167543Skib
1575207736Smckusick	/*
1576207736Smckusick	 * Write the quota record to the quota file, performing any
1577207736Smckusick	 * necessary conversions.  See dqget() for additional details.
1578207736Smckusick	 */
1579207736Smckusick	if (ump->um_qflags[dq->dq_type] & QTF_64BIT) {
1580207736Smckusick		dq_dqb64(dq, (struct dqblk64 *)buf);
1581207736Smckusick		recsize = sizeof(struct dqblk64);
1582207736Smckusick		base = sizeof(struct dqhdr64);
1583207736Smckusick	} else {
1584207736Smckusick		dq_dqb32(dq, (struct dqblk32 *)buf);
1585207736Smckusick		recsize = sizeof(struct dqblk32);
1586207736Smckusick		base = 0;
1587207736Smckusick	}
1588207736Smckusick
15891541Srgrimes	auio.uio_iov = &aiov;
15901541Srgrimes	auio.uio_iovcnt = 1;
1591207736Smckusick	aiov.iov_base = buf;
1592207736Smckusick	aiov.iov_len = recsize;
1593207736Smckusick	auio.uio_resid = recsize;
1594207736Smckusick	auio.uio_offset = base + dq->dq_id * recsize;
15951541Srgrimes	auio.uio_segflg = UIO_SYSSPACE;
15961541Srgrimes	auio.uio_rw = UIO_WRITE;
159783366Sjulian	auio.uio_td = (struct thread *)0;
1598167543Skib	vfslocked = VFS_LOCK_GIANT(dqvp->v_mount);
15991541Srgrimes	error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
1600167543Skib	VFS_UNLOCK_GIANT(vfslocked);
16011541Srgrimes	if (auio.uio_resid && error == 0)
16021541Srgrimes		error = EIO;
1603167543Skib
1604167543Skib	DQI_LOCK(dq);
1605167543Skib	DQI_WAKEUP(dq);
1606167543Skib	dq->dq_flags &= ~DQ_MOD;
1607207736Smckusickout:
1608207736Smckusick	DQI_UNLOCK(dq);
1609167543Skib	vfslocked = VFS_LOCK_GIANT(dqvp->v_mount);
16101541Srgrimes	if (vp != dqvp)
1611167543Skib		vput(dqvp);
1612167543Skib	else
1613167543Skib		vrele(dqvp);
1614156451Stegge	vn_finished_secondary_write(mp);
1615167543Skib	VFS_UNLOCK_GIANT(vfslocked);
16161541Srgrimes	return (error);
16171541Srgrimes}
16181541Srgrimes
16191541Srgrimes/*
16201541Srgrimes * Flush all entries from the cache for a particular vnode.
16211541Srgrimes */
1622248233Skibstatic int
1623181327Sdesdqflush(struct vnode *vp)
16241541Srgrimes{
162596506Sphk	struct dquot *dq, *nextdq;
162622521Sdyson	struct dqhash *dqh;
1627248233Skib	int error;
16281541Srgrimes
16291541Srgrimes	/*
16301541Srgrimes	 * Move all dquot's that used to refer to this quota
16311541Srgrimes	 * file off their hash chains (they will eventually
16321541Srgrimes	 * fall off the head of the free list and be re-used).
16331541Srgrimes	 */
1634248233Skib	error = 0;
1635167543Skib	DQH_LOCK();
163622521Sdyson	for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
163771999Sphk		for (dq = LIST_FIRST(dqh); dq; dq = nextdq) {
163871999Sphk			nextdq = LIST_NEXT(dq, dq_hash);
16391541Srgrimes			if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
16401541Srgrimes				continue;
16411541Srgrimes			if (dq->dq_cnt)
1642248233Skib				error = EBUSY;
1643248233Skib			else {
1644248233Skib				LIST_REMOVE(dq, dq_hash);
1645248233Skib				dq->dq_ump = NULL;
1646248233Skib			}
16471541Srgrimes		}
16481541Srgrimes	}
1649167543Skib	DQH_UNLOCK();
1650248233Skib	return (error);
16511541Srgrimes}
1652207736Smckusick
1653207736Smckusick/*
1654222955Sjeff * The following three functions are provided for the adjustment of
1655222955Sjeff * quotas by the soft updates code.
1656222955Sjeff */
1657222955Sjeff#ifdef SOFTUPDATES
1658222955Sjeff/*
1659222955Sjeff * Acquire a reference to the quota structures associated with a vnode.
1660222955Sjeff * Return count of number of quota structures found.
1661222955Sjeff */
1662222955Sjeffint
1663222955Sjeffquotaref(vp, qrp)
1664222955Sjeff	struct vnode *vp;
1665222955Sjeff	struct dquot **qrp;
1666222955Sjeff{
1667222955Sjeff	struct inode *ip;
1668222955Sjeff	struct dquot *dq;
1669222955Sjeff	int i, found;
1670222955Sjeff
1671222955Sjeff	for (i = 0; i < MAXQUOTAS; i++)
1672222955Sjeff		qrp[i] = NODQUOT;
1673222955Sjeff	/*
1674222955Sjeff	 * Disk quotas must be turned off for system files.  Currently
1675222955Sjeff	 * snapshot and quota files.
1676222955Sjeff	 */
1677222955Sjeff	if ((vp->v_vflag & VV_SYSTEM) != 0)
1678222955Sjeff		return (0);
1679222955Sjeff	/*
1680222955Sjeff	 * Iterate through and copy active quotas.
1681222955Sjeff	 */
1682222955Sjeff	found = 0;
1683222955Sjeff	ip = VTOI(vp);
1684232285Skib	mtx_lock(&dqhlock);
1685222955Sjeff	for (i = 0; i < MAXQUOTAS; i++) {
1686222955Sjeff		if ((dq = ip->i_dquot[i]) == NODQUOT)
1687222955Sjeff			continue;
1688222955Sjeff		DQREF(dq);
1689222955Sjeff		qrp[i] = dq;
1690222955Sjeff		found++;
1691222955Sjeff	}
1692232285Skib	mtx_unlock(&dqhlock);
1693222955Sjeff	return (found);
1694222955Sjeff}
1695222955Sjeff
1696222955Sjeff/*
1697222955Sjeff * Release a set of quota structures obtained from a vnode.
1698222955Sjeff */
1699222955Sjeffvoid
1700222955Sjeffquotarele(qrp)
1701222955Sjeff	struct dquot **qrp;
1702222955Sjeff{
1703222955Sjeff	struct dquot *dq;
1704222955Sjeff	int i;
1705222955Sjeff
1706222955Sjeff	for (i = 0; i < MAXQUOTAS; i++) {
1707222955Sjeff		if ((dq = qrp[i]) == NODQUOT)
1708222955Sjeff			continue;
1709222955Sjeff		dqrele(NULL, dq);
1710222955Sjeff	}
1711222955Sjeff}
1712222955Sjeff
1713222955Sjeff/*
1714222955Sjeff * Adjust the number of blocks associated with a quota.
1715222955Sjeff * Positive numbers when adding blocks; negative numbers when freeing blocks.
1716222955Sjeff */
1717222955Sjeffvoid
1718222955Sjeffquotaadj(qrp, ump, blkcount)
1719222955Sjeff	struct dquot **qrp;
1720222955Sjeff	struct ufsmount *ump;
1721222955Sjeff	int64_t blkcount;
1722222955Sjeff{
1723222955Sjeff	struct dquot *dq;
1724222955Sjeff	ufs2_daddr_t ncurblocks;
1725222955Sjeff	int i;
1726222955Sjeff
1727222955Sjeff	if (blkcount == 0)
1728222955Sjeff		return;
1729222955Sjeff	for (i = 0; i < MAXQUOTAS; i++) {
1730222955Sjeff		if ((dq = qrp[i]) == NODQUOT)
1731222955Sjeff			continue;
1732222955Sjeff		DQI_LOCK(dq);
1733222955Sjeff		DQI_WAIT(dq, PINOD+1, "adjqta");
1734222955Sjeff		ncurblocks = dq->dq_curblocks + blkcount;
1735222955Sjeff		if (ncurblocks >= 0)
1736222955Sjeff			dq->dq_curblocks = ncurblocks;
1737222955Sjeff		else
1738222955Sjeff			dq->dq_curblocks = 0;
1739222955Sjeff		if (blkcount < 0)
1740222955Sjeff			dq->dq_flags &= ~DQ_BLKS;
1741222955Sjeff		else if (dq->dq_curblocks + blkcount >= dq->dq_bsoftlimit &&
1742222955Sjeff			 dq->dq_curblocks < dq->dq_bsoftlimit)
1743222955Sjeff			dq->dq_btime = time_second + ump->um_btime[i];
1744222955Sjeff		dq->dq_flags |= DQ_MOD;
1745222955Sjeff		DQI_UNLOCK(dq);
1746222955Sjeff	}
1747222955Sjeff}
1748222955Sjeff#endif /* SOFTUPDATES */
1749222955Sjeff
1750222955Sjeff/*
1751207736Smckusick * 32-bit / 64-bit conversion functions.
1752207736Smckusick *
1753207736Smckusick * 32-bit quota records are stored in native byte order.  Attention must
1754207736Smckusick * be paid to overflow issues.
1755207736Smckusick *
1756207736Smckusick * 64-bit quota records are stored in network byte order.
1757207736Smckusick */
1758207736Smckusick
1759207736Smckusick#define CLIP32(u64) (u64 > UINT32_MAX ? UINT32_MAX : (uint32_t)u64)
1760207736Smckusick
1761207736Smckusick/*
1762207736Smckusick * Convert 32-bit host-order structure to dquot.
1763207736Smckusick */
1764207736Smckusickstatic void
1765207736Smckusickdqb32_dq(const struct dqblk32 *dqb32, struct dquot *dq)
1766207736Smckusick{
1767207736Smckusick
1768207736Smckusick	dq->dq_bhardlimit = dqb32->dqb_bhardlimit;
1769207736Smckusick	dq->dq_bsoftlimit = dqb32->dqb_bsoftlimit;
1770207736Smckusick	dq->dq_curblocks = dqb32->dqb_curblocks;
1771207736Smckusick	dq->dq_ihardlimit = dqb32->dqb_ihardlimit;
1772207736Smckusick	dq->dq_isoftlimit = dqb32->dqb_isoftlimit;
1773207736Smckusick	dq->dq_curinodes = dqb32->dqb_curinodes;
1774207736Smckusick	dq->dq_btime = dqb32->dqb_btime;
1775207736Smckusick	dq->dq_itime = dqb32->dqb_itime;
1776207736Smckusick}
1777207736Smckusick
1778207736Smckusick/*
1779207736Smckusick * Convert 64-bit network-order structure to dquot.
1780207736Smckusick */
1781207736Smckusickstatic void
1782207736Smckusickdqb64_dq(const struct dqblk64 *dqb64, struct dquot *dq)
1783207736Smckusick{
1784207736Smckusick
1785207736Smckusick	dq->dq_bhardlimit = be64toh(dqb64->dqb_bhardlimit);
1786207736Smckusick	dq->dq_bsoftlimit = be64toh(dqb64->dqb_bsoftlimit);
1787207736Smckusick	dq->dq_curblocks = be64toh(dqb64->dqb_curblocks);
1788207736Smckusick	dq->dq_ihardlimit = be64toh(dqb64->dqb_ihardlimit);
1789207736Smckusick	dq->dq_isoftlimit = be64toh(dqb64->dqb_isoftlimit);
1790207736Smckusick	dq->dq_curinodes = be64toh(dqb64->dqb_curinodes);
1791207736Smckusick	dq->dq_btime = be64toh(dqb64->dqb_btime);
1792207736Smckusick	dq->dq_itime = be64toh(dqb64->dqb_itime);
1793207736Smckusick}
1794207736Smckusick
1795207736Smckusick/*
1796207736Smckusick * Convert dquot to 32-bit host-order structure.
1797207736Smckusick */
1798207736Smckusickstatic void
1799207736Smckusickdq_dqb32(const struct dquot *dq, struct dqblk32 *dqb32)
1800207736Smckusick{
1801207736Smckusick
1802207736Smckusick	dqb32->dqb_bhardlimit = CLIP32(dq->dq_bhardlimit);
1803207736Smckusick	dqb32->dqb_bsoftlimit = CLIP32(dq->dq_bsoftlimit);
1804207736Smckusick	dqb32->dqb_curblocks = CLIP32(dq->dq_curblocks);
1805207736Smckusick	dqb32->dqb_ihardlimit = CLIP32(dq->dq_ihardlimit);
1806207736Smckusick	dqb32->dqb_isoftlimit = CLIP32(dq->dq_isoftlimit);
1807207736Smckusick	dqb32->dqb_curinodes = CLIP32(dq->dq_curinodes);
1808207736Smckusick	dqb32->dqb_btime = CLIP32(dq->dq_btime);
1809207736Smckusick	dqb32->dqb_itime = CLIP32(dq->dq_itime);
1810207736Smckusick}
1811207736Smckusick
1812207736Smckusick/*
1813207736Smckusick * Convert dquot to 64-bit network-order structure.
1814207736Smckusick */
1815207736Smckusickstatic void
1816207736Smckusickdq_dqb64(const struct dquot *dq, struct dqblk64 *dqb64)
1817207736Smckusick{
1818207736Smckusick
1819207736Smckusick	dqb64->dqb_bhardlimit = htobe64(dq->dq_bhardlimit);
1820207736Smckusick	dqb64->dqb_bsoftlimit = htobe64(dq->dq_bsoftlimit);
1821207736Smckusick	dqb64->dqb_curblocks = htobe64(dq->dq_curblocks);
1822207736Smckusick	dqb64->dqb_ihardlimit = htobe64(dq->dq_ihardlimit);
1823207736Smckusick	dqb64->dqb_isoftlimit = htobe64(dq->dq_isoftlimit);
1824207736Smckusick	dqb64->dqb_curinodes = htobe64(dq->dq_curinodes);
1825207736Smckusick	dqb64->dqb_btime = htobe64(dq->dq_btime);
1826207736Smckusick	dqb64->dqb_itime = htobe64(dq->dq_itime);
1827207736Smckusick}
1828207736Smckusick
1829207736Smckusick/*
1830207736Smckusick * Convert 64-bit host-order structure to 32-bit host-order structure.
1831207736Smckusick */
1832207736Smckusickstatic void
1833207736Smckusickdqb64_dqb32(const struct dqblk64 *dqb64, struct dqblk32 *dqb32)
1834207736Smckusick{
1835207736Smckusick
1836207736Smckusick	dqb32->dqb_bhardlimit = CLIP32(dqb64->dqb_bhardlimit);
1837207736Smckusick	dqb32->dqb_bsoftlimit = CLIP32(dqb64->dqb_bsoftlimit);
1838207736Smckusick	dqb32->dqb_curblocks = CLIP32(dqb64->dqb_curblocks);
1839207736Smckusick	dqb32->dqb_ihardlimit = CLIP32(dqb64->dqb_ihardlimit);
1840207736Smckusick	dqb32->dqb_isoftlimit = CLIP32(dqb64->dqb_isoftlimit);
1841207736Smckusick	dqb32->dqb_curinodes = CLIP32(dqb64->dqb_curinodes);
1842207736Smckusick	dqb32->dqb_btime = CLIP32(dqb64->dqb_btime);
1843207736Smckusick	dqb32->dqb_itime = CLIP32(dqb64->dqb_itime);
1844207736Smckusick}
1845207736Smckusick
1846207736Smckusick/*
1847207736Smckusick * Convert 32-bit host-order structure to 64-bit host-order structure.
1848207736Smckusick */
1849207736Smckusickstatic void
1850207736Smckusickdqb32_dqb64(const struct dqblk32 *dqb32, struct dqblk64 *dqb64)
1851207736Smckusick{
1852207736Smckusick
1853207736Smckusick	dqb64->dqb_bhardlimit = dqb32->dqb_bhardlimit;
1854207736Smckusick	dqb64->dqb_bsoftlimit = dqb32->dqb_bsoftlimit;
1855207736Smckusick	dqb64->dqb_curblocks = dqb32->dqb_curblocks;
1856207736Smckusick	dqb64->dqb_ihardlimit = dqb32->dqb_ihardlimit;
1857207736Smckusick	dqb64->dqb_isoftlimit = dqb32->dqb_isoftlimit;
1858207736Smckusick	dqb64->dqb_curinodes = dqb32->dqb_curinodes;
1859207736Smckusick	dqb64->dqb_btime = dqb32->dqb_btime;
1860207736Smckusick	dqb64->dqb_itime = dqb32->dqb_itime;
1861207736Smckusick}
1862