#!/bin/sh
set -eu

: "$PRE15_DUT"
: "$PRE15_ROOT"
: "$PRE15_CASE_TMP"
: "$PRE15_RUN_DIR"
: "$PRE15_LIB_DIR"
. "$PRE15_LIB_DIR/runner.sh"

test "$PRE15_MODE" = qemu || pre15_infra_blocked \
	'B19a focused case requires exact-ABI QEMU mode'
fixture_dir=$PRE15_DUT/tests/pre15/fixtures
generator=$fixture_dir/B19a-xattr-generate.py
spec=$fixture_dir/B19a-xattr-spec.json
probe=$fixture_dir/B19a-xattr-probe.c
kld_builder=$fixture_dir/B28-build-kld.sh
artifacts=$PRE15_RUN_DIR/artifacts
first=$PRE15_CASE_TMP/fixtures-first
second=$PRE15_CASE_TMP/fixtures-second
module=$PRE15_CASE_TMP/B19a-erofs.ko

for tool in cc cmp diff file mkfs.erofs nm python3 scp sha256sum tar timeout; do
	command -v "$tool" >/dev/null 2>&1 || \
		pre15_infra_blocked "missing B19a QEMU tool: $tool"
done
for value in PRE15_QEMU_CONTROL_PATH PRE15_QEMU_SSH_KEY \
	PRE15_QEMU_SSH_PORT PRE15_QEMU_SSH_USER; do
	test -n "$(eval "printf '%s' \"\$$value\"")" || \
		pre15_runner_fail "$value is required"
done

for fixture in "$generator" "$spec" "$probe" "$kld_builder"; do
	pre15_record_fixture "b19a-$(basename "$fixture")" "$fixture"
done
for source in erofs_vnops.c internal.h xattr.c; do
	pre15_record_fixture "b19a-$source" "$PRE15_DUT/src/$source"
done
mkdir -p "$artifacts"

if ! timeout -k 5 180 python3 -B "$generator" --spec "$spec" \
	--output "$first" --work "$PRE15_CASE_TMP/generate-first" \
	>"$artifacts/generate-first.json" 2>"$artifacts/generate-first.stderr"; then
	pre15_runner_fail 'B19a first fixture generation failed'
fi
if ! timeout -k 5 180 python3 -B "$generator" --spec "$spec" \
	--output "$second" --work "$PRE15_CASE_TMP/generate-second" \
	>"$artifacts/generate-second.json" 2>"$artifacts/generate-second.stderr"; then
	pre15_runner_fail 'B19a second fixture generation failed'
fi
if ! diff -qr "$first" "$second" >"$artifacts/fixture-repeat.diff"; then
	pre15_runner_fail 'B19a focused fixtures are not byte reproducible'
fi
pre15_record_fixture b19a-valid-image "$first/valid.erofs"
tar -C "$first" -czf "$PRE15_CASE_TMP/B19a-fixtures.tar.gz" .

if ! timeout -k 10 600 /bin/sh "$kld_builder" "$PRE15_DUT" \
	"$PRE15_FREEBSD_SRC" "$module" "$PRE15_CASE_TMP/kld-work" 0 \
	>"$artifacts/kld-build.stdout" 2>"$artifacts/kld-build.stderr"; then
	pre15_dut_fail 'B19a exact-ABI KLD build failed'
fi
pre15_record_module "$module"
file "$module" >"$artifacts/kld-file.txt"
sha256sum "$module" >"$artifacts/kld-sha256.txt"
nm -u "$module" | LC_ALL=C sort >"$artifacts/kld-nm-u.txt"

pre15_scp()
{
	timeout -k 5 60 scp -O -q \
		-o BatchMode=yes -o StrictHostKeyChecking=no \
		-o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 \
		-o "ControlPath=$PRE15_QEMU_CONTROL_PATH" \
		-i "$PRE15_QEMU_SSH_KEY" -P "$PRE15_QEMU_SSH_PORT" \
		"$1" "$PRE15_QEMU_SSH_USER@127.0.0.1:$2"
}

