Commit 77e69dac authored by Al Viro's avatar Al Viro

[PATCH] fix races and leaks in vfs_quota_on() users

* new helper: vfs_quota_on_path(); equivalent of vfs_quota_on() sans the
  pathname resolution.
* callers of vfs_quota_on() that do their own pathname resolution and
  checks based on it are switched to vfs_quota_on_path(); that way we
  avoid the races.
* reiserfs leaked dentry/vfsmount references on several failure exits.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 1b7e190b
......@@ -1793,6 +1793,21 @@ static int vfs_quota_on_remount(struct super_block *sb, int type)
return ret;
}
int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
struct path *path)
{
int error = security_quota_on(path->dentry);
if (error)
return error;
/* Quota file not on the same filesystem? */
if (path->mnt->mnt_sb != sb)
error = -EXDEV;
else
error = vfs_quota_on_inode(path->dentry->d_inode, type,
format_id);
return error;
}
/* Actual function called from quotactl() */
int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
int remount)
......@@ -1804,19 +1819,10 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
return vfs_quota_on_remount(sb, type);
error = path_lookup(path, LOOKUP_FOLLOW, &nd);
if (error < 0)
return error;
error = security_quota_on(nd.path.dentry);
if (error)
goto out_path;
/* Quota file not on the same filesystem? */
if (nd.path.mnt->mnt_sb != sb)
error = -EXDEV;
else
error = vfs_quota_on_inode(nd.path.dentry->d_inode, type,
format_id);
out_path:
path_put(&nd.path);
if (!error) {
error = vfs_quota_on_path(sb, type, format_id, &nd.path);
path_put(&nd.path);
}
return error;
}
......@@ -2185,6 +2191,7 @@ EXPORT_SYMBOL(unregister_quota_format);
EXPORT_SYMBOL(dqstats);
EXPORT_SYMBOL(dq_data_lock);
EXPORT_SYMBOL(vfs_quota_on);
EXPORT_SYMBOL(vfs_quota_on_path);
EXPORT_SYMBOL(vfs_quota_on_mount);
EXPORT_SYMBOL(vfs_quota_off);
EXPORT_SYMBOL(vfs_quota_sync);
......
......@@ -2810,8 +2810,9 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id,
journal_unlock_updates(EXT3_SB(sb)->s_journal);
}
err = vfs_quota_on_path(sb, type, format_id, &nd.path);
path_put(&nd.path);
return vfs_quota_on(sb, type, format_id, path, remount);
return err;
}
/* Read data from quotafile - avoid pagecache and such because we cannot afford
......
......@@ -3352,8 +3352,9 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
}
err = vfs_quota_on_path(sb, type, format_id, &nd.path);
path_put(&nd.path);
return vfs_quota_on(sb, type, format_id, path, remount);
return err;
}
/* Read data from quotafile - avoid pagecache and such because we cannot afford
......
......@@ -2076,8 +2076,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
return err;
/* Quotafile not on the same filesystem? */
if (nd.path.mnt->mnt_sb != sb) {
path_put(&nd.path);
return -EXDEV;
err = -EXDEV;
goto out;
}
inode = nd.path.dentry->d_inode;
/* We must not pack tails for quota files on reiserfs for quota IO to work */
......@@ -2087,8 +2087,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
reiserfs_warning(sb,
"reiserfs: Unpacking tail of quota file failed"
" (%d). Cannot turn on quotas.", err);
path_put(&nd.path);
return -EINVAL;
err = -EINVAL;
goto out;
}
mark_inode_dirty(inode);
}
......@@ -2109,13 +2109,15 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
/* Just start temporary transaction and finish it */
err = journal_begin(&th, sb, 1);
if (err)
return err;
goto out;
err = journal_end_sync(&th, sb, 1);
if (err)
return err;
goto out;
}
err = vfs_quota_on_path(sb, type, format_id, &nd.path);
out:
path_put(&nd.path);
return vfs_quota_on(sb, type, format_id, path, 0);
return err;
}
/* Read data from quotafile - avoid pagecache and such because we cannot afford
......
......@@ -43,6 +43,8 @@ int dquot_mark_dquot_dirty(struct dquot *dquot);
int vfs_quota_on(struct super_block *sb, int type, int format_id,
char *path, int remount);
int vfs_quota_on_path(struct super_block *sb, int type, int format_id,
struct path *path);
int vfs_quota_on_mount(struct super_block *sb, char *qf_name,
int format_id, int type);
int vfs_quota_off(struct super_block *sb, int type, int remount);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment