#!/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/B32-cache-state.json
oracle=$fixture_dir/B32-cache-oracle.c
generator=$fixture_dir/B32-cache-generate.py
probe=$fixture_dir/B32-cache-probe.c
qemu_runner=$fixture_dir/B32-qemu-run.sh
kld_builder=$fixture_dir/B28-build-kld.sh
artifacts=$PRE15_RUN_DIR/artifacts
scenario=${PRE15_B32_SCENARIO:-TC168-cache-inflight}

case "$scenario" in
TC168-cache-inflight|TC184-vnode-races) ;;
*) pre15_fail_usage "unknown B32 scenario: $scenario" ;;
esac

for tool in cc git python3 sha256sum; do
	command -v "$tool" >/dev/null 2>&1 || \
		pre15_infra_blocked "missing B32 host tool: $tool"
done
for fixture in "$spec" "$oracle" "$generator" "$probe" "$qemu_runner" \
    "$kld_builder"; do
	pre15_record_fixture "b32-$(basename "$fixture")" "$fixture"
done
for source in internal.h decompressor.c zdata.c zmap.c; do
	pre15_record_fixture "b32-$source" "$PRE15_DUT/src/$source"
done
mkdir -p "$artifacts"

if test "${PRE15_QEMU_TARGET_ONLY:-0}" = 1; then
	pre15_target_reached
else
if ! python3 -B - "$PRE15_ROOT" "$PRE15_DUT" "$spec" \
    "$artifacts/B32-source-audit.json" <<'PY'
from __future__ import annotations

import json
from pathlib import Path
import re
import subprocess
import sys


root = Path(sys.argv[1])
dut = Path(sys.argv[2])
spec = json.loads(Path(sys.argv[3]).read_text(encoding="ascii"))
report_path = Path(sys.argv[4])
baseline = spec["baseline"]
implementation = "c6a502184da6e973b1f0fa2ff8c0290c041e7092"
current_owners = {
    "internal.h": "aa20623132b6821c6ad4651b263fc0ac0f7a14c8",
    "decompressor.c": implementation,
    "zdata.c": "bc56f830918b76029871b60cca2e53992de70a2e",
    "zmap.c": "d09924362eb29819bd393e1e86602eb38c7608c6",
}


def fail(message: str) -> None:
    raise SystemExit(message)


def committed(commit: str, path: str) -> str:
    completed = subprocess.run(
        ["git", "-C", str(root), "show", f"{commit}:repo-pre-15/{path}"],
        check=False,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        text=True,
    )
    if completed.returncode != 0:
        fail(f"cannot read B32 source {commit}:{path}: {completed.stderr.strip()}")
    return completed.stdout


def function(source: str, name: str) -> str:
    match = re.search(rf"\n(?:static\s+)?[^\n]+\n{name}\([^{{]+\n\{{", source)
    if match is None:
        fail(f"cannot locate function {name}")
    start = match.start() + 1
    brace = source.find("{", match.start())
    depth = 0
    for index in range(brace, len(source)):
        if source[index] == "{":
            depth += 1
        elif source[index] == "}":
            depth -= 1
            if depth == 0:
                return source[start : index + 1]
    fail(f"unterminated function {name}")


expected_changed = {
    "repo-pre-15/src/decompressor.c",
    "repo-pre-15/src/internal.h",
    "repo-pre-15/src/zdata.c",
    "repo-pre-15/src/zmap.c",
    "repo-pre-15/tests/pre15/cases/B32-cache-state.sh",
    "repo-pre-15/tests/pre15/fixtures/B32-cache-generate.py",
    "repo-pre-15/tests/pre15/fixtures/B32-cache-oracle.c",
    "repo-pre-15/tests/pre15/fixtures/B32-cache-probe.c",
    "repo-pre-15/tests/pre15/fixtures/B32-qemu-run.sh",
    "repo-pre-15/tests/pre15/fixtures/B32-cache-state.json",
}
changed = subprocess.run(
    [
        "git",
        "-C",
        str(root),
        "diff-tree",
        "--no-commit-id",
        "--name-only",
        "-r",
        implementation,
    ],
    check=True,
    stdout=subprocess.PIPE,
    text=True,
).stdout.splitlines()
if set(changed) != expected_changed or len(changed) != len(expected_changed):
    fail(f"B32 implementation write set mismatch: {changed!r}")

