|  | #! /bin/bash | 
|  | # SPDX-License-Identifier: GPL-2.0 | 
|  | # Copyright (C) 2022 SUSE Linux Products GmbH. All Rights Reserved. | 
|  | # | 
|  | # FS QA Test 260 | 
|  | # | 
|  | # Make sure "btrfs filesystem defragment" can still convert the compression | 
|  | # algorithm of all regular extents. | 
|  | # | 
|  | . ./common/preamble | 
|  | _begin_fstest auto quick defrag compress prealloc fiemap | 
|  |  | 
|  | # Override the default cleanup function. | 
|  | # _cleanup() | 
|  | # { | 
|  | # 	cd / | 
|  | # 	rm -r -f $tmp.* | 
|  | # } | 
|  |  | 
|  | . ./common/filter | 
|  |  | 
|  | _require_scratch | 
|  | _require_xfs_io_command "fiemap" "ranged" | 
|  | _require_xfs_io_command "falloc" | 
|  | _require_btrfs_command inspect-internal dump-tree | 
|  |  | 
|  | get_inode_number() | 
|  | { | 
|  | local file="$1" | 
|  |  | 
|  | stat -c "%i" "$file" | 
|  | } | 
|  |  | 
|  | get_file_extent() | 
|  | { | 
|  | local file="$1" | 
|  | local offset="$2" | 
|  | local ino=$(get_inode_number "$file") | 
|  | local file_extent_key="($ino EXTENT_DATA $offset)" | 
|  |  | 
|  | $BTRFS_UTIL_PROG inspect-internal dump-tree -t 5 $SCRATCH_DEV |\ | 
|  | grep -A4 "$file_extent_key" | 
|  | } | 
|  |  | 
|  | check_file_extent() | 
|  | { | 
|  | local file="$1" | 
|  | local offset="$2" | 
|  | local expected="$3" | 
|  |  | 
|  | echo "=== file extent at file '$file' offset $offset ===" >> $seqres.full | 
|  | get_file_extent "$file" "$offset" > $tmp.output | 
|  | cat $tmp.output >> $seqres.full | 
|  | grep -q "$expected" $tmp.output ||\ | 
|  | echo "file \"$file\" offset $offset doesn't have expected string \"$expected\"" | 
|  | } | 
|  |  | 
|  | # Unlike file extents whose btrfs specific attributes need to be grabbed from | 
|  | # dump-tree, we can check holes by fiemap. And mkfs enables no-holes feature by | 
|  | # default in recent versions of btrfs-progs, preventing us from grabbing holes | 
|  | # from dump-tree. | 
|  | check_hole() | 
|  | { | 
|  | local file="$1" | 
|  | local offset="$2" | 
|  | local len="$3" | 
|  |  | 
|  | output=$($XFS_IO_PROG -c "fiemap $offset $len" "$file" |\ | 
|  | _filter_xfs_io_fiemap | head -n1) | 
|  | if [ -z $output ]; then | 
|  | echo "=== file extent at file '$file' offset $offset is a hole ===" \ | 
|  | >> $seqres.full | 
|  | else | 
|  | echo "=== file extent at file '$file' offset $offset is not a hole ===" | 
|  | fi | 
|  | } | 
|  |  | 
|  | # Needs 4K sectorsize as the test is crafted using that sectorsize | 
|  | _require_btrfs_support_sectorsize 4096 | 
|  |  | 
|  | _scratch_mkfs -s 4k >> $seqres.full 2>&1 | 
|  |  | 
|  | # Initial data is compressed using lzo | 
|  | _scratch_mount -o compress=lzo | 
|  |  | 
|  | # file 'large' has all of its compressed extents at their maximum size | 
|  | $XFS_IO_PROG -f -c "pwrite 0 1m" "$SCRATCH_MNT/large" >> $seqres.full | 
|  |  | 
|  | # file 'fragment' has all of its compressed extents adjacent to | 
|  | # preallocated/hole ranges, which should not be defragged with regular | 
|  | # defrag ioctl, but should still be defragged by | 
|  | # "btrfs filesystem defragment -c" | 
|  | $XFS_IO_PROG -f -c "pwrite 0 16k" \ | 
|  | -c "pwrite 32k 16k" -c "pwrite 64k 16k" \ | 
|  | "$SCRATCH_MNT/fragment" >> $seqres.full | 
|  | sync | 
|  | # We only do the falloc after the compressed data reached disk. | 
|  | # Or the inode could have PREALLOC flag, and prevent the | 
|  | # data from being compressed. | 
|  | $XFS_IO_PROG -c "falloc 16k 16k" "$SCRATCH_MNT/fragment" | 
|  | sync | 
|  |  | 
|  | echo "====== Before the defrag ======" >> $seqres.full | 
|  |  | 
|  | # Should be lzo compressed | 
|  | check_file_extent "$SCRATCH_MNT/large" 0 "compression 2" | 
|  |  | 
|  | # Should be lzo compressed | 
|  | check_file_extent "$SCRATCH_MNT/fragment" 0 "compression 2" | 
|  |  | 
|  | # Should be preallocated | 
|  | check_file_extent "$SCRATCH_MNT/fragment" 16384 "type 2" | 
|  |  | 
|  | # Should be lzo compressed | 
|  | check_file_extent "$SCRATCH_MNT/fragment" 32768 "compression 2" | 
|  |  | 
|  | # Should be hole | 
|  | check_hole "$SCRATCH_MNT/fragment" 49152 16384 | 
|  |  | 
|  | # Should be lzo compressed | 
|  | check_file_extent "$SCRATCH_MNT/fragment" 65536 "compression 2" | 
|  |  | 
|  | $BTRFS_UTIL_PROG filesystem defragment "$SCRATCH_MNT/large" -czstd \ | 
|  | "$SCRATCH_MNT/fragment" >> $seqres.full | 
|  | # Need to commit the transaction or dump-tree won't grab the new | 
|  | # metadata on-disk. | 
|  | sync | 
|  |  | 
|  | echo "====== After the defrag ======" >> $seqres.full | 
|  |  | 
|  | # Should be zstd compressed | 
|  | check_file_extent "$SCRATCH_MNT/large" 0 "compression 3" | 
|  |  | 
|  | # Should be zstd compressed | 
|  | check_file_extent "$SCRATCH_MNT/fragment" 0 "compression 3" | 
|  |  | 
|  | # Should be preallocated | 
|  | check_file_extent "$SCRATCH_MNT/fragment" 16384 "type 2" | 
|  |  | 
|  | # Should be zstd compressed | 
|  | check_file_extent "$SCRATCH_MNT/fragment" 32768 "compression 3" | 
|  |  | 
|  | # Should be hole | 
|  | check_hole "$SCRATCH_MNT/fragment" 49152 16384 | 
|  |  | 
|  | # Should be zstd compressed | 
|  | check_file_extent "$SCRATCH_MNT/fragment" 65536 "compression 3" | 
|  |  | 
|  | echo "Silence is golden" | 
|  |  | 
|  | # success, all done | 
|  | status=0 | 
|  | exit |