Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4843456c authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs-2.6:
  quota: Fix deadlock during path resolution
parents 2b1caf6e f00c9e44
Loading
Loading
Loading
Loading
+7 −18
Original line number Diff line number Diff line
@@ -754,7 +754,7 @@ static int ext3_release_dquot(struct dquot *dquot);
static int ext3_mark_dquot_dirty(struct dquot *dquot);
static int ext3_write_info(struct super_block *sb, int type);
static int ext3_quota_on(struct super_block *sb, int type, int format_id,
				char *path);
			 struct path *path);
static int ext3_quota_on_mount(struct super_block *sb, int type);
static ssize_t ext3_quota_read(struct super_block *sb, int type, char *data,
			       size_t len, loff_t off);
@@ -2877,27 +2877,20 @@ static int ext3_quota_on_mount(struct super_block *sb, int type)
 * Standard function to be called on quota_on
 */
static int ext3_quota_on(struct super_block *sb, int type, int format_id,
			 char *name)
			 struct path *path)
{
	int err;
	struct path path;

	if (!test_opt(sb, QUOTA))
		return -EINVAL;

	err = kern_path(name, LOOKUP_FOLLOW, &path);
	if (err)
		return err;

	/* Quotafile not on the same filesystem? */
	if (path.mnt->mnt_sb != sb) {
		path_put(&path);
	if (path->mnt->mnt_sb != sb)
		return -EXDEV;
	}
	/* Journaling quota? */
	if (EXT3_SB(sb)->s_qf_names[type]) {
		/* Quotafile not of fs root? */
		if (path.dentry->d_parent != sb->s_root)
		if (path->dentry->d_parent != sb->s_root)
			ext3_msg(sb, KERN_WARNING,
				"warning: Quota file not on filesystem root. "
				"Journaled quota will not work.");
@@ -2907,7 +2900,7 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id,
	 * When we journal data on quota file, we have to flush journal to see
	 * all updates to the file when we bypass pagecache...
	 */
	if (ext3_should_journal_data(path.dentry->d_inode)) {
	if (ext3_should_journal_data(path->dentry->d_inode)) {
		/*
		 * We don't need to lock updates but journal_flush() could
		 * otherwise be livelocked...
@@ -2915,15 +2908,11 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id,
		journal_lock_updates(EXT3_SB(sb)->s_journal);
		err = journal_flush(EXT3_SB(sb)->s_journal);
		journal_unlock_updates(EXT3_SB(sb)->s_journal);
		if (err) {
			path_put(&path);
		if (err)
			return err;
	}
	}

	err = dquot_quota_on_path(sb, type, format_id, &path);
	path_put(&path);
	return err;
	return dquot_quota_on(sb, type, format_id, path);
}

/* Read data from quotafile - avoid pagecache and such because we cannot afford
+7 −18
Original line number Diff line number Diff line
@@ -1161,7 +1161,7 @@ static int ext4_release_dquot(struct dquot *dquot);
static int ext4_mark_dquot_dirty(struct dquot *dquot);
static int ext4_write_info(struct super_block *sb, int type);
static int ext4_quota_on(struct super_block *sb, int type, int format_id,
				char *path);
			 struct path *path);
static int ext4_quota_off(struct super_block *sb, int type);
static int ext4_quota_on_mount(struct super_block *sb, int type);
static ssize_t ext4_quota_read(struct super_block *sb, int type, char *data,
@@ -4558,27 +4558,20 @@ static int ext4_quota_on_mount(struct super_block *sb, int type)
 * Standard function to be called on quota_on
 */
static int ext4_quota_on(struct super_block *sb, int type, int format_id,
			 char *name)
			 struct path *path)
{
	int err;
	struct path path;

	if (!test_opt(sb, QUOTA))
		return -EINVAL;

	err = kern_path(name, LOOKUP_FOLLOW, &path);
	if (err)
		return err;

	/* Quotafile not on the same filesystem? */
	if (path.mnt->mnt_sb != sb) {
		path_put(&path);
	if (path->mnt->mnt_sb != sb)
		return -EXDEV;
	}
	/* Journaling quota? */
	if (EXT4_SB(sb)->s_qf_names[type]) {
		/* Quotafile not in fs root? */
		if (path.dentry->d_parent != sb->s_root)
		if (path->dentry->d_parent != sb->s_root)
			ext4_msg(sb, KERN_WARNING,
				"Quota file not on filesystem root. "
				"Journaled quota will not work");
@@ -4589,7 +4582,7 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
	 * all updates to the file when we bypass pagecache...
	 */
	if (EXT4_SB(sb)->s_journal &&
	    ext4_should_journal_data(path.dentry->d_inode)) {
	    ext4_should_journal_data(path->dentry->d_inode)) {
		/*
		 * We don't need to lock updates but journal_flush() could
		 * otherwise be livelocked...
@@ -4597,15 +4590,11 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
		jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
		err = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
		jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
		if (err) {
			path_put(&path);
		if (err)
			return err;
	}
	}

	err = dquot_quota_on_path(sb, type, format_id, &path);
	path_put(&path);
	return err;
	return dquot_quota_on(sb, type, format_id, path);
}

static int ext4_quota_off(struct super_block *sb, int type)
+2 −3
Original line number Diff line number Diff line
@@ -993,8 +993,7 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb)
}

/* Handle quota on quotactl */
static int ocfs2_quota_on(struct super_block *sb, int type, int format_id,
			  char *path)
static int ocfs2_quota_on(struct super_block *sb, int type, int format_id)
{
	unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
					     OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
@@ -1013,7 +1012,7 @@ static int ocfs2_quota_off(struct super_block *sb, int type)
}

static const struct quotactl_ops ocfs2_quotactl_ops = {
	.quota_on	= ocfs2_quota_on,
	.quota_on_meta	= ocfs2_quota_on,
	.quota_off	= ocfs2_quota_off,
	.quota_sync	= dquot_quota_sync,
	.get_info	= dquot_get_dqinfo,
+2 −16
Original line number Diff line number Diff line
@@ -2189,7 +2189,7 @@ int dquot_resume(struct super_block *sb, int type)
}
EXPORT_SYMBOL(dquot_resume);

int dquot_quota_on_path(struct super_block *sb, int type, int format_id,
int dquot_quota_on(struct super_block *sb, int type, int format_id,
		   struct path *path)
{
	int error = security_quota_on(path->dentry);
@@ -2204,20 +2204,6 @@ int dquot_quota_on_path(struct super_block *sb, int type, int format_id,
					     DQUOT_LIMITS_ENABLED);
	return error;
}
EXPORT_SYMBOL(dquot_quota_on_path);

int dquot_quota_on(struct super_block *sb, int type, int format_id, char *name)
{
	struct path path;
	int error;

	error = kern_path(name, LOOKUP_FOLLOW, &path);
	if (!error) {
		error = dquot_quota_on_path(sb, type, format_id, &path);
		path_put(&path);
	}
	return error;
}
EXPORT_SYMBOL(dquot_quota_on);

/*
+27 −14
Original line number Diff line number Diff line
@@ -64,18 +64,15 @@ static int quota_sync_all(int type)
}

static int quota_quotaon(struct super_block *sb, int type, int cmd, qid_t id,
		         void __user *addr)
		         struct path *path)
{
	char *pathname;
	int ret = -ENOSYS;

	pathname = getname(addr);
	if (IS_ERR(pathname))
		return PTR_ERR(pathname);
	if (sb->s_qcop->quota_on)
		ret = sb->s_qcop->quota_on(sb, type, id, pathname);
	putname(pathname);
	return ret;
	if (!sb->s_qcop->quota_on && !sb->s_qcop->quota_on_meta)
		return -ENOSYS;
	if (sb->s_qcop->quota_on_meta)
		return sb->s_qcop->quota_on_meta(sb, type, id);
	if (IS_ERR(path))
		return PTR_ERR(path);
	return sb->s_qcop->quota_on(sb, type, id, path);
}

static int quota_getfmt(struct super_block *sb, int type, void __user *addr)
@@ -241,7 +238,7 @@ static int quota_getxquota(struct super_block *sb, int type, qid_t id,

/* Copy parameters and call proper function */
static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
		       void __user *addr)
		       void __user *addr, struct path *path)
{
	int ret;

@@ -256,7 +253,7 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,

	switch (cmd) {
	case Q_QUOTAON:
		return quota_quotaon(sb, type, cmd, id, addr);
		return quota_quotaon(sb, type, cmd, id, path);
	case Q_QUOTAOFF:
		if (!sb->s_qcop->quota_off)
			return -ENOSYS;
@@ -335,6 +332,7 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
{
	uint cmds, type;
	struct super_block *sb = NULL;
	struct path path, *pathp = NULL;
	int ret;

	cmds = cmd >> SUBCMDSHIFT;
@@ -351,12 +349,27 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
		return -ENODEV;
	}

	/*
	 * Path for quotaon has to be resolved before grabbing superblock
	 * because that gets s_umount sem which is also possibly needed by path
	 * resolution (think about autofs) and thus deadlocks could arise.
	 */
	if (cmds == Q_QUOTAON) {
		ret = user_path_at(AT_FDCWD, addr, LOOKUP_FOLLOW, &path);
		if (ret)
			pathp = ERR_PTR(ret);
		else
			pathp = &path;
	}

	sb = quotactl_block(special);
	if (IS_ERR(sb))
		return PTR_ERR(sb);

	ret = do_quotactl(sb, type, cmds, id, addr);
	ret = do_quotactl(sb, type, cmds, id, addr, pathp);

	drop_super(sb);
	if (pathp && !IS_ERR(pathp))
		path_put(pathp);
	return ret;
}
Loading