#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2024-2026 Oracle.  All Rights Reserved.
#
# FS QA Test No. 655
#
# Corrupt some metadata and try to access it with the health monitoring program
# running.  Check that healthmon observes a metadata error.
#
. ./common/preamble
_begin_fstest auto quick eio selfhealing

_cleanup()
{
	cd /
	test -n "$healer_pid" && kill $healer_pid &>/dev/null
	wait
	rm -rf $tmp.* $testdir
}

. ./common/filter

_require_scratch_nocheck
_require_scratch_xfs_crc # can't detect minor corruption w/o crc
_require_xfs_io_command healthmon

# Disable the scratch rt device to avoid test failures relating to the rt
# bitmap consuming all the free space in our small data device.
unset SCRATCH_RTDEV

echo "Format and mount"
_scratch_mkfs -d agcount=1 | _filter_mkfs 2> $tmp.mkfs >> $seqres.full
. $tmp.mkfs
_scratch_mount
mkdir $SCRATCH_MNT/a/
# Enough entries to get to a single block directory
for ((i = 0; i < ( (isize + 255) / 256); i++)); do
	path="$(printf "%s/a/%0255d" "$SCRATCH_MNT" "$i")"
	touch "$path"
done
inum="$(stat -c %i "$SCRATCH_MNT/a")"
_scratch_unmount

# Fuzz the directory block so that the touch below will be guaranteed to trip
# a runtime sickness report in exactly the manner we desire.
_scratch_xfs_db -x -c "inode $inum" -c "dblock 0" -c 'fuzz bhdr.hdr.owner add' -c print &>> $seqres.full

# Try to allocate space to trigger a metadata corruption event
echo "Runtime corruption detection"
_scratch_mount
$XFS_IO_PROG -c 'healthmon -c -v' $SCRATCH_MNT > $tmp.healthmon &
healer_pid=$!
sleep 1	# wait for program to start up
touch $SCRATCH_MNT/a/farts &>> $seqres.full
_scratch_unmount

wait	# for healthmon to finish
unset healer_pid

# Did we get errors?
check_healthmon()
{
	cat $tmp.healthmon >> $seqres.full
	_filter_scratch < $tmp.healthmon | \
		grep -E '(sick|corrupt)' | \
		sed -e 's|SCRATCH_MNT/a|VICTIM|g' \
		    -e 's|SCRATCH_MNT ino [0-9]* gen 0x[0-9a-f]*|VICTIM|g' | \
		sort | \
		uniq
}
check_healthmon

# Run scrub to trigger a health event from there too.
echo "Scrub corruption detection"
_scratch_mount
if _supports_xfs_scrub $SCRATCH_MNT $SCRATCH_DEV; then
	$XFS_IO_PROG -c 'healthmon -c -v' $SCRATCH_MNT > $tmp.healthmon &
	healer_pid=$!
	sleep 1	# wait for program to start up
	$XFS_SCRUB_PROG -n $SCRATCH_MNT &>> $seqres.full
	_scratch_unmount

	wait	# for healthmon to finish
	unset healer_pid

	# Did we get errors?
	check_healthmon
else
	# mock the output since we don't support scrub
	_scratch_unmount
	cat << ENDL
VICTIM directory: corrupt
VICTIM directory: sick
VICTIM parent: corrupt
ENDL
fi

_exit 0
