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 + -
显示快捷键?