for name, commit in (("baseline", baseline), ("implementation", implementation)):
    if subprocess.check_output(
        ["git", "-C", str(root), "rev-parse", commit], text=True
    ).strip() != commit:
        fail(f"B32 {name} identity changed")

sources = {
    name: (dut / "src" / name).read_text(encoding="utf-8")
    for name in ("internal.h", "decompressor.c", "zdata.c", "zmap.c")
}
implemented_sources = {
    name: committed(implementation, f"src/{name}") for name in sources
}
for name, owner in current_owners.items():
    if sources[name] != committed(owner, f"src/{name}"):
        fail(f"current B32 source differs from accepted owner {owner}: {name}")
required_key_tokens = {
    "nid": "cache->nid == vi->nid",
    "decoded_size": "cache->decoded_size == decoded_size",
    "m_pa": "cache->map.m_pa == map->m_pa",
    "m_la": "cache->map.m_la == map->m_la",
    "m_plen": "cache->map.m_plen == map->m_plen",
    "m_llen": "cache->map.m_llen == map->m_llen",
    "m_deviceid": "cache->map.m_deviceid == map->m_deviceid",
    "m_algorithmformat": "cache->map.m_algorithmformat == map->m_algorithmformat",
    "m_flags": "cache->map.m_flags == map->m_flags",
}
if list(required_key_tokens) != spec["key_fields"]:
    fail("B32 key fixture field order changed")


def audit_b32_sources(label: str, source_set: dict[str, str]) -> None:
    internal = source_set["internal.h"]
    zdata = source_set["zdata.c"]
    decompressor = source_set["decompressor.c"]
    zmap = source_set["zmap.c"]
    map_match = re.search(
        r"struct erofs_map_blocks \{(?P<body>.*?)\n\};", internal, re.S
    )
    cache_match = re.search(
        r"struct erofs_zextent_cache \{(?P<body>.*?)\n\};", internal, re.S
    )
    if map_match is None or cache_match is None:
        fail(f"{label} canonical map/cache structures are absent")
    if map_match.start() > cache_match.start():
        fail(f"{label} canonical map is not defined before embedded cache use")
    map_body = map_match.group("body")
    cache_body = cache_match.group("body")
    if "uint8_t m_algorithmformat;" not in map_body:
        fail(f"{label} m_algorithmformat is not an unsigned narrow type")
    if "struct erofs_map_blocks map;" not in cache_body:
        fail(f"{label} cache does not embed the canonical map")
    for duplicate in required_key_tokens:
        if duplicate in ("nid", "decoded_size"):
            continue
        if re.search(
            rf"\b{duplicate}\b",
            cache_body.replace("struct erofs_map_blocks map;", ""),
        ):
            fail(f"{label} cache duplicates canonical map field {duplicate}")
    for field in ("decoded_size", "waiters", "error", "state", "cv", "closing"):
        if not re.search(rf"\b{field}\b", cache_body):
            fail(f"{label} cache state is missing {field}")

    match_body = function(zdata, "z_erofs_extent_cache_match")
    for field, token in required_key_tokens.items():
        if match_body.count(token) != 1:
            fail(f"{label} cache key does not compare {field} exactly once")
    for token in (
        "cv_wait(&cache->cv, &sbi->z_extent_cache_lock)",
        "cv_broadcast(&cache->cv)",
        "cache->state = EROFS_ZCACHE_INFLIGHT",
        "cache->state = EROFS_ZCACHE_READY",
        "cache->state = EROFS_ZCACHE_FAILED",
        "cache->waiters != 0",
        "cache->error = error",
        "cache->closing = true",
    ):
        if token not in zdata:
            fail(f"{label} cache state-machine token is absent: {token}")
    if "cache->state == EROFS_ZCACHE_INFLIGHT || cache->waiters != 0" not in zdata:
        fail(f"{label} eviction does not protect registered waiter generations")
    if "z_erofs_extent_cache_eligible(sbi, vi, &map, decoded_len)" not in zdata:
        fail(f"{label} cache admission is not based on decoded size")
    if "Z_EROFS_CACHE_BYPASS" not in zdata or "free(decoded, M_EROFS);" not in zdata:
        fail(f"{label} no-cache fallback is absent")
    if (
        "uint8_t algorithm;" not in decompressor
        or "(unsigned char)map->m_algorithmformat" in decompressor
    ):
        fail(f"{label} decompressor algorithm boundary is not unsigned narrow")
    for token in (
        "(uint8_t)(map->m_plen >>",
        "(uint8_t)(fmt - 1)",
        "(uint8_t)(h->h_algorithmtype & 15)",
        "(uint8_t)(h->h_algorithmtype >> 4)",
    ):
        if token not in zmap:
            fail(f"{label} explicit on-disk conversion is absent: {token}")
    for forbidden in ("bitlock", "wait_on_bit", "workqueue", "work_struct", "folio"):
        if forbidden in "\n".join(source_set.values()):
            fail(f"{label} introduced Linux-only primitive: {forbidden}")


audit_b32_sources("implementation", implemented_sources)
audit_b32_sources("current", sources)

baseline_zdata = committed(baseline, "src/zdata.c")
for preserved in ("z_erofs_decode_length", "z_erofs_decode_extent"):
    if function(implemented_sources["zdata.c"], preserved) != function(
        baseline_zdata, preserved
    ):
        fail(f"B28 {preserved} changed outside B32 cache state")
for untouched in (
    "compress.h",
    "decompressor_lz4.c",
    "decompressor_lzma.c",
    "decompressor_deflate.c",
    "decompressor_zstd.c",
):
    if committed(implementation, f"src/{untouched}") != committed(
        baseline, f"src/{untouched}"
    ):
        fail(f"B32 changed B25/B27/B28 source outside its write set: {untouched}")
    current = (dut / "src" / untouched).read_text(encoding="utf-8")
    owner = current_owners["zmap.c"] if untouched == "decompressor_zstd.c" else baseline
    if current != committed(owner, f"src/{untouched}"):
        fail(f"current preserved codec source differs from accepted owner: {untouched}")

report = {
    "algorithm_type": "uint8_t",
    "baseline": baseline,
    "batch": "B32",
    "cache_map_embedded": True,
    "current_owners": current_owners,
    "decoded_size_admission": True,
    "implementation": implementation,
    "key_fields": spec["key_fields"],
    "lock": "z_extent_cache_lock",
    "no_cache_fallback": True,
    "preserved": ["B25-positive-errno", "B27-trailing", "B28-partial-fallback"],
    "states": spec["states"],
    "write_set": sorted(changed),
}
report_path.write_text(json.dumps(report, indent=2, sort_keys=True) + "\n", encoding="ascii")
print(json.dumps(report, indent=2, sort_keys=True))
PY
then
	pre15_runner_fail 'B32 source, lock, key, or preservation audit failed'
fi

if ! cc -std=c11 -Wall -Wextra -Werror -pthread "$oracle" \
    -o "$PRE15_CASE_TMP/B32-cache-oracle" \
    >"$artifacts/oracle-build.stdout" 2>"$artifacts/oracle-build.stderr"; then
	pre15_runner_fail 'B32 host concurrency oracle did not compile'
fi
if ! timeout -k 5 60 "$PRE15_CASE_TMP/B32-cache-oracle" \
    >"$artifacts/oracle.stdout" 2>"$artifacts/oracle.stderr"; then
	pre15_dut_fail 'B32 host concurrency oracle failed'
fi
sha256sum "$artifacts/B32-source-audit.json" "$artifacts/oracle.stdout" \
    >"$artifacts/SHA256SUMS"
fi
pre15_target_reached

if test "${PRE15_MODE:-host}" != qemu; then
	printf '%s\n' \
		'TC168-cache-inflight host PASS' \
		'Canonical key, one owner, waiter bytes/positive errno, retry, eviction, fallback, and shutdown PASS' \
		'TC126/TC127/TC129 host oracle NOT_RUN (not necessary)' \
		'QEMU and full feature suite NOT_RUN in host mode'
	exit 0
fi

for tool in clang cmp diff file mkfs.erofs nm scp tar timeout; do
	command -v "$tool" >/dev/null 2>&1 || \
		pre15_infra_blocked "missing B32 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}"

first=$PRE15_CASE_TMP/fixtures-first
second=$PRE15_CASE_TMP/fixtures-second
if ! (umask 022 && timeout -k 5 180 python3 -B "$generator" --spec "$spec" \
    --output "$first") >"$artifacts/generate-first.json" \
    2>"$artifacts/generate-first.stderr" || \
    ! (umask 022 && timeout -k 5 180 python3 -B "$generator" --spec "$spec" \
    --output "$second") >"$artifacts/generate-second.json" \
    2>"$artifacts/generate-second.stderr"; then
	pre15_runner_fail 'B32 fixture generation failed'
fi
if ! diff -qr "$first" "$second" >"$artifacts/fixture-repeat.diff"; then
	pre15_runner_fail 'B32 fixtures are not byte reproducible'
fi
cp "$first/fixture-index.json" "$artifacts/B32-fixture-index.json"

module=$PRE15_CASE_TMP/B32-erofs-zstdio0.ko
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 'B32 cross-target zstdio0 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"

fixture_archive=$PRE15_CASE_TMP/B32-fixtures.tar.gz
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"
}

pre15_guest_ssh_bounded()
{
	timeout -k 5 "${PRE15_GUEST_COMMAND_TIMEOUT:-60}" ssh -n -q \
		-o BatchMode=yes -o StrictHostKeyChecking=no \
		-o UserKnownHostsFile=/dev/null -o ConnectTimeout=5 \
		-o "ControlPath=$PRE15_QEMU_CONTROL_PATH" \
		-p "$PRE15_QEMU_SSH_PORT" \
		"$PRE15_QEMU_SSH_USER@127.0.0.1" "$@"
}

if ! pre15_scp "$fixture_archive" /root/B32-fixtures.tar.gz || \
    ! pre15_scp "$probe" /root/B32-cache-probe.c || \
    ! pre15_scp "$module" /root/B32-erofs-zstdio0.ko; then
	pre15_infra_blocked 'could not transfer B32 module, probe, or fixtures'
fi
if ! pre15_guest_ssh_bounded \
    'rm -rf /root/B32-fixtures && mkdir /root/B32-fixtures && tar -xzf /root/B32-fixtures.tar.gz -C /root/B32-fixtures && cc -O2 -Wall -Wextra -Werror -pthread -o /root/B32-cache-probe /root/B32-cache-probe.c'; then
	pre15_infra_blocked 'could not prepare B32 guest fixtures/probe'
fi
if pre15_guest_ssh_bounded kldstat -q -m erofs >/dev/null 2>&1; then
	pre15_infra_blocked 'guest kernel already owns the erofs.1 interface'
fi
pre15_guest_ssh_bounded kldstat >"$artifacts/guest-kldstat-before-load.txt"
pre15_guest_ssh_bounded dmesg >"$artifacts/guest-dmesg-before-load.txt"
cache_tunable=vfs.erofs.decoded_cache.minimum_decode_work
cache_tunable_before=$(pre15_guest_ssh_bounded kenv -q "$cache_tunable" || true)
printf 'name=%s before=%s test-value=0\n' "$cache_tunable" \
    "${cache_tunable_before:-unset}" >"$artifacts/cache-tunable.txt"
if ! pre15_guest_ssh_bounded kenv "$cache_tunable=0"; then
	pre15_infra_blocked 'could not force B32 decoded-cache admission'
