OSDN Git Service

Btrfs: incremental send, fix clone operations for compressed extents
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / fs / btrfs / send.c
index a1216f9..5cf7838 100644 (file)
@@ -1158,6 +1158,9 @@ struct backref_ctx {
        /* may be truncated in case it's the last extent in a file */
        u64 extent_len;
 
+       /* data offset in the file extent item */
+       u64 data_offset;
+
        /* Just to check for bugs in backref resolving */
        int found_itself;
 };
@@ -1221,7 +1224,7 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
        if (ret < 0)
                return ret;
 
-       if (offset + bctx->extent_len > i_size)
+       if (offset + bctx->data_offset + bctx->extent_len > i_size)
                return 0;
 
        /*
@@ -1363,6 +1366,19 @@ static int find_extent_clone(struct send_ctx *sctx,
        backref_ctx->cur_offset = data_offset;
        backref_ctx->found_itself = 0;
        backref_ctx->extent_len = num_bytes;
+       /*
+        * For non-compressed extents iterate_extent_inodes() gives us extent
+        * offsets that already take into account the data offset, but not for
+        * compressed extents, since the offset is logical and not relative to
+        * the physical extent locations. We must take this into account to
+        * avoid sending clone offsets that go beyond the source file's size,
+        * which would result in the clone ioctl failing with -EINVAL on the
+        * receiving end.
+        */
+       if (compressed == BTRFS_COMPRESS_NONE)
+               backref_ctx->data_offset = 0;
+       else
+               backref_ctx->data_offset = btrfs_file_extent_offset(eb, fi);
 
        /*
         * The last extent of a file may be too large due to page alignment.