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