fi
if ! pre15_guest_ssh_bounded kldload /root/B32-erofs-zstdio0.ko \
    >"$artifacts/kldload.stdout" 2>"$artifacts/kldload.stderr"; then
	if test -n "$cache_tunable_before"; then
		pre15_guest_ssh_bounded kenv "$cache_tunable=$cache_tunable_before" || true
	else
		pre15_guest_ssh_bounded kenv -u "$cache_tunable" || true
	fi
	if grep -q 'module already loaded or in kernel' \
	    "$artifacts/kldload.stderr"; then
		pre15_infra_blocked 'guest kernel already owns the erofs.1 interface'
	fi
	pre15_dut_fail 'B32 exact-source zstdio0 KLD failed to load'
fi
if test -n "$cache_tunable_before"; then
	pre15_guest_ssh_bounded kenv "$cache_tunable=$cache_tunable_before" || \
		pre15_infra_blocked 'could not restore B32 cache tunable'
else
	pre15_guest_ssh_bounded kenv -u "$cache_tunable" || \
		pre15_infra_blocked 'could not remove B32 cache tunable override'
fi
cache_tunable_after=$(pre15_guest_ssh_bounded kenv -q "$cache_tunable" || true)
printf 'after=%s\n' "${cache_tunable_after:-unset}" \
    >>"$artifacts/cache-tunable.txt"
if test "$cache_tunable_after" != "$cache_tunable_before"; then
	pre15_runner_fail 'B32 cache tunable was not restored exactly'
fi
if ! pre15_guest_ssh_bounded kldstat -q -m erofs; then
	pre15_dut_fail 'B32 kldload returned success without erofs.1 ownership'
fi
pre15_guest_ssh_bounded kldstat >"$artifacts/guest-kldstat-after-load.txt"
pre15_guest_ssh_bounded dmesg >"$artifacts/guest-dmesg-before.txt"
diff -u "$artifacts/guest-dmesg-before-load.txt" \
    "$artifacts/guest-dmesg-before.txt" >"$artifacts/guest-dmesg-load.diff" || true
sed -n '/^+++ /d; /^+/s/^+//p' "$artifacts/guest-dmesg-load.diff" \
    >"$artifacts/guest-dmesg-load-added.txt"
if grep -Eqi 'panic:|lock order reversal|witness.*warning|use-after-free|fatal trap|pager fault|linker.*(error|undefined)|undefined symbol' \
    "$artifacts/guest-dmesg-load-added.txt"; then
	pre15_dut_fail 'B32 exact-source KLD load produced kernel or linker errors'
fi
pre15_own_guest_kld B32-erofs-zstdio0.ko 'B32 exact-source KLD'

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

b32_attach()
{
	image=$1
	label=$2
	B32_MD=$(pre15_guest_ssh_bounded mdconfig -a -t vnode \
	    -f "/root/B32-fixtures/images/$image") || \
		pre15_dut_fail "$label md attach failed"
	case "$B32_MD" in
	md[0-9]*) ;;
	*) pre15_runner_fail "unexpected B32 md unit: $B32_MD" ;;
	esac
	pre15_own_guest_md "$B32_MD" "$label md"
	B32_MOUNT=/mnt/pre15-b32-$label
	pre15_guest_ssh_bounded mkdir -p "$B32_MOUNT"
	pre15_guest_ssh_bounded mount -t erofs -o ro "/dev/$B32_MD" "$B32_MOUNT" || \
		pre15_dut_fail "$label mount failed"
	pre15_own_guest_mount "$B32_MOUNT" "$label mount"
}

b32_detach()
{
	if pre15_guest_ssh_bounded mount | grep -F " on $B32_MOUNT " >/dev/null; then
		pre15_guest_ssh_bounded umount "$B32_MOUNT" || \
			pre15_dut_fail "$B32_MOUNT unmount failed"
	fi
	b32_unown guest-mount "$B32_MOUNT"
	pre15_guest_ssh_bounded mdconfig -d -u "$B32_MD" || \
		pre15_dut_fail "$B32_MD detach failed"
	b32_unown guest-md "$B32_MD"
}

