[BACK]Return to ufs_quota.c CVS log [TXT][DIR] Up to [local] / sys / ufs / ufs

Annotation of sys/ufs/ufs/ufs_quota.c, Revision 1.1.1.1

1.1       nbrk        1: /*     $OpenBSD: ufs_quota.c,v 1.28 2007/08/03 18:41:44 millert Exp $  */
                      2: /*     $NetBSD: ufs_quota.c,v 1.8 1996/02/09 22:36:09 christos Exp $   */
                      3:
                      4: /*
                      5:  * Copyright (c) 1982, 1986, 1990, 1993, 1995
                      6:  *     The Regents of the University of California.  All rights reserved.
                      7:  *
                      8:  * This code is derived from software contributed to Berkeley by
                      9:  * Robert Elz at The University of Melbourne.
                     10:  *
                     11:  * Redistribution and use in source and binary forms, with or without
                     12:  * modification, are permitted provided that the following conditions
                     13:  * are met:
                     14:  * 1. Redistributions of source code must retain the above copyright
                     15:  *    notice, this list of conditions and the following disclaimer.
                     16:  * 2. Redistributions in binary form must reproduce the above copyright
                     17:  *    notice, this list of conditions and the following disclaimer in the
                     18:  *    documentation and/or other materials provided with the distribution.
                     19:  * 3. Neither the name of the University nor the names of its contributors
                     20:  *    may be used to endorse or promote products derived from this software
                     21:  *    without specific prior written permission.
                     22:  *
                     23:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     24:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     25:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     26:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     27:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     28:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     29:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     30:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     31:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     32:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     33:  * SUCH DAMAGE.
                     34:  *
                     35:  *     @(#)ufs_quota.c 8.5 (Berkeley) 8/19/94
                     36:  */
                     37:
                     38: #include <sys/param.h>
                     39: #include <sys/kernel.h>
                     40: #include <sys/systm.h>
                     41: #include <sys/namei.h>
                     42: #include <sys/malloc.h>
                     43: #include <sys/file.h>
                     44: #include <sys/proc.h>
                     45: #include <sys/vnode.h>
                     46: #include <sys/mount.h>
                     47:
                     48: #include <ufs/ufs/quota.h>
                     49: #include <ufs/ufs/inode.h>
                     50: #include <ufs/ufs/ufsmount.h>
                     51: #include <ufs/ufs/ufs_extern.h>
                     52:
                     53: #include <sys/queue.h>
                     54:
                     55: /*
                     56:  * The following structure records disk usage for a user or group on a
                     57:  * filesystem. There is one allocated for each quota that exists on any
                     58:  * filesystem for the current user or group. A cache is kept of recently
                     59:  * used entries.
                     60:  */
                     61: struct dquot {
                     62:        LIST_ENTRY(dquot) dq_hash;      /* hash list */
                     63:        TAILQ_ENTRY(dquot) dq_freelist; /* free list */
                     64:        u_int16_t dq_flags;             /* flags, see below */
                     65:        u_int16_t dq_type;              /* quota type of this dquot */
                     66:        u_int32_t dq_cnt;               /* count of active references */
                     67:        u_int32_t dq_id;                /* identifier this applies to */
                     68:        struct  vnode *dq_vp;           /* file backing this quota */
                     69:        struct  ucred  *dq_cred;        /* credentials for writing file */
                     70:        struct  dqblk dq_dqb;           /* actual usage & quotas */
                     71: };
                     72:
                     73: /*
                     74:  * Flag values.
                     75:  */
                     76: #define        DQ_LOCK         0x01            /* this quota locked (no MODS) */
                     77: #define        DQ_WANT         0x02            /* wakeup on unlock */
                     78: #define        DQ_MOD          0x04            /* this quota modified since read */
                     79: #define        DQ_FAKE         0x08            /* no limits here, just usage */
                     80: #define        DQ_BLKS         0x10            /* has been warned about blk limit */
                     81: #define        DQ_INODS        0x20            /* has been warned about inode limit */
                     82:
                     83: /*
                     84:  * Shorthand notation.
                     85:  */
                     86: #define        dq_bhardlimit   dq_dqb.dqb_bhardlimit
                     87: #define        dq_bsoftlimit   dq_dqb.dqb_bsoftlimit
                     88: #define        dq_curblocks    dq_dqb.dqb_curblocks
                     89: #define        dq_ihardlimit   dq_dqb.dqb_ihardlimit
                     90: #define        dq_isoftlimit   dq_dqb.dqb_isoftlimit
                     91: #define        dq_curinodes    dq_dqb.dqb_curinodes
                     92: #define        dq_btime        dq_dqb.dqb_btime
                     93: #define        dq_itime        dq_dqb.dqb_itime
                     94:
                     95: /*
                     96:  * If the system has never checked for a quota for this file, then it is
                     97:  * set to NODQUOT.  Once a write attempt is made the inode pointer is set
                     98:  * to reference a dquot structure.
                     99:  */
                    100: #define        NODQUOT         NULL
                    101:
                    102: void   dqref(struct dquot *);
                    103: void   dqrele(struct vnode *, struct dquot *);
                    104: int    dqsync(struct vnode *, struct dquot *);
                    105:
                    106: #ifdef DIAGNOSTIC
                    107: void   chkdquot(struct inode *);
                    108: #endif
                    109:
                    110: int    getquota(struct mount *, u_long, int, caddr_t);
                    111: int    quotaon(struct proc *, struct mount *, int, caddr_t);
                    112: int    setquota(struct mount *, u_long, int, caddr_t);
                    113: int    setuse(struct mount *, u_long, int, caddr_t);
                    114:
                    115: int    chkdqchg(struct inode *, long, struct ucred *, int);
                    116: int    chkiqchg(struct inode *, long, struct ucred *, int);
                    117:
                    118: int dqget(struct vnode *, u_long, struct ufsmount *, int,
                    119:               struct dquot **);
                    120:
                    121: int     quotaon_vnode(struct vnode *, void *);
                    122: int     quotaoff_vnode(struct vnode *, void *);
                    123: int     qsync_vnode(struct vnode *, void *);
                    124:
                    125: /*
                    126:  * Quota name to error message mapping.
                    127:  */
                    128: static char *quotatypes[] = INITQFNAMES;
                    129:
                    130: /*
                    131:  * Obtain a reference to a dquot.
                    132:  */
                    133: void
                    134: dqref(struct dquot *dq)
                    135: {
                    136:        dq->dq_cnt++;
                    137: }
                    138:
                    139: /*
                    140:  * Set up the quotas for an inode.
                    141:  *
                    142:  * This routine completely defines the semantics of quotas.
                    143:  * If other criterion want to be used to establish quotas, the
                    144:  * MAXQUOTAS value in quotas.h should be increased, and the
                    145:  * additional dquots set up here.
                    146:  */
                    147: int
                    148: getinoquota(struct inode *ip)
                    149: {
                    150:        struct ufsmount *ump;
                    151:        struct vnode *vp = ITOV(ip);
                    152:        int error;
                    153:
                    154:        ump = ip->i_ump;
                    155:        /*
                    156:         * Set up the user quota based on file uid.
                    157:         * EINVAL means that quotas are not enabled.
                    158:         */
                    159:        if (ip->i_dquot[USRQUOTA] == NODQUOT &&
                    160:            (error =
                    161:                dqget(vp, DIP(ip, uid), ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) &&
                    162:            error != EINVAL)
                    163:                return (error);
                    164:        /*
                    165:         * Set up the group quota based on file gid.
                    166:         * EINVAL means that quotas are not enabled.
                    167:         */
                    168:        if (ip->i_dquot[GRPQUOTA] == NODQUOT &&
                    169:            (error =
                    170:                dqget(vp, DIP(ip, gid), ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&
                    171:            error != EINVAL)
                    172:                return (error);
                    173:        return (0);
                    174: }
                    175:
                    176: /*
                    177:  * Update disk usage, and take corrective action.
                    178:  */
                    179: int
                    180: ufs_quota_alloc_blocks2(struct inode *ip, int32_t change,
                    181:     struct ucred *cred, enum ufs_quota_flags flags)
                    182: {
                    183:        struct dquot *dq;
                    184:        int i;
                    185:        int error;
                    186:
                    187: #ifdef DIAGNOSTIC
                    188:        chkdquot(ip);
                    189: #endif
                    190:
                    191:        if (change == 0)
                    192:                return (0);
                    193:
                    194:        if ((flags & UFS_QUOTA_FORCE) == 0 &&
                    195:            (cred != NOCRED && cred->cr_uid != 0)) {
                    196:                for (i = 0; i < MAXQUOTAS; i++) {
                    197:                        if (flags & (1 << i))
                    198:                                continue;
                    199:                        if ((dq = ip->i_dquot[i]) == NODQUOT)
                    200:                                continue;
                    201:                        if ((error = chkdqchg(ip, change, cred, i)) != 0)
                    202:                                return (error);
                    203:                }
                    204:        }
                    205:        for (i = 0; i < MAXQUOTAS; i++) {
                    206:                if (flags & (1 << i))
                    207:                        continue;
                    208:                if ((dq = ip->i_dquot[i]) == NODQUOT)
                    209:                        continue;
                    210:                while (dq->dq_flags & DQ_LOCK) {
                    211:                        dq->dq_flags |= DQ_WANT;
                    212:                        (void) tsleep(dq, PINOD+1, "chkdq", 0);
                    213:                }
                    214:                dq->dq_curblocks += change;
                    215:                dq->dq_flags |= DQ_MOD;
                    216:        }
                    217:        return (0);
                    218: }
                    219:
                    220: int
                    221: ufs_quota_free_blocks2(struct inode *ip, int32_t change,
                    222:     struct ucred *cred, enum ufs_quota_flags flags)
                    223: {
                    224:        struct dquot *dq;
                    225:        int i;
                    226:
                    227: #ifdef DIAGNOSTIC
                    228:        if (!VOP_ISLOCKED(ITOV(ip)))
                    229:                panic ("ufs_quota_free_blocks2: vnode is not locked");
                    230: #endif
                    231:
                    232:        if (change == 0)
                    233:                return (0);
                    234:
                    235:        for (i = 0; i < MAXQUOTAS; i++) {
                    236:                if (flags & (1 << i))
                    237:                        continue;
                    238:                if ((dq = ip->i_dquot[i]) == NODQUOT)
                    239:                        continue;
                    240:                while (dq->dq_flags & DQ_LOCK) {
                    241:                        dq->dq_flags |= DQ_WANT;
                    242:                        (void) tsleep(dq, PINOD+1, "chkdq", 0);
                    243:                }
                    244:                if (dq->dq_curblocks >= change)
                    245:                        dq->dq_curblocks -= change;
                    246:                else
                    247:                        dq->dq_curblocks = 0;
                    248:                dq->dq_flags &= ~DQ_BLKS;
                    249:                dq->dq_flags |= DQ_MOD;
                    250:        }
                    251:        return (0);
                    252: }
                    253:
                    254: /*
                    255:  * Check for a valid change to a users allocation.
                    256:  * Issue an error message if appropriate.
                    257:  */
                    258: int
                    259: chkdqchg(struct inode *ip, long change, struct ucred *cred, int type)
                    260: {
                    261:        struct dquot *dq = ip->i_dquot[type];
                    262:        long ncurblocks = dq->dq_curblocks + change;
                    263:
                    264:        /*
                    265:         * If user would exceed their hard limit, disallow space allocation.
                    266:         */
                    267:        if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
                    268:                if ((dq->dq_flags & DQ_BLKS) == 0 &&
                    269:                    DIP(ip, uid) == cred->cr_uid) {
                    270:                        uprintf("\n%s: write failed, %s disk limit reached\n",
                    271:                            ITOV(ip)->v_mount->mnt_stat.f_mntonname,
                    272:                            quotatypes[type]);
                    273:                        dq->dq_flags |= DQ_BLKS;
                    274:                }
                    275:                return (EDQUOT);
                    276:        }
                    277:        /*
                    278:         * If user is over their soft limit for too long, disallow space
                    279:         * allocation. Reset time limit as they cross their soft limit.
                    280:         */
                    281:        if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
                    282:                if (dq->dq_curblocks < dq->dq_bsoftlimit) {
                    283:                        dq->dq_btime = time_second +
                    284:                            ip->i_ump->um_btime[type];
                    285:                        if (DIP(ip, uid) == cred->cr_uid)
                    286:                                uprintf("\n%s: warning, %s %s\n",
                    287:                                    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
                    288:                                    quotatypes[type], "disk quota exceeded");
                    289:                        return (0);
                    290:                }
                    291:                if (time_second > dq->dq_btime) {
                    292:                        if ((dq->dq_flags & DQ_BLKS) == 0 &&
                    293:                            DIP(ip, uid) == cred->cr_uid) {
                    294:                                uprintf("\n%s: write failed, %s %s\n",
                    295:                                    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
                    296:                                    quotatypes[type],
                    297:                                    "disk quota exceeded for too long");
                    298:                                dq->dq_flags |= DQ_BLKS;
                    299:                        }
                    300:                        return (EDQUOT);
                    301:                }
                    302:        }
                    303:        return (0);
                    304: }
                    305:
                    306: /*
                    307:  * Check the inode limit, applying corrective action.
                    308:  */
                    309: int
                    310: ufs_quota_alloc_inode2(struct inode *ip, struct ucred *cred,
                    311:     enum ufs_quota_flags flags)
                    312: {
                    313:        struct dquot *dq;
                    314:        int i;
                    315:        int error;
                    316:
                    317: #ifdef DIAGNOSTIC
                    318:        chkdquot(ip);
                    319: #endif
                    320:
                    321:        if ((flags & UFS_QUOTA_FORCE) == 0 && cred->cr_uid != 0) {
                    322:                for (i = 0; i < MAXQUOTAS; i++) {
                    323:                        if (flags & (1 << i))
                    324:                                continue;
                    325:                        if ((dq = ip->i_dquot[i]) == NODQUOT)
                    326:                                continue;
                    327:                        if ((error = chkiqchg(ip, 1, cred, i)) != 0)
                    328:                                return (error);
                    329:                }
                    330:        }
                    331:        for (i = 0; i < MAXQUOTAS; i++) {
                    332:                if (flags & (1 << i))
                    333:                        continue;
                    334:                if ((dq = ip->i_dquot[i]) == NODQUOT)
                    335:                        continue;
                    336:                while (dq->dq_flags & DQ_LOCK) {
                    337:                        dq->dq_flags |= DQ_WANT;
                    338:                        (void) tsleep(dq, PINOD+1, "chkiq", 0);
                    339:                }
                    340:                dq->dq_curinodes++;
                    341:                dq->dq_flags |= DQ_MOD;
                    342:        }
                    343:        return (0);
                    344: }
                    345:
                    346: int
                    347: ufs_quota_free_inode2(struct inode *ip, struct ucred *cred,
                    348:     enum ufs_quota_flags flags)
                    349: {
                    350:        struct dquot *dq;
                    351:        int i;
                    352:
                    353: #ifdef DIAGNOSTIC
                    354:        if (!VOP_ISLOCKED(ITOV(ip)))
                    355:                panic ("ufs_quota_free_blocks2: vnode is not locked");
                    356: #endif
                    357:
                    358:        for (i = 0; i < MAXQUOTAS; i++) {
                    359:                if (flags & (1 << i))
                    360:                        continue;
                    361:                if ((dq = ip->i_dquot[i]) == NODQUOT)
                    362:                        continue;
                    363:                while (dq->dq_flags & DQ_LOCK) {
                    364:                        dq->dq_flags |= DQ_WANT;
                    365:                        (void) tsleep(dq, PINOD+1, "chkiq", 0);
                    366:                }
                    367:                if (dq->dq_curinodes > 0)
                    368:                        dq->dq_curinodes--;
                    369:                dq->dq_flags &= ~DQ_INODS;
                    370:                dq->dq_flags |= DQ_MOD;
                    371:        }
                    372:        return (0);
                    373: }
                    374:
                    375: /*
                    376:  * Check for a valid change to a users allocation.
                    377:  * Issue an error message if appropriate.
                    378:  */
                    379: int
                    380: chkiqchg(struct inode *ip, long change, struct ucred *cred, int type)
                    381: {
                    382:        struct dquot *dq = ip->i_dquot[type];
                    383:        long ncurinodes = dq->dq_curinodes + change;
                    384:
                    385:        /*
                    386:         * If user would exceed their hard limit, disallow inode allocation.
                    387:         */
                    388:        if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
                    389:                if ((dq->dq_flags & DQ_INODS) == 0 &&
                    390:                    DIP(ip, uid) == cred->cr_uid) {
                    391:                        uprintf("\n%s: write failed, %s inode limit reached\n",
                    392:                            ITOV(ip)->v_mount->mnt_stat.f_mntonname,
                    393:                            quotatypes[type]);
                    394:                        dq->dq_flags |= DQ_INODS;
                    395:                }
                    396:                return (EDQUOT);
                    397:        }
                    398:        /*
                    399:         * If user is over their soft limit for too long, disallow inode
                    400:         * allocation. Reset time limit as they cross their soft limit.
                    401:         */
                    402:        if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
                    403:                if (dq->dq_curinodes < dq->dq_isoftlimit) {
                    404:                        dq->dq_itime = time_second +
                    405:                            ip->i_ump->um_itime[type];
                    406:                        if (DIP(ip, uid) == cred->cr_uid)
                    407:                                uprintf("\n%s: warning, %s %s\n",
                    408:                                    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
                    409:                                    quotatypes[type], "inode quota exceeded");
                    410:                        return (0);
                    411:                }
                    412:                if (time_second > dq->dq_itime) {
                    413:                        if ((dq->dq_flags & DQ_INODS) == 0 &&
                    414:                            DIP(ip, uid) == cred->cr_uid) {
                    415:                                uprintf("\n%s: write failed, %s %s\n",
                    416:                                    ITOV(ip)->v_mount->mnt_stat.f_mntonname,
                    417:                                    quotatypes[type],
                    418:                                    "inode quota exceeded for too long");
                    419:                                dq->dq_flags |= DQ_INODS;
                    420:                        }
                    421:                        return (EDQUOT);
                    422:                }
                    423:        }
                    424:        return (0);
                    425: }
                    426:
                    427: #ifdef DIAGNOSTIC
                    428: /*
                    429:  * On filesystems with quotas enabled, it is an error for a file to change
                    430:  * size and not to have a dquot structure associated with it.
                    431:  */
                    432: void
                    433: chkdquot(struct inode *ip)
                    434: {
                    435:        struct ufsmount *ump = ip->i_ump;
                    436:        int i;
                    437:        struct vnode *vp = ITOV(ip);
                    438:
                    439:        if (!VOP_ISLOCKED(vp))
                    440:                panic ("chkdquot: vnode is not locked");
                    441:
                    442:        for (i = 0; i < MAXQUOTAS; i++) {
                    443:                if (ump->um_quotas[i] == NULLVP ||
                    444:                    (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING)))
                    445:                        continue;
                    446:                if (ip->i_dquot[i] == NODQUOT) {
                    447:                        vprint("chkdquot: missing dquot", ITOV(ip));
                    448:                        panic("missing dquot");
                    449:                }
                    450:        }
                    451: }
                    452: #endif
                    453:
                    454: /*
                    455:  * Code to process quotactl commands.
                    456:  */
                    457:
                    458: int
                    459: quotaon_vnode(struct vnode *vp, void *arg)
                    460: {
                    461:        int error;
                    462:        struct proc *p = (struct proc *)arg;
                    463:
                    464:        if (vp->v_type == VNON || vp->v_writecount == 0)
                    465:                return (0);
                    466:
                    467:        if (vget(vp, LK_EXCLUSIVE, p)) {
                    468:                return (0);
                    469:        }
                    470:
                    471:        error = getinoquota(VTOI(vp));
                    472:        vput(vp);
                    473:
                    474:        return (error);
                    475: }
                    476:
                    477: /*
                    478:  * Q_QUOTAON - set up a quota file for a particular file system.
                    479:  */
                    480: int
                    481: quotaon(struct proc *p, struct mount *mp, int type, caddr_t fname)
                    482: {
                    483:        struct ufsmount *ump = VFSTOUFS(mp);
                    484:        struct vnode *vp, **vpp;
                    485:        struct dquot *dq;
                    486:        int error;
                    487:        struct nameidata nd;
                    488:
                    489: #ifdef DIAGNOSTIC
                    490:        if (!vfs_isbusy(mp))
                    491:                panic ("quotaon: mount point not busy");
                    492: #endif
                    493:
                    494:        vpp = &ump->um_quotas[type];
                    495:        NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, p);
                    496:        if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0)
                    497:                return (error);
                    498:        vp = nd.ni_vp;
                    499:        VOP_UNLOCK(vp, 0, p);
                    500:        if (vp->v_type != VREG) {
                    501:                (void) vn_close(vp, FREAD|FWRITE, p->p_ucred, p);
                    502:                return (EACCES);
                    503:        }
                    504:        if (*vpp != vp)
                    505:                quotaoff(p, mp, type);
                    506:        ump->um_qflags[type] |= QTF_OPENING;
                    507:        mp->mnt_flag |= MNT_QUOTA;
                    508:        vp->v_flag |= VSYSTEM;
                    509:        *vpp = vp;
                    510:        /*
                    511:         * Save the credential of the process that turned on quotas.
                    512:         * Set up the time limits for this quota.
                    513:         */
                    514:        crhold(p->p_ucred);
                    515:        ump->um_cred[type] = p->p_ucred;
                    516:        ump->um_btime[type] = MAX_DQ_TIME;
                    517:        ump->um_itime[type] = MAX_IQ_TIME;
                    518:        if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
                    519:                if (dq->dq_btime > 0)
                    520:                        ump->um_btime[type] = dq->dq_btime;
                    521:                if (dq->dq_itime > 0)
                    522:                        ump->um_itime[type] = dq->dq_itime;
                    523:                dqrele(NULLVP, dq);
                    524:        }
                    525:        /*
                    526:         * Search vnodes associated with this mount point,
                    527:         * adding references to quota file being opened.
                    528:         * NB: only need to add dquot's for inodes being modified.
                    529:         */
                    530:        error = vfs_mount_foreach_vnode(mp, quotaon_vnode, p);
                    531:
                    532:        ump->um_qflags[type] &= ~QTF_OPENING;
                    533:        if (error)
                    534:                quotaoff(p, mp, type);
                    535:        return (error);
                    536: }
                    537:
                    538: struct quotaoff_arg {
                    539:        struct proc *p;
                    540:        int type;
                    541: };
                    542:
                    543: int
                    544: quotaoff_vnode(struct vnode *vp, void *arg)
                    545: {
                    546:        struct quotaoff_arg *qa = (struct quotaoff_arg *)arg;
                    547:        struct inode *ip;
                    548:        struct dquot *dq;
                    549:
                    550:        if (vp->v_type == VNON)
                    551:                return (0);
                    552:
                    553:
                    554:        if (vget(vp, LK_EXCLUSIVE, qa->p))
                    555:                return (0);
                    556:        ip = VTOI(vp);
                    557:        dq = ip->i_dquot[qa->type];
                    558:        ip->i_dquot[qa->type] = NODQUOT;
                    559:        dqrele(vp, dq);
                    560:        vput(vp);
                    561:        return (0);
                    562: }
                    563:
                    564: /*
                    565:  * Q_QUOTAOFF - turn off disk quotas for a filesystem.
                    566:  */
                    567: int
                    568: quotaoff(struct proc *p, struct mount *mp, int type)
                    569: {
                    570:        struct vnode *qvp;
                    571:        struct ufsmount *ump = VFSTOUFS(mp);
                    572:        struct quotaoff_arg qa;
                    573:        int error;
                    574:
                    575: #ifdef DIAGNOSTIC
                    576:        if (!vfs_isbusy(mp))
                    577:                panic ("quotaoff: mount point not busy");
                    578: #endif
                    579:        if ((qvp = ump->um_quotas[type]) == NULLVP)
                    580:                return (0);
                    581:        ump->um_qflags[type] |= QTF_CLOSING;
                    582:        /*
                    583:         * Search vnodes associated with this mount point,
                    584:         * deleting any references to quota file being closed.
                    585:         */
                    586:        qa.p = p;
                    587:        qa.type = type;
                    588:        vfs_mount_foreach_vnode(mp, quotaoff_vnode, &qa);
                    589:
                    590:        error = vn_close(qvp, FREAD|FWRITE, p->p_ucred, p);
                    591:        ump->um_quotas[type] = NULLVP;
                    592:        crfree(ump->um_cred[type]);
                    593:        ump->um_cred[type] = NOCRED;
                    594:        ump->um_qflags[type] &= ~QTF_CLOSING;
                    595:        for (type = 0; type < MAXQUOTAS; type++)
                    596:                if (ump->um_quotas[type] != NULLVP)
                    597:                        break;
                    598:        if (type == MAXQUOTAS)
                    599:                mp->mnt_flag &= ~MNT_QUOTA;
                    600:        return (error);
                    601: }
                    602:
                    603: /*
                    604:  * Q_GETQUOTA - return current values in a dqblk structure.
                    605:  */
                    606: int
                    607: getquota(struct mount *mp, u_long id, int type, caddr_t addr)
                    608: {
                    609:        struct dquot *dq;
                    610:        int error;
                    611:
                    612:        if ((error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq)) != 0)
                    613:                return (error);
                    614:        error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct dqblk));
                    615:        dqrele(NULLVP, dq);
                    616:        return (error);
                    617: }
                    618:
                    619: /*
                    620:  * Q_SETQUOTA - assign an entire dqblk structure.
                    621:  */
                    622: int
                    623: setquota(struct mount *mp, u_long id, int type, caddr_t addr)
                    624: {
                    625:        struct dquot *dq;
                    626:        struct dquot *ndq;
                    627:        struct ufsmount *ump = VFSTOUFS(mp);
                    628:        struct dqblk newlim;
                    629:        int error;
                    630:
                    631:        error = copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk));
                    632:        if (error)
                    633:                return (error);
                    634:        if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
                    635:                return (error);
                    636:        dq = ndq;
                    637:        while (dq->dq_flags & DQ_LOCK) {
                    638:                dq->dq_flags |= DQ_WANT;
                    639:                (void) tsleep(dq, PINOD+1, "setquota", 0);
                    640:        }
                    641:        /*
                    642:         * Copy all but the current values.
                    643:         * Reset time limit if previously had no soft limit or were
                    644:         * under it, but now have a soft limit and are over it.
                    645:         */
                    646:        newlim.dqb_curblocks = dq->dq_curblocks;
                    647:        newlim.dqb_curinodes = dq->dq_curinodes;
                    648:        if (dq->dq_id != 0) {
                    649:                newlim.dqb_btime = dq->dq_btime;
                    650:                newlim.dqb_itime = dq->dq_itime;
                    651:        }
                    652:        if (newlim.dqb_bsoftlimit &&
                    653:            dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
                    654:            (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
                    655:                newlim.dqb_btime = time_second + ump->um_btime[type];
                    656:        if (newlim.dqb_isoftlimit &&
                    657:            dq->dq_curinodes >= newlim.dqb_isoftlimit &&
                    658:            (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
                    659:                newlim.dqb_itime = time_second + ump->um_itime[type];
                    660:        dq->dq_dqb = newlim;
                    661:        if (dq->dq_curblocks < dq->dq_bsoftlimit)
                    662:                dq->dq_flags &= ~DQ_BLKS;
                    663:        if (dq->dq_curinodes < dq->dq_isoftlimit)
                    664:                dq->dq_flags &= ~DQ_INODS;
                    665:        if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
                    666:            dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
                    667:                dq->dq_flags |= DQ_FAKE;
                    668:        else
                    669:                dq->dq_flags &= ~DQ_FAKE;
                    670:        dq->dq_flags |= DQ_MOD;
                    671:        dqrele(NULLVP, dq);
                    672:        return (0);
                    673: }
                    674:
                    675: /*
                    676:  * Q_SETUSE - set current inode and block usage.
                    677:  */
                    678: int
                    679: setuse(struct mount *mp, u_long id, int type, caddr_t addr)
                    680: {
                    681:        struct dquot *dq;
                    682:        struct ufsmount *ump = VFSTOUFS(mp);
                    683:        struct dquot *ndq;
                    684:        struct dqblk usage;
                    685:        int error;
                    686:
                    687:        error = copyin(addr, (caddr_t)&usage, sizeof (struct dqblk));
                    688:        if (error)
                    689:                return (error);
                    690:        if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
                    691:                return (error);
                    692:        dq = ndq;
                    693:        while (dq->dq_flags & DQ_LOCK) {
                    694:                dq->dq_flags |= DQ_WANT;
                    695:                (void) tsleep(dq, PINOD+1, "setuse", 0);
                    696:        }
                    697:        /*
                    698:         * Reset time limit if have a soft limit and were
                    699:         * previously under it, but are now over it.
                    700:         */
                    701:        if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
                    702:            usage.dqb_curblocks >= dq->dq_bsoftlimit)
                    703:                dq->dq_btime = time_second + ump->um_btime[type];
                    704:        if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
                    705:            usage.dqb_curinodes >= dq->dq_isoftlimit)
                    706:                dq->dq_itime = time_second + ump->um_itime[type];
                    707:        dq->dq_curblocks = usage.dqb_curblocks;
                    708:        dq->dq_curinodes = usage.dqb_curinodes;
                    709:        if (dq->dq_curblocks < dq->dq_bsoftlimit)
                    710:                dq->dq_flags &= ~DQ_BLKS;
                    711:        if (dq->dq_curinodes < dq->dq_isoftlimit)
                    712:                dq->dq_flags &= ~DQ_INODS;
                    713:        dq->dq_flags |= DQ_MOD;
                    714:        dqrele(NULLVP, dq);
                    715:        return (0);
                    716: }
                    717:
                    718: int
                    719: qsync_vnode(struct vnode *vp, void *arg)
                    720: {
                    721:        int i;
                    722:        struct proc *p = curproc;
                    723:        struct dquot *dq;
                    724:
                    725:        if (vp->v_type == VNON)
                    726:                return (0);
                    727:
                    728:        if (vget(vp, LK_EXCLUSIVE | LK_NOWAIT, p))
                    729:                return (0);
                    730:
                    731:        for (i = 0; i < MAXQUOTAS; i++) {
                    732:                dq = VTOI(vp)->i_dquot[i];
                    733:                if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
                    734:                        dqsync(vp, dq);
                    735:        }
                    736:        vput(vp);
                    737:        return (0);
                    738: }
                    739:
                    740: /*
                    741:  * Q_SYNC - sync quota files to disk.
                    742:  */
                    743: int
                    744: qsync(struct mount *mp)
                    745: {
                    746:        struct ufsmount *ump = VFSTOUFS(mp);
                    747:        int i;
                    748:
                    749:        /*
                    750:         * Check if the mount point has any quotas.
                    751:         * If not, simply return.
                    752:         */
                    753:        for (i = 0; i < MAXQUOTAS; i++)
                    754:                if (ump->um_quotas[i] != NULLVP)
                    755:                        break;
                    756:        if (i == MAXQUOTAS)
                    757:                return (0);
                    758:        /*
                    759:         * Search vnodes associated with this mount point,
                    760:         * synchronizing any modified dquot structures.
                    761:         */
                    762:        vfs_mount_foreach_vnode(mp, qsync_vnode, NULL);
                    763:        return (0);
                    764: }
                    765:
                    766: /*
                    767:  * Code pertaining to management of the in-core dquot data structures.
                    768:  */
                    769: #define DQHASH(dqvp, id) \
                    770:        (&dqhashtbl[((((long)(dqvp)) >> 8) + id) & dqhash])
                    771: LIST_HEAD(dqhash, dquot) *dqhashtbl;
                    772: u_long dqhash;
                    773:
                    774: /*
                    775:  * Dquot free list.
                    776:  */
                    777: #define        DQUOTINC        5       /* minimum free dquots desired */
                    778: TAILQ_HEAD(dqfreelist, dquot) dqfreelist;
                    779: long numdquot, desireddquot = DQUOTINC;
                    780:
                    781: /*
                    782:  * Initialize the quota system.
                    783:  */
                    784: void
                    785: ufs_quota_init(void)
                    786: {
                    787:        dqhashtbl = hashinit(desiredvnodes, M_DQUOT, M_WAITOK, &dqhash);
                    788:        TAILQ_INIT(&dqfreelist);
                    789: }
                    790:
                    791: /*
                    792:  * Obtain a dquot structure for the specified identifier and quota file
                    793:  * reading the information from the file if necessary.
                    794:  */
                    795: int
                    796: dqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type,
                    797:     struct dquot **dqp)
                    798: {
                    799:        struct proc *p = curproc;
                    800:        struct dquot *dq;
                    801:        struct dqhash *dqh;
                    802:        struct vnode *dqvp;
                    803:        struct iovec aiov;
                    804:        struct uio auio;
                    805:        int error;
                    806:
                    807:        dqvp = ump->um_quotas[type];
                    808:        if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
                    809:                *dqp = NODQUOT;
                    810:                return (EINVAL);
                    811:        }
                    812:        /*
                    813:         * Check the cache first.
                    814:         */
                    815:        dqh = DQHASH(dqvp, id);
                    816:        LIST_FOREACH(dq, dqh, dq_hash) {
                    817:                if (dq->dq_id != id ||
                    818:                    dq->dq_vp != dqvp)
                    819:                        continue;
                    820:                /*
                    821:                 * Cache hit with no references.  Take
                    822:                 * the structure off the free list.
                    823:                 */
                    824:                if (dq->dq_cnt == 0)
                    825:                        TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
                    826:                dqref(dq);
                    827:                *dqp = dq;
                    828:                return (0);
                    829:        }
                    830:        /*
                    831:         * Not in cache, allocate a new one.
                    832:         */
                    833:        if (TAILQ_FIRST(&dqfreelist) == NODQUOT &&
                    834:            numdquot < MAXQUOTAS * desiredvnodes)
                    835:                desireddquot += DQUOTINC;
                    836:        if (numdquot < desireddquot) {
                    837:                dq = (struct dquot *)malloc(sizeof *dq, M_DQUOT, M_WAITOK);
                    838:                bzero((char *)dq, sizeof *dq);
                    839:                numdquot++;
                    840:        } else {
                    841:                if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) {
                    842:                        tablefull("dquot");
                    843:                        *dqp = NODQUOT;
                    844:                        return (EUSERS);
                    845:                }
                    846:                if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
                    847:                        panic("free dquot isn't");
                    848:                TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
                    849:                LIST_REMOVE(dq, dq_hash);
                    850:                crfree(dq->dq_cred);
                    851:                dq->dq_cred = NOCRED;
                    852:        }
                    853:        /*
                    854:         * Initialize the contents of the dquot structure.
                    855:         */
                    856:        if (vp != dqvp)
                    857:                vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p);
                    858:        LIST_INSERT_HEAD(dqh, dq, dq_hash);
                    859:        dqref(dq);
                    860:        dq->dq_flags = DQ_LOCK;
                    861:        dq->dq_id = id;
                    862:        dq->dq_vp = dqvp;
                    863:        dq->dq_type = type;
                    864:        crhold(ump->um_cred[type]);
                    865:        dq->dq_cred = ump->um_cred[type];
                    866:        auio.uio_iov = &aiov;
                    867:        auio.uio_iovcnt = 1;
                    868:        aiov.iov_base = (caddr_t)&dq->dq_dqb;
                    869:        aiov.iov_len = sizeof (struct dqblk);
                    870:        auio.uio_resid = sizeof (struct dqblk);
                    871:        auio.uio_offset = (off_t)(id * sizeof (struct dqblk));
                    872:        auio.uio_segflg = UIO_SYSSPACE;
                    873:        auio.uio_rw = UIO_READ;
                    874:        auio.uio_procp = (struct proc *)0;
                    875:        error = VOP_READ(dqvp, &auio, 0, dq->dq_cred);
                    876:        if (auio.uio_resid == sizeof(struct dqblk) && error == 0)
                    877:                bzero((caddr_t)&dq->dq_dqb, sizeof(struct dqblk));
                    878:        if (vp != dqvp)
                    879:                VOP_UNLOCK(dqvp, 0, p);
                    880:        if (dq->dq_flags & DQ_WANT)
                    881:                wakeup(dq);
                    882:        dq->dq_flags = 0;
                    883:        /*
                    884:         * I/O error in reading quota file, release
                    885:         * quota structure and reflect problem to caller.
                    886:         */
                    887:        if (error) {
                    888:                LIST_REMOVE(dq, dq_hash);
                    889:                dqrele(vp, dq);
                    890:                *dqp = NODQUOT;
                    891:                return (error);
                    892:        }
                    893:        /*
                    894:         * Check for no limit to enforce.
                    895:         * Initialize time values if necessary.
                    896:         */
                    897:        if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
                    898:            dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
                    899:                dq->dq_flags |= DQ_FAKE;
                    900:        if (dq->dq_id != 0) {
                    901:                if (dq->dq_btime == 0)
                    902:                        dq->dq_btime = time_second + ump->um_btime[type];
                    903:                if (dq->dq_itime == 0)
                    904:                        dq->dq_itime = time_second + ump->um_itime[type];
                    905:        }
                    906:        *dqp = dq;
                    907:        return (0);
                    908: }
                    909:
                    910: /*
                    911:  * Release a reference to a dquot.
                    912:  */
                    913: void
                    914: dqrele(struct vnode *vp, struct dquot *dq)
                    915: {
                    916:
                    917:        if (dq == NODQUOT)
                    918:                return;
                    919:        if (dq->dq_cnt > 1) {
                    920:                dq->dq_cnt--;
                    921:                return;
                    922:        }
                    923:        if (dq->dq_flags & DQ_MOD)
                    924:                (void) dqsync(vp, dq);
                    925:        if (--dq->dq_cnt > 0)
                    926:                return;
                    927:        TAILQ_INSERT_TAIL(&dqfreelist, dq, dq_freelist);
                    928: }
                    929:
                    930: /*
                    931:  * Update the disk quota in the quota file.
                    932:  */
                    933: int
                    934: dqsync(struct vnode *vp, struct dquot *dq)
                    935: {
                    936:        struct proc *p = curproc;
                    937:        struct vnode *dqvp;
                    938:        struct iovec aiov;
                    939:        struct uio auio;
                    940:        int error;
                    941:
                    942:        if (dq == NODQUOT)
                    943:                panic("dqsync: dquot");
                    944:        if ((dq->dq_flags & DQ_MOD) == 0)
                    945:                return (0);
                    946:        if ((dqvp = dq->dq_vp) == NULLVP)
                    947:                panic("dqsync: file");
                    948:
                    949:        if (vp != dqvp)
                    950:                vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY, p);
                    951:        while (dq->dq_flags & DQ_LOCK) {
                    952:                dq->dq_flags |= DQ_WANT;
                    953:                (void) tsleep(dq, PINOD+2, "dqsync", 0);
                    954:                if ((dq->dq_flags & DQ_MOD) == 0) {
                    955:                        if (vp != dqvp)
                    956:                                VOP_UNLOCK(dqvp, 0, p);
                    957:                        return (0);
                    958:                }
                    959:        }
                    960:        dq->dq_flags |= DQ_LOCK;
                    961:        auio.uio_iov = &aiov;
                    962:        auio.uio_iovcnt = 1;
                    963:        aiov.iov_base = (caddr_t)&dq->dq_dqb;
                    964:        aiov.iov_len = sizeof (struct dqblk);
                    965:        auio.uio_resid = sizeof (struct dqblk);
                    966:        auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct dqblk));
                    967:        auio.uio_segflg = UIO_SYSSPACE;
                    968:        auio.uio_rw = UIO_WRITE;
                    969:        auio.uio_procp = (struct proc *)0;
                    970:        error = VOP_WRITE(dqvp, &auio, 0, dq->dq_cred);
                    971:        if (auio.uio_resid && error == 0)
                    972:                error = EIO;
                    973:        if (dq->dq_flags & DQ_WANT)
                    974:                wakeup(dq);
                    975:        dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
                    976:        if (vp != dqvp)
                    977:                VOP_UNLOCK(dqvp, 0, p);
                    978:        return (error);
                    979: }
                    980:
                    981: int
                    982: ufs_quota_delete(struct inode *ip)
                    983: {
                    984:        struct vnode *vp = ITOV(ip);
                    985:        int i;
                    986:        for (i = 0; i < MAXQUOTAS; i++) {
                    987:                if (ip->i_dquot[i] != NODQUOT) {
                    988:                        dqrele(vp, ip->i_dquot[i]);
                    989:                        ip->i_dquot[i] = NODQUOT;
                    990:                }
                    991:        }
                    992:
                    993:        return (0);
                    994: }
                    995:
                    996: /*
                    997:  * Do operations associated with quotas
                    998:  */
                    999: int
                   1000: ufs_quotactl(struct mount *mp, int cmds, uid_t uid, caddr_t arg,
                   1001:     struct proc *p)
                   1002: {
                   1003:        int cmd, type, error;
                   1004:
                   1005:        if (uid == -1)
                   1006:                uid = p->p_cred->p_ruid;
                   1007:        cmd = cmds >> SUBCMDSHIFT;
                   1008:
                   1009:        switch (cmd) {
                   1010:        case Q_SYNC:
                   1011:                break;
                   1012:        case Q_GETQUOTA:
                   1013:                if (uid == p->p_cred->p_ruid)
                   1014:                        break;
                   1015:                /* FALLTHROUGH */
                   1016:        default:
                   1017:                if ((error = suser(p, 0)) != 0)
                   1018:                        return (error);
                   1019:        }
                   1020:
                   1021:        type = cmds & SUBCMDMASK;
                   1022:        if ((u_int)type >= MAXQUOTAS)
                   1023:                return (EINVAL);
                   1024:
                   1025:        if (vfs_busy(mp, VB_READ|VB_NOWAIT))
                   1026:                return (0);
                   1027:
                   1028:
                   1029:        switch (cmd) {
                   1030:
                   1031:        case Q_QUOTAON:
                   1032:                error = quotaon(p, mp, type, arg);
                   1033:                break;
                   1034:
                   1035:        case Q_QUOTAOFF:
                   1036:                error = quotaoff(p, mp, type);
                   1037:                break;
                   1038:
                   1039:        case Q_SETQUOTA:
                   1040:                error = setquota(mp, uid, type, arg) ;
                   1041:                break;
                   1042:
                   1043:        case Q_SETUSE:
                   1044:                error = setuse(mp, uid, type, arg);
                   1045:                break;
                   1046:
                   1047:        case Q_GETQUOTA:
                   1048:                error = getquota(mp, uid, type, arg);
                   1049:                break;
                   1050:
                   1051:        case Q_SYNC:
                   1052:                error = qsync(mp);
                   1053:                break;
                   1054:
                   1055:        default:
                   1056:                error = EINVAL;
                   1057:                break;
                   1058:        }
                   1059:
                   1060:        vfs_unbusy(mp);
                   1061:        return (error);
                   1062: }

CVSweb