[PATCH 5/5] xfs_db: enable blocktrash for checksummed filesystems
Darrick J. Wong
darrick.wong at oracle.com
Thu May 28 00:08:15 CDT 2015
On Tue, May 26, 2015 at 03:51:59PM -0700, Darrick J. Wong wrote:
> 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 at oracle.com>
> ---
> 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],
So... seeing as the the TYP_* and DBM_* values don't correspond and every other
user of typtab uses the TYP_ values for array index, what's the point of
unconditionally using the AGF verifier on this block? Passing NULL as the
first argument doesn't seem to hurt anything.
(DBM_UNKNOWN == TYP_AGF)
Seeing as we're going to trash the block anyway, why bother?
> 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) |
It's useful to be able to fuzz the log, but that'll bias the block selection
towards the log. Maybe it should be masked off by default in tmask = goodmask
below?
> (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
[working on stuffing this into xfstests]
[maybe I'll see about doing likewise for the ext4 fuzzer]
--D
> @@ -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 at oss.sgi.com
> http://oss.sgi.com/mailman/listinfo/xfs
More information about the xfs
mailing list