Files
erofs-freebsd-out-tree/tests/pre15/cases/B05-fields.sh
T
2026-08-18 09:20:44 +02:00

191 lines
6.1 KiB
Bash
Executable File

#!/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"
internal=$PRE15_DUT/src/internal.h
data=$PRE15_DUT/src/data.c
inode=$PRE15_DUT/src/inode.c
super=$PRE15_DUT/src/super.c
zmap=$PRE15_DUT/src/zmap.c
linux_internal=$PRE15_ROOT/src-linux/internal.h
artifacts=$PRE15_RUN_DIR/artifacts
for tool in python3 sha256sum; do
command -v "$tool" >/dev/null 2>&1 || \
pre15_infra_blocked "missing B05 host tool: $tool"
done
pre15_record_fixture b05-freebsd-internal "$internal"
pre15_record_fixture b05-freebsd-data "$data"
pre15_record_fixture b05-freebsd-inode "$inode"
pre15_record_fixture b05-freebsd-super "$super"
pre15_record_fixture b05-freebsd-zmap "$zmap"
pre15_record_fixture b05-linux-internal "$linux_internal"
mkdir -p "$artifacts"
pre15_target_reached
if python3 - "$PRE15_ROOT" "$artifacts/field-check.json" <<'PY'
from __future__ import annotations
import json
from pathlib import Path
import re
import sys
root = Path(sys.argv[1])
output = Path(sys.argv[2])
dut_src = root / "repo-pre-15" / "src"
internal = (dut_src / "internal.h").read_text(encoding="utf-8")
data = (dut_src / "data.c").read_text(encoding="utf-8")
inode = (dut_src / "inode.c").read_text(encoding="utf-8")
super_source = (dut_src / "super.c").read_text(encoding="utf-8")
zmap = (dut_src / "zmap.c").read_text(encoding="utf-8")
linux = (root / "src-linux" / "internal.h").read_text(encoding="utf-8")
def struct_body(source: str, name: str) -> str:
match = re.search(
rf"^struct {re.escape(name)} \{{\n(?P<body>.*?)^\}};",
source,
re.MULTILINE | re.DOTALL,
)
if not match:
raise SystemExit(f"missing struct {name}")
return match.group("body")
def fields(source: str, name: str) -> list[str]:
result = []
for line in struct_body(source, name).splitlines():
declaration = line.strip()
if not declaration or declaration.startswith("/*"):
continue
match = re.search(r"([A-Za-z_][A-Za-z0-9_]*)\s*(?:\[[^]]+\])?;$", declaration)
if not match:
raise SystemExit(f"unparsed field in struct {name}: {declaration!r}")
result.append(match.group(1))
return result
freebsd_device = fields(internal, "erofs_device_info")
linux_device = fields(linux, "erofs_device_info")
expected_device = [
"devvp",
"dev",
"cp",
"mediasize",
"sectorsize",
"blocks",
"uniaddr",
]
if freebsd_device != expected_device:
raise SystemExit(f"FreeBSD device field order mismatch: {freebsd_device!r}")
if [field for field in linux_device if field in {"blocks", "uniaddr"}] != [
"blocks",
"uniaddr",
]:
raise SystemExit(f"Linux device common-field order mismatch: {linux_device!r}")
freebsd_map = fields(internal, "erofs_map_dev")
linux_map = fields(linux, "erofs_map_dev")
expected_map = ["m_dif", "m_pa", "m_deviceid", "m_plen"]
if freebsd_map != expected_map:
raise SystemExit(f"FreeBSD map field order mismatch: {freebsd_map!r}")
if [field for field in linux_map if field in {"m_dif", "m_pa", "m_deviceid"}] != [
"m_dif",
"m_pa",
"m_deviceid",
]:
raise SystemExit(f"Linux map common-field order mismatch: {linux_map!r}")
inode_fields = fields(internal, "erofs_inode")
for removed in ("ino", "inline_data"):
if removed in inode_fields:
raise SystemExit(f"removed inode field remains: {removed}")
if "m_sbi" in freebsd_map:
raise SystemExit("removed map back-pointer remains")
removed_uses = {
"map back-pointer": sum(
source.count("m_sbi") for source in (internal, data, inode, super_source, zmap)
),
"saved ondisk ino": len(re.findall(r"\bvi->ino\b", inode)),
"inline-data cache": len(re.findall(r"\bvi->inline_data\b", inode)),
}
if any(removed_uses.values()):
raise SystemExit(f"removed field use remains: {removed_uses!r}")
helper = re.search(
r"erofs_fill_from_devinfo\(struct erofs_map_dev \*map,\n"
r" struct erofs_device_info \*dif, erofs_off_t pa\)",
data,
)
if not helper:
raise SystemExit("map helper still accepts the removed super back-pointer")
if len(re.findall(r"erofs_fill_from_devinfo\(", data)) != 4:
raise SystemExit("unexpected map helper definition/call count")
initializer = re.search(
r"map = \(struct erofs_map_dev\) \{\n"
r"\t\t\.m_pa = off,\n"
r"\t\t\.m_deviceid = device_id,\n"
r"\t\t\.m_plen = len,\n"
r"\t\};",
data,
)
if not initializer:
raise SystemExit("erofs_map_dev initializer order mismatch")
required_markers = {
"raw nid identity": "vi->nid = nid;",
"layout source": "vi->datalayout = erofs_inode_datalayout(ifmt);",
"device vnode": "struct vnode *devvp;",
"GEOM consumer": "struct g_consumer *cp;",
"provider media size": "uint64_t mediasize;",
"provider sector size": "uint32_t sectorsize;",
"future volume label": "char volume_name[17];",
"deferred LZ4 state": "struct erofs_sb_lz4_info lz4;",
}
for label, marker in required_markers.items():
if marker not in internal and marker not in inode:
raise SystemExit(f"required ownership/source marker missing: {label}")
result = {
"freebsd_device_fields": freebsd_device,
"freebsd_map_fields": freebsd_map,
"linux_device_common_fields": [
field for field in linux_device if field in {"blocks", "uniaddr"}
],
"linux_map_common_fields": [
field for field in linux_map if field in {"m_dif", "m_pa", "m_deviceid"}
],
"removed_field_uses": removed_uses,
"retained_freebsd_ownership_fields": [
"devvp",
"dev",
"cp",
"mediasize",
"sectorsize",
],
}
with output.open("x", encoding="ascii") as stream:
json.dump(result, stream, ensure_ascii=True, indent=2, sort_keys=True)
stream.write("\n")
print("fields PASS device=7 map=4 removed-uses=0")
PY
then
:
else
pre15_dut_fail 'B05 field layout or write-only closure oracle failed'
fi
sha256sum "$artifacts/field-check.json" > "$artifacts/SHA256SUMS"
printf 'B05 PASS private field model and zero-consumer closure\n'