#!/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"

case_script=$PRE15_DUT/tests/pre15/cases/B29-context-pool.sh
kld_builder=$PRE15_DUT/tests/pre15/fixtures/B28-build-kld.sh
old_gate=$PRE15_DUT/tests/pre15/gates/P15-076.sh
old_input=$PRE15_DUT/tests/pre15/gates/P15-076-input.json
old_evidence=$PRE15_ROOT/planning/pre15/evidence/20260815T171051Z-G05-P15-076
checkpoint=$PRE15_ROOT/planning/pre15/evidence/20260817T153426Z-B29/checkpoint.md
artifacts=$PRE15_RUN_DIR/artifacts
fixtures=$PRE15_CASE_TMP/fixtures
action=${1:-runtime}

case "$action" in
runtime|k2) ;;
*) pre15_fail_usage "unknown B29 action: $action" ;;
esac

for tool in awk cc cmp file fsck.erofs git mkfs.erofs nm python3 sha256sum \
    tar timeout; do
	command -v "$tool" >/dev/null 2>&1 || \
		pre15_infra_blocked "missing B29 tool: $tool"
done
for fixture in "$case_script" "$kld_builder" "$old_gate" "$old_input" \
    "$old_evidence/gate-output/benchmark-results.json" \
    "$old_evidence/gate-output/state-model-result.json" \
    "$old_evidence/VERDICT.md" "$checkpoint"; do
	pre15_record_fixture "b29-$(basename "$fixture")" "$fixture"
done
for source in internal.h super.c decompressor_lzma.c decompressor_deflate.c \
    decompressor_zstd.c; do
	pre15_record_fixture "b29-$source" "$PRE15_DUT/src/$source"
done
for source in decompressor_lzma.c decompressor_deflate.c \
    decompressor_zstd.c; do
	pre15_record_fixture "b29-linux-$source" "$PRE15_ROOT/src-linux/$source"
done
mkdir -p "$artifacts"

if test "$action" = k2; then
	for config in 0 1; do
		module=$PRE15_CASE_TMP/B29-zstdio$config.ko
		if ! timeout -k 10 600 /bin/sh "$kld_builder" "$PRE15_DUT" \
		    "$PRE15_FREEBSD_SRC" "$module" \
		    "$PRE15_CASE_TMP/kld-work-$config" "$config" \
		    >"$artifacts/B29-build-zstdio$config.stdout" \
		    2>"$artifacts/B29-build-zstdio$config.stderr"; then
			pre15_dut_fail "B29 zstdio$config exact-ABI KLD build failed"
		fi
		file "$module" >"$artifacts/B29-zstdio$config-file.txt"
		sha256sum "$module" >"$artifacts/B29-zstdio$config-sha256.txt"
		nm -g "$module" | LC_ALL=C sort \
		    >"$artifacts/B29-zstdio$config-nm-global.txt"
		nm -u "$module" | LC_ALL=C sort \
		    >"$artifacts/B29-zstdio$config-nm-u.txt"
	done
	awk '$1 != "U" { print $NF }' "$artifacts/B29-zstdio0-nm-global.txt" \
	    >"$PRE15_CASE_TMP/B29-zstdio0-global-names.txt"
	awk '$1 != "U" { print $NF }' "$artifacts/B29-zstdio1-nm-global.txt" \
	    >"$PRE15_CASE_TMP/B29-zstdio1-global-names.txt"
	if ! diff -u "$PRE15_CASE_TMP/B29-zstdio0-global-names.txt" \
	    "$PRE15_CASE_TMP/B29-zstdio1-global-names.txt" \
	    >"$artifacts/B29-config-nm-global.diff"; then
		pre15_dut_fail 'B29 defined globals differ by ZSTDIO configuration'
	fi
	awk '$NF ~ /^ZSTD_/ { print $NF }' "$artifacts/B29-zstdio1-nm-u.txt" \
	    >"$PRE15_CASE_TMP/B29-zstd-symbols.txt"
	printf '%s\n' ZSTD_DCtx_setParameter ZSTD_createDCtx_advanced \
	    ZSTD_decompressStream ZSTD_freeDCtx ZSTD_isError \
	    >"$PRE15_CASE_TMP/B29-zstd-expected.txt"
	if ! cmp "$PRE15_CASE_TMP/B29-zstd-expected.txt" \
	    "$PRE15_CASE_TMP/B29-zstd-symbols.txt"; then
		pre15_dut_fail 'B29 changed the accepted five-symbol Zstd provider ABI'
	fi
	pre15_target_reached
	printf '%s\n' \
		'B29 FreeBSD 15 exact-ABI K2 PASS' \
		'zstdio0/zstdio1 compile, link, global-symbol parity, and five-symbol optional Zstd ABI PASS'
	exit 0
fi

if ! python3 -B - "$PRE15_ROOT" "$PRE15_DUT" "$PRE15_FREEBSD_SRC" \
    "$old_evidence" "$artifacts/B29-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])
freebsd = Path(sys.argv[3])
evidence = Path(sys.argv[4])
report_path = Path(sys.argv[5])


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


current = {
    name: (dut / "src" / name).read_text(encoding="utf-8")
    for name in (
        "internal.h",
        "super.c",
        "decompressor_lzma.c",
        "decompressor_deflate.c",
        "decompressor_zstd.c",
    )
}
linux = {
    name: (root / "src-linux" / name).read_text(encoding="utf-8")
    for name in (
        "decompressor_lzma.c",
        "decompressor_deflate.c",
        "decompressor_zstd.c",
    )
}
benchmark = json.loads(
    (evidence / "gate-output/benchmark-results.json").read_text(encoding="ascii")
)
old_state = json.loads(
    (evidence / "gate-output/state-model-result.json").read_text(encoding="ascii")
)
old_verdict = (evidence / "VERDICT.md").read_text(encoding="utf-8")

assertions = 0


def require(condition: bool, message: str) -> None:
    global assertions
    assertions += 1
    if not condition:
        fail(message)


for codec, record in benchmark.items():
    require(record["baseline_context_allocator_share_percent"] >= 10.0,
            f"{codec} old allocator-share benefit regressed")
    require(record["allocator_event_reduction_percent"] >= 25.0,
            f"{codec} old lifecycle benefit regressed")
require(old_state["pool_exhaustion_fallback"] == "fresh baseline allocation",
        "historical unbounded fallback record changed")
