Skip to content

Commit

Permalink
Btrfs: disallow mutually exclusive admin operations from user mode
Browse files Browse the repository at this point in the history
Btrfs admin operations that are manually started from user mode
and that cannot be executed at the same time return -EINPROGRESS.
A common way to enter and leave this locked section is introduced
since it used to be specific to the balance operation.

Signed-off-by: Stefan Behrens <sbehrens@giantdisaster.de>
Signed-off-by: Chris Mason <chris.mason@fusionio.com>
  • Loading branch information
Stefan Behrens authored and Josef Bacik committed Dec 12, 2012
1 parent a2bff64 commit 5ac00ad
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 17 deletions.
2 changes: 2 additions & 0 deletions fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -1527,6 +1527,8 @@ struct btrfs_fs_info {

/* device replace state */
struct btrfs_dev_replace dev_replace;

atomic_t mutually_exclusive_operation_running;
};

/*
Expand Down
53 changes: 36 additions & 17 deletions fs/btrfs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1317,13 +1317,13 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root,
if (!capable(CAP_SYS_ADMIN))
return -EPERM;

mutex_lock(&root->fs_info->volume_mutex);
if (root->fs_info->balance_ctl) {
printk(KERN_INFO "btrfs: balance in progress\n");
ret = -EINVAL;
goto out;
if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,
1)) {
pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");
return -EINPROGRESS;
}

mutex_lock(&root->fs_info->volume_mutex);
vol_args = memdup_user(arg, sizeof(*vol_args));
if (IS_ERR(vol_args)) {
ret = PTR_ERR(vol_args);
Expand Down Expand Up @@ -1419,6 +1419,7 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root,
kfree(vol_args);
out:
mutex_unlock(&root->fs_info->volume_mutex);
atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0);
return ret;
}

Expand Down Expand Up @@ -2160,9 +2161,17 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)
if (btrfs_root_readonly(root))
return -EROFS;

if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,
1)) {
pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");
return -EINPROGRESS;
}
ret = mnt_want_write_file(file);
if (ret)
if (ret) {
atomic_set(&root->fs_info->mutually_exclusive_operation_running,
0);
return ret;
}

switch (inode->i_mode & S_IFMT) {
case S_IFDIR:
Expand Down Expand Up @@ -2214,6 +2223,7 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)
}
out:
mnt_drop_write_file(file);
atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0);
return ret;
}

Expand All @@ -2225,13 +2235,13 @@ static long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;

mutex_lock(&root->fs_info->volume_mutex);
if (root->fs_info->balance_ctl) {
printk(KERN_INFO "btrfs: balance in progress\n");
ret = -EINVAL;
goto out;
if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,
1)) {
pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");
return -EINPROGRESS;
}

mutex_lock(&root->fs_info->volume_mutex);
vol_args = memdup_user(arg, sizeof(*vol_args));
if (IS_ERR(vol_args)) {
ret = PTR_ERR(vol_args);
Expand All @@ -2244,6 +2254,7 @@ static long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg)
kfree(vol_args);
out:
mutex_unlock(&root->fs_info->volume_mutex);
atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0);
return ret;
}

Expand All @@ -2258,13 +2269,13 @@ static long btrfs_ioctl_rm_dev(struct btrfs_root *root, void __user *arg)
if (root->fs_info->sb->s_flags & MS_RDONLY)
return -EROFS;

mutex_lock(&root->fs_info->volume_mutex);
if (root->fs_info->balance_ctl) {
printk(KERN_INFO "btrfs: balance in progress\n");
ret = -EINVAL;
goto out;
if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,
1)) {
pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");
return -EINPROGRESS;
}

mutex_lock(&root->fs_info->volume_mutex);
vol_args = memdup_user(arg, sizeof(*vol_args));
if (IS_ERR(vol_args)) {
ret = PTR_ERR(vol_args);
Expand All @@ -2277,6 +2288,7 @@ static long btrfs_ioctl_rm_dev(struct btrfs_root *root, void __user *arg)
kfree(vol_args);
out:
mutex_unlock(&root->fs_info->volume_mutex);
atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0);
return ret;
}

Expand Down Expand Up @@ -3319,6 +3331,7 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)
struct btrfs_ioctl_balance_args *bargs;
struct btrfs_balance_control *bctl;
int ret;
int need_to_clear_lock = 0;

if (!capable(CAP_SYS_ADMIN))
return -EPERM;
Expand Down Expand Up @@ -3354,10 +3367,13 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)
bargs = NULL;
}

if (fs_info->balance_ctl) {
if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running,
1)) {
pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");
ret = -EINPROGRESS;
goto out_bargs;
}
need_to_clear_lock = 1;

bctl = kzalloc(sizeof(*bctl), GFP_NOFS);
if (!bctl) {
Expand Down Expand Up @@ -3391,6 +3407,9 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)
out_bargs:
kfree(bargs);
out:
if (need_to_clear_lock)
atomic_set(&root->fs_info->mutually_exclusive_operation_running,
0);
mutex_unlock(&fs_info->balance_mutex);
mutex_unlock(&fs_info->volume_mutex);
mnt_drop_write_file(file);
Expand Down
2 changes: 2 additions & 0 deletions fs/btrfs/volumes.c
Original file line number Diff line number Diff line change
Expand Up @@ -2952,6 +2952,7 @@ static int balance_kthread(void *data)
ret = btrfs_balance(fs_info->balance_ctl, NULL);
}

atomic_set(&fs_info->mutually_exclusive_operation_running, 0);
mutex_unlock(&fs_info->balance_mutex);
mutex_unlock(&fs_info->volume_mutex);

Expand All @@ -2974,6 +2975,7 @@ int btrfs_resume_balance_async(struct btrfs_fs_info *fs_info)
return 0;
}

WARN_ON(atomic_xchg(&fs_info->mutually_exclusive_operation_running, 1));
tsk = kthread_run(balance_kthread, fs_info, "btrfs-balance");
if (IS_ERR(tsk))
return PTR_ERR(tsk);
Expand Down

0 comments on commit 5ac00ad

Please sign in to comment.