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