Btrfs: send, improve clone range
[sfrench/cifs-2.6.git] / fs / btrfs / send.c
index 7ea2d6b1f170bc7b20eec66051d69f20986fc827..1e9caa552235a8ebebde9deaff84ad91bfcc6653 100644 (file)
@@ -1160,7 +1160,6 @@ out:
 struct backref_ctx {
        struct send_ctx *sctx;
 
-       struct btrfs_path *path;
        /* number of total found references */
        u64 found;
 
@@ -1213,8 +1212,6 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
 {
        struct backref_ctx *bctx = ctx_;
        struct clone_root *found;
-       int ret;
-       u64 i_size;
 
        /* First check if the root is in the list of accepted clone sources */
        found = bsearch((void *)(uintptr_t)root, bctx->sctx->clone_roots,
@@ -1230,19 +1227,6 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
                bctx->found_itself = 1;
        }
 
-       /*
-        * There are inodes that have extents that lie behind its i_size. Don't
-        * accept clones from these extents.
-        */
-       ret = __get_inode_info(found->root, bctx->path, ino, &i_size, NULL, NULL,
-                              NULL, NULL, NULL);
-       btrfs_release_path(bctx->path);
-       if (ret < 0)
-               return ret;
-
-       if (offset + bctx->data_offset + bctx->extent_len > i_size)
-               return 0;
-
        /*
         * Make sure we don't consider clones from send_root that are
         * behind the current inode/offset.
@@ -1319,8 +1303,6 @@ static int find_extent_clone(struct send_ctx *sctx,
                goto out;
        }
 
-       backref_ctx->path = tmp_path;
-
        if (data_offset >= ino_size) {
                /*
                 * There may be extents that lie behind the file's size.
@@ -5082,6 +5064,7 @@ static int clone_range(struct send_ctx *sctx,
        struct btrfs_path *path;
        struct btrfs_key key;
        int ret;
+       u64 clone_src_i_size;
 
        /*
         * Prevent cloning from a zero offset with a length matching the sector
@@ -5106,6 +5089,16 @@ static int clone_range(struct send_ctx *sctx,
        if (!path)
                return -ENOMEM;
 
+       /*
+        * There are inodes that have extents that lie behind its i_size. Don't
+        * accept clones from these extents.
+        */
+       ret = __get_inode_info(clone_root->root, path, clone_root->ino,
+                              &clone_src_i_size, NULL, NULL, NULL, NULL, NULL);
+       btrfs_release_path(path);
+       if (ret < 0)
+               goto out;
+
        /*
         * We can't send a clone operation for the entire range if we find
         * extent items in the respective range in the source file that
@@ -5148,6 +5141,7 @@ static int clone_range(struct send_ctx *sctx,
                u8 type;
                u64 ext_len;
                u64 clone_len;
+               u64 clone_data_offset;
 
                if (slot >= btrfs_header_nritems(leaf)) {
                        ret = btrfs_next_leaf(clone_root->root, path);
@@ -5201,10 +5195,30 @@ static int clone_range(struct send_ctx *sctx,
                if (key.offset >= clone_root->offset + len)
                        break;
 
+               if (key.offset >= clone_src_i_size)
+                       break;
+
+               if (key.offset + ext_len > clone_src_i_size)
+                       ext_len = clone_src_i_size - key.offset;
+
+               clone_data_offset = btrfs_file_extent_offset(leaf, ei);
+               if (btrfs_file_extent_disk_bytenr(leaf, ei) == disk_byte) {
+                       clone_root->offset = key.offset;
+                       if (clone_data_offset < data_offset &&
+                               clone_data_offset + ext_len > data_offset) {
+                               u64 extent_offset;
+
+                               extent_offset = data_offset - clone_data_offset;
+                               ext_len -= extent_offset;
+                               clone_data_offset += extent_offset;
+                               clone_root->offset += extent_offset;
+                       }
+               }
+
                clone_len = min_t(u64, ext_len, len);
 
                if (btrfs_file_extent_disk_bytenr(leaf, ei) == disk_byte &&
-                   btrfs_file_extent_offset(leaf, ei) == data_offset)
+                   clone_data_offset == data_offset)
                        ret = send_clone(sctx, offset, clone_len, clone_root);
                else
                        ret = send_extent_data(sctx, offset, clone_len);