1#!/bin/bash 2 3# Test harness to fuzz a filesystem over and over... 4# Copyright (C) 2014 Oracle. 5 6DIR=/tmp 7PASSES=10000 8SZ=32m 9SCRIPT_DIR="$(dirname "$0")" 10FEATURES="has_journal,extent,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize,64bit,metadata_csum,bigalloc,sparse_super2,inline_data" 11BLK_SZ=4096 12INODE_SZ=256 13EXTENDED_OPTS="discard" 14EXTENDED_FSCK_OPTIONS="" 15RUN_FSCK=1 16OVERRIDE_PATH=1 17HAS_FUSE2FS=0 18USE_FUSE2FS=0 19MAX_FSCK=10 20SRCDIR=/etc 21test -x "${SCRIPT_DIR}/fuse2fs" && HAS_FUSE2FS=1 22 23print_help() { 24 echo "Usage: $0 OPTIONS" 25 echo "-b: FS block size is this. (${BLK_SZ})" 26 echo "-B: Corrupt this many bytes per run." 27 echo "-d: Create test files in this directory. (${DIR})" 28 echo "-E: Extended mke2fs options." 29 echo "-f: Do not run e2fsck after each pass." 30 echo "-F: Extended e2fsck options." 31 echo "-I: Create inodes of this size. (${INODE_SZ})" 32 echo "-n: Run this many passes. (${PASSES})" 33 echo "-O: Create FS with these features." 34 echo "-p: Use system's mke2fs/e2fsck/tune2fs tools." 35 echo "-s: Create FS images of this size. (${SZ})" 36 echo "-S: Copy files from this dir. (${SRCDIR})" 37 echo "-x: Run e2fsck at most this many times. (${MAX_FSCK})" 38 test "${HAS_FUSE2FS}" -gt 0 && echo "-u: Use fuse2fs instead of the kernel." 39 exit 0 40} 41 42GETOPT="d:n:s:O:I:b:B:E:F:fpx:S:" 43test "${HAS_FUSE2FS}" && GETOPT="${GETOPT}u" 44 45while getopts "${GETOPT}" opt; do 46 case "${opt}" in 47 "B") 48 E2FUZZ_ARGS="${E2FUZZ_ARGS} -b ${OPTARG}" 49 ;; 50 "d") 51 DIR="${OPTARG}" 52 ;; 53 "n") 54 PASSES="${OPTARG}" 55 ;; 56 "s") 57 SZ="${OPTARG}" 58 ;; 59 "O") 60 FEATURES="${FEATURES},${OPTARG}" 61 ;; 62 "I") 63 INODE_SZ="${OPTARG}" 64 ;; 65 "b") 66 BLK_SZ="${OPTARG}" 67 ;; 68 "E") 69 EXTENDED_OPTS="${OPTARG}" 70 ;; 71 "F") 72 EXTENDED_FSCK_OPTS="-E ${OPTARG}" 73 ;; 74 "f") 75 RUN_FSCK=0 76 ;; 77 "p") 78 OVERRIDE_PATH=0 79 ;; 80 "u") 81 USE_FUSE2FS=1 82 ;; 83 "x") 84 MAX_FSCK="${OPTARG}" 85 ;; 86 "S") 87 SRCDIR="${OPTARG}" 88 ;; 89 *) 90 print_help 91 ;; 92 esac 93done 94 95if [ "${OVERRIDE_PATH}" -gt 0 ]; then 96 PATH="${SCRIPT_DIR}:${SCRIPT_DIR}/../e2fsck/:${PATH}" 97 export PATH 98fi 99 100TESTDIR="${DIR}/tests/" 101TESTMNT="${DIR}/mnt/" 102BASE_IMG="${DIR}/e2fuzz.img" 103 104cat > /tmp/mke2fs.conf << ENDL 105[defaults] 106 base_features = ${FEATURES} 107 default_mntopts = acl,user_xattr,block_validity 108 enable_periodic_fsck = 0 109 blocksize = ${BLK_SZ} 110 inode_size = ${INODE_SZ} 111 inode_ratio = 4096 112 cluster_size = $((BLK_SZ * 2)) 113 options = ${EXTENDED_OPTS} 114ENDL 115MKE2FS_CONFIG=/tmp/mke2fs.conf 116export MKE2FS_CONFIG 117 118# Set up FS image 119echo "+ create fs image" 120umount "${TESTDIR}" 121umount "${TESTMNT}" 122rm -rf "${TESTDIR}" 123rm -rf "${TESTMNT}" 124mkdir -p "${TESTDIR}" 125mkdir -p "${TESTMNT}" 126rm -rf "${BASE_IMG}" 127truncate -s "${SZ}" "${BASE_IMG}" 128mke2fs -F -v "${BASE_IMG}" 129if [ $? -ne 0 ]; then 130 exit $? 131fi 132 133# Populate FS image 134echo "+ populate fs image" 135modprobe loop 136mount "${BASE_IMG}" "${TESTMNT}" -o loop 137if [ $? -ne 0 ]; then 138 exit $? 139fi 140SRC_SZ="$(du -ks "${SRCDIR}" | awk '{print $1}')" 141FS_SZ="$(( $(stat -f "${TESTMNT}" -c '%a * %S') / 1024 ))" 142NR="$(( (FS_SZ * 4 / 10) / SRC_SZ ))" 143if [ "${NR}" -lt 1 ]; then 144 NR=1 145fi 146echo "+ make ${NR} copies" 147seq 1 "${NR}" | while read nr; do 148 cp -pRdu "${SRCDIR}" "${TESTMNT}/test.${nr}" 2> /dev/null 149done 150umount "${TESTMNT}" 151e2fsck -fn "${BASE_IMG}" 152if [ $? -ne 0 ]; then 153 echo "fsck failed??" 154 exit 1 155fi 156 157# Run tests 158echo "+ run test" 159ret=0 160seq 1 "${PASSES}" | while read pass; do 161 echo "+ pass ${pass}" 162 PASS_IMG="${TESTDIR}/e2fuzz-${pass}.img" 163 FSCK_IMG="${TESTDIR}/e2fuzz-${pass}.fsck" 164 FUZZ_LOG="${TESTDIR}/e2fuzz-${pass}.fuzz.log" 165 OPS_LOG="${TESTDIR}/e2fuzz-${pass}.ops.log" 166 167 echo "++ corrupt image" 168 cp "${BASE_IMG}" "${PASS_IMG}" 169 if [ $? -ne 0 ]; then 170 exit $? 171 fi 172 tune2fs -L "e2fuzz-${pass}" "${PASS_IMG}" 173 e2fuzz -v "${PASS_IMG}" ${E2FUZZ_ARGS} > "${FUZZ_LOG}" 174 if [ $? -ne 0 ]; then 175 exit $? 176 fi 177 178 echo "++ mount image" 179 if [ "${USE_FUSE2FS}" -gt 0 ]; then 180 "${SCRIPT_DIR}/fuse2fs" "${PASS_IMG}" "${TESTMNT}" 181 res=$? 182 else 183 mount "${PASS_IMG}" "${TESTMNT}" -o loop 184 res=$? 185 fi 186 187 if [ "${res}" -eq 0 ]; then 188 echo "+++ ls -laR" 189 ls -laR "${TESTMNT}/test.1/" > /dev/null 2> "${OPS_LOG}" 190 191 echo "+++ cat files" 192 find "${TESTMNT}/test.1/" -type f -size -1048576k -print0 | xargs -0 cat > /dev/null 2>> "${OPS_LOG}" 193 194 echo "+++ expand" 195 find "${TESTMNT}/" -type f 2> /dev/null | head -n 50000 | while read f; do 196 attr -l "$f" > /dev/null 2>> "${OPS_LOG}" 197 if [ -f "$f" -a -w "$f" ]; then 198 dd if=/dev/zero bs="${BLK_SZ}" count=1 >> "$f" 2>> "${OPS_LOG}" 199 fi 200 mv "$f" "$f.longer" > /dev/null 2>> "${OPS_LOG}" 201 done 202 sync 203 204 echo "+++ create files" 205 cp -pRdu "${SRCDIR}" "${TESTMNT}/test.moo" 2>> "${OPS_LOG}" 206 sync 207 208 echo "+++ remove files" 209 rm -rf "${TESTMNT}/test.moo" 2>> "${OPS_LOG}" 210 211 umount "${TESTMNT}" 212 res=$? 213 if [ "${res}" -ne 0 ]; then 214 ret=1 215 break 216 fi 217 sync 218 test "${USE_FUSE2FS}" -gt 0 && sleep 2 219 fi 220 if [ "${RUN_FSCK}" -gt 0 ]; then 221 cp "${PASS_IMG}" "${FSCK_IMG}" 222 pass_img_sz="$(stat -c '%s' "${PASS_IMG}")" 223 224 seq 1 "${MAX_FSCK}" | while read fsck_pass; do 225 echo "++ fsck pass ${fsck_pass}: $(which e2fsck) -fy ${FSCK_IMG} ${EXTENDED_FSCK_OPTS}" 226 FSCK_LOG="${TESTDIR}/e2fuzz-${pass}-${fsck_pass}.log" 227 e2fsck -fy "${FSCK_IMG}" ${EXTENDED_FSCK_OPTS} > "${FSCK_LOG}" 2>&1 228 res=$? 229 echo "++ fsck returns ${res}" 230 if [ "${res}" -eq 0 ]; then 231 exit 0 232 elif [ "${fsck_pass}" -eq "${MAX_FSCK}" ]; then 233 echo "++ fsck did not fix in ${MAX_FSCK} passes." 234 exit 1 235 fi 236 if [ "${res}" -gt 0 -a \ 237 "$(grep 'Memory allocation failed' "${FSCK_LOG}" | wc -l)" -gt 0 ]; then 238 echo "++ Ran out of memory, get more RAM" 239 exit 0 240 fi 241 if [ "${res}" -gt 0 -a \ 242 "$(grep 'Could not allocate block' "${FSCK_LOG}" | wc -l)" -gt 0 -a \ 243 "$(dumpe2fs -h "${FSCK_IMG}" | grep '^Free blocks:' | awk '{print $3}')0" -eq 0 ]; then 244 echo "++ Ran out of space, get a bigger image" 245 exit 0 246 fi 247 if [ "${fsck_pass}" -gt 1 ]; then 248 diff -u "${TESTDIR}/e2fuzz-${pass}-$((fsck_pass - 1)).log" "${FSCK_LOG}" 249 if [ $? -eq 0 ]; then 250 echo "++ fsck makes no progress" 251 exit 2 252 fi 253 fi 254 255 fsck_img_sz="$(stat -c '%s' "${FSCK_IMG}")" 256 if [ "${fsck_img_sz}" -ne "${pass_img_sz}" ]; then 257 echo "++ fsck image size changed" 258 exit 3 259 fi 260 done 261 fsck_loop_ret=$? 262 if [ "${fsck_loop_ret}" -gt 0 ]; then 263 break; 264 fi 265 fi 266 267 echo "+++ check fs for round 2" 268 FSCK_LOG="${TESTDIR}/e2fuzz-${pass}-round2.log" 269 e2fsck -fn "${FSCK_IMG}" ${EXTENDED_FSCK_OPTS} >> "${FSCK_LOG}" 2>&1 270 res=$? 271 if [ "${res}" -ne 0 ]; then 272 echo "++++ fsck failed." 273 exit 1 274 fi 275 276 echo "++ mount image (2)" 277 mount "${FSCK_IMG}" "${TESTMNT}" -o loop 278 res=$? 279 280 if [ "${res}" -eq 0 ]; then 281 echo "+++ ls -laR (2)" 282 ls -laR "${TESTMNT}/test.1/" > /dev/null 2> "${OPS_LOG}" 283 284 echo "+++ cat files (2)" 285 find "${TESTMNT}/test.1/" -type f -size -1048576k -print0 | xargs -0 cat > /dev/null 2>> "${OPS_LOG}" 286 287 echo "+++ expand (2)" 288 find "${TESTMNT}/" -type f 2> /dev/null | head -n 50000 | while read f; do 289 attr -l "$f" > /dev/null 2>> "${OPS_LOG}" 290 if [ -f "$f" -a -w "$f" ]; then 291 dd if=/dev/zero bs="${BLK_SZ}" count=1 >> "$f" 2>> "${OPS_LOG}" 292 fi 293 mv "$f" "$f.longer" > /dev/null 2>> "${OPS_LOG}" 294 done 295 sync 296 297 echo "+++ create files (2)" 298 cp -pRdu "${SRCDIR}" "${TESTMNT}/test.moo" 2>> "${OPS_LOG}" 299 sync 300 301 echo "+++ remove files (2)" 302 rm -rf "${TESTMNT}/test.moo" 2>> "${OPS_LOG}" 303 304 umount "${TESTMNT}" 305 res=$? 306 if [ "${res}" -ne 0 ]; then 307 ret=1 308 break 309 fi 310 sync 311 test "${USE_FUSE2FS}" -gt 0 && sleep 2 312 313 echo "+++ check fs (2)" 314 e2fsck -fn "${FSCK_IMG}" >> "${FSCK_LOG}" 2>&1 315 res=$? 316 if [ "${res}" -ne 0 ]; then 317 echo "++ fsck failed." 318 exit 1 319 fi 320 else 321 echo "++ mount(2) failed with ${res}" 322 exit 1 323 fi 324 rm -rf "${FSCK_IMG}" "${PASS_IMG}" "${FUZZ_LOG}" "${TESTDIR}"/e2fuzz*.log 325done 326 327exit $ret 328