[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: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
1b7e190b47
commit
77e69dac3c
33
fs/dquot.c
33
fs/dquot.c
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue