#!/usr/bin/env python3 """Independently verify B23 explicit-extent images and expectations.""" from __future__ import annotations import argparse import hashlib import json import math from pathlib import Path import struct SUPER = 1024 MAGIC = 0xE0F5E1E2 FEATURE_COMPAT_SB_CHKSUM = 0x00000001 METABOX_NID_BIT = 1 << 63 BLOCK_SIZE = 4096 CRC32C_POLYNOMIAL = 0x82F63B78 class OracleError(RuntimeError): pass def sha256(data: bytes) -> str: return hashlib.sha256(data).hexdigest() def crc32c(data: bytes | bytearray) -> int: checksum = 0xFFFFFFFF for byte in data: checksum ^= byte for _ in range(8): checksum = (checksum >> 1) ^ ( CRC32C_POLYNOMIAL if checksum & 1 else 0 ) return checksum & 0xFFFFFFFF def u16(data: bytes, offset: int) -> int: return struct.unpack_from(" int: return struct.unpack_from(" int: return struct.unpack_from(" tuple[int, int]: if len(data) < SUPER + 144 or u32(data, SUPER) != MAGIC: raise OracleError("invalid EROFS superblock") block_bits = data[SUPER + 12] if block_bits != 12: raise OracleError("B23 image block size changed") declared_size = u32(data, SUPER + 36) << block_bits if declared_size > len(data): raise OracleError("declared image exceeds provider") if u32(data, SUPER + 8) & FEATURE_COMPAT_SB_CHKSUM: window = bytearray(data[SUPER : 1 << block_bits]) expected = u32(window, 4) struct.pack_into(" bytes: return bytes((index * 37 + 11) & 0xFF for index in range(BLOCK_SIZE)) def changed_offsets(left: bytes, right: bytes) -> list[int]: if len(left) != len(right): raise OracleError("negative and baseline provider sizes differ") return [index for index, pair in enumerate(zip(left, right)) if pair[0] != pair[1]] def verify_mutation(directory: Path, image: bytes, case: dict[str, object]) -> None: mutation = case.get("mutation") baseline_name = case.get("baseline") if mutation is None: if baseline_name is not None: raise OracleError("positive case unexpectedly names a baseline") return if not isinstance(mutation, dict) or not isinstance(baseline_name, str): raise OracleError("negative case lacks mutation evidence") baseline = (directory / baseline_name).read_bytes() differences = changed_offsets(image, baseline) start = int(mutation["offset"]) end = start + int(mutation["size"]) if not differences or any(offset < start or offset >= end for offset in differences): raise OracleError(f"mutation is not confined to {mutation['field']}") def record_values( image: bytes, case: dict[str, object] ) -> tuple[list[int], list[int], list[int]]: record_size = int(case["record_size"]) record_offset = int(case["record_image_offset"]) stored_count = int(case["stored_count"]) payload_offset = int(case["payload_offset"]) plens: list[int] = [] pstarts: list[int] = [] lstarts: list[int] = [] physical = u64(image, int(case["header_image_offset"]) + 8) if record_size == 4 else 0 for index in range(stored_count): offset = record_offset + index * record_size if offset + record_size > len(image): raise OracleError("stored record exceeds provider bytes") plen = u32(image, offset) if record_size == 4: pstart = physical physical += plen & ((2 * 1024 * 1024) - 1) lstart = index * BLOCK_SIZE else: pstart = u32(image, offset + 4) if record_size >= 16: pstart |= u32(image, offset + 8) << 32 lstart = u32(image, offset + 12) if record_size == 32: lstart |= u32(image, offset + 16) << 32 else: lstart = index * BLOCK_SIZE plens.append(plen) pstarts.append(pstart) lstarts.append(lstart) if any(plen != BLOCK_SIZE for plen in plens): raise OracleError("mapped shifted extent length changed") if any(pstart != payload_offset for pstart in pstarts): raise OracleError("mapped shifted extent physical offset changed") return plens, pstarts, lstarts def verify_case( directory: Path, spec_case: dict[str, object], case: dict[str, object], chunk_size: int, ) -> dict[str, object]: image_path = directory / str(case["image"]) image = image_path.read_bytes() block_size, declared_size = verify_super(image) if sha256(image) != case["image_sha256"]: raise OracleError(f"image hash mismatch: {image_path.name}") if image[int(case["payload_offset"]) : int(case["payload_offset"]) + block_size] != expected_payload(): raise OracleError(f"payload block mismatch: {image_path.name}") verify_mutation(directory, image, case) if u64(image, int(case["target_dirent_offset"])) != int(case["target_nid"]): raise OracleError("target dirent NID changed") if case["backing"] == "metabox": if not int(case["target_nid"]) & METABOX_NID_BIT: raise OracleError("metabox target lacks bit-63 NID") elif int(case["target_nid"]) & METABOX_NID_BIT: raise OracleError("primary target unexpectedly has bit-63 NID") inode = int(case["inode_image_offset"]) inode_format = u16(image, inode) if inode_format != 3 or u16(image, inode + 4) != 0o100444: raise OracleError("synthetic inode format or mode changed") if u64(image, inode + 8) != int(case["file_size"]): raise OracleError("synthetic inode size changed") header = int(case["header_image_offset"]) advise = u16(image, header + 4) observed_record_size = 4 << ((advise >> 1) & 3) if not advise & 1 or observed_record_size != int(case["record_size"]): raise OracleError("explicit extent advise changed") declared_count = u32(image, header) | (u16(image, header + 6) << 32) if int(case["record_size"]) >= 16 and declared_count != int(case["declared_count"]): raise OracleError("explicit extent count changed") _, _, observed_lstarts = record_values(image, case) if observed_lstarts != case["lstarts"]: raise OracleError("little-endian lstart decode differs from the manifest") outcome = str(spec_case["outcome"]) violations = [ index for index in range(1, len(observed_lstarts)) if observed_lstarts[index] <= observed_lstarts[index - 1] ] if spec_case.get("mutation") == "descending-lstart": if violations != [2]: raise OracleError("ordering negative lacks its single violation") elif violations: raise OracleError("non-ordering case has unordered logical starts") if outcome == "PASS": if int(case["expected_errno"]) != 0: raise OracleError("positive case has an errno") if int(case["record_size"]) <= 8: expected_calls = 1 else: expected_calls = math.ceil( int(case["stored_count"]) * int(case["record_size"]) / chunk_size ) if int(case["expected_scan_calls"]) != expected_calls: raise OracleError("bounded scan call count changed") if int(case["stored_count"]) >= 4096 and expected_calls >= int(case["stored_count"]): raise OracleError("large table regressed to per-record I/O") elif int(case["expected_errno"]) != 97: raise OracleError("corruption case does not use positive EINTEGRITY") mutation = spec_case.get("mutation") if mutation == "missing-tail": requested_end = int(case["record_logical_offset"]) + 2 * int(case["record_size"]) if requested_end <= declared_size: raise OracleError("primary missing-tail request remains provider-backed") elif mutation == "tail-minus-one": if int(case["carrier_size"]) != int(case["table_end_logical"]): raise OracleError("metabox baseline does not end at the final record") if u64(image, int(case["carrier_inode_offset"]) + 8) + 1 != int(case["table_end_logical"]): raise OracleError("metabox tail is not exactly one byte short") elif mutation == "huge-count": if int(case["declared_count"]) <= (1 << 32): raise OracleError("huge-count fixture does not exercise 48-bit count decode") return { "backing": case["backing"], "expected_scan_calls": case["expected_scan_calls"], "name": spec_case["name"], "outcome": outcome, "record_size": case["record_size"], "stored_count": case["stored_count"], "target_marker": case["target_marker"], } def verify(spec_path: Path, directory: Path, report_path: Path) -> None: spec = json.loads(spec_path.read_text(encoding="ascii")) manifest = json.loads((directory / "fixture-manifest.json").read_text(encoding="ascii")) if ( spec.get("schema") != 1 or spec.get("batch") != "B23" or spec.get("test") != "TC171-explicit-extents" or spec.get("candidates") != ["P15-060", "P15-085"] ): raise OracleError("B23 spec identity changed") if ( manifest.get("schema") != 1 or manifest.get("batch") != "B23" or manifest.get("test") != spec["test"] or manifest.get("chunk_size") != spec["chunk_size"] ): raise OracleError("B23 manifest identity changed") if spec.get("errno") != { "corruption": 97, "provider_io": "unchanged", "sign": "positive", }: raise OracleError("B23 errno contract changed") cases = manifest.get("cases") if not isinstance(cases, dict) or set(cases) != { str(case["name"]) for case in spec["cases"] }: raise OracleError("B23 case set changed") decoded = [] markers = set() for spec_case in spec["cases"]: name = str(spec_case["name"]) case = cases[name] if case["target_marker"] != spec_case["target_marker"]: raise OracleError("target marker changed") markers.add(str(case["target_marker"])) decoded.append( verify_case(directory, spec_case, case, int(spec["chunk_size"])) ) if len(markers) != len(spec["cases"]): raise OracleError("target markers are not unique") if {int(case["record_size"]) for case in decoded} != {4, 8, 16, 32}: raise OracleError("record-size coverage is incomplete") if {str(case["backing"]) for case in decoded} != {"primary", "metabox"}: raise OracleError("backing coverage is incomplete") report = { "batch": "B23", "case_count": len(decoded), "cases": decoded, "chunk_size": spec["chunk_size"], "large_scan_calls": { str(case["name"]): case["expected_scan_calls"] for case in decoded if int(case["stored_count"]) >= 4096 }, "status": "PASS", "test": spec["test"], } report_path.write_text( json.dumps(report, ensure_ascii=True, indent=2, sort_keys=True) + "\n", encoding="ascii", ) print( f"B23 independent oracle PASS cases={len(decoded)} " f"markers={len(markers)}" ) def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--spec", required=True, type=Path) parser.add_argument("--fixtures", required=True, type=Path) parser.add_argument("--report", required=True, type=Path) return parser.parse_args() def main() -> None: args = parse_args() verify(args.spec, args.fixtures, args.report) if __name__ == "__main__": try: main() except (OracleError, OSError, ValueError, KeyError, struct.error) as error: raise SystemExit(f"B23 oracle failed: {error}") from error