|
@@ -2962,7 +2962,7 @@ static int btrfs_cmp_data(struct inode *src, u64 loff, struct inode *dst,
|
|
flush_dcache_page(dst_page);
|
|
flush_dcache_page(dst_page);
|
|
|
|
|
|
if (memcmp(addr, dst_addr, cmp_len))
|
|
if (memcmp(addr, dst_addr, cmp_len))
|
|
- ret = BTRFS_SAME_DATA_DIFFERS;
|
|
|
|
|
|
+ ret = -EBADE;
|
|
|
|
|
|
kunmap_atomic(addr);
|
|
kunmap_atomic(addr);
|
|
kunmap_atomic(dst_addr);
|
|
kunmap_atomic(dst_addr);
|
|
@@ -3098,53 +3098,16 @@ out_unlock:
|
|
|
|
|
|
#define BTRFS_MAX_DEDUPE_LEN (16 * 1024 * 1024)
|
|
#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;
|
|
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)) {
|
|
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
|
|
* result, btrfs_cmp_data() won't correctly handle
|
|
* this situation without an update.
|
|
* 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,
|
|
static int clone_finish_inode_update(struct btrfs_trans_handle *trans,
|
|
@@ -3779,17 +3697,16 @@ out:
|
|
return ret;
|
|
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 *inode = file_inode(file);
|
|
|
|
+ struct inode *src = file_inode(file_src);
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
struct btrfs_root *root = BTRFS_I(inode)->root;
|
|
- struct fd src_file;
|
|
|
|
- struct inode *src;
|
|
|
|
int ret;
|
|
int ret;
|
|
u64 len = olen;
|
|
u64 len = olen;
|
|
u64 bs = root->fs_info->sb->s_blocksize;
|
|
u64 bs = root->fs_info->sb->s_blocksize;
|
|
- int same_inode = 0;
|
|
|
|
|
|
+ int same_inode = src == inode;
|
|
|
|
|
|
/*
|
|
/*
|
|
* TODO:
|
|
* TODO:
|
|
@@ -3802,49 +3719,20 @@ static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
|
|
* be either compressed or non-compressed.
|
|
* 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))
|
|
if (btrfs_root_readonly(root))
|
|
return -EROFS;
|
|
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 */
|
|
/* don't make the dst file partly checksummed */
|
|
if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=
|
|
if ((BTRFS_I(src)->flags & BTRFS_INODE_NODATASUM) !=
|
|
(BTRFS_I(inode)->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))
|
|
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) {
|
|
if (!same_inode) {
|
|
btrfs_double_inode_lock(src, inode);
|
|
btrfs_double_inode_lock(src, inode);
|
|
@@ -3921,21 +3809,25 @@ out_unlock:
|
|
btrfs_double_inode_unlock(src, inode);
|
|
btrfs_double_inode_unlock(src, inode);
|
|
else
|
|
else
|
|
mutex_unlock(&src->i_mutex);
|
|
mutex_unlock(&src->i_mutex);
|
|
-out_fput:
|
|
|
|
- fdput(src_file);
|
|
|
|
-out_drop_write:
|
|
|
|
- mnt_drop_write_file(file);
|
|
|
|
return ret;
|
|
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);
|
|
return btrfs_ioctl_dev_info(root, argp);
|
|
case BTRFS_IOC_BALANCE:
|
|
case BTRFS_IOC_BALANCE:
|
|
return btrfs_ioctl_balance(file, NULL);
|
|
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:
|
|
case BTRFS_IOC_TRANS_START:
|
|
return btrfs_ioctl_trans_start(file);
|
|
return btrfs_ioctl_trans_start(file);
|
|
case BTRFS_IOC_TRANS_END:
|
|
case BTRFS_IOC_TRANS_END:
|
|
@@ -5566,8 +5454,6 @@ long btrfs_ioctl(struct file *file, unsigned int
|
|
return btrfs_ioctl_get_fslabel(file, argp);
|
|
return btrfs_ioctl_get_fslabel(file, argp);
|
|
case BTRFS_IOC_SET_FSLABEL:
|
|
case BTRFS_IOC_SET_FSLABEL:
|
|
return btrfs_ioctl_set_fslabel(file, argp);
|
|
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:
|
|
case BTRFS_IOC_GET_SUPPORTED_FEATURES:
|
|
return btrfs_ioctl_get_supported_features(file, argp);
|
|
return btrfs_ioctl_get_supported_features(file, argp);
|
|
case BTRFS_IOC_GET_FEATURES:
|
|
case BTRFS_IOC_GET_FEATURES:
|