| #!/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="has_journal,extent,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize,64bit,metadata_csum,bigalloc,sparse_super2,inline_data" | |
| BLK_SZ=4096 | |
| INODE_SZ=256 | |
| EXTENDED_OPTS="discard" | |
| EXTENDED_FSCK_OPTIONS="" | |
| RUN_FSCK=1 | |
| OVERRIDE_PATH=1 | |
| HAS_FUSE2FS=0 | |
| USE_FUSE2FS=0 | |
| MAX_FSCK=10 | |
| SRCDIR=/etc | |
| test -x "${SCRIPT_DIR}/fuse2fs" && HAS_FUSE2FS=1 | |
| 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 "-E: Extended mke2fs options." | |
| echo "-f: Do not run e2fsck after each pass." | |
| echo "-F: Extended e2fsck options." | |
| echo "-I: Create inodes of this size. (${INODE_SZ})" | |
| echo "-n: Run this many passes. (${PASSES})" | |
| echo "-O: Create FS with these features." | |
| echo "-p: Use system's mke2fs/e2fsck/tune2fs tools." | |
| echo "-s: Create FS images of this size. (${SZ})" | |
| echo "-S: Copy files from this dir. (${SRCDIR})" | |
| echo "-x: Run e2fsck at most this many times. (${MAX_FSCK})" | |
| test "${HAS_FUSE2FS}" -gt 0 && echo "-u: Use fuse2fs instead of the kernel." | |
| exit 0 | |
| } | |
| GETOPT="d:n:s:O:I:b:B:E:F:fpx:S:" | |
| test "${HAS_FUSE2FS}" && GETOPT="${GETOPT}u" | |
| while getopts "${GETOPT}" opt; do | |
| case "${opt}" in | |
| "B") | |
| E2FUZZ_ARGS="${E2FUZZ_ARGS} -b ${OPTARG}" | |
| ;; | |
| "d") | |
| DIR="${OPTARG}" | |
| ;; | |
| "n") | |
| PASSES="${OPTARG}" | |
| ;; | |
| "s") | |
| SZ="${OPTARG}" | |
| ;; | |
| "O") | |
| FEATURES="${FEATURES},${OPTARG}" | |
| ;; | |
| "I") | |
| INODE_SZ="${OPTARG}" | |
| ;; | |
| "b") | |
| BLK_SZ="${OPTARG}" | |
| ;; | |
| "E") | |
| EXTENDED_OPTS="${OPTARG}" | |
| ;; | |
| "F") | |
| EXTENDED_FSCK_OPTS="-E ${OPTARG}" | |
| ;; | |
| "f") | |
| RUN_FSCK=0 | |
| ;; | |
| "p") | |
| OVERRIDE_PATH=0 | |
| ;; | |
| "u") | |
| USE_FUSE2FS=1 | |
| ;; | |
| "x") | |
| MAX_FSCK="${OPTARG}" | |
| ;; | |
| "S") | |
| SRCDIR="${OPTARG}" | |
| ;; | |
| *) | |
| print_help | |
| ;; | |
| esac | |
| done | |
| if [ "${OVERRIDE_PATH}" -gt 0 ]; then | |
| PATH="${SCRIPT_DIR}:${SCRIPT_DIR}/../e2fsck/:${PATH}" | |
| export PATH | |
| fi | |
| TESTDIR="${DIR}/tests/" | |
| TESTMNT="${DIR}/mnt/" | |
| BASE_IMG="${DIR}/e2fuzz.img" | |
| cat > /tmp/mke2fs.conf << ENDL | |
| [defaults] | |
| base_features = ${FEATURES} | |
| default_mntopts = acl,user_xattr,block_validity | |
| enable_periodic_fsck = 0 | |
| blocksize = ${BLK_SZ} | |
| inode_size = ${INODE_SZ} | |
| inode_ratio = 4096 | |
| cluster_size = $((BLK_SZ * 2)) | |
| options = ${EXTENDED_OPTS} | |
| ENDL | |
| MKE2FS_CONFIG=/tmp/mke2fs.conf | |
| export MKE2FS_CONFIG | |
| # 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}" | |
| mke2fs -F -v "${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 * 4 / 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}" | |
| e2fsck -fn "${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}/e2fuzz-${pass}.img" | |
| FSCK_IMG="${TESTDIR}/e2fuzz-${pass}.fsck" | |
| FUZZ_LOG="${TESTDIR}/e2fuzz-${pass}.fuzz.log" | |
| OPS_LOG="${TESTDIR}/e2fuzz-${pass}.ops.log" | |
| echo "++ corrupt image" | |
| cp "${BASE_IMG}" "${PASS_IMG}" | |
| if [ $? -ne 0 ]; then | |
| exit $? | |
| fi | |
| tune2fs -L "e2fuzz-${pass}" "${PASS_IMG}" | |
| e2fuzz -v "${PASS_IMG}" ${E2FUZZ_ARGS} > "${FUZZ_LOG}" | |
| if [ $? -ne 0 ]; then | |
| exit $? | |
| fi | |
| echo "++ mount image" | |
| if [ "${USE_FUSE2FS}" -gt 0 ]; then | |
| "${SCRIPT_DIR}/fuse2fs" "${PASS_IMG}" "${TESTMNT}" | |
| res=$? | |
| else | |
| mount "${PASS_IMG}" "${TESTMNT}" -o loop | |
| res=$? | |
| fi | |
| 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 | |
| test "${USE_FUSE2FS}" -gt 0 && sleep 2 | |
| 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 e2fsck) -fy ${FSCK_IMG} ${EXTENDED_FSCK_OPTS}" | |
| FSCK_LOG="${TESTDIR}/e2fuzz-${pass}-${fsck_pass}.log" | |
| e2fsck -fy "${FSCK_IMG}" ${EXTENDED_FSCK_OPTS} > "${FSCK_LOG}" 2>&1 | |
| res=$? | |
| echo "++ fsck returns ${res}" | |
| if [ "${res}" -eq 0 ]; then | |
| exit 0 | |
| elif [ "${fsck_pass}" -eq "${MAX_FSCK}" ]; then | |
| echo "++ fsck did not fix in ${MAX_FSCK} passes." | |
| exit 1 | |
| fi | |
| if [ "${res}" -gt 0 -a \ | |
| "$(grep 'Memory allocation failed' "${FSCK_LOG}" | wc -l)" -gt 0 ]; then | |
| echo "++ Ran out of memory, get more RAM" | |
| exit 0 | |
| fi | |
| if [ "${res}" -gt 0 -a \ | |
| "$(grep 'Could not allocate block' "${FSCK_LOG}" | wc -l)" -gt 0 -a \ | |
| "$(dumpe2fs -h "${FSCK_IMG}" | grep '^Free blocks:' | awk '{print $3}')0" -eq 0 ]; then | |
| echo "++ Ran out of space, get a bigger image" | |
| exit 0 | |
| fi | |
| if [ "${fsck_pass}" -gt 1 ]; then | |
| diff -u "${TESTDIR}/e2fuzz-${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}/e2fuzz-${pass}-round2.log" | |
| e2fsck -fn "${FSCK_IMG}" ${EXTENDED_FSCK_OPTS} >> "${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 | |
| test "${USE_FUSE2FS}" -gt 0 && sleep 2 | |
| echo "+++ check fs (2)" | |
| e2fsck -fn "${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}"/e2fuzz*.log | |
| done | |
| exit $ret |