#!/bin/sh
set -eu

: "${PRE15_DUT:?PRE15_DUT is required}"
: "${PRE15_ROOT:?PRE15_ROOT is required}"
: "${PRE15_CASE_TMP:?PRE15_CASE_TMP is required}"
: "${PRE15_RUN_DIR:?PRE15_RUN_DIR is required}"
: "${PRE15_LIB_DIR:?PRE15_LIB_DIR is required}"
. "$PRE15_LIB_DIR/runner.sh"

fixture_dir=$PRE15_DUT/tests/pre15/fixtures
spec=$fixture_dir/B19b-xattr-spec.json
generator=$fixture_dir/B19b-xattr-generate.py
oracle=$fixture_dir/B19b-xattr-oracle.py
probe=$fixture_dir/B19b-xattr-probe.c
kld_builder=$fixture_dir/B19b-build-kld.sh
gate_script=$PRE15_DUT/tests/pre15/gates/P15-021.sh
gate_input=$PRE15_DUT/tests/pre15/gates/P15-021-input.json
gate_commit=675ed9b650b3bc157b38bcc165a0cb215a2aa989
gate_base=666e52f710363df07f7c93919eb835d41092d011
artifacts=$PRE15_RUN_DIR/artifacts
first=$PRE15_CASE_TMP/first
second=$PRE15_CASE_TMP/second
gate_clone=$PRE15_CASE_TMP/gate-clone
gate_output=$PRE15_CASE_TMP/gate-output

for tool in cmp fsck.erofs git mkfs.erofs python3 sha256sum timeout; do
	command -v "$tool" >/dev/null 2>&1 || \
		pre15_infra_blocked "missing B19b host tool: $tool"
done

pre15_record_fixture b19b-spec "$spec"
pre15_record_fixture b19b-generator "$generator"
pre15_record_fixture b19b-oracle "$oracle"
pre15_record_fixture b19b-probe "$probe"
pre15_record_fixture b19b-kld-builder "$kld_builder"
pre15_record_fixture b19b-case "$PRE15_DUT/tests/pre15/cases/B19b-xattr-bloom.sh"
pre15_record_fixture b19b-gate "$gate_script"
pre15_record_fixture b19b-gate-input "$gate_input"
pre15_record_fixture b19b-erofs-fs "$PRE15_DUT/src/erofs_fs.h"
pre15_record_fixture b19b-internal "$PRE15_DUT/src/internal.h"
pre15_record_fixture b19b-xattr "$PRE15_DUT/src/xattr.c"
pre15_record_fixture b19b-linux-erofs-fs "$PRE15_ROOT/src-linux/erofs_fs.h"
pre15_record_fixture b19b-linux-xattr "$PRE15_ROOT/src-linux/xattr.c"
mkdir -p "$artifacts"

if ! git clone -q --shared --no-checkout "$PRE15_ROOT" "$gate_clone" \
    >"$artifacts/gate-clone.stdout" 2>"$artifacts/gate-clone.stderr" || \
    ! git -C "$gate_clone" checkout -q --detach "$gate_commit" \
    >"$artifacts/gate-checkout.stdout" 2>"$artifacts/gate-checkout.stderr"; then
	pre15_runner_fail 'could not materialize the committed P15-021 gate'
fi
frozen_gate=$gate_clone/repo-pre-15/tests/pre15/gates/P15-021.sh
if ! timeout -k 10 240 "$frozen_gate" --base "$gate_base" \
    --output "$gate_output" >"$artifacts/gate.stdout" \
    2>"$artifacts/gate.stderr"; then
	pre15_runner_fail 'committed P15-021 gate did not replay GO'
fi

if ! python3 -B "$generator" generate --spec "$spec" --output "$first" \
    --work "$PRE15_CASE_TMP/first-work" >"$artifacts/generate-first.stdout" \
    2>"$artifacts/generate-first.stderr"; then
	pre15_runner_fail 'B19b first real-fixture generation failed'
fi
if ! python3 -B "$generator" generate --spec "$spec" --output "$second" \
    --work "$PRE15_CASE_TMP/second-work" >"$artifacts/generate-second.stdout" \
    2>"$artifacts/generate-second.stderr"; then
	pre15_runner_fail 'B19b repeat real-fixture generation failed'