require("global-exhaustion scenario" in old_verdict,
        "historical global-exhaustion rejection is no longer explicit")
require("maximum window log 16" in old_verdict and "8 MiB" in old_verdict,
        "historical maximum-parameter gap is no longer explicit")

super_source = current["super.c"]
hard_caps = {
    "EROFS_STREAM_MOUNT_HARD_BUDGET": "32UL * 1024 * 1024",
    "EROFS_STREAM_GLOBAL_HARD_BUDGET": "128UL * 1024 * 1024",
    "EROFS_STREAM_ALLOCATION_HARD_MAX": "4UL * 1024 * 1024",
    "EROFS_STREAM_MOUNT_HARD_CONTEXTS": "16U",
    "EROFS_STREAM_GLOBAL_HARD_CONTEXTS": "64U",
    "EROFS_STREAM_MOUNT_CACHED_PER_CODEC": "2U",
    "EROFS_STREAM_GLOBAL_CACHED_PER_CODEC": "16U",
}
for name, value in hard_caps.items():
    require(f"#define {name} ({value})" in super_source,
            f"missing production hard cap {name}")
for token in (
    'TUNABLE_ULONG("vfs.erofs.stream_pool.mount_budget"',
    'TUNABLE_ULONG("vfs.erofs.stream_pool.global_budget"',
    'TUNABLE_INT("vfs.erofs.stream_pool.global_contexts"',
    "MIN(erofs_stream_mount_budget,",
    "MIN(erofs_stream_global_budget,",
    "uma_zone_set_max(erofs_stream_zone,",
    "erofs_stream_reserve_locked(sbi, EROFS_STREAM_CTX_WRAPPER_SIZE)",
    "++erofs_stream_fallbacks",
    "++erofs_stream_exhaustions",
    "EVENTHANDLER_REGISTER(vm_lowmem",
    "while (!erofs_stream_pool_empty_locked(&sbi->stream_pool))",
    "KASSERT(erofs_stream_global_contexts == 0",
):
    require(token in super_source, f"missing bounded pool token: {token}")
require(super_source.find("erofs_stream_reserve_locked(sbi, EROFS_STREAM_CTX_WRAPPER_SIZE)") <
        super_source.find("uma_zalloc(erofs_stream_zone"),
        "wrapper allocation occurs before hard-budget reservation")
require("fresh baseline allocation" not in super_source,
        "production retained the historical unbounded fallback")

internal = current["internal.h"]
for field in ("resident_bytes", "contexts", "cached", "borrowed", "idle_count",
              "closing", "stream_pool_initialized"):
    require(re.search(rf"\b{field}\b", internal) is not None,
            f"pool state omits {field}")

lzma = current["decompressor_lzma.c"]
deflate = current["decompressor_deflate.c"]
zstd = current["decompressor_zstd.c"]
case_source = (dut / "tests/pre15/cases/B29-context-pool.sh").read_text(
    encoding="utf-8"
)
require("xz_dec_microlzma_alloc(XZ_SINGLE" in lzma,
        "FreeBSD LZMA no longer uses the bounded XZ_SINGLE mode")
require("ctx->state_bytes = sizeof(*ctx->state);" in lzma,
        "LZMA fixed state is not charged exactly")
require("dict_size > Z_EROFS_LZMA_MAX_DICT_SIZE" in lzma,
        "LZMA 8 MiB format hard limit is absent")
require("M_WAITOK" not in lzma,
        "LZMA backend still has an unbounded waiting allocation")
require("z_erofs_stream_ctx_alloc(pool, bytes)" in deflate,
        "Deflate backend allocations bypass hard accounting")
require("inflateReset2(strm" in deflate,
        "Deflate reusable context is not reset")
require("z_erofs_stream_ctx_alloc(opaque, size)" in zstd,
        "Zstd backend allocations bypass hard accounting")
require("zstd->windowlog > 10" in zstd and
        "rq->sbi->zstd_windowlog + 10 > 20" in zstd,
        "Zstd maximum window parameter is not enforced")
require("ZSTD_DCtx_reset" not in zstd,
        "B29 added a new Zstd provider ABI symbol")
require("error == 0 && !rq->partial_decoding" in zstd,
        "partial Zstd state is incorrectly cached without reset")
require('"$B29_BAD_MOUNT" 45' in case_source,
        "B29 runtime uses the Linux EOPNOTSUPP value instead of FreeBSD 45")
require("run_exhaustion_phase lzma byte" in case_source and
        "run_exhaustion_phase zstd byte" in case_source and
        "run_exhaustion_phase lzma count" in case_source,
        "B29 runtime omits a production LZMA/Zstd byte or global count gate")
for source in (deflate, zstd):
    require("M_WAITOK" not in source,
            "codec backend retained an unbounded waiting allocation")

xz_source = (freebsd / "sys/contrib/xz-embedded/linux/lib/xz/xz_dec_lzma2.c").read_text(
    encoding="utf-8"
)
xz_private = (freebsd / "sys/contrib/xz-embedded/linux/lib/xz/xz_private.h").read_text(
    encoding="utf-8"
)
require("if (DEC_IS_MULTI(mode))" in xz_source and
        "s->s.dict.buf = vmalloc(dict_size);" in xz_source,
        "FreeBSD MicroLZMA allocation branch changed")
require("#\tdefine DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE)" in xz_private,
        "FreeBSD XZ_SINGLE dictionary-allocation proof changed")

for codec, tokens in {
    "lzma": ("get an available lzma context", "push back LZMA stream context"),
    "deflate": ("get an available DEFLATE context", "push back DEFLATE stream context"),
    "zstd": ("get an available ZSTD context", "push back ZSTD stream context"),
}.items():
    for token in tokens:
        require(token in linux[f"decompressor_{codec}.c"],
                f"Linux reusable-stream intent changed for {codec}")

status = subprocess.run(
    ["git", "-C", str(root), "status", "--porcelain"],
    check=True, text=True, stdout=subprocess.PIPE,
).stdout.splitlines()
allowed_implementation = {
    "repo-pre-15/src/decompressor_lzma.c",
    "repo-pre-15/src/decompressor_deflate.c",
    "repo-pre-15/src/decompressor_zstd.c",
    "repo-pre-15/src/internal.h",
    "repo-pre-15/src/super.c",
    "repo-pre-15/tests/pre15/cases/B29-context-pool.sh",
}
for line in status:
    path = line[3:]
    require(path in allowed_implementation or path.startswith("planning/pre15/evidence/20260817T153426Z-B29/") or
            path in {
                "planning/pre15/execution-status.md",
                "planning/pre15/final-review/closure.tsv",
                "planning/pre15/final-review/completion-audit.md",
                "planning/pre15/final-review/completion-audit.tsv",
            }, f"B29 dirty path escapes allowed scope: {path}")