b32_start_lifecycle()
{
	label=$1
	B32_CONTROL=/tmp/B32-$label-control
	B32_LIFECYCLE_PID=$(pre15_guest_ssh_bounded \
	    "rm -rf '$B32_CONTROL'; mkdir '$B32_CONTROL'; nohup /root/B32-cache-probe lifecycle '$B32_MOUNT/payload.bin' '$B32_CONTROL' >/tmp/B32-$label.stdout 2>/tmp/B32-$label.stderr </dev/null & echo \$!") || \
		pre15_infra_blocked "$label lifecycle process did not start"
	case "$B32_LIFECYCLE_PID" in
	''|*[!0-9]*) pre15_runner_fail "invalid B32 lifecycle PID: $B32_LIFECYCLE_PID" ;;
	esac
	pre15_guest_ssh_bounded \
	    "i=0; while test ! -f '$B32_CONTROL/ready'; do i=\$((i + 1)); test \$i -lt 600 || exit 1; sleep 0.1; done" || \
		pre15_infra_blocked "$label lifecycle ready marker timed out"
}

b32_stop_lifecycle()
{
	pre15_guest_ssh_bounded touch "$B32_CONTROL/exit"
	pre15_guest_ssh_bounded \
	    "i=0; while kill -0 '$B32_LIFECYCLE_PID' 2>/dev/null; do i=\$((i + 1)); test \$i -lt 600 || exit 1; sleep 0.1; done" || \
		pre15_infra_blocked 'B32 lifecycle process did not exit'
}

reference=/root/B32-fixtures/source/payload.bin
b32_attach lzma-valid.erofs valid
if ! pre15_guest_ssh_bounded /root/B32-cache-probe concurrent \
    "$B32_MOUNT/payload.bin" "$reference" 0 64 8 0 65536 \
    >"$artifacts/valid-concurrent.stdout" \
    2>"$artifacts/valid-concurrent.stderr"; then
	pre15_dut_fail 'TC168 valid same-key concurrent reads diverged'
fi
if ! grep -qx 'concurrent PASS workers=64 loops=8 assertions=512 errno=0 offset=0 length=65536' \
    "$artifacts/valid-concurrent.stdout"; then
	pre15_runner_fail 'TC168 valid assertion count was not exactly 512'
fi
if test "$scenario" = TC184-vnode-races; then
	if ! pre15_guest_ssh_bounded /root/B32-cache-probe vnode-race \
	    "$B32_MOUNT/payload.bin" "$reference" 64 50 \
	    >"$artifacts/vnode-race.stdout" \
	    2>"$artifacts/vnode-race.stderr"; then
		pre15_dut_fail 'TC184 lookup/open/read/reclaim loop diverged'
	fi
	if ! grep -qx 'vnode-race PASS workers=64 loops=50 assertions=3200' \
	    "$artifacts/vnode-race.stdout"; then
		pre15_runner_fail 'TC184 vnode assertion count was not exactly 3200'
	fi
fi
b32_detach

if test "$scenario" = TC168-cache-inflight; then
	b32_attach lzma-truncated.erofs truncated
	if ! pre15_guest_ssh_bounded /root/B32-cache-probe concurrent \
	    "$B32_MOUNT/payload.bin" "$reference" 97 1 1 0 65536 \
	    >"$artifacts/failure-single.stdout" \
	    2>"$artifacts/failure-single.stderr"; then
		pre15_dut_fail 'TC168 single corrupt read did not return EINTEGRITY'
	fi
	if ! grep -qx 'concurrent PASS workers=1 loops=1 assertions=1 errno=97 offset=0 length=65536' \
	    "$artifacts/failure-single.stdout"; then
		pre15_runner_fail 'TC168 single corrupt assertion count was not 1'
	fi
	if ! pre15_guest_ssh_bounded /root/B32-cache-probe concurrent \
	    "$B32_MOUNT/payload.bin" "$reference" 97 64 2 0 65536 \
	    >"$artifacts/failure-concurrent.stdout" \
	    2>"$artifacts/failure-concurrent.stderr"; then
		pre15_dut_fail 'TC168 failure waiters did not receive EINTEGRITY'
	fi
	if ! grep -qx 'concurrent PASS workers=64 loops=2 assertions=128 errno=97 offset=0 length=65536' \
	    "$artifacts/failure-concurrent.stdout"; then
		pre15_runner_fail 'TC168 failure assertion count was not exactly 128'
	fi
	b32_detach
