Files
pristontale-eu-linux/src/ptdiag/filesystem.py

60 lines
1.8 KiB
Python

"""Read-only filesystem and hash verification helpers."""
from __future__ import annotations
from dataclasses import asdict, dataclass
from hashlib import sha256
from pathlib import Path
from typing import Mapping
KNOWN_GAME_HASHES: dict[str, str] = {
"Game.exe": "3cc0980aac0f65b379ff340ec61750498fb9f620fb6c88aa66281e8ab27023a5",
"game.dll": "7ea85b19f452962a39c91f967cad22495f066b2d52e8ac37f4e234463d5c4f09",
"gamese.exe": "c3443e50a93c6c778eff456122c351e5c7d2c1ceaea50e5a650bffe846a05c87",
"gamese.dll": "0ae6822f5f347f04bd2b0dc947f0286089c711c84703e110f6f8fc6e0d626131",
}
@dataclass(frozen=True)
class HashVerification:
path: Path
expected_sha256: str
actual_sha256: str | None
ok: bool
error: str | None = None
def to_dict(self) -> dict[str, str | bool | None]:
data = asdict(self)
data["path"] = str(self.path)
return data
def sha256_file(path: Path, chunk_size: int = 1024 * 1024) -> str:
if chunk_size <= 0:
raise ValueError("chunk_size must be positive")
digest = sha256()
with path.open("rb") as handle:
for chunk in iter(lambda: handle.read(chunk_size), b""):
digest.update(chunk)
return digest.hexdigest()
def verify_file_hash(path: Path, expected_sha256: str) -> HashVerification:
expected = expected_sha256.casefold()
try:
actual = sha256_file(path)
except (OSError, ValueError) as exc:
return HashVerification(path, expected, None, False, str(exc))
return HashVerification(path, expected, actual, actual == expected)
def verify_hashes(expected: Mapping[Path, str]) -> list[HashVerification]:
return [verify_file_hash(path, digest) for path, digest in expected.items()]
def verify_known_game_hashes(live_game: Path) -> list[HashVerification]:
expected = {live_game / name: digest for name, digest in KNOWN_GAME_HASHES.items()}
return verify_hashes(expected)