diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
| -rw-r--r-- | fs/btrfs/ioctl.c | 294 | 
1 files changed, 250 insertions, 44 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a90e749ed6d..ab620014bcc 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -176,6 +176,8 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)  	struct btrfs_trans_handle *trans;  	unsigned int flags, oldflags;  	int ret; +	u64 ip_oldflags; +	unsigned int i_oldflags;  	if (btrfs_root_readonly(root))  		return -EROFS; @@ -192,6 +194,9 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)  	mutex_lock(&inode->i_mutex); +	ip_oldflags = ip->flags; +	i_oldflags = inode->i_flags; +  	flags = btrfs_mask_flags(inode->i_mode, flags);  	oldflags = btrfs_flags_to_ioctl(ip->flags);  	if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { @@ -201,7 +206,7 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)  		}  	} -	ret = mnt_want_write(file->f_path.mnt); +	ret = mnt_want_write_file(file);  	if (ret)  		goto out_unlock; @@ -249,19 +254,24 @@ static int btrfs_ioctl_setflags(struct file *file, void __user *arg)  		ip->flags &= ~(BTRFS_INODE_COMPRESS | BTRFS_INODE_NOCOMPRESS);  	} -	trans = btrfs_join_transaction(root); -	BUG_ON(IS_ERR(trans)); - -	ret = btrfs_update_inode(trans, root, inode); -	BUG_ON(ret); +	trans = btrfs_start_transaction(root, 1); +	if (IS_ERR(trans)) { +		ret = PTR_ERR(trans); +		goto out_drop; +	}  	btrfs_update_iflags(inode);  	inode->i_ctime = CURRENT_TIME; -	btrfs_end_transaction(trans, root); +	ret = btrfs_update_inode(trans, root, inode); -	mnt_drop_write(file->f_path.mnt); +	btrfs_end_transaction(trans, root); + out_drop: +	if (ret) { +		ip->flags = ip_oldflags; +		inode->i_flags = i_oldflags; +	} -	ret = 0; +	mnt_drop_write_file(file);   out_unlock:  	mutex_unlock(&inode->i_mutex);  	return ret; @@ -276,14 +286,13 @@ static int btrfs_ioctl_getversion(struct file *file, int __user *arg)  static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)  { -	struct btrfs_root *root = fdentry(file)->d_sb->s_fs_info; -	struct btrfs_fs_info *fs_info = root->fs_info; +	struct btrfs_fs_info *fs_info = btrfs_sb(fdentry(file)->d_sb);  	struct btrfs_device *device;  	struct request_queue *q;  	struct fstrim_range range;  	u64 minlen = ULLONG_MAX;  	u64 num_devices = 0; -	u64 total_bytes = btrfs_super_total_bytes(root->fs_info->super_copy); +	u64 total_bytes = btrfs_super_total_bytes(fs_info->super_copy);  	int ret;  	if (!capable(CAP_SYS_ADMIN)) @@ -312,7 +321,7 @@ static noinline int btrfs_ioctl_fitrim(struct file *file, void __user *arg)  	range.len = min(range.len, total_bytes - range.start);  	range.minlen = max(range.minlen, minlen); -	ret = btrfs_trim_fs(root, &range); +	ret = btrfs_trim_fs(fs_info->tree_root, &range);  	if (ret < 0)  		return ret; @@ -358,7 +367,7 @@ static noinline int create_subvol(struct btrfs_root *root,  		return PTR_ERR(trans);  	leaf = btrfs_alloc_free_block(trans, root, root->leafsize, -				      0, objectid, NULL, 0, 0, 0); +				      0, objectid, NULL, 0, 0, 0, 0);  	if (IS_ERR(leaf)) {  		ret = PTR_ERR(leaf);  		goto fail; @@ -1201,13 +1210,21 @@ 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; +	} +  	vol_args = memdup_user(arg, sizeof(*vol_args)); -	if (IS_ERR(vol_args)) -		return PTR_ERR(vol_args); +	if (IS_ERR(vol_args)) { +		ret = PTR_ERR(vol_args); +		goto out; +	}  	vol_args->name[BTRFS_PATH_NAME_MAX] = '\0'; -	mutex_lock(&root->fs_info->volume_mutex);  	sizestr = vol_args->name;  	devstr = strchr(sizestr, ':');  	if (devstr) { @@ -1224,7 +1241,7 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root,  		printk(KERN_INFO "btrfs: resizer unable to find device %llu\n",  		       (unsigned long long)devid);  		ret = -EINVAL; -		goto out_unlock; +		goto out_free;  	}  	if (!strcmp(sizestr, "max"))  		new_size = device->bdev->bd_inode->i_size; @@ -1239,7 +1256,7 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root,  		new_size = memparse(sizestr, NULL);  		if (new_size == 0) {  			ret = -EINVAL; -			goto out_unlock; +			goto out_free;  		}  	} @@ -1248,7 +1265,7 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root,  	if (mod < 0) {  		if (new_size > old_size) {  			ret = -EINVAL; -			goto out_unlock; +			goto out_free;  		}  		new_size = old_size - new_size;  	} else if (mod > 0) { @@ -1257,11 +1274,11 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root,  	if (new_size < 256 * 1024 * 1024) {  		ret = -EINVAL; -		goto out_unlock; +		goto out_free;  	}  	if (new_size > device->bdev->bd_inode->i_size) {  		ret = -EFBIG; -		goto out_unlock; +		goto out_free;  	}  	do_div(new_size, root->sectorsize); @@ -1274,17 +1291,18 @@ static noinline int btrfs_ioctl_resize(struct btrfs_root *root,  		trans = btrfs_start_transaction(root, 0);  		if (IS_ERR(trans)) {  			ret = PTR_ERR(trans); -			goto out_unlock; +			goto out_free;  		}  		ret = btrfs_grow_device(trans, device, new_size);  		btrfs_commit_transaction(trans, root); -	} else { +	} else if (new_size < old_size) {  		ret = btrfs_shrink_device(device, new_size);  	} -out_unlock: -	mutex_unlock(&root->fs_info->volume_mutex); +out_free:  	kfree(vol_args); +out: +	mutex_unlock(&root->fs_info->volume_mutex);  	return ret;  } @@ -1853,7 +1871,7 @@ static noinline int btrfs_ioctl_snap_destroy(struct file *file,  		goto out;  	} -	err = mnt_want_write(file->f_path.mnt); +	err = mnt_want_write_file(file);  	if (err)  		goto out; @@ -1969,7 +1987,7 @@ out_dput:  	dput(dentry);  out_unlock_dir:  	mutex_unlock(&dir->i_mutex); -	mnt_drop_write(file->f_path.mnt); +	mnt_drop_write_file(file);  out:  	kfree(vol_args);  	return err; @@ -1985,7 +2003,7 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)  	if (btrfs_root_readonly(root))  		return -EROFS; -	ret = mnt_want_write(file->f_path.mnt); +	ret = mnt_want_write_file(file);  	if (ret)  		return ret; @@ -2038,7 +2056,7 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)  		ret = -EINVAL;  	}  out: -	mnt_drop_write(file->f_path.mnt); +	mnt_drop_write_file(file);  	return ret;  } @@ -2050,14 +2068,25 @@ 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; +	} +  	vol_args = memdup_user(arg, sizeof(*vol_args)); -	if (IS_ERR(vol_args)) -		return PTR_ERR(vol_args); +	if (IS_ERR(vol_args)) { +		ret = PTR_ERR(vol_args); +		goto out; +	}  	vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';  	ret = btrfs_init_new_device(root, vol_args->name);  	kfree(vol_args); +out: +	mutex_unlock(&root->fs_info->volume_mutex);  	return ret;  } @@ -2072,14 +2101,25 @@ 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; +	} +  	vol_args = memdup_user(arg, sizeof(*vol_args)); -	if (IS_ERR(vol_args)) -		return PTR_ERR(vol_args); +	if (IS_ERR(vol_args)) { +		ret = PTR_ERR(vol_args); +		goto out; +	}  	vol_args->name[BTRFS_PATH_NAME_MAX] = '\0';  	ret = btrfs_rm_device(root, vol_args->name);  	kfree(vol_args); +out: +	mutex_unlock(&root->fs_info->volume_mutex);  	return ret;  } @@ -2193,7 +2233,7 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  	if (btrfs_root_readonly(root))  		return -EROFS; -	ret = mnt_want_write(file->f_path.mnt); +	ret = mnt_want_write_file(file);  	if (ret)  		return ret; @@ -2425,7 +2465,8 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  							disko, diskl, 0,  							root->root_key.objectid,  							btrfs_ino(inode), -							new_key.offset - datao); +							new_key.offset - datao, +							0);  					BUG_ON(ret);  				}  			} else if (type == BTRFS_FILE_EXTENT_INLINE) { @@ -2508,7 +2549,7 @@ out_unlock:  out_fput:  	fput(src_file);  out_drop_write: -	mnt_drop_write(file->f_path.mnt); +	mnt_drop_write_file(file);  	return ret;  } @@ -2547,7 +2588,7 @@ static long btrfs_ioctl_trans_start(struct file *file)  	if (btrfs_root_readonly(root))  		goto out; -	ret = mnt_want_write(file->f_path.mnt); +	ret = mnt_want_write_file(file);  	if (ret)  		goto out; @@ -2563,7 +2604,7 @@ static long btrfs_ioctl_trans_start(struct file *file)  out_drop:  	atomic_dec(&root->fs_info->open_ioctl_trans); -	mnt_drop_write(file->f_path.mnt); +	mnt_drop_write_file(file);  out:  	return ret;  } @@ -2798,7 +2839,7 @@ long btrfs_ioctl_trans_end(struct file *file)  	atomic_dec(&root->fs_info->open_ioctl_trans); -	mnt_drop_write(file->f_path.mnt); +	mnt_drop_write_file(file);  	return 0;  } @@ -2975,7 +3016,7 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_root *root,  {  	int ret = 0;  	int size; -	u64 extent_offset; +	u64 extent_item_pos;  	struct btrfs_ioctl_logical_ino_args *loi;  	struct btrfs_data_container *inodes = NULL;  	struct btrfs_path *path = NULL; @@ -3006,15 +3047,17 @@ static long btrfs_ioctl_logical_to_ino(struct btrfs_root *root,  	}  	ret = extent_from_logical(root->fs_info, loi->logical, path, &key); +	btrfs_release_path(path);  	if (ret & BTRFS_EXTENT_FLAG_TREE_BLOCK)  		ret = -ENOENT;  	if (ret < 0)  		goto out; -	extent_offset = loi->logical - key.objectid; +	extent_item_pos = loi->logical - key.objectid;  	ret = iterate_extent_inodes(root->fs_info, path, key.objectid, -					extent_offset, build_ino_list, inodes); +					extent_item_pos, build_ino_list, +					inodes);  	if (ret < 0)  		goto out; @@ -3032,6 +3075,163 @@ out:  	return ret;  } +void update_ioctl_balance_args(struct btrfs_fs_info *fs_info, int lock, +			       struct btrfs_ioctl_balance_args *bargs) +{ +	struct btrfs_balance_control *bctl = fs_info->balance_ctl; + +	bargs->flags = bctl->flags; + +	if (atomic_read(&fs_info->balance_running)) +		bargs->state |= BTRFS_BALANCE_STATE_RUNNING; +	if (atomic_read(&fs_info->balance_pause_req)) +		bargs->state |= BTRFS_BALANCE_STATE_PAUSE_REQ; +	if (atomic_read(&fs_info->balance_cancel_req)) +		bargs->state |= BTRFS_BALANCE_STATE_CANCEL_REQ; + +	memcpy(&bargs->data, &bctl->data, sizeof(bargs->data)); +	memcpy(&bargs->meta, &bctl->meta, sizeof(bargs->meta)); +	memcpy(&bargs->sys, &bctl->sys, sizeof(bargs->sys)); + +	if (lock) { +		spin_lock(&fs_info->balance_lock); +		memcpy(&bargs->stat, &bctl->stat, sizeof(bargs->stat)); +		spin_unlock(&fs_info->balance_lock); +	} else { +		memcpy(&bargs->stat, &bctl->stat, sizeof(bargs->stat)); +	} +} + +static long btrfs_ioctl_balance(struct btrfs_root *root, void __user *arg) +{ +	struct btrfs_fs_info *fs_info = root->fs_info; +	struct btrfs_ioctl_balance_args *bargs; +	struct btrfs_balance_control *bctl; +	int ret; + +	if (!capable(CAP_SYS_ADMIN)) +		return -EPERM; + +	if (fs_info->sb->s_flags & MS_RDONLY) +		return -EROFS; + +	mutex_lock(&fs_info->volume_mutex); +	mutex_lock(&fs_info->balance_mutex); + +	if (arg) { +		bargs = memdup_user(arg, sizeof(*bargs)); +		if (IS_ERR(bargs)) { +			ret = PTR_ERR(bargs); +			goto out; +		} + +		if (bargs->flags & BTRFS_BALANCE_RESUME) { +			if (!fs_info->balance_ctl) { +				ret = -ENOTCONN; +				goto out_bargs; +			} + +			bctl = fs_info->balance_ctl; +			spin_lock(&fs_info->balance_lock); +			bctl->flags |= BTRFS_BALANCE_RESUME; +			spin_unlock(&fs_info->balance_lock); + +			goto do_balance; +		} +	} else { +		bargs = NULL; +	} + +	if (fs_info->balance_ctl) { +		ret = -EINPROGRESS; +		goto out_bargs; +	} + +	bctl = kzalloc(sizeof(*bctl), GFP_NOFS); +	if (!bctl) { +		ret = -ENOMEM; +		goto out_bargs; +	} + +	bctl->fs_info = fs_info; +	if (arg) { +		memcpy(&bctl->data, &bargs->data, sizeof(bctl->data)); +		memcpy(&bctl->meta, &bargs->meta, sizeof(bctl->meta)); +		memcpy(&bctl->sys, &bargs->sys, sizeof(bctl->sys)); + +		bctl->flags = bargs->flags; +	} else { +		/* balance everything - no filters */ +		bctl->flags |= BTRFS_BALANCE_TYPE_MASK; +	} + +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 +	 */ +	if (arg) { +		if (copy_to_user(arg, bargs, sizeof(*bargs))) +			ret = -EFAULT; +	} + +out_bargs: +	kfree(bargs); +out: +	mutex_unlock(&fs_info->balance_mutex); +	mutex_unlock(&fs_info->volume_mutex); +	return ret; +} + +static long btrfs_ioctl_balance_ctl(struct btrfs_root *root, int cmd) +{ +	if (!capable(CAP_SYS_ADMIN)) +		return -EPERM; + +	switch (cmd) { +	case BTRFS_BALANCE_CTL_PAUSE: +		return btrfs_pause_balance(root->fs_info); +	case BTRFS_BALANCE_CTL_CANCEL: +		return btrfs_cancel_balance(root->fs_info); +	} + +	return -EINVAL; +} + +static long btrfs_ioctl_balance_progress(struct btrfs_root *root, +					 void __user *arg) +{ +	struct btrfs_fs_info *fs_info = root->fs_info; +	struct btrfs_ioctl_balance_args *bargs; +	int ret = 0; + +	if (!capable(CAP_SYS_ADMIN)) +		return -EPERM; + +	mutex_lock(&fs_info->balance_mutex); +	if (!fs_info->balance_ctl) { +		ret = -ENOTCONN; +		goto out; +	} + +	bargs = kzalloc(sizeof(*bargs), GFP_NOFS); +	if (!bargs) { +		ret = -ENOMEM; +		goto out; +	} + +	update_ioctl_balance_args(fs_info, 1, bargs); + +	if (copy_to_user(arg, bargs, sizeof(*bargs))) +		ret = -EFAULT; + +	kfree(bargs); +out: +	mutex_unlock(&fs_info->balance_mutex); +	return ret; +} +  long btrfs_ioctl(struct file *file, unsigned int  		cmd, unsigned long arg)  { @@ -3076,7 +3276,7 @@ long btrfs_ioctl(struct file *file, unsigned int  	case BTRFS_IOC_DEV_INFO:  		return btrfs_ioctl_dev_info(root, argp);  	case BTRFS_IOC_BALANCE: -		return btrfs_balance(root->fs_info->dev_root); +		return btrfs_ioctl_balance(root, NULL);  	case BTRFS_IOC_CLONE:  		return btrfs_ioctl_clone(file, arg, 0, 0, 0);  	case BTRFS_IOC_CLONE_RANGE: @@ -3108,6 +3308,12 @@ long btrfs_ioctl(struct file *file, unsigned int  		return btrfs_ioctl_scrub_cancel(root, argp);  	case BTRFS_IOC_SCRUB_PROGRESS:  		return btrfs_ioctl_scrub_progress(root, argp); +	case BTRFS_IOC_BALANCE_V2: +		return btrfs_ioctl_balance(root, argp); +	case BTRFS_IOC_BALANCE_CTL: +		return btrfs_ioctl_balance_ctl(root, arg); +	case BTRFS_IOC_BALANCE_PROGRESS: +		return btrfs_ioctl_balance_progress(root, argp);  	}  	return -ENOTTY;  | 