fi
if ! cmp "$first/SHA256SUMS" "$second/SHA256SUMS" || \
    ! cmp "$first/manifest.json" "$second/manifest.json"; then
	pre15_runner_fail 'B19b fixture generation is not byte reproducible'
fi
for image in valid.erofs unknown-filter.erofs feature-off.erofs \
    corrupt-shared-count.erofs corrupt-shared-id.erofs; do
	if ! cmp "$first/$image" "$second/$image"; then
		pre15_runner_fail "B19b repeat image differs: $image"
	fi
	pre15_record_fixture "b19b-$image" "$first/$image"
done

if ! python3 -B "$oracle" --spec "$spec" --fixtures "$first" \
    --root "$PRE15_ROOT" --dut "$PRE15_DUT" \
    --report "$artifacts/B19b-oracle.json" \
    >"$artifacts/oracle.stdout" 2>"$artifacts/oracle.stderr"; then
	pre15_dut_fail 'B19b fixture/source/Linux oracle failed'
fi
cp "$gate_output/result.json" "$artifacts/P15-021-gate-result.json"
cp "$gate_output/million.json" "$artifacts/P15-021-million.json"
cp "$gate_output/benchmark.json" "$artifacts/P15-021-benchmark.json"
cp "$first/SHA256SUMS" "$artifacts/B19b-SHA256SUMS"
pre15_record_fixture b19b-generated-sums "$artifacts/B19b-SHA256SUMS"
pre15_target_reached

if test "${PRE15_MODE:-host}" != qemu; then
	printf '%s\n' \
		'TC169 host real fixture/hash/filter/integrity/concurrency oracle PASS' \
		'QEMU runtime NOT_RUN in host mode'
	exit 0
fi

for tool in awk clang file nm scp tar; do
	command -v "$tool" >/dev/null 2>&1 || \
		pre15_infra_blocked "missing B19b QEMU tool: $tool"
done
: "${PRE15_QEMU_CONTROL_PATH:?QEMU control path is required}"
: "${PRE15_QEMU_SSH_KEY:?QEMU SSH key is required}"
: "${PRE15_QEMU_SSH_PORT:?QEMU SSH port is required}"
: "${PRE15_QEMU_SSH_USER:?QEMU SSH user is required}"

fixture_archive=$PRE15_CASE_TMP/B19b-fixtures.tar.gz
module=$PRE15_CASE_TMP/B19b-erofs.ko
if ! /bin/sh "$kld_builder" "$PRE15_DUT" "$PRE15_FREEBSD_SRC" "$module" \
    "$PRE15_CASE_TMP/kld-work" >"$artifacts/B19b-kld-build.stdout" \
    2>"$artifacts/B19b-kld-build.stderr"; then
	pre15_dut_fail 'B19b cross-target KLD build failed'
fi
pre15_record_module "$module"
file "$module" >"$artifacts/B19b-kld-file.txt"
sha256sum "$module" >"$artifacts/B19b-kld-sha256.txt"
nm -u "$module" | LC_ALL=C sort >"$artifacts/B19b-kld-nm-u.txt"
tar -C "$first" -czf "$fixture_archive" .

