loff_t pos, size_t write_bytes,
                      struct extent_state **cached);
 int btrfs_fdatawrite_range(struct inode *inode, loff_t start, loff_t end);
+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);
 
 /* tree-defrag.c */
 int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
 
        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:
         *   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);
                btrfs_double_inode_unlock(src, inode);
        else
                mutex_unlock(&src->i_mutex);
+       return ret;
+}
+
+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)
+{
+       ssize_t ret;
+
+       ret = btrfs_clone_files(file_out, file_in, pos_in, len, pos_out);
+       if (ret == 0)
+               ret = len;
+       return ret;
+}
+
+static noinline long btrfs_ioctl_clone(struct file *file, unsigned long srcfd,
+                                      u64 off, u64 olen, u64 destoff)
+{
+       struct fd src_file;
+       int ret;
+
+       /* the destination must be opened for writing */
+       if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND))
+               return -EINVAL;
+
+       ret = mnt_want_write_file(file);
+       if (ret)
+               return ret;
+
+       src_file = fdget(srcfd);
+       if (!src_file.file) {
+               ret = -EBADF;
+               goto out_drop_write;
+       }
+
+       /* the src must be open for reading */
+       if (!(src_file.file->f_mode & FMODE_READ)) {
+               ret = -EINVAL;
+               goto out_fput;
+       }
+
+       ret = btrfs_clone_files(file, src_file.file, off, olen, destoff);
+
 out_fput:
        fdput(src_file);
 out_drop_write: