Example #1
0
/*
 * Q_SETUSE - set current inode and block usage.
 */
static int
_setuse(struct thread *td, struct mount *mp, u_long id, int type,
    struct dqblk64 *dqb)
{
	struct dquot *dq;
	struct ufsmount *ump;
	struct dquot *ndq;
	struct dqblk64 usage;
	int error;

	error = priv_check(td, PRIV_UFS_SETUSE);
	if (error)
		return (error);

	usage = *dqb;

	ump = VFSTOUFS(mp);
	ndq = NODQUOT;

	error = dqget(NULLVP, id, ump, type, &ndq);
	if (error)
		return (error);
	dq = ndq;
	DQI_LOCK(dq);
	DQI_WAIT(dq, PINOD+1, "setuse");
	/*
	 * Reset time limit if have a soft limit and were
	 * previously under it, but are now over it.
	 */
	if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
	    usage.dqb_curblocks >= dq->dq_bsoftlimit)
		dq->dq_btime = time_second + ump->um_btime[type];
	if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
	    usage.dqb_curinodes >= dq->dq_isoftlimit)
		dq->dq_itime = time_second + ump->um_itime[type];
	dq->dq_curblocks = usage.dqb_curblocks;
	dq->dq_curinodes = usage.dqb_curinodes;
	if (dq->dq_curblocks < dq->dq_bsoftlimit)
		dq->dq_flags &= ~DQ_BLKS;
	if (dq->dq_curinodes < dq->dq_isoftlimit)
		dq->dq_flags &= ~DQ_INODS;
	dq->dq_flags |= DQ_MOD;
	DQI_UNLOCK(dq);
	dqrele(NULLVP, dq);
	return (0);
}
Example #2
0
/*
 * Check the inode limit, applying corrective action.
 */
