xref: /onnv-gate/usr/src/uts/common/fs/ufs/quota_ufs.c (revision 4662:9c48274ded8b)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*4662Sfrankho  * Common Development and Distribution License (the "License").
6*4662Sfrankho  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*4662Sfrankho  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
270Sstevel@tonic-gate /*	  All Rights Reserved  	*/
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
310Sstevel@tonic-gate  * The Regents of the University of California
320Sstevel@tonic-gate  * All Rights Reserved
330Sstevel@tonic-gate  *
340Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
350Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
360Sstevel@tonic-gate  * contributors.
370Sstevel@tonic-gate  */
380Sstevel@tonic-gate 
390Sstevel@tonic-gate 
400Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
410Sstevel@tonic-gate 
420Sstevel@tonic-gate /*
430Sstevel@tonic-gate  * Routines used in checking limits on file system usage.
440Sstevel@tonic-gate  */
450Sstevel@tonic-gate 
460Sstevel@tonic-gate #include <sys/types.h>
470Sstevel@tonic-gate #include <sys/t_lock.h>
480Sstevel@tonic-gate #include <sys/param.h>
490Sstevel@tonic-gate #include <sys/time.h>
500Sstevel@tonic-gate #include <sys/systm.h>
510Sstevel@tonic-gate #include <sys/kmem.h>
520Sstevel@tonic-gate #include <sys/signal.h>
530Sstevel@tonic-gate #include <sys/cred.h>
540Sstevel@tonic-gate #include <sys/proc.h>
550Sstevel@tonic-gate #include <sys/user.h>
560Sstevel@tonic-gate #include <sys/proc.h>
570Sstevel@tonic-gate #include <sys/vfs.h>
580Sstevel@tonic-gate #include <sys/vnode.h>
590Sstevel@tonic-gate #include <sys/buf.h>
600Sstevel@tonic-gate #include <sys/uio.h>
610Sstevel@tonic-gate #include <sys/fs/ufs_inode.h>
620Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
630Sstevel@tonic-gate #include <sys/fs/ufs_quota.h>
640Sstevel@tonic-gate #include <sys/errno.h>
650Sstevel@tonic-gate #include <sys/cmn_err.h>
660Sstevel@tonic-gate #include <sys/session.h>
670Sstevel@tonic-gate #include <sys/debug.h>
680Sstevel@tonic-gate 
690Sstevel@tonic-gate /*
700Sstevel@tonic-gate  * Find the dquot structure that should
710Sstevel@tonic-gate  * be used in checking i/o on inode ip.
720Sstevel@tonic-gate  */
730Sstevel@tonic-gate struct dquot *
getinoquota(struct inode * ip)740Sstevel@tonic-gate getinoquota(struct inode *ip)
750Sstevel@tonic-gate {
760Sstevel@tonic-gate 	struct dquot *dqp, *xdqp;
770Sstevel@tonic-gate 	struct ufsvfs *ufsvfsp = ip->i_ufsvfs;
780Sstevel@tonic-gate 
790Sstevel@tonic-gate 	ASSERT(RW_LOCK_HELD(&ufsvfsp->vfs_dqrwlock));
800Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&ip->i_contents));
810Sstevel@tonic-gate 	/*
820Sstevel@tonic-gate 	 * Check for quotas enabled.
830Sstevel@tonic-gate 	 */
840Sstevel@tonic-gate 	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {
850Sstevel@tonic-gate 		return (NULL);
860Sstevel@tonic-gate 	}
870Sstevel@tonic-gate 
880Sstevel@tonic-gate 	/*
890Sstevel@tonic-gate 	 * Check for someone doing I/O to quota file.
900Sstevel@tonic-gate 	 */
910Sstevel@tonic-gate 	if (ip == ufsvfsp->vfs_qinod) {
920Sstevel@tonic-gate 		return (NULL);
930Sstevel@tonic-gate 	}
940Sstevel@tonic-gate 
950Sstevel@tonic-gate 	/*
960Sstevel@tonic-gate 	 * Check for a legal inode, e.g. not a shadow inode,
970Sstevel@tonic-gate 	 * not a extended attribute directory inode and a valid mode.
980Sstevel@tonic-gate 	 */
990Sstevel@tonic-gate 	ASSERT((ip->i_mode & IFMT) != IFSHAD);
1000Sstevel@tonic-gate 	ASSERT((ip->i_mode & IFMT) != IFATTRDIR);
1010Sstevel@tonic-gate 	ASSERT(ip->i_mode);
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate 	if (getdiskquota((uid_t)ip->i_uid, ufsvfsp, 0, &xdqp)) {
1040Sstevel@tonic-gate 		return (NULL);
1050Sstevel@tonic-gate 	}
1060Sstevel@tonic-gate 	dqp = xdqp;
1070Sstevel@tonic-gate 	mutex_enter(&dqp->dq_lock);
1080Sstevel@tonic-gate 	ASSERT(ip->i_uid == dqp->dq_uid);
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	if (dqp->dq_fhardlimit == 0 && dqp->dq_fsoftlimit == 0 &&
1110Sstevel@tonic-gate 	    dqp->dq_bhardlimit == 0 && dqp->dq_bsoftlimit == 0) {
1120Sstevel@tonic-gate 		dqput(dqp);
1130Sstevel@tonic-gate 		mutex_exit(&dqp->dq_lock);
1140Sstevel@tonic-gate 		dqp = NULL;
1150Sstevel@tonic-gate 	} else {
1160Sstevel@tonic-gate 		mutex_exit(&dqp->dq_lock);
1170Sstevel@tonic-gate 	}
1180Sstevel@tonic-gate 	return (dqp);
1190Sstevel@tonic-gate }
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate /*
1220Sstevel@tonic-gate  * Update disk usage, and take corrective action.
1230Sstevel@tonic-gate  */
1240Sstevel@tonic-gate int
chkdq(struct inode * ip,long change,int force,struct cred * cr,char ** uerrp,size_t * lenp)1250Sstevel@tonic-gate chkdq(struct inode *ip, long change, int force, struct cred *cr,
1260Sstevel@tonic-gate 	char **uerrp, size_t *lenp)
1270Sstevel@tonic-gate {
1280Sstevel@tonic-gate 	struct dquot *dqp;
1290Sstevel@tonic-gate 	uint64_t ncurblocks;
1300Sstevel@tonic-gate 	struct ufsvfs *ufsvfsp = ip->i_ufsvfs;
1310Sstevel@tonic-gate 	int error = 0;
1320Sstevel@tonic-gate 	long abs_change;
1330Sstevel@tonic-gate 	char *msg1 =
1340Sstevel@tonic-gate "!quota_ufs: over hard disk limit (pid %d, uid %d, inum %d, fs %s)\n";
1350Sstevel@tonic-gate 	char *msg2 =
1360Sstevel@tonic-gate "!quota_ufs: Warning: over disk limit (pid %d, uid %d, inum %d, fs %s)\n";
1370Sstevel@tonic-gate 	char *msg3 =
1380Sstevel@tonic-gate "!quota_ufs: over disk and time limit (pid %d, uid %d, inum %d, fs %s)\n";
1390Sstevel@tonic-gate 	char *msg4 =
1400Sstevel@tonic-gate "!quota_ufs: Warning: quota overflow (pid %d, uid %d, inum %d, fs %s)\n";
1410Sstevel@tonic-gate 	char *errmsg = NULL;
1420Sstevel@tonic-gate 	time_t now;
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate 	/*
1450Sstevel@tonic-gate 	 * Shadow inodes do not need to hold the vfs_dqrwlock lock.
1460Sstevel@tonic-gate 	 */
1470Sstevel@tonic-gate 	ASSERT((ip->i_mode & IFMT) == IFSHAD ||
148*4662Sfrankho 	    RW_LOCK_HELD(&ufsvfsp->vfs_dqrwlock));
1490Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&ip->i_contents));
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate 	if (change == 0)
1520Sstevel@tonic-gate 		return (0);
1530Sstevel@tonic-gate 	dqp = ip->i_dquot;
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate 	/*
1560Sstevel@tonic-gate 	 * Make sure the quota info record matches the owner.
1570Sstevel@tonic-gate 	 */
1580Sstevel@tonic-gate 	ASSERT(dqp == NULL || ip->i_uid == dqp->dq_uid);
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate #ifdef DEBUG
1610Sstevel@tonic-gate 	/*
1620Sstevel@tonic-gate 	 * Shadow inodes and extended attribute directories
1630Sstevel@tonic-gate 	 * should not have quota info records.
1640Sstevel@tonic-gate 	 */
1650Sstevel@tonic-gate 	if ((ip->i_mode & IFMT) == IFSHAD || (ip->i_mode & IFMT) == IFATTRDIR) {
1660Sstevel@tonic-gate 		ASSERT(dqp == NULL);
1670Sstevel@tonic-gate 	}
1680Sstevel@tonic-gate 	/*
1690Sstevel@tonic-gate 	 * Paranoia for verifying that quotas are okay.
1700Sstevel@tonic-gate 	 */
1710Sstevel@tonic-gate 	else {
1720Sstevel@tonic-gate 		struct dquot *expect_dq;
1730Sstevel@tonic-gate 		int mismatch_ok = 0;
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate 		/* Get current quota information */
1760Sstevel@tonic-gate 		expect_dq = getinoquota(ip);
1770Sstevel@tonic-gate 		/*
1780Sstevel@tonic-gate 		 * We got NULL back from getinoquota(), but there is
1790Sstevel@tonic-gate 		 * no error code return from that interface and some
1800Sstevel@tonic-gate 		 * errors are "ok" because we may be testing via error
1810Sstevel@tonic-gate 		 * injection.  If this is not the quota inode then we
1820Sstevel@tonic-gate 		 * use getdiskquota() to see if there is an error and
1830Sstevel@tonic-gate 		 * if the error is ok.
1840Sstevel@tonic-gate 		 */
1850Sstevel@tonic-gate 		if (expect_dq == NULL && ip != ufsvfsp->vfs_qinod) {
1860Sstevel@tonic-gate 			int error;
1870Sstevel@tonic-gate 			struct dquot *xdqp;
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 			error = getdiskquota((uid_t)ip->i_uid, ufsvfsp, 0,
190*4662Sfrankho 			    &xdqp);
1910Sstevel@tonic-gate 			switch (error) {
1920Sstevel@tonic-gate 			/*
1930Sstevel@tonic-gate 			 * Either the error was transient or the quota
1940Sstevel@tonic-gate 			 * info record has no limits which gets optimized
1950Sstevel@tonic-gate 			 * out by getinoquota().
1960Sstevel@tonic-gate 			 */
1970Sstevel@tonic-gate 			case 0:
1980Sstevel@tonic-gate 				if (xdqp->dq_fhardlimit == 0 &&
1990Sstevel@tonic-gate 				    xdqp->dq_fsoftlimit == 0 &&
2000Sstevel@tonic-gate 				    xdqp->dq_bhardlimit == 0 &&
2010Sstevel@tonic-gate 				    xdqp->dq_bsoftlimit == 0) {
2020Sstevel@tonic-gate 					mutex_enter(&xdqp->dq_lock);
2030Sstevel@tonic-gate 					dqput(xdqp);
2040Sstevel@tonic-gate 					mutex_exit(&xdqp->dq_lock);
2050Sstevel@tonic-gate 				} else {
2060Sstevel@tonic-gate 					expect_dq = xdqp;
2070Sstevel@tonic-gate 				}
2080Sstevel@tonic-gate 				break;
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 			case ESRCH:	/* quotas are not enabled */
2110Sstevel@tonic-gate 			case EINVAL:	/* error flag set on cached record */
2120Sstevel@tonic-gate 			case EUSERS:	/* quota table is full */
2130Sstevel@tonic-gate 			case EIO:	/* I/O error */
2140Sstevel@tonic-gate 				mismatch_ok = 1;
2150Sstevel@tonic-gate 				break;
2160Sstevel@tonic-gate 			}
2170Sstevel@tonic-gate 		}
2180Sstevel@tonic-gate 
2190Sstevel@tonic-gate 		/*
2200Sstevel@tonic-gate 		 * Make sure dqp and the current quota info agree.
2210Sstevel@tonic-gate 		 * The first part of the #ifndef is the quick way to
2220Sstevel@tonic-gate 		 * do the check and should be part of the standard
2230Sstevel@tonic-gate 		 * DEBUG code. The #else part is useful if you are
2240Sstevel@tonic-gate 		 * actually chasing an inconsistency and don't want
2250Sstevel@tonic-gate 		 * to have to look at stack frames to figure which
2260Sstevel@tonic-gate 		 * variable has what value.
2270Sstevel@tonic-gate 		 */
2280Sstevel@tonic-gate #ifndef CHASE_QUOTA
2290Sstevel@tonic-gate 		ASSERT(mismatch_ok || dqp == expect_dq);
2300Sstevel@tonic-gate #else /* CHASE_QUOTA */
2310Sstevel@tonic-gate 		if (expect_dq == NULL) {
2320Sstevel@tonic-gate 			/*
2330Sstevel@tonic-gate 			 * If you hit this ASSERT() you know that quota
2340Sstevel@tonic-gate 			 * subsystem does not expect quota info for this
2350Sstevel@tonic-gate 			 * inode, but the inode has it.
2360Sstevel@tonic-gate 			 */
2370Sstevel@tonic-gate 			ASSERT(mismatch_ok || dqp == NULL);
2380Sstevel@tonic-gate 		} else {
2390Sstevel@tonic-gate 			/*
2400Sstevel@tonic-gate 			 * If you hit this ASSERT() you know that quota
2410Sstevel@tonic-gate 			 * subsystem expects quota info for this inode,
2420Sstevel@tonic-gate 			 * but the inode does not have it.
2430Sstevel@tonic-gate 			 */
2440Sstevel@tonic-gate 			ASSERT(dqp);
2450Sstevel@tonic-gate 			/*
2460Sstevel@tonic-gate 			 * If you hit this ASSERT() you know that quota
2470Sstevel@tonic-gate 			 * subsystem expects quota info for this inode
2480Sstevel@tonic-gate 			 * and the inode has quota info, but the two
2490Sstevel@tonic-gate 			 * quota info pointers are not the same.
2500Sstevel@tonic-gate 			 */
2510Sstevel@tonic-gate 			ASSERT(dqp == expect_dq);
2520Sstevel@tonic-gate 		}
2530Sstevel@tonic-gate #endif /* !CHASE_QUOTA */
2540Sstevel@tonic-gate 		/*
2550Sstevel@tonic-gate 		 * Release for getinoquota() above or getdiskquota()
2560Sstevel@tonic-gate 		 * call when error is transient.
2570Sstevel@tonic-gate 		 */
2580Sstevel@tonic-gate 		if (expect_dq) {
2590Sstevel@tonic-gate 			mutex_enter(&expect_dq->dq_lock);
2600Sstevel@tonic-gate 			dqput(expect_dq);
2610Sstevel@tonic-gate 			mutex_exit(&expect_dq->dq_lock);
2620Sstevel@tonic-gate 		}
2630Sstevel@tonic-gate 	}
2640Sstevel@tonic-gate #endif /* DEBUG */
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 	/*
2670Sstevel@tonic-gate 	 * Shadow inodes and extended attribute directories
2680Sstevel@tonic-gate 	 * do not have quota info records.
2690Sstevel@tonic-gate 	 */
2700Sstevel@tonic-gate 	if (dqp == NULL)
2710Sstevel@tonic-gate 		return (0);
2720Sstevel@tonic-gate 	/*
2730Sstevel@tonic-gate 	 * Quotas are not enabled on this file system so there is nothing
2740Sstevel@tonic-gate 	 * more to do.
2750Sstevel@tonic-gate 	 */
2760Sstevel@tonic-gate 	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {
2770Sstevel@tonic-gate 		return (0);
2780Sstevel@tonic-gate 	}
2790Sstevel@tonic-gate 	mutex_enter(&dqp->dq_lock);
2800Sstevel@tonic-gate 	if (change < 0) {
2810Sstevel@tonic-gate 		dqp->dq_flags |= DQ_MOD;
2820Sstevel@tonic-gate 		abs_change = -change;	/* abs_change must be positive */
2830Sstevel@tonic-gate 		if (dqp->dq_curblocks < abs_change)
2840Sstevel@tonic-gate 			dqp->dq_curblocks = 0;
2850Sstevel@tonic-gate 		else
2860Sstevel@tonic-gate 			dqp->dq_curblocks += change;
2870Sstevel@tonic-gate 		if (dqp->dq_curblocks < dqp->dq_bsoftlimit)
2880Sstevel@tonic-gate 			dqp->dq_btimelimit = 0;
2890Sstevel@tonic-gate 		dqp->dq_flags &= ~DQ_BLKS;
2900Sstevel@tonic-gate 		TRANS_QUOTA(dqp);
2910Sstevel@tonic-gate 		mutex_exit(&dqp->dq_lock);
2920Sstevel@tonic-gate 		return (0);
2930Sstevel@tonic-gate 	}
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 	/*
2960Sstevel@tonic-gate 	 * Adding 'change' to dq_curblocks could cause an overflow.
2970Sstevel@tonic-gate 	 * So store the result in a 64-bit variable and check for
2980Sstevel@tonic-gate 	 * overflow below.
2990Sstevel@tonic-gate 	 */
3000Sstevel@tonic-gate 	ncurblocks = (uint64_t)dqp->dq_curblocks + change;
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	/*
3030Sstevel@tonic-gate 	 * Allocation. Check hard and soft limits.
3040Sstevel@tonic-gate 	 * Skip checks for uid 0 owned files.
3050Sstevel@tonic-gate 	 * This check used to require both euid and ip->i_uid
3060Sstevel@tonic-gate 	 * to be 0; but there are no quotas for uid 0 so
3070Sstevel@tonic-gate 	 * it really doesn't matter who is writing to the
3080Sstevel@tonic-gate 	 * root owned file.  And even root cannot write
3090Sstevel@tonic-gate 	 * past a user's quota limit.
3100Sstevel@tonic-gate 	 */
3110Sstevel@tonic-gate 	if (ip->i_uid == 0)
3120Sstevel@tonic-gate 		goto out;
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 	/*
3150Sstevel@tonic-gate 	 * Disallow allocation if it would bring the current usage over
3160Sstevel@tonic-gate 	 * the hard limit or if the user is over his soft limit and his time
3170Sstevel@tonic-gate 	 * has run out.
3180Sstevel@tonic-gate 	 */
3190Sstevel@tonic-gate 	if (dqp->dq_bhardlimit && ncurblocks >= (uint64_t)dqp->dq_bhardlimit &&
3200Sstevel@tonic-gate 	    !force) {
3210Sstevel@tonic-gate 		/* If the user was not informed yet and the caller	*/
3220Sstevel@tonic-gate 		/* is the owner of the file				*/
3230Sstevel@tonic-gate 		if ((dqp->dq_flags & DQ_BLKS) == 0 &&
324*4662Sfrankho 		    ip->i_uid == crgetruid(cr)) {
3250Sstevel@tonic-gate 			errmsg = msg1;
3260Sstevel@tonic-gate 			dqp->dq_flags |= DQ_BLKS;
3270Sstevel@tonic-gate 		}
3280Sstevel@tonic-gate 		error = EDQUOT;
3290Sstevel@tonic-gate 		goto out;
3300Sstevel@tonic-gate 	}
3310Sstevel@tonic-gate 	if (dqp->dq_bsoftlimit && ncurblocks >= (uint64_t)dqp->dq_bsoftlimit) {
3320Sstevel@tonic-gate 		now = gethrestime_sec();
3330Sstevel@tonic-gate 		if (dqp->dq_curblocks < dqp->dq_bsoftlimit ||
3340Sstevel@tonic-gate 		    dqp->dq_btimelimit == 0) {
3350Sstevel@tonic-gate 			dqp->dq_flags |= DQ_MOD;
3360Sstevel@tonic-gate 			dqp->dq_btimelimit = now +
3370Sstevel@tonic-gate 			    ((struct ufsvfs *)ITOV(ip)->v_vfsp->vfs_data)
338*4662Sfrankho 			    ->vfs_btimelimit;
3390Sstevel@tonic-gate 			if (ip->i_uid == crgetruid(cr)) {
3400Sstevel@tonic-gate 				errmsg = msg2;
3410Sstevel@tonic-gate 			}
3420Sstevel@tonic-gate 		} else if (now > dqp->dq_btimelimit && !force) {
3430Sstevel@tonic-gate 			/* If the user was not informed yet and the	*/
3440Sstevel@tonic-gate 			/* caller is the owner of the file		*/
3450Sstevel@tonic-gate 			if ((dqp->dq_flags & DQ_BLKS) == 0 &&
346*4662Sfrankho 			    ip->i_uid == crgetruid(cr)) {
3470Sstevel@tonic-gate 				errmsg = msg3;
3480Sstevel@tonic-gate 				dqp->dq_flags |= DQ_BLKS;
3490Sstevel@tonic-gate 			}
3500Sstevel@tonic-gate 			error = EDQUOT;
3510Sstevel@tonic-gate 		}
3520Sstevel@tonic-gate 	}
3530Sstevel@tonic-gate out:
3540Sstevel@tonic-gate 	if (error == 0) {
3550Sstevel@tonic-gate 		dqp->dq_flags |= DQ_MOD;
3560Sstevel@tonic-gate 		/*
3570Sstevel@tonic-gate 		 * ncurblocks can be bigger than the maximum
3580Sstevel@tonic-gate 		 * number that can be represented in 32-bits.
3590Sstevel@tonic-gate 		 * When copying ncurblocks to dq_curblocks
3600Sstevel@tonic-gate 		 * (an unsigned 32-bit quantity), make sure there
3610Sstevel@tonic-gate 		 * is no overflow.  The only way this can happen
3620Sstevel@tonic-gate 		 * is if "force" is set.  Otherwise, this allocation
3630Sstevel@tonic-gate 		 * would have exceeded the hard limit check above
3640Sstevel@tonic-gate 		 * (since the hard limit is a 32-bit quantity).
3650Sstevel@tonic-gate 		 */
3660Sstevel@tonic-gate 		if (ncurblocks > 0xffffffffLL) {
3670Sstevel@tonic-gate 			dqp->dq_curblocks = 0xffffffff;
3680Sstevel@tonic-gate 			errmsg = msg4;
3690Sstevel@tonic-gate 		} else {
3700Sstevel@tonic-gate 			dqp->dq_curblocks = ncurblocks;
3710Sstevel@tonic-gate 		}
3720Sstevel@tonic-gate 	}
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 	if (dqp->dq_flags & DQ_MOD)
3750Sstevel@tonic-gate 		TRANS_QUOTA(dqp);
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate 	mutex_exit(&dqp->dq_lock);
3780Sstevel@tonic-gate 	/*
3790Sstevel@tonic-gate 	 * Check for any error messages to be sent
3800Sstevel@tonic-gate 	 */
3810Sstevel@tonic-gate 	if (errmsg != NULL) {
3820Sstevel@tonic-gate 		/*
3830Sstevel@tonic-gate 		 * Send message to the error log.
3840Sstevel@tonic-gate 		 */
3850Sstevel@tonic-gate 		if (uerrp != NULL) {
3860Sstevel@tonic-gate 			/*
3870Sstevel@tonic-gate 			 * Set up message caller should send to user;
3880Sstevel@tonic-gate 			 * gets copied to the message buffer as a side-
3890Sstevel@tonic-gate 			 * effect of the caller's uprintf().
3900Sstevel@tonic-gate 			 */
3910Sstevel@tonic-gate 			*lenp = strlen(errmsg) + 20 + 20 +
392*4662Sfrankho 			    strlen(ip->i_fs->fs_fsmnt) + 1;
3930Sstevel@tonic-gate 			*uerrp = (char *)kmem_alloc(*lenp, KM_NOSLEEP);
3940Sstevel@tonic-gate 			if (*uerrp != NULL) {
3950Sstevel@tonic-gate 				/* errmsg+1 => skip leading ! */
3960Sstevel@tonic-gate 				(void) sprintf(*uerrp, errmsg+1,
397*4662Sfrankho 				    (int)ttoproc(curthread)->p_pid,
398*4662Sfrankho 				    (int)ip->i_uid, (int)ip->i_number,
399*4662Sfrankho 				    ip->i_fs->fs_fsmnt);
4000Sstevel@tonic-gate 			}
4010Sstevel@tonic-gate 		} else {
4020Sstevel@tonic-gate 			/*
4030Sstevel@tonic-gate 			 * Caller doesn't care, so just copy to the
4040Sstevel@tonic-gate 			 * message buffer.
4050Sstevel@tonic-gate 			 */
4060Sstevel@tonic-gate 			cmn_err(CE_NOTE, errmsg,
407*4662Sfrankho 			    (int)ttoproc(curthread)->p_pid,
408*4662Sfrankho 			    (int)ip->i_uid, (int)ip->i_number,
409*4662Sfrankho 			    ip->i_fs->fs_fsmnt);
4100Sstevel@tonic-gate 		}
4110Sstevel@tonic-gate 	}
4120Sstevel@tonic-gate 	return (error);
4130Sstevel@tonic-gate }
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate /*
4160Sstevel@tonic-gate  * Check the inode limit, applying corrective action.
4170Sstevel@tonic-gate  */
4180Sstevel@tonic-gate int
chkiq(struct ufsvfs * ufsvfsp,int change,struct inode * ip,uid_t uid,int force,struct cred * cr,char ** uerrp,size_t * lenp)4190Sstevel@tonic-gate chkiq(struct ufsvfs *ufsvfsp, int change, struct inode *ip, uid_t uid,
4200Sstevel@tonic-gate 	int force, struct cred *cr, char **uerrp, size_t *lenp)
4210Sstevel@tonic-gate {
4220Sstevel@tonic-gate 	struct dquot *dqp, *xdqp;
4230Sstevel@tonic-gate 	unsigned int ncurfiles;
4240Sstevel@tonic-gate 	char *errmsg = NULL;
4250Sstevel@tonic-gate 	char *err1 =
4260Sstevel@tonic-gate "!quota_ufs: over file hard limit (pid %d, uid %d, fs %s)\n";
4270Sstevel@tonic-gate 	char *err2 =
4280Sstevel@tonic-gate "!quota_ufs: Warning: too many files (pid %d, uid %d, fs %s)\n";
4290Sstevel@tonic-gate 	char *err3 =
4300Sstevel@tonic-gate "!quota_ufs: over file and time limit (pid %d, uid %d, fs %s)\n";
4310Sstevel@tonic-gate 	int error = 0;
4320Sstevel@tonic-gate 	time_t now;
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	ASSERT(RW_READ_HELD(&ufsvfsp->vfs_dqrwlock));
4350Sstevel@tonic-gate 	/*
4360Sstevel@tonic-gate 	 * Change must be either a single increment or decrement.
4370Sstevel@tonic-gate 	 * If change is an increment, then ip must be NULL.
4380Sstevel@tonic-gate 	 */
4390Sstevel@tonic-gate 	ASSERT(change == 1 || change == -1);
4400Sstevel@tonic-gate 	ASSERT(change != 1 || ip == NULL);
4410Sstevel@tonic-gate 
4420Sstevel@tonic-gate 	/*
4430Sstevel@tonic-gate 	 * Quotas are not enabled so bail out now.
4440Sstevel@tonic-gate 	 */
4450Sstevel@tonic-gate 	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {
4460Sstevel@tonic-gate 		return (0);
4470Sstevel@tonic-gate 	}
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate 	/*
4500Sstevel@tonic-gate 	 * Free a specific inode.
4510Sstevel@tonic-gate 	 */
4520Sstevel@tonic-gate 	if (change == -1 && ip) {
4530Sstevel@tonic-gate 		dqp = ip->i_dquot;
4540Sstevel@tonic-gate 		/*
4550Sstevel@tonic-gate 		 * Shadow inodes and extended attribute directories
4560Sstevel@tonic-gate 		 * do not have quota info records.
4570Sstevel@tonic-gate 		 */
4580Sstevel@tonic-gate 		if (dqp == NULL)
4590Sstevel@tonic-gate 			return (0);
4600Sstevel@tonic-gate 		mutex_enter(&dqp->dq_lock);
4610Sstevel@tonic-gate 		if (dqp->dq_curfiles) {
4620Sstevel@tonic-gate 			dqp->dq_curfiles--;
4630Sstevel@tonic-gate 			dqp->dq_flags |= DQ_MOD;
4640Sstevel@tonic-gate 		}
4650Sstevel@tonic-gate 		if (dqp->dq_curfiles < dqp->dq_fsoftlimit) {
4660Sstevel@tonic-gate 			dqp->dq_ftimelimit = 0;
4670Sstevel@tonic-gate 			dqp->dq_flags |= DQ_MOD;
4680Sstevel@tonic-gate 		}
4690Sstevel@tonic-gate 		dqp->dq_flags &= ~DQ_FILES;
4700Sstevel@tonic-gate 		if (dqp->dq_flags & DQ_MOD)
4710Sstevel@tonic-gate 			TRANS_QUOTA(dqp);
4720Sstevel@tonic-gate 		mutex_exit(&dqp->dq_lock);
4730Sstevel@tonic-gate 		return (0);
4740Sstevel@tonic-gate 	}
4750Sstevel@tonic-gate 
4760Sstevel@tonic-gate 	/*
4770Sstevel@tonic-gate 	 * Allocation or deallocation without a specific inode.
4780Sstevel@tonic-gate 	 * Get dquot for for uid, fs.
4790Sstevel@tonic-gate 	 */
4800Sstevel@tonic-gate 	if (getdiskquota(uid, ufsvfsp, 0, &xdqp)) {
4810Sstevel@tonic-gate 		return (0);
4820Sstevel@tonic-gate 	}
4830Sstevel@tonic-gate 	dqp = xdqp;
4840Sstevel@tonic-gate 	mutex_enter(&dqp->dq_lock);
4850Sstevel@tonic-gate 	if (dqp->dq_fsoftlimit == 0 && dqp->dq_fhardlimit == 0) {
4860Sstevel@tonic-gate 		dqput(dqp);
4870Sstevel@tonic-gate 		mutex_exit(&dqp->dq_lock);
4880Sstevel@tonic-gate 		return (0);
4890Sstevel@tonic-gate 	}
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 	/*
4920Sstevel@tonic-gate 	 * Skip checks for uid 0 owned files.
4930Sstevel@tonic-gate 	 * This check used to require both euid and uid
4940Sstevel@tonic-gate 	 * to be 0; but there are no quotas for uid 0 so
4950Sstevel@tonic-gate 	 * it really doesn't matter who is writing to the
4960Sstevel@tonic-gate 	 * root owned file.  And even root can not write
4970Sstevel@tonic-gate 	 * past the user's quota limit.
4980Sstevel@tonic-gate 	 */
4990Sstevel@tonic-gate 	if (uid == 0)
5000Sstevel@tonic-gate 		goto out;
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate 	/*
5030Sstevel@tonic-gate 	 * Theoretically, this could overflow, but in practice, it
5040Sstevel@tonic-gate 	 * won't.  Multi-terabyte file systems are required to have an
5050Sstevel@tonic-gate 	 * nbpi value of at least 1MB.  In order to overflow this
5060Sstevel@tonic-gate 	 * field, there would have to be 2^32 inodes in the file.
5070Sstevel@tonic-gate 	 * That would imply a file system of 2^32 * 1MB, which is
5080Sstevel@tonic-gate 	 * 2^(32 + 20), which is 4096 terabytes, which is not
5090Sstevel@tonic-gate 	 * contemplated for ufs any time soon.
5100Sstevel@tonic-gate 	 */
5110Sstevel@tonic-gate 	ncurfiles = dqp->dq_curfiles + change;
5120Sstevel@tonic-gate 
5130Sstevel@tonic-gate 	/*
5140Sstevel@tonic-gate 	 * Dissallow allocation if it would bring the current usage over
5150Sstevel@tonic-gate 	 * the hard limit or if the user is over his soft limit and his time
5160Sstevel@tonic-gate 	 * has run out.
5170Sstevel@tonic-gate 	 */
5180Sstevel@tonic-gate 	if (change == 1 && ncurfiles >= dqp->dq_fhardlimit &&
5190Sstevel@tonic-gate 	    dqp->dq_fhardlimit && !force) {
5200Sstevel@tonic-gate 		/* If the user was not informed yet and the caller	*/
5210Sstevel@tonic-gate 		/* is the owner of the file 				*/
5220Sstevel@tonic-gate 		if ((dqp->dq_flags & DQ_FILES) == 0 && uid == crgetruid(cr)) {
5230Sstevel@tonic-gate 			errmsg = err1;
5240Sstevel@tonic-gate 			dqp->dq_flags |= DQ_FILES;
5250Sstevel@tonic-gate 		}
5260Sstevel@tonic-gate 		error = EDQUOT;
5270Sstevel@tonic-gate 	} else if (change == 1 && ncurfiles >= dqp->dq_fsoftlimit &&
528*4662Sfrankho 	    dqp->dq_fsoftlimit) {
5290Sstevel@tonic-gate 		now = gethrestime_sec();
5300Sstevel@tonic-gate 		if (ncurfiles == dqp->dq_fsoftlimit ||
5310Sstevel@tonic-gate 		    dqp->dq_ftimelimit == 0) {
5320Sstevel@tonic-gate 			dqp->dq_flags |= DQ_MOD;
5330Sstevel@tonic-gate 			dqp->dq_ftimelimit = now + ufsvfsp->vfs_ftimelimit;
5340Sstevel@tonic-gate 			/* If the caller owns the file */
5350Sstevel@tonic-gate 			if (uid == crgetruid(cr))
5360Sstevel@tonic-gate 				errmsg = err2;
5370Sstevel@tonic-gate 		} else if (now > dqp->dq_ftimelimit && !force) {
5380Sstevel@tonic-gate 			/* If the user was not informed yet and the	*/
5390Sstevel@tonic-gate 			/* caller is the owner of the file 		*/
5400Sstevel@tonic-gate 			if ((dqp->dq_flags & DQ_FILES) == 0 &&
541*4662Sfrankho 			    uid == crgetruid(cr)) {
5420Sstevel@tonic-gate 				errmsg = err3;
5430Sstevel@tonic-gate 				dqp->dq_flags |= DQ_FILES;
5440Sstevel@tonic-gate 			}
5450Sstevel@tonic-gate 			error = EDQUOT;
5460Sstevel@tonic-gate 		}
5470Sstevel@tonic-gate 	}
5480Sstevel@tonic-gate out:
5490Sstevel@tonic-gate 	if (error == 0) {
5500Sstevel@tonic-gate 		dqp->dq_flags |= DQ_MOD;
5510Sstevel@tonic-gate 		dqp->dq_curfiles += change;
5520Sstevel@tonic-gate 	}
5530Sstevel@tonic-gate 	if (dqp->dq_flags & DQ_MOD)
5540Sstevel@tonic-gate 		TRANS_QUOTA(dqp);
5550Sstevel@tonic-gate 	dqput(dqp);
5560Sstevel@tonic-gate 	mutex_exit(&dqp->dq_lock);
5570Sstevel@tonic-gate 	/*
5580Sstevel@tonic-gate 	 * Check for any error messages to be sent
5590Sstevel@tonic-gate 	 */
5600Sstevel@tonic-gate 	if (errmsg != NULL) {
5610Sstevel@tonic-gate 		/*
5620Sstevel@tonic-gate 		 * Send message to the error log.
5630Sstevel@tonic-gate 		 */
5640Sstevel@tonic-gate 		if (uerrp != NULL) {
5650Sstevel@tonic-gate 			/*
5660Sstevel@tonic-gate 			 * Set up message caller should send to user;
5670Sstevel@tonic-gate 			 * gets copied to the message buffer as a side-
5680Sstevel@tonic-gate 			 * effect of the caller's uprintf().
5690Sstevel@tonic-gate 			 */
5700Sstevel@tonic-gate 			*lenp = strlen(errmsg) + 20 + 20 +
571*4662Sfrankho 			    strlen(ufsvfsp->vfs_fs->fs_fsmnt) + 1;
5720Sstevel@tonic-gate 			*uerrp = (char *)kmem_alloc(*lenp, KM_NOSLEEP);
5730Sstevel@tonic-gate 			if (*uerrp != NULL) {
5740Sstevel@tonic-gate 				/* errmsg+1 => skip leading ! */
5750Sstevel@tonic-gate 				(void) sprintf(*uerrp, errmsg+1,
576*4662Sfrankho 				    (int)ttoproc(curthread)->p_pid,
577*4662Sfrankho 				    (int)uid, ufsvfsp->vfs_fs->fs_fsmnt);
5780Sstevel@tonic-gate 			}
5790Sstevel@tonic-gate 		} else {
5800Sstevel@tonic-gate 			/*
5810Sstevel@tonic-gate 			 * Caller doesn't care, so just copy to the
5820Sstevel@tonic-gate 			 * message buffer.
5830Sstevel@tonic-gate 			 */
5840Sstevel@tonic-gate 			cmn_err(CE_NOTE, errmsg,
585*4662Sfrankho 			    (int)ttoproc(curthread)->p_pid,
586*4662Sfrankho 			    (int)uid, ufsvfsp->vfs_fs->fs_fsmnt);
5870Sstevel@tonic-gate 		}
5880Sstevel@tonic-gate 	}
5890Sstevel@tonic-gate 	return (error);
5900Sstevel@tonic-gate }
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate /*
5930Sstevel@tonic-gate  * Release a dquot.
5940Sstevel@tonic-gate  */
5950Sstevel@tonic-gate void
dqrele(struct dquot * dqp)5960Sstevel@tonic-gate dqrele(struct dquot *dqp)
5970Sstevel@tonic-gate {
5980Sstevel@tonic-gate 	/*
5990Sstevel@tonic-gate 	 * Shadow inodes and extended attribute directories
6000Sstevel@tonic-gate 	 * do not have quota info records.
6010Sstevel@tonic-gate 	 */
6020Sstevel@tonic-gate 	if (dqp != NULL) {
6030Sstevel@tonic-gate 		mutex_enter(&dqp->dq_lock);
6040Sstevel@tonic-gate 		if (dqp->dq_cnt == 1 && dqp->dq_flags & DQ_MOD)
6050Sstevel@tonic-gate 			dqupdate(dqp);
6060Sstevel@tonic-gate 		dqput(dqp);
6070Sstevel@tonic-gate 		mutex_exit(&dqp->dq_lock);
6080Sstevel@tonic-gate 	}
6090Sstevel@tonic-gate }
610