mirror of
https://github.com/linux-apfs/apfstests.git
synced 2026-05-01 15:01:44 -07:00
4b69106b60
When we're doing the second online scrub (to figure out if the repair did any good) we shouldn't let that second scrub preen the filesystem in any way. If scrub finds things it can't/won't preen that turns into a nonzero return code which gets reported (incorrectly) as a failure. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Eryu Guan <eguan@redhat.com> Signed-off-by: Eryu Guan <eguan@redhat.com>
345 lines
9.7 KiB
Plaintext
345 lines
9.7 KiB
Plaintext
##/bin/bash
|
|
|
|
# Routines for fuzzing and scrubbing a filesystem.
|
|
#
|
|
#-----------------------------------------------------------------------
|
|
# Copyright (c) 2017 Oracle. All Rights Reserved.
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
# USA
|
|
#-----------------------------------------------------------------------
|
|
|
|
# Modify various files after a fuzzing operation
|
|
_scratch_fuzz_modify() {
|
|
nr="$1"
|
|
|
|
test -z "${nr}" && nr=50000
|
|
echo "+++ touch ${nr} files"
|
|
blk_sz=$(stat -f -c '%s' ${SCRATCH_MNT})
|
|
$XFS_IO_PROG -f -c "pwrite -S 0x63 0 ${blk_sz}" "/tmp/afile" > /dev/null
|
|
date="$(date)"
|
|
find "${SCRATCH_MNT}/" -type f 2> /dev/null | head -n "${nr}" | while read f; do
|
|
setfattr -n "user.date" -v "${date}" "$f"
|
|
cat "/tmp/afile" >> "$f"
|
|
mv "$f" "$f.longer"
|
|
done
|
|
sync
|
|
rm -rf "/tmp/afile"
|
|
|
|
echo "+++ create files"
|
|
mkdir -p "${SCRATCH_MNT}/test.moo"
|
|
$XFS_IO_PROG -f -c 'pwrite -S 0x80 0 65536' "${SCRATCH_MNT}/test.moo/urk" > /dev/null
|
|
sync
|
|
|
|
echo "+++ remove files"
|
|
rm -rf "${SCRATCH_MNT}/test.moo"
|
|
}
|
|
|
|
# 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
|
|
}
|
|
|
|
# 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
|
|
grep ' = ' | while read key equals value; do
|
|
# Filter out any keys with an array index >= 10, and
|
|
# collapse any array range ("[1-195]") to the first item.
|
|
fuzzkey="$(echo "${key}" | sed -e '/\([a-z]*\)\[\([0-9][0-9]\+\)\].*/d' -e 's/\([a-zA-Z0-9_]*\)\[\([0-9]*\)-[0-9]*\]/\1[\2]/g')"
|
|
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 | egrep "${filter}"
|
|
}
|
|
|
|
# Navigate to some part of the filesystem and print the field info.
|
|
# The first argument is an egrep 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
|
|
_scratch_xfs_db "${cmds[@]}" -c print | __filter_xfs_db_print_fields "${filter}"
|
|
}
|
|
|
|
# Get a metadata field
|
|
# The first arg is the field name
|
|
# The rest of the arguments are xfs_db commands to find the metadata.
|
|
_scratch_xfs_get_metadata_field() {
|
|
key="$1"
|
|
shift
|
|
|
|
grep_key="$(echo "${key}" | tr '[]()' '....')"
|
|
local cmds=()
|
|
for arg in "$@"; do
|
|
cmds+=("-c" "${arg}")
|
|
done
|
|
_scratch_xfs_db "${cmds[@]}" -c "print ${key}" | grep "^${grep_key}" | \
|
|
sed -e 's/^.* = //g'
|
|
}
|
|
|
|
# Set a metadata field
|
|
# The first arg is the field name
|
|
# The second arg is the new value
|
|
# The rest of the arguments are xfs_db commands to find the metadata.
|
|
_scratch_xfs_set_metadata_field() {
|
|
key="$1"
|
|
value="$2"
|
|
shift; shift
|
|
|
|
local cmds=()
|
|
for arg in "$@"; do
|
|
cmds+=("-c" "${arg}")
|
|
done
|
|
_scratch_xfs_db -x "${cmds[@]}" -c "write -d ${key} ${value}"
|
|
echo
|
|
}
|
|
|
|
# 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
|
|
_scratch_xfs_db -x "${cmds[@]}" -c "fuzz ${fuzz_arg} ${key} ${value}"
|
|
echo
|
|
newval="$(_scratch_xfs_get_metadata_field "${key}" "$@" 2> /dev/null)"
|
|
if [ "${oldval}" = "${newval}" ]; then
|
|
echo "Field ${key} already set to ${newval}, 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()
|
|
{
|
|
test -e "${POPULATE_METADUMP}" || _fail "Need to set POPULATE_METADUMP"
|
|
|
|
__scratch_xfs_fuzz_unmount
|
|
xfs_mdrestore "${POPULATE_METADUMP}" "${SCRATCH_DEV}"
|
|
}
|
|
|
|
__fuzz_notify() {
|
|
echo "$@"
|
|
test -w /dev/ttyprintk && echo "$@" >> /dev/ttyprintk
|
|
}
|
|
|
|
# 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)
|
|
__scratch_xfs_fuzz_field_test() {
|
|
field="$1"
|
|
fuzzverb="$2"
|
|
repair="$3"
|
|
shift; shift; shift
|
|
|
|
# Set the new field value
|
|
__fuzz_notify "+ Fuzz ${field} = ${fuzzverb}"
|
|
echo "========================"
|
|
_scratch_xfs_fuzz_metadata_field "${field}" ${fuzzverb} "$@"
|
|
res=$?
|
|
test $res -ne 0 && return
|
|
|
|
# Try to catch the error with scrub
|
|
echo "+ Try to catch the error"
|
|
_scratch_mount 2>&1
|
|
res=$?
|
|
if [ $res -eq 0 ]; then
|
|
# Try an online scrub unless we're fuzzing ag 0's sb,
|
|
# which scrub doesn't know how to fix.
|
|
echo "++ Online scrub"
|
|
if [ "$1" != "sb 0" ]; then
|
|
_scratch_scrub -n -a 1 -e continue 2>&1
|
|
res=$?
|
|
test $res -eq 0 && \
|
|
(>&2 echo "scrub didn't fail with ${field} = ${fuzzverb}.")
|
|
fi
|
|
|
|
# Try fixing the filesystem online?!
|
|
if [ "${repair}" = "online" ] || [ "${repair}" = "both" ]; then
|
|
__fuzz_notify "++ Try to repair filesystem online"
|
|
_scratch_scrub -y 2>&1
|
|
res=$?
|
|
test $res -ne 0 && \
|
|
(>&2 echo "online repair failed ($res) with ${field} = ${fuzzverb}.")
|
|
fi
|
|
|
|
__scratch_xfs_fuzz_unmount
|
|
elif [ "${repair}" = "online" ] || [ "${repair}" = "both" ]; then
|
|
(>&2 echo "mount failed ($res) with ${field} = ${fuzzverb}.")
|
|
fi
|
|
|
|
# Repair the filesystem offline?
|
|
if [ "${repair}" = "offline" ] || [ "${repair}" = "both" ]; then
|
|
echo "+ Try to repair the filesystem offline"
|
|
_repair_scratch_fs 2>&1
|
|
res=$?
|
|
test $res -ne 0 && \
|
|
(>&2 echo "offline repair failed ($res) with ${field} = ${fuzzverb}.")
|
|
fi
|
|
|
|
# See if repair finds a clean fs
|
|
echo "+ Make sure error is gone (offline)"
|
|
_scratch_xfs_repair -n 2>&1
|
|
res=$?
|
|
test $res -ne 0 && \
|
|
(>&2 echo "offline re-scrub ($res) with ${field} = ${fuzzverb}.")
|
|
|
|
# See if scrub finds a clean fs
|
|
echo "+ Make sure error is gone (online)"
|
|
_scratch_mount 2>&1
|
|
res=$?
|
|
if [ $res -eq 0 ]; then
|
|
# Try an online scrub unless we're fuzzing ag 0's sb,
|
|
# which scrub doesn't know how to fix.
|
|
echo "++ Online scrub"
|
|
if [ "$1" != "sb 0" ]; then
|
|
_scratch_scrub -n -e continue 2>&1
|
|
res=$?
|
|
test $res -ne 0 && \
|
|
(>&2 echo "online re-scrub ($res) with ${field} = ${fuzzverb}.")
|
|
fi
|
|
|
|
# Try modifying the filesystem again!
|
|
__fuzz_notify "++ Try to write filesystem again"
|
|
_scratch_fuzz_modify 100 2>&1
|
|
__scratch_xfs_fuzz_unmount
|
|
else
|
|
(>&2 echo "re-mount failed ($res) with ${field} = ${fuzzverb}.")
|
|
fi
|
|
|
|
# See if repair finds a clean fs
|
|
echo "+ Re-check the filesystem (offline)"
|
|
_scratch_xfs_repair -n 2>&1
|
|
res=$?
|
|
test $res -ne 0 && \
|
|
(>&2 echo "re-repair failed ($res) with ${field} = ${fuzzverb}.")
|
|
}
|
|
|
|
# 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"
|
|
}
|
|
|
|
# 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
|
|
_scratch_xfs_db -x -c 'sb 0' -c 'fuzz' | grep '^Fuzz commands:' | \
|
|
sed -e 's/[,.]//g' -e 's/Fuzz commands: //g' -e 's/ /\n/g'
|
|
}
|
|
|
|
# Fuzz some of the fields of some piece of metadata
|
|
# The first argument is an egrep 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 under: $@"
|
|
echo $(echo "${fields}")
|
|
echo "Verbs we propose to fuzz with:"
|
|
echo $(echo "${verbs}")
|
|
|
|
echo "${fields}" | while read field; do
|
|
echo "${verbs}" | while read fuzzverb; do
|
|
__scratch_xfs_fuzz_mdrestore
|
|
__scratch_xfs_fuzz_field_test "${field}" "${fuzzverb}" "${repair}" "$@"
|
|
done
|
|
done
|
|
}
|