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