else
	b32_attach lzma-valid.erofs normal-busy
	b32_start_lifecycle normal-busy
	if pre15_guest_ssh_bounded umount "$B32_MOUNT" \
	    >"$artifacts/normal-unmount.stdout" 2>"$artifacts/normal-unmount.stderr"; then
		pre15_dut_fail 'TC184 normal unmount with held vnode unexpectedly succeeded'
	fi
	if ! grep -qi 'busy' "$artifacts/normal-unmount.stderr"; then
		pre15_dut_fail 'TC184 normal unmount did not report EBUSY'
	fi
	b32_stop_lifecycle
	b32_detach

	b32_attach lzma-valid.erofs forced-held
	b32_start_lifecycle forced-held
	pre15_guest_ssh_bounded umount -f "$B32_MOUNT" || \
		pre15_dut_fail 'TC184 held-vnode forced unmount command failed'
	b32_unown guest-mount "$B32_MOUNT"
	pre15_guest_ssh_bounded touch "$B32_CONTROL/read"
	pre15_guest_ssh_bounded \
	    "i=0; while test ! -f '$B32_CONTROL/result'; do i=\$((i + 1)); test \$i -lt 600 || exit 1; sleep 0.1; done" || \
		pre15_infra_blocked 'TC184 held-vnode result timed out'
	pre15_guest_ssh_bounded cat "$B32_CONTROL/result" \
	    >"$artifacts/forced-held-result.txt"
	if ! grep -qx -- '-1 6' "$artifacts/forced-held-result.txt"; then
		pre15_dut_fail 'TC184 revoked held vnode did not return exact ENXIO'
	fi
	b32_detach

	b32_attach lzma-valid.erofs forced-closed
	b32_start_lifecycle forced-closed
	pre15_guest_ssh_bounded touch "$B32_CONTROL/close"
	pre15_guest_ssh_bounded \
	    "i=0; while test ! -f '$B32_CONTROL/closed'; do i=\$((i + 1)); test \$i -lt 600 || exit 1; sleep 0.1; done" || \
		pre15_infra_blocked 'TC184 closed-descriptor marker timed out'
	pre15_guest_ssh_bounded umount -f "$B32_MOUNT" || \
		pre15_dut_fail 'TC184 closed-descriptor forced unmount command failed'
	b32_unown guest-mount "$B32_MOUNT"
	pre15_guest_ssh_bounded touch "$B32_CONTROL/read"
	pre15_guest_ssh_bounded \
	    "i=0; while test ! -f '$B32_CONTROL/result'; do i=\$((i + 1)); test \$i -lt 600 || exit 1; sleep 0.1; done" || \
		pre15_infra_blocked 'TC184 closed-descriptor result timed out'
	pre15_guest_ssh_bounded cat "$B32_CONTROL/result" \
	    >"$artifacts/forced-closed-result.txt"
	if ! grep -qx -- '-1 9' "$artifacts/forced-closed-result.txt"; then
		pre15_dut_fail 'TC184 closed descriptor did not return exact EBADF'
	fi
	b32_detach
fi

pre15_guest_ssh_bounded dmesg >"$artifacts/guest-dmesg-after.txt"
diff -u "$artifacts/guest-dmesg-before.txt" \
    "$artifacts/guest-dmesg-after.txt" >"$artifacts/guest-dmesg.diff" || true
sed -n '/^+++ /d; /^+/s/^+//p' "$artifacts/guest-dmesg.diff" \
    >"$artifacts/guest-dmesg-added.txt"
if grep -Eqi 'panic:|lock order reversal|witness.*warning|use-after-free|fatal trap|pager fault|linker.*(error|undefined)|undefined symbol' \
    "$artifacts/guest-dmesg-added.txt"; then
	pre15_dut_fail "$scenario produced kernel, linker, WITNESS, or UAF evidence"
fi
printf '%s PASS\n' "$scenario"
printf '%s\n' \
	'QEMU used an owned overlay and dynamic forwarded port' \
	'KLD erofs.1 ownership and cache tunable restoration PASS' \
	'TC130 and full feature suite NOT_RUN'
