# SPDX-License-Identifier: GPL-2.0 # Copyright 2018 Google LLC # # Functions for setting up and testing fs-verity _require_scratch_verity() { _require_scratch _require_command "$FSVERITY_PROG" fsverity if ! _scratch_mkfs_verity &>>$seqres.full; then # ext4: need e2fsprogs v1.44.5 or later (but actually v1.45.2+ # is needed for some tests to pass, due to an e2fsck bug) # f2fs: need f2fs-tools v1.11.0 or later _notrun "$FSTYP userspace tools don't support fs-verity" fi # Try to mount the filesystem. If this fails then either the kernel # isn't aware of fs-verity, or the mkfs options were not compatible with # verity (e.g. ext4 with block size != PAGE_SIZE). if ! _try_scratch_mount &>>$seqres.full; then _notrun "kernel is unaware of $FSTYP verity feature," \ "or mkfs options are not compatible with verity" fi # 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" fi # 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. echo foo > $SCRATCH_MNT/tmpfile _disable_fsverity_signatures if ! _fsv_enable $SCRATCH_MNT/tmpfile; then _restore_fsverity_signatures _notrun "$FSTYP verity isn't usable by default with these mkfs options" fi _restore_fsverity_signatures rm -f $SCRATCH_MNT/tmpfile _scratch_unmount # Merkle tree block size. Currently all filesystems only support # PAGE_SIZE for this. This is also the default for 'fsverity enable'. FSV_BLOCK_SIZE=$(get_page_size) } # Check for CONFIG_FS_VERITY_BUILTIN_SIGNATURES=y. _require_fsverity_builtin_signatures() { if [ ! -e /proc/sys/fs/verity/require_signatures ]; then _notrun "kernel doesn't support fs-verity builtin signatures" fi } # Disable mandatory signatures for fs-verity files, if they are supported. _disable_fsverity_signatures() { if [ -e /proc/sys/fs/verity/require_signatures ]; then if [ -z "$FSVERITY_SIG_CTL_ORIG" ]; then FSVERITY_SIG_CTL_ORIG=$( /proc/sys/fs/verity/require_signatures fi } # Enable mandatory signatures for fs-verity files. # This assumes that _require_fsverity_builtin_signatures() was called. _enable_fsverity_signatures() { if [ -z "$FSVERITY_SIG_CTL_ORIG" ]; then FSVERITY_SIG_CTL_ORIG=$( /proc/sys/fs/verity/require_signatures } # Restore the original signature verification setting. _restore_fsverity_signatures() { if [ -n "$FSVERITY_SIG_CTL_ORIG" ]; then echo "$FSVERITY_SIG_CTL_ORIG" > /proc/sys/fs/verity/require_signatures fi } _scratch_mkfs_verity() { case $FSTYP in ext4|f2fs) _scratch_mkfs -O verity ;; *) _notrun "No verity support for $FSTYP" ;; esac } _scratch_mkfs_encrypted_verity() { case $FSTYP in ext4) _scratch_mkfs -O encrypt,verity ;; f2fs) # f2fs-tools as of v1.11.0 doesn't allow comma-separated # features with -O. Instead -O must be supplied multiple times. _scratch_mkfs -O encrypt -O verity ;; *) _notrun "$FSTYP not supported in _scratch_mkfs_encrypted_verity" ;; esac } _fsv_scratch_begin_subtest() { local msg=$1 rm -rf "${SCRATCH_MNT:?}"/* echo -e "\n# $msg" } _fsv_enable() { $FSVERITY_PROG enable "$@" } _fsv_measure() { $FSVERITY_PROG measure "$@" | awk '{print $1}' } _fsv_sign() { $FSVERITY_PROG sign "$@" } # Generate a file, then enable verity on it. _fsv_create_enable_file() { local file=$1 shift head -c $((FSV_BLOCK_SIZE * 2)) /dev/zero > "$file" _fsv_enable "$file" "$@" } _fsv_have_hash_algorithm() { local hash_alg=$1 local test_file=$2 rm -f $test_file head -c 4096 /dev/zero > $test_file if ! _fsv_enable --hash-alg=$hash_alg $test_file &>> $seqres.full; then # no kernel support return 1 fi rm -f $test_file return 0 } # # _fsv_scratch_corrupt_bytes - Write some bytes to a file, bypassing the filesystem # # Write the bytes sent on stdin to the given offset in the given file, but do so # by writing directly to the extents on the block device, with the filesystem # unmounted. This can be used to corrupt a verity file for testing purposes, # bypassing the restrictions imposed by the filesystem. # # The file is assumed to be located on $SCRATCH_DEV. # _fsv_scratch_corrupt_bytes() { local file=$1 local offset=$2 local lstart lend pstart pend local dd_cmds=() local cmd sync # Sync to avoid unwritten extents cat > $tmp.bytes local end=$(( offset + $(_get_filesize $tmp.bytes ) )) # For each extent that intersects the requested range in order, add a # command that writes the next part of the data to that extent. while read -r lstart lend pstart pend; do lstart=$((lstart * 512)) lend=$(((lend + 1) * 512)) pstart=$((pstart * 512)) pend=$(((pend + 1) * 512)) if (( lend - lstart != pend - pstart )); then _fail "Logical and physical extent lengths differ for file '$file'" elif (( offset < lstart )); then _fail "Hole in file '$file' at byte $offset. Next extent begins at byte $lstart" elif (( offset < lend )); then local len=$((lend - offset)) local seek=$((pstart + (offset - lstart))) dd_cmds+=("head -c $len | dd of=$SCRATCH_DEV oflag=seek_bytes seek=$seek status=none") (( offset += len )) fi done < <($XFS_IO_PROG -r -c "fiemap $offset $((end - offset))" "$file" \ | _filter_xfs_io_fiemap) if (( offset < end )); then _fail "Extents of file '$file' ended at byte $offset, but needed until $end" fi # Execute the commands to write the data _scratch_unmount for cmd in "${dd_cmds[@]}"; do eval "$cmd" done < $tmp.bytes sync # Sync to flush the block device's pagecache _scratch_mount } # # _fsv_scratch_corrupt_merkle_tree - Corrupt a file's Merkle tree # # Like _fsv_scratch_corrupt_bytes(), but this corrupts the file's fs-verity # Merkle tree. The offset is given as a byte offset into the Merkle tree. # _fsv_scratch_corrupt_merkle_tree() { local file=$1 local offset=$2 case $FSTYP in ext4|f2fs) # ext4 and f2fs store the Merkle tree after the file contents # itself, starting at the next 65536-byte aligned boundary. (( offset += ($(_get_filesize $file) + 65535) & ~65535 )) _fsv_scratch_corrupt_bytes $file $offset ;; *) _fail "_fsv_scratch_corrupt_merkle_tree() unimplemented on $FSTYP" ;; esac }