static int
closedq_scan_inode(struct inode *ip, void *arg)
{
	struct dquot *dqp;
	struct ufsvfs *ufsvfsp = ip->i_ufsvfs;

	/*
	 * wrong file system; keep looking
	 */
	if (ufsvfsp != (struct ufsvfs *)arg)
		return (0);

	ASSERT(RW_WRITE_HELD(&ufsvfsp->vfs_dqrwlock));
	rw_enter(&ip->i_contents, RW_WRITER);

	/*
	 * Shadow inodes and extended attribute directories
	 * do not have quota info records.
	 */
	if ((dqp = ip->i_dquot) != NULL) {
		ASSERT((ip->i_mode & IFMT) != IFSHAD);
		ASSERT((ip->i_mode & IFMT) != IFATTRDIR);
		ip->i_dquot = NULL;
		mutex_enter(&dqp->dq_lock);
		dqput(dqp);

		/*
		 * If we have a pending logging file system quota
		 * transaction, then cancel it.  Clear the flag to
		 * prevent ufs_trans_push_quota() from trying to
		 * deal with this transaction just in case it is
		 * waiting for the mutex.  We decrement the counter
		 * since the transaction won't be needing the quota
		 * info record anymore.
		 */
		if (dqp->dq_flags & DQ_TRANS) {
			dqp->dq_flags &= ~DQ_TRANS;
			dqput(dqp);
		}
		mutex_exit(&dqp->dq_lock);
	}
	rw_exit(&ip->i_contents);

	return (0);
}
static int
setquota_scan_inode(struct inode *ip, void *arg)
{
	struct setquota_data *sqdp = (struct setquota_data *)arg;
	struct ufsvfs *ufsvfsp = ip->i_ufsvfs;

	/*
	 * wrong file system; keep looking
	 */
	if (ufsvfsp != sqdp->sqd_ufsvfsp)
		return (0);

	ASSERT(RW_WRITE_HELD(&ufsvfsp->vfs_dqrwlock));

	/*
	 * The file system does not have quotas enabled or this is the
	 * file system's quota inode; keep looking.
	 */
	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0 ||
	    ip == ufsvfsp->vfs_qinod) {
		return (0);
	}

	rw_enter(&ip->i_contents, RW_WRITER);
	/*
	 * This inode is in the cache (by definition), is still valid,
	 * is not a shadow inode or extended attribute directory inode
	 * and has the right uid.
	 */
	if (ip->i_mode && (ip->i_mode & IFMT) != IFSHAD &&
	    (ip->i_mode & IFMT) != IFATTRDIR && ip->i_uid == sqdp->sqd_uid) {
		/*
		 * Transition is "no limit" to "at least one limit":
		 */
		if (sqdp->sqd_type == SQD_TYPE_LIMIT &&
		    ip->i_dquot == NULL) {
			ip->i_dquot = getinoquota(ip);
		}
		/*
		 * Transition is "at least one limit" to "no limit":
		 */
		else if (sqdp->sqd_type == SQD_TYPE_NO_LIMIT && ip->i_dquot) {
			mutex_enter(&ip->i_dquot->dq_lock);
			dqput(ip->i_dquot);
			mutex_exit(&ip->i_dquot->dq_lock);
			ip->i_dquot = NULL;
		}
	}
	rw_exit(&ip->i_contents);

	return (0);
}
/*
 * Q_GETQUOTA - return current values in a dqblk structure.
 */
static int
getquota(uid_t uid, struct ufsvfs *ufsvfsp, caddr_t addr, cred_t *cr)
{
	struct dquot *dqp;
	struct dquot *xdqp;
	struct dqblk dqb;
	int error = 0;

	if (uid != crgetruid(cr) &&
	    secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0)
		return (EPERM);
	rw_enter(&ufsvfsp->vfs_dqrwlock, RW_READER);
	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {
		rw_exit(&ufsvfsp->vfs_dqrwlock);
		return (ESRCH);
	}
	error = getdiskquota(uid, ufsvfsp, 0, &xdqp);
	if (error) {
		rw_exit(&ufsvfsp->vfs_dqrwlock);
		return (error);
	}
	dqp = xdqp;
	mutex_enter(&dqp->dq_lock);
	if (dqp->dq_fhardlimit == 0 && dqp->dq_fsoftlimit == 0 &&
	    dqp->dq_bhardlimit == 0 && dqp->dq_bsoftlimit == 0) {
		error = ESRCH;
	} else {
		bcopy(&dqp->dq_dqb, &dqb, sizeof (struct dqblk));
	}
	dqput(dqp);
	mutex_exit(&dqp->dq_lock);
	rw_exit(&ufsvfsp->vfs_dqrwlock);
	if (error == 0 && copyout(&dqb, addr, sizeof (struct dqblk)) != 0)
		error = EFAULT;
	return (error);
}
示例#4
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;
}
示例#5
0
文件: ioctl.c 项目: andy-shev/linux
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;
}
/*
 * Set various fields of the dqblk according to the command.
 * Q_SETQUOTA - assign an entire dqblk structure.
 * Q_SETQLIM - assign a dqblk structure except for the usage.
 */
