| From 6b1f72e5b82a5c2a4da4d1ebb8cc01913ddbea21 Mon Sep 17 00:00:00 2001 |
| From: Filipe Manana <fdmanana@suse.com> |
| Date: Mon, 20 May 2019 09:55:42 +0100 |
| Subject: Btrfs: incremental send, fix file corruption when no-holes feature is enabled |
| |
| From: Filipe Manana <fdmanana@suse.com> |
| |
| commit 6b1f72e5b82a5c2a4da4d1ebb8cc01913ddbea21 upstream. |
| |
| When using the no-holes feature, if we have a file with prealloc extents |
| with a start offset beyond the file's eof, doing an incremental send can |
| cause corruption of the file due to incorrect hole detection. Such case |
| requires that the prealloc extent(s) exist in both the parent and send |
| snapshots, and that a hole is punched into the file that covers all its |
| extents that do not cross the eof boundary. |
| |
| Example reproducer: |
| |
| $ mkfs.btrfs -f -O no-holes /dev/sdb |
| $ mount /dev/sdb /mnt/sdb |
| |
| $ xfs_io -f -c "pwrite -S 0xab 0 500K" /mnt/sdb/foobar |
| $ xfs_io -c "falloc -k 1200K 800K" /mnt/sdb/foobar |
| |
| $ btrfs subvolume snapshot -r /mnt/sdb /mnt/sdb/base |
| |
| $ btrfs send -f /tmp/base.snap /mnt/sdb/base |
| |
| $ xfs_io -c "fpunch 0 500K" /mnt/sdb/foobar |
| |
| $ btrfs subvolume snapshot -r /mnt/sdb /mnt/sdb/incr |
| |
| $ btrfs send -p /mnt/sdb/base -f /tmp/incr.snap /mnt/sdb/incr |
| |
| $ md5sum /mnt/sdb/incr/foobar |
| 816df6f64deba63b029ca19d880ee10a /mnt/sdb/incr/foobar |
| |
| $ mkfs.btrfs -f /dev/sdc |
| $ mount /dev/sdc /mnt/sdc |
| |
| $ btrfs receive -f /tmp/base.snap /mnt/sdc |
| $ btrfs receive -f /tmp/incr.snap /mnt/sdc |
| |
| $ md5sum /mnt/sdc/incr/foobar |
| cf2ef71f4a9e90c2f6013ba3b2257ed2 /mnt/sdc/incr/foobar |
| |
| --> Different checksum, because the prealloc extent beyond the |
| file's eof confused the hole detection code and it assumed |
| a hole starting at offset 0 and ending at the offset of the |
| prealloc extent (1200Kb) instead of ending at the offset |
| 500Kb (the file's size). |
| |
| Fix this by ensuring we never cross the file's size when issuing the |
| write operations for a hole. |
| |
| Fixes: 16e7549f045d33 ("Btrfs: incompatible format change to remove hole extents") |
| CC: stable@vger.kernel.org # 3.14+ |
| Signed-off-by: Filipe Manana <fdmanana@suse.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/btrfs/send.c | 6 ++++++ |
| 1 file changed, 6 insertions(+) |
| |
| --- a/fs/btrfs/send.c |
| +++ b/fs/btrfs/send.c |
| @@ -5021,6 +5021,12 @@ static int send_hole(struct send_ctx *sc |
| if (offset >= sctx->cur_inode_size) |
| return 0; |
| |
| + /* |
| + * Don't go beyond the inode's i_size due to prealloc extents that start |
| + * after the i_size. |
| + */ |
| + end = min_t(u64, end, sctx->cur_inode_size); |
| + |
| if (sctx->flags & BTRFS_SEND_FLAG_NO_FILE_DATA) |
| return send_update_extent(sctx, offset, end - offset); |
| |