report = {
    "assertions": assertions,
    "batch": "B29",
    "benefit_gate_retained": True,
    "freebsd_difference": {
        "allocator": "UMA wrapper plus exact mount/global byte charging",
        "lzma": "XZ_SINGLE fixed state; no dictionary buffer allocation",
        "locking": "one FreeBSD mutex, no Linux waitqueue/per-CPU API copy",
    },
    "global_exhaustion_required": True,
    "hard_caps": hard_caps,
    "linux_reusable_stream_intent": True,
    "status": "PASS",
}
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_dut_fail 'B29 benefit/source/hard-cap audit failed'
fi

cat >"$PRE15_CASE_TMP/B29-pool-oracle.c" <<'EOF'
#include <errno.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define WRAPPER_BYTES 256U
#define MOUNT_CACHED 2U
#define GLOBAL_CACHED 16U

struct budget {
	pthread_mutex_t lock;
	pthread_cond_t cv;
	size_t bytes;
	size_t byte_limit;
	unsigned contexts;
	unsigned context_limit;
	unsigned cached;
	unsigned borrowed;
	unsigned exhaustions;
};

struct mount_pool {
	struct budget *global;
	size_t bytes;
	size_t byte_limit;
	unsigned contexts;
	unsigned context_limit;
	unsigned cached;
	unsigned borrowed;
	unsigned idle;
	bool closing;
};

struct context {
	struct mount_pool *mount;
	bool cached;
};

static unsigned assertions;

static void
check(bool condition, const char *message)
{
	++assertions;
	if (!condition) {
		fprintf(stderr, "B29 oracle failure: %s\n", message);
		exit(1);
	}
}

static void
budget_init(struct budget *global, size_t bytes, unsigned contexts)
{
	memset(global, 0, sizeof(*global));
	pthread_mutex_init(&global->lock, NULL);
	pthread_cond_init(&global->cv, NULL);
	global->byte_limit = bytes;
	global->context_limit = contexts;
}

static void
budget_fini(struct budget *global)
{
	pthread_cond_destroy(&global->cv);
	pthread_mutex_destroy(&global->lock);
}

static void
mount_init(struct mount_pool *mount, struct budget *global, size_t bytes,
    unsigned contexts)
{
	memset(mount, 0, sizeof(*mount));
	mount->global = global;
	mount->byte_limit = bytes;
	mount->context_limit = contexts;
}

static int
acquire(struct mount_pool *mount, bool inject_failure, struct context **out)
{
	struct budget *global = mount->global;
	struct context *ctx;
	bool cached;

	*out = NULL;
	pthread_mutex_lock(&global->lock);
	if (mount->closing) {
		pthread_mutex_unlock(&global->lock);
		return ENXIO;
	}
	if (mount->idle != 0) {
		--mount->idle;
		++mount->borrowed;
		++global->borrowed;
		pthread_mutex_unlock(&global->lock);
		ctx = calloc(1, sizeof(*ctx));
		check(ctx != NULL, "idle wrapper allocation");
		ctx->mount = mount;
		ctx->cached = true;
		*out = ctx;
		return 0;
	}
	cached = mount->cached < MOUNT_CACHED && global->cached < GLOBAL_CACHED;
	if (mount->contexts >= mount->context_limit ||
	    global->contexts >= global->context_limit ||
	    mount->bytes > mount->byte_limit - WRAPPER_BYTES ||
	    global->bytes > global->byte_limit - WRAPPER_BYTES) {
		++global->exhaustions;
		pthread_mutex_unlock(&global->lock);
		return ENOMEM;
	}
	mount->bytes += WRAPPER_BYTES;
	global->bytes += WRAPPER_BYTES;
	++mount->contexts;
	++global->contexts;
	++mount->borrowed;
	++global->borrowed;
	if (cached) {
		++mount->cached;
		++global->cached;
	}
	pthread_mutex_unlock(&global->lock);
	if (inject_failure) {
		pthread_mutex_lock(&global->lock);
		mount->bytes -= WRAPPER_BYTES;
		global->bytes -= WRAPPER_BYTES;
		--mount->contexts;
		--global->contexts;
		--mount->borrowed;
		--global->borrowed;
		if (cached) {
			--mount->cached;
			--global->cached;
		}
		pthread_cond_broadcast(&global->cv);
		pthread_mutex_unlock(&global->lock);
		return ENOMEM;
	}
	ctx = calloc(1, sizeof(*ctx));
	check(ctx != NULL, "new wrapper allocation");
	ctx->mount = mount;
	ctx->cached = cached;
	*out = ctx;
	return 0;
}

static void
release(struct context *ctx, bool reusable)
{
	struct mount_pool *mount = ctx->mount;
	struct budget *global = mount->global;

	pthread_mutex_lock(&global->lock);
	if (reusable && ctx->cached && !mount->closing) {
		--mount->borrowed;
		--global->borrowed;
		++mount->idle;
		pthread_cond_broadcast(&global->cv);
		pthread_mutex_unlock(&global->lock);
		free(ctx);
		return;
	}
	--mount->borrowed;
	--global->borrowed;
	--mount->contexts;
	--global->contexts;
	mount->bytes -= WRAPPER_BYTES;
	global->bytes -= WRAPPER_BYTES;
	if (ctx->cached) {
		--mount->cached;
		--global->cached;
	}
	pthread_cond_broadcast(&global->cv);
	pthread_mutex_unlock(&global->lock);
	free(ctx);
}

static void
drain(struct mount_pool *mount)
{
	struct budget *global = mount->global;

	pthread_mutex_lock(&global->lock);
	mount->closing = true;
	while (mount->idle != 0) {
		--mount->idle;
		--mount->contexts;
		--global->contexts;
		--mount->cached;
		--global->cached;
		mount->bytes -= WRAPPER_BYTES;
		global->bytes -= WRAPPER_BYTES;
	}
	while (mount->contexts != 0)
		pthread_cond_wait(&global->cv, &global->lock);
	pthread_mutex_unlock(&global->lock);
}

