btrfs-progs/tests/common
David Sterba b6bf79eb96 btrfs-progs: tests: don't treat segfault as ignorable error
Some fuzzed images cause various tools to crash but the mayfail helper
would not recognize that. We don't mind if the utility failes but it
must not crash.

Signed-off-by: David Sterba <dsterba@suse.com>
2016-10-05 12:39:01 +02:00

281 lines
5.8 KiB
Bash

#!/bin/bash
#
# Common routines for all tests
#
_fail()
{
echo "$*" | tee -a $RESULTS
exit 1
}
# log a message to the results file
_log()
{
echo "$*" | tee -a $RESULTS
}
_not_run()
{
echo " [NOTRUN] $*"
exit 0
}
run_check()
{
echo "############### $@" >> $RESULTS 2>&1
if [ "$TEST_LOG" = 'tty' ]; then echo "CMD: $@" > /dev/tty; fi
if [ "$1" = 'root_helper' ]; then
"$@" >> $RESULTS 2>&1 || _fail "failed: $@"
else
$INSTRUMENT "$@" >> $RESULTS 2>&1 || _fail "failed: $@"
fi
}
# same as run_check but the stderr+stdout output is duplicated on stdout and
# can be processed further
run_check_stdout()
{
echo "############### $@" >> $RESULTS 2>&1
if [ "$TEST_LOG" = 'tty' ]; then echo "CMD(stdout): $@" > /dev/tty; fi
if [ "$1" = 'root_helper' ]; then
"$@" 2>&1 | tee -a $RESULTS || _fail "failed: $@"
else
$INSTRUMENT "$@" 2>&1 | tee -a $RESULTS || _fail "failed: $@"
fi
}
# same as run_check but does not fail the test, output is logged
run_mayfail()
{
local ret
echo "############### $@" >> $RESULTS 2>&1
if [ "$TEST_LOG" = 'tty' ]; then echo "CMD(mayfail): $@" > /dev/tty; fi
if [ "$1" = 'root_helper' ]; then
"$@" >> $RESULTS 2>&1
else
$INSTRUMENT "$@" >> $RESULTS 2>&1
fi
ret=$?
if [ $ret != 0 ]; then
echo "failed (ignored, ret=$ret): $@" >> $RESULTS
if [ $ret == 139 ]; then
_fail "mayfail: returned code 139 (SEGFAULT), not ignored"
fi
return $ret
fi
}
# first argument is error message to print if it fails, otherwise
# same as run_check but expects the command to fail, output is logged
run_mustfail()
{
local msg
msg="$1"
shift
echo "############### $@" >> $RESULTS 2>&1
if [ "$TEST_LOG" = 'tty' ]; then echo "CMD(mustfail): $@" > /dev/tty; fi
if [ "$1" = 'root_helper' ]; then
"$@" >> $RESULTS 2>&1
else
$INSTRUMENT "$@" >> $RESULTS 2>&1
fi
if [ $? != 0 ]; then
echo "failed (expected): $@" >> $RESULTS
return 0
else
echo "succeeded (unexpected!): $@" >> $RESULTS
_fail "unexpected success: $msg"
return 1
fi
}
check_prereq()
{
if ! [ -f $TOP/$1 ]; then
_fail "Failed prerequisites: $1";
fi
}
check_global_prereq()
{
which $1 &> /dev/null
if [ $? -ne 0 ]; then
_fail "Failed system wide prerequisities: $1";
fi
}
check_image()
{
local image
image=$1
echo "testing image $(basename $image)" >> $RESULTS
$TOP/btrfs check $image >> $RESULTS 2>&1
[ $? -eq 0 ] && _fail "btrfs check should have detected corruption"
run_check $TOP/btrfs check --repair $image
run_check $TOP/btrfs check $image
}
# Extract a usable image from packed formats
# - raw btrfs filesystem images, suffix .raw
# - dtto compressed by XZ, suffix .raw.xz
# - meta-dump images with suffix .img
# - dtto compressed by XZ, suffix .img.xz
extract_image()
{
local image
local cleanme
image="$1"
case "$image" in
*.img)
rm -f $image.restored
: ;;
*.img.xz)
xz --decompress --keep "$image" || \
_fail "failed to decompress image $image" >&2
image=${image%%.xz}
rm -f $image.restored
cleanme=$image
;;
*.raw)
cp --sparse=auto $image $image.restored
;;
*.raw.xz)
xz --decompress --keep "$image" || \
_fail "failed to decompress image $image" >&2
image=${image%%.xz}
mv "$image" "$image".restored
;;
esac
if ! [ -f $image.restored ]; then
echo "restoring image $(basename $image)" >> $RESULTS
$TOP/btrfs-image -r $image $image.restored \
&>> $RESULTS \
|| _fail "failed to restore image $image" >&2
fi
[ -f "$cleanme" ] && rm -f "$cleanme"
echo "$image.restored"
}
# Process all image dumps in a given directory
check_all_images()
{
local dir
local extracted
dir="$1"
for image in $(find $dir \( -iname '*.img' -o \
-iname '*.img.xz' -o \
-iname '*.raw' -o \
-iname '*.raw.xz' \) | sort)
do
extracted=$(extract_image "$image")
check_image "$extracted"
rm -f "$extracted"
done
}
# some tests need to mount the recovered image and do verifications call
# 'setup_root_helper' and then check for have_root_helper == 1 if the test
# needs to fail otherwise; using sudo by default for now
SUDO_HELPER=
NEED_SUDO_VALIDATE=unknown
export SUDO_HELPER
export NEED_SUDO_VALIDATE
root_helper()
{
if [ $UID -eq 0 ]; then
"$@"
else
if [ "$NEED_SUDO_VALIDATE" = 'yes' ]; then
sudo -v -n &>/dev/null || \
_not_run "Need to validate sudo credentials"
sudo -n "$@"
elif [ "$NEED_SUDO_VALIDATE" = 'no' ]; then
sudo -n /bin/true &> /dev/null || \
_not_run "Need to validate sudo user settings"
sudo -n "$@"
else
# should not happen
_not_run "Need to validate root privileges"
fi
fi
}
setup_root_helper()
{
if [ $UID -eq 0 -o -n "$SUDO_HELPER" ]; then
return
fi
# Test for old sudo or special settings, which make sudo -v fail even
# if user setting is NOPASSWD
sudo -n /bin/true &>/dev/null && NEED_SUDO_VALIDATE=no
# Newer sudo or default sudo setting
sudo -v -n &>/dev/null && NEED_SUDO_VALIDATE=yes
if [ "$NEED_SUDO_VALIDATE" = 'unknown' ]; then
_not_run "Need to validate root privileges"
fi
SUDO_HELPER=root_helper
}
prepare_test_dev()
{
# num[K/M/G/T...]
local size="$1"
[[ "$TEST_DEV" ]] && return
[[ "$size" ]] || size='2G'
echo "\$TEST_DEV not given, use $TOP/test/test.img as fallback" >> \
$RESULTS
TEST_DEV="$TOP/tests/test.img"
truncate -s "$size" "$TEST_DEV" || _not_run "create file for loop device failed"
}
run_check_mount_test_dev()
{
setup_root_helper
local loop_opt
if [[ -b "$TEST_DEV" ]]; then
loop_opt=""
elif [[ -f "$TEST_DEV" ]]; then
loop_opt="-o loop"
else
_fail "Invalid \$TEST_DEV: $TEST_DEV"
fi
[[ -d "$TEST_MNT" ]] || {
_fail "Invalid \$TEST_MNT: $TEST_MNT"
}
run_check $SUDO_HELPER mount $loop_opt "$@" "$TEST_DEV" "$TEST_MNT"
}
run_check_umount_test_dev()
{
setup_root_helper
run_check $SUDO_HELPER umount "$@" "$TEST_DEV"
}
init_env()
{
TEST_MNT="${TEST_MNT:-$TOP/tests/mnt}"
export TEST_MNT
mkdir -p "$TEST_MNT" || { echo "Failed mkdir -p $TEST_MNT"; exit 1; }
}
init_env