int
chkiq(struct inode *ip, int change, struct ucred *cred, int flags)
{
    struct dquot *dq;
    int i, error, warn, do_check;

#ifdef DIAGNOSTIC
    if ((flags & CHOWN) == 0)
        chkdquot(ip);
#endif
    if (change == 0)
        return (0);
    if (change < 0) {
        for (i = 0; i < MAXQUOTAS; i++) {
            if ((dq = ip->i_dquot[i]) == NODQUOT)
                continue;
            DQI_LOCK(dq);
            DQI_WAIT(dq, PINOD+1, "chkiq1");
            if (dq->dq_curinodes >= -change)
                dq->dq_curinodes += change;
            else
                dq->dq_curinodes = 0;
            dq->dq_flags &= ~DQ_INODS;
            dq->dq_flags |= DQ_MOD;
            DQI_UNLOCK(dq);
        }
        return (0);
    }
    if ((flags & FORCE) == 0 &&
            priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
        do_check = 1;
    else
        do_check = 0;
    for (i = 0; i < MAXQUOTAS; i++) {
        if ((dq = ip->i_dquot[i]) == NODQUOT)
            continue;
        warn = 0;
        DQI_LOCK(dq);
        DQI_WAIT(dq, PINOD+1, "chkiq2");
        if (do_check) {
            error = chkiqchg(ip, change, cred, i, &warn);
            if (error) {
                /*
                 * Roll back user quota changes when
                 * group quota failed.
                 */
                while (i > 0) {
                    --i;
                    dq = ip->i_dquot[i];
                    if (dq == NODQUOT)
                        continue;
                    DQI_LOCK(dq);
                    DQI_WAIT(dq, PINOD+1, "chkiq3");
                    if (dq->dq_curinodes >= change)
                        dq->dq_curinodes -= change;
                    else
                        dq->dq_curinodes = 0;
                    dq->dq_flags &= ~DQ_INODS;
                    dq->dq_flags |= DQ_MOD;
                    DQI_UNLOCK(dq);
                }
                return (error);
            }
        }
        /* Reset timer when crossing soft limit */
        if (dq->dq_curinodes + change >= dq->dq_isoftlimit &&
                dq->dq_curinodes < dq->dq_isoftlimit)
            dq->dq_itime = time_second + ITOUMP(ip)->um_itime[i];
        dq->dq_curinodes += change;
        dq->dq_flags |= DQ_MOD;
        DQI_UNLOCK(dq);
        if (warn)
            uprintf("\n%s: warning, %s inode quota exceeded\n",
                    ITOVFS(ip)->mnt_stat.f_mntonname,
                    quotatypes[i]);
    }
    return (0);
}
Example #3
0
/*
 * Update the disk quota in the quota file.
 */
static int
dqsync(struct vnode *vp, struct dquot *dq)
{
    uint8_t buf[sizeof(struct dqblk64)];
    off_t base, recsize;
    struct vnode *dqvp;
    struct iovec aiov;
    struct uio auio;
    int error;
    struct mount *mp;
    struct ufsmount *ump;

#ifdef DEBUG_VFS_LOCKS
    if (vp != NULL)
        ASSERT_VOP_ELOCKED(vp, "dqsync");
#endif

    mp = NULL;
    error = 0;
    if (dq == NODQUOT)
        panic("dqsync: dquot");
    if ((ump = dq->dq_ump) == NULL)
        return (0);
    UFS_LOCK(ump);
    if ((dqvp = ump->um_quotas[dq->dq_type]) == NULLVP) {
        if (vp == NULL) {
            UFS_UNLOCK(ump);
            return (0);
        } else
            panic("dqsync: file");
    }
    vref(dqvp);
    UFS_UNLOCK(ump);

    DQI_LOCK(dq);
    if ((dq->dq_flags & DQ_MOD) == 0) {
        DQI_UNLOCK(dq);
        vrele(dqvp);
        return (0);
    }
    DQI_UNLOCK(dq);

    (void) vn_start_secondary_write(dqvp, &mp, V_WAIT);
    if (vp != dqvp)
        vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);

    DQI_LOCK(dq);
    DQI_WAIT(dq, PINOD+2, "dqsync");
    if ((dq->dq_flags & DQ_MOD) == 0)
        goto out;
    dq->dq_flags |= DQ_LOCK;
    DQI_UNLOCK(dq);

    /*
     * Write the quota record to the quota file, performing any
     * necessary conversions.  See dqget() for additional details.
     */
    if (ump->um_qflags[dq->dq_type] & QTF_64BIT) {
        dq_dqb64(dq, (struct dqblk64 *)buf);
        recsize = sizeof(struct dqblk64);
        base = sizeof(struct dqhdr64);
    } else {
        dq_dqb32(dq, (struct dqblk32 *)buf);
        recsize = sizeof(struct dqblk32);
        base = 0;
    }

    auio.uio_iov = &aiov;
    auio.uio_iovcnt = 1;
    aiov.iov_base = buf;
    aiov.iov_len = recsize;
    auio.uio_resid = recsize;
    auio.uio_offset = base + dq->dq_id * recsize;
    auio.uio_segflg = UIO_SYSSPACE;
    auio.uio_rw = UIO_WRITE;
    auio.uio_td = (struct thread *)0;
    error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
    if (auio.uio_resid && error == 0)
        error = EIO;

    DQI_LOCK(dq);
    DQI_WAKEUP(dq);
    dq->dq_flags &= ~DQ_MOD;
out:
    DQI_UNLOCK(dq);
    if (vp != dqvp)
        vput(dqvp);
    else
        vrele(dqvp);
    vn_finished_secondary_write(mp);
    return (error);
}
Example #4
0
/*
 * Update disk usage, and take corrective action.
 */
int
chkdq(struct inode *ip, ufs2_daddr_t change, struct ucred *cred, int flags)
{
    struct dquot *dq;
    ufs2_daddr_t ncurblocks;
    struct vnode *vp = ITOV(ip);
    int i, error, warn, do_check;

    /*
     * Disk quotas must be turned off for system files.  Currently
     * snapshot and quota files.
     */
    if ((vp->v_vflag & VV_SYSTEM) != 0)
        return (0);
    /*
     * XXX: Turn off quotas for files with a negative UID or GID.
     * This prevents the creation of 100GB+ quota files.
     */
    if ((int)ip->i_uid < 0 || (int)ip->i_gid < 0)
        return (0);
#ifdef DIAGNOSTIC
    if ((flags & CHOWN) == 0)
        chkdquot(ip);
#endif
    if (change == 0)
        return (0);
    if (change < 0) {
        for (i = 0; i < MAXQUOTAS; i++) {
            if ((dq = ip->i_dquot[i]) == NODQUOT)
                continue;
            DQI_LOCK(dq);
            DQI_WAIT(dq, PINOD+1, "chkdq1");
            ncurblocks = dq->dq_curblocks + change;
            if (ncurblocks >= 0)
                dq->dq_curblocks = ncurblocks;
            else
                dq->dq_curblocks = 0;
            dq->dq_flags &= ~DQ_BLKS;
            dq->dq_flags |= DQ_MOD;
            DQI_UNLOCK(dq);
        }
        return (0);
    }
    if ((flags & FORCE) == 0 &&
            priv_check_cred(cred, PRIV_VFS_EXCEEDQUOTA, 0))
        do_check = 1;
    else
        do_check = 0;
    for (i = 0; i < MAXQUOTAS; i++) {
        if ((dq = ip->i_dquot[i]) == NODQUOT)
            continue;
        warn = 0;
        DQI_LOCK(dq);
        DQI_WAIT(dq, PINOD+1, "chkdq2");
        if (do_check) {
            error = chkdqchg(ip, change, cred, i, &warn);
            if (error) {
                /*
                 * Roll back user quota changes when
                 * group quota failed.
                 */
                while (i > 0) {
                    --i;
                    dq = ip->i_dquot[i];
                    if (dq == NODQUOT)
                        continue;
                    DQI_LOCK(dq);
                    DQI_WAIT(dq, PINOD+1, "chkdq3");
                    ncurblocks = dq->dq_curblocks - change;
                    if (ncurblocks >= 0)
                        dq->dq_curblocks = ncurblocks;
                    else
                        dq->dq_curblocks = 0;
                    dq->dq_flags &= ~DQ_BLKS;
                    dq->dq_flags |= DQ_MOD;
                    DQI_UNLOCK(dq);
                }
                return (error);
            }
        }
        /* Reset timer when crossing soft limit */
        if (dq->dq_curblocks + change >= dq->dq_bsoftlimit &&
                dq->dq_curblocks < dq->dq_bsoftlimit)
            dq->dq_btime = time_second + ITOUMP(ip)->um_btime[i];
        dq->dq_curblocks += change;
        dq->dq_flags |= DQ_MOD;
        DQI_UNLOCK(dq);
        if (warn)
            uprintf("\n%s: warning, %s disk quota exceeded\n",
                    ITOVFS(ip)->mnt_stat.f_mntonname,
                    quotatypes[i]);
    }
    return (0);
}
Example #5
0
/*
 * Obtain a dquot structure for the specified identifier and quota file
 * reading the information from the file if necessary.
 */
static int
dqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type,
      struct dquot **dqp)
{
    uint8_t buf[sizeof(struct dqblk64)];
    off_t base, recsize;
    struct dquot *dq, *dq1;
    struct dqhash *dqh;
    struct vnode *dqvp;
    struct iovec aiov;
    struct uio auio;
    int dqvplocked, error;

#ifdef DEBUG_VFS_LOCKS
    if (vp != NULLVP)
        ASSERT_VOP_ELOCKED(vp, "dqget");
#endif

    if (vp != NULLVP && *dqp != NODQUOT) {
        return (0);
    }

    /* XXX: Disallow negative id values to prevent the
    * creation of 100GB+ quota data files.
    */
    if ((int)id < 0)
        return (EINVAL);

