|  | #! /bin/bash | 
|  | # SPDX-License-Identifier: GPL-2.0 | 
|  | # Copyright (C) 2015 SUSE Linux Products GmbH. All Rights Reserved. | 
|  | # | 
|  | # FSQA Test No. 103 | 
|  | # | 
|  | # Regression test for file read corruption when using compressed extents that | 
|  | # are shared by multiple consecutive ranges of the same file. | 
|  | # | 
|  | . ./common/preamble | 
|  | _begin_fstest auto quick clone compress | 
|  |  | 
|  | # Import common functions. | 
|  | . ./common/filter | 
|  |  | 
|  | # real QA test starts here | 
|  | _supported_fs btrfs | 
|  | _require_scratch | 
|  | _require_cloner | 
|  |  | 
|  | test_clone_and_read_compressed_extent() | 
|  | { | 
|  | local mount_opts=$1 | 
|  |  | 
|  | _scratch_mkfs >>$seqres.full 2>&1 | 
|  | _scratch_mount $mount_opts | 
|  |  | 
|  | BLOCK_SIZE=$(_get_block_size $SCRATCH_MNT) | 
|  |  | 
|  | # Create a test file with a single extent that is compressed (the | 
|  | # data we write into it is highly compressible no matter which | 
|  | # compression algorithm is used, zlib or lzo). | 
|  | $XFS_IO_PROG -f -c "pwrite -S 0xaa 0K $((1 * $BLOCK_SIZE))" \ | 
|  | -c "pwrite -S 0xbb $((1 * $BLOCK_SIZE)) $((2 * $BLOCK_SIZE))" \ | 
|  | -c "pwrite -S 0xcc $((3 * $BLOCK_SIZE)) $((1 * $BLOCK_SIZE))" \ | 
|  | $SCRATCH_MNT/foo | _filter_xfs_io_blocks_modified | 
|  |  | 
|  | # Now clone our extent into an adjacent offset. | 
|  | $CLONER_PROG -s $((1 * $BLOCK_SIZE)) -d $((4 * $BLOCK_SIZE)) \ | 
|  | -l $((2 * $BLOCK_SIZE)) $SCRATCH_MNT/foo $SCRATCH_MNT/foo | 
|  |  | 
|  | # Same as before but for this file we clone the extent into a lower | 
|  | # file offset. | 
|  | $XFS_IO_PROG -f \ | 
|  | -c "pwrite -S 0xaa $((2 * $BLOCK_SIZE)) $((1 * $BLOCK_SIZE))" \ | 
|  | -c "pwrite -S 0xbb $((3 * $BLOCK_SIZE)) $((2 * $BLOCK_SIZE))" \ | 
|  | -c "pwrite -S 0xcc $((5 * $BLOCK_SIZE)) $((1 * $BLOCK_SIZE))" \ | 
|  | $SCRATCH_MNT/bar | _filter_xfs_io_blocks_modified | 
|  |  | 
|  | $CLONER_PROG -s $((3 * $BLOCK_SIZE)) -d 0 -l $((2 * $BLOCK_SIZE)) \ | 
|  | $SCRATCH_MNT/bar $SCRATCH_MNT/bar | 
|  |  | 
|  | echo "File contents before unmounting filesystem:" | 
|  | echo "foo:" | 
|  | od -t x1 $SCRATCH_MNT/foo | _filter_od | 
|  | echo "bar:" | 
|  | od -t x1 $SCRATCH_MNT/bar | _filter_od | 
|  |  | 
|  | # Evicting the inode or clearing the page cache before reading again | 
|  | # the file would also trigger the bug - reads were returning all bytes | 
|  | # in the range corresponding to the second reference to the extent with | 
|  | # a value of 0, but the correct data was persisted (it was a bug | 
|  | # exclusively in the read path). The issue happened only if the same | 
|  | # readpages() call targeted pages belonging to the first and second | 
|  | # ranges that point to the same compressed extent. | 
|  | _scratch_cycle_mount | 
|  |  | 
|  | echo "File contents after mounting filesystem again:" | 
|  | # Must match the same contents we got before. | 
|  | echo "foo:" | 
|  | od -t x1 $SCRATCH_MNT/foo | _filter_od | 
|  | echo "bar:" | 
|  | od -t x1 $SCRATCH_MNT/bar | _filter_od | 
|  | } | 
|  |  | 
|  | echo -e "\nTesting with zlib compression..." | 
|  | test_clone_and_read_compressed_extent "-o compress=zlib" | 
|  |  | 
|  | _scratch_unmount | 
|  |  | 
|  | echo -e "\nTesting with lzo compression..." | 
|  | test_clone_and_read_compressed_extent "-o compress=lzo" | 
|  |  | 
|  | status=0 | 
|  | exit |