xfs: test that the needsrepair feature works as advertised
authorDarrick J. Wong <djwong@kernel.org>
Thu, 22 Apr 2021 00:49:13 +0000 (17:49 -0700)
committerEryu Guan <guaneryu@gmail.com>
Sun, 25 Apr 2021 05:41:18 +0000 (13:41 +0800)
Make sure that the needsrepair feature flag can be cleared only by
repair and that mounts are prohibited when the feature is set.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Eryu Guan <guaneryu@gmail.com>
common/xfs
tests/xfs/154 [new file with mode: 0755]
tests/xfs/154.out [new file with mode: 0644]
tests/xfs/155 [new file with mode: 0755]
tests/xfs/155.out [new file with mode: 0644]
tests/xfs/group

index 887bd00198f5022530c01116c6acba6dc6195fb6..cf4afb5341339ed295d1f27086c835dbef3c1a68 100644 (file)
@@ -312,6 +312,14 @@ _scratch_xfs_check()
        _xfs_check $SCRATCH_OPTIONS $* $SCRATCH_DEV
 }
 
+# Check for secret debugging hooks in xfs_repair
+_require_libxfs_debug_flag() {
+       local hook="$1"
+
+       grep -q "$hook" "$(type -P xfs_repair)" || \
+               _notrun "libxfs debug hook $hook not detected?"
+}
+
 _scratch_xfs_repair()
 {
        SCRATCH_OPTIONS=""
@@ -1114,3 +1122,24 @@ _xfs_get_cowgc_interval() {
                _fail "Can't find cowgc interval procfs knob?"
        fi
 }
+
+# Print the status of the given features on the scratch filesystem.
+# Returns 0 if all features are found, 1 otherwise.
+_check_scratch_xfs_features()
+{
+       local features="$(_scratch_xfs_db -c 'version')"
+       local output=("FEATURES:")
+       local found=0
+
+       for feature in "$@"; do
+               local status="NO"
+               if echo "${features}" | grep -q -w "${feature}"; then
+                       status="YES"
+                       found=$((found + 1))
+               fi
+               output+=("${feature}:${status}")
+       done
+
+       echo "${output[@]}"
+       test "${found}" -eq "$#"
+}
diff --git a/tests/xfs/154 b/tests/xfs/154
new file mode 100755 (executable)
index 0000000..03000b7
--- /dev/null
@@ -0,0 +1,80 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2021 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 154
+#
+# Make sure that the kernel won't mount a filesystem if repair forcibly sets
+# NEEDSREPAIR while fixing metadata.  Corrupt a directory in such a way as
+# to force repair to write an invalid dirent value as a sentinel to trigger a
+# repair activity in a later phase.  Use a debug knob in xfs_repair to abort
+# the repair immediately after forcing the flag on.
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1    # failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+       cd /
+       rm -f $tmp.*
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/filter
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_nocheck
+_require_scratch_xfs_crc               # needsrepair only exists for v5
+_require_libxfs_debug_flag LIBXFS_DEBUG_WRITE_CRASH
+
+rm -f $seqres.full
+
+# Set up a real filesystem for our actual test
+_scratch_mkfs >> $seqres.full
+
+# Create a directory large enough to have a dir data block.  2k worth of
+# dirent names ought to do it.
+_scratch_mount
+mkdir -p $SCRATCH_MNT/fubar
+for i in $(seq 0 256 2048); do
+       fname=$(printf "%0255d" $i)
+       ln -s -f urk $SCRATCH_MNT/fubar/$fname
+done
+inum=$(stat -c '%i' $SCRATCH_MNT/fubar)
+_scratch_unmount
+
+# Fuzz the directory
+_scratch_xfs_db -x -c "inode $inum" -c "dblock 0" \
+       -c "fuzz -d bu[2].inumber add" >> $seqres.full
+
+# Try to repair the directory, force it to crash after setting needsrepair
+LIBXFS_DEBUG_WRITE_CRASH=ddev=2 _scratch_xfs_repair 2>> $seqres.full
+test $? -eq 137 || echo "repair should have been killed??"
+
+# We can't mount, right?
+_check_scratch_xfs_features NEEDSREPAIR
+_try_scratch_mount &> $tmp.mount
+res=$?
+_filter_scratch < $tmp.mount
+if [ $res -eq 0 ]; then
+       echo "Should not be able to mount after needsrepair crash"
+       _scratch_unmount
+fi
+
+# Repair properly this time and retry the mount
+_scratch_xfs_repair 2>> $seqres.full
+_check_scratch_xfs_features NEEDSREPAIR
+
+_scratch_mount
+
+# success, all done
+status=0
+exit
diff --git a/tests/xfs/154.out b/tests/xfs/154.out
new file mode 100644 (file)
index 0000000..12f154a
--- /dev/null
@@ -0,0 +1,4 @@
+QA output created by 154
+FEATURES: NEEDSREPAIR:YES
+mount: SCRATCH_MNT: mount(2) system call failed: Structure needs cleaning.
+FEATURES: NEEDSREPAIR:NO
diff --git a/tests/xfs/155 b/tests/xfs/155
new file mode 100755 (executable)
index 0000000..5dcfbfb
--- /dev/null
@@ -0,0 +1,91 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2021 Oracle.  All Rights Reserved.
+#
+# FS QA Test No. 155
+#
+# Populate a filesystem with all types of metadata, then run repair with the
+# libxfs write failure trigger set to go after a single write.  Check that the
+# injected error trips, causing repair to abort, that needsrepair is set on the
+# fs, the kernel won't mount; and that a non-injecting repair run clears
+# needsrepair and makes the filesystem mountable again.
+#
+# Repeat with the trip point set to successively higher numbers of writes until
+# we hit ~200 writes or repair manages to run to completion without tripping.
+
+seq=`basename $0`
+seqres=$RESULT_DIR/$seq
+echo "QA output created by $seq"
+
+here=`pwd`
+tmp=/tmp/$$
+status=1    # failure is the default!
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+_cleanup()
+{
+       cd /
+       rm -f $tmp.*
+}
+
+# get standard environment, filters and checks
+. ./common/rc
+. ./common/populate
+. ./common/filter
+
+# real QA test starts here
+_supported_fs xfs
+_require_scratch_nocheck
+_require_scratch_xfs_crc               # needsrepair only exists for v5
+_require_populate_commands
+_require_libxfs_debug_flag LIBXFS_DEBUG_WRITE_CRASH
+
+rm -f $seqres.full
+
+# Populate the filesystem
+_scratch_populate_cached nofill >> $seqres.full 2>&1
+
+max_writes=200                 # 200 loops should be enough for anyone
+nr_incr=$((13 / TIME_FACTOR))
+test $nr_incr -lt 1 && nr_incr=1
+for ((nr_writes = 1; nr_writes < max_writes; nr_writes += nr_incr)); do
+       # Add a tiny bit of randomness into each run
+       allowed_writes=$(( nr_writes + (RANDOM % 7) ))
+       echo "Setting debug hook to crash after $allowed_writes writes." >> $seqres.full
+
+       # Start a repair and force it to abort after some number of writes
+       LIBXFS_DEBUG_WRITE_CRASH=ddev=$allowed_writes \
+                       _scratch_xfs_repair 2>> $seqres.full
+       res=$?
+       if [ $res -ne 0 ] && [ $res -ne 137 ]; then
+               echo "repair failed with $res??"
+               break
+       elif [ $res -eq 0 ]; then
+               [ $nr_writes -eq 1 ] && \
+                       echo "ran to completion on the first try?"
+               break
+       fi
+
+       # Check the state of NEEDSREPAIR after repair fails.  If it isn't set
+       # but repair -n says the fs is clean, then it's possible that the
+       # injected error caused it to abort immediately after the write that
+       # cleared NEEDSREPAIR.
+       if ! _check_scratch_xfs_features NEEDSREPAIR > /dev/null &&
+          ! _scratch_xfs_repair -n &>> $seqres.full; then
+               echo "NEEDSREPAIR should be set on corrupt fs"
+       fi
+done
+
+# If NEEDSREPAIR is still set on the filesystem, ensure that a full run
+# cleans everything up.
+if _check_scratch_xfs_features NEEDSREPAIR > /dev/null; then
+       echo "Clearing NEEDSREPAIR" >> $seqres.full
+       _scratch_xfs_repair 2>> $seqres.full
+       _check_scratch_xfs_features NEEDSREPAIR > /dev/null && \
+               echo "Repair failed to clear NEEDSREPAIR on the $nr_writes writes test"
+fi
+
+# success, all done
+echo Silence is golden.
+status=0
+exit
diff --git a/tests/xfs/155.out b/tests/xfs/155.out
new file mode 100644 (file)
index 0000000..d82a8b4
--- /dev/null
@@ -0,0 +1,2 @@
+QA output created by 155
+Silence is golden.
index d1b1456b19c4a4110adb0591efef2480a5f634c6..511000923815a04f0be09a728bc2b159c5ebcce3 100644 (file)
 151 auto quick db
 152 auto quick quota idmapped
 153 auto quick quota idmapped
+154 auto quick repair
+155 auto repair
 164 rw pattern auto prealloc quick
 165 rw pattern auto prealloc quick
 166 rw metadata auto quick