Commit 4843456c authored by Linus Torvalds's avatar Linus Torvalds

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
parents 2b1caf6e f00c9e44
...@@ -754,7 +754,7 @@ static int ext3_release_dquot(struct dquot *dquot); ...@@ -754,7 +754,7 @@ static int ext3_release_dquot(struct dquot *dquot);
static int ext3_mark_dquot_dirty(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_write_info(struct super_block *sb, int type);
static int ext3_quota_on(struct super_block *sb, int type, int format_id, 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 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, static ssize_t ext3_quota_read(struct super_block *sb, int type, char *data,
size_t len, loff_t off); size_t len, loff_t off);
...@@ -2877,27 +2877,20 @@ static int ext3_quota_on_mount(struct super_block *sb, int type) ...@@ -2877,27 +2877,20 @@ static int ext3_quota_on_mount(struct super_block *sb, int type)
* Standard function to be called on quota_on * Standard function to be called on quota_on
*/ */
static int ext3_quota_on(struct super_block *sb, int type, int format_id, static int ext3_quota_on(struct super_block *sb, int type, int format_id,
char *name) struct path *path)
{ {
int err; int err;
struct path path;
if (!test_opt(sb, QUOTA)) if (!test_opt(sb, QUOTA))
return -EINVAL; return -EINVAL;
err = kern_path(name, LOOKUP_FOLLOW, &path);
if (err)
return err;
/* Quotafile not on the same filesystem? */ /* Quotafile not on the same filesystem? */
if (path.mnt->mnt_sb != sb) { if (path->mnt->mnt_sb != sb)
path_put(&path);
return -EXDEV; return -EXDEV;
}
/* Journaling quota? */ /* Journaling quota? */
if (EXT3_SB(sb)->s_qf_names[type]) { if (EXT3_SB(sb)->s_qf_names[type]) {
/* Quotafile not of fs root? */ /* 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, ext3_msg(sb, KERN_WARNING,
"warning: Quota file not on filesystem root. " "warning: Quota file not on filesystem root. "
"Journaled quota will not work."); "Journaled quota will not work.");
...@@ -2907,7 +2900,7 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id, ...@@ -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 * When we journal data on quota file, we have to flush journal to see
* all updates to the file when we bypass pagecache... * 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 * We don't need to lock updates but journal_flush() could
* otherwise be livelocked... * otherwise be livelocked...
...@@ -2915,15 +2908,11 @@ static int ext3_quota_on(struct super_block *sb, int type, int format_id, ...@@ -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); journal_lock_updates(EXT3_SB(sb)->s_journal);
err = journal_flush(EXT3_SB(sb)->s_journal); err = journal_flush(EXT3_SB(sb)->s_journal);
journal_unlock_updates(EXT3_SB(sb)->s_journal); journal_unlock_updates(EXT3_SB(sb)->s_journal);
if (err) { if (err)
path_put(&path);
return err; return err;
} }
}
err = dquot_quota_on_path(sb, type, format_id, &path); return dquot_quota_on(sb, type, format_id, path);
path_put(&path);
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
......
...@@ -1161,7 +1161,7 @@ static int ext4_release_dquot(struct dquot *dquot); ...@@ -1161,7 +1161,7 @@ static int ext4_release_dquot(struct dquot *dquot);
static int ext4_mark_dquot_dirty(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_write_info(struct super_block *sb, int type);
static int ext4_quota_on(struct super_block *sb, int type, int format_id, 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_off(struct super_block *sb, int type);
static int ext4_quota_on_mount(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, 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) ...@@ -4558,27 +4558,20 @@ static int ext4_quota_on_mount(struct super_block *sb, int type)
* Standard function to be called on quota_on * Standard function to be called on quota_on
*/ */
static int ext4_quota_on(struct super_block *sb, int type, int format_id, static int ext4_quota_on(struct super_block *sb, int type, int format_id,
char *name) struct path *path)
{ {
int err; int err;
struct path path;
if (!test_opt(sb, QUOTA)) if (!test_opt(sb, QUOTA))
return -EINVAL; return -EINVAL;
err = kern_path(name, LOOKUP_FOLLOW, &path);
if (err)
return err;
/* Quotafile not on the same filesystem? */ /* Quotafile not on the same filesystem? */
if (path.mnt->mnt_sb != sb) { if (path->mnt->mnt_sb != sb)
path_put(&path);
return -EXDEV; return -EXDEV;
}
/* Journaling quota? */ /* Journaling quota? */
if (EXT4_SB(sb)->s_qf_names[type]) { if (EXT4_SB(sb)->s_qf_names[type]) {
/* Quotafile not in fs root? */ /* 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, ext4_msg(sb, KERN_WARNING,
"Quota file not on filesystem root. " "Quota file not on filesystem root. "
"Journaled quota will not work"); "Journaled quota will not work");
...@@ -4589,7 +4582,7 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id, ...@@ -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... * all updates to the file when we bypass pagecache...
*/ */
if (EXT4_SB(sb)->s_journal && 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 * We don't need to lock updates but journal_flush() could
* otherwise be livelocked... * otherwise be livelocked...
...@@ -4597,15 +4590,11 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id, ...@@ -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); jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
err = jbd2_journal_flush(EXT4_SB(sb)->s_journal); err = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
if (err) { if (err)
path_put(&path);
return err; return err;
} }
}
err = dquot_quota_on_path(sb, type, format_id, &path); return dquot_quota_on(sb, type, format_id, path);
path_put(&path);
return err;
} }
static int ext4_quota_off(struct super_block *sb, int type) static int ext4_quota_off(struct super_block *sb, int type)
......
...@@ -993,8 +993,7 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb) ...@@ -993,8 +993,7 @@ static void ocfs2_disable_quotas(struct ocfs2_super *osb)
} }
/* Handle quota on quotactl */ /* Handle quota on quotactl */
static int ocfs2_quota_on(struct super_block *sb, int type, int format_id, static int ocfs2_quota_on(struct super_block *sb, int type, int format_id)
char *path)
{ {
unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA, unsigned int feature[MAXQUOTAS] = { OCFS2_FEATURE_RO_COMPAT_USRQUOTA,
OCFS2_FEATURE_RO_COMPAT_GRPQUOTA}; OCFS2_FEATURE_RO_COMPAT_GRPQUOTA};
...@@ -1013,7 +1012,7 @@ static int ocfs2_quota_off(struct super_block *sb, int type) ...@@ -1013,7 +1012,7 @@ static int ocfs2_quota_off(struct super_block *sb, int type)
} }
static const struct quotactl_ops ocfs2_quotactl_ops = { 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_off = ocfs2_quota_off,
.quota_sync = dquot_quota_sync, .quota_sync = dquot_quota_sync,
.get_info = dquot_get_dqinfo, .get_info = dquot_get_dqinfo,
......
...@@ -2189,7 +2189,7 @@ int dquot_resume(struct super_block *sb, int type) ...@@ -2189,7 +2189,7 @@ int dquot_resume(struct super_block *sb, int type)
} }
EXPORT_SYMBOL(dquot_resume); 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) struct path *path)
{ {
int error = security_quota_on(path->dentry); 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, ...@@ -2204,20 +2204,6 @@ int dquot_quota_on_path(struct super_block *sb, int type, int format_id,
DQUOT_LIMITS_ENABLED); DQUOT_LIMITS_ENABLED);
return error; 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); EXPORT_SYMBOL(dquot_quota_on);
/* /*
......
...@@ -64,18 +64,15 @@ static int quota_sync_all(int type) ...@@ -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, static int quota_quotaon(struct super_block *sb, int type, int cmd, qid_t id,
void __user *addr) struct path *path)
{ {
char *pathname; if (!sb->s_qcop->quota_on && !sb->s_qcop->quota_on_meta)
int ret = -ENOSYS; return -ENOSYS;
if (sb->s_qcop->quota_on_meta)
pathname = getname(addr); return sb->s_qcop->quota_on_meta(sb, type, id);
if (IS_ERR(pathname)) if (IS_ERR(path))
return PTR_ERR(pathname); return PTR_ERR(path);
if (sb->s_qcop->quota_on) return sb->s_qcop->quota_on(sb, type, id, path);
ret = sb->s_qcop->quota_on(sb, type, id, pathname);
putname(pathname);
return ret;
} }
static int quota_getfmt(struct super_block *sb, int type, void __user *addr) 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, ...@@ -241,7 +238,7 @@ static int quota_getxquota(struct super_block *sb, int type, qid_t id,
/* Copy parameters and call proper function */ /* Copy parameters and call proper function */
static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, 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; int ret;
...@@ -256,7 +253,7 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, ...@@ -256,7 +253,7 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
switch (cmd) { switch (cmd) {
case Q_QUOTAON: case Q_QUOTAON:
return quota_quotaon(sb, type, cmd, id, addr); return quota_quotaon(sb, type, cmd, id, path);
case Q_QUOTAOFF: case Q_QUOTAOFF:
if (!sb->s_qcop->quota_off) if (!sb->s_qcop->quota_off)
return -ENOSYS; return -ENOSYS;
...@@ -335,6 +332,7 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special, ...@@ -335,6 +332,7 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
{ {
uint cmds, type; uint cmds, type;
struct super_block *sb = NULL; struct super_block *sb = NULL;
struct path path, *pathp = NULL;
int ret; int ret;
cmds = cmd >> SUBCMDSHIFT; cmds = cmd >> SUBCMDSHIFT;
...@@ -351,12 +349,27 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special, ...@@ -351,12 +349,27 @@ SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
return -ENODEV; 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); sb = quotactl_block(special);
if (IS_ERR(sb)) if (IS_ERR(sb))
return PTR_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); drop_super(sb);
if (pathp && !IS_ERR(pathp))
path_put(pathp);
return ret; return ret;
} }
...@@ -632,7 +632,7 @@ static int reiserfs_acquire_dquot(struct dquot *); ...@@ -632,7 +632,7 @@ static int reiserfs_acquire_dquot(struct dquot *);
static int reiserfs_release_dquot(struct dquot *); static int reiserfs_release_dquot(struct dquot *);
static int reiserfs_mark_dquot_dirty(struct dquot *); static int reiserfs_mark_dquot_dirty(struct dquot *);
static int reiserfs_write_info(struct super_block *, int); 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 = { static const struct dquot_operations reiserfs_quota_operations = {
.write_dquot = reiserfs_write_dquot, .write_dquot = reiserfs_write_dquot,
...@@ -2048,25 +2048,21 @@ static int reiserfs_quota_on_mount(struct super_block *sb, int type) ...@@ -2048,25 +2048,21 @@ static int reiserfs_quota_on_mount(struct super_block *sb, int type)
* Standard function to be called on quota_on * Standard function to be called on quota_on
*/ */
static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
char *name) struct path *path)
{ {
int err; int err;
struct path path;
struct inode *inode; struct inode *inode;
struct reiserfs_transaction_handle th; struct reiserfs_transaction_handle th;
if (!(REISERFS_SB(sb)->s_mount_opt & (1 << REISERFS_QUOTA))) if (!(REISERFS_SB(sb)->s_mount_opt & (1 << REISERFS_QUOTA)))
return -EINVAL; return -EINVAL;
err = kern_path(name, LOOKUP_FOLLOW, &path);
if (err)
return err;
/* Quotafile not on the same filesystem? */ /* Quotafile not on the same filesystem? */
if (path.mnt->mnt_sb != sb) { if (path->mnt->mnt_sb != sb) {
err = -EXDEV; err = -EXDEV;
goto out; 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 */ /* We must not pack tails for quota files on reiserfs for quota IO to work */
if (!(REISERFS_I(inode)->i_flags & i_nopack_mask)) { if (!(REISERFS_I(inode)->i_flags & i_nopack_mask)) {
err = reiserfs_unpack(inode, NULL); err = reiserfs_unpack(inode, NULL);
...@@ -2082,7 +2078,7 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, ...@@ -2082,7 +2078,7 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
/* Journaling quota? */ /* Journaling quota? */
if (REISERFS_SB(sb)->s_qf_names[type]) { if (REISERFS_SB(sb)->s_qf_names[type]) {
/* Quotafile not of fs root? */ /* 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", reiserfs_warning(sb, "super-6521",
"Quota file not on filesystem root. " "Quota file not on filesystem root. "
"Journalled quota will not work."); "Journalled quota will not work.");
...@@ -2101,9 +2097,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id, ...@@ -2101,9 +2097,8 @@ static int reiserfs_quota_on(struct super_block *sb, int type, int format_id,
if (err) if (err)
goto out; goto out;
} }
err = dquot_quota_on_path(sb, type, format_id, &path); err = dquot_quota_on(sb, type, format_id, path);
out: out:
path_put(&path);
return err; return err;
} }
......
...@@ -322,9 +322,12 @@ struct dquot_operations { ...@@ -322,9 +322,12 @@ struct dquot_operations {
qsize_t *(*get_reserved_space) (struct inode *); qsize_t *(*get_reserved_space) (struct inode *);
}; };
struct path;
/* Operations handling requests from userspace */ /* Operations handling requests from userspace */
struct quotactl_ops { 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_off)(struct super_block *, int);
int (*quota_sync)(struct super_block *, int, int); int (*quota_sync)(struct super_block *, int, int);
int (*get_info)(struct super_block *, int, struct if_dqinfo *); int (*get_info)(struct super_block *, int, struct if_dqinfo *);
......
...@@ -76,11 +76,9 @@ int dquot_mark_dquot_dirty(struct dquot *dquot); ...@@ -76,11 +76,9 @@ int dquot_mark_dquot_dirty(struct dquot *dquot);
int dquot_file_open(struct inode *inode, struct file *file); 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, int dquot_enable(struct inode *inode, int type, int format_id,
unsigned int flags); 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); struct path *path);
int dquot_quota_on_mount(struct super_block *sb, char *qf_name, int dquot_quota_on_mount(struct super_block *sb, char *qf_name,
int format_id, int type); int format_id, int type);
......
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