    UFS_LOCK(ump);
    dqvp = ump->um_quotas[type];
    if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
        *dqp = NODQUOT;
        UFS_UNLOCK(ump);
        return (EINVAL);
    }
    vref(dqvp);
    UFS_UNLOCK(ump);
    error = 0;
    dqvplocked = 0;

    /*
     * Check the cache first.
     */
    dqh = DQHASH(dqvp, id);
    DQH_LOCK();
    dq = dqhashfind(dqh, id, dqvp);
    if (dq != NULL) {
        DQH_UNLOCK();
hfound:
        DQI_LOCK(dq);
        DQI_WAIT(dq, PINOD+1, "dqget");
        DQI_UNLOCK(dq);
        if (dq->dq_ump == NULL) {
            dqrele(vp, dq);
            dq = NODQUOT;
            error = EIO;
        }
        *dqp = dq;
        if (dqvplocked)
            vput(dqvp);
        else
            vrele(dqvp);
        return (error);
    }

    /*
     * Quota vnode lock is before DQ_LOCK. Acquire dqvp lock there
     * since new dq will appear on the hash chain DQ_LOCKed.
     */
    if (vp != dqvp) {
        DQH_UNLOCK();
        vn_lock(dqvp, LK_SHARED | LK_RETRY);
        dqvplocked = 1;
        DQH_LOCK();
        /*
         * Recheck the cache after sleep for quota vnode lock.
         */
        dq = dqhashfind(dqh, id, dqvp);
        if (dq != NULL) {
            DQH_UNLOCK();
            goto hfound;
        }
    }

    /*
     * Not in cache, allocate a new one or take it from the
     * free list.
     */
    if (TAILQ_FIRST(&dqfreelist) == NODQUOT &&
            numdquot < MAXQUOTAS * desiredvnodes)
        desireddquot += DQUOTINC;
    if (numdquot < desireddquot) {
        numdquot++;
        DQH_UNLOCK();
        dq1 = malloc(sizeof *dq1, M_DQUOT, M_WAITOK | M_ZERO);
        mtx_init(&dq1->dq_lock, "dqlock", NULL, MTX_DEF);
        DQH_LOCK();
        /*
         * Recheck the cache after sleep for memory.
         */
        dq = dqhashfind(dqh, id, dqvp);
        if (dq != NULL) {
            numdquot--;
            DQH_UNLOCK();
            mtx_destroy(&dq1->dq_lock);
            free(dq1, M_DQUOT);
            goto hfound;
        }
        dq = dq1;
    } else {
        if ((dq = TAILQ_FIRST(&dqfreelist)) == NULL) {
            DQH_UNLOCK();
            tablefull("dquot");
            *dqp = NODQUOT;
            if (dqvplocked)
                vput(dqvp);
            else
                vrele(dqvp);
            return (EUSERS);
        }
        if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
            panic("dqget: free dquot isn't %p", dq);
        TAILQ_REMOVE(&dqfreelist, dq, dq_freelist);
        if (dq->dq_ump != NULL)
            LIST_REMOVE(dq, dq_hash);
    }

    /*
     * Dq is put into hash already locked to prevent parallel
     * usage while it is being read from file.
     */
    dq->dq_flags = DQ_LOCK;
    dq->dq_id = id;
    dq->dq_type = type;
    dq->dq_ump = ump;
    LIST_INSERT_HEAD(dqh, dq, dq_hash);
    DQREF(dq);
    DQH_UNLOCK();

    /*
     * Read the requested quota record from the quota file, performing
     * any necessary conversions.
     */
    if (ump->um_qflags[type] & QTF_64BIT) {
        recsize = sizeof(struct dqblk64);
        base = sizeof(struct dqhdr64);
    } else {
        recsize = sizeof(struct dqblk32);
        base = 0;
    }
    auio.uio_iov = &aiov;
    auio.uio_iovcnt = 1;
    aiov.iov_base = buf;
    aiov.iov_len = recsize;
    auio.uio_resid = recsize;
    auio.uio_offset = base + id * recsize;
    auio.uio_segflg = UIO_SYSSPACE;
    auio.uio_rw = UIO_READ;
    auio.uio_td = (struct thread *)0;

    error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
    if (auio.uio_resid == recsize && error == 0) {
        bzero(&dq->dq_dqb, sizeof(dq->dq_dqb));
    } else {
        if (ump->um_qflags[type] & QTF_64BIT)
            dqb64_dq((struct dqblk64 *)buf, dq);
        else
            dqb32_dq((struct dqblk32 *)buf, dq);
    }
    if (dqvplocked)
        vput(dqvp);
    else
        vrele(dqvp);
    /*
     * I/O error in reading quota file, release
     * quota structure and reflect problem to caller.
     */
    if (error) {
        DQH_LOCK();
        dq->dq_ump = NULL;
        LIST_REMOVE(dq, dq_hash);
        DQH_UNLOCK();
        DQI_LOCK(dq);
        if (dq->dq_flags & DQ_WANT)
            wakeup(dq);
        dq->dq_flags = 0;
        DQI_UNLOCK(dq);
        dqrele(vp, dq);
        *dqp = NODQUOT;
        return (error);
    }
    DQI_LOCK(dq);
    /*
     * Check for no limit to enforce.
     * Initialize time values if necessary.
     */
    if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
            dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
        dq->dq_flags |= DQ_FAKE;
    if (dq->dq_id != 0) {
        if (dq->dq_btime == 0) {
            dq->dq_btime = time_second + ump->um_btime[type];
            if (dq->dq_bsoftlimit &&
                    dq->dq_curblocks >= dq->dq_bsoftlimit)
                dq->dq_flags |= DQ_MOD;
        }
        if (dq->dq_itime == 0) {
            dq->dq_itime = time_second + ump->um_itime[type];
            if (dq->dq_isoftlimit &&
                    dq->dq_curinodes >= dq->dq_isoftlimit)
                dq->dq_flags |= DQ_MOD;
        }
    }
    DQI_WAKEUP(dq);
    DQI_UNLOCK(dq);
    *dqp = dq;
    return (0);
}