#! /bin/bash # SPDX-License-Identifier: GPL-2.0 # Copyright (C) 2021 Facebook, Inc. All Rights Reserved. # # FS QA Test 290 # # Test btrfs support for fsverity. # This test extends the generic fsverity testing by corrupting inline extents, # preallocated extents, holes, and the Merkle descriptor in a btrfs-aware way. # . ./common/preamble _begin_fstest auto quick verity prealloc . ./common/filter . ./common/verity # Override the default cleanup function. _cleanup() { cd / _restore_fsverity_signatures rm -f $tmp.* } _supported_fs btrfs _require_scratch_verity _require_scratch_nocheck _require_odirect _require_xfs_io_command "falloc" _require_xfs_io_command "pread" _require_xfs_io_command "pwrite" _require_btrfs_corrupt_block "value" _require_btrfs_corrupt_block "offset" _disable_fsverity_signatures get_ino() { local file=$1 stat -c "%i" $file } validate() { local f=$1 local sz=$(_get_filesize $f) # buffered io echo $(basename $f) $XFS_IO_PROG -rc "pread -q 0 $sz" $f 2>&1 | _filter_scratch # direct io $XFS_IO_PROG -rdc "pread -q 0 $sz" $f 2>&1 | _filter_scratch } # corrupt the data portion of an inline extent corrupt_inline() { local f=$SCRATCH_MNT/inl $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 42" $f local ino=$(get_ino $f) _fsv_enable $f _scratch_unmount # inline data starts at disk_bytenr # overwrite the first u64 with random bogus junk $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 0 -f disk_bytenr $SCRATCH_DEV _scratch_mount validate $f } # preallocate a file, then corrupt it by changing it to a regular file corrupt_prealloc_to_reg() { local f=$SCRATCH_MNT/prealloc $XFS_IO_PROG -fc "falloc 0 12k" $f local ino=$(get_ino $f) _fsv_enable $f _scratch_unmount # ensure non-zero at the pre-allocated region on disk # set extent type from prealloc (2) to reg (1) $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 0 -f type --value 1 $SCRATCH_DEV _scratch_mount # now that it's a regular file, reading actually looks at the previously # preallocated region, so ensure that has non-zero contents. head -c 5 /dev/zero | tr '\0' X | _fsv_scratch_corrupt_bytes $f 0 validate $f } # corrupt a regular file by changing the type to preallocated corrupt_reg_to_prealloc() { local f=$SCRATCH_MNT/reg $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f local ino=$(get_ino $f) _fsv_enable $f _scratch_unmount # set type from reg (1) to prealloc (2) $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 0 -f type --value 2 $SCRATCH_DEV _scratch_mount validate $f } # corrupt a file by punching a hole corrupt_punch_hole() { local f=$SCRATCH_MNT/punch $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f local ino=$(get_ino $f) # make a new extent in the middle, sync so the writes don't coalesce $XFS_IO_PROG -c sync $SCRATCH_MNT $XFS_IO_PROG -fc "pwrite -q -S 0x59 4096 4096" $f _fsv_enable $f _scratch_unmount # change disk_bytenr to 0, representing a hole $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 4096 -f disk_bytenr --value 0 \ $SCRATCH_DEV _scratch_mount validate $f } # plug hole corrupt_plug_hole() { local f=$SCRATCH_MNT/plug $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f local ino=$(get_ino $f) $XFS_IO_PROG -fc "falloc 4k 4k" $f _fsv_enable $f _scratch_unmount # change disk_bytenr to some value, plugging the hole $BTRFS_CORRUPT_BLOCK_PROG -i $ino -x 4096 -f disk_bytenr \ --value 13639680 $SCRATCH_DEV _scratch_mount validate $f } # corrupt the fsverity descriptor item indiscriminately (causes EINVAL) corrupt_verity_descriptor() { local f=$SCRATCH_MNT/desc $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f local ino=$(get_ino $f) _fsv_enable $f _scratch_unmount # key for the descriptor item is , # 88 is X. So we write 5 Xs to the start of the descriptor $BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,36,1 --value 88 --offset 0 -b 5 \ $SCRATCH_DEV _scratch_mount validate $f } # specifically target the root hash in the descriptor (causes EIO) corrupt_root_hash() { local f=$SCRATCH_MNT/roothash $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f local ino=$(get_ino $f) _fsv_enable $f _scratch_unmount $BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,36,1 --value 88 --offset 16 -b 1 \ $SCRATCH_DEV _scratch_mount validate $f } # corrupt the Merkle tree data itself corrupt_merkle_tree() { local f=$SCRATCH_MNT/merkle $XFS_IO_PROG -fc "pwrite -q -S 0x58 0 12288" $f local ino=$(get_ino $f) _fsv_enable $f _scratch_unmount # key for the descriptor item is , # 88 is X. So we write 5 Xs to somewhere in the middle of the first # merkle item $BTRFS_CORRUPT_BLOCK_PROG -r 5 -I $ino,37,0 --value 88 --offset 100 \ -b 5 $SCRATCH_DEV _scratch_mount validate $f } _scratch_mkfs >/dev/null _scratch_mount corrupt_inline corrupt_prealloc_to_reg corrupt_reg_to_prealloc corrupt_punch_hole corrupt_plug_hole corrupt_verity_descriptor corrupt_root_hash corrupt_merkle_tree status=0 exit