Skip to content

Instantly share code, notes, and snippets.

@djwong
Created March 6, 2015 19:19
Show Gist options
  • Save djwong/e61c6ec38dc029063db4 to your computer and use it in GitHub Desktop.
Save djwong/e61c6ec38dc029063db4 to your computer and use it in GitHub Desktop.
fuzzy XFS kernel/repair fuzzer script
#!/bin/bash
# Test harness to fuzz a filesystem over and over...
# Copyright (C) 2014 Oracle.
DIR=/tmp
PASSES=10000
SZ=32m
SCRIPT_DIR="$(dirname "$0")"
FEATURES="-m crc=1,finobt=1"
BLK_SZ=4096
INODE_SZ=512
RUN_FSCK=1
OVERRIDE_PATH=1
MAX_FSCK=10
SRCDIR=/etc
FUZZ_ARGS="-3 -n 32"
print_help() {
echo "Usage: $0 OPTIONS"
echo "-b: FS block size is this. (${BLK_SZ})"
echo "-B: Corrupt this many bytes per run."
echo "-d: Create test files in this directory. (${DIR})"
echo "-f: Do not run xfs_repair after each pass."
echo "-I: Create inodes of this size. (${INODE_SZ})"
echo "-n: Run this many passes. (${PASSES})"
echo "-O: Pass this to mkfs.xfs."
echo "-p: Use system's xfsprogs tools."
echo "-s: Create FS images of this size. (${SZ})"
echo "-S: Copy files from this dir. (${SRCDIR})"
echo "-x: Run xfs_repair at most this many times. (${MAX_FSCK})"
exit 0
}
GETOPT="d:n:s:O:I:b:B:fpx:S:"
while getopts "${GETOPT}" opt; do
case "${opt}" in
"B")
FUZZ_ARGS="-3 -n ${OPTARG}"
;;
"d")
DIR="${OPTARG}"
;;
"n")
PASSES="${OPTARG}"
;;
"s")
SZ="${OPTARG}"
;;
"O")
FEATURES="${OPTARG}"
;;
"I")
INODE_SZ="${OPTARG}"
;;
"b")
BLK_SZ="${OPTARG}"
;;
"f")
RUN_FSCK=0
;;
"p")
OVERRIDE_PATH=0
;;
"x")
MAX_FSCK="${OPTARG}"
;;
"S")
SRCDIR="${OPTARG}"
;;
*)
print_help
;;
esac
done
if [ "${OVERRIDE_PATH}" -gt 0 ]; then
PATH="${SCRIPT_DIR}:${SCRIPT_DIR}/../repair/:${SCRIPT_DIR}/../db/:${SCRIPT_DIR}/../mkfs/:${PATH}"
export PATH
fi
TESTDIR="${DIR}/tests/"
TESTMNT="${DIR}/mnt/"
BASE_IMG="${DIR}/xfsfuzz.img"
# Set up FS image
echo "+ create fs image"
umount "${TESTDIR}"
umount "${TESTMNT}"
rm -rf "${TESTDIR}"
rm -rf "${TESTMNT}"
mkdir -p "${TESTDIR}"
mkdir -p "${TESTMNT}"
rm -rf "${BASE_IMG}"
truncate -s "${SZ}" "${BASE_IMG}"
mkfs.xfs -f ${FEATURES} -b "size=${BLK_SZ}" -i "size=${INODE_SZ}" "${BASE_IMG}"
if [ $? -ne 0 ]; then
exit $?
fi
# Populate FS image
echo "+ populate fs image"
modprobe loop
mount "${BASE_IMG}" "${TESTMNT}" -o loop
if [ $? -ne 0 ]; then
exit $?
fi
SRC_SZ="$(du -ks "${SRCDIR}" | awk '{print $1}')"
FS_SZ="$(( $(stat -f "${TESTMNT}" -c '%a * %S') / 1024 ))"
NR="$(( (FS_SZ * 6 / 10) / SRC_SZ ))"
if [ "${NR}" -lt 1 ]; then
NR=1
fi
echo "+ make ${NR} copies"
seq 1 "${NR}" | while read nr; do
cp -pRdu "${SRCDIR}" "${TESTMNT}/test.${nr}" 2> /dev/null
done
umount "${TESTMNT}"
xfs_repair -vn "${BASE_IMG}"
if [ $? -ne 0 ]; then
echo "fsck failed??"
exit 1
fi
# Run tests
echo "+ run test"
ret=0
seq 1 "${PASSES}" | while read pass; do
echo "+ pass ${pass}"
PASS_IMG="${TESTDIR}/xfsfuzz-${pass}.img"
FSCK_IMG="${TESTDIR}/xfsfuzz-${pass}.fsck"
FUZZ_LOG="${TESTDIR}/xfsfuzz-${pass}.fuzz.log"
OPS_LOG="${TESTDIR}/xfsfuzz-${pass}.ops.log"
echo "++ corrupt image"
cp "${BASE_IMG}" "${PASS_IMG}"
if [ $? -ne 0 ]; then
exit $?
fi
xfs_db -x -c "label xfsfuzz-${pass}" "${PASS_IMG}"
xfs_db -x -c blockget -c "blocktrash ${FUZZ_ARGS}" "${PASS_IMG}" > "${FUZZ_LOG}"
# res=$?
# if [ "${res}" -ne 0 ]; then
# echo "blocktrash returns ${res}"
# exit "${res}"
# fi
echo "++ mount image"
mount "${PASS_IMG}" "${TESTMNT}" -o loop
res=$?
if [ "${res}" -eq 0 ]; then
echo "+++ ls -laR"
ls -laR "${TESTMNT}/test.1/" > /dev/null 2> "${OPS_LOG}"
echo "+++ cat files"
find "${TESTMNT}/test.1/" -type f -size -1048576k -print0 | xargs -0 cat > /dev/null 2>> "${OPS_LOG}"
echo "+++ expand"
find "${TESTMNT}/" -type f 2> /dev/null | head -n 50000 | while read f; do
attr -l "$f" > /dev/null 2>> "${OPS_LOG}"
if [ -f "$f" -a -w "$f" ]; then
dd if=/dev/zero bs="${BLK_SZ}" count=1 >> "$f" 2>> "${OPS_LOG}"
fi
mv "$f" "$f.longer" > /dev/null 2>> "${OPS_LOG}"
done
sync
echo "+++ create files"
cp -pRdu "${SRCDIR}" "${TESTMNT}/test.moo" 2>> "${OPS_LOG}"
sync
echo "+++ remove files"
rm -rf "${TESTMNT}/test.moo" 2>> "${OPS_LOG}"
umount "${TESTMNT}"
res=$?
if [ "${res}" -ne 0 ]; then
ret=1
break
fi
sync
fi
if [ "${RUN_FSCK}" -gt 0 ]; then
cp "${PASS_IMG}" "${FSCK_IMG}"
pass_img_sz="$(stat -c '%s' "${PASS_IMG}")"
seq 1 "${MAX_FSCK}" | while read fsck_pass; do
echo "++ fsck pass ${fsck_pass}: $(which xfs_repair) -v ${FSCK_IMG}"
FSCK_LOG="${TESTDIR}/xfsfuzz-${pass}-${fsck_pass}.log"
echo "repairing" > "${FSCK_LOG}"
xfs_repair -v "${FSCK_IMG}" >> "${FSCK_LOG}" 2>&1
res=$?
if [ "${res}" -eq 0 ]; then
echo "reverify" >> "${FSCK_LOG}"
xfs_repair -v -n "${FSCK_IMG}" >> "${FSCK_LOG}" 2>&1
res=$?
fi
echo "++ fsck returns ${res}"
if [ "${res}" -eq 0 ]; then
exit 0
elif [ "${res}" -eq 2 ]; then
# replay log?
echo "replaying log" >> "${FSCK_LOG}"
dmesg > /tmp/a
mount "${FSCK_IMG}" "${TESTMNT}" -o loop
res=$?
if [ "${res}" -gt 0 ]; then
echo "+++ zeroing log"
echo "zeroing log" >> "${FSCK_LOG}"
xfs_repair -L -v "${FSCK_IMG}" >> "${FSCK_LOG}" 2>&1
else
umount "${TESTMNT}"
fi
dmesg > /tmp/b
diff -u /tmp/a /tmp/b >> "${FSCK_LOG}"
elif [ "${fsck_pass}" -eq "${MAX_FSCK}" ]; then
echo "++ fsck did not fix in ${MAX_FSCK} passes."
exit 1
fi
if [ "${fsck_pass}" -gt 1 ]; then
diff -u "${TESTDIR}/xfsfuzz-${pass}-$((fsck_pass - 1)).log" "${FSCK_LOG}"
if [ $? -eq 0 ]; then
echo "++ fsck makes no progress"
exit 2
fi
fi
fsck_img_sz="$(stat -c '%s' "${FSCK_IMG}")"
if [ "${fsck_img_sz}" -ne "${pass_img_sz}" ]; then
echo "++ fsck image size changed"
exit 3
fi
done
fsck_loop_ret=$?
if [ "${fsck_loop_ret}" -gt 0 ]; then
break;
fi
fi
echo "+++ check fs for round 2"
FSCK_LOG="${TESTDIR}/xfsfuzz-${pass}-round2.log"
xfs_repair -v -n "${FSCK_IMG}" > "${FSCK_LOG}" 2>&1
res=$?
if [ "${res}" -ne 0 ]; then
echo "++++ fsck failed."
exit 1
fi
echo "++ mount image (2)"
mount "${FSCK_IMG}" "${TESTMNT}" -o loop
res=$?
if [ "${res}" -eq 0 ]; then
echo "+++ ls -laR (2)"
ls -laR "${TESTMNT}/test.1/" > /dev/null 2> "${OPS_LOG}"
echo "+++ cat files (2)"
find "${TESTMNT}/test.1/" -type f -size -1048576k -print0 | xargs -0 cat > /dev/null 2>> "${OPS_LOG}"
echo "+++ expand (2)"
find "${TESTMNT}/" -type f 2> /dev/null | head -n 50000 | while read f; do
attr -l "$f" > /dev/null 2>> "${OPS_LOG}"
if [ -f "$f" -a -w "$f" ]; then
dd if=/dev/zero bs="${BLK_SZ}" count=1 >> "$f" 2>> "${OPS_LOG}"
fi
mv "$f" "$f.longer" > /dev/null 2>> "${OPS_LOG}"
done
sync
echo "+++ create files (2)"
cp -pRdu "${SRCDIR}" "${TESTMNT}/test.moo" 2>> "${OPS_LOG}"
sync
echo "+++ remove files (2)"
rm -rf "${TESTMNT}/test.moo" 2>> "${OPS_LOG}"
umount "${TESTMNT}"
res=$?
if [ "${res}" -ne 0 ]; then
ret=1
break
fi
sync
echo "+++ check fs (2)"
xfs_repair -v -n "${FSCK_IMG}" >> "${FSCK_LOG}" 2>&1
res=$?
if [ "${res}" -ne 0 ]; then
echo "++++ fsck failed."
exit 1
fi
else
echo "++ mount(2) failed with ${res}"
exit 1
fi
rm -rf "${FSCK_IMG}" "${PASS_IMG}" "${FUZZ_LOG}" "${TESTDIR}"/xfsfuzz*.log
done
exit $ret
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment