-
-
Notifications
You must be signed in to change notification settings - Fork 867
check --repair: resync past corrupt object headers when rebuilding the chunks index #10094
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
a21d855
90c4c07
46162be
f2b5d0e
90fecfc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -91,10 +91,37 @@ A reader locates the next blob by advancing:: | |
|
|
||
| next_blob_offset = current_blob_offset + REPOOBJ_HEADER_SIZE + meta_size + data_size | ||
|
|
||
| The per-blob magic limits the blast radius of corrupted length fields: if | ||
| ``meta_size`` or ``data_size`` is damaged, the scanner loses at most one blob. | ||
| Once it finds the next ``OBJ_MAGIC`` sequence it resumes. Other corruption | ||
| (payload bit flips) is caught by AEAD on that blob without losing position. | ||
| ``iter_headers()`` checks every header it walks: it must have ``OBJ_MAGIC``, a | ||
| supported version, and sizes that keep the blob inside the pack and within | ||
| ``MAX_DATA_SIZE``. A header that fails these checks means a corrupt pack, and | ||
| ``IntegrityError`` is raised, naming which check it failed. | ||
|
|
||
| The per-blob magic limits the blast radius of corrupted length fields. The | ||
| repair walk (``iter_headers(validate=...)``, used when ``borg check --repair`` | ||
| rebuilds the chunks index from the packs) validates every header it walks, | ||
| reading the metadata slot along with it: the slot's tag covers the header AAD | ||
| described above and the slot itself, so a corrupted magic, version, chunk id or | ||
| ``meta_size`` fails it, and ``data_size`` - the one header field outside the | ||
| tag - must equal ``csize`` (the data payload size recorded in the tagged | ||
| metadata) plus the key's fixed envelope overhead. A header that fails makes the | ||
| walk scan for the next blob that validates and resume there, so the blobs after | ||
| the damaged one are still found; the damaged blob itself is dropped, it can not | ||
| be read back. | ||
|
|
||
| ``OBJ_MAGIC`` occurs inside the payloads as well, and in the ``none-*`` and | ||
| ``authenticated-*`` modes the payloads are user content stored as it is, so a | ||
| backed up file can contain something shaped like a blob. The scan therefore | ||
| accepts a candidate only when it validates like any walked header. Validating | ||
| needs the key, so a repair that cannot read the manifest walks without it. | ||
|
|
||
| In the ``none-*`` modes the tag is an unkeyed checksum, so the walk accepts any | ||
| well-formed blob, including one a backed up file contains. Such a blob carries | ||
| its own chunk id and reads back as itself, so indexing it is harmless. Bytes | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. review by claude fable 5 "indexing it is harmless" holds for the innocent case, but not for the crafted one, and the next sentence only says crafted bytes are "not caught" — the consequence is bigger than a spurious index entry: a crafted blob can hide the blobs its claimed extent spans.
The crafted chunk itself fails its id check on read, but the blobs it swallowed are already missing, and the next The innocent case really is harmless — same setup with a real object copied verbatim into a file gives Docs-only suggestion, no code change — the preconditions (
Unrelated and even smaller, while I was in here:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If borg is used to back up a borg repo that was made with same key, unencrypted, not compressed, things might get interesting. |
||
| crafted to pass the unkeyed checksum are not caught here - authenticating them | ||
| is what these modes give up. The scan reaches a payload only after the blob | ||
| owning it failed to validate. | ||
|
|
||
| Bit flips in the data are caught when the blob is read, on that blob alone. | ||
|
|
||
| Blobs follow one another contiguously with no padding:: | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.