blob: f5d45cb28f07b6d50daeb25452a85f17c644564d [file] [log] [blame]
##/bin/bash
# SPDX-License-Identifier: GPL-2.0+
# Copyright (c) 2017 Oracle. All Rights Reserved.
#
# Routines for fuzzing and scrubbing a filesystem.
# Modify various files after a fuzzing operation
_scratch_fuzz_modify() {
echo "+++ stressing filesystem"
mkdir -p $SCRATCH_MNT/data
_xfs_force_bdev data $SCRATCH_MNT/data
$FSSTRESS_PROG -n $((TIME_FACTOR * 10000)) -p $((LOAD_FACTOR * 4)) -d $SCRATCH_MNT/data
if _xfs_has_feature "$SCRATCH_MNT" realtime; then
mkdir -p $SCRATCH_MNT/rt
_xfs_force_bdev realtime $SCRATCH_MNT/rt
$FSSTRESS_PROG -n $((TIME_FACTOR * 10000)) -p $((LOAD_FACTOR * 4)) -d $SCRATCH_MNT/rt
else
echo "+++ xfs realtime not configured"
fi
}
# Try to access files after fuzzing
_scratch_fuzz_test() {
echo "+++ ls -laR" >> $seqres.full
ls -laR "${SCRATCH_MNT}/test.1/" >/dev/null 2>&1
echo "+++ cat files" >> $seqres.full
(find "${SCRATCH_MNT}/test.1/" -type f -size -1048576k -print0 | xargs -0 cat) >/dev/null 2>&1
}
# Do we have an online scrub program?
_require_scrub() {
case "${FSTYP}" in
"xfs")
test -x "$XFS_SCRUB_PROG" || _notrun "xfs_scrub not found"
;;
*)
_notrun "No online scrub program for ${FSTYP}."
;;
esac
}
# Scrub the scratch filesystem metadata (online)
_scratch_scrub() {
case "${FSTYP}" in
"xfs")
$XFS_SCRUB_PROG -d -T -v "$@" $SCRATCH_MNT
;;
*)
_fail "No online scrub program for ${FSTYP}."
;;
esac
}
# Expand indexed keys (i.e. arrays) into a long format so that we can filter
# the array indices individually, and pass regular keys right through.
#
# For example, "u3.bmx[0-1] = [foo,bar]" is exploded into:
# u3.bmx[0] = [foo,bar]
# u3.bmx[1] = [foo,bar]
#
# Note that we restrict array indices to [0-9] to reduce fuzz runtime. The
# minimum and maximum array indices can be changed by setting the variables
# SCRATCH_XFS_{MIN,MAX}_ARRAY_IDX.
#
# Also filter padding fields.
__explode_xfs_db_fields() {
local min_idx="${SCRATCH_XFS_MIN_ARRAY_IDX}"
local max_idx="${SCRATCH_XFS_MAX_ARRAY_IDX}"
test -z "${min_idx}" && min_idx=0
test -z "${max_idx}" && max_idx=9
test "${max_idx}" = "none" && max_idx=99999
grep ' = ' | \
sed -e 's/^\([.a-zA-Z0-9_]*\)\[\([0-9]*\)-\([0-9]*\)\]\(.*\) = \(.*\)$/\1[%d]\4 \2 \3 = \5/g' \
-e 's/^\([.a-zA-Z0-9_]*\)\[\([0-9]*\)\]\(.*\) = \(.*\)$/\1[%d]\3 \2 \2 = \4/g' | \
while read name col1 col2 rest; do
if [[ "${name}" == *pad* ]]; then
continue
fi
if [ "${col1}" = "=" ]; then
echo "${name} ${col1} ${col2} ${rest}"
continue
fi
test "${min_idx}" -gt "${col1}" && col1="${min_idx}"
test "${max_idx}" -lt "${col2}" && col2="${max_idx}"
seq "${col1}" "${col2}" | while read idx; do
printf "${name} %s\n" "${idx}" "${rest}"
done
done
}
# Filter out metadata fields that are completely controlled by userspace
# or are arbitrary bit sequences. In other words, fields where the filesystem
# does no validation.
__filter_unvalidated_xfs_db_fields() {
sed -e '/\.sec/d' \
-e '/\.nsec/d' \
-e '/^lsn$/d' \
-e '/\.lsn/d' \
-e '/^core.flushiter/d' \
-e '/^core.dmevmask/d' \
-e '/^core.dmstate/d' \
-e '/^core.gen/d' \
-e '/^core.prealloc/d' \
-e '/^core.immutable/d' \
-e '/^core.append/d' \
-e '/^core.sync/d' \
-e '/^core.noatime/d' \
-e '/^core.nodump/d' \
-e '/^core.nodefrag/d' \
-e '/^v3.dax/d' \
-e '/^nvlist.*value/d' \
-e '/^entries.*root/d' \
-e '/^entries.*secure/d' \
-e '/^a.sfattr.list.*value/d' \
-e '/^a.sfattr.list.*root/d' \
-e '/^a.sfattr.list.*secure/d'
}
# Filter the xfs_db print command's field debug information
# into field name and type.
__filter_xfs_db_print_fields() {
filter="$1"
if [ -z "${filter}" ] || [ "${filter}" = "nofilter" ]; then
filter='^'
fi
__explode_xfs_db_fields | while read key equals value; do
fuzzkey="$(echo "${key}")"
if [ -z "${fuzzkey}" ]; then
continue
elif [[ "${value}" == "["* ]]; then
echo "${value}" | sed -e 's/^.//g' -e 's/.$//g' -e 's/,/\n/g' | while read subfield; do
echo "${fuzzkey}.${subfield}"
done
else
echo "${fuzzkey}"
fi
done | grep -E "${filter}" | __filter_unvalidated_xfs_db_fields
}
# Dump the current contents of a metadata object.
# All arguments are xfs_db commands to locate the metadata.
_scratch_xfs_dump_metadata() {
local cmds=()
for arg in "$@"; do
cmds+=("-c" "${arg}")
done
_scratch_xfs_db "${cmds[@]}" -c print
}
# Decide from the output of the xfs_db "stack" command if the debugger's io
# cursor is pointed at a block that is an unstructured data format (blob).
__scratch_xfs_detect_blob_from_stack() {
grep -q -E 'inode.*, type (data|rtsummary|rtbitmap)'
}
# Navigate to some part of the filesystem and print the field info.
# The first argument is an grep filter for the fields
# The rest of the arguments are xfs_db commands to locate the metadata.
_scratch_xfs_list_metadata_fields() {
filter="$1"
shift
if [ -n "${SCRATCH_XFS_LIST_METADATA_FIELDS}" ]; then
echo "${SCRATCH_XFS_LIST_METADATA_FIELDS}" | tr '[ ,]' '[\n\n]'
return;
fi
local cmds=()
for arg in "$@"; do
cmds+=("-c" "${arg}")
done
# Does the path argument point towards something that is an
# unstructured blob?
if _scratch_xfs_db "${cmds[@]}" -c stack 2>/dev/null | \
__scratch_xfs_detect_blob_from_stack; then
echo "<blob>"
return
fi
_scratch_xfs_db "${cmds[@]}" -c print | \
__filter_xfs_db_print_fields "${filter}"
}
# Fuzz a metadata field
# The first arg is the field name
# The second arg is the xfs_db fuzz verb
# The rest of the arguments are xfs_db commands to find the metadata.
_scratch_xfs_fuzz_metadata_field() {
key="$1"
value="$2"
shift; shift
if [[ "${key}" == *crc ]]; then
fuzz_arg="-c"
else
fuzz_arg="-d"
fi
oldval="$(_scratch_xfs_get_metadata_field "${key}" "$@")"
local cmds=()
for arg in "$@"; do
cmds+=("-c" "${arg}")
done
while true; do
_scratch_xfs_db -x "${cmds[@]}" -c "fuzz ${fuzz_arg} ${key} ${value}"
echo
newval="$(_scratch_xfs_get_metadata_field "${key}" "$@" 2> /dev/null)"
if [ "${key}" != "random" ] || [ "${oldval}" != "${newval}" ]; then
break;
fi
done
if [ "${oldval}" = "${newval}" ]; then
echo "Field ${key} already set to ${newval}, skipping test."
return 1
fi
return 0
}
# List the fuzzing verbs available for unstructured blobs
__scratch_xfs_list_blob_fuzz_verbs() {
cat << ENDL
zeroes
ones
firstbit
middlebit
lastbit
random
ENDL
}
# Fuzz a metadata blob
# The first arg is a blob fuzzing verb
# The rest of the arguments are xfs_db commands to find the metadata.
_scratch_xfs_fuzz_metadata_blob() {
local fuzzverb="$1"
shift
local trashcmd=(blocktrash -z)
local cmds=()
for arg in "$@"; do
cmds+=("-c" "${arg}")
done
local bytecount=$(_scratch_xfs_db "${cmds[@]}" -c "stack" | grep 'byte.*length' | awk '{print $5}')
local bitmax=$((bytecount * 8))
case "${fuzzverb}" in
"zeroes")
trashcmd+=(-0 -o 0 -x "${bitmax}" -y "${bitmax}");;
"ones")
trashcmd+=(-1 -o 0 -x "${bitmax}" -y "${bitmax}");;
"firstbit")
trashcmd+=(-2 -o 0 -x 1 -y 1);;
"middlebit")
trashcmd+=(-2 -o $((bitmax / 2)) -x 1 -y 1);;
"lastbit")
trashcmd+=(-2 -o "${bitmax}" -x 1 -y 1);;
"random")
trashcmd+=(-3 -o 0 -x "${bitmax}" -y "${bitmax}");;
*)
echo "Unknown blob fuzz verb \"${fuzzverb}\"."
return 1
;;
esac
trashcmd="${trashcmd[@]}"
oldval="$(_scratch_xfs_get_metadata_field "" "$@")"
while true; do
_scratch_xfs_db -x "${cmds[@]}" -c "${trashcmd}"
echo
newval="$(_scratch_xfs_get_metadata_field "" "$@" 2> /dev/null)"
if [ "${fuzzverb}" != "random" ] || [ "${oldval}" != "${newval}" ]; then
break;
fi
done
if [ "${oldval}" = "${newval}" ]; then
echo "Blob already set to new value, skipping test."
return 1
fi
return 0
}
# Try to forcibly unmount the scratch fs
__scratch_xfs_fuzz_unmount()
{
while _scratch_unmount 2>/dev/null; do sleep 0.2; done
}
# Restore metadata to scratch device prior to field-fuzzing.
__scratch_xfs_fuzz_mdrestore()
{
__scratch_xfs_fuzz_unmount
_xfs_mdrestore "${POPULATE_METADUMP}" "${SCRATCH_DEV}" || \
_fail "${POPULATE_METADUMP}: Could not find metadump to restore?"
}
__fuzz_notify() {
echo '========================================'
echo "$*"
echo '========================================'
_kernlog "$*"
}
# Perform the online repair part of a fuzz test.
__scratch_xfs_fuzz_field_online() {
local fuzz_action="$1"
# Mount or else we can't do anything online
__fuzz_notify "+ Mount filesystem to try online repair"
_try_scratch_mount 2>&1
res=$?
if [ $res -ne 0 ]; then
(>&2 echo "${fuzz_action}: mount failed ($res).")
return 1
fi
# Make sure online scrub will catch whatever we fuzzed
__fuzz_notify "++ Detect fuzzed field (online)"
_scratch_scrub -n -a 1 -e continue 2>&1
res=$?
test $res -eq 0 && \
(>&2 echo "${fuzz_action}: online scrub didn't fail.")
# Does the health status report reflect the corruption?
if [ $res -ne 0 ]; then
__fuzz_notify "++ Detect fuzzed field ill-health report"
_check_xfs_health $SCRATCH_MNT 2>&1
res=$?
test $res -ne 1 && \
(>&2 echo "${fuzz_action}: online health check failed ($res).")
fi
# Try fixing the filesystem online
__fuzz_notify "++ Try to repair filesystem (online)"
_scratch_scrub 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "${fuzz_action}: online repair failed ($res).")
# Online scrub should pass now
__fuzz_notify "++ Make sure error is gone (online)"
_scratch_scrub -n -a 1 -e continue 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "${fuzz_action}: online re-scrub failed ($res).")
__scratch_xfs_fuzz_unmount
# Offline scrub should pass now
__fuzz_notify "+ Make sure error is gone (offline)"
_scratch_xfs_repair -P -n 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "${fuzz_action}: offline re-scrub failed ($res).")
return 0
}
# Perform the offline repair part of a fuzz test.
__scratch_xfs_fuzz_field_offline() {
local fuzz_action="$1"
# Make sure offline scrub will catch whatever we fuzzed
__fuzz_notify "+ Detect fuzzed field (offline)"
_scratch_xfs_repair -P -n 2>&1
res=$?
test $res -eq 0 && \
(>&2 echo "${fuzz_action}: offline scrub didn't fail.")
# Make sure xfs_repair catches at least as many things as the old
# xfs_check did.
if [ -n "${SCRATCH_XFS_FUZZ_CHECK}" ]; then
__fuzz_notify "+ Detect fuzzed field (xfs_check)"
_scratch_xfs_check 2>&1
res1=$?
if [ $res1 -ne 0 ] && [ $res -eq 0 ]; then
(>&2 echo "${fuzz_action}: xfs_repair passed but xfs_check failed ($res1).")
fi
fi
# Repair the filesystem offline
__fuzz_notify "+ Try to repair the filesystem (offline)"
_repair_scratch_fs -P 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "${fuzz_action}: offline repair failed ($res).")
# See if repair finds a clean fs
__fuzz_notify "+ Make sure error is gone (offline)"
_scratch_xfs_repair -P -n 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "${fuzz_action}: offline re-scrub failed ($res).")
return 0
}
# Perform the no-repair part of a fuzz test.
__scratch_xfs_fuzz_field_norepair() {
local fuzz_action="$1"
# Make sure offline scrub will catch whatever we fuzzed
__fuzz_notify "+ Detect fuzzed field (offline)"
_scratch_xfs_repair -P -n 2>&1
res=$?
test $res -eq 0 && \
(>&2 echo "${fuzz_action}: offline scrub didn't fail.")
# Mount or else we can't do anything in norepair mode
__fuzz_notify "+ Mount filesystem to try online scan"
_try_scratch_mount 2>&1
res=$?
if [ $res -ne 0 ]; then
(>&2 echo "${fuzz_action}: mount failed ($res).")
return 1
fi
# Skip scrub and health check if scrub is not supported
if ! _supports_xfs_scrub $SCRATCH_MNT $SCRATCH_DEV; then
__scratch_xfs_fuzz_unmount
return 0
fi
# Make sure online scrub will catch whatever we fuzzed
__fuzz_notify "++ Detect fuzzed field (online)"
_scratch_scrub -n -a 1 -e continue 2>&1
res=$?
test $res -eq 0 && \
(>&2 echo "${fuzz_action}: online scrub didn't fail.")
# Does the health status report reflect the corruption?
if [ $res -ne 0 ]; then
__fuzz_notify "++ Detect fuzzed field ill-health report"
_check_xfs_health $SCRATCH_MNT 2>&1
res=$?
test $res -ne 1 && \
(>&2 echo "${fuzz_action}: online health check failed ($res).")
fi
__scratch_xfs_fuzz_unmount
return 0
}
# Perform the online-then-offline repair part of a fuzz test.
__scratch_xfs_fuzz_field_both() {
local fuzz_action="$1"
# Make sure offline scrub will catch whatever we fuzzed
__fuzz_notify "+ Detect fuzzed field (offline)"
_scratch_xfs_repair -P -n 2>&1
res=$?
test $res -eq 0 && \
(>&2 echo "${fuzz_action}: offline scrub didn't fail.")
# Mount or else we can't do anything in both repair mode
__fuzz_notify "+ Mount filesystem to try both repairs"
_try_scratch_mount 2>&1
res=$?
if [ $res -ne 0 ]; then
(>&2 echo "${fuzz_action}: mount failed ($res).")
else
# Make sure online scrub will catch whatever we fuzzed
__fuzz_notify "++ Detect fuzzed field (online)"
_scratch_scrub -n -a 1 -e continue 2>&1
res=$?
test $res -eq 0 && \
(>&2 echo "${fuzz_action}: online scrub didn't fail.")
# Does the health status report reflect the corruption?
if [ $res -ne 0 ]; then
__fuzz_notify "++ Detect fuzzed field ill-health report"
_check_xfs_health $SCRATCH_MNT 2>&1
res=$?
test $res -ne 1 && \
(>&2 echo "${fuzz_action}: online health check failed ($res).")
fi
# Try fixing the filesystem online
__fuzz_notify "++ Try to repair filesystem (online)"
_scratch_scrub 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "${fuzz_action}: online repair failed ($res).")
__scratch_xfs_fuzz_unmount
fi
# Repair the filesystem offline if online repair failed?
if [ $res -ne 0 ]; then
__fuzz_notify "+ Try to repair the filesystem (offline)"
_repair_scratch_fs -P 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "${fuzz_action}: offline repair failed ($res).")
fi
# See if repair finds a clean fs
__fuzz_notify "+ Make sure error is gone (offline)"
_scratch_xfs_repair -P -n 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "${fuzz_action}: offline re-scrub failed ($res).")
# Mount so that we can see what scrub says after we've fixed the fs
__fuzz_notify "+ Re-mount filesystem to re-try online scan"
_try_scratch_mount 2>&1
res=$?
if [ $res -ne 0 ]; then
(>&2 echo "${fuzz_action}: mount failed ($res).")
return 1
fi
# Online scrub should pass now
__fuzz_notify "++ Make sure error is gone (online)"
_scratch_scrub -n -a 1 -e continue 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "${fuzz_action}: online re-scrub failed ($res).")
__scratch_xfs_fuzz_unmount
return 0
}
# Assess the state of the filesystem after a repair strategy has been run by
# trying to make changes to it.
_scratch_xfs_fuzz_field_modifyfs() {
local fuzz_action="$1"
local repair="$2"
# Try to mount the filesystem so that we can make changes
__fuzz_notify "+ Mount filesystem to make changes"
_try_scratch_mount 2>&1
res=$?
if [ $res -ne 0 ]; then
(>&2 echo "${fuzz_action}: pre-mod mount failed ($res).")
return $res
fi
# Try modifying the filesystem again
__fuzz_notify "++ Try to write filesystem again"
_scratch_fuzz_modify 2>&1
# If we didn't repair anything, there's no point in checking further,
# the fs is still corrupt.
if [ "${repair}" = "none" ]; then
__scratch_xfs_fuzz_unmount
return 0
fi
# Run an online check to make sure the fs is still ok, unless we
# are running the norepair strategy.
__fuzz_notify "+ Re-check the filesystem (online)"
_scratch_scrub -n -e continue 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "${fuzz_action}: online post-mod scrub failed ($res).")
__scratch_xfs_fuzz_unmount
# Run an offline check to make sure the fs is still ok, unless we
# are running the norepair strategy.
__fuzz_notify "+ Re-check the filesystem (offline)"
_scratch_xfs_repair -P -n 2>&1
res=$?
test $res -ne 0 && \
(>&2 echo "${fuzz_action}: offline post-mod scrub failed ($res).")
return 0
}
# Fuzz one field of some piece of metadata.
# First arg is the field name
# Second arg is the fuzz verb (ones, zeroes, random, add, sub...)
# Third arg is the repair mode (online, offline, both, none)
__scratch_xfs_fuzz_field_test() {
field="$1"
fuzzverb="$2"
repair="$3"
shift; shift; shift
# Set the new field value
__fuzz_notify "+ Fuzz ${field} = ${fuzzverb}"
if [ "$field" = "<blob>" ]; then
_scratch_xfs_fuzz_metadata_blob ${fuzzverb} "$@"
else
_scratch_xfs_fuzz_metadata_field "${field}" ${fuzzverb} "$@"
fi
res=$?
test $res -ne 0 && return
# Try to catch the error with whatever repair strategy we picked.
# The fs should not be mounted before or after the strategy call.
local fuzz_action="${field} = ${fuzzverb}"
case "${repair}" in
"online")
__scratch_xfs_fuzz_field_online "${fuzz_action}"
res=$?
;;
"offline")
__scratch_xfs_fuzz_field_offline "${fuzz_action}"
res=$?
;;
"none")
__scratch_xfs_fuzz_field_norepair "${fuzz_action}"
res=$?
;;
"both")
__scratch_xfs_fuzz_field_both "${fuzz_action}"
res=$?
;;
*)
(>&2 echo "unknown repair strategy ${repair}.")
res=2
;;
esac
test $res -eq 0 || return $res
# See what happens when we modify the fs
_scratch_xfs_fuzz_field_modifyfs "${fuzz_action}" "${repair}"
return $?
}
# Make sure we have all the pieces we need for field fuzzing
_require_scratch_xfs_fuzz_fields()
{
_require_scratch_nocheck
_require_scrub
_require_populate_commands
_scratch_mkfs_xfs >/dev/null 2>&1
_require_xfs_db_command "fuzz"
}
# Sets the array SCRATCH_XFS_DIR_FUZZ_TYPES to the list of directory formats
# available for fuzzing. Each list item must match one of the /S_IFDIR.FMT_*
# files created by the fs population code. Users can override this by setting
# SCRATCH_XFS_LIST_FUZZ_DIRTYPE in the environment. BTREE is omitted here
# because that refers to the fork format and does not affect the directory
# structure at all.
_scratch_xfs_set_dir_fuzz_types() {
if [ -n "${SCRATCH_XFS_LIST_FUZZ_DIRTYPE}" ]; then
mapfile -t SCRATCH_XFS_DIR_FUZZ_TYPES < \
<(echo "${SCRATCH_XFS_LIST_FUZZ_DIRTYPE}" | tr '[ ,]' '[\n\n]')
return
fi
SCRATCH_XFS_DIR_FUZZ_TYPES=(BLOCK LEAF LEAFN NODE)
}
# Sets the array SCRATCH_XFS_XATTR_FUZZ_TYPES to the list of xattr formats
# available for fuzzing. Each list item must match one of the /ATTR.FMT_*
# files created by the fs population code. Users can override this by setting
# SCRATCH_XFS_LIST_FUZZ_XATTRTYPE in the environment. BTREE is omitted here
# because that refers to the fork format and does not affect the extended
# attribute structure at all.
_scratch_xfs_set_xattr_fuzz_types() {
if [ -n "${SCRATCH_XFS_LIST_FUZZ_XATTRTYPE}" ]; then
mapfile -t SCRATCH_XFS_XATTR_FUZZ_TYPES < \
<(echo "${SCRATCH_XFS_LIST_FUZZ_XATTRTYPE}" | tr '[ ,]' '[\n\n]')
return
fi
SCRATCH_XFS_XATTR_FUZZ_TYPES=(EXTENTS_REMOTE3K EXTENTS_REMOTE4K LEAF NODE)
}
# Grab the list of available fuzzing verbs
_scratch_xfs_list_fuzz_verbs() {
if [ -n "${SCRATCH_XFS_LIST_FUZZ_VERBS}" ]; then
echo "${SCRATCH_XFS_LIST_FUZZ_VERBS}" | tr '[ ,]' '[\n\n]'
return;
fi
local cmds=()
for arg in "$@"; do
cmds+=("-c" "${arg}")
done
test "${#cmds[@]}" -eq 0 && cmds=('-c' 'sb 0')
# Does the path argument point towards something that is an
# unstructured blob?
if _scratch_xfs_db "${cmds[@]}" -c stack 2>/dev/null | \
__scratch_xfs_detect_blob_from_stack; then
__scratch_xfs_list_blob_fuzz_verbs
return
fi
_scratch_xfs_db -x "${cmds[@]}" -c 'fuzz' | grep '^Fuzz commands:' | \
sed -e 's/[,.]//g' -e 's/Fuzz commands: //g' -e 's/ /\n/g' | \
grep -v '^random$'
}
# Fuzz some of the fields of some piece of metadata
# The first argument is an grep filter for the field names
# The second argument is the repair mode (online, offline, both)
# The rest of the arguments are xfs_db commands to locate the metadata.
#
# Users can specify the fuzz verbs via SCRATCH_XFS_LIST_FUZZ_VERBS
# They can specify the fields via SCRATCH_XFS_LIST_METADATA_FIELDS
_scratch_xfs_fuzz_metadata() {
filter="$1"
repair="$2"
shift; shift
fields="$(_scratch_xfs_list_metadata_fields "${filter}" "$@")"
verbs="$(_scratch_xfs_list_fuzz_verbs "$@")"
echo "Fields we propose to fuzz with the \"${repair}\" repair strategy: $@"
echo $(echo "${fields}")
echo "Verbs we propose to fuzz with:"
echo $(echo "${verbs}")
echo "Current metadata object state:"
_scratch_xfs_dump_metadata "$@"
# Always capture full core dumps from crashing tools
ulimit -c unlimited
_xfs_skip_online_rebuild
_xfs_skip_offline_rebuild
echo "${fields}" | while read field; do
echo "${verbs}" | while read fuzzverb; do
__scratch_xfs_fuzz_mdrestore
__scratch_xfs_fuzz_field_test "${field}" "${fuzzverb}" "${repair}" "$@"
# Collect compresssed coredumps in the test results
# directory if the sysadmin didn't override the default
# coredump strategy.
for i in core core.*; do
test -f "$i" || continue
_save_coredump "$i"
done
done
done
}
# Functions to race fsstress, fs freeze, and xfs metadata scrubbing against
# each other to shake out bugs in xfs online repair.
# Filter freeze and thaw loop output so that we don't tarnish the golden output
# if the kernel temporarily won't let us freeze.
__stress_freeze_filter_output() {
_filter_scratch | \
sed -e '/Device or resource busy/d' \
-e '/Invalid argument/d'
}
# Filter scrub output so that we don't tarnish the golden output if the fs is
# too busy to scrub. Note: Tests should _notrun if the scrub type is not
# supported. Callers can provide extra strings to filter out as function
# arguments.
__stress_scrub_filter_output() {
local extra_args=()
for arg in "$@"; do
extra_args+=(-e "/${arg}/d")
done
_filter_scratch | \
sed -e '/Device or resource busy/d' \
-e '/Optimization possible/d' \
-e '/No space left on device/d' \
"${extra_args[@]}"
}
# Decide if the scratch filesystem is still alive.
__stress_scrub_scratch_alive() {
# If we can't stat the scratch filesystem, there's a reasonably good
# chance that the fs shut down, which is not good.
stat "$SCRATCH_MNT" &>/dev/null
}
# Decide if we want to keep running stress tests. The first argument is the
# stop time, and second argument is the path to the sentinel file.
__stress_scrub_running() {
test -e "$2" && test "$(date +%s)" -lt "$1" && __stress_scrub_scratch_alive
}
# Run fs freeze and thaw in a tight loop.
__stress_scrub_freeze_loop() {
local end="$1"
local runningfile="$2"
while __stress_scrub_running "$end" "$runningfile"; do
$XFS_IO_PROG -x -c 'freeze' -c 'thaw' $SCRATCH_MNT 2>&1 | \
__stress_freeze_filter_output
done
}
# Run individual xfs_io commands in a tight loop.
__stress_xfs_io_loop() {
local end="$1"
local runningfile="$2"
shift; shift
local xfs_io_args=()
for arg in "$@"; do
xfs_io_args+=('-c' "$arg")
done
while __stress_scrub_running "$end" "$runningfile"; do
$XFS_IO_PROG -x "${xfs_io_args[@]}" "$SCRATCH_MNT" \
> /dev/null 2>> $seqres.full
done
}
# Run individual XFS online fsck commands in a tight loop with xfs_io.
__stress_one_scrub_loop() {
local end="$1"
local runningfile="$2"
local scrub_tgt="$3"
local scrub_startat="$4"
local start_agno="$5"
shift; shift; shift; shift; shift
local agcount="$(_xfs_mount_agcount $SCRATCH_MNT)"
local xfs_io_args=()
for arg in "$@"; do
if [ -n "$SCRUBSTRESS_USE_FORCE_REBUILD" ]; then
arg="$(echo "$arg" | sed -e 's/^repair/repair -R/g')"
fi
if echo "$arg" | grep -q -w '%agno%'; then
# Substitute the AG number
for ((agno = start_agno; agno < agcount; agno++)); do
local ag_arg="$(echo "$arg" | sed -e "s|%agno%|$agno|g")"
xfs_io_args+=('-c' "$ag_arg")
done
else
xfs_io_args+=('-c' "$arg")
fi
done
local extra_filters=()
case "$scrub_tgt" in
"%file%"|"%datafile%"|"%attrfile%")
extra_filters+=('No such file or directory' 'No such device or address')
;;
"%dir%")
extra_filters+=('No such file or directory' 'Not a directory')
;;
"%regfile%"|"%cowfile%")
extra_filters+=('No such file or directory')
;;
esac
local target_cmd=(echo "$scrub_tgt")
case "$scrub_tgt" in
"%file%") target_cmd=($here/src/xfsfind -q "$SCRATCH_MNT");;
"%attrfile%") target_cmd=($here/src/xfsfind -qa "$SCRATCH_MNT");;
"%datafile%") target_cmd=($here/src/xfsfind -qb "$SCRATCH_MNT");;
"%dir%") target_cmd=($here/src/xfsfind -qd "$SCRATCH_MNT");;
"%regfile%") target_cmd=($here/src/xfsfind -qr "$SCRATCH_MNT");;
"%cowfile%") target_cmd=($here/src/xfsfind -qs "$SCRATCH_MNT");;
esac
while __stress_scrub_running "$scrub_startat" "$runningfile"; do
sleep 1
done
while __stress_scrub_running "$end" "$runningfile"; do
readarray -t fnames < <("${target_cmd[@]}" 2>> $seqres.full)
for fname in "${fnames[@]}"; do
$XFS_IO_PROG -x "${xfs_io_args[@]}" "$fname" 2>&1 | \
__stress_scrub_filter_output "${extra_filters[@]}"
__stress_scrub_running "$end" "$runningfile" || break
done
done
}
# Run xfs_scrub online fsck in a tight loop.
__stress_xfs_scrub_loop() {
local end="$1"
local runningfile="$2"
local scrub_startat="$3"
shift; shift; shift
local sigint_ret="$(( $(kill -l SIGINT) + 128 ))"
local scrublog="$tmp.scrub"
while __stress_scrub_running "$scrub_startat" "$runningfile"; do
sleep 1
done
while __stress_scrub_running "$end" "$runningfile"; do
_scratch_scrub "$@" &> $scrublog
res=$?
if [ "$res" -eq "$sigint_ret" ]; then
# Ignore SIGINT because the cleanup function sends
# that to terminate xfs_scrub
res=0
fi
echo "xfs_scrub exits with $res at $(date)" >> $seqres.full
if [ "$res" -ge 128 ]; then
# Report scrub death due to fatal signals
echo "xfs_scrub died with SIG$(kill -l $res)"
cat $scrublog >> $seqres.full 2>/dev/null
elif [ "$((res & 0x1))" -gt 0 ]; then
# Report uncorrected filesystem errors
echo "xfs_scrub reports uncorrected errors:"
grep -E '(Repair unsuccessful;|Corruption:)' $scrublog
cat $scrublog >> $seqres.full 2>/dev/null
fi
rm -f $scrublog
done
}
# Clean the scratch filesystem between rounds of fsstress if there is 2%
# available space or less because that isn't an interesting stress test.
#
# Returns 0 if we cleared anything, and 1 if we did nothing.
__stress_scrub_clean_scratch() {
local used_pct="$(_used $SCRATCH_DEV)"
test "$used_pct" -lt 98 && return 1
echo "Clearing scratch fs at $(date)" >> $seqres.full
rm -r -f $SCRATCH_MNT/p*
return 0
}
# Run fsx while we're testing online fsck.
__stress_scrub_fsx_loop() {
local end="$1"
local runningfile="$2"
local remount_period="$3"
local stress_tgt="$4" # ignored
local focus=(-q -X) # quiet, validate file contents
# As of November 2022, 2 million fsx ops should be enough to keep
# any filesystem busy for a couple of hours.
focus+=(-N 2000000)
focus+=(-o $((128000 * LOAD_FACTOR)) )
focus+=(-l $((600000 * LOAD_FACTOR)) )
local args="$FSX_AVOID ${focus[@]} ${SCRATCH_MNT}/fsx.$seq"
echo "Running $here/ltp/fsx $args" >> $seqres.full
if [ -n "$remount_period" ]; then
local mode="rw"
local rw_arg=""
while __stress_scrub_running "$end" "$runningfile"; do
# Need to recheck running conditions if we cleared
# anything.
test "$mode" = "rw" && __stress_scrub_clean_scratch && continue
timeout -s TERM "$remount_period" $here/ltp/fsx \
$args $rw_arg >> $seqres.full
res=$?
echo "$mode fsx exits with $res at $(date)" >> $seqres.full
if [ "$res" -ne 0 ] && [ "$res" -ne 124 ]; then
# Stop if fsstress returns error. Mask off
# the magic code 124 because that is how the
# timeout(1) program communicates that we ran
# out of time.
break;
fi
if [ "$mode" = "rw" ]; then
mode="ro"
rw_arg="-t 0 -w 0 -FHzCIJBE0"
else
mode="rw"
rw_arg=""
fi
# Try remounting until we get the result we wanted
while ! _scratch_remount "$mode" &>/dev/null && \
__stress_scrub_running "$end" "$runningfile"; do
sleep 0.2
done
done
rm -f "$runningfile"
return 0
fi
while __stress_scrub_running "$end" "$runningfile"; do
# Need to recheck running conditions if we cleared anything
__stress_scrub_clean_scratch && continue
$here/ltp/fsx $args >> $seqres.full
echo "fsx exits with $? at $(date)" >> $seqres.full
done
rm -f "$runningfile"
}
# Run fsstress while we're testing online fsck.
__stress_scrub_fsstress_loop() {
local end="$1"
local runningfile="$2"
local remount_period="$3"
local stress_tgt="$4"
local focus=()
case "$stress_tgt" in
"parent")
focus+=('-z')
# Create a directory tree very gradually
for op in creat link mkdir; do
focus+=('-f' "${op}=2")
done
focus+=('-f' 'unlink=1' '-f' 'rmdir=1')
# But do a lot of renames to cycle parent pointers
for op in rename rnoreplace rexchange; do
focus+=('-f' "${op}=40")
done
;;
"dir")
focus+=('-z')
# Create a directory tree rapidly
for op in creat link mkdir mknod symlink; do
focus+=('-f' "${op}=8")
done
focus+=('-f' 'rmdir=2' '-f' 'unlink=8')
# Rename half as often
for op in rename rnoreplace rexchange; do
focus+=('-f' "${op}=4")
done
# Read and sync occasionally
for op in getdents stat fsync; do
focus+=('-f' "${op}=1")
done
;;
"xattr")
focus+=('-z')
# Create a directory tree slowly
for op in creat ; do
focus+=('-f' "${op}=2")
done
for op in unlink rmdir; do
focus+=('-f' "${op}=1")
done
# Create xattrs rapidly
for op in attr_set setfattr; do
focus+=('-f' "${op}=80")
done
# Remove xattrs 1/4 as quickly
for op in attr_remove removefattr; do
focus+=('-f' "${op}=20")
done
# Read and sync occasionally
for op in listfattr getfattr fsync; do
focus+=('-f' "${op}=10")
done
;;
"writeonly")
# Only do things that cause filesystem writes
focus+=('-w')
;;
"default")
# No new arguments
;;
"symlink")
focus+=('-z')
# Only create, read, and delete symbolic links
focus+=('-f' 'symlink=4')
focus+=('-f' 'readlink=10')
focus+=('-f' 'unlink=1')
;;
"mknod")
focus+=('-z')
# Only create and delete special files
focus+=('-f' 'mknod=4')
focus+=('-f' 'getdents=100')
focus+=('-f' 'unlink=1')
;;
*)
echo "$stress_tgt: Unrecognized stress target, using defaults."
;;
esac
# As of March 2022, 2 million fsstress ops should be enough to keep
# any filesystem busy for a couple of hours.
local args=$(_scale_fsstress_args -p 4 -d $SCRATCH_MNT -n 2000000 "${focus[@]}" $FSSTRESS_AVOID)
echo "Running $FSSTRESS_PROG $args" >> $seqres.full
if [ -n "$remount_period" ]; then
local mode="rw"
local rw_arg=""
while __stress_scrub_running "$end" "$runningfile"; do
# Need to recheck running conditions if we cleared
# anything.
test "$mode" = "rw" && __stress_scrub_clean_scratch && continue
timeout -s TERM "$remount_period" $FSSTRESS_PROG \
$args $rw_arg >> $seqres.full
res=$?
echo "$mode fsstress exits with $res at $(date)" >> $seqres.full
if [ "$res" -ne 0 ] && [ "$res" -ne 124 ]; then
# Stop if fsstress returns error. Mask off
# the magic code 124 because that is how the
# timeout(1) program communicates that we ran
# out of time.
break;
fi
if [ "$mode" = "rw" ]; then
mode="ro"
rw_arg="-R"
else
mode="rw"
rw_arg=""
fi
# Try remounting until we get the result we wanted
while ! _scratch_remount "$mode" &>/dev/null && \
__stress_scrub_running "$end" "$runningfile"; do
sleep 0.2
done
done
rm -f "$runningfile"
return 0
fi
while __stress_scrub_running "$end" "$runningfile"; do
# Need to recheck running conditions if we cleared anything
__stress_scrub_clean_scratch && continue
$FSSTRESS_PROG $args >> $seqres.full
echo "fsstress exits with $? at $(date)" >> $seqres.full
done
rm -f "$runningfile"
}
# Make sure we have everything we need to run stress and scrub
_require_xfs_stress_scrub() {
_require_xfs_io_command "scrub"
_require_test_program "xfsfind"
_require_command "$KILLALL_PROG" killall
_require_freeze
command -v _filter_scratch &>/dev/null || \
_notrun 'xfs scrub stress test requires common/filter'
}
# Make sure that we can force repairs either by error injection or passing
# FORCE_REBUILD via ioctl.
__require_xfs_stress_force_rebuild() {
local output="$($XFS_IO_PROG -x -c 'repair -R probe' $SCRATCH_MNT 2>&1)"
test -z "$output" && return
_require_xfs_io_error_injection "force_repair"
}
# Make sure we have everything we need to run stress and online repair
_require_xfs_stress_online_repair() {
_require_xfs_stress_scrub
_require_xfs_io_command "repair"
command -v _require_xfs_io_error_injection &>/dev/null || \
_notrun 'xfs repair stress test requires common/inject'
__require_xfs_stress_force_rebuild
_require_freeze
}
# Clean up after the loops in case they didn't do it themselves.
_scratch_xfs_stress_scrub_cleanup() {
rm -f "$runningfile"
echo "Cleaning up scrub stress run at $(date)" >> $seqres.full
# Send SIGINT so that bash won't print a 'Terminated' message that
# distorts the golden output.
echo "Killing stressor processes at $(date)" >> $seqres.full
$KILLALL_PROG -INT xfs_io fsstress fsx xfs_scrub >> $seqres.full 2>&1
# Tests are not allowed to exit with the scratch fs frozen. If we
# started a fs freeze/thaw background loop, wait for that loop to exit
# and then thaw the filesystem. Cleanup for the freeze loop must be
# performed prior to waiting for the other children to avoid triggering
# a race condition that can hang fstests.
#
# If the xfs_io -c freeze process is asleep waiting for a write lock on
# s_umount or sb_write when the killall signal is delivered, it will
# not check for pending signals until after it has frozen the fs. If
# even one thread of the stress test processes (xfs_io, fsstress, etc.)
# is waiting for read locks on sb_write when the killall signals are
# delivered, they will block in the kernel until someone thaws the fs,
# and the `wait' below will wait forever.
#
# Hence we issue the killall, wait for the freezer loop to exit, thaw
# the filesystem, and wait for the rest of the children.
if [ -n "$__SCRUB_STRESS_FREEZE_PID" ]; then
echo "Waiting for fs freezer $__SCRUB_STRESS_FREEZE_PID to exit at $(date)" >> $seqres.full
wait "$__SCRUB_STRESS_FREEZE_PID"
echo "Thawing filesystem at $(date)" >> $seqres.full
$XFS_IO_PROG -x -c 'thaw' $SCRATCH_MNT >> $seqres.full 2>&1
__SCRUB_STRESS_FREEZE_PID=""
fi
# Wait for the remaining children to exit.
echo "Waiting for children to exit at $(date)" >> $seqres.full
wait
# Ensure the scratch fs is also writable before we exit.
if [ -n "$__SCRUB_STRESS_REMOUNT_LOOP" ]; then
echo "Remounting rw at $(date)" >> $seqres.full
_scratch_remount rw >> $seqres.full 2>&1
__SCRUB_STRESS_REMOUNT_LOOP=""
fi
echo "Cleanup finished at $(date)" >> $seqres.full
}
# Make sure the provided scrub/repair commands actually work on the scratch
# filesystem before we start running them in a loop.
__stress_scrub_check_commands() {
local scrub_tgt="$1"
local start_agno="$2"
shift; shift
local cooked_tgt="$scrub_tgt"
case "$scrub_tgt" in
"%file%"|"%dir%")
cooked_tgt="$SCRATCH_MNT"
;;
"%regfile%"|"%datafile%")
cooked_tgt="$SCRATCH_MNT/testfile"
echo test > "$cooked_tgt"
;;
"%attrfile%")
cooked_tgt="$SCRATCH_MNT/testfile"
$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 64k' "$cooked_tgt" &>/dev/null
attr -s attrname "$cooked_tgt" < "$cooked_tgt" &>/dev/null
;;
"%cowfile%")
cooked_tgt="$SCRATCH_MNT/testfile"
$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 128k' "$cooked_tgt" &>/dev/null
_cp_reflink "$cooked_tgt" "$cooked_tgt.1"
$XFS_IO_PROG -f -c 'pwrite -S 0x58 0 1' "$cooked_tgt.1" &>/dev/null
;;
esac
for arg in "$@"; do
local cooked_arg="$arg"
if [ -n "$SCRUBSTRESS_USE_FORCE_REBUILD" ]; then
cooked_arg="$(echo "$cooked_arg" | sed -e 's/^repair/repair -R/g')"
fi
cooked_arg="$(echo "$cooked_arg" | sed -e "s/%agno%/$start_agno/g")"
testio=`$XFS_IO_PROG -x -c "$cooked_arg" "$cooked_tgt" 2>&1`
echo $testio | grep -q "Unknown type" && \
_notrun "xfs_io scrub subcommand support is missing"
echo $testio | grep -q "Inappropriate ioctl" && \
_notrun "kernel scrub ioctl is missing"
echo $testio | grep -q "No such file or directory" && \
_notrun "kernel does not know about: $arg"
echo $testio | grep -q "Operation not supported" && \
_notrun "kernel does not support: $arg"
done
}
# Start scrub, freeze, and fsstress in background looping processes, and wait
# for 30*TIME_FACTOR seconds to see if the filesystem goes down. Callers
# must call _scratch_xfs_stress_scrub_cleanup from their cleanup functions.
#
# Various options include:
#
# -a For %agno% substitution, start with this AG instead of AG 0.
# -f Run a freeze/thaw loop while we're doing other things. Defaults to
# disabled, unless XFS_SCRUB_STRESS_FREEZE is set.
# -i Pass this command to xfs_io to exercise something that is not scrub
# in a separate loop. If zero -i options are specified, do not run.
# Callers must check each of these commands (via _require_xfs_io_command)
# before calling here.
# -r Run fsstress for this amount of time, then remount the fs ro or rw.
# The default is to run fsstress continuously with no remount, unless
# XFS_SCRUB_STRESS_REMOUNT_PERIOD is set.
# -s Pass this command to xfs_io to test scrub. If zero -s options are
# specified, xfs_io will not be run.
# -S Pass this option to xfs_scrub. If zero -S options are specified,
# xfs_scrub will not be run. To select repair mode, pass '-k' or '-v'.
# -t Run online scrub against this file; $SCRATCH_MNT is the default.
# Special values are as follows:
#
# %file% all files
# %regfile% regular files
# %dir% direct
# %datafile% regular files with data blocks
# %attrfile% regular files with xattr blocks
# %cowfile% regular files with shared blocks
#
# File selection races with fsstress, so the selection is best-effort.
# -w Delay the start of the scrub/repair loop by this number of seconds.
# Defaults to no delay unless XFS_SCRUB_STRESS_DELAY is set. This value
# will be clamped to ten seconds before the end time.
# -x Focus on this type of fsstress operation. Possible values:
#
# 'dir': Grow the directory trees as much as possible.
# 'xattr': Grow extended attributes in a small tree.
# 'default': Run fsstress with default arguments.
# 'writeonly': Only perform fs updates, no reads.
# 'symlink': Only create symbolic links.
# 'mknod': Only create special files.
# 'parent': Focus on updating parent pointers
#
# The default is 'default' unless XFS_SCRUB_STRESS_TARGET is set.
# -X Run this program to exercise the filesystem. Currently supported
# options are 'fsx' and 'fsstress'. The default is 'fsstress'.
_scratch_xfs_stress_scrub() {
local one_scrub_args=()
local xfs_scrub_args=()
local scrub_tgt="$SCRATCH_MNT"
local runningfile="$tmp.fsstress"
local freeze="${XFS_SCRUB_STRESS_FREEZE}"
local scrub_delay="${XFS_SCRUB_STRESS_DELAY:--1}"
local exerciser="fsstress"
local io_args=()
local remount_period="${XFS_SCRUB_STRESS_REMOUNT_PERIOD}"
local stress_tgt="${XFS_SCRUB_STRESS_TARGET:-default}"
local start_agno=0
__SCRUB_STRESS_FREEZE_PID=""
__SCRUB_STRESS_REMOUNT_LOOP=""
rm -f "$runningfile"
touch "$runningfile"
OPTIND=1
while getopts "a:fi:r:s:S:t:w:x:X:" c; do
case "$c" in
a) start_agno="$OPTARG";;
f) freeze=yes;;
i) io_args+=("$OPTARG");;
r) remount_period="$OPTARG";;
s) one_scrub_args+=("$OPTARG");;
S) xfs_scrub_args+=("$OPTARG");;
t) scrub_tgt="$OPTARG";;
w) scrub_delay="$OPTARG";;
x) stress_tgt="$OPTARG";;
X) exerciser="$OPTARG";;
*) return 1; ;;
esac
done
__stress_scrub_check_commands "$scrub_tgt" "$start_agno" \
"${one_scrub_args[@]}"
if ! command -v "__stress_scrub_${exerciser}_loop" &>/dev/null; then
echo "${exerciser}: Unknown fs exercise program."
return 1
fi
if [ "${#xfs_scrub_args[@]}" -gt 0 ]; then
_scratch_scrub "${xfs_scrub_args[@]}" &> "$tmp.scrub"
res=$?
if [ $res -ne 0 ]; then
echo "xfs_scrub ${xfs_scrub_args[@]} failed, err $res" >> $seqres.full
cat "$tmp.scrub" >> $seqres.full
rm -f "$tmp.scrub"
_notrun 'scrub not supported on scratch filesystem'
fi
rm -f "$tmp.scrub"
fi
_xfs_skip_online_rebuild
_xfs_skip_offline_rebuild
local start="$(date +%s)"
local end
if [ -n "$SOAK_DURATION" ]; then
end="$((start + SOAK_DURATION))"
else
end="$((start + (30 * TIME_FACTOR) ))"
fi
local scrub_startat="$((start + scrub_delay))"
test "$scrub_startat" -gt "$((end - 10))" &&
scrub_startat="$((end - 10))"
echo "Loop started at $(date --date="@${start}")," \
"ending at $(date --date="@${end}")" >> $seqres.full
if [ -n "$remount_period" ]; then
__SCRUB_STRESS_REMOUNT_LOOP="1"
fi
"__stress_scrub_${exerciser}_loop" "$end" "$runningfile" \
"$remount_period" "$stress_tgt" &
if [ -n "$freeze" ]; then
__stress_scrub_freeze_loop "$end" "$runningfile" &
__SCRUB_STRESS_FREEZE_PID="$!"
fi
if [ "${#io_args[@]}" -gt 0 ]; then
__stress_xfs_io_loop "$end" "$runningfile" \
"${io_args[@]}" &
fi
if [ "${#one_scrub_args[@]}" -gt 0 ]; then
__stress_one_scrub_loop "$end" "$runningfile" "$scrub_tgt" \
"$scrub_startat" "$start_agno" \
"${one_scrub_args[@]}" &
fi
if [ "${#xfs_scrub_args[@]}" -gt 0 ]; then
__stress_xfs_scrub_loop "$end" "$runningfile" "$scrub_startat" \
"${xfs_scrub_args[@]}" &
fi
# Wait until the designated end time or fsstress dies, then kill all of
# our background processes.
while __stress_scrub_running "$end" "$runningfile"; do
sleep 1
done
_scratch_xfs_stress_scrub_cleanup
# Warn the user if we think the scratch filesystem went down.
__stress_scrub_scratch_alive || \
echo "Did the scratch filesystem die?"
echo "Loop finished at $(date)" >> $seqres.full
}
# Decide if we're going to force repairs either by error injection or passing
# FORCE_REBUILD via ioctl.
__scratch_xfs_stress_setup_force_rebuild() {
local output="$($XFS_IO_PROG -x -c 'repair -R probe' $SCRATCH_MNT 2>&1)"
if [ -z "$output" ]; then
SCRUBSTRESS_USE_FORCE_REBUILD=1
return
fi
$XFS_IO_PROG -x -c 'inject force_repair' $SCRATCH_MNT
}
# Start online repair, freeze, and fsstress in background looping processes,
# and wait for 30*TIME_FACTOR seconds to see if the filesystem goes down.
# Same requirements and arguments as _scratch_xfs_stress_scrub.
_scratch_xfs_stress_online_repair() {
__scratch_xfs_stress_setup_force_rebuild
XFS_SCRUB_FORCE_REPAIR=1 _scratch_xfs_stress_scrub "$@"
}