struct acquire_result {
	struct mount_pool *mount;
	int error;
};

static void *
acquire_worker(void *opaque)
{
	struct acquire_result *result = opaque;
	struct context *ctx;

	result->error = acquire(result->mount, false, &ctx);
	if (result->error == 0)
		release(ctx, false);
	return NULL;
}

struct delayed_release {
	struct context *ctx;
};

static void *
release_worker(void *opaque)
{
	struct delayed_release *delayed = opaque;
	struct timespec delay = { .tv_nsec = 50000000 };

	nanosleep(&delay, NULL);
	release(delayed->ctx, true);
	return NULL;
}

int
main(void)
{
	struct acquire_result results[2];
	struct delayed_release delayed;
	struct mount_pool a, b, c;
	struct budget global;
	struct context *one, *two, *three;
	pthread_t threads[2];
	unsigned before;

	budget_init(&global, 4096, 8);
	mount_init(&a, &global, 4096, 8);
	check(acquire(&a, false, &one) == 0, "first cached acquire");
	release(one, true);
	check(a.idle == 1 && a.cached == 1 && global.contexts == 1,
	    "first release did not cache");
	check(acquire(&a, false, &one) == 0, "idle reuse acquire");
	check(a.idle == 0 && a.borrowed == 1, "idle reuse counters");
	release(one, true);
	drain(&a);
	check(global.contexts == 0 && global.bytes == 0, "reuse drain leaked");
	budget_fini(&global);

	budget_init(&global, 4096, 8);
	mount_init(&a, &global, 4096, 8);
	check(acquire(&a, false, &one) == 0, "fallback first acquire");
	check(acquire(&a, false, &two) == 0, "fallback second acquire");
	check(acquire(&a, false, &three) == 0, "bounded fallback acquire");
	check(one->cached && two->cached && !three->cached,
	    "cache saturation did not select bounded temporary context");
	release(one, true);
	release(two, true);
	release(three, true);
	check(a.contexts == 2 && a.idle == 2 && global.contexts == 2,
	    "temporary fallback was retained or cached contexts were lost");
	drain(&a);
	check(global.contexts == 0 && global.bytes == 0, "fallback drain leaked");
	budget_fini(&global);

	budget_init(&global, 4096, 1);
	mount_init(&a, &global, 4096, 8);
	mount_init(&b, &global, 4096, 8);
	mount_init(&c, &global, 4096, 8);
	check(acquire(&a, false, &one) == 0, "global owner acquire");
	release(one, true);
	before = global.exhaustions;
	results[0].mount = &b;
	results[1].mount = &c;
	check(pthread_create(&threads[0], NULL, acquire_worker, &results[0]) == 0,
	    "first exhaustion worker create");
	check(pthread_create(&threads[1], NULL, acquire_worker, &results[1]) == 0,
	    "second exhaustion worker create");
	check(pthread_join(threads[0], NULL) == 0, "first exhaustion worker join");
	check(pthread_join(threads[1], NULL) == 0, "second exhaustion worker join");
	check(results[0].error == ENOMEM && results[1].error == ENOMEM,
	    "concurrent global exhaustion errno is unstable");
	check(global.exhaustions == before + 2 && global.contexts == 1 &&
	    global.bytes == WRAPPER_BYTES,
	    "global exhaustion changed retained ownership");
	drain(&a);
	check(acquire(&b, false, &one) == 0, "recovery acquire failed");
	release(one, true);
	drain(&b);
	drain(&c);
	check(global.contexts == 0 && global.bytes == 0 && global.borrowed == 0,
	    "global recovery leaked resources");
	budget_fini(&global);

	budget_init(&global, 511, 8);
	mount_init(&a, &global, 511, 8);
	mount_init(&b, &global, 511, 8);
	check(acquire(&a, false, &one) == 0, "byte owner acquire");
	check(acquire(&b, false, &two) == ENOMEM,
	    "global byte exhaustion did not return ENOMEM");
	release(one, false);
	check(acquire(&b, false, &two) == 0, "byte recovery acquire failed");
	release(two, false);
	drain(&a);
	drain(&b);
	check(global.bytes == 0 && global.contexts == 0, "byte recovery leaked");
	budget_fini(&global);

	budget_init(&global, 4096, 8);
	mount_init(&a, &global, 4096, 8);
	check(acquire(&a, true, &one) == ENOMEM,
	    "construction failure did not return ENOMEM");
	check(global.bytes == 0 && global.contexts == 0 && global.borrowed == 0,
	    "construction failure did not roll back reservations");
	drain(&a);
	budget_fini(&global);

	budget_init(&global, 4096, 8);
	mount_init(&a, &global, 4096, 8);
	check(acquire(&a, false, &one) == 0, "drain borrower acquire");
	delayed.ctx = one;
	check(pthread_create(&threads[0], NULL, release_worker, &delayed) == 0,
	    "drain release worker create");
	drain(&a);
	check(pthread_join(threads[0], NULL) == 0, "drain release worker join");
	check(global.bytes == 0 && global.contexts == 0 && global.borrowed == 0,
	    "unmount drain did not wait for borrower cleanup");
	budget_fini(&global);

	printf("B29 pool oracle PASS assertions=%u global-count global-bytes failure rollback recovery drain\n",
	    assertions);
	return 0;
}
EOF

if ! cc -std=c11 -Wall -Wextra -Werror -pthread \
    "$PRE15_CASE_TMP/B29-pool-oracle.c" -o "$PRE15_CASE_TMP/B29-pool-oracle" \
    >"$artifacts/B29-pool-oracle-build.stdout" \
    2>"$artifacts/B29-pool-oracle-build.stderr" || \
    ! "$PRE15_CASE_TMP/B29-pool-oracle" \
    >"$artifacts/B29-pool-oracle.stdout" \
    2>"$artifacts/B29-pool-oracle.stderr"; then
	pre15_dut_fail 'B29 concurrent pool accounting oracle failed'
fi

if ! python3 -B - "$fixtures" "$artifacts/B29-fixture-index.json" <<'PY'
from __future__ import annotations

import hashlib
import json
from pathlib import Path
import shutil
import struct
import subprocess
import sys


output = Path(sys.argv[1])
index_path = Path(sys.argv[2])
source = output / "source"
images = output / "images"
output.mkdir(parents=True)
source.mkdir()
images.mkdir()


