ufs_quota.c

来自「早期freebsd实现」· C语言 代码 · 共 939 行 · 第 1/2 页

C
939
字号
/* * Copyright (c) 1982, 1986, 1990, 1993 *	The Regents of the University of California.  All rights reserved. * * This code is derived from software contributed to Berkeley by * Robert Elz at The University of Melbourne. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software *    must display the following acknowledgement: *	This product includes software developed by the University of *	California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors *    may be used to endorse or promote products derived from this software *    without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *	@(#)ufs_quota.c	8.2 (Berkeley) 12/30/93 */#include <sys/param.h>#include <sys/kernel.h>#include <sys/systm.h>#include <sys/namei.h>#include <sys/malloc.h>#include <sys/file.h>#include <sys/proc.h>#include <sys/vnode.h>#include <sys/mount.h>#include <ufs/ufs/quota.h>#include <ufs/ufs/inode.h>#include <ufs/ufs/ufsmount.h>#include <ufs/ufs/ufs_extern.h>/* * Quota name to error message mapping. */static char *quotatypes[] = INITQFNAMES;/* * Set up the quotas for an inode. * * This routine completely defines the semantics of quotas. * If other criterion want to be used to establish quotas, the * MAXQUOTAS value in quotas.h should be increased, and the * additional dquots set up here. */intgetinoquota(ip)	register struct inode *ip;{	struct ufsmount *ump;	struct vnode *vp = ITOV(ip);	int error;	ump = VFSTOUFS(vp->v_mount);	/*	 * Set up the user quota based on file uid.	 * EINVAL means that quotas are not enabled.	 */	if (ip->i_dquot[USRQUOTA] == NODQUOT &&	    (error =		dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) &&	    error != EINVAL)		return (error);	/*	 * Set up the group quota based on file gid.	 * EINVAL means that quotas are not enabled.	 */	if (ip->i_dquot[GRPQUOTA] == NODQUOT &&	    (error =		dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&	    error != EINVAL)		return (error);	return (0);}/* * Update disk usage, and take corrective action. */intchkdq(ip, change, cred, flags)	register struct inode *ip;	long change;	struct ucred *cred;	int flags;{	register struct dquot *dq;	register int i;	int ncurblocks, error;#ifdef DIAGNOSTIC	if ((flags & CHOWN) == 0)		chkdquot(ip);#endif	if (change == 0)		return (0);	if (change < 0) {		for (i = 0; i < MAXQUOTAS; i++) {			if ((dq = ip->i_dquot[i]) == NODQUOT)				continue;			while (dq->dq_flags & DQ_LOCK) {				dq->dq_flags |= DQ_WANT;				sleep((caddr_t)dq, PINOD+1);			}			ncurblocks = dq->dq_curblocks + change;			if (ncurblocks >= 0)				dq->dq_curblocks = ncurblocks;			else				dq->dq_curblocks = 0;			dq->dq_flags &= ~DQ_BLKS;			dq->dq_flags |= DQ_MOD;		}		return (0);	}	if ((flags & FORCE) == 0 && cred->cr_uid != 0) {		for (i = 0; i < MAXQUOTAS; i++) {			if ((dq = ip->i_dquot[i]) == NODQUOT)				continue;			if (error = chkdqchg(ip, change, cred, i))				return (error);		}	}	for (i = 0; i < MAXQUOTAS; i++) {		if ((dq = ip->i_dquot[i]) == NODQUOT)			continue;		while (dq->dq_flags & DQ_LOCK) {			dq->dq_flags |= DQ_WANT;			sleep((caddr_t)dq, PINOD+1);		}		dq->dq_curblocks += change;		dq->dq_flags |= DQ_MOD;	}	return (0);}/* * Check for a valid change to a users allocation. * Issue an error message if appropriate. */intchkdqchg(ip, change, cred, type)	struct inode *ip;	long change;	struct ucred *cred;	int type;{	register struct dquot *dq = ip->i_dquot[type];	long ncurblocks = dq->dq_curblocks + change;	/*	 * If user would exceed their hard limit, disallow space allocation.	 */	if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {		if ((dq->dq_flags & DQ_BLKS) == 0 &&		    ip->i_uid == cred->cr_uid) {			uprintf("\n%s: write failed, %s disk limit reached\n",			    ITOV(ip)->v_mount->mnt_stat.f_mntonname,			    quotatypes[type]);			dq->dq_flags |= DQ_BLKS;		}		return (EDQUOT);	}	/*	 * If user is over their soft limit for too long, disallow space	 * allocation. Reset time limit as they cross their soft limit.	 */	if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {		if (dq->dq_curblocks < dq->dq_bsoftlimit) {			dq->dq_btime = time.tv_sec +			    VFSTOUFS(ITOV(ip)->v_mount)->um_btime[type];			if (ip->i_uid == cred->cr_uid)				uprintf("\n%s: warning, %s %s\n",				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,				    quotatypes[type], "disk quota exceeded");			return (0);		}		if (time.tv_sec > dq->dq_btime) {			if ((dq->dq_flags & DQ_BLKS) == 0 &&			    ip->i_uid == cred->cr_uid) {				uprintf("\n%s: write failed, %s %s\n",				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,				    quotatypes[type],				    "disk quota exceeded for too long");				dq->dq_flags |= DQ_BLKS;			}			return (EDQUOT);		}	}	return (0);}/* * Check the inode limit, applying corrective action. */intchkiq(ip, change, cred, flags)	register struct inode *ip;	long change;	struct ucred *cred;	int flags;{	register struct dquot *dq;	register int i;	int ncurinodes, error;#ifdef DIAGNOSTIC	if ((flags & CHOWN) == 0)		chkdquot(ip);#endif	if (change == 0)		return (0);	if (change < 0) {		for (i = 0; i < MAXQUOTAS; i++) {			if ((dq = ip->i_dquot[i]) == NODQUOT)				continue;			while (dq->dq_flags & DQ_LOCK) {				dq->dq_flags |= DQ_WANT;				sleep((caddr_t)dq, PINOD+1);			}			ncurinodes = dq->dq_curinodes + change;			if (ncurinodes >= 0)				dq->dq_curinodes = ncurinodes;			else				dq->dq_curinodes = 0;			dq->dq_flags &= ~DQ_INODS;			dq->dq_flags |= DQ_MOD;		}		return (0);	}	if ((flags & FORCE) == 0 && cred->cr_uid != 0) {		for (i = 0; i < MAXQUOTAS; i++) {			if ((dq = ip->i_dquot[i]) == NODQUOT)				continue;			if (error = chkiqchg(ip, change, cred, i))				return (error);		}	}	for (i = 0; i < MAXQUOTAS; i++) {		if ((dq = ip->i_dquot[i]) == NODQUOT)			continue;		while (dq->dq_flags & DQ_LOCK) {			dq->dq_flags |= DQ_WANT;			sleep((caddr_t)dq, PINOD+1);		}		dq->dq_curinodes += change;		dq->dq_flags |= DQ_MOD;	}	return (0);}/* * Check for a valid change to a users allocation. * Issue an error message if appropriate. */intchkiqchg(ip, change, cred, type)	struct inode *ip;	long change;	struct ucred *cred;	int type;{	register struct dquot *dq = ip->i_dquot[type];	long ncurinodes = dq->dq_curinodes + change;	/*	 * If user would exceed their hard limit, disallow inode allocation.	 */	if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {		if ((dq->dq_flags & DQ_INODS) == 0 &&		    ip->i_uid == cred->cr_uid) {			uprintf("\n%s: write failed, %s inode limit reached\n",			    ITOV(ip)->v_mount->mnt_stat.f_mntonname,			    quotatypes[type]);			dq->dq_flags |= DQ_INODS;		}		return (EDQUOT);	}	/*	 * If user is over their soft limit for too long, disallow inode	 * allocation. Reset time limit as they cross their soft limit.	 */	if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {		if (dq->dq_curinodes < dq->dq_isoftlimit) {			dq->dq_itime = time.tv_sec +			    VFSTOUFS(ITOV(ip)->v_mount)->um_itime[type];			if (ip->i_uid == cred->cr_uid)				uprintf("\n%s: warning, %s %s\n",				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,				    quotatypes[type], "inode quota exceeded");			return (0);		}		if (time.tv_sec > dq->dq_itime) {			if ((dq->dq_flags & DQ_INODS) == 0 &&			    ip->i_uid == cred->cr_uid) {				uprintf("\n%s: write failed, %s %s\n",				    ITOV(ip)->v_mount->mnt_stat.f_mntonname,				    quotatypes[type],				    "inode quota exceeded for too long");				dq->dq_flags |= DQ_INODS;			}			return (EDQUOT);		}	}	return (0);}#ifdef DIAGNOSTIC/* * On filesystems with quotas enabled, it is an error for a file to change * size and not to have a dquot structure associated with it. */voidchkdquot(ip)	register struct inode *ip;{	struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount);	register int i;	for (i = 0; i < MAXQUOTAS; i++) {		if (ump->um_quotas[i] == NULLVP ||		    (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING)))			continue;		if (ip->i_dquot[i] == NODQUOT) {			vprint("chkdquot: missing dquot", ITOV(ip));			panic("missing dquot");		}	}}#endif/* * Code to process quotactl commands. *//* * Q_QUOTAON - set up a quota file for a particular file system. */intquotaon(p, mp, type, fname)	struct proc *p;	struct mount *mp;	register int type;	caddr_t fname;{	register struct ufsmount *ump = VFSTOUFS(mp);	register struct vnode *vp, **vpp;	struct vnode *nextvp;	struct dquot *dq;	int error;	struct nameidata nd;	vpp = &ump->um_quotas[type];	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, p);	if (error = vn_open(&nd, FREAD|FWRITE, 0))		return (error);	vp = nd.ni_vp;	VOP_UNLOCK(vp);	if (vp->v_type != VREG) {		(void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);		return (EACCES);	}	if (vfs_busy(mp)) {		(void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);		return (EBUSY);	}	if (*vpp != vp)		quotaoff(p, mp, type);	ump->um_qflags[type] |= QTF_OPENING;	mp->mnt_flag |= MNT_QUOTA;	vp->v_flag |= VSYSTEM;	*vpp = vp;	/*	 * Save the credential of the process that turned on quotas.	 * Set up the time limits for this quota.	 */	crhold(p->p_ucred);	ump->um_cred[type] = p->p_ucred;	ump->um_btime[type] = MAX_DQ_TIME;	ump->um_itime[type] = MAX_IQ_TIME;	if (dqget(NULLVP, 0, ump, type, &dq) == 0) {		if (dq->dq_btime > 0)			ump->um_btime[type] = dq->dq_btime;		if (dq->dq_itime > 0)			ump->um_itime[type] = dq->dq_itime;		dqrele(NULLVP, dq);	}	/*	 * Search vnodes associated with this mount point,	 * adding references to quota file being opened.	 * NB: only need to add dquot's for inodes being modified.	 */again:	for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {		nextvp = vp->v_mntvnodes.le_next;		if (vp->v_writecount == 0)			continue;		if (vget(vp, 1))			goto again;		if (error = getinoquota(VTOI(vp))) {			vput(vp);			break;		}		vput(vp);		if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp)			goto again;	}	ump->um_qflags[type] &= ~QTF_OPENING;	if (error)		quotaoff(p, mp, type);	vfs_unbusy(mp);	return (error);}/* * Q_QUOTAOFF - turn off disk quotas for a filesystem. */intquotaoff(p, mp, type)	struct proc *p;	struct mount *mp;	register int type;{	register struct vnode *vp;	struct vnode *qvp, *nextvp;	struct ufsmount *ump = VFSTOUFS(mp);	register struct dquot *dq;	register struct inode *ip;	int error;		if ((mp->mnt_flag & MNT_MPBUSY) == 0)		panic("quotaoff: not busy");	if ((qvp = ump->um_quotas[type]) == NULLVP)		return (0);	ump->um_qflags[type] |= QTF_CLOSING;	/*	 * Search vnodes associated with this mount point,	 * deleting any references to quota file being closed.	 */again:	for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nextvp) {		nextvp = vp->v_mntvnodes.le_next;		if (vget(vp, 1))			goto again;		ip = VTOI(vp);		dq = ip->i_dquot[type];		ip->i_dquot[type] = NODQUOT;		dqrele(vp, dq);		vput(vp);		if (vp->v_mntvnodes.le_next != nextvp || vp->v_mount != mp)			goto again;	}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?