0003-Btrfs-send-fix-emission-of-invalid-clone-operations-.patch 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. From 693469a2b9d6d27282c06ed55cb70ff648740efd Mon Sep 17 00:00:00 2001
  2. From: Filipe Manana <fdmanana@suse.com>
  3. Date: Fri, 24 Jan 2020 11:52:04 +0000
  4. Subject: [PATCH 3/3] Btrfs: send, fix emission of invalid clone operations
  5. within the same file
  6. When doing an incremental send and a file has extents shared with itself
  7. at different file offsets, it's possible for send to emit clone operations
  8. that will fail at the destination because the source range goes beyond the
  9. file's current size. This happens when the file size has increased in the
  10. send snapshot, there is a hole between the shared extents and both shared
  11. extents are at file offsets which are greater the file's size in the
  12. parent snapshot.
  13. Example:
  14. $ mkfs.btrfs -f /dev/sdb
  15. $ mount /dev/sdb /mnt/sdb
  16. $ xfs_io -f -c "pwrite -S 0xf1 0 64K" /mnt/sdb/foobar
  17. $ btrfs subvolume snapshot -r /mnt/sdb /mnt/sdb/base
  18. $ btrfs send -f /tmp/1.snap /mnt/sdb/base
  19. # Create a 320K extent at file offset 512K.
  20. $ xfs_io -c "pwrite -S 0xab 512K 64K" /mnt/sdb/foobar
  21. $ xfs_io -c "pwrite -S 0xcd 576K 64K" /mnt/sdb/foobar
  22. $ xfs_io -c "pwrite -S 0xef 640K 64K" /mnt/sdb/foobar
  23. $ xfs_io -c "pwrite -S 0x64 704K 64K" /mnt/sdb/foobar
  24. $ xfs_io -c "pwrite -S 0x73 768K 64K" /mnt/sdb/foobar
  25. # Clone part of that 320K extent into a lower file offset (192K).
  26. # This file offset is greater than the file's size in the parent
  27. # snapshot (64K). Also the clone range is a bit behind the offset of
  28. # the 320K extent so that we leave a hole between the shared extents.
  29. $ xfs_io -c "reflink /mnt/sdb/foobar 448K 192K 192K" /mnt/sdb/foobar
  30. $ btrfs subvolume snapshot -r /mnt/sdb /mnt/sdb/incr
  31. $ btrfs send -p /mnt/sdb/base -f /tmp/2.snap /mnt/sdb/incr
  32. $ mkfs.btrfs -f /dev/sdc
  33. $ mount /dev/sdc /mnt/sdc
  34. $ btrfs receive -f /tmp/1.snap /mnt/sdc
  35. $ btrfs receive -f /tmp/2.snap /mnt/sdc
  36. ERROR: failed to clone extents to foobar: Invalid argument
  37. The problem is that after processing the extent at file offset 192K, send
  38. does not issue a write operation full of zeroes for the hole between that
  39. extent and the extent starting at file offset 520K (hole range from 384K
  40. to 512K), this is because the hole is at an offset larger the size of the
  41. file in the parent snapshot (384K > 64K). As a consequence the field
  42. 'cur_inode_next_write_offset' of the send context remains with a value of
  43. 384K when we start to process the extent at file offset 512K, which is the
  44. value set after processing the extent at offset 192K.
  45. This screws up the lookup of possible extents to clone because due to an
  46. incorrect value of 'cur_inode_next_write_offset' we can now consider
  47. extents for cloning, in the same inode, that lie beyond the current size
  48. of the file in the receiver of the send stream. Also, when checking if
  49. an extent in the same file can be used for cloning, we must also check
  50. that not only its start offset doesn't start at or beyond the current eof
  51. of the file in the receiver but that the source range doesn't go beyond
  52. current eof, that is we must check offset + length does not cross the
  53. current eof, as that makes clone operations fail with -EINVAL.
  54. So fix this by updating 'cur_inode_next_write_offset' whenever we start
  55. processing an extent and checking an extent's offset and length when
  56. considering it for cloning operations.
  57. A test case for fstests follows soon.
  58. Fixes: 11f2069c113e02 ("Btrfs: send, allow clone operations within the same file")
  59. Signed-off-by: Filipe Manana <fdmanana@suse.com>
  60. Reviewed-by: Josef Bacik <josef@toxicpanda.com>
  61. ---
  62. fs/btrfs/send.c | 15 ++++++++++++++-
  63. 1 file changed, 14 insertions(+), 1 deletion(-)
  64. diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
  65. index 091e5bc8c7ea..0b42dac8a35f 100644
  66. --- a/fs/btrfs/send.c
  67. +++ b/fs/btrfs/send.c
  68. @@ -1269,7 +1269,8 @@ static int __iterate_backrefs(u64 ino, u64 offset, u64 root, void *ctx_)
  69. * destination of the stream.
  70. */
  71. if (ino == bctx->cur_objectid &&
  72. - offset >= bctx->sctx->cur_inode_next_write_offset)
  73. + offset + bctx->extent_len >
  74. + bctx->sctx->cur_inode_next_write_offset)
  75. return 0;
  76. }
  77. @@ -5804,6 +5805,18 @@ static int process_extent(struct send_ctx *sctx,
  78. }
  79. }
  80. + /*
  81. + * There might be a hole between the end of the last processed extent
  82. + * and this extent, and we may have not sent a write operation for that
  83. + * hole because it was not needed (range is beyond eof in the parent
  84. + * snapshot). So adjust the next write offset to the offset of this
  85. + * extent, as we want to make sure we don't do mistakes when checking if
  86. + * we can clone this extent from some other offset in this inode or when
  87. + * detecting if we need to issue a truncate operation when finishing the
  88. + * processing this inode.
  89. + */
  90. + sctx->cur_inode_next_write_offset = key->offset;
  91. +
  92. ret = find_extent_clone(sctx, path, key->objectid, key->offset,
  93. sctx->cur_inode_size, &found_clone);
  94. if (ret != -ENOENT && ret < 0)
  95. --
  96. 2.25.0