fix: reread a contradicted hash from disk

_stable_hashes retried through the hash cache, so both retries returned
the first reading and the file was read once before the manifest run
stopped. The retries now read the file again.
This commit is contained in:
Abdessamad Derraz committed 2026-10-21 05:07:06 +02:00
1 parent 8008439033
commit ae874534e2
2 files changed
+30 -1

No files matched your search

+3 -1
View File
@@ -3468,7 +3468,9 @@ def _stable_hashes(local_path: str, db: dict) -> dict:
if expected is None or hashes["sha1"] == expected:
_STABLE_HASHES[key] = hashes
return hashes
hashes = compute_hashes(local_path, _MANIFEST_HASHES)
# Read from disk again: the cached digest is the one in doubt, and a
# retry through the cache answered with it without a second read.
hashes = compute_hashes(local_path, _MANIFEST_HASHES, cache=False)
raise RuntimeError(
f"{local_path}: sha1 {hashes['sha1']} contradicts the database scan "
f"({expected}). Rehash the file or regenerate database.json"
+27
View File
@@ -237,6 +237,33 @@ class ManifestHashesMatchTheScan(unittest.TestCase):
generate_pack._stable_hashes(str(path), bad)
self.assertEqual(generate_pack._stable_hashes(str(path), {"files": {}})["sha1"], real)
def test_a_wrong_first_reading_is_read_again_from_disk(self):
"""The retries went through the hash cache and got the first, wrong
value back: one read of the file, then a refusal a second read would
have avoided."""
sys.path.insert(0, str(REPO_ROOT / "scripts"))
import generate_pack
import hashing
real_compute = hashing._compute_hashes
reads: list[str] = []
def flaky(filepath, algos):
reads.append(str(filepath))
result = real_compute(filepath, algos)
if len(reads) == 1:
result = {**result, "sha1": "0" * 40}
return result
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "nand.bin"
path.write_bytes(b"flaky")
real = hashlib.sha1(b"flaky").hexdigest()
db = {"files": {real: {"path": str(path)}}}
with mock.patch.object(hashing, "_compute_hashes", flaky):
self.assertEqual(generate_pack._stable_hashes(str(path), db)["sha1"], real)
self.assertEqual(len(reads), 2)
def test_the_cache_is_keyed_by_what_was_checked(self):
"""Keyed by id(db), a database freed and replaced at the same address
got hashes verified against the first one back, unchecked."""