static int
setquota(int cmd, uid_t uid, struct ufsvfs *ufsvfsp,
    caddr_t addr, struct cred *cr)
{
	struct dquot *dqp;
	struct inode	*qip;
	struct dquot *xdqp;
	struct dqblk newlim;
	int error;
	int scan_type = SQD_TYPE_NONE;
	daddr_t bn;
	int contig;

	if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0)
		return (EPERM);

	rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER);

	/*
	 * Quotas are not enabled on this file system so there is
	 * nothing more to do.
	 */
	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) == 0) {
		rw_exit(&ufsvfsp->vfs_dqrwlock);
		return (ESRCH);
	}

	/*
	 * At this point, the quota subsystem is quiescent on this file
	 * system so we can do all the work necessary to modify the quota
	 * information for this user.
	 */

	if (copyin(addr, (caddr_t)&newlim, sizeof (struct dqblk)) != 0) {
		rw_exit(&ufsvfsp->vfs_dqrwlock);
		return (EFAULT);
	}
	error = getdiskquota(uid, ufsvfsp, 0, &xdqp);
	if (error) {
		rw_exit(&ufsvfsp->vfs_dqrwlock);
		return (error);
	}
	dqp = xdqp;
	/*
	 * Don't change disk usage on Q_SETQLIM
	 */
	mutex_enter(&dqp->dq_lock);
	if (cmd == Q_SETQLIM) {
		newlim.dqb_curblocks = dqp->dq_curblocks;
		newlim.dqb_curfiles = dqp->dq_curfiles;
	}
	if (uid == 0) {
		/*
		 * Timelimits for uid 0 set the relative time
		 * the other users can be over quota for this file system.
		 * If it is zero a default is used (see quota.h).
		 */
		ufsvfsp->vfs_btimelimit =
		    newlim.dqb_btimelimit? newlim.dqb_btimelimit: DQ_BTIMELIMIT;
		ufsvfsp->vfs_ftimelimit =
		    newlim.dqb_ftimelimit? newlim.dqb_ftimelimit: DQ_FTIMELIMIT;
	} else {
		if (newlim.dqb_bsoftlimit &&
		    newlim.dqb_curblocks >= newlim.dqb_bsoftlimit) {
			if (dqp->dq_bsoftlimit == 0 ||
			    dqp->dq_curblocks < dqp->dq_bsoftlimit) {
				/* If we're suddenly over the limit(s),	*/
				/* start the timer(s)			*/
				newlim.dqb_btimelimit =
				    (uint32_t)gethrestime_sec() +
				    ufsvfsp->vfs_btimelimit;
				dqp->dq_flags &= ~DQ_BLKS;
			} else {
				/* If we're currently over the soft	*/
				/* limit and were previously over the	*/
				/* soft limit then preserve the old	*/
				/* time limit but make sure the DQ_BLKS	*/
				/* flag is set since we must have been	*/
				/* previously warned.			*/
				newlim.dqb_btimelimit = dqp->dq_btimelimit;
				dqp->dq_flags |= DQ_BLKS;
			}
		} else {
			/* Either no quota or under quota, clear time limit */
			newlim.dqb_btimelimit = 0;
			dqp->dq_flags &= ~DQ_BLKS;
		}

		if (newlim.dqb_fsoftlimit &&
		    newlim.dqb_curfiles >= newlim.dqb_fsoftlimit) {
			if (dqp->dq_fsoftlimit == 0 ||
			    dqp->dq_curfiles < dqp->dq_fsoftlimit) {
				/* If we're suddenly over the limit(s),	*/
				/* start the timer(s)			*/
				newlim.dqb_ftimelimit =
				    (uint32_t)gethrestime_sec() +
				    ufsvfsp->vfs_ftimelimit;
				dqp->dq_flags &= ~DQ_FILES;
			} else {
				/* If we're currently over the soft	*/
				/* limit and were previously over the	*/
				/* soft limit then preserve the old	*/
				/* time limit but make sure the		*/
				/* DQ_FILES flag is set since we must	*/
				/* have been previously warned.		*/
				newlim.dqb_ftimelimit = dqp->dq_ftimelimit;
				dqp->dq_flags |= DQ_FILES;
			}
		} else {
			/* Either no quota or under quota, clear time limit */
			newlim.dqb_ftimelimit = 0;
			dqp->dq_flags &= ~DQ_FILES;
		}
	}

	/*
	 * If there was previously no limit and there is now at least
	 * one limit, then any inodes in the cache have NULL d_iquot
	 * fields (getinoquota() returns NULL when there are no limits).
	 */
	if ((dqp->dq_fhardlimit == 0 && dqp->dq_fsoftlimit == 0 &&
	    dqp->dq_bhardlimit == 0 && dqp->dq_bsoftlimit == 0) &&
	    (newlim.dqb_fhardlimit || newlim.dqb_fsoftlimit ||
	    newlim.dqb_bhardlimit || newlim.dqb_bsoftlimit)) {
		scan_type = SQD_TYPE_LIMIT;
	}

	/*
	 * If there was previously at least one limit and there is now
	 * no limit, then any inodes in the cache have non-NULL d_iquot
	 * fields need to be reset to NULL.
	 */
	else if ((dqp->dq_fhardlimit || dqp->dq_fsoftlimit ||
	    dqp->dq_bhardlimit || dqp->dq_bsoftlimit) &&
	    (newlim.dqb_fhardlimit == 0 && newlim.dqb_fsoftlimit == 0 &&
	    newlim.dqb_bhardlimit == 0 && newlim.dqb_bsoftlimit == 0)) {
		scan_type = SQD_TYPE_NO_LIMIT;
	}

	dqp->dq_dqb = newlim;
	dqp->dq_flags |= DQ_MOD;

	/*
	 *  push the new quota to disk now.  If this is a trans device
	 *  then force the page out with ufs_putpage so it will be deltaed
	 *  by ufs_startio.
	 */
	qip = ufsvfsp->vfs_qinod;
	rw_enter(&qip->i_contents, RW_WRITER);
	(void) ufs_rdwri(UIO_WRITE, FWRITE | FSYNC, qip, (caddr_t)&dqp->dq_dqb,
	    sizeof (struct dqblk), dqoff(uid), UIO_SYSSPACE,
	    (int *)NULL, kcred);
	rw_exit(&qip->i_contents);

	(void) VOP_PUTPAGE(ITOV(qip), dqoff(dqp->dq_uid) & ~qip->i_fs->fs_bmask,
	    qip->i_fs->fs_bsize, B_INVAL, kcred, NULL);

	/*
	 * We must set the dq_mof even if not we are not logging in case
	 * we are later remount to logging.
	 */
	contig = 0;
	rw_enter(&qip->i_contents, RW_WRITER);
	error = bmap_read(qip, dqoff(dqp->dq_uid), &bn, &contig);
	rw_exit(&qip->i_contents);
	if (error || (bn == UFS_HOLE)) {
		dqp->dq_mof = UFS_HOLE;
	} else {
		dqp->dq_mof = ldbtob(bn) +
		    (offset_t)((dqoff(dqp->dq_uid)) & (DEV_BSIZE - 1));
	}

	dqp->dq_flags &= ~DQ_MOD;
	dqput(dqp);
	mutex_exit(&dqp->dq_lock);
	if (scan_type) {
		struct setquota_data sqd;

		sqd.sqd_type = scan_type;
		sqd.sqd_ufsvfsp = ufsvfsp;
		sqd.sqd_uid = uid;
		(void) ufs_scan_inodes(0, setquota_scan_inode, &sqd, ufsvfsp);
	}
	rw_exit(&ufsvfsp->vfs_dqrwlock);
	return (0);
}
/*
 * Set the quota file up for a particular file system.
 * Called as the result of a quotaon (Q_QUOTAON) ioctl.
 */
