#!/bin/sh
set -eu

gate_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
root=$(CDPATH= cd -- "$gate_dir/../../../.." && pwd -P)
input=$gate_dir/P15-005-input.json

mode=base
base=
output=
oracle=
while test "$#" -gt 0; do
	case "$1" in
	--base)
		test "$#" -ge 2 || { echo '--base requires a commit' >&2; exit 2; }
		mode=base
		base=$2
		shift 2
		;;
	--worktree)
		mode=worktree
		shift
		;;
	--output)
		test "$#" -ge 2 || { echo '--output requires a directory' >&2; exit 2; }
		output=$2
		shift 2
		;;
	--oracle)
		test "$#" -ge 2 || { echo '--oracle requires a JSON file' >&2; exit 2; }
		oracle=$2
		shift 2
		;;
	*)
		echo "unknown argument: $1" >&2
		exit 2
		;;
	esac
done

test -n "$output" || { echo '--output is required' >&2; exit 2; }
case "$output" in
/*) ;;
*) output=$PWD/$output ;;
esac
test ! -e "$output" || { echo "refusing existing output: $output" >&2; exit 2; }
mkdir -p "$output"

python3 - "$root" "$input" "$mode" "$base" "$output" "$oracle" <<'PY'
from __future__ import annotations

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


root = Path(sys.argv[1])
input_path = Path(sys.argv[2])
mode = sys.argv[3]
requested_base = sys.argv[4]
output = Path(sys.argv[5])
oracle_path = Path(sys.argv[6]) if sys.argv[6] else None
spec = json.loads(input_path.read_text(encoding="ascii"))


def run(argv: list[str], **kwargs: object) -> subprocess.CompletedProcess[str]:
    return subprocess.run(argv, check=True, text=True, **kwargs)


def git(*args: str) -> str:
    return subprocess.check_output(
        ["git", "-C", str(root), *args], text=True
    ).strip()


if mode == "base":
    if not requested_base:
        raise SystemExit("base mode requires --base")
    resolved = git("rev-parse", f"{requested_base}^{{commit}}")
    if resolved != spec["required_base"]:
        raise SystemExit(
            f"P15-005 must gate the frozen BASE {spec['required_base']}, got {resolved}"
        )
else:
    resolved = git("rev-parse", "HEAD")


def source(path: str) -> str:
    if mode == "base":
        return subprocess.check_output(
            ["git", "-C", str(root), "show", f"{resolved}:{path}"],
            text=True,
        )
    return (root / path).read_text(encoding="utf-8")


sources = {path: source(path) for path in spec["source_paths"]}
source_hashes = {
    path: hashlib.sha256(text.encode("utf-8")).hexdigest()
    for path, text in sources.items()
}
for path, expected in spec["linux_sha256"].items():
    if source_hashes[path] != expected:
        raise SystemExit(f"Linux oracle identity mismatch for {path}")
for path, anchors in spec["linux_semantic_anchors"].items():
    for anchor in anchors:
        if anchor not in sources[path]:
            raise SystemExit(f"Linux semantic anchor absent in {path}: {anchor}")


def extract_function(text: str, name: str) -> str:
    match = re.search(r"^" + re.escape(name) + r"\s*\(", text, re.MULTILINE)
    if not match:
        raise ValueError(f"function not found: {name}")
    start = text.rfind("\n\n", 0, match.start()) + 2
    brace = text.find("{", match.end())
    if brace < 0:
        raise ValueError(f"function has no body: {name}")
    depth = 0
    state = "code"
    index = brace
    while index < len(text):
        char = text[index]
        following = text[index + 1] if index + 1 < len(text) else ""
        if state == "code":
            if char == "/" and following == "*":
                state = "block"
                index += 2
                continue
            if char == "/" and following == "/":
                state = "line"
                index += 2
                continue
            if char == '"':
                state = "string"
            elif char == "'":
                state = "character"
            elif char == "{":
                depth += 1
            elif char == "}":
                depth -= 1
                if depth == 0:
                    return text[start : index + 1] + "\n"
        elif state == "block" and char == "*" and following == "/":
            state = "code"
            index += 2
            continue
        elif state == "line" and char == "\n":
            state = "code"
        elif state in {"string", "character"}:
            if char == "\\":
                index += 2
                continue
            if (state == "string" and char == '"') or (
                state == "character" and char == "'"
            ):
                state = "code"
        index += 1
    raise ValueError(f"unterminated function: {name}")


function_bodies: dict[str, str] = {}
for key in spec["baseline_function_sha256"]:
    filename, function = key.split(":", 1)
    function_bodies[key] = extract_function(
        sources[f"repo-pre-15/src/{filename}"], function
    )
if mode == "base":
    for key, expected in spec["baseline_function_sha256"].items():
        actual = hashlib.sha256(function_bodies[key].encode("utf-8")).hexdigest()
        if actual != expected:
            raise SystemExit(f"unreviewed baseline consumer body: {key}")


def all_functions(text: str) -> dict[str, str]:
    result: dict[str, str] = {}
    for match in re.finditer(r"^([A-Za-z_][A-Za-z0-9_]*)\s*\(", text, re.MULTILINE):
        name = match.group(1)
        try:
            result[name] = extract_function(text, name)
        except ValueError:
            continue
    return result


direct_sites: set[str] = set()
for filename in (
    "data.c",
    "decompressor.c",
    "inode.c",
    "super.c",
    "xattr.c",
    "zdata.c",
    "zmap.c",
):
    funcs = all_functions(sources[f"repo-pre-15/src/{filename}"])
    for name, body in funcs.items():
        calls = body[body.find("{") :]
        direct = "erofs_read_metadata(" in calls
        if filename in {"decompressor.c", "super.c", "xattr.c"}:
            direct = direct or "erofs_bread(" in calls
        if direct:
            direct_sites.add(f"{filename}:{name}")
expected_direct = set(spec["direct_metadata_functions"])
if direct_sites != expected_direct:
    raise SystemExit(
        "direct metadata consumer inventory mismatch: "
        f"missing={sorted(expected_direct - direct_sites)} "
        f"extra={sorted(direct_sites - expected_direct)}"
    )


object_mode = "struct erofs_buf {" in sources["repo-pre-15/src/internal.h"]
ownership_failures: list[dict[str, object]] = []
candidate_contract_result: dict[str, object] = {"status": "NOT_APPLICABLE"}
if mode == "worktree":
    internal = sources["repo-pre-15/src/internal.h"]
    data_source = sources["repo-pre-15/src/data.c"]
    required_object_markers = [
        "struct erofs_buf {",
        "void *data;",
        "void (*release)(void *);",
        "void erofs_put_metabuf(struct erofs_buf *buf);",
        "erofs_read_metadata(struct erofs_sb_info *sbi, erofs_nid_t nid,",
        "struct erofs_buf *buf)",
    ]
    for marker in required_object_markers:
        if marker not in internal and marker not in data_source:
            raise SystemExit(f"candidate metadata object marker absent: {marker}")
    if re.search(
        r"erofs_read_metadata\s*\([^;]*size_t\s+len\s*,\s*void\s*\*\*bufp\s*\)\s*;",
        internal,
        re.DOTALL,
    ):
        raise SystemExit("legacy metadata void-pointer prototype remains")

    contract = spec["candidate_ownership_contract"]
    contract_keys = set(contract["object_local_counts"])
    if contract_keys != set(spec["baseline_function_sha256"]):
        raise SystemExit("candidate ownership contract does not cover every audited function")
    function_metrics = []
    for key in sorted(contract_keys):
        body = function_bodies[key]
        calls = body[body.find("{") :]
        initialized_objects = re.findall(
            r"\bstruct\s+erofs_buf\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*"
            r"EROFS_BUF_INITIALIZER\s*;",
            calls,
        )
        all_objects = re.findall(
            r"\bstruct\s+erofs_buf\s+([A-Za-z_][A-Za-z0-9_]*)\s*(?:=|;)",
            calls,
        )
        metrics = {
            "function": key,
            "object_local_count": len(initialized_objects),
            "put_count": calls.count("erofs_put_metabuf("),
            "legacy_release_count": calls.count("erofs_brelse("),
            "raw_bread_count": calls.count("erofs_bread("),
        }
        function_metrics.append(metrics)
        expected = {
            "object_local_count": contract["object_local_counts"][key],
            "put_count": contract["put_counts"][key],
            "legacy_release_count": contract["legacy_release_counts"].get(key, 0),
            "raw_bread_count": contract["raw_bread_counts"].get(key, 0),
        }
        for field, value in expected.items():
            if metrics[field] != value:
                ownership_failures.append(
                    {
                        "function": key,
                        "field": field,
                        "expected": value,
                        "actual": metrics[field],
                    }
                )
        if sorted(all_objects) != sorted(initialized_objects):
            ownership_failures.append(
                {"function": key, "field": "uninitialized-metadata-object"}
            )
        for variable in initialized_objects:
            if f"&{variable}" not in calls:
                ownership_failures.append(
                    {"function": key, "field": "object-address-unused", "variable": variable}
                )
            if f"erofs_put_metabuf(&{variable})" not in calls:
                ownership_failures.append(
                    {"function": key, "field": "object-not-released", "variable": variable}
                )
        raw_pointer_names = re.findall(
            r"\bvoid\s*\*\s*([A-Za-z_][A-Za-z0-9_]*)\s*(?:[;=,])", calls
        )
        for api in contract["acquire_apis"]:
            for call in re.findall(
                r"\b" + re.escape(api) + r"\s*\((.*?);", calls, re.DOTALL
            ):
                for variable in raw_pointer_names:
                    if re.search(r"&\s*" + re.escape(variable) + r"\b", call):
                        ownership_failures.append(
                            {
                                "function": key,
                                "field": "raw-pointer-metadata-acquire",
                                "api": api,
                                "variable": variable,
                            }
                        )
    candidate_contract_result = {
        "status": "PASS" if not ownership_failures else "FAIL",
        "function_metrics": function_metrics,
        "transfer_functions": contract["transfer_functions"],
        "failures": ownership_failures,
    }


def compile_and_run(name: str, program: str) -> list[dict[str, int | str]]:
    source_path = output / f"{name}.c"
    binary_path = output / name
    compile_stdout = output / f"{name}.compile.stdout"
    compile_stderr = output / f"{name}.compile.stderr"
    run_stdout = output / f"{name}.stdout"
    run_stderr = output / f"{name}.stderr"
    source_path.write_text(program, encoding="ascii")
    compiler = subprocess.run(
        [
            "cc",
            "-std=c17",
            "-O0",
            "-Wall",
            "-Wextra",
            "-Werror",
            str(source_path),
            "-o",
            str(binary_path),
        ],
        text=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
    compile_stdout.write_text(compiler.stdout, encoding="utf-8")
    compile_stderr.write_text(compiler.stderr, encoding="utf-8")
    if compiler.returncode != 0:
        raise SystemExit(f"{name} extractor compilation failed")
    executed = subprocess.run(
        [str(binary_path)],
        text=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        timeout=60,
    )
    run_stdout.write_text(executed.stdout, encoding="utf-8")
    run_stderr.write_text(executed.stderr, encoding="utf-8")
    if executed.returncode != 0:
        raise SystemExit(f"{name} extractor execution failed")
    records = []
    for line in executed.stdout.splitlines():
        fields = line.split("\t")
        if len(fields) != 11 or fields[0] != "tuple":
            raise SystemExit(f"invalid {name} extractor output: {line!r}")
        records.append(
            {
                "id": fields[1],
                "m_la": int(fields[2]),
                "m_pa": int(fields[3]),
                "m_llen": int(fields[4]),
                "m_plen": int(fields[5]),
                "m_deviceid": int(fields[6]),
                "m_flags": int(fields[7]),
                "errno": int(fields[8]),
                "acquire_count": int(fields[9]),
                "release_count": int(fields[10]),
            }
        )
    return records


def compile_helper(name: str, program: str) -> list[str]:
    source_path = output / f"{name}.c"
    binary_path = output / name
    compile_stdout = output / f"{name}.compile.stdout"
    compile_stderr = output / f"{name}.compile.stderr"
    run_stdout = output / f"{name}.stdout"
    run_stderr = output / f"{name}.stderr"
    source_path.write_text(program, encoding="ascii")
    compiler = subprocess.run(
        [
            "cc",
            "-std=c17",
            "-O0",
            "-Wall",
            "-Wextra",
            "-Werror",
            str(source_path),
            "-o",
            str(binary_path),
        ],
        text=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
    compile_stdout.write_text(compiler.stdout, encoding="utf-8")
    compile_stderr.write_text(compiler.stderr, encoding="utf-8")
    if compiler.returncode != 0:
        raise SystemExit(f"{name} helper extractor compilation failed")
    executed = subprocess.run(
        [str(binary_path)],
        text=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        timeout=60,
    )
    run_stdout.write_text(executed.stdout, encoding="utf-8")
    run_stderr.write_text(executed.stderr, encoding="utf-8")
    if executed.returncode != 0:
        raise SystemExit(f"{name} helper extractor execution failed")
    return executed.stdout.splitlines()


common = r'''
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define EIO 5
#define EINVAL 22
#define EOPNOTSUPP 45
#define EOVERFLOW 84
#define EINTEGRITY 97
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define roundup2(x, y) ((((x) + ((y) - 1)) / (y)) * (y))
#define rounddown2(x, y) ((x) - ((x) % (y)))
#define bzero(p, n) memset((p), 0, (n))
#define le16toh(x) (x)
#define le32toh(x) (x)
#define le64toh(x) (x)
typedef uint64_t erofs_nid_t;
typedef uint64_t erofs_off_t;
typedef uint64_t erofs_blk_t;
static __attribute__((unused)) uint16_t le16dec(const void *pointer) { uint16_t value; memcpy(&value, pointer, sizeof(value)); return value; }
static __attribute__((unused)) uint32_t le32dec(const void *pointer) { uint32_t value; memcpy(&value, pointer, sizeof(value)); return value; }
static __attribute__((unused)) uint64_t le64dec(const void *pointer) { uint64_t value; memcpy(&value, pointer, sizeof(value)); return value; }
'''


buffer_compat = r'''
struct erofs_buf {
	void *data;
	void (*release)(void *);
};
#define EROFS_BUF_INITIALIZER { .data = NULL, .release = NULL }
static unsigned int gate_acquires;
static unsigned int gate_releases;
static const unsigned char *gate_bytes;
static size_t gate_bytes_len;
static uint64_t gate_bytes_base;
static int gate_reader_mode;
static void gate_release(void *data)
{
	if (data != NULL) {
		++gate_releases;
		free(data);
	}
}
static __attribute__((unused)) void erofs_brelse(void *data) { gate_release(data); }
static __attribute__((unused)) void erofs_put_metabuf(struct erofs_buf *buf)
{
	void *data;
	void (*release)(void *);
	if (buf == NULL || buf->data == NULL)
		return;
	data = buf->data;
	release = buf->release;
	buf->data = NULL;
	buf->release = NULL;
	if (release != NULL)
		release(data);
}
'''


def reader_definition(objects: bool) -> str:
    if objects:
        return r'''
static int erofs_read_metadata(struct erofs_sb_info *sbi, erofs_nid_t nid,
    erofs_off_t off, size_t len, struct erofs_buf *buf)
{
	void *data;
	(void)sbi; (void)nid;
	if (gate_reader_mode == 2 || off < gate_bytes_base ||
	    len > gate_bytes_len || off - gate_bytes_base > gate_bytes_len - len)
		return (EIO);
	data = malloc(len);
	if (data == NULL)
		return (EIO);
	memcpy(data, gate_bytes + (off - gate_bytes_base), len);
	buf->data = data;
	buf->release = gate_release;
	++gate_acquires;
	return (0);
}
'''
    return r'''
static int erofs_read_metadata(struct erofs_sb_info *sbi, erofs_nid_t nid,
    erofs_off_t off, size_t len, void **bufp)
{
	void *data;
	(void)sbi; (void)nid;
	if (gate_reader_mode == 2 || off < gate_bytes_base ||
	    len > gate_bytes_len || off - gate_bytes_base > gate_bytes_len - len)
		return (EIO);
	data = malloc(len);
	if (data == NULL)
		return (EIO);
	memcpy(data, gate_bytes + (off - gate_bytes_base), len);
	*bufp = data;
	++gate_acquires;
	return (0);
}
'''


def c_bytes(data: bytes) -> str:
    if not data:
        return "0"
    return ", ".join(f"0x{byte:02x}" for byte in data)


data_cases = [case for case in spec["tuple_cases"] if case["engine"] == "data"]
data_case_code = []
for index, case in enumerate(data_cases):
    reader = case["reader"]
    raw = bytes.fromhex(reader.get("bytes_hex", ""))
    sbi = case["sbi"]
    inode = case["inode"]
    data_case_code.append(
        f'''
	static const unsigned char bytes_{index}[] = {{ {c_bytes(raw)} }};
	struct erofs_sb_info sbi_{index} = {{
		.block_size = {sbi['block_size']}ULL,
		.blkszbits = {sbi['blkszbits']},
		.blocks = {sbi['blocks']}ULL,
		.device_id_mask = {sbi['device_id_mask']},
	}};
	struct erofs_inode vi_{index} = {{
		.nid = {inode['nid']}ULL,
		.size = {inode['size']}ULL,
		.inode_off = {inode['inode_off']}ULL,
		.startblk = {inode['startblk']}ULL,
		.datalayout = {inode['datalayout']},
		.inode_isize = {inode['inode_isize']},
		.xattr_isize = {inode['xattr_isize']},
		.chunkformat = {inode['chunkformat']},
		.chunkbits = {inode['chunkbits']},
	}};
	gate_bytes = bytes_{index};
	gate_bytes_len = {len(raw)};
	gate_bytes_base = {inode['inode_off'] + inode['inode_isize'] + inode['xattr_isize']}ULL;
	gate_reader_mode = {2 if reader['mode'] == 'error' else 0};
	gate_acquires = gate_releases = 0;
	pa = 0; device = 0; run = 0; hole = false; metadata = false;
	error = erofs_map_blocks(&sbi_{index}, &vi_{index}, {case['request']}ULL,
	    &pa, &device, &run, &hole, &metadata);
	flags = (error == 0 && run != 0 && !hole ? EROFS_MAP_MAPPED : 0) |
	    (metadata ? EROFS_MAP_META : 0);
	printf("tuple\\t{case['id']}\\t%llu\\t%llu\\t%zu\\t%zu\\t%u\\t%u\\t%d\\t%u\\t%u\\n",
	    (unsigned long long){case['request']}ULL, (unsigned long long)pa,
	    run, run, device, flags, error, gate_acquires, gate_releases);
'''
    )

data_preamble = common + buffer_compat + r'''
#define EROFS_INODE_FLAT_PLAIN 0
#define EROFS_INODE_COMPRESSED_FULL 1
#define EROFS_INODE_FLAT_INLINE 2
#define EROFS_INODE_COMPRESSED_COMPACT 3
#define EROFS_INODE_CHUNK_BASED 4
#define EROFS_CHUNK_FORMAT_INDEXES 0x20
#define EROFS_CHUNK_FORMAT_48BIT 0x40
#define EROFS_BLOCK_MAP_ENTRY_SIZE 4
#define EROFS_DIRENT_NID_METABOX (1ULL << 63)
#define EROFS_NULL_ADDR UINT64_MAX
#define EROFS_MAP_MAPPED 0x0001
#define EROFS_MAP_META 0x0002
struct erofs_inode_chunk_index { uint16_t startblk_hi; uint16_t device_id; uint32_t startblk_lo; } __attribute__((packed));
struct erofs_inode {
	erofs_nid_t nid; uint64_t size; erofs_off_t inode_off; erofs_blk_t startblk;
	uint8_t datalayout; uint8_t inode_isize; uint32_t xattr_isize;
	uint16_t chunkformat; uint8_t chunkbits;
};
struct erofs_sb_info {
	uint64_t block_size; uint8_t blkszbits; erofs_blk_t blocks;
	uint16_t device_id_mask; struct erofs_inode *metabox_en;
};
static bool erofs_nid_in_metabox(erofs_nid_t nid) { return (nid & EROFS_DIRENT_NID_METABOX) != 0; }
'''
data_program = (
    data_preamble
    + reader_definition(object_mode)
    + extract_function(sources["repo-pre-15/src/data.c"], "erofs_inline_tail_start")
    + extract_function(sources["repo-pre-15/src/data.c"], "erofs_map_blocks_chunk")
    + extract_function(sources["repo-pre-15/src/data.c"], "erofs_map_blocks")
    + r'''
int main(void)
{
	erofs_off_t pa; unsigned int device, flags; size_t run;
	bool hole, metadata; int error;
'''
    + "".join(data_case_code)
    + "\treturn (0);\n}\n"
)


zmap_cases = [case for case in spec["tuple_cases"] if case["engine"] == "zmap"]
zmap_case_code = []
for index, case in enumerate(zmap_cases):
    reader = case["reader"]
    if "records" in reader:
        raw = b"".join(
            struct.pack(
                "<IIIII",
                record["plen"],
                record["pstart"] & 0xFFFFFFFF,
                record["pstart"] >> 32,
                record["lstart"] & 0xFFFFFFFF,
                record["lstart"] >> 32,
            )[:16]
            for record in reader["records"]
        )
    else:
        raw = bytes.fromhex(reader.get("bytes_hex", ""))
    sbi = case["sbi"]
    inode = case["inode"]
    zmap_case_code.append(
        f'''
	static const unsigned char bytes_{index}[] = {{ {c_bytes(raw)} }};
	struct erofs_inode packed_{index} = {{ .nid = 999, .size = {sbi['packed_size']}ULL }};
	struct erofs_sb_info sbi_{index} = {{
		.block_size = {sbi['block_size']}, .blkszbits = {sbi['blkszbits']},
		.available_compr_algs = {sbi['available_compr_algs']},
		.packed_inode = &packed_{index},
	}};
	struct erofs_inode vi_{index} = {{
		.nid = {inode['nid']}ULL, .size = {inode['size']}ULL,
		.inode_off = {inode['inode_off']}ULL,
		.inode_isize = {inode['inode_isize']},
		.xattr_isize = {inode['xattr_isize']},
		.datalayout = {inode['datalayout']},
		.z_advise = {inode['z_advise']},
		.z_lclusterbits = {inode['z_lclusterbits']},
		.z_extents = {inode['z_extents']}ULL, .z_initialized = true,
	}};
	struct erofs_map_blocks map_{index} = {{ .m_la = {case['request']}ULL }};
	gate_bytes = bytes_{index}; gate_bytes_len = {len(raw)};
	gate_bytes_base = {reader.get('base', 0)}ULL;
	gate_reader_mode = {2 if reader['mode'] == 'error' else 0};
	gate_acquires = gate_releases = 0;
	error = z_erofs_map_blocks_iter(&sbi_{index}, &vi_{index}, &map_{index}, 0);
	printf("tuple\\t{case['id']}\\t%llu\\t%llu\\t%llu\\t%llu\\t%u\\t%u\\t%d\\t%u\\t%u\\n",
	    (unsigned long long)map_{index}.m_la,
	    (unsigned long long)map_{index}.m_pa,
	    (unsigned long long)map_{index}.m_llen,
	    (unsigned long long)map_{index}.m_plen,
	    map_{index}.m_deviceid, map_{index}.m_flags, error,
	    gate_acquires, gate_releases);
'''
    )

zmap_preamble = common + buffer_compat + r'''
#define EROFS_INODE_COMPRESSED_FULL 1
#define EROFS_MAP_MAPPED 0x0001
#define EROFS_MAP_META 0x0002
#define EROFS_MAP_PARTIAL_MAPPED 0x0004
#define EROFS_MAP_PARTIAL_REF 0x0008
#define EROFS_MAP_FRAGMENT 0x0010
#define EROFS_MAP_FULL(f) (!((f) & (EROFS_MAP_PARTIAL_MAPPED | EROFS_MAP_PARTIAL_REF)))
#define EROFS_NULL_ADDR UINT64_MAX
#define Z_EROFS_COMPRESSION_LZ4 0
#define Z_EROFS_COMPRESSION_MAX 4
#define Z_EROFS_COMPRESSION_SHIFTED 4
#define Z_EROFS_COMPRESSION_INTERLACED 5
#define Z_EROFS_COMPRESSION_RUNTIME_MAX 6
#define Z_EROFS_PCLUSTER_MAX_SIZE (1024 * 1024)
#define Z_EROFS_PCLUSTER_MAX_DSIZE (12 * 1024 * 1024)
#define Z_EROFS_ADVISE_EXTENTS 0x0001
#define Z_EROFS_ADVISE_INTERLACED_PCLUSTER 0x0010
#define Z_EROFS_ADVISE_FRAGMENT_PCLUSTER 0x0020
#define Z_EROFS_ADVISE_EXTRECSZ_BIT 1
#define Z_EROFS_ADVISE_EXTRECSZ_MASK 0x3
#define Z_EROFS_EXTENT_PLEN_PARTIAL (1U << 27)
#define Z_EROFS_EXTENT_PLEN_FMT_BIT 28
#define Z_EROFS_EXTENT_PLEN_MASK ((Z_EROFS_PCLUSTER_MAX_SIZE << 1) - 1)
struct z_erofs_extent {
	uint32_t plen, pstart_lo, pstart_hi, lstart_lo, lstart_hi;
	uint8_t reserved[12];
} __attribute__((packed));
struct z_erofs_map_header { uint32_t word0; uint16_t h_advise; uint8_t h_algorithmtype; uint8_t h_clusterbits; } __attribute__((packed));
struct erofs_map_blocks {
	erofs_off_t m_pa, m_la; uint64_t m_plen, m_llen;
	unsigned short m_deviceid; char m_algorithmformat; unsigned int m_flags;
};
struct erofs_inode {
	erofs_nid_t nid; uint64_t size; erofs_off_t inode_off;
	uint8_t datalayout, inode_isize; uint32_t xattr_isize;
	uint16_t z_advise; uint8_t z_algorithmtype[2], z_lclusterbits;
	uint16_t z_idata_size; erofs_off_t z_fragmentoff;
	uint64_t z_tailextent_headlcn, z_extents; bool z_initialized, fragment;
};
struct erofs_sb_info {
	uint32_t block_size; uint8_t blkszbits; uint16_t available_compr_algs;
	uint64_t packed_nid; struct erofs_inode *packed_inode;
};
'''
zmap_stubs = r'''
static int z_erofs_fill_inode(struct erofs_sb_info *sbi, struct erofs_inode *vi)
{ (void)sbi; return (vi->z_initialized ? 0 : EINTEGRITY); }
static int z_erofs_map_blocks_fo(struct erofs_sb_info *sbi, struct erofs_inode *vi,
    struct erofs_map_blocks *map, int flags)
{ (void)sbi; (void)vi; (void)map; (void)flags; return (EOPNOTSUPP); }
'''
zmap_names = [
    "z_erofs_extent_add",
    "z_erofs_extent_roundup",
    "z_erofs_extent_table_pos",
    "z_erofs_extent_record_pos",
    "z_erofs_read_extent",
    "z_erofs_extent_lstart",
    "z_erofs_map_blocks_ext",
    "z_erofs_map_sanity_check",
    "z_erofs_map_blocks_iter",
]
zmap_program = zmap_preamble + reader_definition(object_mode) + zmap_stubs
zmap_program += extract_function(
    sources["repo-pre-15/src/erofs_fs.h"], "z_erofs_extent_recsize"
)
for name in zmap_names:
    zmap_program += extract_function(sources["repo-pre-15/src/zmap.c"], name)
zmap_program += "int main(void)\n{\n\tint error;\n"
zmap_program += "".join(zmap_case_code)
zmap_program += "\treturn (0);\n}\n"


helper_result: dict[str, object] = {"status": "NOT_APPLICABLE"}
if mode == "worktree":
    helper_program = common + r'''
#define EROFS_DIRENT_NID_METABOX (1ULL << 63)
typedef int64_t off_t;
struct erofs_inode { uint64_t size; };
struct erofs_sb_info { struct erofs_inode *metabox_en; };
struct erofs_buf {
	void *data;
	void (*release)(void *);
};
#define EROFS_BUF_INITIALIZER { .data = NULL, .release = NULL }
static int gate_error;
static unsigned int gate_acquires;
static unsigned int gate_releases;
static unsigned int gate_backend;
static bool erofs_nid_in_metabox(erofs_nid_t nid)
{ return ((nid & EROFS_DIRENT_NID_METABOX) != 0); }
static bool erofs_sb_has_metabox(struct erofs_sb_info *sbi)
{ return (sbi->metabox_en != NULL); }
static int gate_acquire(size_t len, void **bufp, unsigned int backend)
{
	void *data;
	if (gate_error != 0)
		return (gate_error);
	data = malloc(len == 0 ? 1 : len);
	if (data == NULL)
		return (EIO);
	*bufp = data;
	++gate_acquires;
	gate_backend = backend;
	return (0);
}
static int erofs_bread(struct erofs_sb_info *sbi, erofs_off_t off,
    size_t len, void **bufp)
{ (void)sbi; (void)off; return (gate_acquire(len, bufp, 1)); }
static int erofs_read_data(struct erofs_sb_info *sbi, struct erofs_inode *vi,
    erofs_off_t off, size_t len, void **bufp)
{ (void)sbi; (void)vi; (void)off; return (gate_acquire(len, bufp, 2)); }
static void erofs_brelse(void *data)
{
	if (data != NULL) {
		++gate_releases;
		free(data);
	}
}
'''
    helper_program += extract_function(
        sources["repo-pre-15/src/data.c"], "erofs_put_metabuf"
    )
    helper_program += extract_function(
        sources["repo-pre-15/src/data.c"], "erofs_read_metadata"
    )
    helper_program += r'''
int main(void)
{
	struct erofs_inode metabox = { .size = 4096 };
	struct erofs_sb_info sbi = { .metabox_en = &metabox };
	struct erofs_buf buf = EROFS_BUF_INITIALIZER;
	int error;
	int marker;

	gate_error = 0; gate_acquires = gate_releases = gate_backend = 0;
	error = erofs_read_metadata(&sbi, 1, 32, 8, &buf);
	if (error != 0 || buf.data == NULL || buf.release == NULL ||
	    gate_acquires != 1 || gate_backend != 1)
		return (10);
	erofs_put_metabuf(&buf);
	erofs_put_metabuf(&buf);
	if (buf.data != NULL || buf.release != NULL || gate_releases != 1)
		return (11);
	printf("helper\tprimary-success\t1\t1\n");

	buf = (struct erofs_buf)EROFS_BUF_INITIALIZER;
	gate_error = 0; gate_acquires = gate_releases = gate_backend = 0;
	error = erofs_read_metadata(&sbi, EROFS_DIRENT_NID_METABOX | 2, 64, 8,
	    &buf);
	if (error != 0 || buf.data == NULL || buf.release == NULL ||
	    gate_acquires != 1 || gate_backend != 2)
		return (12);
	erofs_put_metabuf(&buf);
	if (buf.data != NULL || buf.release != NULL || gate_releases != 1)
		return (13);
	printf("helper\tmetabox-success\t1\t1\n");

	buf = (struct erofs_buf)EROFS_BUF_INITIALIZER;
	gate_error = EIO; gate_acquires = gate_releases = gate_backend = 0;
	error = erofs_read_metadata(&sbi, 1, 32, 8, &buf);
	if (error != EIO || buf.data != NULL || buf.release != NULL ||
	    gate_acquires != 0 || gate_releases != 0)
		return (14);
	printf("helper\tprovider-error\t0\t0\n");

	buf = (struct erofs_buf)EROFS_BUF_INITIALIZER;
	gate_error = 0; gate_acquires = gate_releases = gate_backend = 0;
	error = erofs_read_metadata(&sbi, 1, UINT64_MAX, 8, &buf);
	if (error != EOVERFLOW || buf.data != NULL || buf.release != NULL ||
	    gate_acquires != 0 || gate_releases != 0)
		return (15);
	printf("helper\toffset-overflow\t0\t0\n");

	buf.data = &marker;
	buf.release = NULL;
	erofs_put_metabuf(&buf);
	erofs_put_metabuf(NULL);
	if (buf.data != NULL || buf.release != NULL)
		return (16);
	printf("helper\tnull-release\t0\t0\n");
	return (0);
}
'''
    helper_lines = compile_helper("P15-005-buffer-helper", helper_program)
    expected_helper_lines = [
        "helper\tprimary-success\t1\t1",
        "helper\tmetabox-success\t1\t1",
        "helper\tprovider-error\t0\t0",
        "helper\toffset-overflow\t0\t0",
        "helper\tnull-release\t0\t0",
    ]
    if helper_lines != expected_helper_lines:
        ownership_failures.append(
            {
                "field": "helper-paths",
                "expected": expected_helper_lines,
                "actual": helper_lines,
            }
        )
    helper_result = {
        "status": "PASS" if helper_lines == expected_helper_lines else "FAIL",
        "paths": helper_lines,
    }


actual_records = compile_and_run("P15-005-data-extractor", data_program)
actual_records.extend(compile_and_run("P15-005-zmap-extractor", zmap_program))
actual_by_id = {record["id"]: record for record in actual_records}
expected_by_id = {case["id"]: case["expected"] for case in spec["tuple_cases"]}
if set(actual_by_id) != set(expected_by_id):
    raise SystemExit("extractor case set mismatch")
tuple_failures = []
for case in spec["tuple_cases"]:
    actual = actual_by_id[case["id"]]
    expected = case["expected"]
    for field in spec["tuple_fields"]:
        if actual[field] != expected[field]:
            tuple_failures.append(
                {
                    "id": case["id"],
                    "field": field,
                    "expected": expected[field],
                    "actual": actual[field],
                }
            )

coverage = sorted(
    {case["category"] for case in spec["tuple_cases"] if not case.get("supplemental")}
)
if set(coverage) != set(spec["required_categories"]):
    raise SystemExit("required tuple category coverage is incomplete")
for case in spec["tuple_cases"]:
    actual = actual_by_id[case["id"]]
    if actual["acquire_count"] != actual["release_count"]:
        tuple_failures.append({"id": case["id"], "field": "ownership"})
    if (
        actual["errno"] == 0
        and case["request"] < case["inode"]["size"]
        and actual["m_llen"] == 0
    ):
        tuple_failures.append({"id": case["id"], "field": "H07-positive-run"})


ownership_paths = spec["ownership_paths"]
consumers = sorted({path["consumer"] for path in ownership_paths})
required_consumers = ["compressed", "inode", "map", "plain", "super", "xattr"]
if consumers != required_consumers:
    raise SystemExit(f"ownership consumer coverage mismatch: {consumers}")
for path in ownership_paths:
    if path["function"] not in function_bodies:
        raise SystemExit(f"ownership path has no frozen function body: {path['id']}")
    if path["acquire_count"] != path["release_count"]:
        raise SystemExit(f"incomplete ownership oracle path: {path['id']}")

ownership_result = {
    "status": "PASS" if not ownership_failures else "FAIL",
    "consumers": consumers,
    "direct_metadata_functions": sorted(direct_sites),
    "paths": ownership_paths,
    "baseline_body_sha256_verified": mode == "base",
    "linux_semantic_anchors_verified": True,
    "candidate_contract": candidate_contract_result,
    "helper_paths": helper_result,
    "failures": ownership_failures,
    "short_read_rule": "a failed read before returned storage has acquire=release=0",
    "success_rule": "each returned metadata allocation has exactly one release",
}

tuple_result = {
    "status": "PASS" if not tuple_failures else "FAIL",
    "coverage": coverage,
    "tuple_fields": spec["tuple_fields"],
    "records": [
        {
            **actual_by_id[case["id"]],
            "category": case["category"],
            "supplemental": bool(case.get("supplemental")),
        }
        for case in spec["tuple_cases"]
    ],
    "failures": tuple_failures,
    "authoritative_sources": [
        "compiled function bodies extracted from the frozen/current FreeBSD source",
        "independently frozen expected fields from EROFS records and Linux map semantics",
    ],
}
(output / "tuples.json").write_text(
    json.dumps(tuple_result, indent=2, sort_keys=True) + "\n", encoding="ascii"
)
(output / "ownership.json").write_text(
    json.dumps(ownership_result, indent=2, sort_keys=True) + "\n", encoding="ascii"
)
(output / "source-sha256.json").write_text(
    json.dumps(source_hashes, indent=2, sort_keys=True) + "\n", encoding="ascii"
)

oracle_equal = None
if oracle_path is not None:
    oracle = json.loads(oracle_path.read_text(encoding="ascii"))
    oracle_records = {record["id"]: record for record in oracle["records"]}
    oracle_equal = True
    for case in spec["tuple_cases"]:
        current = actual_by_id[case["id"]]
        frozen = oracle_records.get(case["id"])
        if frozen is None or any(current[field] != frozen[field] for field in spec["tuple_fields"]):
            oracle_equal = False
            break
    if not oracle_equal:
        tuple_failures.append({"field": "frozen-baseline-replay"})

status = "GO" if not tuple_failures and not ownership_failures else "STOP"
result = {
    "schema": 1,
    "gate": spec["gate"],
    "candidate": spec["candidate"],
    "status": status,
    "mode": mode,
    "requested_base": requested_base or None,
    "resolved_head": resolved,
    "object_mode": object_mode,
    "tuple_status": tuple_result["status"],
    "ownership_status": ownership_result["status"],
    "coverage": coverage,
    "case_count": len(actual_records),
    "ownership_path_count": len(ownership_paths),
    "oracle_equal": oracle_equal,
    "qemu": "NOT_RUN",
    "full_feature_suite": "NOT_RUN",
    "failures": tuple_failures + ownership_failures,
}
(output / "result.json").write_text(
    json.dumps(result, indent=2, sort_keys=True) + "\n", encoding="ascii"
)
hashes = []
for path in sorted(output.iterdir()):
    if path.is_file() and path.name != "SHA256SUMS":
        hashes.append(f"{hashlib.sha256(path.read_bytes()).hexdigest()}  {path.name}")
(output / "SHA256SUMS").write_text("\n".join(hashes) + "\n", encoding="ascii")
print(json.dumps(result, sort_keys=True))
if status != "GO":
    raise SystemExit(1)
PY
