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) ...@@ -1793,6 +1793,21 @@ static int vfs_quota_on_remount(struct super_block *sb, int type)
return ret; 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() */ /* Actual function called from quotactl() */
int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path, int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path,
int remount) int remount)
...@@ -1804,19 +1819,10 @@ int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path, ...@@ -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); return vfs_quota_on_remount(sb, type);
error = path_lookup(path, LOOKUP_FOLLOW, &nd); error = path_lookup(path, LOOKUP_FOLLOW, &nd);
if (error < 0) if (!error) {
return error; error = vfs_quota_on_path(sb, type, format_id, &nd.path);
error = security_quota_on(nd.path.dentry); path_put(&nd.path);
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);
return error; return error;
} }
...@@ -2185,6 +2191,7 @@ EXPORT_SYMBOL(unregister_quota_format); ...@@ -2185,6 +2191,7 @@ EXPORT_SYMBOL(unregister_quota_format);
EXPORT_SYMBOL(dqstats); EXPORT_SYMBOL(dqstats);
EXPORT_SYMBOL(dq_data_lock); EXPORT_SYMBOL(dq_data_lock);
EXPORT_SYMBOL(vfs_quota_on); EXPORT_SYMBOL(vfs_quota_on);
EXPORT_SYMBOL(vfs_quota_on_path);
EXPORT_SYMBOL(vfs_quota_on_mount); EXPORT_SYMBOL(vfs_quota_on_mount);
EXPORT_SYMBOL(vfs_quota_off); EXPORT_SYMBOL(vfs_quota_off);
EXPORT_SYMBOL(vfs_quota_sync); EXPORT_SYMBOL(vfs_quota_sync);
......
...@@ -2810,8 +2810,9 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id, ...@@ -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); journal_unlock_updates(EXT3_SB(sb)->s_journal);
} }
err = vfs_quota_on_path(sb, type, format_id, &nd.path);
path_put(&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 /* 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, ...@@ -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); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
} }
err = vfs_quota_on_path(sb, type, format_id, &nd.path);
path_put(&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 /* 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, ...@@ -2076,8 +2076,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
return err; return err;
/* Quotafile not on the same filesystem? */ /* Quotafile not on the same filesystem? */
if (nd.path.mnt->mnt_sb != sb) { if (nd.path.mnt->mnt_sb != sb) {
path_put(&nd.path); err = -EXDEV;
return -EXDEV; goto out;
} }
inode = nd.path.dentry->d_inode; inode = nd.path.dentry->d_inode;
/* We must not pack tails for quota files on reiserfs for quota IO to work */ /* 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, ...@@ -2087,8 +2087,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
reiserfs_warning(sb, reiserfs_warning(sb,
"reiserfs: Unpacking tail of quota file failed" "reiserfs: Unpacking tail of quota file failed"
" (%d). Cannot turn on quotas.", err); " (%d). Cannot turn on quotas.", err);
path_put(&nd.path); err = -EINVAL;
return -EINVAL; goto out;
} }
mark_inode_dirty(inode); mark_inode_dirty(inode);
} }
...@@ -2109,13 +2109,15 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, ...@@ -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 */ /* Just start temporary transaction and finish it */
err = journal_begin(&th, sb, 1); err = journal_begin(&th, sb, 1);
if (err) if (err)
return err; goto out;
err = journal_end_sync(&th, sb, 1); err = journal_end_sync(&th, sb, 1);
if (err) if (err)
return err; goto out;
} }
err = vfs_quota_on_path(sb, type, format_id, &nd.path);
out:
path_put(&nd.path); 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 /* Read data from quotafile - avoid pagecache and such because we cannot afford
......
...@@ -43,6 +43,8 @@ int dquot_mark_dquot_dirty(struct dquot *dquot); ...@@ -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, int vfs_quota_on(struct super_block *sb, int type, int format_id,
char *path, int remount); 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 vfs_quota_on_mount(struct super_block *sb, char *qf_name,
int format_id, int type); int format_id, int type);
int vfs_quota_off(struct super_block *sb, int type, int remount); 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