#!/usr/bin/env python3 """Создание и проверка детерминированного HDD-набора для P4 copy.""" from __future__ import annotations import pathlib import sys SOURCE_FILES = { "CANCEL.BIN": (4 * 1024 * 1024 + 37, 0x31), "EMPTY.ZRO": (0, 0x42), "EXIST.TXT": (513, 0x53), "F65535.BIN": (65535, 0x64), "F65536.BIN": (65536, 0x75), "K4095.BIN": (4095, 0x86), "K4096.BIN": (4096, 0x97), "LARGE.BIN": (131109, 0xA8), "ONE.BIN": (1, 0xB9), } TARGET_EXISTING = b"TARGET MUST SURVIVE AN F5 REFUSAL\n" def pattern(size: int, seed: int) -> bytes: return bytes(((index * 37 + (index >> 8) * 11 + seed) & 0xFF) for index in range(size)) def create(root: pathlib.Path) -> None: source = root / "COPYFROM" target = root / "COPYTO" source.mkdir(parents=True) target.mkdir(parents=True) for name, (size, seed) in SOURCE_FILES.items(): (source / name).write_bytes(pattern(size, seed)) (target / "EXIST.TXT").write_bytes(TARGET_EXISTING) def verify_common(root: pathlib.Path) -> list[str]: source = root / "COPYFROM" target = root / "COPYTO" failures: list[str] = [] for name, (size, seed) in SOURCE_FILES.items(): path = source / name expected = pattern(size, seed) if not path.is_file() or path.read_bytes() != expected: failures.append(f"повреждён источник {name}") if not (target / "EXIST.TXT").is_file(): failures.append("нет исходного COPYTO/EXIST.TXT") elif (target / "EXIST.TXT").read_bytes() != TARGET_EXISTING: failures.append("существующий COPYTO/EXIST.TXT был изменён") return failures def verify_initial(root: pathlib.Path) -> None: target = root / "COPYTO" failures = verify_common(root) if (target / "LARGE.BIN").exists(): failures.append("COPYTO/LARGE.BIN существует до теста") if (target / "CANCEL.BIN").exists(): failures.append("COPYTO/CANCEL.BIN существует до теста") if list(target.glob("~SC*.TMP")): failures.append("в исходном образе есть временный ~SC*.TMP") if failures: for failure in failures: print(f"FAIL: {failure}") raise SystemExit(1) print("PASS: исходный HDD-набор цел и готов к P4 copy-тесту.") def verify(root: pathlib.Path) -> None: target = root / "COPYTO" failures = verify_common(root) large_expected = pattern(*SOURCE_FILES["LARGE.BIN"]) if not (target / "LARGE.BIN").is_file(): failures.append("нет скопированного COPYTO/LARGE.BIN") elif (target / "LARGE.BIN").read_bytes() != large_expected: failures.append("COPYTO/LARGE.BIN отличается от источника") if (target / "CANCEL.BIN").exists(): failures.append("отменённый COPYTO/CANCEL.BIN остался на диске") if not (target / "EMPTY.ZRO").is_file(): failures.append("нет скопированного нулевого COPYTO/EMPTY.ZRO") elif (target / "EMPTY.ZRO").stat().st_size != 0: failures.append("COPYTO/EMPTY.ZRO имеет ненулевой размер") if list(target.glob("~SC*.TMP")): failures.append("после операции остался временный ~SC*.TMP") if failures: for failure in failures: print(f"FAIL: {failure}") raise SystemExit(1) print("PASS: LARGE/EMPTY скопированы; EXIST сохранён; cancel/temp отсутствуют.") def main() -> None: commands = {"create", "verify-initial", "verify"} if len(sys.argv) != 3 or sys.argv[1] not in commands: raise SystemExit( f"usage: {sys.argv[0]} create|verify-initial|verify DIR") root = pathlib.Path(sys.argv[2]) if sys.argv[1] == "create": create(root) elif sys.argv[1] == "verify-initial": verify_initial(root) else: verify(root) if __name__ == "__main__": main()