if ! pre15_scp "$PRE15_CASE_TMP/B19a-fixtures.tar.gz" \
	/root/B19a-fixtures.tar.gz || ! pre15_scp "$probe" /root/B19a-xattr-probe.c ||
	! pre15_scp "$module" /root/B19a-erofs.ko; then
	pre15_infra_blocked 'could not transfer B19a module, probe, or fixtures'
fi
pre15_guest_ssh_bounded \
	'rm -rf /root/pre15-b19a-fixtures && mkdir /root/pre15-b19a-fixtures && tar -xzf /root/B19a-fixtures.tar.gz -C /root/pre15-b19a-fixtures' ||
	pre15_infra_blocked 'could not prepare B19a guest fixtures'
pre15_guest_ssh_bounded cc -std=c11 -Wall -Wextra -Werror \
	-o /root/B19a-xattr-probe /root/B19a-xattr-probe.c ||
	pre15_infra_blocked 'could not compile the B19a guest probe'
if pre15_guest_ssh_bounded kldstat -n B19a-erofs >/dev/null 2>&1; then
	pre15_infra_blocked 'guest already has an EROFS KLD loaded'
fi
pre15_guest_ssh_bounded dmesg >"$artifacts/dmesg-before-load.txt"
if ! pre15_guest_ssh_bounded kldload /root/B19a-erofs.ko \
	>"$artifacts/kldload.stdout" 2>"$artifacts/kldload.stderr"; then
	pre15_dut_fail 'B19a exact-ABI KLD load failed'
fi
pre15_own_guest_kld B19a-erofs 'B19a exact-source KLD'
pre15_guest_ssh_bounded kldstat >"$artifacts/kldstat-after-load.txt"

b19a_unown()
{
	tmp=$PRE15_CASE_TMP/ownership.$$
	awk -F '	' -v kind="$1" -v value="$2" \
		'!($1 == kind && $2 == value)' "$PRE15_OWNERSHIP_FILE" >"$tmp"
	mv "$tmp" "$PRE15_OWNERSHIP_FILE"
}

b19a_mount()
{
	image=$1
	mountpoint=$2
	label=$3
	md=$(pre15_guest_ssh_bounded mdconfig -a -t vnode \
		-f "/root/pre15-b19a-fixtures/$image") ||
		pre15_dut_fail "$label provider attach failed"
	case "$md" in
	md[0-9]*) ;;
	*) pre15_runner_fail "unexpected B19a md unit: $md" ;;
	esac
	pre15_own_guest_md "$md" "$label provider"
	pre15_guest_ssh_bounded mkdir -p "$mountpoint"
	if ! pre15_guest_ssh_bounded mount -t erofs -o ro "/dev/$md" \
		"$mountpoint"; then
		pre15_dut_fail "$label mount failed"
	fi
	pre15_own_guest_mount "$mountpoint" "$label mount"
	printf '%s\n' "$md"
}

b19a_detach()
{
	mountpoint=$1
	md=$2
	pre15_guest_ssh_bounded umount "$mountpoint" ||
		pre15_dut_fail "B19a unmount failed: $mountpoint"
	b19a_unown guest-mount "$mountpoint"
	pre15_guest_ssh_bounded mdconfig -d -u "$md" ||
		pre15_dut_fail "B19a md detach failed: $md"
	b19a_unown guest-md "$md"
}

valid_md=$(b19a_mount valid.erofs /mnt/pre15-b19a-valid 'B19a valid')
valid=/mnt/pre15-b19a-valid/target.bin
pre15_guest_ssh_bounded /root/B19a-xattr-probe valid "$valid" \
	>"$artifacts/valid-first.txt"
pre15_guest_ssh_bounded /root/B19a-xattr-probe concurrent "$valid" 16 10 \
	>"$artifacts/valid-concurrent.txt"
pre15_guest_ssh_bounded /root/B19a-xattr-probe valid "$valid" \
	>"$artifacts/valid-repeat.txt"
b19a_detach /mnt/pre15-b19a-valid "$valid_md"

