Add three new fields to ChunkIndexEntry and update all call sites:
- pack_id (32 bytes): identifies the pack file containing the chunk
- obj_offset (uint32): byte offset of the chunk within the pack
- obj_size (uint32): stored (compressed) size of the chunk on disk
At N=1 (one chunk per pack), chunk_id == pack_id, obj_offset == 0,
and obj_size == pack_size. All sites use chunk_id as the ChunkIndex
key and extract pack_id as a separate variable with an N=1 comment.
compact_cmd.py: use obj_size (stored size) in repository_size sum.
cache.py: preserve pack fields when serializing the chunk index cache.
repository.py: populate pack_id/obj_size from borgstore object info.
archive.py: extract pack_id on its own line, obj_size=0 for now.
hashindex.pyx: update add(), namedtuple, format string, and docstring.
hashindex.pyi: add new fields to ChunkIndexEntry and CIE type alias.
testsuite/hashindex_test.py: update all ChunkIndexEntry constructions.
- Add an epilog to the main ArgumentParser in src/borg/archiver/__init__.py.
- Import process_epilog and use it to format and list additional help topics: patterns, match-archives, placeholders, compression.
- Add test_main_help_epilog to help_cmd_test.py.
- Fixes#3432
- Add --from-borg1 option to borg repo-list command.
- Add allow_v1 argument to @with_repository decorator.
- If allow_v1 is True and v1_legacy is requested, allow version 1 repositories and load Manifest with RepoObj1 object class.
- Support LegacyRemoteRepository in Manifest class.
- Add test_repo_list_from_borg1.
info.size is the on-disk pack file size, which equals the chunk size only
when N=1 (one chunk per pack). Extract it into a named variable with a
comment so the assumption is visible and easy to fix when N>1 is introduced.
Corruption at offset 123 lands inside meta_encrypted (header is 49 bytes),
causing extract_crypted_data to return a shifted slice whose first byte is
a random AES-OCB ciphertext byte. When that byte equals 0x02 (PlaintextKey
type) key detection silently selects the wrong key, leading to a flaky
IntegrityError in rebuild_archives.
Move the insertion point to offset 250, which is safely inside data_encrypted
for any realistic manifest size, so key detection always reads the correct
type byte and the corruption is caught by AEAD authentication instead.
_common.py had a hard-coded version check that only allowed v3.
Now that repository.py creates v4 repos, every archiver command
failed to open the repo. Extend the guard to (3, 4).
The --other-repo check (v1 or v3 for borg transfer source) is
intentionally left unchanged.
Wrap each pack file in a 13-byte header (magic + version + blob_len) so
packs are self-identifying and the [len][blob] unit extends to N>1 without
a format revision. Bump version 3->4: packs/ and 49-byte ObjHeader are
incompatible with version-3 readers. Fix test_extra_chunks chunk_id mismatch.
Introduces pack_id as the borgstore storage key (N=1: pack_id == chunk_id).
Chunks move from data/ to packs/ with single-level directory sharding (256
subdirs). check_object() validates the header chunk_id against the pack
filename. Adds packs/ to ns_config with levels=[1] and to the permissions
maps for no-delete and write-only modes.
Stores chunk_id unencrypted in the per-blob header so borg check can
rebuild the chunk_id -> pack location index without decryption. AEAD
uses chunk_id as additional data, making key-free recovery circular
without an explicit plaintext copy.
Header layout: OBJ_MAGIC(8) + version(1) + chunk_id(32) + meta_size(4)
+ data_size(4) = REPOOBJ_HEADER_SIZE = 49 bytes.
- always have a starting line with FILE_ID repoid
- store repkeys content-addressed, name is sha256(content)
- search by repo id on load
- add keyfile_format / keyfile_parse / is_keyfile helpers