Disable the write verifiers when we're trashing a block. With this in place, create a xfs fuzzer script that formats, populates, corrupts, tries to use, repairs, and tries again to use a crash test xfs image. Hopefully this will shake out some v5 filesystem bugs. Signed-off-by: Darrick J. Wong <darrick.wong@xxxxxxxxxx> --- db/check.c | 7 + db/xfsfuzz.sh | 305 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 312 insertions(+) create mode 100755 db/xfsfuzz.sh diff --git a/db/check.c b/db/check.c index 8f8096d..2c2a02e 100644 --- a/db/check.c +++ b/db/check.c @@ -953,6 +953,7 @@ blocktrash_b( int mask; int newbit; int offset; + const struct xfs_buf_ops *stashed_ops; static char *modestr[] = { N_("zeroed"), N_("set"), N_("flipped"), N_("randomized") }; @@ -963,6 +964,8 @@ blocktrash_b( push_cur(); set_cur(&typtab[DBM_UNKNOWN], XFS_AGB_TO_DADDR(mp, agno, agbno), blkbb, DB_RING_IGN, NULL); + stashed_ops = iocur_top->bp->b_ops; + iocur_top->bp->b_ops = NULL; if ((buf = iocur_top->data) == NULL) { dbprintf(_("can't read block %u/%u for trashing\n"), agno, agbno); pop_cur(); @@ -993,6 +996,7 @@ blocktrash_b( buf[byte] &= ~mask; } write_cur(); + iocur_top->bp->b_ops = stashed_ops; pop_cur(); printf(_("blocktrash: %u/%u %s block %d bit%s starting %d:%d %s\n"), agno, agbno, typename[type], len, len == 1 ? "" : "s", @@ -1049,9 +1053,12 @@ blocktrash_f( (1 << DBM_BTINO) | (1 << DBM_DIR) | (1 << DBM_INODE) | + (1 << DBM_LOG) | (1 << DBM_QUOTA) | (1 << DBM_RTBITMAP) | (1 << DBM_RTSUM) | + (1 << DBM_SYMLINK) | + (1 << DBM_BTFINO) | (1 << DBM_SB); while ((c = getopt(argc, argv, "0123n:s:t:x:y:")) != EOF) { switch (c) { diff --git a/db/xfsfuzz.sh b/db/xfsfuzz.sh new file mode 100755 index 0000000..fc40e97 --- /dev/null +++ b/db/xfsfuzz.sh @@ -0,0 +1,305 @@ +#!/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" +xEATURES="-m crc=0" +BLK_SZ=4096 +INODE_SZ=512 +RUN_FSCK=1 +OVERRIDE_PATH=1 +MAX_FSCK=10 +SRCDIR=/etc +FUZZ_ARGS="-3 -n 32" +XFS_REPAIR_OPTS="-P" + +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 ${XFS_REPAIR_OPTS} -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 "++ copy image" + cp "${BASE_IMG}" "${PASS_IMG}" + if [ $? -ne 0 ]; then + exit $? + fi + xfs_db -x -c "label xfsfuzz-${pass}" "${PASS_IMG}" + + echo "++ corrupt image" + 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 ${XFS_REPAIR_OPTS} -v "${FSCK_IMG}" >> "${FSCK_LOG}" 2>&1 + res=$? + if [ "${res}" -eq 0 ]; then + echo "reverify" >> "${FSCK_LOG}" + xfs_repair ${XFS_REPAIR_OPTS} -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 ${XFS_REPAIR_OPTS} -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 ${XFS_REPAIR_OPTS} -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 ${XFS_REPAIR_OPTS} -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 _______________________________________________ xfs mailing list xfs@xxxxxxxxxxx http://oss.sgi.com/mailman/listinfo/xfs