for image in corrupt-shared-count.erofs corrupt-shared-id.erofs corrupt-inline-name.erofs; do
	label=$(basename "$image" .erofs)
	mountpoint=/mnt/pre15-b19a-$label
	md=$(b19a_mount "$image" "$mountpoint" "B19a $label")
	if ! pre15_guest_ssh_bounded /root/B19a-xattr-probe corrupt \
		"$mountpoint/target.bin" >"$artifacts/$label.txt" 2>&1; then
		pre15_dut_fail "$label did not return EINTEGRITY"
	fi
	pre15_guest_ssh_bounded umount "$mountpoint" ||
		pre15_dut_fail "$label unmount failed"
	b19a_unown guest-mount "$mountpoint"
	pre15_guest_ssh_bounded mdconfig -d -u "$md" ||
		pre15_dut_fail "$label md detach failed"
	b19a_unown guest-md "$md"
done

normal_md=$(b19a_mount valid.erofs /mnt/pre15-b19a-normal \
	'B19a normal-unmount')
normal_pid=$(pre15_guest_ssh_bounded \
	'cd /mnt/pre15-b19a-normal && sleep 60 >/tmp/B19a-normal.sleep 2>&1 & echo $!')
if pre15_guest_ssh_bounded umount /mnt/pre15-b19a-normal \
	>"$artifacts/normal-unmount.stdout" 2>"$artifacts/normal-unmount.stderr"; then
	pre15_dut_fail 'normal unmount unexpectedly ignored a held vnode'
fi
pre15_guest_ssh_bounded kill "$normal_pid" || true
pre15_guest_ssh_bounded umount /mnt/pre15-b19a-normal ||
	pre15_dut_fail 'normal unmount failed after releasing held vnode'
b19a_unown guest-mount /mnt/pre15-b19a-normal
pre15_guest_ssh_bounded mdconfig -d -u "$normal_md" ||
	pre15_dut_fail 'normal-unmount md detach failed'
b19a_unown guest-md "$normal_md"

forced_md=$(b19a_mount valid.erofs /mnt/pre15-b19a-forced \
	'B19a forced-unmount')
forced_pid=$(pre15_guest_ssh_bounded \
	'cd /mnt/pre15-b19a-forced && sleep 60 >/tmp/B19a-forced.sleep 2>&1 & echo $!')
pre15_guest_ssh_bounded umount -f /mnt/pre15-b19a-forced ||
	pre15_dut_fail 'forced unmount failed'
b19a_unown guest-mount /mnt/pre15-b19a-forced
pre15_guest_ssh_bounded kill "$forced_pid" || true
pre15_guest_ssh_bounded mdconfig -d -u "$forced_md" ||
	pre15_dut_fail 'forced-unmount md detach failed'
b19a_unown guest-md "$forced_md"

pre15_guest_ssh_bounded dmesg >"$artifacts/dmesg-after.txt"
diff -u "$artifacts/dmesg-before-load.txt" "$artifacts/dmesg-after.txt" \
	>"$artifacts/dmesg.diff" || true
sed -n '/^+++ /d; /^+/s/^+/ /p' "$artifacts/dmesg.diff" \
	>"$artifacts/dmesg-added.txt"
if grep -Eqi 'panic:|fatal trap|lock order reversal|witness.*warning|use-after-free|pager fault|undefined symbol|linker.*error' \
	"$artifacts/dmesg-added.txt"; then
	pre15_dut_fail 'B19a QEMU produced a kernel diagnostic'
fi
pre15_guest_ssh_bounded kldunload B19a-erofs ||
	pre15_dut_fail 'B19a KLD unload failed'
b19a_unown guest-kld B19a-erofs
if pre15_guest_ssh_bounded kldstat -n B19a-erofs >/dev/null 2>&1; then
	pre15_dut_fail 'B19a KLD remained loaded after cleanup'
fi
pre15_guest_ssh_bounded mount >"$artifacts/mount-after.txt"
pre15_guest_ssh_bounded mdconfig -l >"$artifacts/md-after.txt"
if grep -q 'pre15-b19a-' "$artifacts/mount-after.txt" ||
	grep -q 'B19a' "$artifacts/md-after.txt"; then
	pre15_dut_fail 'B19a guest mount or md resource remained after cleanup'
fi
pre15_target_reached
printf '%s\n' 'B19a exact-ABI xattr cache lifecycle PASS'
