]>
Commit | Line | Data |
---|---|---|
14e9555d GKH |
1 | From 6b1f72e5b82a5c2a4da4d1ebb8cc01913ddbea21 Mon Sep 17 00:00:00 2001 |
2 | From: Filipe Manana <fdmanana@suse.com> | |
3 | Date: Mon, 20 May 2019 09:55:42 +0100 | |
4 | Subject: Btrfs: incremental send, fix file corruption when no-holes feature is enabled | |
5 | ||
6 | From: Filipe Manana <fdmanana@suse.com> | |
7 | ||
8 | commit 6b1f72e5b82a5c2a4da4d1ebb8cc01913ddbea21 upstream. | |
9 | ||
10 | When using the no-holes feature, if we have a file with prealloc extents | |
11 | with a start offset beyond the file's eof, doing an incremental send can | |
12 | cause corruption of the file due to incorrect hole detection. Such case | |
13 | requires that the prealloc extent(s) exist in both the parent and send | |
14 | snapshots, and that a hole is punched into the file that covers all its | |
15 | extents that do not cross the eof boundary. | |
16 | ||
17 | Example reproducer: | |
18 | ||
19 | $ mkfs.btrfs -f -O no-holes /dev/sdb | |
20 | $ mount /dev/sdb /mnt/sdb | |
21 | ||
22 | $ xfs_io -f -c "pwrite -S 0xab 0 500K" /mnt/sdb/foobar | |
23 | $ xfs_io -c "falloc -k 1200K 800K" /mnt/sdb/foobar | |
24 | ||
25 | $ btrfs subvolume snapshot -r /mnt/sdb /mnt/sdb/base | |
26 | ||
27 | $ btrfs send -f /tmp/base.snap /mnt/sdb/base | |
28 | ||
29 | $ xfs_io -c "fpunch 0 500K" /mnt/sdb/foobar | |
30 | ||
31 | $ btrfs subvolume snapshot -r /mnt/sdb /mnt/sdb/incr | |
32 | ||
33 | $ btrfs send -p /mnt/sdb/base -f /tmp/incr.snap /mnt/sdb/incr | |
34 | ||
35 | $ md5sum /mnt/sdb/incr/foobar | |
36 | 816df6f64deba63b029ca19d880ee10a /mnt/sdb/incr/foobar | |
37 | ||
38 | $ mkfs.btrfs -f /dev/sdc | |
39 | $ mount /dev/sdc /mnt/sdc | |
40 | ||
41 | $ btrfs receive -f /tmp/base.snap /mnt/sdc | |
42 | $ btrfs receive -f /tmp/incr.snap /mnt/sdc | |
43 | ||
44 | $ md5sum /mnt/sdc/incr/foobar | |
45 | cf2ef71f4a9e90c2f6013ba3b2257ed2 /mnt/sdc/incr/foobar | |
46 | ||
47 | --> Different checksum, because the prealloc extent beyond the | |
48 | file's eof confused the hole detection code and it assumed | |
49 | a hole starting at offset 0 and ending at the offset of the | |
50 | prealloc extent (1200Kb) instead of ending at the offset | |
51 | 500Kb (the file's size). | |
52 | ||
53 | Fix this by ensuring we never cross the file's size when issuing the | |
54 | write operations for a hole. | |
55 | ||
56 | Fixes: 16e7549f045d33 ("Btrfs: incompatible format change to remove hole extents") | |
57 | CC: stable@vger.kernel.org # 3.14+ | |
58 | Signed-off-by: Filipe Manana <fdmanana@suse.com> | |
59 | Signed-off-by: David Sterba <dsterba@suse.com> | |
60 | Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> | |
61 | ||
62 | --- | |
63 | fs/btrfs/send.c | 6 ++++++ | |
64 | 1 file changed, 6 insertions(+) | |
65 | ||
66 | --- a/fs/btrfs/send.c | |
67 | +++ b/fs/btrfs/send.c | |
68 | @@ -5021,6 +5021,12 @@ static int send_hole(struct send_ctx *sc | |
69 | if (offset >= sctx->cur_inode_size) | |
70 | return 0; | |
71 | ||
72 | + /* | |
73 | + * Don't go beyond the inode's i_size due to prealloc extents that start | |
74 | + * after the i_size. | |
75 | + */ | |
76 | + end = min_t(u64, end, sctx->cur_inode_size); | |
77 | + | |
78 | if (sctx->flags & BTRFS_SEND_FLAG_NO_FILE_DATA) | |
79 | return send_update_extent(sctx, offset, end - offset); | |
80 |