def run(argv: list[str]) -> None:
    completed = subprocess.run(
        argv, check=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
        text=True, timeout=120,
    )
    if completed.returncode != 0:
        raise SystemExit(f"command failed ({completed.returncode}): {' '.join(argv)}\n{completed.stdout}")


block = bytes((index * 29 + 17) & 0xFF for index in range(4096))
for index in range(3):
    payload = (block[index * 257 :] + block[: index * 257]) * 64
    (source / f"payload-{index}.bin").write_bytes(payload)

specs = {
    "lzma": "-zlzma,level=6,dictsize=65536",
    "lzma-max": "-zlzma,level=6,dictsize=8388608",
    "deflate": "-zdeflate,level=1,dictsize=32768",
    "zstd": "-zzstd,level=3,dictsize=65536",
    "zstd-max": "-zzstd,level=3,dictsize=1048576",
}
records = []
for name, option in specs.items():
    image = images / f"{name}.erofs"
    run([
        "mkfs.erofs", "-T0", "--all-time", "--force-uid=0", "--force-gid=0",
        "-C65536", "--max-extent-bytes=1048576", option, str(image), str(source),
    ])
    run(["fsck.erofs", "--extract=" + str(output / (name + "-extract")), str(image)])
    for index in range(3):
        actual = output / (name + "-extract") / f"payload-{index}.bin"
        if actual.read_bytes() != (source / f"payload-{index}.bin").read_bytes():
            raise SystemExit(f"{name} extraction mismatch for payload-{index}")
    records.append({"name": name, "sha256": hashlib.sha256(image.read_bytes()).hexdigest()})


def crc32c(data: bytes | bytearray, seed: int = 0xFFFFFFFF) -> int:
    value = seed
    polynomial = 0x82F63B78
    for byte in data:
        value ^= byte
        for _ in range(8):
            value = (value >> 1) ^ (polynomial if value & 1 else 0)
    return value & 0xFFFFFFFF


def mutate(source_image: Path, target: int, mutate) -> Path:
    data = bytearray(source_image.read_bytes())
    super_offset = 1024
    block_size = 1 << data[super_offset + 12]
    sb_size = 128 + data[super_offset + 13] * 16
    algorithms = struct.unpack_from("<H", data, super_offset + 84)[0]
    offset = super_offset + sb_size
    found = False
    for algorithm in range(4):
        if algorithms & (1 << algorithm) == 0:
            continue
        offset = (offset + 3) & ~3
        length = struct.unpack_from("<H", data, offset)[0]
        size = length if length else 65536
        payload = offset + 2
        if algorithm == target:
            mutate(data, payload, size)
            found = True
            break
        offset = payload + size
    if not found:
        raise SystemExit(f"algorithm {target} config not found in {source_image}")
    feature_compat = struct.unpack_from("<I", data, super_offset + 8)[0]
    if feature_compat & 1:
        checksum_size = block_size
        if checksum_size > super_offset:
            checksum_size -= super_offset
        checksum = crc32c(
            data[super_offset + 8 : super_offset + checksum_size],
            seed=0x5045B54A,
        )
        struct.pack_into("<I", data, super_offset + 4, checksum)
    destination = images / (source_image.stem + "-over.erofs")
    destination.write_bytes(data)
    return destination


lzma_over = mutate(images / "lzma-max.erofs", 1,
                   lambda data, offset, size: struct.pack_into("<I", data, offset, 8388609))
zstd_over = mutate(images / "zstd-max.erofs", 3,
                   lambda data, offset, size: data.__setitem__(offset + 1, 11))
records.extend([
    {"name": "lzma-over", "sha256": hashlib.sha256(lzma_over.read_bytes()).hexdigest()},
    {"name": "zstd-over", "sha256": hashlib.sha256(zstd_over.read_bytes()).hexdigest()},
])

for extracted in output.glob("*-extract"):
    shutil.rmtree(extracted)

index_path.write_text(json.dumps({
    "files_per_image": 3,
    "images": records,
    "lzma_max_dict": 8388608,
    "schema": 1,
    "zstd_max_window": 1048576,
}, indent=2, sort_keys=True) + "\n", encoding="ascii")
print(json.dumps({"images": len(records), "status": "PASS"}, sort_keys=True))
PY
then
	pre15_runner_fail 'B29 real EROFS fixture generation failed'
fi

sha256sum "$artifacts/B29-source-audit.json" \
    "$artifacts/B29-fixture-index.json" \
    "$artifacts/B29-pool-oracle.stdout" >"$artifacts/SHA256SUMS"

if test "${PRE15_QEMU_TARGET_ONLY:-0}" = 1; then
	pre15_target_reached
elif test "${PRE15_MODE:-host}" != qemu; then
	pre15_target_reached
	printf '%s\n' \
		'B29 focused host/static gate PASS' \
		"$(cat "$artifacts/B29-pool-oracle.stdout")" \
		'Old G05 benefit retained; production hard caps, bounded fallback, true global count/byte exhaustion model, rollback, drain, and recovery PASS' \
		'QEMU runtime NOT_RUN in host mode; full feature suite NOT_RUN'
	exit 0
fi

for tool in scp ssh; do
	command -v "$tool" >/dev/null 2>&1 || \
		pre15_infra_blocked "missing B29 QEMU tool: $tool"
done
: "${PRE15_QEMU_CONTROL_PATH:?QEMU control path is required}"
: "${PRE15_QEMU_SSH_PORT:?QEMU SSH port is required}"
: "${PRE15_QEMU_SSH_USER:?QEMU SSH user is required}"

module=$PRE15_CASE_TMP/B29-erofs-zstdio1.ko
fixture_archive=$PRE15_CASE_TMP/B29-fixtures.tar.gz
if ! /bin/sh "$kld_builder" "$PRE15_DUT" "$PRE15_FREEBSD_SRC" "$module" \
    "$PRE15_CASE_TMP/kld-work-qemu" 1 >"$artifacts/B29-kld-build.stdout" \
    2>"$artifacts/B29-kld-build.stderr"; then
	pre15_dut_fail 'B29 exact-ABI zstdio1 QEMU KLD build failed'
fi
pre15_record_module "$module"
file "$module" >"$artifacts/B29-kld-file.txt"
sha256sum "$module" >"$artifacts/B29-kld-sha256.txt"
nm -u "$module" | LC_ALL=C sort >"$artifacts/B29-kld-nm-u.txt"
tar -C "$fixtures" -czf "$fixture_archive" images source

