new file mode 100755
@@ -0,0 +1,168 @@
+#! /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
+ rm -f $img
+ $LVM_PROG vgremove -f -y $vgname >>$seqres.full 2>&1
+ losetup -d $loop_dev >>$seqres.full 2>&1
+}
+
+# Import common functions.
+. ./common/filter
+. ./common/attr
+. ./common/dmlogwrites
+. ./common/verity
+
+# real QA test starts here
+_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"
+
+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
new file mode 100644
@@ -0,0 +1,2 @@
+QA output created by 291
+Silence is golden