Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs-2.6
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs-2.6: quota: Fix deadlock during path resolution
This commit is contained in:
commit
4843456c5c
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -2189,8 +2189,8 @@ 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,
|
||||
struct path *path)
|
||||
int dquot_quota_on(struct super_block *sb, int type, int format_id,
|
||||
struct path *path)
|
||||
{
|
||||
int error = security_quota_on(path->dentry);
|
||||
if (error)
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -632,7 +632,7 @@ static int reiserfs_acquire_dquot(struct dquot *);
|
|||
static int reiserfs_release_dquot(struct dquot *);
|
||||
static int reiserfs_mark_dquot_dirty(struct dquot *);
|
||||
static int reiserfs_write_info(struct super_block *, int);
|
||||
static int reiserfs_quota_on(struct super_block *, int, int, char *);
|
||||
static int reiserfs_quota_on(struct super_block *, int, int, struct path *);
|
||||
|
||||
static const struct dquot_operations reiserfs_quota_operations = {
|
||||
.write_dquot = reiserfs_write_dquot,
|
||||
|
@ -2048,25 +2048,21 @@ static int reiserfs_quota_on_mount(struct super_block *sb, int type)
|
|||
* Standard function to be called on quota_on
|
||||
*/
|
||||
static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
|
||||
char *name)
|
||||
struct path *path)
|
||||
{
|
||||
int err;
|
||||
struct path path;
|
||||
struct inode *inode;
|
||||
struct reiserfs_transaction_handle th;
|
||||
|
||||
if (!(REISERFS_SB(sb)->s_mount_opt & (1 << REISERFS_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) {
|
||||
if (path->mnt->mnt_sb != sb) {
|
||||
err = -EXDEV;
|
||||
goto out;
|
||||
}
|
||||
inode = path.dentry->d_inode;
|
||||
inode = path->dentry->d_inode;
|
||||
/* We must not pack tails for quota files on reiserfs for quota IO to work */
|
||||
if (!(REISERFS_I(inode)->i_flags & i_nopack_mask)) {
|
||||
err = reiserfs_unpack(inode, NULL);
|
||||
|
@ -2082,7 +2078,7 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
|
|||
/* Journaling quota? */
|
||||
if (REISERFS_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)
|
||||
reiserfs_warning(sb, "super-6521",
|
||||
"Quota file not on filesystem root. "
|
||||
"Journalled quota will not work.");
|
||||
|
@ -2101,9 +2097,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
|
|||
if (err)
|
||||
goto out;
|
||||
}
|
||||
err = dquot_quota_on_path(sb, type, format_id, &path);
|
||||
err = dquot_quota_on(sb, type, format_id, path);
|
||||
out:
|
||||
path_put(&path);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
@ -322,9 +322,12 @@ struct dquot_operations {
|
|||
qsize_t *(*get_reserved_space) (struct inode *);
|
||||
};
|
||||
|
||||
struct path;
|
||||
|
||||
/* Operations handling requests from userspace */
|
||||
struct quotactl_ops {
|
||||
int (*quota_on)(struct super_block *, int, int, char *);
|
||||
int (*quota_on)(struct super_block *, int, int, struct path *);
|
||||
int (*quota_on_meta)(struct super_block *, int, int);
|
||||
int (*quota_off)(struct super_block *, int);
|
||||
int (*quota_sync)(struct super_block *, int, int);
|
||||
int (*get_info)(struct super_block *, int, struct if_dqinfo *);
|
||||
|
|
|
@ -76,11 +76,9 @@ int dquot_mark_dquot_dirty(struct dquot *dquot);
|
|||
|
||||
int dquot_file_open(struct inode *inode, struct file *file);
|
||||
|
||||
int dquot_quota_on(struct super_block *sb, int type, int format_id,
|
||||
char *path);
|
||||
int dquot_enable(struct inode *inode, int type, int format_id,
|
||||
unsigned int flags);
|
||||
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 dquot_quota_on_mount(struct super_block *sb, char *qf_name,
|
||||
int format_id, int type);
|
||||
|
|
Loading…
Reference in New Issue