cat >"$PRE15_CASE_TMP/B29-read-probe.c" <<'EOF'
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static int
read_all(const char *path, unsigned char **datap, size_t *sizep, int *errorp)
{
	unsigned char *data;
	size_t capacity, size;
	ssize_t count;
	int fd;

	fd = open(path, O_RDONLY);
	if (fd < 0) {
		*errorp = errno;
		return (-1);
	}
	capacity = 1024 * 1024;
	data = malloc(capacity);
	if (data == NULL)
		return (-1);
	size = 0;
	while ((count = read(fd, data + size, capacity - size)) > 0) {
		size += (size_t)count;
		if (size == capacity)
			break;
	}
	if (count < 0) {
		*errorp = errno;
		close(fd);
		free(data);
		return (-1);
	}
	close(fd);
	*datap = data;
	*sizep = size;
	*errorp = 0;
	return (0);
}

int
main(int argc, char **argv)
{
	unsigned char *actual, *expected;
	size_t actual_size, expected_size;
	int actual_error, expected_error;

	if (argc == 4 && strcmp(argv[1], "pass") == 0) {
		if (read_all(argv[2], &actual, &actual_size, &actual_error) != 0 ||
		    read_all(argv[3], &expected, &expected_size, &expected_error) != 0)
			return (1);
		if (actual_size != expected_size ||
		    memcmp(actual, expected, actual_size) != 0)
			return (1);
		free(actual);
		free(expected);
		return (0);
	}
	if (argc == 4 && strcmp(argv[1], "errno") == 0) {
		expected_error = atoi(argv[3]);
		if (read_all(argv[2], &actual, &actual_size, &actual_error) == 0) {
			free(actual);
			return (1);
		}
		return (actual_error == expected_error ? 0 : 1);
	}
	fprintf(stderr, "usage: B29-read-probe pass ACTUAL EXPECTED | errno PATH ERRNO\n");
	return (2);
}
EOF

cat >"$PRE15_CASE_TMP/B29-mount-probe.c" <<'EOF'
#include <sys/mount.h>
#include <sys/uio.h>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void
add_iovec(struct iovec **iov, int *iovlen, const char *name, const char *value)
{
	*iov = realloc(*iov, sizeof(**iov) * (size_t)(*iovlen + 2));
	(*iov)[*iovlen].iov_base = __DECONST(char *, name);
	(*iov)[*iovlen].iov_len = strlen(name) + 1;
	++*iovlen;
	(*iov)[*iovlen].iov_base = __DECONST(char *, value);
	(*iov)[*iovlen].iov_len = strlen(value) + 1;
	++*iovlen;
}

int
main(int argc, char **argv)
{
	struct iovec *iov;
	int error, expected, iovlen;

	if (argc != 4)
		return (2);
	expected = atoi(argv[3]);
	iov = NULL;
	iovlen = 0;
	add_iovec(&iov, &iovlen, "fstype", "erofs");
	add_iovec(&iov, &iovlen, "fspath", argv[2]);
	add_iovec(&iov, &iovlen, "from", argv[1]);
	if (nmount(iov, (unsigned)iovlen, MNT_RDONLY) == 0) {
		unmount(argv[2], 0);
		free(iov);
		return (1);
	}
	error = errno;
	free(iov);
	return (error == expected ? 0 : 1);
}
EOF

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" \
		-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/B29-fixtures.tar.gz || \
    ! pre15_scp "$module" /root/B29-erofs-zstdio1.ko || \
    ! pre15_scp "$PRE15_CASE_TMP/B29-read-probe.c" /root/B29-read-probe.c || \
    ! pre15_scp "$PRE15_CASE_TMP/B29-mount-probe.c" /root/B29-mount-probe.c; then
	pre15_infra_blocked 'could not transfer B29 module, fixtures, or probes'
fi
if ! pre15_guest_ssh_bounded \
    'rm -rf /root/B29-fixtures && mkdir /root/B29-fixtures && tar -xzf /root/B29-fixtures.tar.gz -C /root/B29-fixtures && cc -O2 -Wall -Wextra -Werror -o /root/B29-read-probe /root/B29-read-probe.c && cc -O2 -Wall -Wextra -Werror -o /root/B29-mount-probe /root/B29-mount-probe.c'; then
	pre15_infra_blocked 'could not prepare B29 guest fixtures/probes'
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 \
    'kenv -u vfs.erofs.stream_pool.global_budget >/dev/null 2>&1 || true; kenv -u vfs.erofs.stream_pool.global_contexts >/dev/null 2>&1 || true; kenv -u vfs.erofs.stream_pool.global_cached_per_codec >/dev/null 2>&1 || true'
if ! pre15_guest_ssh_bounded kldload /root/B29-erofs-zstdio1.ko \
    >"$artifacts/B29-kldload.stdout" 2>"$artifacts/B29-kldload.stderr"; then
	if grep -Eq 'link_elf|symbol|not defined' "$artifacts/B29-kldload.stderr"; then
		pre15_infra_blocked 'guest lacks the exact B29 KLD provider ABI'
	fi
	pre15_dut_fail 'B29 exact-ABI KLD failed to load'
fi
pre15_own_guest_kld B29-erofs-zstdio1.ko 'B29 exact-source KLD'

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

drop_guest_ownership()
{
	kind=$1
	value=$2
	temporary=$PRE15_CASE_TMP/B29-ownership.$$
	awk -v kind="$kind" -v value="$value" \
	    'BEGIN { FS = "\\t" } !($1 == kind && $2 == value)' \
	    "$PRE15_OWNERSHIP_FILE" >"$temporary"
	mv "$temporary" "$PRE15_OWNERSHIP_FILE"
}

unmount_owned()
{
	mountpoint=$1
	label=$2
	pre15_guest_ssh_bounded umount "$mountpoint" || \
		pre15_dut_fail "$label unmount failed"
	drop_guest_ownership guest-mount "$mountpoint"
}

unload_owned_kld()
{
	label=$1
	if ! pre15_guest_ssh_bounded kldunload B29-erofs-zstdio1.ko; then
		pre15_dut_fail "$label module unload did not drain cleanly"
	fi
	drop_guest_ownership guest-kld B29-erofs-zstdio1.ko
}

