diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
| -rw-r--r-- | fs/btrfs/ioctl.c | 409 | 
1 files changed, 313 insertions, 96 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 5b3429ab8ec..338f2597bf7 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -55,6 +55,7 @@  #include "backref.h"  #include "rcu-string.h"  #include "send.h" +#include "dev-replace.h"  /* Mask out flags that are inappropriate for the given type of inode. */  static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags) @@ -140,8 +141,11 @@ void btrfs_inherit_iflags(struct inode *inode, struct inode *dir)  		BTRFS_I(inode)->flags |= BTRFS_INODE_COMPRESS;  	} -	if (flags & BTRFS_INODE_NODATACOW) +	if (flags & BTRFS_INODE_NODATACOW) {  		BTRFS_I(inode)->flags |= BTRFS_INODE_NODATACOW; +		if (S_ISREG(inode->i_mode)) +			BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM; +	}  	btrfs_update_iflags(inode);  } @@ -511,7 +515,6 @@ static noinline int create_subvol(struct btrfs_root *root,  	BUG_ON(ret); -	d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry));  fail:  	if (async_transid) {  		*async_transid = trans->transid; @@ -521,6 +524,10 @@ fail:  	}  	if (err && !ret)  		ret = err; + +	if (!ret) +		d_instantiate(dentry, btrfs_lookup_dentry(dir, dentry)); +  	return ret;  } @@ -571,8 +578,12 @@ static int create_snapshot(struct btrfs_root *root, struct dentry *dentry,  		ret = btrfs_commit_transaction(trans,  					       root->fs_info->extent_root);  	} -	if (ret) +	if (ret) { +		/* cleanup_transaction has freed this for us */ +		if (trans->aborted) +			pending_snapshot = NULL;  		goto fail; +	}  	ret = pending_snapshot->error;  	if (ret) @@ -705,6 +716,16 @@ static noinline int btrfs_mksubvol(struct path *parent,  	if (error)  		goto out_dput; +	/* +	 * even if this name doesn't exist, we may get hash collisions. +	 * check for them now when we can safely fail +	 */ +	error = btrfs_check_dir_item_collision(BTRFS_I(dir)->root, +					       dir->i_ino, name, +					       namelen); +	if (error) +		goto out_dput; +  	down_read(&BTRFS_I(dir)->root->fs_info->subvol_sem);  	if (btrfs_root_refs(&BTRFS_I(dir)->root->root_item) == 0) @@ -1293,12 +1314,13 @@ out_ra:  	return ret;  } -static noinline int btrfs_ioctl_resize(struct btrfs_root *root, +static noinline int btrfs_ioctl_resize(struct file *file,  					void __user *arg)  {  	u64 new_size;  	u64 old_size;  	u64 devid = 1; +	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;  	struct btrfs_ioctl_vol_args *vol_args;  	struct btrfs_trans_handle *trans;  	struct btrfs_device *device = NULL; @@ -1313,13 +1335,18 @@ 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; +	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"); +		mnt_drop_write_file(file); +		return -EINVAL;  	} +	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); @@ -1339,16 +1366,18 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root,  		printk(KERN_INFO "btrfs: resizing devid %llu\n",  		       (unsigned long long)devid);  	} -	device = btrfs_find_device(root, devid, NULL, NULL); + +	device = btrfs_find_device(root->fs_info, devid, NULL, NULL);  	if (!device) {  		printk(KERN_INFO "btrfs: resizer unable to find device %llu\n",  		       (unsigned long long)devid);  		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; @@ -1371,6 +1400,11 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root,  		}  	} +	if (device->is_tgtdev_for_dev_replace) { +		ret = -EINVAL; +		goto out_free; +	} +  	old_size = device->total_bytes;  	if (mod < 0) { @@ -1409,12 +1443,14 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root,  		btrfs_commit_transaction(trans, root);  	} else if (new_size < old_size) {  		ret = btrfs_shrink_device(device, new_size); -	} +	} /* equal, nothing need to do */  out_free:  	kfree(vol_args);  out:  	mutex_unlock(&root->fs_info->volume_mutex); +	atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0); +	mnt_drop_write_file(file);  	return ret;  } @@ -2065,13 +2101,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; @@ -2153,13 +2189,22 @@ 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"); +		mnt_drop_write_file(file); +		return -EINVAL; +	} + +	if (btrfs_root_readonly(root)) { +		ret = -EROFS; +		goto out; +	} +  	switch (inode->i_mode & S_IFMT) {  	case S_IFDIR:  		if (!capable(CAP_SYS_ADMIN)) { @@ -2209,6 +2254,7 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)  		ret = -EINVAL;  	}  out: +	atomic_set(&root->fs_info->mutually_exclusive_operation_running, 0);  	mnt_drop_write_file(file);  	return ret;  } @@ -2221,13 +2267,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 -EINVAL;  	} +	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); @@ -2240,27 +2286,31 @@ 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;  } -static long btrfs_ioctl_rm_dev(struct btrfs_root *root, void __user *arg) +static long btrfs_ioctl_rm_dev(struct file *file, void __user *arg)  { +	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;  	struct btrfs_ioctl_vol_args *vol_args;  	int ret;  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; -	if (root->fs_info->sb->s_flags & MS_RDONLY) -		return -EROFS; +	ret = mnt_want_write_file(file); +	if (ret) +		return ret; -	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"); +		mnt_drop_write_file(file); +		return -EINVAL;  	} +	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); @@ -2273,6 +2323,8 @@ 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); +	mnt_drop_write_file(file);  	return ret;  } @@ -2328,7 +2380,7 @@ static long btrfs_ioctl_dev_info(struct btrfs_root *root, void __user *arg)  		s_uuid = di_args->uuid;  	mutex_lock(&fs_devices->device_list_mutex); -	dev = btrfs_find_device(root, di_args->devid, s_uuid, NULL); +	dev = btrfs_find_device(root->fs_info, di_args->devid, s_uuid, NULL);  	mutex_unlock(&fs_devices->device_list_mutex);  	if (!dev) { @@ -2821,12 +2873,19 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)  	struct btrfs_disk_key disk_key;  	u64 objectid = 0;  	u64 dir_id; +	int ret;  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; -	if (copy_from_user(&objectid, argp, sizeof(objectid))) -		return -EFAULT; +	ret = mnt_want_write_file(file); +	if (ret) +		return ret; + +	if (copy_from_user(&objectid, argp, sizeof(objectid))) { +		ret = -EFAULT; +		goto out; +	}  	if (!objectid)  		objectid = root->root_key.objectid; @@ -2836,21 +2895,28 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)  	location.offset = (u64)-1;  	new_root = btrfs_read_fs_root_no_name(root->fs_info, &location); -	if (IS_ERR(new_root)) -		return PTR_ERR(new_root); +	if (IS_ERR(new_root)) { +		ret = PTR_ERR(new_root); +		goto out; +	} -	if (btrfs_root_refs(&new_root->root_item) == 0) -		return -ENOENT; +	if (btrfs_root_refs(&new_root->root_item) == 0) { +		ret = -ENOENT; +		goto out; +	}  	path = btrfs_alloc_path(); -	if (!path) -		return -ENOMEM; +	if (!path) { +		ret = -ENOMEM; +		goto out; +	}  	path->leave_spinning = 1;  	trans = btrfs_start_transaction(root, 1);  	if (IS_ERR(trans)) {  		btrfs_free_path(path); -		return PTR_ERR(trans); +		ret = PTR_ERR(trans); +		goto out;  	}  	dir_id = btrfs_super_root_dir(root->fs_info->super_copy); @@ -2861,7 +2927,8 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)  		btrfs_end_transaction(trans, root);  		printk(KERN_ERR "Umm, you don't have the default dir item, "  		       "this isn't going to work\n"); -		return -ENOENT; +		ret = -ENOENT; +		goto out;  	}  	btrfs_cpu_key_to_disk(&disk_key, &new_root->root_key); @@ -2871,8 +2938,9 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp)  	btrfs_set_fs_incompat(root->fs_info, DEFAULT_SUBVOL);  	btrfs_end_transaction(trans, root); - -	return 0; +out: +	mnt_drop_write_file(file); +	return ret;  }  void btrfs_get_block_group_info(struct list_head *groups_list, @@ -3036,32 +3104,38 @@ long btrfs_ioctl_trans_end(struct file *file)  	return 0;  } -static noinline long btrfs_ioctl_start_sync(struct file *file, void __user *argp) +static noinline long btrfs_ioctl_start_sync(struct btrfs_root *root, +					    void __user *argp)  { -	struct btrfs_root *root = BTRFS_I(file->f_dentry->d_inode)->root;  	struct btrfs_trans_handle *trans;  	u64 transid;  	int ret; -	trans = btrfs_start_transaction(root, 0); -	if (IS_ERR(trans)) -		return PTR_ERR(trans); +	trans = btrfs_attach_transaction(root); +	if (IS_ERR(trans)) { +		if (PTR_ERR(trans) != -ENOENT) +			return PTR_ERR(trans); + +		/* No running transaction, don't bother */ +		transid = root->fs_info->last_trans_committed; +		goto out; +	}  	transid = trans->transid;  	ret = btrfs_commit_transaction_async(trans, root, 0);  	if (ret) {  		btrfs_end_transaction(trans, root);  		return ret;  	} - +out:  	if (argp)  		if (copy_to_user(argp, &transid, sizeof(transid)))  			return -EFAULT;  	return 0;  } -static noinline long btrfs_ioctl_wait_sync(struct file *file, void __user *argp) +static noinline long btrfs_ioctl_wait_sync(struct btrfs_root *root, +					   void __user *argp)  { -	struct btrfs_root *root = BTRFS_I(file->f_dentry->d_inode)->root;  	u64 transid;  	if (argp) { @@ -3073,10 +3147,11 @@ static noinline long btrfs_ioctl_wait_sync(struct file *file, void __user *argp)  	return btrfs_wait_for_commit(root, transid);  } -static long btrfs_ioctl_scrub(struct btrfs_root *root, void __user *arg) +static long btrfs_ioctl_scrub(struct file *file, void __user *arg)  { -	int ret; +	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;  	struct btrfs_ioctl_scrub_args *sa; +	int ret;  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; @@ -3085,12 +3160,22 @@ static long btrfs_ioctl_scrub(struct btrfs_root *root, void __user *arg)  	if (IS_ERR(sa))  		return PTR_ERR(sa); -	ret = btrfs_scrub_dev(root, sa->devid, sa->start, sa->end, -			      &sa->progress, sa->flags & BTRFS_SCRUB_READONLY); +	if (!(sa->flags & BTRFS_SCRUB_READONLY)) { +		ret = mnt_want_write_file(file); +		if (ret) +			goto out; +	} + +	ret = btrfs_scrub_dev(root->fs_info, sa->devid, sa->start, sa->end, +			      &sa->progress, sa->flags & BTRFS_SCRUB_READONLY, +			      0);  	if (copy_to_user(arg, sa, sizeof(*sa)))  		ret = -EFAULT; +	if (!(sa->flags & BTRFS_SCRUB_READONLY)) +		mnt_drop_write_file(file); +out:  	kfree(sa);  	return ret;  } @@ -3100,7 +3185,7 @@ static long btrfs_ioctl_scrub_cancel(struct btrfs_root *root, void __user *arg)  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; -	return btrfs_scrub_cancel(root); +	return btrfs_scrub_cancel(root->fs_info);  }  static long btrfs_ioctl_scrub_progress(struct btrfs_root *root, @@ -3149,6 +3234,51 @@ static long btrfs_ioctl_get_dev_stats(struct btrfs_root *root,  	return ret;  } +static long btrfs_ioctl_dev_replace(struct btrfs_root *root, void __user *arg) +{ +	struct btrfs_ioctl_dev_replace_args *p; +	int ret; + +	if (!capable(CAP_SYS_ADMIN)) +		return -EPERM; + +	p = memdup_user(arg, sizeof(*p)); +	if (IS_ERR(p)) +		return PTR_ERR(p); + +	switch (p->cmd) { +	case BTRFS_IOCTL_DEV_REPLACE_CMD_START: +		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; +		} else { +			ret = btrfs_dev_replace_start(root, p); +			atomic_set( +			 &root->fs_info->mutually_exclusive_operation_running, +			 0); +		} +		break; +	case BTRFS_IOCTL_DEV_REPLACE_CMD_STATUS: +		btrfs_dev_replace_status(root->fs_info, p); +		ret = 0; +		break; +	case BTRFS_IOCTL_DEV_REPLACE_CMD_CANCEL: +		ret = btrfs_dev_replace_cancel(root->fs_info, p); +		break; +	default: +		ret = -EINVAL; +		break; +	} + +	if (copy_to_user(arg, p, sizeof(*p))) +		ret = -EFAULT; + +	kfree(p); +	return ret; +} +  static long btrfs_ioctl_ino_to_path(struct btrfs_root *root, void __user *arg)  {  	int ret = 0; @@ -3314,6 +3444,7 @@ 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;  	if (!capable(CAP_SYS_ADMIN)) @@ -3323,14 +3454,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) { @@ -3374,11 +3552,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; @@ -3386,9 +3570,12 @@ do_balance:  out_bargs:  	kfree(bargs); -out: +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;  } @@ -3441,8 +3628,9 @@ out:  	return ret;  } -static long btrfs_ioctl_quota_ctl(struct btrfs_root *root, void __user *arg) +static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg)  { +	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;  	struct btrfs_ioctl_quota_ctl_args *sa;  	struct btrfs_trans_handle *trans = NULL;  	int ret; @@ -3451,12 +3639,15 @@ static long btrfs_ioctl_quota_ctl(struct btrfs_root *root, void __user *arg)  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; -	if (root->fs_info->sb->s_flags & MS_RDONLY) -		return -EROFS; +	ret = mnt_want_write_file(file); +	if (ret) +		return ret;  	sa = memdup_user(arg, sizeof(*sa)); -	if (IS_ERR(sa)) -		return PTR_ERR(sa); +	if (IS_ERR(sa)) { +		ret = PTR_ERR(sa); +		goto drop_write; +	}  	if (sa->cmd != BTRFS_QUOTA_CTL_RESCAN) {  		trans = btrfs_start_transaction(root, 2); @@ -3489,14 +3680,16 @@ static long btrfs_ioctl_quota_ctl(struct btrfs_root *root, void __user *arg)  		if (err && !ret)  			ret = err;  	} -  out:  	kfree(sa); +drop_write: +	mnt_drop_write_file(file);  	return ret;  } -static long btrfs_ioctl_qgroup_assign(struct btrfs_root *root, void __user *arg) +static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg)  { +	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;  	struct btrfs_ioctl_qgroup_assign_args *sa;  	struct btrfs_trans_handle *trans;  	int ret; @@ -3505,12 +3698,15 @@ static long btrfs_ioctl_qgroup_assign(struct btrfs_root *root, void __user *arg)  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; -	if (root->fs_info->sb->s_flags & MS_RDONLY) -		return -EROFS; +	ret = mnt_want_write_file(file); +	if (ret) +		return ret;  	sa = memdup_user(arg, sizeof(*sa)); -	if (IS_ERR(sa)) -		return PTR_ERR(sa); +	if (IS_ERR(sa)) { +		ret = PTR_ERR(sa); +		goto drop_write; +	}  	trans = btrfs_join_transaction(root);  	if (IS_ERR(trans)) { @@ -3533,11 +3729,14 @@ static long btrfs_ioctl_qgroup_assign(struct btrfs_root *root, void __user *arg)  out:  	kfree(sa); +drop_write: +	mnt_drop_write_file(file);  	return ret;  } -static long btrfs_ioctl_qgroup_create(struct btrfs_root *root, void __user *arg) +static long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg)  { +	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;  	struct btrfs_ioctl_qgroup_create_args *sa;  	struct btrfs_trans_handle *trans;  	int ret; @@ -3546,12 +3745,20 @@ static long btrfs_ioctl_qgroup_create(struct btrfs_root *root, void __user *arg)  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; -	if (root->fs_info->sb->s_flags & MS_RDONLY) -		return -EROFS; +	ret = mnt_want_write_file(file); +	if (ret) +		return ret;  	sa = memdup_user(arg, sizeof(*sa)); -	if (IS_ERR(sa)) -		return PTR_ERR(sa); +	if (IS_ERR(sa)) { +		ret = PTR_ERR(sa); +		goto drop_write; +	} + +	if (!sa->qgroupid) { +		ret = -EINVAL; +		goto out; +	}  	trans = btrfs_join_transaction(root);  	if (IS_ERR(trans)) { @@ -3573,11 +3780,14 @@ static long btrfs_ioctl_qgroup_create(struct btrfs_root *root, void __user *arg)  out:  	kfree(sa); +drop_write: +	mnt_drop_write_file(file);  	return ret;  } -static long btrfs_ioctl_qgroup_limit(struct btrfs_root *root, void __user *arg) +static long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg)  { +	struct btrfs_root *root = BTRFS_I(fdentry(file)->d_inode)->root;  	struct btrfs_ioctl_qgroup_limit_args *sa;  	struct btrfs_trans_handle *trans;  	int ret; @@ -3587,12 +3797,15 @@ static long btrfs_ioctl_qgroup_limit(struct btrfs_root *root, void __user *arg)  	if (!capable(CAP_SYS_ADMIN))  		return -EPERM; -	if (root->fs_info->sb->s_flags & MS_RDONLY) -		return -EROFS; +	ret = mnt_want_write_file(file); +	if (ret) +		return ret;  	sa = memdup_user(arg, sizeof(*sa)); -	if (IS_ERR(sa)) -		return PTR_ERR(sa); +	if (IS_ERR(sa)) { +		ret = PTR_ERR(sa); +		goto drop_write; +	}  	trans = btrfs_join_transaction(root);  	if (IS_ERR(trans)) { @@ -3615,6 +3828,8 @@ static long btrfs_ioctl_qgroup_limit(struct btrfs_root *root, void __user *arg)  out:  	kfree(sa); +drop_write: +	mnt_drop_write_file(file);  	return ret;  } @@ -3735,11 +3950,11 @@ long btrfs_ioctl(struct file *file, unsigned int  	case BTRFS_IOC_DEFRAG_RANGE:  		return btrfs_ioctl_defrag(file, argp);  	case BTRFS_IOC_RESIZE: -		return btrfs_ioctl_resize(root, argp); +		return btrfs_ioctl_resize(file, argp);  	case BTRFS_IOC_ADD_DEV:  		return btrfs_ioctl_add_dev(root, argp);  	case BTRFS_IOC_RM_DEV: -		return btrfs_ioctl_rm_dev(root, argp); +		return btrfs_ioctl_rm_dev(file, argp);  	case BTRFS_IOC_FS_INFO:  		return btrfs_ioctl_fs_info(root, argp);  	case BTRFS_IOC_DEV_INFO: @@ -3768,11 +3983,11 @@ long btrfs_ioctl(struct file *file, unsigned int  		btrfs_sync_fs(file->f_dentry->d_sb, 1);  		return 0;  	case BTRFS_IOC_START_SYNC: -		return btrfs_ioctl_start_sync(file, argp); +		return btrfs_ioctl_start_sync(root, argp);  	case BTRFS_IOC_WAIT_SYNC: -		return btrfs_ioctl_wait_sync(file, argp); +		return btrfs_ioctl_wait_sync(root, argp);  	case BTRFS_IOC_SCRUB: -		return btrfs_ioctl_scrub(root, argp); +		return btrfs_ioctl_scrub(file, argp);  	case BTRFS_IOC_SCRUB_CANCEL:  		return btrfs_ioctl_scrub_cancel(root, argp);  	case BTRFS_IOC_SCRUB_PROGRESS: @@ -3790,13 +4005,15 @@ long btrfs_ioctl(struct file *file, unsigned int  	case BTRFS_IOC_GET_DEV_STATS:  		return btrfs_ioctl_get_dev_stats(root, argp);  	case BTRFS_IOC_QUOTA_CTL: -		return btrfs_ioctl_quota_ctl(root, argp); +		return btrfs_ioctl_quota_ctl(file, argp);  	case BTRFS_IOC_QGROUP_ASSIGN: -		return btrfs_ioctl_qgroup_assign(root, argp); +		return btrfs_ioctl_qgroup_assign(file, argp);  	case BTRFS_IOC_QGROUP_CREATE: -		return btrfs_ioctl_qgroup_create(root, argp); +		return btrfs_ioctl_qgroup_create(file, argp);  	case BTRFS_IOC_QGROUP_LIMIT: -		return btrfs_ioctl_qgroup_limit(root, argp); +		return btrfs_ioctl_qgroup_limit(file, argp); +	case BTRFS_IOC_DEV_REPLACE: +		return btrfs_ioctl_dev_replace(root, argp);  	}  	return -ENOTTY;  | 
