Home | History | Annotate | Line # | Download | only in ufs
ufs_quota.c revision 1.43
      1 /*	$NetBSD: ufs_quota.c,v 1.43 2007/01/04 16:55:30 elad 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.43 2007/01/04 16:55:30 elad 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, caddr_t 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, caddr_t 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((caddr_t)&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, caddr_t 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, (caddr_t)&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, caddr_t 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, (caddr_t)&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_DEFINE(M_DQUOT, "UFS quota", "UFS quota entries");
    651 
    652 /*
    653  * Initialize the quota system.
    654  */
    655 void
    656 dqinit(void)
    657 {
    658 	dqhashtbl =
    659 	    hashinit(desiredvnodes, HASH_LIST, M_DQUOT, M_WAITOK, &dqhash);
    660 	TAILQ_INIT(&dqfreelist);
    661 }
    662 
    663 void
    664 dqreinit(void)
    665 {
    666 	struct dquot *dq;
    667 	struct dqhashhead *oldhash, *hash;
    668 	struct vnode *dqvp;
    669 	u_long oldmask, mask, hashval;
    670 	int i;
    671 
    672 	hash = hashinit(desiredvnodes, HASH_LIST, M_DQUOT, M_WAITOK, &mask);
    673 	oldhash = dqhashtbl;
    674 	oldmask = dqhash;
    675 	dqhashtbl = hash;
    676 	dqhash = mask;
    677 	for (i = 0; i <= oldmask; i++) {
    678 		while ((dq = LIST_FIRST(&oldhash[i])) != NULL) {
    679 			dqvp = dq->dq_ump->um_quotas[dq->dq_type];
    680 			LIST_REMOVE(dq, dq_hash);
    681 			hashval = DQHASH(dqvp, dq->dq_id);
    682 			LIST_INSERT_HEAD(&dqhashtbl[hashval], dq, dq_hash);
    683 		}
    684 	}
    685 	hashdone(oldhash, M_DQUOT);
    686 }
    687 
    688 /*
    689  * Free resources held by quota system.
    690  */
    691 void
    692 dqdone(void)
    693 {
    694 	hashdone(dqhashtbl, M_DQUOT);
    695 }
    696 
    697 /*
    698  * Obtain a dquot structure for the specified identifier and quota file
    699  * reading the information from the file if necessary.
    700  */
    701 int
    702 dqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type,
    703     struct dquot **dqp)
    704 {
    705 	struct dquot *dq;
    706 	struct dqhashhead *dqh;
    707 	struct vnode *dqvp;
    708 	struct iovec aiov;
    709 	struct uio auio;
    710 	int error;
    711 
    712 	dqvp = ump->um_quotas[type];
    713 	if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
    714 		*dqp = NODQUOT;
    715 		return (EINVAL);
    716 	}
    717 	/*
    718 	 * Check the cache first.
    719 	 */
    720 	dqh = &dqhashtbl[DQHASH(dqvp, id)];
    721 	LIST_FOREACH(dq, dqh, dq_hash) {
    722 		if (dq->dq_id != id ||
    723 		    dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
    724 			continue;
    725 		/*
    726 		 * Cache hit with no references.  Take
    727 		 * the structure off the free list.
    728 		 */
    729 		if (dq->dq_cnt == 0)
    730 			TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
    731 		dqref(dq);
    732 		*dqp = dq;
    733 		return (0);
    734 	}
    735 	/*
    736 	 * Not in cache, allocate a new one.
    737 	 */
    738 	if (dqfreelist.tqh_first == NODQUOT &&
    739 	    numdquot < MAXQUOTAS * desiredvnodes)
    740 		desireddquot += DQUOTINC;
    741 	if (numdquot < desireddquot) {
    742 		dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK);
    743 		memset((char *)dq, 0, sizeof *dq);
    744 		numdquot++;
    745 	} else {
    746 		if ((dq = dqfreelist.tqh_first) == NULL) {
    747 			tablefull("dquot",
    748 			    "increase kern.maxvnodes or NVNODE");
    749 			*dqp = NODQUOT;
    750 			return (EUSERS);
    751 		}
    752 		if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
    753 			panic("free dquot isn't");
    754 		TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
    755 		LIST_REMOVE(dq, dq_hash);
    756 	}
    757 	/*
    758 	 * Initialize the contents of the dquot structure.
    759 	 */
    760 	if (vp != dqvp)
    761 		vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
    762 	LIST_INSERT_HEAD(dqh, dq, dq_hash);
    763 	dqref(dq);
    764 	dq->dq_flags = DQ_LOCK;
    765 	dq->dq_id = id;
    766 	dq->dq_ump = ump;
    767 	dq->dq_type = type;
    768 	auio.uio_iov = &aiov;
    769 	auio.uio_iovcnt = 1;
    770 	aiov.iov_base = (caddr_t)&dq->dq_dqb;
    771 	aiov.iov_len = sizeof (struct dqblk);
    772 	auio.uio_resid = sizeof (struct dqblk);
    773 	auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
    774 	auio.uio_rw = UIO_READ;
    775 	UIO_SETUP_SYSSPACE(&auio);
    776 	error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
    777 	if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
    778 		memset((caddr_t)&dq->dq_dqb, 0, sizeof(struct dqblk));
    779 	if (vp != dqvp)
    780 		VOP_UNLOCK(dqvp, 0);
    781 	if (dq->dq_flags & DQ_WANT)
    782 		wakeup((caddr_t)dq);
    783 	dq->dq_flags = 0;
    784 	/*
    785 	 * I/O error in reading quota file, release
    786 	 * quota structure and reflect problem to caller.
    787 	 */
    788 	if (error) {
    789 		LIST_REMOVE(dq, dq_hash);
    790 		dqrele(vp, dq);
    791 		*dqp = NODQUOT;
    792 		return (error);
    793 	}
    794 	/*
    795 	 * Check for no limit to enforce.
    796 	 * Initialize time values if necessary.
    797 	 */
    798 	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
    799 	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
    800 		dq->dq_flags |= DQ_FAKE;
    801 	if (dq->dq_id != 0) {
    802 		if (dq->dq_btime == 0)
    803 			dq->dq_btime = time_second + ump->um_btime[type];
    804 		if (dq->dq_itime == 0)
    805 			dq->dq_itime = time_second + ump->um_itime[type];
    806 	}
    807 	*dqp = dq;
    808 	return (0);
    809 }
    810 
    811 /*
    812  * Obtain a reference to a dquot.
    813  */
    814 void
    815 dqref(struct dquot *dq)
    816 {
    817 
    818 	dq->dq_cnt++;
    819 }
    820 
    821 /*
    822  * Release a reference to a dquot.
    823  */
    824 void
    825 dqrele(struct vnode *vp, struct dquot *dq)
    826 {
    827 
    828 	if (dq == NODQUOT)
    829 		return;
    830 	if (dq->dq_cnt > 1) {
    831 		dq->dq_cnt--;
    832 		return;
    833 	}
    834 	if (dq->dq_flags & DQ_MOD)
    835 		(void) dqsync(vp, dq);
    836 	if (--dq->dq_cnt > 0)
    837 		return;
    838 	TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
    839 }
    840 
    841 /*
    842  * Update the disk quota in the quota file.
    843  */
    844 int
    845 dqsync(struct vnode *vp, struct dquot *dq)
    846 {
    847 	struct vnode *dqvp;
    848 	struct mount *mp;
    849 	struct iovec aiov;
    850 	struct uio auio;
    851 	int error;
    852 
    853 	if (dq == NODQUOT)
    854 		panic("dqsync: dquot");
    855 	if ((dq->dq_flags & DQ_MOD) == 0)
    856 		return (0);
    857 	if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
    858 		panic("dqsync: file");
    859 	vn_start_write(dqvp, &mp, V_WAIT | V_LOWER);
    860 	if (vp != dqvp)
    861 		vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
    862 	while (dq->dq_flags & DQ_LOCK) {
    863 		dq->dq_flags |= DQ_WANT;
    864 		(void) tsleep(dq, PINOD+2, "dqsync", 0);
    865 		if ((dq->dq_flags & DQ_MOD) == 0) {
    866 			if (vp != dqvp)
    867 				VOP_UNLOCK(dqvp, 0);
    868 			vn_finished_write(mp, V_LOWER);
    869 			return (0);
    870 		}
    871 	}
    872 	dq->dq_flags |= DQ_LOCK;
    873 	auio.uio_iov = &aiov;
    874 	auio.uio_iovcnt = 1;
    875 	aiov.iov_base = (caddr_t)&dq->dq_dqb;
    876 	aiov.iov_len = sizeof (struct dqblk);
    877 	auio.uio_resid = sizeof (struct dqblk);
    878 	auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
    879 	auio.uio_rw = UIO_WRITE;
    880 	UIO_SETUP_SYSSPACE(&auio);
    881 	error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
    882 	if (auio.uio_resid && error == 0)
    883 		error = EIO;
    884 	if (dq->dq_flags & DQ_WANT)
    885 		wakeup((caddr_t)dq);
    886 	dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
    887 	if (vp != dqvp)
    888 		VOP_UNLOCK(dqvp, 0);
    889 	vn_finished_write(mp, V_LOWER);
    890 	return (error);
    891 }
    892 
    893 /*
    894  * Flush all entries from the cache for a particular vnode.
    895  */
    896 void
    897 dqflush(struct vnode *vp)
    898 {
    899 	struct dquot *dq, *nextdq;
    900 	struct dqhashhead *dqh;
    901 
    902 	/*
    903 	 * Move all dquot's that used to refer to this quota
    904 	 * file off their hash chains (they will eventually
    905 	 * fall off the head of the free list and be re-used).
    906 	 */
    907 	for (dqh = &dqhashtbl[dqhash]; dqh >= dqhashtbl; dqh--) {
    908 		for (dq = LIST_FIRST(dqh); dq; dq = nextdq) {
    909 			nextdq = LIST_NEXT(dq, dq_hash);
    910 			if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
    911 				continue;
    912 			if (dq->dq_cnt)
    913 				panic("dqflush: stray dquot");
    914 			LIST_REMOVE(dq, dq_hash);
    915 			dq->dq_ump = NULL;
    916 		}
    917 	}
    918 }
    919