clear_pool_tunables()
{
	pre15_guest_ssh_bounded \
	    'kenv -u vfs.erofs.stream_pool.global_budget >/dev/null 2>&1 || true; kenv -u vfs.erofs.stream_pool.global_contexts >/dev/null 2>&1 || true; kenv -u vfs.erofs.stream_pool.global_cached_per_codec >/dev/null 2>&1 || true'
}

run_exhaustion_phase()
{
	codec=$1
	limit_kind=$2
	limit_value=$3
	phase=$4
	clear_pool_tunables
	case "$limit_kind" in
	byte)
		pre15_guest_ssh_bounded kenv \
		    "vfs.erofs.stream_pool.global_budget=$limit_value"
		expected_budget=$limit_value
		expected_contexts=64
		;;
	count)
		pre15_guest_ssh_bounded kenv \
		    "vfs.erofs.stream_pool.global_contexts=$limit_value"
		expected_budget=134217728
		expected_contexts=$limit_value
		;;
	*) pre15_runner_fail "unknown B29 exhaustion limit: $limit_kind" ;;
	esac
	pre15_guest_ssh_bounded kenv \
	    vfs.erofs.stream_pool.global_cached_per_codec=1
	if ! pre15_guest_ssh_bounded kldload /root/B29-erofs-zstdio1.ko \
	    >"$artifacts/B29-kldload-$phase.stdout" \
	    2>"$artifacts/B29-kldload-$phase.stderr"; then
		pre15_dut_fail "B29 $phase constrained-limit KLD reload failed"
	fi
	pre15_own_guest_kld B29-erofs-zstdio1.ko "$phase exact-source KLD"
	pre15_guest_ssh_bounded \
	    'sysctl -n vfs.erofs.stream_pool.global_budget; sysctl -n vfs.erofs.stream_pool.global_contexts_limit' \
	    >"$artifacts/B29-$phase-effective-limits.txt"
	set -- $(cat "$artifacts/B29-$phase-effective-limits.txt")
	if test "$1" -ne "$expected_budget" || test "$2" -ne "$expected_contexts"; then
		pre15_dut_fail "B29 $phase production tunable did not reach its clamped limit"
	fi
	runtime_assertions=$((runtime_assertions + 2))

	B29_MOUNTS=
	attach_mount "$codec.erofs" "$phase-owner"
	B29_OWNER_MOUNT=$B29_MOUNT
	attach_mount "$codec.erofs" "$phase-b"
	B29_B_MOUNT=$B29_MOUNT
	attach_mount "$codec.erofs" "$phase-c"
	B29_C_MOUNT=$B29_MOUNT
	pre15_guest_ssh_bounded /root/B29-read-probe pass \
	    "$B29_OWNER_MOUNT/payload-0.bin" \
	    /root/B29-fixtures/source/payload-0.bin || \
		pre15_dut_fail "B29 $phase global exhaustion owner read failed"
	runtime_assertions=$((runtime_assertions + 1))

	pre15_guest_ssh_bounded \
	    'sysctl -n vfs.erofs.stream_pool.contexts; sysctl -n vfs.erofs.stream_pool.idle; sysctl -n vfs.erofs.stream_pool.borrowed; sysctl -n vfs.erofs.stream_pool.resident_bytes; sysctl -n vfs.erofs.stream_pool.exhaustions' \
	    >"$artifacts/B29-$phase-before-exhaustion.txt"
	set -- $(cat "$artifacts/B29-$phase-before-exhaustion.txt")
	before_contexts=$1
	before_idle=$2
	before_borrowed=$3
	before_resident=$4
	before_exhaustions=$5
	if test "$before_contexts" -ne 1 || test "$before_idle" -ne 1 || \
	    test "$before_borrowed" -ne 0 || test "$before_resident" -le 256; then
		pre15_dut_fail "B29 $phase owner did not retain one real idle context"
	fi
	if test "$limit_kind" = byte && \
	    test "$before_resident" -ne "$limit_value"; then
		pre15_dut_fail "B29 $phase did not consume the exact production byte budget"
	fi
	runtime_assertions=$((runtime_assertions + 4))

	if ! pre15_guest_ssh_bounded \
	    "/root/B29-read-probe errno '$B29_B_MOUNT/payload-1.bin' 12 & p1=\$!; /root/B29-read-probe errno '$B29_C_MOUNT/payload-2.bin' 12 & p2=\$!; wait \$p1; r1=\$?; wait \$p2; r2=\$?; test \$r1 -eq 0 -a \$r2 -eq 0"; then
		pre15_dut_fail "B29 $phase concurrent exhaustion errno was not stable ENOMEM"
	fi
	runtime_assertions=$((runtime_assertions + 2))
	pre15_guest_ssh_bounded \
	    'sysctl -n vfs.erofs.stream_pool.contexts; sysctl -n vfs.erofs.stream_pool.idle; sysctl -n vfs.erofs.stream_pool.borrowed; sysctl -n vfs.erofs.stream_pool.resident_bytes; sysctl -n vfs.erofs.stream_pool.exhaustions' \
	    >"$artifacts/B29-$phase-after-exhaustion.txt"
	set -- $(cat "$artifacts/B29-$phase-after-exhaustion.txt")
	if test "$1" -ne 1 || test "$2" -ne 1 || test "$3" -ne 0 || \
	    test "$4" -ne "$before_resident" || \
	    test "$5" -lt $((before_exhaustions + 2)); then
		pre15_dut_fail "B29 $phase exhaustion changed ownership or missed accounting"
	fi
	runtime_assertions=$((runtime_assertions + 5))

	unmount_owned "$B29_OWNER_MOUNT" "B29 $phase owner"
	pre15_guest_ssh_bounded \
	    'sysctl -n vfs.erofs.stream_pool.contexts; sysctl -n vfs.erofs.stream_pool.resident_bytes' \
	    >"$artifacts/B29-$phase-after-owner-unmount.txt"
	set -- $(cat "$artifacts/B29-$phase-after-owner-unmount.txt")
	if test "$1" -ne 0 || test "$2" -ne 0; then
		pre15_dut_fail "B29 $phase owner unmount did not release global resources"
	fi
	runtime_assertions=$((runtime_assertions + 2))
	pre15_guest_ssh_bounded /root/B29-read-probe pass \
	    "$B29_B_MOUNT/payload-1.bin" \
	    /root/B29-fixtures/source/payload-1.bin || \
		pre15_dut_fail "B29 $phase read did not recover after resource release"
	runtime_assertions=$((runtime_assertions + 1))

	unmount_owned "$B29_B_MOUNT" "B29 $phase recovery B"
	unmount_owned "$B29_C_MOUNT" "B29 $phase recovery C"
	pre15_guest_ssh_bounded \
	    'sysctl -n vfs.erofs.stream_pool.contexts; sysctl -n vfs.erofs.stream_pool.resident_bytes; sysctl -n vfs.erofs.stream_pool.borrowed; sysctl -n vfs.erofs.stream_pool.idle' \
	    >"$artifacts/B29-$phase-final.txt"
	set -- $(cat "$artifacts/B29-$phase-final.txt")
	if test "$1" -ne 0 || test "$2" -ne 0 || test "$3" -ne 0 || \
	    test "$4" -ne 0; then
		pre15_dut_fail "B29 $phase final pool state is not fully drained"
	fi
	runtime_assertions=$((runtime_assertions + 4))
	unload_owned_kld "B29 $phase final"
	runtime_assertions=$((runtime_assertions + 1))
}

