mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-11 05:53:23 -05:00
fix: withhold a file only on the frontend digest
This commit is contained in:
1 parent
dbc8d63318
commit
ac4776b2d0
4 files changed
+130
No files matched your search
@@ -75,6 +75,7 @@ from nativemode import (
|
||||
)
|
||||
from validation import (
|
||||
agnostic_substitute,
|
||||
frontend_digest_matches,
|
||||
_build_validation_index,
|
||||
check_file_validation,
|
||||
filter_files_by_mode,
|
||||
@@ -885,6 +886,11 @@ def generate_pack(
|
||||
)
|
||||
if not zip_ok:
|
||||
continue
|
||||
elif frontend_digest_matches(
|
||||
file_entry, local_path, digest_algorithm(verification_mode)
|
||||
):
|
||||
status = "frontend_digest_exact"
|
||||
file_status.setdefault(dedup_key, "ok")
|
||||
else:
|
||||
file_status[dedup_key] = "excluded"
|
||||
file_reasons[dedup_key] = "hash mismatch"
|
||||
@@ -3108,6 +3114,16 @@ def generate_manifest(
|
||||
and _inner_rom_check(file_entry, local_path) == "ok"
|
||||
):
|
||||
status = "zip_exact"
|
||||
if (
|
||||
status == "hash_mismatch"
|
||||
and local_path
|
||||
and not file_entry.get("zipped_file")
|
||||
and hash_mismatch_excludes_file(verification_mode)
|
||||
and frontend_digest_matches(
|
||||
file_entry, local_path, digest_algorithm(verification_mode)
|
||||
)
|
||||
):
|
||||
status = "frontend_digest_exact"
|
||||
# An existence platform never reads the bytes, so a declared
|
||||
# hash the local dump contradicts is not a reason to withhold
|
||||
# the file. Hash platforms would reject it, so they omit it.
|
||||
|
||||
@@ -17,6 +17,7 @@ from packpaths import _register_path
|
||||
from ziptools import build_zip_contents_index
|
||||
from ziptools import check_inside_zip
|
||||
from nativemode import digest_algorithm
|
||||
from validation import frontend_digest_matches
|
||||
from nativemode import hash_mismatch_excludes_file
|
||||
import hashlib
|
||||
from common import filter_systems_by_target
|
||||
@@ -406,6 +407,11 @@ def _intentional_hash_exclusion(
|
||||
)
|
||||
if status != "hash_mismatch":
|
||||
return False
|
||||
if not entry.get("zipped_file") and frontend_digest_matches(
|
||||
entry, local_path, digest_algorithm(verification_mode)
|
||||
):
|
||||
# The frontend's own digest accepts it: the builder ships it.
|
||||
return False
|
||||
|
||||
# A container can mismatch the outer declaration while still carrying
|
||||
# the exact inner ROM requested by Batocera-style zipped_file entries.
|
||||
|
||||
@@ -431,3 +431,22 @@ def agnostic_substitute(
|
||||
return held, prefix
|
||||
break
|
||||
return None
|
||||
|
||||
|
||||
def frontend_digest_matches(file_entry: dict, local_path: str, algorithm: str) -> bool:
|
||||
"""Whether the frontend's own digest accepts the file.
|
||||
|
||||
resolve_local_file calls a file `hash_mismatch` when any declared hash
|
||||
disagrees, but a frontend compares one digest. RomM's list carries md5,
|
||||
sha1 and crc32: a mistyped crc32 next to the right md5 must not withhold
|
||||
a file RomM accepts. Same comparison as verify_entry_md5/_sha1.
|
||||
"""
|
||||
if algorithm == "md5":
|
||||
declared = parse_md5_list(file_entry.get("md5"))
|
||||
else:
|
||||
value = file_entry.get(algorithm)
|
||||
values = value if isinstance(value, list) else [value]
|
||||
declared = [str(v).strip().lower() for v in values if v]
|
||||
if not declared or not local_path:
|
||||
return False
|
||||
return compute_hashes(local_path)[algorithm].lower() in declared
|
||||
@@ -0,0 +1,89 @@
|
||||
"""A digest frontend withholds a file only when ITS digest disagrees.
|
||||
|
||||
RomM's list declares md5, sha1 and crc32 per file and RomM accepts any of
|
||||
them. The resolver calls a file `hash_mismatch` when any declared hash
|
||||
disagrees, so a mistyped crc32 beside the right md5 dropped the file from
|
||||
the pack and the manifest while verify, comparing md5, called it OK.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
|
||||
class FrontendDigestDecides(unittest.TestCase):
|
||||
def setUp(self):
|
||||
(REPO_ROOT / "tmp").mkdir(exist_ok=True)
|
||||
self.tmp = tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp")
|
||||
self.addCleanup(self.tmp.cleanup)
|
||||
root = Path(self.tmp.name)
|
||||
self.bios = root / "bios"
|
||||
self.bios.mkdir()
|
||||
payload = b"psx bios " * 64
|
||||
held = self.bios / "scph5501.bin"
|
||||
held.write_bytes(payload)
|
||||
md5 = hashlib.md5(payload).hexdigest()
|
||||
sha1 = hashlib.sha1(payload).hexdigest()
|
||||
self.platforms = root / "platforms"
|
||||
self.platforms.mkdir()
|
||||
(root / "emulators").mkdir()
|
||||
self.emulators = root / "emulators"
|
||||
(self.platforms / "_registry.yml").write_text(
|
||||
yaml.safe_dump({"platforms": {"digestplat": {"status": "active"}}})
|
||||
)
|
||||
(self.platforms / "digestplat.yml").write_text(yaml.safe_dump({
|
||||
"platform": "DigestPlat",
|
||||
"verification_mode": "md5",
|
||||
"base_destination": "bios",
|
||||
"systems": {"psx": {"files": [{
|
||||
"name": "scph5501.bin", "destination": "scph5501.bin",
|
||||
"md5": md5, "crc32": "00000000", "required": True,
|
||||
}]}},
|
||||
}))
|
||||
self.db = {
|
||||
"files": {sha1: {"path": str(held), "name": "scph5501.bin", "size": len(payload),
|
||||
"sha1": sha1, "md5": md5, "crc32": "deadbeef"}},
|
||||
"indexes": {"by_md5": {md5: sha1}, "by_name": {"scph5501.bin": [sha1]},
|
||||
"by_crc32": {}, "by_path_suffix": {}},
|
||||
}
|
||||
self.root = root
|
||||
|
||||
def test_pack_manifest_and_verify_agree(self):
|
||||
from common import load_platform_config
|
||||
from generate_pack import generate_manifest, generate_pack
|
||||
from verify import verify_platform
|
||||
|
||||
os.chdir(self.root)
|
||||
self.addCleanup(os.chdir, REPO_ROOT)
|
||||
zip_path = generate_pack(
|
||||
"digestplat", str(self.platforms), self.db, str(self.bios),
|
||||
str(self.root / "out"), emulators_dir=str(self.emulators),
|
||||
emu_profiles={}, offline=True,
|
||||
)
|
||||
with zipfile.ZipFile(zip_path) as zf:
|
||||
self.assertIn("scph5501.bin", {Path(n).name for n in zf.namelist()})
|
||||
manifest = generate_manifest(
|
||||
"digestplat", str(self.platforms), self.db, str(self.bios),
|
||||
str(self.platforms / "_registry.yml"), emulators_dir=str(self.emulators),
|
||||
emu_profiles={}, offline=True,
|
||||
)
|
||||
self.assertEqual([f["dest"] for f in manifest["files"]], ["scph5501.bin"])
|
||||
config = load_platform_config("digestplat", str(self.platforms))
|
||||
result = verify_platform(config, self.db, str(self.emulators), emu_profiles={})
|
||||
self.assertEqual(result["details"][0]["status"], "ok")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user