pre15_scp()
{
	timeout -k 5 "${PRE15_GUEST_COMMAND_TIMEOUT:-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 "$fixture_archive" /root/B19b-fixtures.tar.gz || \
    ! pre15_scp "$probe" /root/B19b-xattr-probe.c || \
    ! pre15_scp "$module" /root/B19b-erofs.ko; then
	pre15_infra_blocked 'could not transfer B19b module, probe, or fixtures'
fi
if ! pre15_guest_ssh_bounded \
    'rm -rf /root/pre15-b19b-fixtures && mkdir /root/pre15-b19b-fixtures && tar -xzf /root/B19b-fixtures.tar.gz -C /root/pre15-b19b-fixtures'; then
	pre15_infra_blocked 'could not prepare B19b guest fixtures'
fi
if pre15_guest_ssh_bounded kldstat -n erofs >/dev/null 2>&1; then
	pre15_infra_blocked 'FreeBSD guest already has an EROFS module loaded'
fi
if ! pre15_guest_ssh_bounded kldload /root/B19b-erofs.ko \
    >"$artifacts/B19b-kldload.stdout" 2>"$artifacts/B19b-kldload.stderr"; then
	if grep -q 'module already loaded or in kernel' "$artifacts/B19b-kldload.stderr"; then
		pre15_infra_blocked 'guest kernel already owns the erofs.1 interface'
	fi
	pre15_dut_fail 'B19b exact-source KLD failed to load'
fi
pre15_own_guest_kld erofs 'B19b exact-source KLD'
if ! pre15_guest_ssh_bounded cc -std=c11 -Wall -Wextra -Werror \
    -o /root/B19b-xattr-probe /root/B19b-xattr-probe.c; then
	pre15_infra_blocked 'could not compile the B19b guest probe'
fi

pre15_mount_image()
{
	pre15_image=$1
	pre15_mountpoint=$2
	pre15_label=$3
	pre15_md=$(pre15_guest_ssh_bounded mdconfig -a -t vnode \
	    -f "/root/pre15-b19b-fixtures/$pre15_image") || \
		pre15_dut_fail "could not attach B19b provider: $pre15_image"
	case "$pre15_md" in
	md[0-9]*) ;;
	*) pre15_runner_fail "unexpected mdconfig output: $pre15_md" ;;
	esac
	pre15_own_guest_md "$pre15_md" "$pre15_label provider"
	pre15_guest_ssh_bounded mkdir -p "$pre15_mountpoint"
	if ! pre15_guest_ssh_bounded mount -t erofs -o ro "/dev/$pre15_md" \
	    "$pre15_mountpoint"; then
		pre15_dut_fail "$pre15_label mount failed"
	fi
	pre15_own_guest_mount "$pre15_mountpoint" "$pre15_label mount"
}

pre15_mount_image valid.erofs /mnt/pre15-b19b-valid 'B19b valid'
valid=/mnt/pre15-b19b-valid/target.bin
pre15_guest_ssh_bounded /root/B19b-xattr-probe hit "$valid" attr00 0
pre15_guest_ssh_bounded /root/B19b-xattr-probe errno "$valid" user \
    absent-00001 87
pre15_guest_ssh_bounded /root/B19b-xattr-probe errno "$valid" user \
    absent-00000 87
pre15_guest_ssh_bounded /root/B19b-xattr-probe list "$valid"
pre15_guest_ssh_bounded /root/B19b-xattr-probe concurrent "$valid" attr00 \
    absent-00001 absent-00000 64 10

pre15_mount_image unknown-filter.erofs /mnt/pre15-b19b-unknown \
    'B19b unknown filter'
unknown=/mnt/pre15-b19b-unknown/target.bin
pre15_guest_ssh_bounded /root/B19b-xattr-probe hit "$unknown" attr00 0
pre15_guest_ssh_bounded /root/B19b-xattr-probe errno "$unknown" user \
    absent-00001 87

pre15_mount_image feature-off.erofs /mnt/pre15-b19b-feature-off \
    'B19b feature off'
feature_off=/mnt/pre15-b19b-feature-off/target.bin
pre15_guest_ssh_bounded /root/B19b-xattr-probe hit "$feature_off" attr00 0
pre15_guest_ssh_bounded /root/B19b-xattr-probe errno "$feature_off" user \
    absent-00001 87

pre15_mount_image corrupt-shared-count.erofs /mnt/pre15-b19b-bad-count \
    'B19b corrupt shared count'
pre15_guest_ssh_bounded /root/B19b-xattr-probe errno \
    /mnt/pre15-b19b-bad-count/target.bin user absent-00001 97

pre15_mount_image corrupt-shared-id.erofs /mnt/pre15-b19b-bad-id \
    'B19b corrupt shared ID'
pre15_guest_ssh_bounded /root/B19b-xattr-probe errno \
    /mnt/pre15-b19b-bad-id/target.bin user attr00 97

pre15_guest_ssh_bounded dmesg >"$artifacts/B19b-dmesg.txt"
if grep -Eq 'panic:|Fatal trap|lock order reversal|KDB: stack backtrace' \
    "$artifacts/B19b-dmesg.txt"; then
	pre15_dut_fail 'B19b runtime produced a kernel diagnostic'
fi
printf '%s\n' 'TC169 QEMU hit/miss/collision/fallback/integrity/concurrency PASS'
