xref: /titanic_51/usr/src/uts/common/fs/ufs/quota_ufs.c (revision 80d3443290aca22ad7fb6c18568d19d37517ebbf)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*80d34432Sfrankho  * Common Development and Distribution License (the "License").
6*80d34432Sfrankho  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*80d34432Sfrankho  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
277c478bd9Sstevel@tonic-gate /*	  All Rights Reserved  	*/
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * University Copyright- Copyright (c) 1982, 1986, 1988
317c478bd9Sstevel@tonic-gate  * The Regents of the University of California
327c478bd9Sstevel@tonic-gate  * All Rights Reserved
337c478bd9Sstevel@tonic-gate  *
347c478bd9Sstevel@tonic-gate  * University Acknowledgment- Portions of this document are derived from
357c478bd9Sstevel@tonic-gate  * software developed by the University of California, Berkeley, and its
367c478bd9Sstevel@tonic-gate  * contributors.
377c478bd9Sstevel@tonic-gate  */
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate /*
437c478bd9Sstevel@tonic-gate  * Routines used in checking limits on file system usage.
447c478bd9Sstevel@tonic-gate  */
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate #include <sys/types.h>
477c478bd9Sstevel@tonic-gate #include <sys/t_lock.h>
487c478bd9Sstevel@tonic-gate #include <sys/param.h>
497c478bd9Sstevel@tonic-gate #include <sys/time.h>
507c478bd9Sstevel@tonic-gate #include <sys/systm.h>
517c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
527c478bd9Sstevel@tonic-gate #include <sys/signal.h>
537c478bd9Sstevel@tonic-gate #include <sys/cred.h>
547c478bd9Sstevel@tonic-gate #include <sys/proc.h>
557c478bd9Sstevel@tonic-gate #include <sys/user.h>
567c478bd9Sstevel@tonic-gate #include <sys/proc.h>
577c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
587c478bd9Sstevel@tonic-gate #include <sys/vnode.h>
597c478bd9Sstevel@tonic-gate #include <sys/buf.h>
607c478bd9Sstevel@tonic-gate #include <sys/uio.h>
617c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_inode.h>
627c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
637c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_quota.h>
647c478bd9Sstevel@tonic-gate #include <sys/errno.h>
657c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
667c478bd9Sstevel@tonic-gate #include <sys/session.h>
677c478bd9Sstevel@tonic-gate #include <sys/debug.h>
687c478bd9Sstevel@tonic-gate 
697c478bd9Sstevel@tonic-gate /*
707c478bd9Sstevel@tonic-gate  * Find the dquot structure that should
717c478bd9Sstevel@tonic-gate  * be used in checking i/o on inode ip.
727c478bd9Sstevel@tonic-gate  */
737c478bd9Sstevel@tonic-gate struct dquot *
747c478bd9Sstevel@tonic-gate getinoquota(struct inode *ip)
757c478bd9Sstevel@tonic-gate {
767c478bd9Sstevel@tonic-gate 	struct dquot *dqp, *xdqp;
777c478bd9Sstevel@tonic-gate 	struct ufsvfs *ufsvfsp = ip->i_ufsvfs;
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate 	ASSERT(RW_LOCK_HELD(&ufsvfsp->vfs_dqrwlock));
807c478bd9Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&ip->i_contents));
817c478bd9Sstevel@tonic-gate 	/*
827c478bd9Sstevel@tonic-gate 	 * Check for quotas enabled.
837c478bd9Sstevel@tonic-gate 	 */
847c478bd9Sstevel@tonic-gate 	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {
857c478bd9Sstevel@tonic-gate 		return (NULL);
867c478bd9Sstevel@tonic-gate 	}
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate 	/*
897c478bd9Sstevel@tonic-gate 	 * Check for someone doing I/O to quota file.
907c478bd9Sstevel@tonic-gate 	 */
917c478bd9Sstevel@tonic-gate 	if (ip == ufsvfsp->vfs_qinod) {
927c478bd9Sstevel@tonic-gate 		return (NULL);
937c478bd9Sstevel@tonic-gate 	}
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate 	/*
967c478bd9Sstevel@tonic-gate 	 * Check for a legal inode, e.g. not a shadow inode,
977c478bd9Sstevel@tonic-gate 	 * not a extended attribute directory inode and a valid mode.
987c478bd9Sstevel@tonic-gate 	 */
997c478bd9Sstevel@tonic-gate 	ASSERT((ip->i_mode & IFMT) != IFSHAD);
1007c478bd9Sstevel@tonic-gate 	ASSERT((ip->i_mode & IFMT) != IFATTRDIR);
1017c478bd9Sstevel@tonic-gate 	ASSERT(ip->i_mode);
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate 	if (getdiskquota((uid_t)ip->i_uid, ufsvfsp, 0, &xdqp)) {
1047c478bd9Sstevel@tonic-gate 		return (NULL);
1057c478bd9Sstevel@tonic-gate 	}
1067c478bd9Sstevel@tonic-gate 	dqp = xdqp;
1077c478bd9Sstevel@tonic-gate 	mutex_enter(&dqp->dq_lock);
1087c478bd9Sstevel@tonic-gate 	ASSERT(ip->i_uid == dqp->dq_uid);
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	if (dqp->dq_fhardlimit == 0 && dqp->dq_fsoftlimit == 0 &&
1117c478bd9Sstevel@tonic-gate 	    dqp->dq_bhardlimit == 0 && dqp->dq_bsoftlimit == 0) {
1127c478bd9Sstevel@tonic-gate 		dqput(dqp);
1137c478bd9Sstevel@tonic-gate 		mutex_exit(&dqp->dq_lock);
1147c478bd9Sstevel@tonic-gate 		dqp = NULL;
1157c478bd9Sstevel@tonic-gate 	} else {
1167c478bd9Sstevel@tonic-gate 		mutex_exit(&dqp->dq_lock);
1177c478bd9Sstevel@tonic-gate 	}
1187c478bd9Sstevel@tonic-gate 	return (dqp);
1197c478bd9Sstevel@tonic-gate }
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate /*
1227c478bd9Sstevel@tonic-gate  * Update disk usage, and take corrective action.
1237c478bd9Sstevel@tonic-gate  */
1247c478bd9Sstevel@tonic-gate int
1257c478bd9Sstevel@tonic-gate chkdq(struct inode *ip, long change, int force, struct cred *cr,
1267c478bd9Sstevel@tonic-gate 	char **uerrp, size_t *lenp)
1277c478bd9Sstevel@tonic-gate {
1287c478bd9Sstevel@tonic-gate 	struct dquot *dqp;
1297c478bd9Sstevel@tonic-gate 	uint64_t ncurblocks;
1307c478bd9Sstevel@tonic-gate 	struct ufsvfs *ufsvfsp = ip->i_ufsvfs;
1317c478bd9Sstevel@tonic-gate 	int error = 0;
1327c478bd9Sstevel@tonic-gate 	long abs_change;
1337c478bd9Sstevel@tonic-gate 	char *msg1 =
1347c478bd9Sstevel@tonic-gate "!quota_ufs: over hard disk limit (pid %d, uid %d, inum %d, fs %s)\n";
1357c478bd9Sstevel@tonic-gate 	char *msg2 =
1367c478bd9Sstevel@tonic-gate "!quota_ufs: Warning: over disk limit (pid %d, uid %d, inum %d, fs %s)\n";
1377c478bd9Sstevel@tonic-gate 	char *msg3 =
1387c478bd9Sstevel@tonic-gate "!quota_ufs: over disk and time limit (pid %d, uid %d, inum %d, fs %s)\n";
1397c478bd9Sstevel@tonic-gate 	char *msg4 =
1407c478bd9Sstevel@tonic-gate "!quota_ufs: Warning: quota overflow (pid %d, uid %d, inum %d, fs %s)\n";
1417c478bd9Sstevel@tonic-gate 	char *errmsg = NULL;
1427c478bd9Sstevel@tonic-gate 	time_t now;
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate 	/*
1457c478bd9Sstevel@tonic-gate 	 * Shadow inodes do not need to hold the vfs_dqrwlock lock.
1467c478bd9Sstevel@tonic-gate 	 */
1477c478bd9Sstevel@tonic-gate 	ASSERT((ip->i_mode & IFMT) == IFSHAD ||
1487c478bd9Sstevel@tonic-gate 	    RW_LOCK_HELD(&ufsvfsp->vfs_dqrwlock));
1497c478bd9Sstevel@tonic-gate 	ASSERT(RW_WRITE_HELD(&ip->i_contents));
1507c478bd9Sstevel@tonic-gate 
1517c478bd9Sstevel@tonic-gate 	if (change == 0)
1527c478bd9Sstevel@tonic-gate 		return (0);
1537c478bd9Sstevel@tonic-gate 	dqp = ip->i_dquot;
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate 	/*
1567c478bd9Sstevel@tonic-gate 	 * Make sure the quota info record matches the owner.
1577c478bd9Sstevel@tonic-gate 	 */
1587c478bd9Sstevel@tonic-gate 	ASSERT(dqp == NULL || ip->i_uid == dqp->dq_uid);
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate #ifdef DEBUG
1617c478bd9Sstevel@tonic-gate 	/*
1627c478bd9Sstevel@tonic-gate 	 * Shadow inodes and extended attribute directories
1637c478bd9Sstevel@tonic-gate 	 * should not have quota info records.
1647c478bd9Sstevel@tonic-gate 	 */
1657c478bd9Sstevel@tonic-gate 	if ((ip->i_mode & IFMT) == IFSHAD || (ip->i_mode & IFMT) == IFATTRDIR) {
1667c478bd9Sstevel@tonic-gate 		ASSERT(dqp == NULL);
1677c478bd9Sstevel@tonic-gate 	}
1687c478bd9Sstevel@tonic-gate 	/*
1697c478bd9Sstevel@tonic-gate 	 * Paranoia for verifying that quotas are okay.
1707c478bd9Sstevel@tonic-gate 	 */
1717c478bd9Sstevel@tonic-gate 	else {
1727c478bd9Sstevel@tonic-gate 		struct dquot *expect_dq;
1737c478bd9Sstevel@tonic-gate 		int mismatch_ok = 0;
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 		/* Get current quota information */
1767c478bd9Sstevel@tonic-gate 		expect_dq = getinoquota(ip);
1777c478bd9Sstevel@tonic-gate 		/*
1787c478bd9Sstevel@tonic-gate 		 * We got NULL back from getinoquota(), but there is
1797c478bd9Sstevel@tonic-gate 		 * no error code return from that interface and some
1807c478bd9Sstevel@tonic-gate 		 * errors are "ok" because we may be testing via error
1817c478bd9Sstevel@tonic-gate 		 * injection.  If this is not the quota inode then we
1827c478bd9Sstevel@tonic-gate 		 * use getdiskquota() to see if there is an error and
1837c478bd9Sstevel@tonic-gate 		 * if the error is ok.
1847c478bd9Sstevel@tonic-gate 		 */
1857c478bd9Sstevel@tonic-gate 		if (expect_dq == NULL && ip != ufsvfsp->vfs_qinod) {
1867c478bd9Sstevel@tonic-gate 			int error;
1877c478bd9Sstevel@tonic-gate 			struct dquot *xdqp;
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 			error = getdiskquota((uid_t)ip->i_uid, ufsvfsp, 0,
1907c478bd9Sstevel@tonic-gate 			    &xdqp);
1917c478bd9Sstevel@tonic-gate 			switch (error) {
1927c478bd9Sstevel@tonic-gate 			/*
1937c478bd9Sstevel@tonic-gate 			 * Either the error was transient or the quota
1947c478bd9Sstevel@tonic-gate 			 * info record has no limits which gets optimized
1957c478bd9Sstevel@tonic-gate 			 * out by getinoquota().
1967c478bd9Sstevel@tonic-gate 			 */
1977c478bd9Sstevel@tonic-gate 			case 0:
1987c478bd9Sstevel@tonic-gate 				if (xdqp->dq_fhardlimit == 0 &&
1997c478bd9Sstevel@tonic-gate 				    xdqp->dq_fsoftlimit == 0 &&
2007c478bd9Sstevel@tonic-gate 				    xdqp->dq_bhardlimit == 0 &&
2017c478bd9Sstevel@tonic-gate 				    xdqp->dq_bsoftlimit == 0) {
2027c478bd9Sstevel@tonic-gate 					mutex_enter(&xdqp->dq_lock);
2037c478bd9Sstevel@tonic-gate 					dqput(xdqp);
2047c478bd9Sstevel@tonic-gate 					mutex_exit(&xdqp->dq_lock);
2057c478bd9Sstevel@tonic-gate 				} else {
2067c478bd9Sstevel@tonic-gate 					expect_dq = xdqp;
2077c478bd9Sstevel@tonic-gate 				}
2087c478bd9Sstevel@tonic-gate 				break;
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 			case ESRCH:	/* quotas are not enabled */
2117c478bd9Sstevel@tonic-gate 			case EINVAL:	/* error flag set on cached record */
2127c478bd9Sstevel@tonic-gate 			case EUSERS:	/* quota table is full */
2137c478bd9Sstevel@tonic-gate 			case EIO:	/* I/O error */
2147c478bd9Sstevel@tonic-gate 				mismatch_ok = 1;
2157c478bd9Sstevel@tonic-gate 				break;
2167c478bd9Sstevel@tonic-gate 			}
2177c478bd9Sstevel@tonic-gate 		}
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 		/*
2207c478bd9Sstevel@tonic-gate 		 * Make sure dqp and the current quota info agree.
2217c478bd9Sstevel@tonic-gate 		 * The first part of the #ifndef is the quick way to
2227c478bd9Sstevel@tonic-gate 		 * do the check and should be part of the standard
2237c478bd9Sstevel@tonic-gate 		 * DEBUG code. The #else part is useful if you are
2247c478bd9Sstevel@tonic-gate 		 * actually chasing an inconsistency and don't want
2257c478bd9Sstevel@tonic-gate 		 * to have to look at stack frames to figure which
2267c478bd9Sstevel@tonic-gate 		 * variable has what value.
2277c478bd9Sstevel@tonic-gate 		 */
2287c478bd9Sstevel@tonic-gate #ifndef CHASE_QUOTA
2297c478bd9Sstevel@tonic-gate 		ASSERT(mismatch_ok || dqp == expect_dq);
2307c478bd9Sstevel@tonic-gate #else /* CHASE_QUOTA */
2317c478bd9Sstevel@tonic-gate 		if (expect_dq == NULL) {
2327c478bd9Sstevel@tonic-gate 			/*
2337c478bd9Sstevel@tonic-gate 			 * If you hit this ASSERT() you know that quota
2347c478bd9Sstevel@tonic-gate 			 * subsystem does not expect quota info for this
2357c478bd9Sstevel@tonic-gate 			 * inode, but the inode has it.
2367c478bd9Sstevel@tonic-gate 			 */
2377c478bd9Sstevel@tonic-gate 			ASSERT(mismatch_ok || dqp == NULL);
2387c478bd9Sstevel@tonic-gate 		} else {
2397c478bd9Sstevel@tonic-gate 			/*
2407c478bd9Sstevel@tonic-gate 			 * If you hit this ASSERT() you know that quota
2417c478bd9Sstevel@tonic-gate 			 * subsystem expects quota info for this inode,
2427c478bd9Sstevel@tonic-gate 			 * but the inode does not have it.
2437c478bd9Sstevel@tonic-gate 			 */
2447c478bd9Sstevel@tonic-gate 			ASSERT(dqp);
2457c478bd9Sstevel@tonic-gate 			/*
2467c478bd9Sstevel@tonic-gate 			 * If you hit this ASSERT() you know that quota
2477c478bd9Sstevel@tonic-gate 			 * subsystem expects quota info for this inode
2487c478bd9Sstevel@tonic-gate 			 * and the inode has quota info, but the two
2497c478bd9Sstevel@tonic-gate 			 * quota info pointers are not the same.
2507c478bd9Sstevel@tonic-gate 			 */
2517c478bd9Sstevel@tonic-gate 			ASSERT(dqp == expect_dq);
2527c478bd9Sstevel@tonic-gate 		}
2537c478bd9Sstevel@tonic-gate #endif /* !CHASE_QUOTA */
2547c478bd9Sstevel@tonic-gate 		/*
2557c478bd9Sstevel@tonic-gate 		 * Release for getinoquota() above or getdiskquota()
2567c478bd9Sstevel@tonic-gate 		 * call when error is transient.
2577c478bd9Sstevel@tonic-gate 		 */
2587c478bd9Sstevel@tonic-gate 		if (expect_dq) {
2597c478bd9Sstevel@tonic-gate 			mutex_enter(&expect_dq->dq_lock);
2607c478bd9Sstevel@tonic-gate 			dqput(expect_dq);
2617c478bd9Sstevel@tonic-gate 			mutex_exit(&expect_dq->dq_lock);
2627c478bd9Sstevel@tonic-gate 		}
2637c478bd9Sstevel@tonic-gate 	}
2647c478bd9Sstevel@tonic-gate #endif /* DEBUG */
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 	/*
2677c478bd9Sstevel@tonic-gate 	 * Shadow inodes and extended attribute directories
2687c478bd9Sstevel@tonic-gate 	 * do not have quota info records.
2697c478bd9Sstevel@tonic-gate 	 */
2707c478bd9Sstevel@tonic-gate 	if (dqp == NULL)
2717c478bd9Sstevel@tonic-gate 		return (0);
2727c478bd9Sstevel@tonic-gate 	/*
2737c478bd9Sstevel@tonic-gate 	 * Quotas are not enabled on this file system so there is nothing
2747c478bd9Sstevel@tonic-gate 	 * more to do.
2757c478bd9Sstevel@tonic-gate 	 */
2767c478bd9Sstevel@tonic-gate 	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {
2777c478bd9Sstevel@tonic-gate 		return (0);
2787c478bd9Sstevel@tonic-gate 	}
2797c478bd9Sstevel@tonic-gate 	mutex_enter(&dqp->dq_lock);
2807c478bd9Sstevel@tonic-gate 	if (change < 0) {
2817c478bd9Sstevel@tonic-gate 		dqp->dq_flags |= DQ_MOD;
2827c478bd9Sstevel@tonic-gate 		abs_change = -change;	/* abs_change must be positive */
2837c478bd9Sstevel@tonic-gate 		if (dqp->dq_curblocks < abs_change)
2847c478bd9Sstevel@tonic-gate 			dqp->dq_curblocks = 0;
2857c478bd9Sstevel@tonic-gate 		else
2867c478bd9Sstevel@tonic-gate 			dqp->dq_curblocks += change;
2877c478bd9Sstevel@tonic-gate 		if (dqp->dq_curblocks < dqp->dq_bsoftlimit)
2887c478bd9Sstevel@tonic-gate 			dqp->dq_btimelimit = 0;
2897c478bd9Sstevel@tonic-gate 		dqp->dq_flags &= ~DQ_BLKS;
2907c478bd9Sstevel@tonic-gate 		TRANS_QUOTA(dqp);
2917c478bd9Sstevel@tonic-gate 		mutex_exit(&dqp->dq_lock);
2927c478bd9Sstevel@tonic-gate 		return (0);
2937c478bd9Sstevel@tonic-gate 	}
2947c478bd9Sstevel@tonic-gate 
2957c478bd9Sstevel@tonic-gate 	/*
2967c478bd9Sstevel@tonic-gate 	 * Adding 'change' to dq_curblocks could cause an overflow.
2977c478bd9Sstevel@tonic-gate 	 * So store the result in a 64-bit variable and check for
2987c478bd9Sstevel@tonic-gate 	 * overflow below.
2997c478bd9Sstevel@tonic-gate 	 */
3007c478bd9Sstevel@tonic-gate 	ncurblocks = (uint64_t)dqp->dq_curblocks + change;
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	/*
3037c478bd9Sstevel@tonic-gate 	 * Allocation. Check hard and soft limits.
3047c478bd9Sstevel@tonic-gate 	 * Skip checks for uid 0 owned files.
3057c478bd9Sstevel@tonic-gate 	 * This check used to require both euid and ip->i_uid
3067c478bd9Sstevel@tonic-gate 	 * to be 0; but there are no quotas for uid 0 so
3077c478bd9Sstevel@tonic-gate 	 * it really doesn't matter who is writing to the
3087c478bd9Sstevel@tonic-gate 	 * root owned file.  And even root cannot write
3097c478bd9Sstevel@tonic-gate 	 * past a user's quota limit.
3107c478bd9Sstevel@tonic-gate 	 */
3117c478bd9Sstevel@tonic-gate 	if (ip->i_uid == 0)
3127c478bd9Sstevel@tonic-gate 		goto out;
3137c478bd9Sstevel@tonic-gate 
3147c478bd9Sstevel@tonic-gate 	/*
3157c478bd9Sstevel@tonic-gate 	 * Disallow allocation if it would bring the current usage over
3167c478bd9Sstevel@tonic-gate 	 * the hard limit or if the user is over his soft limit and his time
3177c478bd9Sstevel@tonic-gate 	 * has run out.
3187c478bd9Sstevel@tonic-gate 	 */
3197c478bd9Sstevel@tonic-gate 	if (dqp->dq_bhardlimit && ncurblocks >= (uint64_t)dqp->dq_bhardlimit &&
3207c478bd9Sstevel@tonic-gate 	    !force) {
3217c478bd9Sstevel@tonic-gate 		/* If the user was not informed yet and the caller	*/
3227c478bd9Sstevel@tonic-gate 		/* is the owner of the file				*/
3237c478bd9Sstevel@tonic-gate 		if ((dqp->dq_flags & DQ_BLKS) == 0 &&
3247c478bd9Sstevel@tonic-gate 		    ip->i_uid == crgetruid(cr)) {
3257c478bd9Sstevel@tonic-gate 			errmsg = msg1;
3267c478bd9Sstevel@tonic-gate 			dqp->dq_flags |= DQ_BLKS;
3277c478bd9Sstevel@tonic-gate 		}
3287c478bd9Sstevel@tonic-gate 		error = EDQUOT;
3297c478bd9Sstevel@tonic-gate 		goto out;
3307c478bd9Sstevel@tonic-gate 	}
3317c478bd9Sstevel@tonic-gate 	if (dqp->dq_bsoftlimit && ncurblocks >= (uint64_t)dqp->dq_bsoftlimit) {
3327c478bd9Sstevel@tonic-gate 		now = gethrestime_sec();
3337c478bd9Sstevel@tonic-gate 		if (dqp->dq_curblocks < dqp->dq_bsoftlimit ||
3347c478bd9Sstevel@tonic-gate 		    dqp->dq_btimelimit == 0) {
3357c478bd9Sstevel@tonic-gate 			dqp->dq_flags |= DQ_MOD;
3367c478bd9Sstevel@tonic-gate 			dqp->dq_btimelimit = now +
3377c478bd9Sstevel@tonic-gate 			    ((struct ufsvfs *)ITOV(ip)->v_vfsp->vfs_data)
3387c478bd9Sstevel@tonic-gate 			    ->vfs_btimelimit;
3397c478bd9Sstevel@tonic-gate 			if (ip->i_uid == crgetruid(cr)) {
3407c478bd9Sstevel@tonic-gate 				errmsg = msg2;
3417c478bd9Sstevel@tonic-gate 			}
3427c478bd9Sstevel@tonic-gate 		} else if (now > dqp->dq_btimelimit && !force) {
3437c478bd9Sstevel@tonic-gate 			/* If the user was not informed yet and the	*/
3447c478bd9Sstevel@tonic-gate 			/* caller is the owner of the file		*/
3457c478bd9Sstevel@tonic-gate 			if ((dqp->dq_flags & DQ_BLKS) == 0 &&
3467c478bd9Sstevel@tonic-gate 			    ip->i_uid == crgetruid(cr)) {
3477c478bd9Sstevel@tonic-gate 				errmsg = msg3;
3487c478bd9Sstevel@tonic-gate 				dqp->dq_flags |= DQ_BLKS;
3497c478bd9Sstevel@tonic-gate 			}
3507c478bd9Sstevel@tonic-gate 			error = EDQUOT;
3517c478bd9Sstevel@tonic-gate 		}
3527c478bd9Sstevel@tonic-gate 	}
3537c478bd9Sstevel@tonic-gate out:
3547c478bd9Sstevel@tonic-gate 	if (error == 0) {
3557c478bd9Sstevel@tonic-gate 		dqp->dq_flags |= DQ_MOD;
3567c478bd9Sstevel@tonic-gate 		/*
3577c478bd9Sstevel@tonic-gate 		 * ncurblocks can be bigger than the maximum
3587c478bd9Sstevel@tonic-gate 		 * number that can be represented in 32-bits.
3597c478bd9Sstevel@tonic-gate 		 * When copying ncurblocks to dq_curblocks
3607c478bd9Sstevel@tonic-gate 		 * (an unsigned 32-bit quantity), make sure there
3617c478bd9Sstevel@tonic-gate 		 * is no overflow.  The only way this can happen
3627c478bd9Sstevel@tonic-gate 		 * is if "force" is set.  Otherwise, this allocation
3637c478bd9Sstevel@tonic-gate 		 * would have exceeded the hard limit check above
3647c478bd9Sstevel@tonic-gate 		 * (since the hard limit is a 32-bit quantity).
3657c478bd9Sstevel@tonic-gate 		 */
3667c478bd9Sstevel@tonic-gate 		if (ncurblocks > 0xffffffffLL) {
3677c478bd9Sstevel@tonic-gate 			dqp->dq_curblocks = 0xffffffff;
3687c478bd9Sstevel@tonic-gate 			errmsg = msg4;
3697c478bd9Sstevel@tonic-gate 		} else {
3707c478bd9Sstevel@tonic-gate 			dqp->dq_curblocks = ncurblocks;
3717c478bd9Sstevel@tonic-gate 		}
3727c478bd9Sstevel@tonic-gate 	}
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate 	if (dqp->dq_flags & DQ_MOD)
3757c478bd9Sstevel@tonic-gate 		TRANS_QUOTA(dqp);
3767c478bd9Sstevel@tonic-gate 
3777c478bd9Sstevel@tonic-gate 	mutex_exit(&dqp->dq_lock);
3787c478bd9Sstevel@tonic-gate 	/*
3797c478bd9Sstevel@tonic-gate 	 * Check for any error messages to be sent
3807c478bd9Sstevel@tonic-gate 	 */
3817c478bd9Sstevel@tonic-gate 	if (errmsg != NULL) {
3827c478bd9Sstevel@tonic-gate 		/*
3837c478bd9Sstevel@tonic-gate 		 * Send message to the error log.
3847c478bd9Sstevel@tonic-gate 		 */
3857c478bd9Sstevel@tonic-gate 		if (uerrp != NULL) {
3867c478bd9Sstevel@tonic-gate 			/*
3877c478bd9Sstevel@tonic-gate 			 * Set up message caller should send to user;
3887c478bd9Sstevel@tonic-gate 			 * gets copied to the message buffer as a side-
3897c478bd9Sstevel@tonic-gate 			 * effect of the caller's uprintf().
3907c478bd9Sstevel@tonic-gate 			 */
3917c478bd9Sstevel@tonic-gate 			*lenp = strlen(errmsg) + 20 + 20 +
3927c478bd9Sstevel@tonic-gate 			    strlen(ip->i_fs->fs_fsmnt) + 1;
3937c478bd9Sstevel@tonic-gate 			*uerrp = (char *)kmem_alloc(*lenp, KM_NOSLEEP);
3947c478bd9Sstevel@tonic-gate 			if (*uerrp != NULL) {
3957c478bd9Sstevel@tonic-gate 				/* errmsg+1 => skip leading ! */
3967c478bd9Sstevel@tonic-gate 				(void) sprintf(*uerrp, errmsg+1,
3977c478bd9Sstevel@tonic-gate 				    (int)ttoproc(curthread)->p_pid,
3987c478bd9Sstevel@tonic-gate 				    (int)ip->i_uid, (int)ip->i_number,
3997c478bd9Sstevel@tonic-gate 				    ip->i_fs->fs_fsmnt);
4007c478bd9Sstevel@tonic-gate 			}
4017c478bd9Sstevel@tonic-gate 		} else {
4027c478bd9Sstevel@tonic-gate 			/*
4037c478bd9Sstevel@tonic-gate 			 * Caller doesn't care, so just copy to the
4047c478bd9Sstevel@tonic-gate 			 * message buffer.
4057c478bd9Sstevel@tonic-gate 			 */
4067c478bd9Sstevel@tonic-gate 			cmn_err(CE_NOTE, errmsg,
4077c478bd9Sstevel@tonic-gate 			    (int)ttoproc(curthread)->p_pid,
4087c478bd9Sstevel@tonic-gate 			    (int)ip->i_uid, (int)ip->i_number,
4097c478bd9Sstevel@tonic-gate 			    ip->i_fs->fs_fsmnt);
4107c478bd9Sstevel@tonic-gate 		}
4117c478bd9Sstevel@tonic-gate 	}
4127c478bd9Sstevel@tonic-gate 	return (error);
4137c478bd9Sstevel@tonic-gate }
4147c478bd9Sstevel@tonic-gate 
4157c478bd9Sstevel@tonic-gate /*
4167c478bd9Sstevel@tonic-gate  * Check the inode limit, applying corrective action.
4177c478bd9Sstevel@tonic-gate  */
4187c478bd9Sstevel@tonic-gate int
4197c478bd9Sstevel@tonic-gate chkiq(struct ufsvfs *ufsvfsp, int change, struct inode *ip, uid_t uid,
4207c478bd9Sstevel@tonic-gate 	int force, struct cred *cr, char **uerrp, size_t *lenp)
4217c478bd9Sstevel@tonic-gate {
4227c478bd9Sstevel@tonic-gate 	struct dquot *dqp, *xdqp;
4237c478bd9Sstevel@tonic-gate 	unsigned int ncurfiles;
4247c478bd9Sstevel@tonic-gate 	char *errmsg = NULL;
4257c478bd9Sstevel@tonic-gate 	char *err1 =
4267c478bd9Sstevel@tonic-gate "!quota_ufs: over file hard limit (pid %d, uid %d, fs %s)\n";
4277c478bd9Sstevel@tonic-gate 	char *err2 =
4287c478bd9Sstevel@tonic-gate "!quota_ufs: Warning: too many files (pid %d, uid %d, fs %s)\n";
4297c478bd9Sstevel@tonic-gate 	char *err3 =
4307c478bd9Sstevel@tonic-gate "!quota_ufs: over file and time limit (pid %d, uid %d, fs %s)\n";
4317c478bd9Sstevel@tonic-gate 	int error = 0;
4327c478bd9Sstevel@tonic-gate 	time_t now;
4337c478bd9Sstevel@tonic-gate 
4347c478bd9Sstevel@tonic-gate 	ASSERT(RW_READ_HELD(&ufsvfsp->vfs_dqrwlock));
4357c478bd9Sstevel@tonic-gate 	/*
4367c478bd9Sstevel@tonic-gate 	 * Change must be either a single increment or decrement.
4377c478bd9Sstevel@tonic-gate 	 * If change is an increment, then ip must be NULL.
4387c478bd9Sstevel@tonic-gate 	 */
4397c478bd9Sstevel@tonic-gate 	ASSERT(change == 1 || change == -1);
4407c478bd9Sstevel@tonic-gate 	ASSERT(change != 1 || ip == NULL);
4417c478bd9Sstevel@tonic-gate 
4427c478bd9Sstevel@tonic-gate 	/*
4437c478bd9Sstevel@tonic-gate 	 * Quotas are not enabled so bail out now.
4447c478bd9Sstevel@tonic-gate 	 */
4457c478bd9Sstevel@tonic-gate 	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {
4467c478bd9Sstevel@tonic-gate 		return (0);
4477c478bd9Sstevel@tonic-gate 	}
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	/*
4507c478bd9Sstevel@tonic-gate 	 * Free a specific inode.
4517c478bd9Sstevel@tonic-gate 	 */
4527c478bd9Sstevel@tonic-gate 	if (change == -1 && ip) {
4537c478bd9Sstevel@tonic-gate 		dqp = ip->i_dquot;
4547c478bd9Sstevel@tonic-gate 		/*
4557c478bd9Sstevel@tonic-gate 		 * Shadow inodes and extended attribute directories
4567c478bd9Sstevel@tonic-gate 		 * do not have quota info records.
4577c478bd9Sstevel@tonic-gate 		 */
4587c478bd9Sstevel@tonic-gate 		if (dqp == NULL)
4597c478bd9Sstevel@tonic-gate 			return (0);
4607c478bd9Sstevel@tonic-gate 		mutex_enter(&dqp->dq_lock);
4617c478bd9Sstevel@tonic-gate 		if (dqp->dq_curfiles) {
4627c478bd9Sstevel@tonic-gate 			dqp->dq_curfiles--;
4637c478bd9Sstevel@tonic-gate 			dqp->dq_flags |= DQ_MOD;
4647c478bd9Sstevel@tonic-gate 		}
4657c478bd9Sstevel@tonic-gate 		if (dqp->dq_curfiles < dqp->dq_fsoftlimit) {
4667c478bd9Sstevel@tonic-gate 			dqp->dq_ftimelimit = 0;
4677c478bd9Sstevel@tonic-gate 			dqp->dq_flags |= DQ_MOD;
4687c478bd9Sstevel@tonic-gate 		}
4697c478bd9Sstevel@tonic-gate 		dqp->dq_flags &= ~DQ_FILES;
4707c478bd9Sstevel@tonic-gate 		if (dqp->dq_flags & DQ_MOD)
4717c478bd9Sstevel@tonic-gate 			TRANS_QUOTA(dqp);
4727c478bd9Sstevel@tonic-gate 		mutex_exit(&dqp->dq_lock);
4737c478bd9Sstevel@tonic-gate 		return (0);
4747c478bd9Sstevel@tonic-gate 	}
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	/*
4777c478bd9Sstevel@tonic-gate 	 * Allocation or deallocation without a specific inode.
4787c478bd9Sstevel@tonic-gate 	 * Get dquot for for uid, fs.
4797c478bd9Sstevel@tonic-gate 	 */
4807c478bd9Sstevel@tonic-gate 	if (getdiskquota(uid, ufsvfsp, 0, &xdqp)) {
4817c478bd9Sstevel@tonic-gate 		return (0);
4827c478bd9Sstevel@tonic-gate 	}
4837c478bd9Sstevel@tonic-gate 	dqp = xdqp;
4847c478bd9Sstevel@tonic-gate 	mutex_enter(&dqp->dq_lock);
4857c478bd9Sstevel@tonic-gate 	if (dqp->dq_fsoftlimit == 0 && dqp->dq_fhardlimit == 0) {
4867c478bd9Sstevel@tonic-gate 		dqput(dqp);
4877c478bd9Sstevel@tonic-gate 		mutex_exit(&dqp->dq_lock);
4887c478bd9Sstevel@tonic-gate 		return (0);
4897c478bd9Sstevel@tonic-gate 	}
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate 	/*
4927c478bd9Sstevel@tonic-gate 	 * Skip checks for uid 0 owned files.
4937c478bd9Sstevel@tonic-gate 	 * This check used to require both euid and uid
4947c478bd9Sstevel@tonic-gate 	 * to be 0; but there are no quotas for uid 0 so
4957c478bd9Sstevel@tonic-gate 	 * it really doesn't matter who is writing to the
4967c478bd9Sstevel@tonic-gate 	 * root owned file.  And even root can not write
4977c478bd9Sstevel@tonic-gate 	 * past the user's quota limit.
4987c478bd9Sstevel@tonic-gate 	 */
4997c478bd9Sstevel@tonic-gate 	if (uid == 0)
5007c478bd9Sstevel@tonic-gate 		goto out;
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate 	/*
5037c478bd9Sstevel@tonic-gate 	 * Theoretically, this could overflow, but in practice, it
5047c478bd9Sstevel@tonic-gate 	 * won't.  Multi-terabyte file systems are required to have an
5057c478bd9Sstevel@tonic-gate 	 * nbpi value of at least 1MB.  In order to overflow this
5067c478bd9Sstevel@tonic-gate 	 * field, there would have to be 2^32 inodes in the file.
5077c478bd9Sstevel@tonic-gate 	 * That would imply a file system of 2^32 * 1MB, which is
5087c478bd9Sstevel@tonic-gate 	 * 2^(32 + 20), which is 4096 terabytes, which is not
5097c478bd9Sstevel@tonic-gate 	 * contemplated for ufs any time soon.
5107c478bd9Sstevel@tonic-gate 	 */
5117c478bd9Sstevel@tonic-gate 	ncurfiles = dqp->dq_curfiles + change;
5127c478bd9Sstevel@tonic-gate 
5137c478bd9Sstevel@tonic-gate 	/*
5147c478bd9Sstevel@tonic-gate 	 * Dissallow allocation if it would bring the current usage over
5157c478bd9Sstevel@tonic-gate 	 * the hard limit or if the user is over his soft limit and his time
5167c478bd9Sstevel@tonic-gate 	 * has run out.
5177c478bd9Sstevel@tonic-gate 	 */
5187c478bd9Sstevel@tonic-gate 	if (change == 1 && ncurfiles >= dqp->dq_fhardlimit &&
5197c478bd9Sstevel@tonic-gate 	    dqp->dq_fhardlimit && !force) {
5207c478bd9Sstevel@tonic-gate 		/* If the user was not informed yet and the caller	*/
5217c478bd9Sstevel@tonic-gate 		/* is the owner of the file 				*/
5227c478bd9Sstevel@tonic-gate 		if ((dqp->dq_flags & DQ_FILES) == 0 && uid == crgetruid(cr)) {
5237c478bd9Sstevel@tonic-gate 			errmsg = err1;
5247c478bd9Sstevel@tonic-gate 			dqp->dq_flags |= DQ_FILES;
5257c478bd9Sstevel@tonic-gate 		}
5267c478bd9Sstevel@tonic-gate 		error = EDQUOT;
5277c478bd9Sstevel@tonic-gate 	} else if (change == 1 && ncurfiles >= dqp->dq_fsoftlimit &&
5287c478bd9Sstevel@tonic-gate 	    dqp->dq_fsoftlimit) {
5297c478bd9Sstevel@tonic-gate 		now = gethrestime_sec();
5307c478bd9Sstevel@tonic-gate 		if (ncurfiles == dqp->dq_fsoftlimit ||
5317c478bd9Sstevel@tonic-gate 		    dqp->dq_ftimelimit == 0) {
5327c478bd9Sstevel@tonic-gate 			dqp->dq_flags |= DQ_MOD;
5337c478bd9Sstevel@tonic-gate 			dqp->dq_ftimelimit = now + ufsvfsp->vfs_ftimelimit;
5347c478bd9Sstevel@tonic-gate 			/* If the caller owns the file */
5357c478bd9Sstevel@tonic-gate 			if (uid == crgetruid(cr))
5367c478bd9Sstevel@tonic-gate 				errmsg = err2;
5377c478bd9Sstevel@tonic-gate 		} else if (now > dqp->dq_ftimelimit && !force) {
5387c478bd9Sstevel@tonic-gate 			/* If the user was not informed yet and the	*/
5397c478bd9Sstevel@tonic-gate 			/* caller is the owner of the file 		*/
5407c478bd9Sstevel@tonic-gate 			if ((dqp->dq_flags & DQ_FILES) == 0 &&
5417c478bd9Sstevel@tonic-gate 			    uid == crgetruid(cr)) {
5427c478bd9Sstevel@tonic-gate 				errmsg = err3;
5437c478bd9Sstevel@tonic-gate 				dqp->dq_flags |= DQ_FILES;
5447c478bd9Sstevel@tonic-gate 			}
5457c478bd9Sstevel@tonic-gate 			error = EDQUOT;
5467c478bd9Sstevel@tonic-gate 		}
5477c478bd9Sstevel@tonic-gate 	}
5487c478bd9Sstevel@tonic-gate out:
5497c478bd9Sstevel@tonic-gate 	if (error == 0) {
5507c478bd9Sstevel@tonic-gate 		dqp->dq_flags |= DQ_MOD;
5517c478bd9Sstevel@tonic-gate 		dqp->dq_curfiles += change;
5527c478bd9Sstevel@tonic-gate 	}
5537c478bd9Sstevel@tonic-gate 	if (dqp->dq_flags & DQ_MOD)
5547c478bd9Sstevel@tonic-gate 		TRANS_QUOTA(dqp);
5557c478bd9Sstevel@tonic-gate 	dqput(dqp);
5567c478bd9Sstevel@tonic-gate 	mutex_exit(&dqp->dq_lock);
5577c478bd9Sstevel@tonic-gate 	/*
5587c478bd9Sstevel@tonic-gate 	 * Check for any error messages to be sent
5597c478bd9Sstevel@tonic-gate 	 */
5607c478bd9Sstevel@tonic-gate 	if (errmsg != NULL) {
5617c478bd9Sstevel@tonic-gate 		/*
5627c478bd9Sstevel@tonic-gate 		 * Send message to the error log.
5637c478bd9Sstevel@tonic-gate 		 */
5647c478bd9Sstevel@tonic-gate 		if (uerrp != NULL) {
5657c478bd9Sstevel@tonic-gate 			/*
5667c478bd9Sstevel@tonic-gate 			 * Set up message caller should send to user;
5677c478bd9Sstevel@tonic-gate 			 * gets copied to the message buffer as a side-
5687c478bd9Sstevel@tonic-gate 			 * effect of the caller's uprintf().
5697c478bd9Sstevel@tonic-gate 			 */
5707c478bd9Sstevel@tonic-gate 			*lenp = strlen(errmsg) + 20 + 20 +
5717c478bd9Sstevel@tonic-gate 			    strlen(ufsvfsp->vfs_fs->fs_fsmnt) + 1;
5727c478bd9Sstevel@tonic-gate 			*uerrp = (char *)kmem_alloc(*lenp, KM_NOSLEEP);
5737c478bd9Sstevel@tonic-gate 			if (*uerrp != NULL) {
5747c478bd9Sstevel@tonic-gate 				/* errmsg+1 => skip leading ! */
5757c478bd9Sstevel@tonic-gate 				(void) sprintf(*uerrp, errmsg+1,
5767c478bd9Sstevel@tonic-gate 				    (int)ttoproc(curthread)->p_pid,
5777c478bd9Sstevel@tonic-gate 				    (int)uid, ufsvfsp->vfs_fs->fs_fsmnt);
5787c478bd9Sstevel@tonic-gate 			}
5797c478bd9Sstevel@tonic-gate 		} else {
5807c478bd9Sstevel@tonic-gate 			/*
5817c478bd9Sstevel@tonic-gate 			 * Caller doesn't care, so just copy to the
5827c478bd9Sstevel@tonic-gate 			 * message buffer.
5837c478bd9Sstevel@tonic-gate 			 */
5847c478bd9Sstevel@tonic-gate 			cmn_err(CE_NOTE, errmsg,
5857c478bd9Sstevel@tonic-gate 			    (int)ttoproc(curthread)->p_pid,
5867c478bd9Sstevel@tonic-gate 			    (int)uid, ufsvfsp->vfs_fs->fs_fsmnt);
5877c478bd9Sstevel@tonic-gate 		}
5887c478bd9Sstevel@tonic-gate 	}
5897c478bd9Sstevel@tonic-gate 	return (error);
5907c478bd9Sstevel@tonic-gate }
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate /*
5937c478bd9Sstevel@tonic-gate  * Release a dquot.
5947c478bd9Sstevel@tonic-gate  */
5957c478bd9Sstevel@tonic-gate void
5967c478bd9Sstevel@tonic-gate dqrele(struct dquot *dqp)
5977c478bd9Sstevel@tonic-gate {
5987c478bd9Sstevel@tonic-gate 	/*
5997c478bd9Sstevel@tonic-gate 	 * Shadow inodes and extended attribute directories
6007c478bd9Sstevel@tonic-gate 	 * do not have quota info records.
6017c478bd9Sstevel@tonic-gate 	 */
6027c478bd9Sstevel@tonic-gate 	if (dqp != NULL) {
6037c478bd9Sstevel@tonic-gate 		mutex_enter(&dqp->dq_lock);
6047c478bd9Sstevel@tonic-gate 		if (dqp->dq_cnt == 1 && dqp->dq_flags & DQ_MOD)
6057c478bd9Sstevel@tonic-gate 			dqupdate(dqp);
6067c478bd9Sstevel@tonic-gate 		dqput(dqp);
6077c478bd9Sstevel@tonic-gate 		mutex_exit(&dqp->dq_lock);
6087c478bd9Sstevel@tonic-gate 	}
6097c478bd9Sstevel@tonic-gate }
610