update
This commit is contained in:
Executable
+337
@@ -0,0 +1,337 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
: "${PRE15_DUT:?PRE15_DUT is required}"
|
||||
: "${PRE15_ROOT:?PRE15_ROOT is required}"
|
||||
: "${PRE15_RUN_DIR:?PRE15_RUN_DIR is required}"
|
||||
: "${PRE15_LIB_DIR:?PRE15_LIB_DIR is required}"
|
||||
. "$PRE15_LIB_DIR/runner.sh"
|
||||
|
||||
baseline=fb79d17edba7c47fae6e60b2a92771a8f3a2fcdf
|
||||
zmap=$PRE15_DUT/src/zmap.c
|
||||
internal=$PRE15_DUT/src/internal.h
|
||||
linux_zmap=$PRE15_ROOT/src-linux/zmap.c
|
||||
oracle=$PRE15_DUT/tests/pre15/fixtures/B07-map-oracle.json
|
||||
gate_input=$PRE15_DUT/tests/pre15/gates/P15-006-input.json
|
||||
artifacts=$PRE15_RUN_DIR/artifacts
|
||||
|
||||
for tool in git python3 sha256sum; do
|
||||
command -v "$tool" >/dev/null 2>&1 || \
|
||||
pre15_infra_blocked "missing B24 host tool: $tool"
|
||||
done
|
||||
|
||||
pre15_record_fixture b24-zmap "$zmap"
|
||||
pre15_record_fixture b24-internal "$internal"
|
||||
pre15_record_fixture b24-linux-zmap "$linux_zmap"
|
||||
pre15_record_fixture b24-map-oracle "$oracle"
|
||||
pre15_record_fixture b24-gate-input "$gate_input"
|
||||
pre15_record_fixture b24-case \
|
||||
"$PRE15_DUT/tests/pre15/cases/B24-zmap-order.sh"
|
||||
|
||||
mkdir -p "$artifacts"
|
||||
pre15_target_reached
|
||||
|
||||
if python3 -B - "$PRE15_ROOT" "$PRE15_DUT" "$baseline" "$oracle" \
|
||||
"$gate_input" "$artifacts/B24-zmap-order.json" <<'PY'
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import Counter
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
root = Path(sys.argv[1])
|
||||
dut = Path(sys.argv[2])
|
||||
baseline = sys.argv[3]
|
||||
oracle_path = Path(sys.argv[4])
|
||||
gate_input_path = Path(sys.argv[5])
|
||||
report_path = Path(sys.argv[6])
|
||||
|
||||
|
||||
def committed(path: str) -> str:
|
||||
completed = subprocess.run(
|
||||
["git", "-C", str(root), "show", f"{baseline}:repo-pre-15/{path}"],
|
||||
check=False,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
)
|
||||
if completed.returncode != 0:
|
||||
raise SystemExit(f"cannot read B24 baseline {path}: {completed.stderr}")
|
||||
return completed.stdout
|
||||
|
||||
|
||||
def function(source: str, name: str) -> str:
|
||||
match = re.search(r"^" + re.escape(name) + r"\s*\(", source, re.MULTILINE)
|
||||
if match is None:
|
||||
raise SystemExit(f"missing function: {name}")
|
||||
name_line = source.rfind("\n", 0, match.start()) + 1
|
||||
start = source.rfind("\n", 0, name_line - 1) + 1
|
||||
brace = source.find("{", match.end())
|
||||
if brace < 0:
|
||||
raise SystemExit(f"missing function body: {name}")
|
||||
depth = 0
|
||||
state = "code"
|
||||
index = brace
|
||||
while index < len(source):
|
||||
char = source[index]
|
||||
following = source[index + 1] if index + 1 < len(source) 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 source[start : index + 1]
|
||||
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 SystemExit(f"unterminated function: {name}")
|
||||
|
||||
|
||||
function_order = [
|
||||
"z_erofs_index_base",
|
||||
"z_erofs_index_advance",
|
||||
"z_erofs_lcluster_count",
|
||||
"z_erofs_lcluster_pos",
|
||||
"z_erofs_lcn_advance",
|
||||
"z_erofs_compact_pblk",
|
||||
"z_erofs_fragment_offset",
|
||||
"z_erofs_post_eof_len",
|
||||
"z_erofs_physical_end",
|
||||
"z_erofs_read_index",
|
||||
"z_erofs_load_full_lcluster",
|
||||
"decode_compactedbits",
|
||||
"get_compacted_la_distance",
|
||||
"z_erofs_load_compact_lcluster",
|
||||
"z_erofs_load_lcluster_from_disk",
|
||||
"z_erofs_extent_lookback",
|
||||
"z_erofs_get_extent_compressedlen",
|
||||
"z_erofs_get_extent_decompressedlen",
|
||||
"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_validate_extent_table",
|
||||
"z_erofs_map_blocks_fo",
|
||||
"z_erofs_map_blocks_ext",
|
||||
"z_erofs_fill_inode",
|
||||
"z_erofs_map_sanity_check",
|
||||
"z_erofs_map_blocks",
|
||||
]
|
||||
linux_algorithm_order = [
|
||||
"z_erofs_load_full_lcluster",
|
||||
"decode_compactedbits",
|
||||
"get_compacted_la_distance",
|
||||
"z_erofs_load_compact_lcluster",
|
||||
"z_erofs_load_lcluster_from_disk",
|
||||
"z_erofs_extent_lookback",
|
||||
"z_erofs_get_extent_compressedlen",
|
||||
"z_erofs_get_extent_decompressedlen",
|
||||
"z_erofs_map_blocks_fo",
|
||||
"z_erofs_map_blocks_ext",
|
||||
"z_erofs_fill_inode",
|
||||
"z_erofs_map_sanity_check",
|
||||
]
|
||||
|
||||
current = (dut / "src/zmap.c").read_text(encoding="utf-8")
|
||||
before = committed("src/zmap.c")
|
||||
current_internal = (dut / "src/internal.h").read_text(encoding="utf-8")
|
||||
before_internal = committed("src/internal.h")
|
||||
linux = (root / "src-linux/zmap.c").read_text(encoding="utf-8")
|
||||
|
||||
moved_start = before.index("static int\nz_erofs_extent_add(")
|
||||
moved_end = before.index("static int\nz_erofs_map_blocks_ext(", moved_start)
|
||||
moved = before[moved_start:moved_end]
|
||||
without_moved = before[:moved_start] + before[moved_end:]
|
||||
insert_at = without_moved.index("static int\nz_erofs_map_blocks_fo(")
|
||||
expected = without_moved[:insert_at] + moved + without_moved[insert_at:]
|
||||
if current != expected:
|
||||
raise SystemExit("zmap.c differs from the exact B24 validator-group move")
|
||||
|
||||
before_functions = {name: function(before, name) for name in function_order}
|
||||
current_functions = {name: function(current, name) for name in function_order}
|
||||
for name in function_order:
|
||||
if current_functions[name] != before_functions[name]:
|
||||
raise SystemExit(f"B24 changed function text instead of moving it: {name}")
|
||||
|
||||
def definition_position(source: str, name: str) -> int:
|
||||
match = re.search(
|
||||
r"^(?:" + re.escape(name) + r"|(?:static\s+)?"
|
||||
r"[A-Za-z_][A-Za-z0-9_ *]*\b" + re.escape(name) + r")\s*\(",
|
||||
source,
|
||||
re.MULTILINE,
|
||||
)
|
||||
if match is None:
|
||||
raise SystemExit(f"missing definition position: {name}")
|
||||
return match.start()
|
||||
|
||||
|
||||
positions = [definition_position(current, name) for name in function_order]
|
||||
if positions != sorted(positions):
|
||||
raise SystemExit("B24 zmap definition order does not match the reviewed order")
|
||||
for source, label in ((current, "FreeBSD"), (linux, "Linux")):
|
||||
positions = [definition_position(source, name) for name in linux_algorithm_order]
|
||||
if positions != sorted(positions):
|
||||
raise SystemExit(f"{label} common zmap algorithm order drifted")
|
||||
|
||||
def direct_calls(functions: dict[str, str]) -> Counter[tuple[str, str]]:
|
||||
calls: Counter[tuple[str, str]] = Counter()
|
||||
for caller, body in functions.items():
|
||||
brace = body.find("{")
|
||||
for callee in re.findall(r"\b([A-Za-z_][A-Za-z0-9_]*)\s*\(", body[brace + 1 :]):
|
||||
if callee not in {"if", "for", "while", "switch", "return", "sizeof"}:
|
||||
calls[(caller, callee)] += 1
|
||||
return calls
|
||||
|
||||
|
||||
before_calls = direct_calls(before_functions)
|
||||
current_calls = direct_calls(current_functions)
|
||||
if current_calls != before_calls:
|
||||
raise SystemExit("B24 changed the direct-call multiset")
|
||||
|
||||
global_definitions = sorted(
|
||||
name for name, body in current_functions.items() if not body.startswith("static ")
|
||||
)
|
||||
if global_definitions != ["z_erofs_fill_inode", "z_erofs_map_blocks"]:
|
||||
raise SystemExit(f"unexpected zmap global definitions: {global_definitions}")
|
||||
if current_internal != before_internal:
|
||||
raise SystemExit("B24 changed internal.h despite no removable zmap global")
|
||||
inode_source = (dut / "src/inode.c").read_text(encoding="utf-8")
|
||||
data_source = (dut / "src/data.c").read_text(encoding="utf-8")
|
||||
if inode_source.count("z_erofs_fill_inode(sbi, vi)") != 1:
|
||||
raise SystemExit("eager z_erofs_fill_inode no longer has its inode consumer")
|
||||
if data_source.count("z_erofs_map_blocks(sbi, vi, &next)") != 1:
|
||||
raise SystemExit("compressed map backend no longer has its common adapter consumer")
|
||||
for prototype in (
|
||||
"int z_erofs_fill_inode(struct erofs_sb_info *sbi, struct erofs_inode *vi);",
|
||||
"int z_erofs_map_blocks(struct erofs_sb_info *sbi, struct erofs_inode *vi,\n"
|
||||
" struct erofs_map_blocks *map);",
|
||||
):
|
||||
if current_internal.count(prototype) != 1:
|
||||
raise SystemExit(f"required FreeBSD zmap prototype drifted: {prototype!r}")
|
||||
if "int z_erofs_map_blocks_iter(struct inode *inode, struct erofs_map_blocks *map," not in linux:
|
||||
raise SystemExit("Linux zmap iterator naming anchor changed")
|
||||
|
||||
for marker in ("erofs_read_metadata(", "erofs_put_metabuf(", "goto out;"):
|
||||
if current.count(marker) != before.count(marker):
|
||||
raise SystemExit(f"B24 changed provider I/O or cleanup marker count: {marker}")
|
||||
if re.search(r"return\s*\(\s*-E[A-Z0-9_]+", current):
|
||||
raise SystemExit("B24 introduced Linux negative errno")
|
||||
|
||||
oracle = json.loads(oracle_path.read_text(encoding="ascii"))
|
||||
gate_input = json.loads(gate_input_path.read_text(encoding="ascii"))
|
||||
records = oracle.get("records", [])
|
||||
if oracle.get("map_case_count") != 80 or len(records) != 80:
|
||||
raise SystemExit("frozen G02/B07 map tuple denominator changed")
|
||||
if gate_input.get("expected_device_case_count") != 13:
|
||||
raise SystemExit("frozen G02/B07 device denominator changed")
|
||||
tuple_bytes = b"".join(bytes.fromhex(record["tuple_hex"]) for record in records)
|
||||
tuple_digest = hashlib.sha256(tuple_bytes).hexdigest()
|
||||
if tuple_digest != oracle.get("tuple_bytes_sha256"):
|
||||
raise SystemExit("frozen G02/B07 tuple bytes no longer match their digest")
|
||||
if len({record["id"] for record in records}) != 80:
|
||||
raise SystemExit("frozen G02/B07 tuple IDs are not unique")
|
||||
|
||||
changed = set(
|
||||
subprocess.run(
|
||||
["git", "-C", str(root), "diff", "--name-only", baseline, "--", "repo-pre-15"],
|
||||
check=True,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
).stdout.splitlines()
|
||||
)
|
||||
changed.update(
|
||||
subprocess.run(
|
||||
[
|
||||
"git", "-C", str(root), "ls-files", "--others", "--exclude-standard",
|
||||
"--", "repo-pre-15",
|
||||
],
|
||||
check=True,
|
||||
text=True,
|
||||
stdout=subprocess.PIPE,
|
||||
).stdout.splitlines()
|
||||
)
|
||||
allowed = {
|
||||
"repo-pre-15/src/zmap.c",
|
||||
"repo-pre-15/tests/pre15/cases/B24-zmap-order.sh",
|
||||
}
|
||||
if changed != allowed:
|
||||
raise SystemExit(f"B24 write set differs: {sorted(changed ^ allowed)}")
|
||||
|
||||
function_digest = hashlib.sha256(
|
||||
"\n\n".join(current_functions[name] for name in function_order).encode("utf-8")
|
||||
).hexdigest()
|
||||
report = {
|
||||
"baseline": baseline,
|
||||
"batch": "B24",
|
||||
"candidate": "P15-016",
|
||||
"direct_call_edges": sum(current_calls.values()),
|
||||
"freebsd_backend_name": "z_erofs_map_blocks",
|
||||
"freebsd_positive_errno": True,
|
||||
"function_count": len(function_order),
|
||||
"function_text_sha256": function_digest,
|
||||
"global_definitions": global_definitions,
|
||||
"linux_iterator_name": "z_erofs_map_blocks_iter",
|
||||
"map_cases": len(records),
|
||||
"device_cases": gate_input["expected_device_case_count"],
|
||||
"provider_io_unchanged": True,
|
||||
"metadata_cleanup_unchanged": True,
|
||||
"status": "PASS",
|
||||
"tuple_bytes_sha256": tuple_digest,
|
||||
"write_set": sorted(changed),
|
||||
}
|
||||
report_path.write_text(
|
||||
json.dumps(report, ensure_ascii=True, indent=2, sort_keys=True) + "\n",
|
||||
encoding="ascii",
|
||||
)
|
||||
print(
|
||||
"B24 order/callgraph PASS "
|
||||
f"functions={len(function_order)} calls={sum(current_calls.values())} globals=2"
|
||||
)
|
||||
print(
|
||||
"B24 map tuples PASS "
|
||||
f"maps={len(records)} devices={gate_input['expected_device_case_count']} "
|
||||
f"sha256={tuple_digest}"
|
||||
)
|
||||
PY
|
||||
then
|
||||
:
|
||||
else
|
||||
pre15_dut_fail 'B24 order, callgraph, visibility, or tuple proof failed'
|
||||
fi
|
||||
|
||||
sha256sum "$artifacts/B24-zmap-order.json" >"$artifacts/SHA256SUMS"
|
||||
printf '%s\n' \
|
||||
'B24 host PASS exact validator move and Linux algorithm order' \
|
||||
'B24 host PASS frozen G02 map tuples; QEMU and full feature suite NOT_RUN'
|
||||
Reference in New Issue
Block a user