diff options
Diffstat (limited to 'fs/btrfs/ioctl.c')
| -rw-r--r-- | fs/btrfs/ioctl.c | 186 | 
1 files changed, 36 insertions, 150 deletions
| diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index da94138eb85e..e21997385d14 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -2962,7 +2962,7 @@ static int btrfs_cmp_data(struct inode *src, u64 loff, struct inode *dst,  		flush_dcache_page(dst_page);  		if (memcmp(addr, dst_addr, cmp_len)) -			ret = BTRFS_SAME_DATA_DIFFERS; +			ret = -EBADE;  		kunmap_atomic(addr);  		kunmap_atomic(dst_addr); @@ -3098,53 +3098,16 @@ out_unlock:  #define BTRFS_MAX_DEDUPE_LEN	(16 * 1024 * 1024) -static long btrfs_ioctl_file_extent_same(struct file *file, -			struct btrfs_ioctl_same_args __user *argp) +ssize_t btrfs_dedupe_file_range(struct file *src_file, u64 loff, u64 olen, +				struct file *dst_file, u64 dst_loff)  { -	struct btrfs_ioctl_same_args *same = NULL; -	struct btrfs_ioctl_same_extent_info *info; -	struct inode *src = file_inode(file); -	u64 off; -	u64 len; -	int i; -	int ret; -	unsigned long size; +	struct inode *src = file_inode(src_file); +	struct inode *dst = file_inode(dst_file);  	u64 bs = BTRFS_I(src)->root->fs_info->sb->s_blocksize; -	bool is_admin = capable(CAP_SYS_ADMIN); -	u16 count; - -	if (!(file->f_mode & FMODE_READ)) -		return -EINVAL; - -	ret = mnt_want_write_file(file); -	if (ret) -		return ret; - -	if (get_user(count, &argp->dest_count)) { -		ret = -EFAULT; -		goto out; -	} - -	size = offsetof(struct btrfs_ioctl_same_args __user, info[count]); - -	same = memdup_user(argp, size); - -	if (IS_ERR(same)) { -		ret = PTR_ERR(same); -		same = NULL; -		goto out; -	} +	ssize_t res; -	off = same->logical_offset; -	len = same->length; - -	/* -	 * Limit the total length we will dedupe for each operation. -	 * This is intended to bound the total time spent in this -	 * ioctl to something sane. -	 */ -	if (len > BTRFS_MAX_DEDUPE_LEN) -		len = BTRFS_MAX_DEDUPE_LEN; +	if (olen > BTRFS_MAX_DEDUPE_LEN) +		olen = BTRFS_MAX_DEDUPE_LEN;  	if (WARN_ON_ONCE(bs < PAGE_CACHE_SIZE)) {  		/* @@ -3152,58 +3115,13 @@ static long btrfs_ioctl_file_extent_same(struct file *file,  		 * result, btrfs_cmp_data() won't correctly handle  		 * this situation without an update.  		 */ -		ret = -EINVAL; -		goto out; -	} - -	ret = -EISDIR; -	if (S_ISDIR(src->i_mode)) -		goto out; - -	ret = -EACCES; -	if (!S_ISREG(src->i_mode)) -		goto out; - -	/* pre-format output fields to sane values */ -	for (i = 0; i < count; i++) { -		same->info[i].bytes_deduped = 0ULL; -		same->info[i].status = 0; -	} - -	for (i = 0, info = same->info; i < count; i++, info++) { -		struct inode *dst; -		struct fd dst_file = fdget(info->fd); -		if (!dst_file.file) { -			info->status = -EBADF; -			continue; -		} -		dst = file_inode(dst_file.file); - -		if (!(is_admin || (dst_file.file->f_mode & FMODE_WRITE))) { -			info->status = -EINVAL; -		} else if (file->f_path.mnt != dst_file.file->f_path.mnt) { -			info->status = -EXDEV; -		} else if (S_ISDIR(dst->i_mode)) { -			info->status = -EISDIR; -		} else if (!S_ISREG(dst->i_mode)) { -			info->status = -EACCES; -		} else { -			info->status = btrfs_extent_same(src, off, len, dst, -							info->logical_offset); -			if (info->status == 0) -				info->bytes_deduped += len; -		} -		fdput(dst_file); +		return -EINVAL;  	} -	ret = copy_to_user(argp, same, size); -	if (ret) -		ret = -EFAULT; - -out: -	mnt_drop_write_file(file); -	kfree(same); -	return ret; +	res = btrfs_extent_same(src, loff, olen, dst, dst_loff); +	if (res) +		return res; +	return olen;  }  static int clone_finish_inode_update(struct btrfs_trans_handle *trans, @@ -3779,17 +3697,16 @@ out:  	return ret;  } -static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd, -				       u64 off, u64 olen, u64 destoff) +static noinline int btrfs_clone_files(struct file *file, struct file *file_src, +					u64 off, u64 olen, u64 destoff)  {  	struct inode *inode = file_inode(file); +	struct inode *src = file_inode(file_src);  	struct btrfs_root *root = BTRFS_I(inode)->root; -	struct fd src_file; -	struct inode *src;  	int ret;  	u64 len = olen;  	u64 bs = root->fs_info->sb->s_blocksize; -	int same_inode = 0; +	int same_inode = src == inode;  	/*  	 * TODO: @@ -3802,49 +3719,20 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,  	 *   be either compressed or non-compressed.  	 */ -	/* the destination must be opened for writing */ -	if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND)) -		return -EINVAL; -  	if (btrfs_root_readonly(root))  		return -EROFS; -	ret = mnt_want_write_file(file); -	if (ret) -		return ret; - -	src_file = fdget(srcfd); -	if (!src_file.file) { -		ret = -EBADF; -		goto out_drop_write; -	} - -	ret = -EXDEV; -	if (src_file.file->f_path.mnt != file->f_path.mnt) -		goto out_fput; - -	src = file_inode(src_file.file); - -	ret = -EINVAL; -	if (src == inode) -		same_inode = 1; - -	/* the src must be open for reading */ -	if (!(src_file.file->f_mode & FMODE_READ)) -		goto out_fput; +	if (file_src->f_path.mnt != file->f_path.mnt || +	    src->i_sb != inode->i_sb) +		return -EXDEV;  	/* don't make the dst file partly checksummed */  	if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=  	    (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) -		goto out_fput; +		return -EINVAL; -	ret = -EISDIR;  	if (S_ISDIR(src->i_mode) || S_ISDIR(inode->i_mode)) -		goto out_fput; - -	ret = -EXDEV; -	if (src->i_sb != inode->i_sb) -		goto out_fput; +		return -EISDIR;  	if (!same_inode) {  		btrfs_double_inode_lock(src, inode); @@ -3921,21 +3809,25 @@ out_unlock:  		btrfs_double_inode_unlock(src, inode);  	else  		mutex_unlock(&src->i_mutex); -out_fput: -	fdput(src_file); -out_drop_write: -	mnt_drop_write_file(file);  	return ret;  } -static long btrfs_ioctl_clone_range(struct file *file, void __user *argp) +ssize_t btrfs_copy_file_range(struct file *file_in, loff_t pos_in, +			      struct file *file_out, loff_t pos_out, +			      size_t len, unsigned int flags)  { -	struct btrfs_ioctl_clone_range_args args; +	ssize_t ret; -	if (copy_from_user(&args, argp, sizeof(args))) -		return -EFAULT; -	return btrfs_ioctl_clone(file, args.src_fd, args.src_offset, -				 args.src_length, args.dest_offset); +	ret = btrfs_clone_files(file_out, file_in, pos_in, len, pos_out); +	if (ret == 0) +		ret = len; +	return ret; +} + +int btrfs_clone_file_range(struct file *src_file, loff_t off, +		struct file *dst_file, loff_t destoff, u64 len) +{ +	return btrfs_clone_files(dst_file, src_file, off, len, destoff);  }  /* @@ -5485,10 +5377,6 @@ long btrfs_ioctl(struct file *file, unsigned int  		return btrfs_ioctl_dev_info(root, argp);  	case BTRFS_IOC_BALANCE:  		return btrfs_ioctl_balance(file, NULL); -	case BTRFS_IOC_CLONE: -		return btrfs_ioctl_clone(file, arg, 0, 0, 0); -	case BTRFS_IOC_CLONE_RANGE: -		return btrfs_ioctl_clone_range(file, argp);  	case BTRFS_IOC_TRANS_START:  		return btrfs_ioctl_trans_start(file);  	case BTRFS_IOC_TRANS_END: @@ -5566,8 +5454,6 @@ long btrfs_ioctl(struct file *file, unsigned int  		return btrfs_ioctl_get_fslabel(file, argp);  	case BTRFS_IOC_SET_FSLABEL:  		return btrfs_ioctl_set_fslabel(file, argp); -	case BTRFS_IOC_FILE_EXTENT_SAME: -		return btrfs_ioctl_file_extent_same(file, argp);  	case BTRFS_IOC_GET_SUPPORTED_FEATURES:  		return btrfs_ioctl_get_supported_features(file, argp);  	case BTRFS_IOC_GET_FEATURES: | 
