Created
March 6, 2015 19:19
-
-
Save djwong/e61c6ec38dc029063db4 to your computer and use it in GitHub Desktop.
fuzzy XFS kernel/repair fuzzer script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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