static int
opendq(
	struct ufsvfs *ufsvfsp,
	struct vnode *vp,		/* quota file */
	struct cred *cr)
{
	struct inode *qip;
	struct dquot *dqp;
	int error;
	int quotaon = 0;

	if (secpolicy_fs_quota(cr, ufsvfsp->vfs_vfs) != 0)
		return (EPERM);

	VN_HOLD(vp);

	/*
	 * Check to be sure its a regular file.
	 */
	if (vp->v_type != VREG) {
		VN_RELE(vp);
		return (EACCES);
	}

	rw_enter(&ufsvfsp->vfs_dqrwlock, RW_WRITER);

	/*
	 * We have vfs_dqrwlock as writer, so if quotas are disabled,
	 * then vfs_qinod should be NULL or we have a race somewhere.
	 */
	ASSERT((ufsvfsp->vfs_qflags & MQ_ENABLED) || (ufsvfsp->vfs_qinod == 0));

	if ((ufsvfsp->vfs_qflags & MQ_ENABLED) != 0) {
		/*
		 * Quotas are already enabled on this file system.
		 *
		 * If the "quotas" file was replaced (different inode)
		 * while quotas were enabled we don't want to re-enable
		 * them with a new "quotas" file. Simply print a warning
		 * message to the console, release the new vnode, and
		 * return.
		 * XXX - The right way to fix this is to return EBUSY
		 * for the ioctl() issued by 'quotaon'.
		 */
		if (VTOI(vp) != ufsvfsp->vfs_qinod) {
			cmn_err(CE_WARN, "Previous quota file still in use."
			    " Disable quotas on %s before enabling.\n",
			    VTOI(vp)->i_fs->fs_fsmnt);
			VN_RELE(vp);
			rw_exit(&ufsvfsp->vfs_dqrwlock);
			return (0);
		}
		(void) quotasync(ufsvfsp, /* do_lock */ 0);
		/* remove extra hold on quota file */
		VN_RELE(vp);
		quotaon++;
		qip = ufsvfsp->vfs_qinod;
	} else {
		int qlen;

		ufsvfsp->vfs_qinod = VTOI(vp);
		qip = ufsvfsp->vfs_qinod;
		/*
		 * Force the file to have no partially allocated blocks
		 * to prevent a realloc from changing the location of
		 * the data. We must do this even if not logging in
		 * case we later remount to logging.
		 */
		qlen = qip->i_fs->fs_bsize * NDADDR;

		/*
		 * Largefiles: i_size needs to be atomically accessed now.
		 */
		rw_enter(&qip->i_contents, RW_WRITER);
		if (qip->i_size < qlen) {
			if (ufs_itrunc(qip, (u_offset_t)qlen, (int)0, cr) != 0)
				cmn_err(CE_WARN, "opendq failed to remove frags"
				    " from quota file\n");
			rw_exit(&qip->i_contents);
			(void) VOP_PUTPAGE(vp, (offset_t)0, (size_t)qip->i_size,
			    B_INVAL, kcred, NULL);
		} else {
			rw_exit(&qip->i_contents);
		}
		TRANS_MATA_IGET(ufsvfsp, qip);
	}

	/*
	 * The file system time limits are in the dquot for uid 0.
	 * The time limits set the relative time the other users
	 * can be over quota for this file system.
	 * If it is zero a default is used (see quota.h).
	 */
	error = getdiskquota((uid_t)0, ufsvfsp, 1, &dqp);
	if (error == 0) {
		mutex_enter(&dqp->dq_lock);
		ufsvfsp->vfs_btimelimit =
		    (dqp->dq_btimelimit? dqp->dq_btimelimit: DQ_BTIMELIMIT);
		ufsvfsp->vfs_ftimelimit =
		    (dqp->dq_ftimelimit? dqp->dq_ftimelimit: DQ_FTIMELIMIT);

		ufsvfsp->vfs_qflags = MQ_ENABLED;	/* enable quotas */
		vfs_setmntopt(ufsvfsp->vfs_vfs, MNTOPT_QUOTA, NULL, 0);
		dqput(dqp);
		mutex_exit(&dqp->dq_lock);
	} else if (!quotaon) {
		/*
		 * Some sort of I/O error on the quota file, and quotas were
		 * not already on when we got here so clean up.
		 */
		ufsvfsp->vfs_qflags = 0;
		ufsvfsp->vfs_qinod = NULL;
		VN_RELE(ITOV(qip));
	}

	/*
	 * If quotas are enabled update all valid inodes in the
	 * cache with quota information.
	 */
	if (ufsvfsp->vfs_qflags & MQ_ENABLED) {
		(void) ufs_scan_inodes(0, opendq_scan_inode, ufsvfsp, ufsvfsp);
	}

	rw_exit(&ufsvfsp->vfs_dqrwlock);
	return (error);
}