[BACK]Return to ufs_quota.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / ufs / ufs

Annotation of src/sys/ufs/ufs/ufs_quota.c, Revision 1.36

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

CVSweb <webmaster@jp.NetBSD.org>