191 lines
6.1 KiB
Bash
Executable File
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'
|