Home | History | Annotate | Download | only in misc
      1 #!/bin/bash
      2 
      3 # Test harness to fuzz a filesystem over and over...
      4 # Copyright (C) 2014 Oracle.
      5 
      6 DIR=/tmp
      7 PASSES=10000
      8 SZ=32m
      9 SCRIPT_DIR="$(dirname "$0")"
     10 FEATURES="has_journal,extent,huge_file,flex_bg,uninit_bg,dir_nlink,extra_isize,64bit,metadata_csum,bigalloc,sparse_super2,inline_data"
     11 BLK_SZ=4096
     12 INODE_SZ=256
     13 EXTENDED_OPTS="discard"
     14 EXTENDED_FSCK_OPTIONS=""
     15 RUN_FSCK=1
     16 OVERRIDE_PATH=1
     17 HAS_FUSE2FS=0
     18 USE_FUSE2FS=0
     19 MAX_FSCK=10
     20 SRCDIR=/etc
     21 test -x "${SCRIPT_DIR}/fuse2fs" && HAS_FUSE2FS=1
     22 
     23 print_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 
     42 GETOPT="d:n:s:O:I:b:B:E:F:fpx:S:"
     43 test "${HAS_FUSE2FS}" && GETOPT="${GETOPT}u"
     44 
     45 while 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
     93 done
     94 
     95 if [ "${OVERRIDE_PATH}" -gt 0 ]; then
     96 	PATH="${SCRIPT_DIR}:${SCRIPT_DIR}/../e2fsck/:${PATH}"
     97 	export PATH
     98 fi
     99 
    100 TESTDIR="${DIR}/tests/"
    101 TESTMNT="${DIR}/mnt/"
    102 BASE_IMG="${DIR}/e2fuzz.img"
    103 
    104 cat > /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}
    114 ENDL
    115 MKE2FS_CONFIG=/tmp/mke2fs.conf
    116 export MKE2FS_CONFIG
    117 
    118 # Set up FS image
    119 echo "+ create fs image"
    120 umount "${TESTDIR}"
    121 umount "${TESTMNT}"
    122 rm -rf "${TESTDIR}"
    123 rm -rf "${TESTMNT}"
    124 mkdir -p "${TESTDIR}"
    125 mkdir -p "${TESTMNT}"
    126 rm -rf "${BASE_IMG}"
    127 truncate -s "${SZ}" "${BASE_IMG}"
    128 mke2fs -F -v "${BASE_IMG}"
    129 if [ $? -ne 0 ]; then
    130 	exit $?
    131 fi
    132 
    133 # Populate FS image
    134 echo "+ populate fs image"
    135 modprobe loop
    136 mount "${BASE_IMG}" "${TESTMNT}" -o loop
    137 if [ $? -ne 0 ]; then
    138 	exit $?
    139 fi
    140 SRC_SZ="$(du -ks "${SRCDIR}" | awk '{print $1}')"
    141 FS_SZ="$(( $(stat -f "${TESTMNT}" -c '%a * %S') / 1024 ))"
    142 NR="$(( (FS_SZ * 4 / 10) / SRC_SZ ))"
    143 if [ "${NR}" -lt 1 ]; then
    144 	NR=1
    145 fi
    146 echo "+ make ${NR} copies"
    147 seq 1 "${NR}" | while read nr; do
    148 	cp -pRdu "${SRCDIR}" "${TESTMNT}/test.${nr}" 2> /dev/null
    149 done
    150 umount "${TESTMNT}"
    151 e2fsck -fn "${BASE_IMG}"
    152 if [ $? -ne 0 ]; then
    153 	echo "fsck failed??"
    154 	exit 1
    155 fi
    156 
    157 # Run tests
    158 echo "+ run test"
    159 ret=0
    160 seq 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
    325 done
    326 
    327 exit $ret
    328