#! /bin/bash # SPDX-License-Identifier: GPL-2.0 # Copyright (C) 2021 Facebook, Inc. All Rights Reserved. # # FS QA Test 291 # # Test btrfs consistency after each FUA while enabling verity on a file # This test works by following the pattern in log-writes/replay-individual.sh: # 1. run a workload (verity + sync) while logging to the log device # 2. replay an entry to the replay device # 3. snapshot the replay device to the snapshot device # 4. run destructive tests on the snapshot device (e.g. mount with orphans) # 5. goto 2 # . ./common/preamble _begin_fstest auto verity recoveryloop # Override the default cleanup function. _cleanup() { cd / _log_writes_cleanup &> /dev/null $LVM_PROG vgremove -f -y $vgname >>$seqres.full 2>&1 losetup -d $loop_dev >>$seqres.full 2>&1 rm -f $img _restore_fsverity_signatures } . ./common/filter . ./common/attr . ./common/dmlogwrites . ./common/verity _supported_fs btrfs _require_scratch _require_test _require_loop _require_log_writes _require_dm_target snapshot _require_command $LVM_PROG lvm _require_scratch_verity _require_btrfs_command inspect-internal dump-tree _require_test_program "log-writes/replay-log" _disable_fsverity_signatures sync_loop() { i=$1 [ -z "$i" ] && _fail "sync loop needs a number of iterations" while [ $i -gt 0 ] do $XFS_IO_PROG -c sync $SCRATCH_MNT let i-=1 done } dump_tree() { local dev=$1 $BTRFS_UTIL_PROG inspect-internal dump-tree $dev } count_item() { local dev=$1 local item=$2 dump_tree $dev | grep -c "$item" } count_merkle_items() { local dev=$1 count_item $dev 'VERITY_\(DESC\|MERKLE\)_ITEM' } _log_writes_init $SCRATCH_DEV _log_writes_mkfs _log_writes_mount f=$SCRATCH_MNT/fsv MB=$((1024 * 1024)) img=$TEST_DIR/$$.img $XFS_IO_PROG -fc "pwrite -q 0 $((10 * $MB))" $f $XFS_IO_PROG -c sync $SCRATCH_MNT sync_loop 10 & sync_proc=$! _fsv_enable $f $XFS_IO_PROG -c sync $SCRATCH_MNT wait $sync_proc _log_writes_unmount _log_writes_remove # the snapshot and the replay will each be the size of the log writes dev # so we create a loop device of size 2 * logwrites and then split it into # replay and snapshot with lvm. log_writes_blocks=$(blockdev --getsz $LOGWRITES_DEV) replay_bytes=$((512 * $log_writes_blocks)) img_bytes=$((2 * $replay_bytes)) $XFS_IO_PROG -fc "pwrite -q -S 0 $img_bytes $MB" $img >>$seqres.full 2>&1 || \ _fail "failed to create image for loop device" loop_dev=$(losetup -f --show $img) vgname=vg_replay lvname=lv_replay replay_dev=/dev/mapper/vg_replay-lv_replay snapname=lv_snap snap_dev=/dev/mapper/vg_replay-$snapname $LVM_PROG vgcreate -f $vgname $loop_dev >>$seqres.full 2>&1 || _fail "failed to vgcreate $vgname" $LVM_PROG lvcreate -L "$replay_bytes"B -n $lvname $vgname -y >>$seqres.full 2>&1 || \ _fail "failed to lvcreate $lvname" $UDEV_SETTLE_PROG >>$seqres.full 2>&1 replay_log_prog=$here/src/log-writes/replay-log num_entries=$($replay_log_prog --log $LOGWRITES_DEV --num-entries) entry=$($replay_log_prog --log $LOGWRITES_DEV --replay $replay_dev --find --end-mark mkfs | cut -d@ -f1) prev=$(_log_writes_mark_to_entry_number mkfs) [ -z "$prev" ] && _fail "failed to locate entry mark 'mkfs'" cur=$(_log_writes_find_next_fua $prev) # state = 0: verity hasn't started # state = 1: verity underway # state = 2: verity done state=0 while [ ! -z "$cur" ]; do _log_writes_replay_log_range $cur $replay_dev >> $seqres.full $LVM_PROG lvcreate -s -L 4M -n $snapname $vgname/$lvname >>$seqres.full 2>&1 || \ _fail "Failed to create snapshot" $UDEV_SETTLE_PROG >>$seqres.full 2>&1 orphan=$(count_item $snap_dev ORPHAN) [ $state -eq 0 ] && [ $orphan -gt 0 ] && state=1 pre_mount=$(count_merkle_items $snap_dev) _mount $snap_dev $SCRATCH_MNT || _fail "mount failed at entry $cur" fsverity measure $SCRATCH_MNT/fsv >>$seqres.full 2>&1 measured=$? umount $SCRATCH_MNT [ $state -eq 1 ] && [ $measured -eq 0 ] && state=2 [ $state -eq 2 ] && ([ $measured -eq 0 ] || _fail "verity done, but measurement failed at entry $cur") post_mount=$(count_merkle_items $snap_dev) echo "entry: $cur, state: $state, orphan: $orphan, pre_mount: $pre_mount, post_mount: $post_mount" >> $seqres.full if [ $state -eq 1 ]; then [ $post_mount -eq 0 ] || \ _fail "mount failed to clear under-construction merkle items pre: $pre_mount, post: $post_mount at entry $cur"; fi if [ $state -eq 2 ]; then [ $pre_mount -gt 0 ] || \ _fail "expected to have verity items before mount at entry $cur" [ $pre_mount -eq $post_mount ] || \ _fail "mount cleared merkle items after verity was enabled $pre_mount vs $post_mount at entry $cur"; fi $LVM_PROG lvremove $vgname/$snapname -y >>$seqres.full prev=$cur cur=$(_log_writes_find_next_fua $(($cur + 1))) done [ $state -eq 2 ] || _fail "expected to reach verity done state" echo "Silence is golden" # success, all done status=0 exit