mirror of
https://github.com/linux-apfs/apfstests.git
synced 2026-05-01 15:01:44 -07:00
57eba17be8
When stressing xfs/083, I found it sometimes fails as the following: +++ touch 50000 files setfattr: /home/fsgqa/scratchmnt/INOBT/20627: Operation not permitted ./common/fuzzy: line 18: /home/fsgqa/scratchmnt/INOBT/20627: Operation not permitted mv: cannot move '/home/fsgqa/scratchmnt/INOBT/20627' to '/home/fsgqa/scratchmnt/INOBT/20627.longer': Operation not permitted ... xfs_repair did not fix everything It's simply that INOBT/20627 was an immutable file generated from fuzzing. Therefore, this patch tries to clear append, immutable flag first before modification. Note that it clears dax flag as well since it prevents immutable flag from clearing. Signed-off-by: Gao Xiang <hsiangkao@redhat.com> Reviewed-by: Eryu Guan <guaneryu@gmail.com> Signed-off-by: Eryu Guan <guaneryu@gmail.com>
317 lines
8.6 KiB
Plaintext
317 lines
8.6 KiB
Plaintext
##/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() {
|
|
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
|
|
# try to remove append, immutable (and even dax) flag if exists
|
|
$XFS_IO_PROG -rc 'chattr -x -i -a' "$f" > /dev/null 2>&1
|
|
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 out any keys with an array index >= 10, collapse any array range
|
|
# ("[1-195]") to the first item, and ignore padding fields.
|
|
__filter_xfs_db_keys() {
|
|
sed -e '/\([a-z]*\)\[\([0-9][0-9]\+\)\].*/d' \
|
|
-e 's/\([a-zA-Z0-9_]*\)\[\([0-9]*\)-[0-9]*\]/\1[\2]/g' \
|
|
-e '/pad/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
|
|
grep ' = ' | while read key equals value; do
|
|
fuzzkey="$(echo "${key}" | __filter_xfs_db_keys)"
|
|
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 | __filter_xfs_db_keys
|
|
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}"
|
|
}
|
|
|
|
# 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
|
|
}
|
|
|
|
# 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, 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}"
|
|
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"
|
|
_try_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.
|
|
if [ "${repair}" != "none" ]; then
|
|
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
|
|
fi
|
|
|
|
# Try fixing the filesystem online?!
|
|
if [ "${repair}" = "online" ] || [ "${repair}" = "both" ]; then
|
|
__fuzz_notify "++ Try to repair filesystem online"
|
|
_scratch_scrub 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
|
|
if [ "${repair}" != "none" ]; then
|
|
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}.")
|
|
fi
|
|
|
|
# See if scrub finds a clean fs
|
|
echo "+ Make sure error is gone (online)"
|
|
_try_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.
|
|
if [ "${repair}" != "none" ]; then
|
|
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
|
|
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
|
|
if [ "${repair}" != "none" ]; then
|
|
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}.")
|
|
fi
|
|
}
|
|
|
|
# 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
|
|
}
|