#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2015 Oracle, Inc.  All Rights Reserved.
#
# FS QA Test No. 083
#
# Create and populate an XFS filesystem, fuzz the metadata, then see how
# the kernel reacts, how xfs_repair fares in fixing the mess, and then
# try more kernel accesses to see if it really fixed things.
#
. ./common/preamble
_begin_fstest dangerous_fuzzers punch

# Override the default cleanup function.
_cleanup()
{
    cd /
    #rm -f $tmp.*
}

# Import common functions.
. ./common/filter
. ./common/attr
. ./common/populate
. ./common/fuzzy


_require_scratch
_require_attrs
_require_populate_commands

scratch_repair() {
	fsck_pass="$1"

	FSCK_LOG="${tmp}-fuzz-${fsck_pass}.log"
	echo "++ fsck pass ${fsck_pass}" > "${FSCK_LOG}"
	_repair_scratch_fs >> "${FSCK_LOG}" 2>&1
	res=$?
	if [ "${res}" -eq 0 ]; then
		echo "++ allegedly fixed, reverify" >> "${FSCK_LOG}"
		_scratch_xfs_repair -n >> "${FSCK_LOG}" 2>&1
		res=$?
	fi
	echo "++ fsck returns ${res}" >> "${FSCK_LOG}"
	if [ "${res}" -eq 0 ]; then
		echo "++ fsck thinks we are done" >> "${FSCK_LOG}"
		cat "${FSCK_LOG}"
		return 0
	elif [ "${res}" -eq 2 ]; then
		# replay log?
		echo "+++ replaying log" >> "${FSCK_LOG}"
		_try_scratch_mount >> "${FSCK_LOG}" 2>&1
		res=$?
		echo "+++ mount returns ${res}" >> "${FSCK_LOG}"
		if [ "${res}" -gt 0 ]; then
			echo "+++ zeroing log" >> "${FSCK_LOG}"
			_scratch_xfs_repair -L >> "${FSCK_LOG}" 2>&1
			echo "+++ returns $?" >> "${FSCK_LOG}"
		else
			umount "${SCRATCH_MNT}" >> "${FSCK_LOG}" 2>&1
		fi
	elif [ "${fsck_pass}" -eq "${FSCK_PASSES}" ]; then
		echo "++ fsck did not fix in ${FSCK_PASSES} passes." >> "${FSCK_LOG}"
		cat "${FSCK_LOG}"
		return 0
	fi
	cat "${FSCK_LOG}"
	if [ "${fsck_pass}" -gt 1 ]; then
		cmp -s "${tmp}-fuzz-$((fsck_pass - 1)).log" "${FSCK_LOG}"
		if [ $? -eq 0 ]; then
			echo "++ fsck makes no progress"
			return 2
		fi
	fi
	return 1
}

# This test will corrupt fs intentionally, so there will be WARNINGs
# in dmesg as expected
_disable_dmesg_check

echo "See interesting results in $seqres.full" | sed -e "s,$RESULT_DIR,RESULT_DIR,g"
SRCDIR=`pwd`
test -z "${FUZZ_ARGS}" && FUZZ_ARGS="-3 -n 32"
test -z "${FSCK_PASSES}" && FSCK_PASSES=10
BLK_SZ=4096

echo "fuzzing xfs with FUZZ_ARGS=$FUZZ_ARGS and FSCK_PASSES=$FSCK_PASSES" > $seqres.full

echo "+ create scratch fs" >> $seqres.full
_scratch_mkfs_xfs >> $seqres.full 2>&1

echo "+ populate fs image" >> $seqres.full
_scratch_populate >> $seqres.full

echo "+ check fs" >> $seqres.full
_repair_scratch_fs >> $seqres.full 2>&1 || _fail "should pass initial fsck"

echo "++ corrupt image" >> $seqres.full
_scratch_xfs_db -x -c blockget -c "blocktrash ${FUZZ_ARGS}" >> $seqres.full 2>&1

echo "++ mount image" >> $seqres.full
_try_scratch_mount >> $seqres.full 2>&1

echo "+++ test scratch" >> $seqres.full
_scratch_fuzz_test >> $seqres.full 2>&1

echo "+++ modify scratch" >> $seqres.full
_scratch_fuzz_modify >> $seqres.full 2>&1

echo "++ umount" >> $seqres.full
umount "${SCRATCH_MNT}"

# repair in a loop...
for p in $(seq 1 "${FSCK_PASSES}"); do
	scratch_repair "$p" >> $seqres.full 2>&1 && break
done
echo "+ fsck loop returns ${fsck_loop_ret}" >> $seqres.full

echo "++ check fs for round 2" >> $seqres.full
_repair_scratch_fs >> $seqres.full 2>&1

ROUND2_LOG="${tmp}-round2-${fsck_pass}.log"
echo "++ mount image (2)" >> $ROUND2_LOG
_try_scratch_mount >> $ROUND2_LOG 2>&1

echo "++ chattr -R -i" >> $ROUND2_LOG
$CHATTR_PROG -R -f -i "${SCRATCH_MNT}/" > /dev/null 2>> $ROUND2_LOG

echo "+++ test scratch" >> $ROUND2_LOG
_scratch_fuzz_test >> $ROUND2_LOG 2>&1

echo "+++ modify scratch" >> $ROUND2_LOG
_scratch_fuzz_modify >> $ROUND2_LOG 2>&1

echo "++ umount" >> $ROUND2_LOG
umount "${SCRATCH_MNT}" >> $ROUND2_LOG 2>&1

cat "$ROUND2_LOG" >> $seqres.full

echo "++ check fs (2)" >> $seqres.full
_repair_scratch_fs >> $seqres.full 2>&1

grep -E -q '(did not fix|makes no progress)' $seqres.full && echo "xfs_repair failed" | tee -a $seqres.full
if [ "$(wc -l < "$ROUND2_LOG")" -ne 8 ]; then
	echo "xfs_repair did not fix everything" | tee -a $seqres.full
fi
echo "finished fuzzing" | tee -a "$seqres.full"

status=0
exit
