linux/fs/btrfs
Filipe Manana 3078ec66dc Btrfs: fix corruption reading shared and compressed extents after hole punching
commit 8e92821878 upstream.

In the past we had data corruption when reading compressed extents that
are shared within the same file and they are consecutive, this got fixed
by commit 005efedf2c ("Btrfs: fix read corruption of compressed and
shared extents") and by commit 808f80b467 ("Btrfs: update fix for read
corruption of compressed and shared extents"). However there was a case
that was missing in those fixes, which is when the shared and compressed
extents are referenced with a non-zero offset. The following shell script
creates a reproducer for this issue:

  #!/bin/bash

  mkfs.btrfs -f /dev/sdc &> /dev/null
  mount -o compress /dev/sdc /mnt/sdc

  # Create a file with 3 consecutive compressed extents, each has an
  # uncompressed size of 128Kb and a compressed size of 4Kb.
  for ((i = 1; i <= 3; i++)); do
      head -c 4096 /dev/zero
      for ((j = 1; j <= 31; j++)); do
          head -c 4096 /dev/zero | tr '\0' "\377"
      done
  done > /mnt/sdc/foobar
  sync

  echo "Digest after file creation:   $(md5sum /mnt/sdc/foobar)"

  # Clone the first extent into offsets 128K and 256K.
  xfs_io -c "reflink /mnt/sdc/foobar 0 128K 128K" /mnt/sdc/foobar
  xfs_io -c "reflink /mnt/sdc/foobar 0 256K 128K" /mnt/sdc/foobar
  sync

  echo "Digest after cloning:         $(md5sum /mnt/sdc/foobar)"

  # Punch holes into the regions that are already full of zeroes.
  xfs_io -c "fpunch 0 4K" /mnt/sdc/foobar
  xfs_io -c "fpunch 128K 4K" /mnt/sdc/foobar
  xfs_io -c "fpunch 256K 4K" /mnt/sdc/foobar
  sync

  echo "Digest after hole punching:   $(md5sum /mnt/sdc/foobar)"

  echo "Dropping page cache..."
  sysctl -q vm.drop_caches=1
  echo "Digest after hole punching:   $(md5sum /mnt/sdc/foobar)"

  umount /dev/sdc

When running the script we get the following output:

  Digest after file creation:   5a0888d80d7ab1fd31c229f83a3bbcc8  /mnt/sdc/foobar
  linked 131072/131072 bytes at offset 131072
  128 KiB, 1 ops; 0.0033 sec (36.960 MiB/sec and 295.6830 ops/sec)
  linked 131072/131072 bytes at offset 262144
  128 KiB, 1 ops; 0.0015 sec (78.567 MiB/sec and 628.5355 ops/sec)
  Digest after cloning:         5a0888d80d7ab1fd31c229f83a3bbcc8  /mnt/sdc/foobar
  Digest after hole punching:   5a0888d80d7ab1fd31c229f83a3bbcc8  /mnt/sdc/foobar
  Dropping page cache...
  Digest after hole punching:   fba694ae8664ed0c2e9ff8937e7f1484  /mnt/sdc/foobar

This happens because after reading all the pages of the extent in the
range from 128K to 256K for example, we read the hole at offset 256K
and then when reading the page at offset 260K we don't submit the
existing bio, which is responsible for filling all the page in the
range 128K to 256K only, therefore adding the pages from range 260K
to 384K to the existing bio and submitting it after iterating over the
entire range. Once the bio completes, the uncompressed data fills only
the pages in the range 128K to 256K because there's no more data read
from disk, leaving the pages in the range 260K to 384K unfilled. It is
just a slightly different variant of what was solved by commit
005efedf2c ("Btrfs: fix read corruption of compressed and shared
extents").

Fix this by forcing a bio submit, during readpages(), whenever we find a
compressed extent map for a page that is different from the extent map
for the previous page or has a different starting offset (in case it's
the same compressed extent), instead of the extent map's original start
offset.

A test case for fstests follows soon.

Reported-by: Zygo Blaxell <ce3g8jdj@umail.furryterror.org>
Fixes: 808f80b467 ("Btrfs: update fix for read corruption of compressed and shared extents")
Fixes: 005efedf2c ("Btrfs: fix read corruption of compressed and shared extents")
Tested-by: Zygo Blaxell <ce3g8jdj@umail.furryterror.org>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
History 2019-07-09 22:04:09 +01:00
..
tests btrfs: tests/qgroup: Fix wrong tree backref level 2018-10-21 08:45:07 +01:00
acl.c btrfs: preserve i_mode if __btrfs_set_acl() fails 2017-10-12 15:28:09 +01:00
async-thread.c btrfs: limit async_work allocation and worker func duration 2017-03-16 02:26:36 +00:00
async-thread.h btrfs: limit async_work allocation and worker func duration 2017-03-16 02:26:36 +00:00
backref.c btrfs: remove spurious WARN_ON(ref->count < 0) in find_parent_nodes 2018-06-16 22:22:05 +01:00
backref.h Btrfs: fix scrub_print_warning to handle skinny metadata extents 2014-06-09 17:21:17 -07:00
btrfs_inode.h Btrfs: fix fsync of files with multiple hard links in new directories 2019-04-04 16:13:51 +01:00
check-integrity.c btrfs: check_int: propagate out-of-memory error upwards 2014-06-09 17:20:21 -07:00
check-integrity.h block: submit_bio_wait() conversions 2013-11-24 16:33:41 -07:00
compression.c btrfs: zero out left over bytes after processing compression streams 2014-12-10 17:15:43 +00:00
compression.h btrfs: zero out left over bytes after processing compression streams 2014-12-10 17:15:43 +00:00
ctree.c Btrfs: send, fix invalid access to commit roots due to concurrent snapshotting 2018-10-21 08:46:05 +01:00
ctree.h btrfs: bugfix: handle FS_IOC32_{GETFLAGS,SETFLAGS,GETVERSION} in btrfs_ioctl 2016-08-22 22:37:52 +01:00
delayed-inode.c Btrfs: fill ->last_trans for delayed inode in btrfs_fill_inode. 2019-04-04 16:13:50 +01:00
delayed-inode.h btrfs: properly set the termination value of ctx->pos in readdir 2016-02-25 10:34:58 +00:00
delayed-ref.c Btrfs: rework qgroup accounting 2014-06-09 17:20:48 -07:00
delayed-ref.h Btrfs: rework qgroup accounting 2014-06-09 17:20:48 -07:00
dev-replace.c btrfs: dev-replace: go back to suspended state if target device is missing 2019-04-04 16:13:50 +01:00
dev-replace.h Btrfs: add new sources for device replace code 2012-12-12 17:15:41 -05:00
dir-item.c Btrfs: make xattr replace operations atomic 2015-03-30 11:11:47 +01:00
disk-io.c btrfs: Always try all copies when reading extent buffers 2019-02-11 17:54:03 +00:00
disk-io.h Btrfs: add sanity tests for new qgroup accounting code 2014-06-09 17:20:49 -07:00
export.c btrfs: remove fs/btrfs/compat.h 2013-11-11 22:03:19 -05:00
export.h NFS support for btrfs - v3 2008-09-25 11:04:06 -04:00
extent-tree.c btrfs: wait on caching when putting the bg cache 2019-02-11 17:53:32 +00:00
extent_io.c Btrfs: fix corruption reading shared and compressed extents after hole punching 2019-07-09 22:04:09 +01:00
extent_io.h Btrfs: remove unused wait queue in struct extent_buffer 2014-06-19 14:20:28 -07:00
extent_map.c Btrfs: do not move em to modified list when unpinning 2015-01-05 13:48:52 +00:00
extent_map.h Btrfs: fix NULL pointer crash when running balance and scrub concurrently 2014-06-19 14:20:55 -07:00
file-item.c Btrfs: fix kfree on list_head in btrfs_lookup_csums_range error cleanup 2014-11-14 15:19:58 +00:00
file.c Btrfs: don't clean dirty pages during buffered writes 2019-02-11 17:53:33 +00:00
free-space-cache.c Btrfs: fix use-after-free when dumping free space 2019-02-11 17:53:39 +00:00
free-space-cache.h Btrfs: remove path arg from btrfs_truncate_free_space_cache 2013-11-11 21:51:33 -05:00
hash.c Btrfs: fix btrfs boot when compiled as built-in 2014-01-28 13:20:31 -08:00
hash.h Btrfs: fix btrfs boot when compiled as built-in 2014-01-28 13:20:31 -08:00
inode-item.c btrfs: cleanup: removed unused 'btrfs_get_inode_ref_index' 2014-01-28 13:19:39 -08:00
inode-map.c Btrfs: fix race between caching kthread and returning inode to inode cache 2015-07-20 14:38:51 +01:00
inode-map.h Btrfs: Support reading/writing on disk free ino cache 2011-04-25 16:46:11 +08:00
inode.c Btrfs: fix fsync of files with multiple hard links in new directories 2019-04-04 16:13:51 +01:00
ioctl.c Btrfs: fix data corruption due to cloning of eof block 2019-02-11 17:53:49 +00:00
Kconfig Btrfs: fix btrfs boot when compiled as built-in 2014-01-28 13:20:31 -08:00
locking.c btrfs: fix lockups from btrfs_clear_path_blocking 2014-12-01 10:26:06 +00:00
locking.h btrfs: fix lockups from btrfs_clear_path_blocking 2014-12-01 10:26:06 +00:00
lzo.c btrfs: zero out left over bytes after processing compression streams 2014-12-10 17:15:43 +00:00
Makefile Btrfs: add sanity tests for new qgroup accounting code 2014-06-09 17:20:49 -07:00
math.h Btrfs: cleanup duplicated division functions 2012-12-11 13:31:30 -05:00
ordered-data.c Btrfs: make sure logged extents complete in the current transaction V3 2015-01-05 13:48:52 +00:00
ordered-data.h Btrfs: make sure logged extents complete in the current transaction V3 2015-01-05 13:48:52 +00:00
orphan.c btrfs: expand btrfs_find_item() to include find_orphan_item functionality 2014-01-28 13:19:37 -08:00
print-tree.c Btrfs: fix btrfs_print_leaf for skinny metadata 2014-07-03 07:04:16 -07:00
print-tree.h btrfs: make static code static & remove dead code 2013-05-06 15:55:23 -04:00
props.c Btrfs: add support for inode properties 2014-01-28 13:20:24 -08:00
props.h Btrfs: add support for inode properties 2014-01-28 13:20:24 -08:00
qgroup.c Btrfs: fix race between enabling quotas and subvolume creation 2019-02-11 17:54:09 +00:00
qgroup.h Btrfs: rework qgroup accounting 2014-06-09 17:20:48 -07:00
raid56.c Btrfs: fix task hang under heavy compressed write 2014-09-05 16:36:37 -07:00
raid56.h Btrfs: RAID5 and RAID6 2013-02-01 14:24:23 -05:00
rcu-string.h Btrfs: use rcu to protect device->name 2012-06-14 21:29:16 -04:00
reada.c Btrfs: fix task hang under heavy compressed write 2014-09-05 16:36:37 -07:00
relocation.c btrfs: relocation: set trans to be NULL after ending transaction 2019-02-11 17:54:10 +00:00
root-tree.c Btrfs: use bitfield instead of integer data type for the some variants in btrfs_root 2014-06-09 17:20:40 -07:00
scrub.c btrfs: init csum_list before possible free 2019-07-09 22:04:06 +01:00
send.c Btrfs: send, don't leave without decrementing clone root's send_progress 2015-06-17 15:26:38 +01:00
send.h btrfs: make static code static & remove dead code 2013-05-06 15:55:23 -04:00
struct-funcs.c Btrfs: rewrite BTRFS_SETGET_FUNCS 2012-07-23 16:28:06 -04:00
super.c Btrfs: ensure path name is null terminated at btrfs_control_ioctl 2019-02-11 17:54:04 +00:00
sysfs.c btrfs: dev add should add its sysfs entry 2014-06-28 13:48:43 -07:00
sysfs.h btrfs: dev add should add its sysfs entry 2014-06-28 13:48:43 -07:00
transaction.c Btrfs: check if previous transaction aborted to avoid fs corruption 2015-09-28 10:21:46 +01:00
transaction.h Btrfs: make sure logged extents complete in the current transaction V3 2015-01-05 13:48:52 +00:00
tree-defrag.c Btrfs: use bitfield instead of integer data type for the some variants in btrfs_root 2014-06-09 17:20:40 -07:00
tree-log.c Btrfs: fix fsync of files with multiple hard links in new directories 2019-04-04 16:13:51 +01:00
tree-log.h Btrfs: use helpers for last_trans_log_full_commit instead of opencode 2014-06-09 17:20:45 -07:00
ulist.c Btrfs: do not export ulist functions 2014-01-29 07:06:27 -08:00
ulist.h Btrfs: Fix memory corruption by ulist_add_merge() on 32bit arch 2014-09-05 16:36:36 -07:00
uuid-tree.c Btrfs: convert printk to btrfs_ and fix BTRFS prefix 2014-01-28 13:20:05 -08:00
volumes.c btrfs: rename total_bytes to avoid confusion 2018-12-16 22:08:37 +00:00
volumes.h btrfs: check unsupported filters in balance arguments 2015-11-16 11:26:49 +00:00
xattr.c Btrfs: fix race when listing an inode's xattrs 2015-12-13 17:49:18 +00:00
xattr.h btrfs: use generic posix ACL infrastructure 2014-01-25 23:58:18 -05:00
zlib.c btrfs: zero out left over bytes after processing compression streams 2014-12-10 17:15:43 +00:00