btrfs/035: check for data loss
authorFilipe Manana <fdmanana@suse.com>
Wed, 14 Oct 2015 03:19:34 +0000 (14:19 +1100)
committerDave Chinner <david@fromorbit.com>
Wed, 14 Oct 2015 03:19:34 +0000 (14:19 +1100)
The test currently verifies that cloning one file with an inline extent
with a size of 10 bytes into a file with an inline extent that has a size
of 20 bytes succeeds. But this results in data loss, because the btrfs
clone operation drops the 20 bytes inline extent from the destination
inode and then copies the 10 bytes inline extent from the source file
into the destination file, resulting in data loss of the last 10 bytes
of data that the destination file had.

Fixing btrfs to correctly operate for this case (not resulting in data
loss) is actually a lot of work and brings a lot of complexity, specially
considering that any of the inline extents can be compressed. For the
moment there's a fix to make the clone operation return the errno
EOPNOTSUPP and not touch any of the inodes. This is the same approach
we do for other cases involving operation against inline extents, so
this just adds one more case that should have never been allowed.
Cloning inline extents is a rare operation and pointless, since it
involves copying them and not doing any actual deduplication or saving
space.

The btrfs patch for the linux kernel that prevents this data loss,
and fixes some file corruption cases, is titled:

  "Btrfs: fix file corruption and data loss after cloning inline extents"

Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
tests/btrfs/035
tests/btrfs/035.out

index 35ddfce7f3382decdf10685edbba74ea5eb3c62e..0f8a70de232e2f9dd8cf8195a8e2bd4b1973009d 100755 (executable)
@@ -67,9 +67,23 @@ echo "attempting ioctl (src.clone1 src)"
 $CLONER_PROG -s 0 -d 0 -l ${snap_src_sz} \
        $SCRATCH_MNT/src.clone1 $SCRATCH_MNT/src
 
+# The clone operation should have failed. If it did not it meant we had data
+# loss, because file "src.clone1" has an inline extent which is 10 bytes long
+# while file "src" has an inline extent which is 20 bytes long. The clone
+# operation would remove the inline extent of "src" and then copy the inline
+# extent from "src.clone1" into "src", which means we would lose the last 10
+# bytes of data from "src" (on read we would get 0x00 as the value for any
+# of those 10 bytes, because the file's size remains as 20 bytes).
+echo "File src data after attempt to clone from src.clone1 into src:"
+od -t x1 $SCRATCH_MNT/src
+
 snap_src_sz=`ls -lah $SCRATCH_MNT/src.clone2 | awk '{print $5}'`
 echo "attempting ioctl (src.clone2 src)"
 $CLONER_PROG -s 0 -d 0 -l ${snap_src_sz} \
        $SCRATCH_MNT/src.clone2 $SCRATCH_MNT/src
 
+# The clone operation should have succeeded.
+echo "File src data after attempt to clone from src.clone2 into src:"
+od -t x1 $SCRATCH_MNT/src
+
 status=0 ; exit
index f86cadf82f523991fd84693212396223b366d143..3ea7d779078f23117c038f21307f22e1ce817974 100644 (file)
@@ -1,3 +1,12 @@
 QA output created by 035
 attempting ioctl (src.clone1 src)
+clone failed: Operation not supported
+File src data after attempt to clone from src.clone1 into src:
+0000000 62 62 62 62 62 62 62 62 62 62 63 63 63 63 63 63
+0000020 63 63 63 63
+0000024
 attempting ioctl (src.clone2 src)
+File src data after attempt to clone from src.clone2 into src:
+0000000 62 62 62 62 62 62 62 62 62 62 63 63 63 63 63 63
+0000020 63 63 63 63
+0000024