diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
| -rw-r--r-- | fs/btrfs/ioctl.c | 129 | 
1 files changed, 94 insertions, 35 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 4b4516770f0..5b22d45d3c6 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1339,7 +1339,8 @@ static noinline int btrfs_ioctl_resize(struct file *file,  	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; +		mnt_drop_write_file(file); +		return -EINVAL;  	}  	mutex_lock(&root->fs_info->volume_mutex); @@ -1362,6 +1363,7 @@ static noinline int btrfs_ioctl_resize(struct file *file,  		printk(KERN_INFO "btrfs: resizing devid %llu\n",  		       (unsigned long long)devid);  	} +  	device = btrfs_find_device(root->fs_info, devid, NULL, NULL);  	if (!device) {  		printk(KERN_INFO "btrfs: resizer unable to find device %llu\n", @@ -1369,9 +1371,10 @@ static noinline int btrfs_ioctl_resize(struct file *file,  		ret = -EINVAL;  		goto out_free;  	} -	if (device->fs_devices && device->fs_devices->seeding) { + +	if (!device->writeable) {  		printk(KERN_INFO "btrfs: resizer unable to apply on " -		       "seeding device %llu\n", +		       "readonly device %llu\n",  		       (unsigned long long)devid);  		ret = -EINVAL;  		goto out_free; @@ -1443,8 +1446,8 @@ out_free:  	kfree(vol_args);  out:  	mutex_unlock(&root->fs_info->volume_mutex); -	mnt_drop_write_file(file);  	atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); +	mnt_drop_write_file(file);  	return ret;  } @@ -2095,13 +2098,13 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,  		err = inode_permission(inode, MAY_WRITE | MAY_EXEC);  		if (err)  			goto out_dput; - -		/* check if subvolume may be deleted by a non-root user */ -		err = btrfs_may_delete(dir, dentry, 1); -		if (err) -			goto out_dput;  	} +	/* check if subvolume may be deleted by a user */ +	err = btrfs_may_delete(dir, dentry, 1); +	if (err) +		goto out_dput; +  	if (btrfs_ino(inode) != BTRFS_FIRST_FREE_OBJECTID) {  		err = -EINVAL;  		goto out_dput; @@ -2183,19 +2186,20 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)  	struct btrfs_ioctl_defrag_range_args *range;  	int ret; -	if (btrfs_root_readonly(root)) -		return -EROFS; +	ret = mnt_want_write_file(file); +	if (ret) +		return ret;  	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; +		mnt_drop_write_file(file); +		return -EINVAL;  	} -	ret = mnt_want_write_file(file); -	if (ret) { -		atomic_set(&root->fs_info->mutually_exclusive_operation_running, -			   0); -		return ret; + +	if (btrfs_root_readonly(root)) { +		ret = -EROFS; +		goto out;  	}  	switch (inode->i_mode & S_IFMT) { @@ -2247,8 +2251,8 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)  		ret = -EINVAL;  	}  out: -	mnt_drop_write_file(file);  	atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); +	mnt_drop_write_file(file);  	return ret;  } @@ -2263,7 +2267,7 @@ static long btrfs_ioctl_add_dev(struct btrfs_root *root, void __user *arg)  	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; +		return -EINVAL;  	}  	mutex_lock(&root->fs_info->volume_mutex); @@ -2300,7 +2304,7 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)  			1)) {  		pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n");  		mnt_drop_write_file(file); -		return -EINPROGRESS; +		return -EINVAL;  	}  	mutex_lock(&root->fs_info->volume_mutex); @@ -2316,8 +2320,8 @@ static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)  	kfree(vol_args);  out:  	mutex_unlock(&root->fs_info->volume_mutex); -	mnt_drop_write_file(file);  	atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); +	mnt_drop_write_file(file);  	return ret;  } @@ -3437,8 +3441,8 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)  	struct btrfs_fs_info *fs_info = root->fs_info;  	struct btrfs_ioctl_balance_args *bargs;  	struct btrfs_balance_control *bctl; +	bool need_unlock; /* for mut. excl. ops lock */  	int ret; -	int need_to_clear_lock = 0;  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; @@ -3447,14 +3451,61 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)  	if (ret)  		return ret; -	mutex_lock(&fs_info->volume_mutex); +again: +	if (!atomic_xchg(&fs_info->mutually_exclusive_operation_running, 1)) { +		mutex_lock(&fs_info->volume_mutex); +		mutex_lock(&fs_info->balance_mutex); +		need_unlock = true; +		goto locked; +	} + +	/* +	 * mut. excl. ops lock is locked.  Three possibilites: +	 *   (1) some other op is running +	 *   (2) balance is running +	 *   (3) balance is paused -- special case (think resume) +	 */  	mutex_lock(&fs_info->balance_mutex); +	if (fs_info->balance_ctl) { +		/* this is either (2) or (3) */ +		if (!atomic_read(&fs_info->balance_running)) { +			mutex_unlock(&fs_info->balance_mutex); +			if (!mutex_trylock(&fs_info->volume_mutex)) +				goto again; +			mutex_lock(&fs_info->balance_mutex); + +			if (fs_info->balance_ctl && +			    !atomic_read(&fs_info->balance_running)) { +				/* this is (3) */ +				need_unlock = false; +				goto locked; +			} + +			mutex_unlock(&fs_info->balance_mutex); +			mutex_unlock(&fs_info->volume_mutex); +			goto again; +		} else { +			/* this is (2) */ +			mutex_unlock(&fs_info->balance_mutex); +			ret = -EINPROGRESS; +			goto out; +		} +	} else { +		/* this is (1) */ +		mutex_unlock(&fs_info->balance_mutex); +		pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n"); +		ret = -EINVAL; +		goto out; +	} + +locked: +	BUG_ON(!atomic_read(&fs_info->mutually_exclusive_operation_running));  	if (arg) {  		bargs = memdup_user(arg, sizeof(*bargs));  		if (IS_ERR(bargs)) {  			ret = PTR_ERR(bargs); -			goto out; +			goto out_unlock;  		}  		if (bargs->flags & BTRFS_BALANCE_RESUME) { @@ -3474,13 +3525,10 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)  		bargs = NULL;  	} -	if (atomic_xchg(&root->fs_info->mutually_exclusive_operation_running, -			1)) { -		pr_info("btrfs: dev add/delete/balance/replace/resize operation in progress\n"); +	if (fs_info->balance_ctl) {  		ret = -EINPROGRESS;  		goto out_bargs;  	} -	need_to_clear_lock = 1;  	bctl = kzalloc(sizeof(*bctl), GFP_NOFS);  	if (!bctl) { @@ -3501,11 +3549,17 @@ static long btrfs_ioctl_balance(struct file *file, void __user *arg)  	}  do_balance: -	ret = btrfs_balance(bctl, bargs);  	/* -	 * bctl is freed in __cancel_balance or in free_fs_info if -	 * restriper was paused all the way until unmount +	 * Ownership of bctl and mutually_exclusive_operation_running +	 * goes to to btrfs_balance.  bctl is freed in __cancel_balance, +	 * or, if restriper was paused all the way until unmount, in +	 * free_fs_info.  mutually_exclusive_operation_running is +	 * cleared in __cancel_balance.  	 */ +	need_unlock = false; + +	ret = btrfs_balance(bctl, bargs); +  	if (arg) {  		if (copy_to_user(arg, bargs, sizeof(*bargs)))  			ret = -EFAULT; @@ -3513,12 +3567,12 @@ do_balance:  out_bargs:  	kfree(bargs); -out: -	if (need_to_clear_lock) -		atomic_set(&root->fs_info->mutually_exclusive_operation_running, -			   0); +out_unlock:  	mutex_unlock(&fs_info->balance_mutex);  	mutex_unlock(&fs_info->volume_mutex); +	if (need_unlock) +		atomic_set(&fs_info->mutually_exclusive_operation_running, 0); +out:  	mnt_drop_write_file(file);  	return ret;  } @@ -3698,6 +3752,11 @@ static long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg)  		goto drop_write;  	} +	if (!sa->qgroupid) { +		ret = -EINVAL; +		goto out; +	} +  	trans = btrfs_join_transaction(root);  	if (IS_ERR(trans)) {  		ret = PTR_ERR(trans);  |