From: Alexander Larsson <alexl@xxxxxxxxxx> This tests that the right xattrs are set during copy-up, and that we properly fail on missing of erronous fs-verity digests when validating. We also ensure that verity=require fails if a metacopy has not fs-verity, and doesn't do a meta-coopy-up if the base file lacks verity. Signed-off-by: Alexander Larsson <alexl@xxxxxxxxxx> Reviewed-by: Amir Goldstein <amir73il@xxxxxxxxx> --- common/overlay | 14 ++ common/verity | 16 ++- tests/overlay/080 | 326 ++++++++++++++++++++++++++++++++++++++++++ tests/overlay/080.out | 7 + 4 files changed, 358 insertions(+), 5 deletions(-) create mode 100755 tests/overlay/080 create mode 100644 tests/overlay/080.out diff --git a/common/overlay b/common/overlay index 816ed66d..7004187f 100644 --- a/common/overlay +++ b/common/overlay @@ -201,6 +201,20 @@ _require_scratch_overlay_features() _scratch_unmount } +_require_scratch_overlay_verity() +{ + local lowerdirs="$OVL_BASE_SCRATCH_MNT/$OVL_UPPER:$OVL_BASE_SCRATCH_MNT/$OVL_LOWER" + + _require_scratch_verity "$OVL_BASE_FSTYP" "$OVL_BASE_SCRATCH_MNT" + + _scratch_mkfs > /dev/null 2>&1 + _overlay_scratch_mount_dirs "$lowerdirs" "-" "-" \ + -o ro,redirect_dir=follow,metacopy=on,verity=on > /dev/null 2>&1 || \ + _notrun "overlay verity not supported on ${SCRATCH_DEV}" + + _scratch_unmount +} + # Check kernel support for <lowerdirs>::<lowerdatadir> format _require_scratch_overlay_lowerdata_layers() { diff --git a/common/verity b/common/verity index 77c257d3..e0937717 100644 --- a/common/verity +++ b/common/verity @@ -38,10 +38,13 @@ _require_scratch_verity() "or mkfs options are not compatible with verity" fi + local fstyp=${1:-$FSTYP} + local scratch_mnt=${2:-$SCRATCH_MNT} + # The filesystem may be aware of fs-verity but have it disabled by # CONFIG_FS_VERITY=n. Detect support via sysfs. - if [ ! -e /sys/fs/$FSTYP/features/verity ]; then - _notrun "kernel $FSTYP isn't configured with verity support" + if [ ! -e /sys/fs/$fstyp/features/verity ]; then + _notrun "kernel $fstyp isn't configured with verity support" fi # Select a default Merkle tree block size for when tests don't @@ -57,7 +60,7 @@ _require_scratch_verity() # # Therefore, we default to merkle_tree_block_size == min(fs_block_size, # page_size). That maximizes the chance of verity actually working. - local fs_block_size=$(_get_block_size $SCRATCH_MNT) + local fs_block_size=$(_get_block_size $scratch_mnt) local page_size=$(get_page_size) if (( fs_block_size <= page_size )); then FSV_BLOCK_SIZE=$fs_block_size @@ -68,8 +71,8 @@ _require_scratch_verity() # The filesystem may have fs-verity enabled but not actually usable by # default. E.g., ext4 only supports verity on extent-based files, so it # doesn't work on ext3-style filesystems. So, try actually using it. - if ! _fsv_can_enable $SCRATCH_MNT/tmpfile; then - _notrun "$FSTYP verity isn't usable by default with these mkfs options" + if ! _fsv_can_enable $scratch_mnt/tmpfile; then + _notrun "$fstyp verity isn't usable by default with these mkfs options" fi _scratch_unmount @@ -201,6 +204,9 @@ _scratch_mkfs_verity() btrfs) _scratch_mkfs ;; + overlay) + _scratch_mkfs # This relies on the scratch fs supporting verity + ;; *) _notrun "No verity support for $FSTYP" ;; diff --git a/tests/overlay/080 b/tests/overlay/080 new file mode 100755 index 00000000..0b5dca09 --- /dev/null +++ b/tests/overlay/080 @@ -0,0 +1,326 @@ +#! /bin/bash +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2023 Red Hat, Inc. All Rights Reserved. +# Copyright (C) 2023 CTERA Networks. All Rights Reserved. +# +# FS QA Test No. 080 +# +# Test fs-verity functionallity +# +. ./common/preamble +_begin_fstest auto quick metacopy redirect verity + +# Import common functions. +. ./common/filter +. ./common/attr +. ./common/verity + +# real QA test starts here +_supported_fs overlay +# We use non-default scratch underlying overlay dirs, we need to check +# them explicity after test. +_require_scratch_nocheck +_require_scratch_overlay_features redirect_dir metacopy +_require_scratch_overlay_lowerdata_layers +_require_scratch_overlay_verity + +# remove all files from previous tests +_scratch_mkfs + +verityname="verityfile" +noverityname="noverityfile" +wrongverityname="wrongverityfile" +missingverityname="missingverityfile" +lowerdata="data1" +lowerdata2="data2" +lowerdata3="data3" +lowerdata4="data4" +lowersize="5" + +# Create test directories +lowerdir=$OVL_BASE_SCRATCH_MNT/lower +lowerdir2=$OVL_BASE_SCRATCH_MNT/lower2 +upperdir=$OVL_BASE_SCRATCH_MNT/upper +workdir=$OVL_BASE_SCRATCH_MNT/workdir +workdir2=$OVL_BASE_SCRATCH_MNT/workdir2 + +# Check metacopy xattr +check_metacopy() +{ + local target=$1 exist=$2 dataonlybase=$3 + local out_f target_f + local msg + + out_f=$( { _getfattr --absolute-names --only-values -n \ + $OVL_XATTR_METACOPY $target 2>&3 | od -A n -t x1 -w256 ; } 3>&1 | _filter_scratch) + has_version0=`echo $out_f | awk 'NR==1{print $1 == 0}'` + + if [ "$exist" == "y" ];then + [ "$out_f" == "" -o "$has_version0" == "1" ] && return + echo "Metacopy xattr does not exist on ${target}. stdout=$out_f" + return + fi + + if [ "$out_f" == "" -o "$has_version0" == "1" ];then + echo "Metacopy xattr exists on ${target} unexpectedly." + return + fi + + target_f=`echo $target | _filter_scratch` + msg="$target_f: trusted.overlay.metacopy: No such attribute" + + [ "$out_f" == "$msg" ] && return + + echo "Error while checking xattr on ${target}. stdout=$out" +} + +# Check verity set in metacopy +check_verity() +{ + local target=$1 exist=$2 + local out_f target_f + local msg + + out_f=$( { _getfattr --absolute-names --only-values -n $OVL_XATTR_METACOPY $target 2>&3 | od -A n -t x1 -w256 ; } 3>&1 | _filter_scratch) + + target_f=`echo $target | _filter_scratch` + msg="$target_f: trusted.overlay.metacopy: No such attribute" + has_digest=`echo $out_f | awk 'NR==1{print $4 == 1}'` + + if [ "$exist" == "y" ]; then + [ "$out_f" == "$msg" -o "$has_digest" == "0" ] && echo "No verity on ${target}. stdout=$out_f" + return + fi + + [ "$out_f" == "$msg" -o "$has_digest" == "0" ] && return + echo "Verity xattr exists on ${target} unexpectedly. stdout=$out_f" +} + +# Check redirect xattr +check_redirect() +{ + local target=$1 + local expect=$2 + + value=$(_getfattr --absolute-names --only-values -n \ + $OVL_XATTR_REDIRECT $target) + + [[ "$value" == "$expect" ]] || echo "Redirect xattr incorrect. Expected=\"$expect\", actual=\"$value\"" +} + +# Check size +check_file_size() +{ + local target=$1 expected_size=$2 actual_size + + actual_size=$(_get_filesize $target) + + [ "$actual_size" == "$expected_size" ] || echo "Expected file size of $target $expected_size but actual size is $actual_size" +} + +check_file_contents() +{ + local target=$1 expected=$2 + local actual target_f + + target_f=`echo $target | _filter_scratch` + + read actual<$target + + [ "$actual" == "$expected" ] || echo "Expected file $target_f contents to be \"$expected\" but actual contents are \"$actual\"" +} + +check_file_size_contents() +{ + local target=$1 expected_size=$2 expected_content=$3 + + check_file_size $target $expected_size + check_file_contents $target $expected_content +} + +check_io_error() +{ + local target=$1 + local actual target_f out_f + + target_f=`echo $target | _filter_scratch` + out_f=`cat $target 2>&1 | _filter_scratch` + msg="cat: $target_f: Input/output error" + + [ "$out_f" == "$msg" ] && return + + echo "$target_f unexpectedly has no I/O error" +} + +create_basic_files() +{ + local subdir=$1 + + _scratch_mkfs + mkdir -p $lowerdir $lowerdir2 $upperdir $workdir $workdir2 + + if [ "$subdir" != "" ]; then + mkdir $lowerdir/$subdir + fi + + echo -n "$lowerdata" > $lowerdir/$subdir$verityname + echo -n "$lowerdata2" > $lowerdir/$subdir$noverityname + echo -n "$lowerdata3" > $lowerdir/$subdir$wrongverityname + echo -n "$lowerdata4" > $lowerdir/$subdir$missingverityname + + for f in $verityname $noverityname $wrongverityname $missingverityname; do + chmod 600 $lowerdir/$subdir$f + + if [ "$f" != "$noverityname" ]; then + _fsv_enable $lowerdir/$subdir$f + fi + done +} + +prepare_midlayer() +{ + local dataonlybase=$1 + + subdir="" + if [ "$dataonlybase" == "y" ]; then + subdir="base/" + fi + + create_basic_files "$subdir" + # Create midlayer + _overlay_scratch_mount_dirs $lowerdir $lowerdir2 $workdir2 -o redirect_dir=on,index=on,verity=on,metacopy=on + for f in $verityname $noverityname $wrongverityname $missingverityname; do + if [ "$dataonlybase" == "y" ]; then + mv $SCRATCH_MNT/base/$f $SCRATCH_MNT/$f + else + chmod 400 $SCRATCH_MNT/$f + fi + done + umount_overlay + + if [ "$dataonlybase" == "y" ]; then + rm -rf $lowerdir2/base + fi + + for f in $verityname $noverityname $wrongverityname $missingverityname; do + # Ensure we have right metacopy and verity xattrs + check_metacopy $lowerdir2/$f "y" + + if [ "$f" == "$noverityname" ]; then + check_verity $lowerdir2/$f "n" + else + check_verity $lowerdir2/$f "y" + fi + + if [ "$dataonlybase" == "y" ]; then + check_redirect $lowerdir2/$f "/base/$f" + fi + + check_file_size_contents $lowerdir2/$f $lowersize "" + done + + # Fixup missing and wrong verity in lowerdir + rm -f $lowerdir/$subdir$wrongverityname $lowerdir/$subdir$missingverityname + echo -n "changed" > $lowerdir/$subdir$wrongverityname + _fsv_enable $lowerdir/$subdir$wrongverityname + echo "$lowerdata4" > $lowerdir/$subdir$missingverityname +} + +test_common() +{ + local dataonlybase=$1 + local verity=$2 + + if [ $dataonlybase == "y" ]; then + mount_overlay "$lowerdir2::$lowerdir" $verity + else + mount_overlay "$lowerdir2:$lowerdir" $verity + fi + + check_file_size_contents $SCRATCH_MNT/$verityname $lowersize "$lowerdata" + + if [ "$verity" == "require" ]; then + check_io_error $SCRATCH_MNT/$noverityname + else + check_file_size_contents $SCRATCH_MNT/$noverityname $lowersize "$lowerdata2" + fi + + if [ "$verity" == "off" ]; then + check_file_size_contents $SCRATCH_MNT/$wrongverityname $lowersize "changed" + check_file_size_contents $SCRATCH_MNT/$missingverityname $lowersize "$lowerdata4" + else + check_io_error $SCRATCH_MNT/$missingverityname + check_io_error $SCRATCH_MNT/$wrongverityname + fi + + umount_overlay +} + +mount_overlay() +{ + local _lowerdir=$1 + local _verity=$2 + + _overlay_scratch_mount_dirs "$_lowerdir" $upperdir $workdir -o redirect_dir=on,index=on,metacopy=on,verity=$_verity +} + +umount_overlay() +{ + $UMOUNT_PROG $SCRATCH_MNT +} + + +echo -e "\n== Check fsverity validation ==" + +prepare_midlayer "n" +test_common "n" "off" +prepare_midlayer "n" +test_common "n" "on" + +# Now with data-only layers +prepare_midlayer "y" +test_common "y" "off" +prepare_midlayer "y" +test_common "y" "on" + +echo -e "\n== Check fsverity require ==" + +prepare_midlayer "n" +test_common "n" "require" + +# Now with data-only layers +prepare_midlayer "y" +test_common "y" "require" + +echo -e "\n== Check fsverity copy-up ==" + +# Ensure Second level metacopy sets verity xattr +prepare_midlayer "n" +mount_overlay "$lowerdir2:$lowerdir" "on" +chmod 200 $SCRATCH_MNT/$verityname +umount_overlay +check_metacopy $upperdir/$verityname "y" +check_verity $upperdir/$verityname "y" + +# Ensure data copy up remove verity xattr +create_basic_files "" +mount_overlay "$lowerdir" "on" +echo foo >> $SCRATCH_MNT/$verityname +umount_overlay +check_metacopy $upperdir/$verityname "n" +check_verity $upperdir/$verityname "n" + +# Ensure metacopy is only used if verity is enabled in lower for verity=require +create_basic_files "" +mount_overlay "$lowerdir" "require" +chmod 200 $SCRATCH_MNT/$verityname +chmod 200 $SCRATCH_MNT/$noverityname +umount_overlay +check_metacopy $upperdir/$verityname "y" +check_verity $upperdir/$verityname "y" +check_metacopy $upperdir/$noverityname "n" +check_verity $upperdir/$noverityname "n" + +# success, all done +status=0 +exit diff --git a/tests/overlay/080.out b/tests/overlay/080.out new file mode 100644 index 00000000..7d1f1d10 --- /dev/null +++ b/tests/overlay/080.out @@ -0,0 +1,7 @@ +QA output created by 080 + +== Check fsverity validation == + +== Check fsverity require == + +== Check fsverity copy-up == -- 2.40.1