This commit is contained in:
2026-08-18 09:20:44 +02:00
commit b826cd721a
522 changed files with 93730 additions and 0 deletions
+448
View File
@@ -0,0 +1,448 @@
#!/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=1b4b8abc249d9bbbb75e19b5259c5f46f91909ef
artifacts=$PRE15_RUN_DIR/artifacts
for tool in git python3 sha256sum; do
command -v "$tool" >/dev/null 2>&1 || \
pre15_infra_blocked "missing B30 host tool: $tool"
done
for source in internal.h compress.h decompressor.c decompressor_lz4.c \
decompressor_lzma.c decompressor_deflate.c decompressor_zstd.c zdata.c zmap.c; do
pre15_record_fixture "b30-$source" "$PRE15_DUT/src/$source"
done
pre15_record_fixture b30-linux-compress "$PRE15_ROOT/src-linux/compress.h"
pre15_record_fixture b30-linux-decompressor "$PRE15_ROOT/src-linux/decompressor.c"
pre15_record_fixture b30-case \
"$PRE15_DUT/tests/pre15/cases/B30-codec-structure.sh"
mkdir -p "$artifacts"
pre15_target_reached
if python3 -B - "$PRE15_ROOT" "$PRE15_DUT" "$baseline" \
"$artifacts/B30-codec-structure.json" <<'PY'
from __future__ import annotations
from collections import Counter
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]
report_path = Path(sys.argv[4])
src = dut / "src"
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 B30 baseline {path}: {completed.stderr}")
return completed.stdout
def replace_once(source: str, old: str, new: str, label: str) -> str:
count = source.count(old)
if count != 1:
raise SystemExit(f"B30 baseline anchor count for {label}: {count}")
return source.replace(old, new, 1)
expected_changed = {
"repo-pre-15/src/compress.h",
"repo-pre-15/src/decompressor.c",
"repo-pre-15/src/decompressor_deflate.c",
"repo-pre-15/src/decompressor_lzma.c",
"repo-pre-15/src/decompressor_zstd.c",
"repo-pre-15/src/internal.h",
"repo-pre-15/src/zdata.c",
"repo-pre-15/tests/pre15/cases/B30-codec-structure.sh",
}
changed = subprocess.run(
["git", "-C", str(root), "diff", "--name-only", baseline, "--", "repo-pre-15"],
check=True,
text=True,
stdout=subprocess.PIPE,
).stdout.splitlines()
changed.extend(
subprocess.run(
[
"git",
"-C",
str(root),
"ls-files",
"--others",
"--exclude-standard",
"--",
"repo-pre-15",
],
check=True,
text=True,
stdout=subprocess.PIPE,
).stdout.splitlines()
)
if set(changed) != expected_changed or len(changed) != len(expected_changed):
raise SystemExit(f"B30 repo-pre-15 write set mismatch: {changed!r}")
current = {
path: (src / path).read_text(encoding="utf-8")
for path in (
"internal.h",
"compress.h",
"decompressor.c",
"decompressor_lz4.c",
"decompressor_lzma.c",
"decompressor_deflate.c",
"decompressor_zstd.c",
"zdata.c",
"zmap.c",
)
}
before = {
path: committed(f"src/{path}")
for path in current
}
expected = before["internal.h"]
expected = replace_once(
expected,
"struct erofs_sb_lz4_info {\n"
"\tuint16_t max_distance_pages;\n"
"\tuint16_t max_pclusterblks;\n"
"};\n\n",
"",
"LZ4 state type",
)
expected = replace_once(
expected,
"\tstruct erofs_sb_lz4_info lz4;\n",
"",
"LZ4 state member",
)
if current["internal.h"] != expected:
raise SystemExit("internal.h differs from exact B30 dead-state removal")
expected = before["compress.h"]
expected = replace_once(
expected,
"struct z_erofs_decompressor {\n"
"\tconst char *name;\n"
"\tbool supports_subextent;\n"
"\t/* Callbacks return zero or a positive FreeBSD errno. */\n"
"\tint (*config)(struct erofs_sb_info *, const struct erofs_super_block *,\n"
"\t const void *, size_t);\n"
"\tint (*decompress)(const struct z_erofs_decompress_req *);\n"
"};",
"struct z_erofs_decompressor {\n"
"\t/* Callbacks return zero or a positive FreeBSD errno. */\n"
"\tint (*config)(struct erofs_sb_info *, const struct erofs_super_block *,\n"
"\t const void *, size_t);\n"
"\tint (*decompress)(const struct z_erofs_decompress_req *);\n"
"\tbool supports_subextent;\n"
"\tconst char *name;\n"
"};",
"descriptor fields",
)
if current["compress.h"] != expected:
raise SystemExit("compress.h differs from exact B30 descriptor alignment")
expected = before["decompressor.c"]
for old, new, label in (
("\t\tdistance = le16toh(lz4->max_distance);\n", "", "unused cfg distance"),
("\t\tmax_pclusterblks = 1;\n\t\tsbi->available_compr_algs", "\t\tsbi->available_compr_algs", "legacy dead local store"),
(
"\tsbi->lz4.max_pclusterblks = max_pclusterblks;\n"
"\tsbi->lz4.max_distance_pages = distance != 0 ?\n"
"\t howmany(distance, PAGE_SIZE) + 1 :\n"
"\t howmany(UINT16_MAX, PAGE_SIZE) + 1;\n",
"",
"runtime LZ4 stores",
),
(
"static const struct z_erofs_decompressor z_erofs_shifted_decomp = {\n"
"\t.name = \"shifted\",\n"
"\t.decompress = z_erofs_transform_plain,\n"
"};",
"static const struct z_erofs_decompressor z_erofs_shifted_decomp = {\n"
"\t.decompress = z_erofs_transform_plain,\n"
"\t.name = \"shifted\",\n"
"};",
"shifted descriptor",
),
(
"static const struct z_erofs_decompressor z_erofs_interlaced_decomp = {\n"
"\t.name = \"interlaced\",\n"
"\t.decompress = z_erofs_transform_plain,\n"
"};",
"static const struct z_erofs_decompressor z_erofs_interlaced_decomp = {\n"
"\t.decompress = z_erofs_transform_plain,\n"
"\t.name = \"interlaced\",\n"
"};",
"interlaced descriptor",
),
(
"static const struct z_erofs_decompressor z_erofs_lz4_decomp = {\n"
"\t.name = \"lz4\",\n"
"\t.supports_subextent = 1,\n"
"\t.config = z_erofs_load_lz4_config,\n"
"\t.decompress = z_erofs_lz4_decompress,\n"
"};",
"static const struct z_erofs_decompressor z_erofs_lz4_decomp = {\n"
"\t.config = z_erofs_load_lz4_config,\n"
"\t.decompress = z_erofs_lz4_decompress,\n"
"\t.supports_subextent = 1,\n"
"\t.name = \"lz4\",\n"
"};",
"LZ4 descriptor",
),
(
"static const struct z_erofs_decompressor * const z_erofs_decomp[] = {\n"
"\t[Z_EROFS_COMPRESSION_LZ4] = &z_erofs_lz4_decomp,\n"
"\t[Z_EROFS_COMPRESSION_LZMA] = &z_erofs_lzma_decomp,\n"
"\t[Z_EROFS_COMPRESSION_DEFLATE] = &z_erofs_deflate_decomp,\n"
"\t[Z_EROFS_COMPRESSION_ZSTD] = &z_erofs_zstd_decomp,\n"
"\t[Z_EROFS_COMPRESSION_SHIFTED] = &z_erofs_shifted_decomp,\n"
"\t[Z_EROFS_COMPRESSION_INTERLACED] = &z_erofs_interlaced_decomp,\n"
"};",
"static const struct z_erofs_decompressor * const z_erofs_decomp[] = {\n"
"\t[Z_EROFS_COMPRESSION_SHIFTED] = &z_erofs_shifted_decomp,\n"
"\t[Z_EROFS_COMPRESSION_INTERLACED] = &z_erofs_interlaced_decomp,\n"
"\t[Z_EROFS_COMPRESSION_LZ4] = &z_erofs_lz4_decomp,\n"
"\t[Z_EROFS_COMPRESSION_LZMA] = &z_erofs_lzma_decomp,\n"
"\t[Z_EROFS_COMPRESSION_DEFLATE] = &z_erofs_deflate_decomp,\n"
"\t[Z_EROFS_COMPRESSION_ZSTD] = &z_erofs_zstd_decomp,\n"
"};",
"decompressor table",
),
):
expected = replace_once(expected, old, new, label)
if current["decompressor.c"] != expected:
raise SystemExit("decompressor.c differs from exact B30 structural edits")
for path, name, capability in (
("decompressor_lzma.c", "lzma", "1"),
("decompressor_deflate.c", "deflate", "1"),
("decompressor_zstd.c", "zstd", "0"),
):
expected = before[path]
expected = replace_once(
expected,
f"const struct z_erofs_decompressor z_erofs_{name}_decomp = {{\n"
f"\t.name = \"{name}\",\n"
f"\t.supports_subextent = {capability},\n"
f"\t.config = z_erofs_load_{name}_config,\n"
f"\t.decompress = z_erofs_{name}_decompress,\n"
"};",
f"const struct z_erofs_decompressor z_erofs_{name}_decomp = {{\n"
f"\t.config = z_erofs_load_{name}_config,\n"
f"\t.decompress = z_erofs_{name}_decompress,\n"
f"\t.supports_subextent = {capability},\n"
f"\t.name = \"{name}\",\n"
"};",
f"{name} descriptor",
)
if current[path] != expected:
raise SystemExit(f"{path} differs from exact B30 descriptor reorder")
if current["decompressor_lz4.c"] != before["decompressor_lz4.c"]:
raise SystemExit("B30 changed the independent FreeBSD LZ4 backend")
expected = before["zdata.c"]
if expected.count("z_erofs_read_extent") != 2:
raise SystemExit("B30 zdata baseline rename denominator changed")
expected = expected.replace("z_erofs_read_extent", "z_erofs_decode_extent")
if current["zdata.c"] != expected:
raise SystemExit("zdata.c differs from the exact two-token decode rename")
if current["zmap.c"] != before["zmap.c"]:
raise SystemExit("B30 changed the zmap record reader")
descriptor_markers = [
"int (*config)(",
"int (*decompress)(",
"bool supports_subextent;",
"const char *name;",
]
positions = [current["compress.h"].index(marker) for marker in descriptor_markers]
if positions != sorted(positions):
raise SystemExit("FreeBSD descriptor field order drifted")
def initializer_fields(source: str, object_name: str) -> list[tuple[str, str]]:
match = re.search(
rf"(?:static )?const struct z_erofs_decompressor {object_name} = \{{\n"
rf"(?P<body>.*?)\n\}};",
source,
re.DOTALL,
)
if match is None:
raise SystemExit(f"missing descriptor initializer: {object_name}")
return re.findall(r"^\s*\.([a-z_]+)\s*=\s*([^,]+),$", match.group("body"), re.MULTILINE)
descriptors = {
"shifted": initializer_fields(current["decompressor.c"], "z_erofs_shifted_decomp"),
"interlaced": initializer_fields(current["decompressor.c"], "z_erofs_interlaced_decomp"),
"lz4": initializer_fields(current["decompressor.c"], "z_erofs_lz4_decomp"),
"lzma": initializer_fields(current["decompressor_lzma.c"], "z_erofs_lzma_decomp"),
"deflate": initializer_fields(current["decompressor_deflate.c"], "z_erofs_deflate_decomp"),
"zstd": initializer_fields(current["decompressor_zstd.c"], "z_erofs_zstd_decomp"),
}
expected_descriptors = {
"shifted": [("decompress", "z_erofs_transform_plain"), ("name", '"shifted"')],
"interlaced": [("decompress", "z_erofs_transform_plain"), ("name", '"interlaced"')],
"lz4": [
("config", "z_erofs_load_lz4_config"),
("decompress", "z_erofs_lz4_decompress"),
("supports_subextent", "1"),
("name", '"lz4"'),
],
"lzma": [
("config", "z_erofs_load_lzma_config"),
("decompress", "z_erofs_lzma_decompress"),
("supports_subextent", "1"),
("name", '"lzma"'),
],
"deflate": [
("config", "z_erofs_load_deflate_config"),
("decompress", "z_erofs_deflate_decompress"),
("supports_subextent", "1"),
("name", '"deflate"'),
],
"zstd": [
("config", "z_erofs_load_zstd_config"),
("decompress", "z_erofs_zstd_decompress"),
("supports_subextent", "0"),
("name", '"zstd"'),
],
}
if descriptors != expected_descriptors:
raise SystemExit(f"backend descriptor initializer mismatch: {descriptors!r}")
table_match = re.search(
r"static const struct z_erofs_decompressor \* const z_erofs_decomp\[\] = \{\n"
r"(?P<body>.*?)\n\};",
current["decompressor.c"],
re.DOTALL,
)
if table_match is None:
raise SystemExit("missing FreeBSD decompressor table")
table_order = re.findall(r"\[(Z_EROFS_COMPRESSION_[A-Z0-9_]+)\]", table_match.group("body"))
expected_order = [
"Z_EROFS_COMPRESSION_SHIFTED",
"Z_EROFS_COMPRESSION_INTERLACED",
"Z_EROFS_COMPRESSION_LZ4",
"Z_EROFS_COMPRESSION_LZMA",
"Z_EROFS_COMPRESSION_DEFLATE",
"Z_EROFS_COMPRESSION_ZSTD",
]
if table_order != expected_order:
raise SystemExit(f"FreeBSD decompressor table order mismatch: {table_order!r}")
linux_decompressor = (root / "src-linux/decompressor.c").read_text(encoding="utf-8")
linux_table = linux_decompressor[linux_decompressor.index("z_erofs_decomp[] = {") :]
linux_order = re.findall(r"\[(Z_EROFS_COMPRESSION_[A-Z0-9_]+)\]", linux_table)[:6]
if linux_order != expected_order:
raise SystemExit(f"Linux decompressor table anchor drifted: {linux_order!r}")
callbacks = []
for path in sorted(src.glob("decompressor*.c")):
for line in path.read_text(encoding="utf-8").splitlines():
match = re.match(r"\s*\.(config|decompress)\s*=\s*([^,]+),", line)
if match:
callbacks.append((path.name, match.group(1), match.group(2)))
expected_callbacks = [
("decompressor.c", "decompress", "z_erofs_transform_plain"),
("decompressor.c", "decompress", "z_erofs_transform_plain"),
("decompressor.c", "config", "z_erofs_load_lz4_config"),
("decompressor.c", "decompress", "z_erofs_lz4_decompress"),
("decompressor_deflate.c", "config", "z_erofs_load_deflate_config"),
("decompressor_deflate.c", "decompress", "z_erofs_deflate_decompress"),
("decompressor_lzma.c", "config", "z_erofs_load_lzma_config"),
("decompressor_lzma.c", "decompress", "z_erofs_lzma_decompress"),
("decompressor_zstd.c", "config", "z_erofs_load_zstd_config"),
("decompressor_zstd.c", "decompress", "z_erofs_zstd_decompress"),
]
if Counter(callbacks) != Counter(expected_callbacks) or len(callbacks) != 10:
raise SystemExit(f"G01 decompressor callback multiset changed: {callbacks!r}")
all_source = "\n".join(current.values())
for removed in ("erofs_sb_lz4_info", "max_distance_pages", "sbi->lz4"):
if removed in all_source:
raise SystemExit(f"removed LZ4 runtime state remains: {removed}")
config = current["decompressor.c"][: current["decompressor.c"].index("static int\nz_erofs_transform_plain")]
for marker in (
"uint32_t max_pclusterblks;",
"max_pclusterblks = le16toh(lz4->max_pclusterblks);",
"if (max_pclusterblks == 0)",
"max_pclusterblks = 1;",
"max_pclusterblks >\n\t\t (Z_EROFS_PCLUSTER_MAX_SIZE >> sbi->blkszbits)",
"return (EOPNOTSUPP);",
"distance = le16toh(dsb->u1.lz4_max_distance);",
"if (distance == 0 && !erofs_sb_has_lz4_0padding(sbi))",
):
if marker not in config:
raise SystemExit(f"LZ4 config validation marker missing: {marker!r}")
if current["zdata.c"].count("z_erofs_decode_extent") != 2:
raise SystemExit("zdata decode helper rename denominator changed")
if "z_erofs_read_extent" in current["zdata.c"]:
raise SystemExit("old zdata decode helper name remains")
if current["zmap.c"].count("z_erofs_read_extent") != 4:
raise SystemExit("zmap record reader definition/calls changed")
if "z_erofs_decode_extent" in current["zmap.c"]:
raise SystemExit("zmap record reader was incorrectly renamed")
if re.search(r"return\s*\(\s*-E[A-Z0-9_]+", all_source):
raise SystemExit("B30 introduced Linux negative errno")
for path, load_name, decode_name, descriptor_name in (
("decompressor_lzma.c", "z_erofs_load_lzma_config", "z_erofs_lzma_decompress", "z_erofs_lzma_decomp"),
("decompressor_deflate.c", "z_erofs_load_deflate_config", "z_erofs_deflate_decompress", "z_erofs_deflate_decomp"),
("decompressor_zstd.c", "z_erofs_load_zstd_config", "z_erofs_zstd_decompress", "z_erofs_zstd_decomp"),
):
positions = [current[path].index(name) for name in (load_name, decode_name, descriptor_name)]
if positions != sorted(positions):
raise SystemExit(f"backend definition order drifted: {path}")
report = {
"status": "PASS",
"baseline": baseline,
"changed_paths": sorted(changed),
"descriptor_fields": ["config", "decompress", "supports_subextent", "name"],
"descriptor_table_order": table_order,
"callback_field_count": len(callbacks),
"callback_target_multiset": sorted(callbacks),
"removed_runtime_state": ["erofs_sb_lz4_info", "max_distance_pages", "max_pclusterblks store"],
"retained_config_validation": ["max_pclusterblks local", "format cap", "legacy distance branch"],
"rename": {"zdata_decode": 2, "zmap_record_reader": 4},
"preserved": ["B25 typed errno", "B27 trailing policy", "B28 partial fallback ownership"],
}
with report_path.open("x", encoding="ascii") as stream:
json.dump(report, stream, ensure_ascii=True, indent=2, sort_keys=True)
stream.write("\n")
print("B30-descriptors PASS fields=4 backends=6 callbacks=10")
print("B30-write-only PASS dead-state=2 config-local=1 rename=2/4")
PY
then
:
else
pre15_dut_fail 'B30 descriptor, dead-state, or rename oracle failed'
fi
sha256sum "$artifacts/B30-codec-structure.json" > "$artifacts/SHA256SUMS"
printf 'B30 PASS codec structure aligned without behavior changes\n'