Ejemplo n.º 1
0
/*
 * Q_SETQUOTA - assign an entire dqblk structure.
 */
static int
_setquota(struct thread *td, struct mount *mp, u_long id, int type,
          struct dqblk64 *dqb)
{
    struct dquot *dq;
    struct dquot *ndq;
    struct ufsmount *ump;
    struct dqblk64 newlim;
    int error;

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

    newlim = *dqb;

    ndq = NODQUOT;
    ump = VFSTOUFS(mp);

    error = dqget(NULLVP, id, ump, type, &ndq);
    if (error)
        return (error);
    dq = ndq;
    DQI_LOCK(dq);
    DQI_WAIT(dq, PINOD+1, "setqta");
    /*
     * Copy all but the current values.
     * Reset time limit if previously had no soft limit or were
     * under it, but now have a soft limit and are over it.
     */
    newlim.dqb_curblocks = dq->dq_curblocks;
    newlim.dqb_curinodes = dq->dq_curinodes;
    if (dq->dq_id != 0) {
        newlim.dqb_btime = dq->dq_btime;
        newlim.dqb_itime = dq->dq_itime;
    }
    if (newlim.dqb_bsoftlimit &&
            dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
            (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
        newlim.dqb_btime = time_second + ump->um_btime[type];
    if (newlim.dqb_isoftlimit &&
            dq->dq_curinodes >= newlim.dqb_isoftlimit &&
            (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
        newlim.dqb_itime = time_second + ump->um_itime[type];
    dq->dq_dqb = newlim;
    if (dq->dq_curblocks < dq->dq_bsoftlimit)
        dq->dq_flags &= ~DQ_BLKS;
    if (dq->dq_curinodes < dq->dq_isoftlimit)
        dq->dq_flags &= ~DQ_INODS;
    if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
            dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
        dq->dq_flags |= DQ_FAKE;
    else
        dq->dq_flags &= ~DQ_FAKE;
    dq->dq_flags |= DQ_MOD;
    DQI_UNLOCK(dq);
    dqrele(NULLVP, dq);
    return (0);
}
Ejemplo n.º 2
0
/*
 * Q_GETQUOTA - return current values in a dqblk structure.
 */
int
getquota(struct mount *mp, u_long id, int type, void *addr)
{
	struct dquot *dq;
	int error;

	if ((error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq)) != 0)
		return (error);
	error = copyout((void *)&dq->dq_dqb, addr, sizeof (struct dqblk));
	dqrele(NULLVP, dq);
	return (error);
}
Ejemplo n.º 3
0
/*
 * Set up the quotas for an inode.
 *
 * This routine completely defines the semantics of quotas.
 * If other criterion want to be used to establish quotas, the
 * MAXQUOTAS value in quota.h should be increased, and the
 * additional dquots set up here.
 */
int
getinoquota(struct inode *ip)
{
    struct ufsmount *ump;
    struct vnode *vp;
    int error;

    vp = ITOV(ip);

    /*
     * 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);
    ump = VFSTOUFS(vp->v_mount);
    /*
     * Set up the user quota based on file uid.
     * EINVAL means that quotas are not enabled.
     */
    if ((error =
                dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) &&
            error != EINVAL)
        return (error);
    /*
     * Set up the group quota based on file gid.
     * EINVAL means that quotas are not enabled.
     */
    if ((error =
                dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&
            error != EINVAL)
        return (error);
    return (0);
}
Ejemplo n.º 4
0
/*
 * Q_SETQUOTA - assign an entire dqblk structure.
 */
int
setquota(struct mount *mp, u_long id, int type, void *addr)
{
	struct dquot *dq;
	struct dquot *ndq;
	struct ufsmount *ump = VFSTOUFS(mp);
	struct dqblk newlim;
	int error;

	error = copyin(addr, (void *)&newlim, sizeof (struct dqblk));
	if (error)
		return (error);
	if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
		return (error);
	dq = ndq;
	mutex_enter(&dq->dq_interlock);
	/*
	 * Copy all but the current values.
	 * Reset time limit if previously had no soft limit or were
	 * under it, but now have a soft limit and are over it.
	 */
	newlim.dqb_curblocks = dq->dq_curblocks;
	newlim.dqb_curinodes = dq->dq_curinodes;
	if (dq->dq_id != 0) {
		newlim.dqb_btime = dq->dq_btime;
		newlim.dqb_itime = dq->dq_itime;
	}
	if (newlim.dqb_bsoftlimit &&
	    dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
	    (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
		newlim.dqb_btime = time_second + ump->um_btime[type];
	if (newlim.dqb_isoftlimit &&
	    dq->dq_curinodes >= newlim.dqb_isoftlimit &&
	    (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
		newlim.dqb_itime = time_second + ump->um_itime[type];
	dq->dq_dqb = newlim;
	if (dq->dq_curblocks < dq->dq_bsoftlimit)
		dq->dq_flags &= ~DQ_BLKS;
	if (dq->dq_curinodes < dq->dq_isoftlimit)
		dq->dq_flags &= ~DQ_INODS;
	if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
	    dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
		dq->dq_flags |= DQ_FAKE;
	else
		dq->dq_flags &= ~DQ_FAKE;
	dq->dq_flags |= DQ_MOD;
	mutex_exit(&dq->dq_interlock);
	dqrele(NULLVP, dq);
	return (0);
}
Ejemplo n.º 5
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);
}
Ejemplo n.º 6
0
/*
 * Set up the quotas for an inode.
 *
 * This routine completely defines the semantics of quotas.
 * If other criterion want to be used to establish quotas, the
 * MAXQUOTAS value in quotas.h should be increased, and the
 * additional dquots set up here.
 */
int
getinoquota(struct inode *ip)
{
	struct ufsmount *ump = ip->i_ump;
	struct vnode *vp = ITOV(ip);
	int i, error;
	u_int32_t ino_ids[MAXQUOTAS];

	/*
	 * To avoid deadlocks never update quotas for quota files
	 * on the same file system
	 */
	for (i = 0; i < MAXQUOTAS; i++)
		if (ITOV(ip) == ump->um_quotas[i])
			return 0;

	ino_ids[USRQUOTA] = ip->i_uid;
	ino_ids[GRPQUOTA] = ip->i_gid;
	for (i = 0; i < MAXQUOTAS; i++) {
		/*
		 * If the file id changed the quota needs update.
		 */
		if (ip->i_dquot[i] != NODQUOT &&
		    ip->i_dquot[i]->dq_id != ino_ids[i]) {
			dqrele(ITOV(ip), ip->i_dquot[i]);
			ip->i_dquot[i] = NODQUOT;
		}
		/*
		 * Set up the quota based on file id.
		 * EINVAL means that quotas are not enabled.
		 */
		if (ip->i_dquot[i] == NODQUOT &&
		    (error = dqget(vp, ino_ids[i], ump, i, &ip->i_dquot[i])) &&
		    error != EINVAL)
			return (error);
	}
	return 0;
}
Ejemplo n.º 7
0
/*
 * Q_GETQUOTA - return current values in a dqblk structure.
 */
static int
_getquota(struct thread *td, struct mount *mp, u_long id, int type,
          struct dqblk64 *dqb)
{
    struct dquot *dq;
    int error;

    switch (type) {
    case USRQUOTA:
        if ((td->td_ucred->cr_uid != id) && !unprivileged_get_quota) {
            error = priv_check(td, PRIV_VFS_GETQUOTA);
            if (error)
                return (error);
        }
        break;

    case GRPQUOTA:
        if (!groupmember(id, td->td_ucred) &&
                !unprivileged_get_quota) {
            error = priv_check(td, PRIV_VFS_GETQUOTA);
            if (error)
                return (error);
        }
        break;

    default:
        return (EINVAL);
    }

    dq = NODQUOT;
    error = dqget(NULLVP, id, VFSTOUFS(mp), type, &dq);
    if (error)
        return (error);
    *dqb = dq->dq_dqb;
    dqrele(NULLVP, dq);
    return (error);
}
Ejemplo n.º 8
0
/*
 * Q_SETUSE - set current inode and block usage.
 */
int
setuse(struct mount *mp, u_long id, int type, void *addr)
{
	struct dquot *dq;
	struct ufsmount *ump = VFSTOUFS(mp);
	struct dquot *ndq;
	struct dqblk usage;
	int error;

	error = copyin(addr, (void *)&usage, sizeof (struct dqblk));
	if (error)
		return (error);
	if ((error = dqget(NULLVP, id, ump, type, &ndq)) != 0)
		return (error);
	dq = ndq;
	mutex_enter(&dq->dq_interlock);
	/*
	 * 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;
	mutex_exit(&dq->dq_interlock);
	dqrele(NULLVP, dq);
	return (0);
}
Ejemplo n.º 9
0
/*
 * Q_QUOTAON - set up a quota file for a particular filesystem.
 */
int
quotaon(struct thread *td, struct mount *mp, int type, void *fname)
{
    struct ufsmount *ump;
    struct vnode *vp, **vpp;
    struct vnode *mvp;
    struct dquot *dq;
    int error, flags;
    struct nameidata nd;

    error = priv_check(td, PRIV_UFS_QUOTAON);
    if (error != 0) {
        vfs_unbusy(mp);
        return (error);
    }

    if ((mp->mnt_flag & MNT_RDONLY) != 0) {
        vfs_unbusy(mp);
        return (EROFS);
    }

    ump = VFSTOUFS(mp);
    dq = NODQUOT;

    NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname, td);
    flags = FREAD | FWRITE;
    vfs_ref(mp);
    vfs_unbusy(mp);
    error = vn_open(&nd, &flags, 0, NULL);
    if (error != 0) {
        vfs_rel(mp);
        return (error);
    }
    NDFREE(&nd, NDF_ONLY_PNBUF);
    vp = nd.ni_vp;
    error = vfs_busy(mp, MBF_NOWAIT);
    vfs_rel(mp);
    if (error == 0) {
        if (vp->v_type != VREG) {
            error = EACCES;
            vfs_unbusy(mp);
        }
    }
    if (error != 0) {
        VOP_UNLOCK(vp, 0);
        (void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
        return (error);
    }

    UFS_LOCK(ump);
    if ((ump->um_qflags[type] & (QTF_OPENING|QTF_CLOSING)) != 0) {
        UFS_UNLOCK(ump);
        VOP_UNLOCK(vp, 0);
        (void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
        vfs_unbusy(mp);
        return (EALREADY);
    }
    ump->um_qflags[type] |= QTF_OPENING|QTF_CLOSING;
    UFS_UNLOCK(ump);
    if ((error = dqopen(vp, ump, type)) != 0) {
        VOP_UNLOCK(vp, 0);
        UFS_LOCK(ump);
        ump->um_qflags[type] &= ~(QTF_OPENING|QTF_CLOSING);
        UFS_UNLOCK(ump);
        (void) vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
        vfs_unbusy(mp);
        return (error);
    }
    VOP_UNLOCK(vp, 0);
    MNT_ILOCK(mp);
    mp->mnt_flag |= MNT_QUOTA;
    MNT_IUNLOCK(mp);

    vpp = &ump->um_quotas[type];
    if (*vpp != vp)
        quotaoff1(td, mp, type);

    /*
     * When the directory vnode containing the quota file is
     * inactivated, due to the shared lookup of the quota file
     * vput()ing the dvp, the qsyncvp() call for the containing
     * directory would try to acquire the quota lock exclusive.
     * At the same time, lookup already locked the quota vnode
     * shared.  Mark the quota vnode lock as allowing recursion
     * and automatically converting shared locks to exclusive.
     *
     * Also mark quota vnode as system.
     */
    vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
    vp->v_vflag |= VV_SYSTEM;
    VN_LOCK_AREC(vp);
    VN_LOCK_DSHARE(vp);
    VOP_UNLOCK(vp, 0);
    *vpp = vp;
    /*
     * Save the credential of the process that turned on quotas.
     * Set up the time limits for this quota.
     */
    ump->um_cred[type] = crhold(td->td_ucred);
    ump->um_btime[type] = MAX_DQ_TIME;
    ump->um_itime[type] = MAX_IQ_TIME;
    if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
        if (dq->dq_btime > 0)
            ump->um_btime[type] = dq->dq_btime;
        if (dq->dq_itime > 0)
            ump->um_itime[type] = dq->dq_itime;
        dqrele(NULLVP, dq);
    }
    /*
     * Allow the getdq from getinoquota below to read the quota
     * from file.
     */
    UFS_LOCK(ump);
    ump->um_qflags[type] &= ~QTF_CLOSING;
    UFS_UNLOCK(ump);
    /*
     * Search vnodes associated with this mount point,
     * adding references to quota file being opened.
     * NB: only need to add dquot's for inodes being modified.
     */
again:
    MNT_VNODE_FOREACH_ALL(vp, mp, mvp) {
        if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, td)) {
            MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
            goto again;
        }
        if (vp->v_type == VNON || vp->v_writecount == 0) {
            VOP_UNLOCK(vp, 0);
            vrele(vp);
            continue;
        }
        error = getinoquota(VTOI(vp));
        VOP_UNLOCK(vp, 0);
        vrele(vp);
        if (error) {
            MNT_VNODE_FOREACH_ALL_ABORT(mp, mvp);
            break;
        }
    }

    if (error)
        quotaoff_inchange(td, mp, type);
    UFS_LOCK(ump);
    ump->um_qflags[type] &= ~QTF_OPENING;
    KASSERT((ump->um_qflags[type] & QTF_CLOSING) == 0,
            ("quotaon: leaking flags"));
    UFS_UNLOCK(ump);

    vfs_unbusy(mp);
    return (error);
}
Ejemplo n.º 10
0
/*
 * Q_QUOTAON - set up a quota file for a particular file system.
 */
int
quotaon(struct lwp *l, struct mount *mp, int type, void *fname)
{
	struct ufsmount *ump = VFSTOUFS(mp);
	struct vnode *vp, **vpp, *mvp;
	struct dquot *dq;
	int error;
	struct nameidata nd;

	/* XXX XXX XXX */
	if (mp->mnt_wapbl != NULL) {
		printf("%s: quotas cannot yet be used with -o log\n",
		    mp->mnt_stat.f_mntonname);
		return (EOPNOTSUPP);
	}

	vpp = &ump->um_quotas[type];
	NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, fname);
	if ((error = vn_open(&nd, FREAD|FWRITE, 0)) != 0)
		return (error);
	vp = nd.ni_vp;
	VOP_UNLOCK(vp, 0);
	if (vp->v_type != VREG) {
		(void) vn_close(vp, FREAD|FWRITE, l->l_cred);
		return (EACCES);
	}
	if (*vpp != vp)
		quotaoff(l, mp, type);
	mutex_enter(&dqlock);
	while ((ump->um_qflags[type] & (QTF_CLOSING | QTF_OPENING)) != 0)
		cv_wait(&dqcv, &dqlock);
	ump->um_qflags[type] |= QTF_OPENING;
	mutex_exit(&dqlock);
	mp->mnt_flag |= MNT_QUOTA;
	vp->v_vflag |= VV_SYSTEM;	/* XXXSMP */
	*vpp = vp;
	/*
	 * Save the credential of the process that turned on quotas.
	 * Set up the time limits for this quota.
	 */
	kauth_cred_hold(l->l_cred);
	ump->um_cred[type] = l->l_cred;
	ump->um_btime[type] = MAX_DQ_TIME;
	ump->um_itime[type] = MAX_IQ_TIME;
	if (dqget(NULLVP, 0, ump, type, &dq) == 0) {
		if (dq->dq_btime > 0)
			ump->um_btime[type] = dq->dq_btime;
		if (dq->dq_itime > 0)
			ump->um_itime[type] = dq->dq_itime;
		dqrele(NULLVP, dq);
	}
	/* Allocate a marker vnode. */
	if ((mvp = vnalloc(mp)) == NULL) {
		error = ENOMEM;
		goto out;
	}
	/*
	 * Search vnodes associated with this mount point,
	 * adding references to quota file being opened.
	 * NB: only need to add dquot's for inodes being modified.
	 */
	mutex_enter(&mntvnode_lock);
again:
	for (vp = TAILQ_FIRST(&mp->mnt_vnodelist); vp; vp = vunmark(mvp)) {
		vmark(mvp, vp);
		mutex_enter(&vp->v_interlock);
		if (VTOI(vp) == NULL || vp->v_mount != mp || vismarker(vp) ||
		    vp->v_type == VNON || vp->v_writecount == 0 ||
		    (vp->v_iflag & (VI_XLOCK | VI_CLEAN)) != 0) {
			mutex_exit(&vp->v_interlock);
			continue;
		}
		mutex_exit(&mntvnode_lock);
		if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK)) {
			mutex_enter(&mntvnode_lock);
			(void)vunmark(mvp);
			goto again;
		}
		if ((error = getinoquota(VTOI(vp))) != 0) {
			vput(vp);
			mutex_enter(&mntvnode_lock);
			(void)vunmark(mvp);
			break;
		}
		vput(vp);
		mutex_enter(&mntvnode_lock);
	}
	mutex_exit(&mntvnode_lock);
	vnfree(mvp);
 out:
	mutex_enter(&dqlock);
	ump->um_qflags[type] &= ~QTF_OPENING;
	cv_broadcast(&dqcv);
	mutex_exit(&dqlock);
	if (error)
		quotaoff(l, mp, type);
	return (error);
}
Ejemplo n.º 11
0
static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
{
	struct inode *inode = file_inode(filp);
	struct super_block *sb = inode->i_sb;
	struct ext4_inode_info *ei = EXT4_I(inode);
	int err, rc;
	handle_t *handle;
	kprojid_t kprojid;
	struct ext4_iloc iloc;
	struct ext4_inode *raw_inode;
	struct dquot *transfer_to[MAXQUOTAS] = { };

	if (!ext4_has_feature_project(sb)) {
		if (projid != EXT4_DEF_PROJID)
			return -EOPNOTSUPP;
		else
			return 0;
	}

	if (EXT4_INODE_SIZE(sb) <= EXT4_GOOD_OLD_INODE_SIZE)
		return -EOPNOTSUPP;

	kprojid = make_kprojid(&init_user_ns, (projid_t)projid);

	if (projid_eq(kprojid, EXT4_I(inode)->i_projid))
		return 0;

	err = mnt_want_write_file(filp);
	if (err)
		return err;

	err = -EPERM;
	inode_lock(inode);
	/* Is it quota file? Do not allow user to mess with it */
	if (ext4_is_quota_file(inode))
		goto out_unlock;

	err = ext4_get_inode_loc(inode, &iloc);
	if (err)
		goto out_unlock;

	raw_inode = ext4_raw_inode(&iloc);
	if (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)) {
		err = ext4_expand_extra_isize(inode,
					      EXT4_SB(sb)->s_want_extra_isize,
					      &iloc);
		if (err)
			goto out_unlock;
	} else {
		brelse(iloc.bh);
	}

	dquot_initialize(inode);

	handle = ext4_journal_start(inode, EXT4_HT_QUOTA,
		EXT4_QUOTA_INIT_BLOCKS(sb) +
		EXT4_QUOTA_DEL_BLOCKS(sb) + 3);
	if (IS_ERR(handle)) {
		err = PTR_ERR(handle);
		goto out_unlock;
	}

	err = ext4_reserve_inode_write(handle, inode, &iloc);
	if (err)
		goto out_stop;

	transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
	if (!IS_ERR(transfer_to[PRJQUOTA])) {

		/* __dquot_transfer() calls back ext4_get_inode_usage() which
		 * counts xattr inode references.
		 */
		down_read(&EXT4_I(inode)->xattr_sem);
		err = __dquot_transfer(inode, transfer_to);
		up_read(&EXT4_I(inode)->xattr_sem);
		dqput(transfer_to[PRJQUOTA]);
		if (err)
			goto out_dirty;
	}

	EXT4_I(inode)->i_projid = kprojid;
	inode->i_ctime = current_time(inode);
out_dirty:
	rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
	if (!err)
		err = rc;
out_stop:
	ext4_journal_stop(handle);
out_unlock:
	inode_unlock(inode);
	mnt_drop_write_file(filp);
	return err;
}
Ejemplo n.º 12
0
static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
{
	struct inode *inode = file_inode(filp);
	struct super_block *sb = inode->i_sb;
	struct ext4_inode_info *ei = EXT4_I(inode);
	int err, rc;
	handle_t *handle;
	kprojid_t kprojid;
	struct ext4_iloc iloc;
	struct ext4_inode *raw_inode;

	if (!EXT4_HAS_RO_COMPAT_FEATURE(sb,
			EXT4_FEATURE_RO_COMPAT_PROJECT)) {
		if (projid != EXT4_DEF_PROJID)
			return -EOPNOTSUPP;
		else
			return 0;
	}

	if (EXT4_INODE_SIZE(sb) <= EXT4_GOOD_OLD_INODE_SIZE)
		return -EOPNOTSUPP;

	kprojid = make_kprojid(&init_user_ns, (projid_t)projid);

	if (projid_eq(kprojid, EXT4_I(inode)->i_projid))
		return 0;

	err = mnt_want_write_file(filp);
	if (err)
		return err;

	err = -EPERM;
	inode_lock(inode);
	/* Is it quota file? Do not allow user to mess with it */
	if (IS_NOQUOTA(inode))
		goto out_unlock;

	err = ext4_get_inode_loc(inode, &iloc);
	if (err)
		goto out_unlock;

	raw_inode = ext4_raw_inode(&iloc);
	if (!EXT4_FITS_IN_INODE(raw_inode, ei, i_projid)) {
		err = -EOVERFLOW;
		brelse(iloc.bh);
		goto out_unlock;
	}
	brelse(iloc.bh);

	dquot_initialize(inode);

	handle = ext4_journal_start(inode, EXT4_HT_QUOTA,
		EXT4_QUOTA_INIT_BLOCKS(sb) +
		EXT4_QUOTA_DEL_BLOCKS(sb) + 3);
	if (IS_ERR(handle)) {
		err = PTR_ERR(handle);
		goto out_unlock;
	}

	err = ext4_reserve_inode_write(handle, inode, &iloc);
	if (err)
		goto out_stop;

	if (sb_has_quota_limits_enabled(sb, PRJQUOTA)) {
		struct dquot *transfer_to[MAXQUOTAS] = { };

		transfer_to[PRJQUOTA] = dqget(sb, make_kqid_projid(kprojid));
		if (transfer_to[PRJQUOTA]) {
			err = __dquot_transfer(inode, transfer_to);
			dqput(transfer_to[PRJQUOTA]);
			if (err)
				goto out_dirty;
		}
	}
	EXT4_I(inode)->i_projid = kprojid;
	inode->i_ctime = ext4_current_time(inode);
out_dirty:
	rc = ext4_mark_iloc_dirty(handle, inode, &iloc);
	if (!err)
		err = rc;
out_stop:
	ext4_journal_stop(handle);
out_unlock:
	inode_unlock(inode);
	mnt_drop_write_file(filp);
	return err;
}