runtime_assertions=0
B29_LZMA_CONTEXT_BYTES=
B29_ZSTD_CONTEXT_BYTES=
B29_MOUNTS=
for codec in lzma deflate zstd; do
	attach_mount "$codec.erofs" "$codec"
	for index in 0 1 2; do
		pre15_guest_ssh_bounded /root/B29-read-probe pass \
		    "$B29_MOUNT/payload-$index.bin" \
		    "/root/B29-fixtures/source/payload-$index.bin" || \
			pre15_dut_fail "$codec ordinary payload-$index read failed"
		runtime_assertions=$((runtime_assertions + 1))
	done
	case "$codec" in
	lzma|zstd)
		pre15_guest_ssh_bounded \
		    'sysctl -n vfs.erofs.stream_pool.contexts; sysctl -n vfs.erofs.stream_pool.idle; sysctl -n vfs.erofs.stream_pool.resident_bytes' \
		    >"$artifacts/B29-$codec-context-bytes.txt"
		set -- $(cat "$artifacts/B29-$codec-context-bytes.txt")
		if test "$1" -ne 1 || test "$2" -ne 1 || test "$3" -le 256; then
			pre15_dut_fail "B29 $codec did not retain one charged production context"
		fi
		case "$codec" in
		lzma) B29_LZMA_CONTEXT_BYTES=$3 ;;
		zstd) B29_ZSTD_CONTEXT_BYTES=$3 ;;
		esac
		runtime_assertions=$((runtime_assertions + 3))
		;;
	esac
	unmount_owned "$B29_MOUNT" "B29 $codec ordinary"
done
for codec in lzma-max zstd-max; do
	attach_mount "$codec.erofs" "$codec"
	pre15_guest_ssh_bounded /root/B29-read-probe pass \
	    "$B29_MOUNT/payload-0.bin" \
	    /root/B29-fixtures/source/payload-0.bin || \
		pre15_dut_fail "$codec maximum-parameter workload failed"
	runtime_assertions=$((runtime_assertions + 1))
	unmount_owned "$B29_MOUNT" "B29 $codec maximum-parameter"
done

for codec in lzma zstd; do
	B29_BAD_MD=$(pre15_guest_ssh_bounded mdconfig -a -t vnode \
	    -f "/root/B29-fixtures/images/$codec-max-over.erofs") || \
		pre15_dut_fail "$codec over-limit md attach failed"
	pre15_own_guest_md "$B29_BAD_MD" "$codec over-limit md"
	B29_BAD_MOUNT=/mnt/pre15-b29-$codec-over
	pre15_guest_ssh_bounded mkdir -p "$B29_BAD_MOUNT"
	pre15_guest_ssh_bounded /root/B29-mount-probe "/dev/$B29_BAD_MD" \
	    "$B29_BAD_MOUNT" 45 || \
		pre15_dut_fail "$codec over-limit config did not return EOPNOTSUPP"
	runtime_assertions=$((runtime_assertions + 1))
done

pre15_guest_ssh_bounded \
    'sysctl -n vfs.erofs.stream_pool.creations; sysctl -n vfs.erofs.stream_pool.reuses' \
    >"$artifacts/B29-pool-legal-metrics.txt"
set -- $(cat "$artifacts/B29-pool-legal-metrics.txt")
if test "$1" -lt 5 || test "$2" -lt 6; then
	pre15_dut_fail 'B29 legal workloads did not create and reuse real contexts'
fi
runtime_assertions=$((runtime_assertions + 2))
pre15_guest_ssh_bounded \
    'sysctl -n vfs.erofs.stream_pool.contexts; sysctl -n vfs.erofs.stream_pool.resident_bytes' \
    >"$artifacts/B29-pool-after-legal-unmount.txt"
set -- $(cat "$artifacts/B29-pool-after-legal-unmount.txt")
if test "$1" -ne 0 || test "$2" -ne 0; then
	pre15_dut_fail 'B29 legal workload unmount did not drain all contexts'
fi
runtime_assertions=$((runtime_assertions + 2))
unload_owned_kld 'B29 first'
runtime_assertions=$((runtime_assertions + 1))
test -n "$B29_LZMA_CONTEXT_BYTES" || \
	pre15_runner_fail 'B29 LZMA production context size was not recorded'
test -n "$B29_ZSTD_CONTEXT_BYTES" || \
	pre15_runner_fail 'B29 Zstd production context size was not recorded'
run_exhaustion_phase lzma byte "$B29_LZMA_CONTEXT_BYTES" lzma-byte
run_exhaustion_phase zstd byte "$B29_ZSTD_CONTEXT_BYTES" zstd-byte
run_exhaustion_phase lzma count 1 global-count
clear_pool_tunables

if test "$runtime_assertions" -ne 90; then
	pre15_runner_fail "B29 QEMU assertion count mismatch: $runtime_assertions"
fi
pre15_guest_ssh_bounded dmesg >"$artifacts/B29-dmesg.txt"
pre15_target_reached
printf '%s\n' \
	'B29 FreeBSD 15 QEMU context-pool PASS' \
	'ordinary=9 charged-codec-state=6 maximum-parameter=2 over-limit=2 lifecycle=5 constrained-phases=66 total=90' \
	'LZMA-byte, Zstd-byte, and global-count cross-mount exhaustion returned ENOMEM=12; each owner drain reached zero and each retry succeeded; full feature suite NOT_RUN'
