| #!/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 |