From fa986a9f19b9451d7f1e2cc417f55b9d318772f8 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Tue, 23 Aug 2022 03:25:06 +0200 Subject: [PATCH 01/14] repoobj: add a layer to format/parse repo objects borg < 2: obj = encrypted(compressed(data)) borg 2: obj = enc_meta_len32 + encrypted(msgpacked(meta)) + encrypted(compressed(data)) handle compr / decompr in repoobj move the assert_id call from decrypt to RepoObj.parse also: - for AEADKeyBase, add a dummy assert_id (not needed here) - only test assert_id for other if not AEADKeyBase instance - remove test_getting_wrong_chunk. assert_id is called elsewhere and is not needed any more anyway with the new AEAD crypto. - only give manifest (includes key, repo, repo_objs) - only return manifest from Manifest.load (includes key, repo, repo_objs) --- src/borg/archive.py | 98 +++++++++++++---------- src/borg/archiver/_common.py | 32 ++++---- src/borg/archiver/config_cmd.py | 4 +- src/borg/archiver/create_cmd.py | 6 +- src/borg/archiver/debug_cmd.py | 34 ++++---- src/borg/archiver/delete_cmd.py | 11 +-- src/borg/archiver/diff_cmd.py | 4 +- src/borg/archiver/extract_cmd.py | 2 +- src/borg/archiver/info_cmd.py | 10 +-- src/borg/archiver/key_cmds.py | 7 +- src/borg/archiver/list_cmd.py | 8 +- src/borg/archiver/mount_cmds.py | 6 +- src/borg/archiver/prune_cmd.py | 8 +- src/borg/archiver/rcreate_cmd.py | 7 +- src/borg/archiver/rdelete_cmd.py | 2 +- src/borg/archiver/recreate_cmd.py | 4 +- src/borg/archiver/rename_cmd.py | 2 +- src/borg/archiver/rinfo_cmd.py | 3 +- src/borg/archiver/rlist_cmd.py | 4 +- src/borg/archiver/tar_cmds.py | 8 +- src/borg/archiver/transfer_cmd.py | 14 ++-- src/borg/cache.py | 52 +++++------- src/borg/crypto/key.py | 76 ++++++------------ src/borg/crypto/keymanager.py | 5 +- src/borg/fuse.py | 20 ++--- src/borg/helpers/parseformat.py | 2 +- src/borg/manifest.py | 20 ++--- src/borg/remote.py | 8 +- src/borg/repoobj.py | 129 ++++++++++++++++++++++++++++++ src/borg/testsuite/archive.py | 4 +- src/borg/testsuite/archiver.py | 95 +++++++++++----------- src/borg/testsuite/cache.py | 6 +- src/borg/testsuite/key.py | 64 ++++----------- src/borg/testsuite/remote.py | 33 ++++---- src/borg/testsuite/repoobj.py | 59 ++++++++++++++ 35 files changed, 481 insertions(+), 366 deletions(-) create mode 100644 src/borg/repoobj.py create mode 100644 src/borg/testsuite/repoobj.py diff --git a/src/borg/archive.py b/src/borg/archive.py index acbbdd31c..490128f7d 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -48,6 +48,7 @@ from .item import Item, ArchiveItem, ItemDiff from .platform import acl_get, acl_set, set_flags, get_flags, swidth, hostname from .remote import cache_if_remote from .repository import Repository, LIST_SCAN_LIMIT +from .repoobj import RepoObj has_link = hasattr(os, "link") @@ -262,9 +263,9 @@ def OsOpen(*, flags, path=None, parent_fd=None, name=None, noatime=False, op="op class DownloadPipeline: - def __init__(self, repository, key): + def __init__(self, repository, repo_objs): self.repository = repository - self.key = key + self.repo_objs = repo_objs def unpack_many(self, ids, *, filter=None, preload=False): """ @@ -308,8 +309,9 @@ class DownloadPipeline: yield item def fetch_many(self, ids, is_preloaded=False): - for id_, data in zip(ids, self.repository.get_many(ids, is_preloaded=is_preloaded)): - yield self.key.decrypt(id_, data) + for id_, cdata in zip(ids, self.repository.get_many(ids, is_preloaded=is_preloaded)): + _, data = self.repo_objs.parse(id_, cdata) + yield data class ChunkBuffer: @@ -391,12 +393,12 @@ def get_item_uid_gid(item, *, numeric, uid_forced=None, gid_forced=None, uid_def return uid, gid -def archive_get_items(metadata, key, repository): +def archive_get_items(metadata, *, repo_objs, repository): if "item_ptrs" in metadata: # looks like a v2+ archive assert "items" not in metadata items = [] - for id, data in zip(metadata.item_ptrs, repository.get_many(metadata.item_ptrs)): - data = key.decrypt(id, data) + for id, cdata in zip(metadata.item_ptrs, repository.get_many(metadata.item_ptrs)): + _, data = repo_objs.parse(id, cdata) ids = msgpack.unpackb(data) items.extend(ids) return items @@ -406,16 +408,16 @@ def archive_get_items(metadata, key, repository): return metadata.items -def archive_put_items(chunk_ids, *, key, cache=None, stats=None, add_reference=None): +def archive_put_items(chunk_ids, *, repo_objs, cache=None, stats=None, add_reference=None): """gets a (potentially large) list of archive metadata stream chunk ids and writes them to repo objects""" item_ptrs = [] for i in range(0, len(chunk_ids), IDS_PER_CHUNK): data = msgpack.packb(chunk_ids[i : i + IDS_PER_CHUNK]) - id = key.id_hash(data) + id = repo_objs.id_hash(data) if cache is not None and stats is not None: cache.add_chunk(id, data, stats) elif add_reference is not None: - cdata = key.encrypt(id, data) + cdata = repo_objs.format(id, {}, data) add_reference(id, len(data), cdata) else: raise NotImplementedError @@ -435,8 +437,6 @@ class Archive: def __init__( self, - repository, - key, manifest, name, cache=None, @@ -458,10 +458,12 @@ class Archive: iec=False, ): self.cwd = os.getcwd() - self.key = key - self.repository = repository - self.cache = cache + assert isinstance(manifest, Manifest) self.manifest = manifest + self.key = manifest.repo_objs.key + self.repo_objs = manifest.repo_objs + self.repository = manifest.repository + self.cache = cache self.stats = Statistics(output_json=log_json, iec=iec) self.iec = iec self.show_progress = progress @@ -488,7 +490,7 @@ class Archive: end = datetime.now().astimezone() # local time with local timezone self.end = end self.consider_part_files = consider_part_files - self.pipeline = DownloadPipeline(self.repository, self.key) + self.pipeline = DownloadPipeline(self.repository, self.repo_objs) self.create = create if self.create: self.items_buffer = CacheChunkBuffer(self.cache, self.key, self.stats) @@ -507,12 +509,13 @@ class Archive: self.load(info.id) def _load_meta(self, id): - data = self.key.decrypt(id, self.repository.get(id)) + cdata = self.repository.get(id) + _, data = self.repo_objs.parse(id, cdata) metadata = ArchiveItem(internal_dict=msgpack.unpackb(data)) if metadata.version not in (1, 2): # legacy: still need to read v1 archives raise Exception("Unknown archive metadata version") # note: metadata.items must not get written to disk! - metadata.items = archive_get_items(metadata, self.key, self.repository) + metadata.items = archive_get_items(metadata, repo_objs=self.repo_objs, repository=self.repository) return metadata def load(self, id): @@ -626,7 +629,9 @@ Duration: {0.duration} if name in self.manifest.archives: raise self.AlreadyExists(name) self.items_buffer.flush(flush=True) - item_ptrs = archive_put_items(self.items_buffer.chunks, key=self.key, cache=self.cache, stats=self.stats) + item_ptrs = archive_put_items( + self.items_buffer.chunks, repo_objs=self.repo_objs, cache=self.cache, stats=self.stats + ) duration = timedelta(seconds=time.monotonic() - self.start_monotonic) if timestamp is None: end = datetime.now().astimezone() # local time with local timezone @@ -660,7 +665,7 @@ Duration: {0.duration} metadata.update(additional_metadata or {}) metadata = ArchiveItem(metadata) data = self.key.pack_and_authenticate_metadata(metadata.as_dict(), context=b"archive") - self.id = self.key.id_hash(data) + self.id = self.repo_objs.id_hash(data) try: self.cache.add_chunk(self.id, data, self.stats) except IntegrityError as err: @@ -699,7 +704,7 @@ Duration: {0.duration} for id, chunk in zip(self.metadata.items, self.repository.get_many(self.metadata.items)): pi.show(increase=1) add(id) - data = self.key.decrypt(id, chunk) + _, data = self.repo_objs.parse(id, chunk) sync.feed(data) unique_size = archive_index.stats_against(cache.chunks)[1] pi.finish() @@ -1011,7 +1016,7 @@ Duration: {0.duration} for (i, (items_id, data)) in enumerate(zip(items_ids, self.repository.get_many(items_ids))): if progress: pi.show(i) - data = self.key.decrypt(items_id, data) + _, data = self.repo_objs.parse(items_id, data) unpacker.feed(data) chunk_decref(items_id, stats) try: @@ -1666,6 +1671,7 @@ class ArchiveChecker: logger.error("Repository contains no apparent data at all, cannot continue check/repair.") return False self.key = self.make_key(repository) + self.repo_objs = RepoObj(self.key) if verify_data: self.verify_data() if Manifest.MANIFEST_ID not in self.chunks: @@ -1674,7 +1680,7 @@ class ArchiveChecker: self.manifest = self.rebuild_manifest() else: try: - self.manifest, _ = Manifest.load(repository, (Manifest.Operation.CHECK,), key=self.key) + self.manifest = Manifest.load(repository, (Manifest.Operation.CHECK,), key=self.key) except IntegrityErrorBase as exc: logger.error("Repository manifest is corrupted: %s", exc) self.error_found = True @@ -1765,7 +1771,7 @@ class ArchiveChecker: chunk_data_iter = self.repository.get_many(chunk_ids) else: try: - self.key.decrypt(chunk_id, encrypted_data, decompress=decompress) + self.repo_objs.parse(chunk_id, encrypted_data, decompress=decompress) except IntegrityErrorBase as integrity_error: self.error_found = True errors += 1 @@ -1796,7 +1802,7 @@ class ArchiveChecker: # from the underlying media. try: encrypted_data = self.repository.get(defect_chunk) - self.key.decrypt(defect_chunk, encrypted_data, decompress=decompress) + self.repo_objs.parse(defect_chunk, encrypted_data, decompress=decompress) except IntegrityErrorBase: # failed twice -> get rid of this chunk del self.chunks[defect_chunk] @@ -1844,7 +1850,7 @@ class ArchiveChecker: pi.show() cdata = self.repository.get(chunk_id) try: - data = self.key.decrypt(chunk_id, cdata) + _, data = self.repo_objs.parse(chunk_id, cdata) except IntegrityErrorBase as exc: logger.error("Skipping corrupted chunk: %s", exc) self.error_found = True @@ -1890,7 +1896,7 @@ class ArchiveChecker: def add_callback(chunk): id_ = self.key.id_hash(chunk) - cdata = self.key.encrypt(id_, chunk) + cdata = self.repo_objs.format(id_, {}, chunk) add_reference(id_, len(chunk), cdata) return id_ @@ -1913,7 +1919,7 @@ class ArchiveChecker: def replacement_chunk(size): chunk = Chunk(None, allocation=CH_ALLOC, size=size) chunk_id, data = cached_hash(chunk, self.key.id_hash) - cdata = self.key.encrypt(chunk_id, data) + cdata = self.repo_objs.format(chunk_id, {}, data) return chunk_id, size, cdata offset = 0 @@ -2032,7 +2038,7 @@ class ArchiveChecker: return True, "" i = 0 - archive_items = archive_get_items(archive, self.key, repository) + archive_items = archive_get_items(archive, repo_objs=self.repo_objs, repository=repository) for state, items in groupby(archive_items, missing_chunk_detector): items = list(items) if state % 2: @@ -2044,7 +2050,7 @@ class ArchiveChecker: unpacker.resync() for chunk_id, cdata in zip(items, repository.get_many(items)): try: - data = self.key.decrypt(chunk_id, cdata) + _, data = self.repo_objs.parse(chunk_id, cdata) unpacker.feed(data) for item in unpacker: valid, reason = valid_item(item) @@ -2057,7 +2063,7 @@ class ArchiveChecker: i, ) except IntegrityError as integrity_error: - # key.decrypt() detected integrity issues. + # repo_objs.parse() detected integrity issues. # maybe the repo gave us a valid cdata, but not for the chunk_id we wanted. # or the authentication of cdata failed, meaning the encrypted data was corrupted. report(str(integrity_error), chunk_id, i) @@ -2098,7 +2104,7 @@ class ArchiveChecker: mark_as_possibly_superseded(archive_id) cdata = self.repository.get(archive_id) try: - data = self.key.decrypt(archive_id, cdata) + _, data = self.repo_objs.parse(archive_id, cdata) except IntegrityError as integrity_error: logger.error("Archive metadata block %s is corrupted: %s", bin_to_hex(archive_id), integrity_error) self.error_found = True @@ -2114,14 +2120,18 @@ class ArchiveChecker: verify_file_chunks(info.name, item) items_buffer.add(item) items_buffer.flush(flush=True) - for previous_item_id in archive_get_items(archive, self.key, self.repository): + for previous_item_id in archive_get_items( + archive, repo_objs=self.repo_objs, repository=self.repository + ): mark_as_possibly_superseded(previous_item_id) for previous_item_ptr in archive.item_ptrs: mark_as_possibly_superseded(previous_item_ptr) - archive.item_ptrs = archive_put_items(items_buffer.chunks, key=self.key, add_reference=add_reference) + archive.item_ptrs = archive_put_items( + items_buffer.chunks, repo_objs=self.repo_objs, add_reference=add_reference + ) data = msgpack.packb(archive.as_dict()) new_archive_id = self.key.id_hash(data) - cdata = self.key.encrypt(new_archive_id, data) + cdata = self.repo_objs.format(new_archive_id, {}, data) add_reference(new_archive_id, len(data), cdata) self.manifest.archives[info.name] = (new_archive_id, info.ts) pi.finish() @@ -2162,9 +2172,7 @@ class ArchiveRecreater: def __init__( self, - repository, manifest, - key, cache, matcher, exclude_caches=False, @@ -2181,9 +2189,10 @@ class ArchiveRecreater: timestamp=None, checkpoint_interval=1800, ): - self.repository = repository - self.key = key self.manifest = manifest + self.repository = manifest.repository + self.key = manifest.key + self.repo_objs = manifest.repo_objs self.cache = cache self.matcher = matcher @@ -2260,9 +2269,12 @@ class ArchiveRecreater: overwrite = self.recompress if self.recompress and not self.always_recompress and chunk_id in self.cache.chunks: # Check if this chunk is already compressed the way we want it - old_chunk = self.key.decrypt(chunk_id, self.repository.get(chunk_id), decompress=False) + _, old_chunk = self.repo_objs.parse(chunk_id, self.repository.get(chunk_id), decompress=False) compressor_cls, level = Compressor.detect(old_chunk) - if compressor_cls.name == self.key.compressor.decide(data).name and level == self.key.compressor.level: + if ( + compressor_cls.name == self.repo_objs.compressor.decide(data).name + and level == self.repo_objs.compressor.level + ): # Stored chunk has the same compression method and level as we wanted overwrite = False chunk_entry = self.cache.add_chunk(chunk_id, data, target.stats, overwrite=overwrite, wait=False) @@ -2371,8 +2383,6 @@ class ArchiveRecreater: def create_target_archive(self, name): target = Archive( - self.repository, - self.key, self.manifest, name, create=True, @@ -2384,4 +2394,4 @@ class ArchiveRecreater: return target def open_archive(self, name, **kwargs): - return Archive(self.repository, self.key, self.manifest, name, cache=self.cache, **kwargs) + return Archive(self.manifest, name, cache=self.cache, **kwargs) diff --git a/src/borg/archiver/_common.py b/src/borg/archiver/_common.py index e1b12e431..29da8d1cd 100644 --- a/src/borg/archiver/_common.py +++ b/src/borg/archiver/_common.py @@ -14,6 +14,7 @@ from ..manifest import Manifest, AI_HUMAN_SORT_KEYS from ..patterns import PatternMatcher from ..remote import RemoteRepository from ..repository import Repository +from ..repoobj import RepoObj, RepoObj1 from ..patterns import ( ArgparsePatternAction, ArgparseExcludeFileAction, @@ -80,7 +81,7 @@ def with_repository( :param create: create repository :param lock: lock repository :param exclusive: (bool) lock repository exclusively (for writing) - :param manifest: load manifest and key, pass them as keyword arguments + :param manifest: load manifest and repo_objs (key), pass them as keyword arguments :param cache: open cache, pass it as keyword argument (implies manifest) :param secure: do assert_secure after loading manifest :param compatibility: mandatory if not create and (manifest or cache), specifies mandatory feature categories to check @@ -135,16 +136,16 @@ def with_repository( "You can use 'borg transfer' to copy archives from old to new repos." ) if manifest or cache: - kwargs["manifest"], kwargs["key"] = Manifest.load(repository, compatibility) + manifest_ = Manifest.load(repository, compatibility) + kwargs["manifest"] = manifest_ if "compression" in args: - kwargs["key"].compressor = args.compression.compressor + manifest_.repo_objs.compressor = args.compression.compressor if secure: - assert_secure(repository, kwargs["manifest"], self.lock_wait) + assert_secure(repository, manifest_, self.lock_wait) if cache: with Cache( repository, - kwargs["key"], - kwargs["manifest"], + manifest_, progress=getattr(args, "progress", False), lock_wait=self.lock_wait, cache_mode=getattr(args, "files_cache_mode", FILES_CACHE_MODE_DISABLED), @@ -160,7 +161,7 @@ def with_repository( return decorator -def with_other_repository(manifest=False, key=False, cache=False, compatibility=None): +def with_other_repository(manifest=False, cache=False, compatibility=None): """ this is a simplified version of "with_repository", just for the "other location". @@ -170,7 +171,7 @@ def with_other_repository(manifest=False, key=False, cache=False, compatibility= compatibility = compat_check( create=False, manifest=manifest, - key=key, + key=manifest, cache=cache, compatibility=compatibility, decorator_name="with_other_repository", @@ -199,17 +200,16 @@ def with_other_repository(manifest=False, key=False, cache=False, compatibility= if repository.version not in (1, 2): raise Error("This borg version only accepts version 1 or 2 repos for --other-repo.") kwargs["other_repository"] = repository - if manifest or key or cache: - manifest_, key_ = Manifest.load(repository, compatibility) + if manifest or cache: + manifest_ = Manifest.load( + repository, compatibility, ro_cls=RepoObj if repository.version > 1 else RepoObj1 + ) assert_secure(repository, manifest_, self.lock_wait) if manifest: kwargs["other_manifest"] = manifest_ - if key: - kwargs["other_key"] = key_ if cache: with Cache( repository, - key_, manifest_, progress=False, lock_wait=self.lock_wait, @@ -229,12 +229,10 @@ def with_other_repository(manifest=False, key=False, cache=False, compatibility= def with_archive(method): @functools.wraps(method) - def wrapper(self, args, repository, key, manifest, **kwargs): + def wrapper(self, args, repository, manifest, **kwargs): archive_name = getattr(args, "name", None) assert archive_name is not None archive = Archive( - repository, - key, manifest, archive_name, numeric_ids=getattr(args, "numeric_ids", False), @@ -246,7 +244,7 @@ def with_archive(method): log_json=args.log_json, iec=args.iec, ) - return method(self, args, repository=repository, manifest=manifest, key=key, archive=archive, **kwargs) + return method(self, args, repository=repository, manifest=manifest, archive=archive, **kwargs) return wrapper diff --git a/src/borg/archiver/config_cmd.py b/src/borg/archiver/config_cmd.py index 232f35eb0..c8142112b 100644 --- a/src/borg/archiver/config_cmd.py +++ b/src/borg/archiver/config_cmd.py @@ -109,9 +109,9 @@ class ConfigMixIn: name = args.name if args.cache: - manifest, key = Manifest.load(repository, (Manifest.Operation.WRITE,)) + manifest = Manifest.load(repository, (Manifest.Operation.WRITE,)) assert_secure(repository, manifest, self.lock_wait) - cache = Cache(repository, key, manifest, lock_wait=self.lock_wait) + cache = Cache(repository, manifest, lock_wait=self.lock_wait) try: if args.cache: diff --git a/src/borg/archiver/create_cmd.py b/src/borg/archiver/create_cmd.py index 90825f9c4..cf5272a1d 100644 --- a/src/borg/archiver/create_cmd.py +++ b/src/borg/archiver/create_cmd.py @@ -39,8 +39,9 @@ logger = create_logger() class CreateMixIn: @with_repository(exclusive=True, compatibility=(Manifest.Operation.WRITE,)) - def do_create(self, args, repository, manifest=None, key=None): + def do_create(self, args, repository, manifest): """Create new archive""" + key = manifest.key matcher = PatternMatcher(fallback=True) matcher.add_inclexcl(args.patterns) @@ -210,7 +211,6 @@ class CreateMixIn: if not dry_run: with Cache( repository, - key, manifest, progress=args.progress, lock_wait=self.lock_wait, @@ -219,8 +219,6 @@ class CreateMixIn: iec=args.iec, ) as cache: archive = Archive( - repository, - key, manifest, args.name, cache=cache, diff --git a/src/borg/archiver/debug_cmd.py b/src/borg/archiver/debug_cmd.py index 68956b209..b8f49a68d 100644 --- a/src/borg/archiver/debug_cmd.py +++ b/src/borg/archiver/debug_cmd.py @@ -16,6 +16,7 @@ from ..helpers import positive_int_validator, NameSpec from ..manifest import Manifest from ..platform import get_process_id from ..repository import Repository, LIST_SCAN_LIMIT, TAG_PUT, TAG_DELETE, TAG_COMMIT +from ..repoobj import RepoObj from ._common import with_repository from ._common import process_epilog @@ -29,11 +30,12 @@ class DebugMixIn: return EXIT_SUCCESS @with_repository(compatibility=Manifest.NO_OPERATION_CHECK) - def do_debug_dump_archive_items(self, args, repository, manifest, key): + def do_debug_dump_archive_items(self, args, repository, manifest): """dump (decrypted, decompressed) archive items metadata (not: data)""" - archive = Archive(repository, key, manifest, args.name, consider_part_files=args.consider_part_files) + repo_objs = manifest.repo_objs + archive = Archive(manifest, args.name, consider_part_files=args.consider_part_files) for i, item_id in enumerate(archive.metadata.items): - data = key.decrypt(item_id, repository.get(item_id)) + _, data = repo_objs.parse(item_id, repository.get(item_id)) filename = "%06d_%s.items" % (i, bin_to_hex(item_id)) print("Dumping", filename) with open(filename, "wb") as fd: @@ -42,8 +44,9 @@ class DebugMixIn: return EXIT_SUCCESS @with_repository(compatibility=Manifest.NO_OPERATION_CHECK) - def do_debug_dump_archive(self, args, repository, manifest, key): + def do_debug_dump_archive(self, args, repository, manifest): """dump decoded archive metadata (not: data)""" + repo_objs = manifest.repo_objs try: archive_meta_orig = manifest.archives.get_raw_dict()[args.name] except KeyError: @@ -62,7 +65,7 @@ class DebugMixIn: fd.write(do_indent(prepare_dump_dict(archive_meta_orig))) fd.write(",\n") - data = key.decrypt(archive_meta_orig["id"], repository.get(archive_meta_orig["id"])) + _, data = repo_objs.parse(archive_meta_orig["id"], repository.get(archive_meta_orig["id"])) archive_org_dict = msgpack.unpackb(data, object_hook=StableDict) fd.write(' "_meta":\n') @@ -74,10 +77,10 @@ class DebugMixIn: first = True items = [] for chunk_id in archive_org_dict["item_ptrs"]: - data = key.decrypt(chunk_id, repository.get(chunk_id)) + _, data = repo_objs.parse(chunk_id, repository.get(chunk_id)) items.extend(msgpack.unpackb(data)) for item_id in items: - data = key.decrypt(item_id, repository.get(item_id)) + _, data = repo_objs.parse(item_id, repository.get(item_id)) unpacker.feed(data) for item in unpacker: item = prepare_dump_dict(item) @@ -95,10 +98,10 @@ class DebugMixIn: return EXIT_SUCCESS @with_repository(compatibility=Manifest.NO_OPERATION_CHECK) - def do_debug_dump_manifest(self, args, repository, manifest, key): + def do_debug_dump_manifest(self, args, repository, manifest): """dump decoded repository manifest""" - - data = key.decrypt(manifest.MANIFEST_ID, repository.get(manifest.MANIFEST_ID)) + repo_objs = manifest.repo_objs + _, data = repo_objs.parse(manifest.MANIFEST_ID, repository.get(manifest.MANIFEST_ID)) meta = prepare_dump_dict(msgpack.unpackb(data, object_hook=StableDict)) @@ -113,9 +116,9 @@ class DebugMixIn: def decrypt_dump(i, id, cdata, tag=None, segment=None, offset=None): if cdata is not None: - data = key.decrypt(id, cdata) + _, data = repo_objs.parse(id, cdata) else: - data = b"" + _, data = {}, b"" tag_str = "" if tag is None else "_" + tag segment_str = "_" + str(segment) if segment is not None else "" offset_str = "_" + str(offset) if offset is not None else "" @@ -132,6 +135,7 @@ class DebugMixIn: for id, cdata, tag, segment, offset in repository.scan_low_level(): if tag == TAG_PUT: key = key_factory(repository, cdata) + repo_objs = RepoObj(key) break i = 0 for id, cdata, tag, segment, offset in repository.scan_low_level(segment=args.segment, offset=args.offset): @@ -147,6 +151,7 @@ class DebugMixIn: ids = repository.list(limit=1, marker=None) cdata = repository.get(ids[0]) key = key_factory(repository, cdata) + repo_objs = RepoObj(key) marker = None i = 0 while True: @@ -195,6 +200,7 @@ class DebugMixIn: ids = repository.list(limit=1, marker=None) cdata = repository.get(ids[0]) key = key_factory(repository, cdata) + repo_objs = RepoObj(key) marker = None last_data = b"" @@ -207,7 +213,7 @@ class DebugMixIn: marker = result[-1] for id in result: cdata = repository.get(id) - data = key.decrypt(id, cdata) + _, data = repo_objs.parse(id, cdata) # try to locate wanted sequence crossing the border of last_data and data boundary_data = last_data[-(len(wanted) - 1) :] + data[: len(wanted) - 1] @@ -284,7 +290,7 @@ class DebugMixIn: return EXIT_SUCCESS @with_repository(manifest=False, exclusive=True, cache=True, compatibility=Manifest.NO_OPERATION_CHECK) - def do_debug_refcount_obj(self, args, repository, manifest, key, cache): + def do_debug_refcount_obj(self, args, repository, manifest, cache): """display refcounts for the objects with the given IDs""" for hex_id in args.ids: try: diff --git a/src/borg/archiver/delete_cmd.py b/src/borg/archiver/delete_cmd.py index 64d05e149..606e6e2f0 100644 --- a/src/borg/archiver/delete_cmd.py +++ b/src/borg/archiver/delete_cmd.py @@ -19,7 +19,7 @@ class DeleteMixIn: """Delete archives""" self.output_list = args.output_list dry_run = args.dry_run - manifest, key = Manifest.load(repository, (Manifest.Operation.DELETE,)) + manifest = Manifest.load(repository, (Manifest.Operation.DELETE,)) archive_names = tuple(x.name for x in manifest.archives.list_considering(args)) if not archive_names: return self.exit_code @@ -56,7 +56,7 @@ class DeleteMixIn: return self.exit_code stats = Statistics(iec=args.iec) - with Cache(repository, key, manifest, progress=args.progress, lock_wait=self.lock_wait, iec=args.iec) as cache: + with Cache(repository, manifest, progress=args.progress, lock_wait=self.lock_wait, iec=args.iec) as cache: def checkpoint_func(): manifest.write() @@ -80,12 +80,7 @@ class DeleteMixIn: if not dry_run: archive = Archive( - repository, - key, - manifest, - archive_name, - cache=cache, - consider_part_files=args.consider_part_files, + manifest, archive_name, cache=cache, consider_part_files=args.consider_part_files ) archive.delete(stats, progress=args.progress, forced=args.forced) checkpointed = self.maybe_checkpoint( diff --git a/src/borg/archiver/diff_cmd.py b/src/borg/archiver/diff_cmd.py index 7c1084f39..a02c991da 100644 --- a/src/borg/archiver/diff_cmd.py +++ b/src/borg/archiver/diff_cmd.py @@ -15,7 +15,7 @@ logger = create_logger() class DiffMixIn: @with_repository(compatibility=(Manifest.Operation.READ,)) @with_archive - def do_diff(self, args, repository, manifest, key, archive): + def do_diff(self, args, repository, manifest, archive): """Diff contents of two archives""" def print_json_output(diff, path): @@ -27,7 +27,7 @@ class DiffMixIn: print_output = print_json_output if args.json_lines else print_text_output archive1 = archive - archive2 = Archive(repository, key, manifest, args.other_name, consider_part_files=args.consider_part_files) + archive2 = Archive(manifest, args.other_name, consider_part_files=args.consider_part_files) can_compare_chunk_ids = ( archive1.metadata.get("chunker_params", False) == archive2.metadata.get("chunker_params", True) diff --git a/src/borg/archiver/extract_cmd.py b/src/borg/archiver/extract_cmd.py index 5713b4a89..d2236a531 100644 --- a/src/borg/archiver/extract_cmd.py +++ b/src/borg/archiver/extract_cmd.py @@ -22,7 +22,7 @@ logger = create_logger() class ExtractMixIn: @with_repository(compatibility=(Manifest.Operation.READ,)) @with_archive - def do_extract(self, args, repository, manifest, key, archive): + def do_extract(self, args, repository, manifest, archive): """Extract archive contents""" # be restrictive when restoring files, restore permissions later if sys.getfilesystemencoding() == "ascii": diff --git a/src/borg/archiver/info_cmd.py b/src/borg/archiver/info_cmd.py index dac379a71..480fffb97 100644 --- a/src/borg/archiver/info_cmd.py +++ b/src/borg/archiver/info_cmd.py @@ -16,7 +16,7 @@ logger = create_logger() class InfoMixIn: @with_repository(cache=True, compatibility=(Manifest.Operation.READ,)) - def do_info(self, args, repository, manifest, key, cache): + def do_info(self, args, repository, manifest, cache): """Show archive details such as disk space used""" def format_cmdline(cmdline): @@ -29,13 +29,7 @@ class InfoMixIn: for i, archive_name in enumerate(archive_names, 1): archive = Archive( - repository, - key, - manifest, - archive_name, - cache=cache, - consider_part_files=args.consider_part_files, - iec=args.iec, + manifest, archive_name, cache=cache, consider_part_files=args.consider_part_files, iec=args.iec ) info = archive.info() if args.json: diff --git a/src/borg/archiver/key_cmds.py b/src/borg/archiver/key_cmds.py index b6aff109a..fa89d1da9 100644 --- a/src/borg/archiver/key_cmds.py +++ b/src/borg/archiver/key_cmds.py @@ -17,8 +17,9 @@ logger = create_logger(__name__) class KeysMixIn: @with_repository(compatibility=(Manifest.Operation.CHECK,)) - def do_change_passphrase(self, args, repository, manifest, key): + def do_change_passphrase(self, args, repository, manifest): """Change repository key file passphrase""" + key = manifest.key if not hasattr(key, "change_passphrase"): print("This repository is not encrypted, cannot change the passphrase.") return EXIT_ERROR @@ -30,8 +31,9 @@ class KeysMixIn: return EXIT_SUCCESS @with_repository(exclusive=True, manifest=True, cache=True, compatibility=(Manifest.Operation.CHECK,)) - def do_change_location(self, args, repository, manifest, key, cache): + def do_change_location(self, args, repository, manifest, cache): """Change repository key location""" + key = manifest.key if not hasattr(key, "change_passphrase"): print("This repository is not encrypted, cannot change the key location.") return EXIT_ERROR @@ -71,6 +73,7 @@ class KeysMixIn: # rewrite the manifest with the new key, so that the key-type byte of the manifest changes manifest.key = key_new + manifest.repo_objs.key = key_new manifest.write() repository.commit(compact=False) diff --git a/src/borg/archiver/list_cmd.py b/src/borg/archiver/list_cmd.py index 7a09ef17c..7dfc36775 100644 --- a/src/borg/archiver/list_cmd.py +++ b/src/borg/archiver/list_cmd.py @@ -16,7 +16,7 @@ logger = create_logger() class ListMixIn: @with_repository(compatibility=(Manifest.Operation.READ,)) - def do_list(self, args, repository, manifest, key): + def do_list(self, args, repository, manifest): """List archive contents""" matcher = build_matcher(args.patterns, args.paths) if args.format is not None: @@ -27,9 +27,7 @@ class ListMixIn: format = "{mode} {user:6} {group:6} {size:8} {mtime} {path}{extra}{NL}" def _list_inner(cache): - archive = Archive( - repository, key, manifest, args.name, cache=cache, consider_part_files=args.consider_part_files - ) + archive = Archive(manifest, args.name, cache=cache, consider_part_files=args.consider_part_files) formatter = ItemFormatter(archive, format, json_lines=args.json_lines) for item in archive.iter_items(lambda item: matcher.match(item.path)): @@ -37,7 +35,7 @@ class ListMixIn: # Only load the cache if it will be used if ItemFormatter.format_needs_cache(format): - with Cache(repository, key, manifest, lock_wait=self.lock_wait) as cache: + with Cache(repository, manifest, lock_wait=self.lock_wait) as cache: _list_inner(cache) else: _list_inner(cache=None) diff --git a/src/borg/archiver/mount_cmds.py b/src/borg/archiver/mount_cmds.py index a4ab76c10..e51789145 100644 --- a/src/borg/archiver/mount_cmds.py +++ b/src/borg/archiver/mount_cmds.py @@ -31,11 +31,11 @@ class MountMixIn: return self._do_mount(args) @with_repository(compatibility=(Manifest.Operation.READ,)) - def _do_mount(self, args, repository, manifest, key): + def _do_mount(self, args, repository, manifest): from ..fuse import FuseOperations - with cache_if_remote(repository, decrypted_cache=key) as cached_repo: - operations = FuseOperations(key, repository, manifest, args, cached_repo) + with cache_if_remote(repository, decrypted_cache=manifest.repo_objs) as cached_repo: + operations = FuseOperations(manifest, args, cached_repo) logger.info("Mounting filesystem") try: operations.mount(args.mountpoint, args.options, args.foreground) diff --git a/src/borg/archiver/prune_cmd.py b/src/borg/archiver/prune_cmd.py index 031bc8c9a..a5ebf045e 100644 --- a/src/borg/archiver/prune_cmd.py +++ b/src/borg/archiver/prune_cmd.py @@ -71,7 +71,7 @@ def prune_split(archives, rule, n, kept_because=None): class PruneMixIn: @with_repository(exclusive=True, compatibility=(Manifest.Operation.DELETE,)) - def do_prune(self, args, repository, manifest, key): + def do_prune(self, args, repository, manifest): """Prune repository archives according to specified rules""" if not any( (args.secondly, args.minutely, args.hourly, args.daily, args.weekly, args.monthly, args.yearly, args.within) @@ -119,7 +119,7 @@ class PruneMixIn: to_delete = (set(archives) | checkpoints) - (set(keep) | set(keep_checkpoints)) stats = Statistics(iec=args.iec) - with Cache(repository, key, manifest, lock_wait=self.lock_wait, iec=args.iec) as cache: + with Cache(repository, manifest, lock_wait=self.lock_wait, iec=args.iec) as cache: def checkpoint_func(): manifest.write() @@ -142,9 +142,7 @@ class PruneMixIn: else: archives_deleted += 1 log_message = "Pruning archive (%d/%d):" % (archives_deleted, to_delete_len) - archive = Archive( - repository, key, manifest, archive.name, cache, consider_part_files=args.consider_part_files - ) + archive = Archive(manifest, archive.name, cache, consider_part_files=args.consider_part_files) archive.delete(stats, forced=args.forced) checkpointed = self.maybe_checkpoint( checkpoint_func=checkpoint_func, checkpoint_interval=args.checkpoint_interval diff --git a/src/borg/archiver/rcreate_cmd.py b/src/borg/archiver/rcreate_cmd.py index 90dc78cf6..00914f5bf 100644 --- a/src/borg/archiver/rcreate_cmd.py +++ b/src/borg/archiver/rcreate_cmd.py @@ -16,9 +16,10 @@ logger = create_logger() class RCreateMixIn: @with_repository(create=True, exclusive=True, manifest=False) - @with_other_repository(key=True, compatibility=(Manifest.Operation.READ,)) - def do_rcreate(self, args, repository, *, other_repository=None, other_key=None): + @with_other_repository(manifest=True, compatibility=(Manifest.Operation.READ,)) + def do_rcreate(self, args, repository, *, other_repository=None, other_manifest=None): """Create a new, empty repository""" + other_key = other_manifest.key if other_manifest is not None else None path = args.location.canonical_path() logger.info('Initializing repository at "%s"' % path) if other_key is not None: @@ -32,7 +33,7 @@ class RCreateMixIn: manifest.key = key manifest.write() repository.commit(compact=False) - with Cache(repository, key, manifest, warn_if_unencrypted=False): + with Cache(repository, manifest, warn_if_unencrypted=False): pass if key.tam_required: tam_file = tam_required_file(repository) diff --git a/src/borg/archiver/rdelete_cmd.py b/src/borg/archiver/rdelete_cmd.py index afae1b6af..fc1e8691f 100644 --- a/src/borg/archiver/rdelete_cmd.py +++ b/src/borg/archiver/rdelete_cmd.py @@ -28,7 +28,7 @@ class RDeleteMixIn: location = repository._location.canonical_path() msg = [] try: - manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) + manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) n_archives = len(manifest.archives) msg.append( f"You requested to completely DELETE the following repository " diff --git a/src/borg/archiver/recreate_cmd.py b/src/borg/archiver/recreate_cmd.py index 380f59b5e..544588d6c 100644 --- a/src/borg/archiver/recreate_cmd.py +++ b/src/borg/archiver/recreate_cmd.py @@ -17,7 +17,7 @@ logger = create_logger() class RecreateMixIn: @with_repository(cache=True, exclusive=True, compatibility=(Manifest.Operation.CHECK,)) - def do_recreate(self, args, repository, manifest, key, cache): + def do_recreate(self, args, repository, manifest, cache): """Re-create archives""" matcher = build_matcher(args.patterns, args.paths) self.output_list = args.output_list @@ -26,9 +26,7 @@ class RecreateMixIn: always_recompress = args.recompress == "always" recreater = ArchiveRecreater( - repository, manifest, - key, cache, matcher, exclude_caches=args.exclude_caches, diff --git a/src/borg/archiver/rename_cmd.py b/src/borg/archiver/rename_cmd.py index 91b7d1d13..5550508fa 100644 --- a/src/borg/archiver/rename_cmd.py +++ b/src/borg/archiver/rename_cmd.py @@ -13,7 +13,7 @@ logger = create_logger() class RenameMixIn: @with_repository(exclusive=True, cache=True, compatibility=(Manifest.Operation.CHECK,)) @with_archive - def do_rename(self, args, repository, manifest, key, cache, archive): + def do_rename(self, args, repository, manifest, cache, archive): """Rename an existing archive""" archive.rename(args.newname) manifest.write() diff --git a/src/borg/archiver/rinfo_cmd.py b/src/borg/archiver/rinfo_cmd.py index 428221180..23cbf8799 100644 --- a/src/borg/archiver/rinfo_cmd.py +++ b/src/borg/archiver/rinfo_cmd.py @@ -13,8 +13,9 @@ logger = create_logger() class RInfoMixIn: @with_repository(cache=True, compatibility=(Manifest.Operation.READ,)) - def do_rinfo(self, args, repository, manifest, key, cache): + def do_rinfo(self, args, repository, manifest, cache): """Show repository infos""" + key = manifest.key info = basic_json_data(manifest, cache=cache, extra={"security_dir": cache.security_manager.dir}) if args.json: diff --git a/src/borg/archiver/rlist_cmd.py b/src/borg/archiver/rlist_cmd.py index ee34020bb..0001f5ddb 100644 --- a/src/borg/archiver/rlist_cmd.py +++ b/src/borg/archiver/rlist_cmd.py @@ -14,7 +14,7 @@ logger = create_logger() class RListMixIn: @with_repository(compatibility=(Manifest.Operation.READ,)) - def do_rlist(self, args, repository, manifest, key): + def do_rlist(self, args, repository, manifest): """List the archives contained in a repository""" if args.format is not None: format = args.format @@ -22,7 +22,7 @@ class RListMixIn: format = "{archive}{NL}" else: format = "{archive:<36} {time} [{id}]{NL}" - formatter = ArchiveFormatter(format, repository, manifest, key, json=args.json, iec=args.iec) + formatter = ArchiveFormatter(format, repository, manifest, manifest.key, json=args.json, iec=args.iec) output_data = [] diff --git a/src/borg/archiver/tar_cmds.py b/src/borg/archiver/tar_cmds.py index 6bb66a679..8e6379690 100644 --- a/src/borg/archiver/tar_cmds.py +++ b/src/borg/archiver/tar_cmds.py @@ -53,7 +53,7 @@ def get_tar_filter(fname, decompress): class TarMixIn: @with_repository(compatibility=(Manifest.Operation.READ,)) @with_archive - def do_export_tar(self, args, repository, manifest, key, archive): + def do_export_tar(self, args, repository, manifest, archive): """Export archive contents as a tarball""" self.output_list = args.output_list @@ -239,7 +239,7 @@ class TarMixIn: return self.exit_code @with_repository(cache=True, exclusive=True, compatibility=(Manifest.Operation.WRITE,)) - def do_import_tar(self, args, repository, manifest, key, cache): + def do_import_tar(self, args, repository, manifest, cache): """Create a backup archive from a tarball""" self.output_filter = args.output_filter self.output_list = args.output_list @@ -250,7 +250,7 @@ class TarMixIn: tarstream_close = args.tarfile != "-" with create_filter_process(filter, stream=tarstream, stream_close=tarstream_close, inbound=True) as _stream: - self._import_tar(args, repository, manifest, key, cache, _stream) + self._import_tar(args, repository, manifest, manifest.key, cache, _stream) return self.exit_code @@ -259,8 +259,6 @@ class TarMixIn: t0_monotonic = time.monotonic() archive = Archive( - repository, - key, manifest, args.name, cache=cache, diff --git a/src/borg/archiver/transfer_cmd.py b/src/borg/archiver/transfer_cmd.py index c440e52cf..c6c6a8776 100644 --- a/src/borg/archiver/transfer_cmd.py +++ b/src/borg/archiver/transfer_cmd.py @@ -15,12 +15,12 @@ logger = create_logger() class TransferMixIn: - @with_other_repository(manifest=True, key=True, compatibility=(Manifest.Operation.READ,)) + @with_other_repository(manifest=True, compatibility=(Manifest.Operation.READ,)) @with_repository(exclusive=True, manifest=True, cache=True, compatibility=(Manifest.Operation.WRITE,)) - def do_transfer( - self, args, *, repository, manifest, key, cache, other_repository=None, other_manifest=None, other_key=None - ): + def do_transfer(self, args, *, repository, manifest, cache, other_repository=None, other_manifest=None): """archives transfer from other repository, optionally upgrade data format""" + key = manifest.key + other_key = other_manifest.key if not uses_same_id_hash(other_key, key): self.print_error( "You must keep the same ID hash ([HMAC-]SHA256 or BLAKE2b) or deduplication will break. " @@ -57,8 +57,8 @@ class TransferMixIn: else: if not dry_run: print(f"{name}: copying archive to destination repo...") - other_archive = Archive(other_repository, other_key, other_manifest, name) - archive = Archive(repository, key, manifest, name, cache=cache, create=True) if not dry_run else None + other_archive = Archive(other_manifest, name) + archive = Archive(manifest, name, cache=cache, create=True) if not dry_run else None upgrader.new_archive(archive=archive) for item in other_archive.iter_items(): if "chunks" in item: @@ -69,7 +69,7 @@ class TransferMixIn: if not dry_run: cdata = other_repository.get(chunk_id) # keep compressed payload same, avoid decompression / recompression - data = other_key.decrypt(chunk_id, cdata, decompress=False) + meta, data = other_manifest.repo_objs.parse(chunk_id, cdata, decompress=False) data = upgrader.upgrade_compressed_chunk(chunk=data) chunk_entry = cache.add_chunk( chunk_id, data, archive.stats, wait=False, compress=False, size=size diff --git a/src/borg/cache.py b/src/borg/cache.py index 8b89df27c..3c09ab037 100644 --- a/src/borg/cache.py +++ b/src/borg/cache.py @@ -396,7 +396,6 @@ class Cache: def __new__( cls, repository, - key, manifest, path=None, sync=True, @@ -410,8 +409,6 @@ class Cache: ): def local(): return LocalCache( - repository=repository, - key=key, manifest=manifest, path=path, sync=sync, @@ -424,14 +421,7 @@ class Cache: ) def adhoc(): - return AdHocCache( - repository=repository, - key=key, - manifest=manifest, - lock_wait=lock_wait, - iec=iec, - consider_part_files=consider_part_files, - ) + return AdHocCache(manifest=manifest, lock_wait=lock_wait, iec=iec, consider_part_files=consider_part_files) if not permit_adhoc_cache: return local() @@ -481,9 +471,7 @@ Total chunks: {0.total_chunks} # so we can just sum up all archives to get the "all archives" stats: total_size = 0 for archive_name in self.manifest.archives: - archive = Archive( - self.repository, self.key, self.manifest, archive_name, consider_part_files=self.consider_part_files - ) + archive = Archive(self.manifest, archive_name, consider_part_files=self.consider_part_files) stats = archive.calc_stats(self, want_unique=False) total_size += stats.osize stats = self.Summary(total_size, unique_size, total_unique_chunks, total_chunks)._asdict() @@ -503,8 +491,6 @@ class LocalCache(CacheStatsMixin): def __init__( self, - repository, - key, manifest, path=None, sync=True, @@ -522,27 +508,29 @@ class LocalCache(CacheStatsMixin): :param cache_mode: what shall be compared in the file stat infos vs. cached stat infos comparison """ CacheStatsMixin.__init__(self, iec=iec) - self.repository = repository - self.key = key + assert isinstance(manifest, Manifest) self.manifest = manifest + self.repository = manifest.repository + self.key = manifest.key + self.repo_objs = manifest.repo_objs self.progress = progress self.cache_mode = cache_mode self.consider_part_files = consider_part_files self.timestamp = None self.txn_active = False - self.path = cache_dir(repository, path) - self.security_manager = SecurityManager(repository) + self.path = cache_dir(self.repository, path) + self.security_manager = SecurityManager(self.repository) self.cache_config = CacheConfig(self.repository, self.path, lock_wait) # Warn user before sending data to a never seen before unencrypted repository if not os.path.exists(self.path): - self.security_manager.assert_access_unknown(warn_if_unencrypted, manifest, key) + self.security_manager.assert_access_unknown(warn_if_unencrypted, manifest, self.key) self.create() self.open() try: - self.security_manager.assert_secure(manifest, key, cache_config=self.cache_config) + self.security_manager.assert_secure(manifest, self.key, cache_config=self.cache_config) if not self.check_cache_compatibility(): self.wipe_cache() @@ -912,7 +900,7 @@ class LocalCache(CacheStatsMixin): self.manifest.check_repository_compatibility((Manifest.Operation.READ,)) self.begin_txn() - with cache_if_remote(self.repository, decrypted_cache=self.key) as decrypted_repository: + with cache_if_remote(self.repository, decrypted_cache=self.repo_objs) as decrypted_repository: # TEMPORARY HACK: to avoid archive index caching, create a FILE named ~/.cache/borg/REPOID/chunks.archive.d - # this is only recommended if you have a fast, low latency connection to your repo (e.g. if repo is local disk) self.do_cache = os.path.isdir(archive_path) @@ -965,7 +953,7 @@ class LocalCache(CacheStatsMixin): return self.chunk_incref(id, stats) if size is None: raise ValueError("when giving compressed data for a new chunk, the uncompressed size must be given also") - data = self.key.encrypt(id, chunk, compress=compress) + data = self.repo_objs.format(id, {}, chunk, compress=compress, size=size) self.repository.put(id, data, wait=wait) self.chunks.add(id, 1, size) stats.update(size, not refcount) @@ -1094,18 +1082,18 @@ All archives: unknown unknown unknown Unique chunks Total chunks Chunk index: {0.total_unique_chunks:20d} unknown""" - def __init__( - self, repository, key, manifest, warn_if_unencrypted=True, lock_wait=None, consider_part_files=False, iec=False - ): + def __init__(self, manifest, warn_if_unencrypted=True, lock_wait=None, consider_part_files=False, iec=False): CacheStatsMixin.__init__(self, iec=iec) - self.repository = repository - self.key = key + assert isinstance(manifest, Manifest) self.manifest = manifest + self.repository = manifest.repository + self.key = manifest.key + self.repo_objs = manifest.repo_objs self.consider_part_files = consider_part_files self._txn_active = False - self.security_manager = SecurityManager(repository) - self.security_manager.assert_secure(manifest, key, lock_wait=lock_wait) + self.security_manager = SecurityManager(self.repository) + self.security_manager.assert_secure(manifest, self.key, lock_wait=lock_wait) logger.warning("Note: --no-cache-sync is an experimental feature.") @@ -1138,7 +1126,7 @@ Chunk index: {0.total_unique_chunks:20d} unknown""" refcount = self.seen_chunk(id, size) if refcount: return self.chunk_incref(id, stats, size=size) - data = self.key.encrypt(id, chunk, compress=compress) + data = self.repo_objs.format(id, {}, chunk, compress=compress) self.repository.put(id, data, wait=wait) self.chunks.add(id, 1, size) stats.update(size, not refcount) diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index 249ef6726..a6318d8a4 100644 --- a/src/borg/crypto/key.py +++ b/src/borg/crypto/key.py @@ -12,7 +12,6 @@ logger = create_logger() import argon2.low_level from ..constants import * # NOQA -from ..compress import Compressor from ..helpers import StableDict from ..helpers import Error, IntegrityError from ..helpers import get_keys_dir, get_security_dir @@ -23,6 +22,8 @@ from ..helpers import msgpack from ..item import Key, EncryptedKey, want_bytes from ..manifest import Manifest from ..platform import SaveFile +from ..repoobj import RepoObj + from .nonces import NonceManager from .low_level import AES, bytes_to_int, num_cipher_blocks, hmac_sha256, blake2b_256, hkdf_hmac_sha512 @@ -107,7 +108,8 @@ def identify_key(manifest_data): raise UnsupportedPayloadError(key_type) -def key_factory(repository, manifest_data): +def key_factory(repository, manifest_chunk, *, ro_cls=RepoObj): + manifest_data = ro_cls.extract_crypted_data(manifest_chunk) return identify_key(manifest_data).detect(repository, manifest_data) @@ -186,10 +188,6 @@ class KeyBase: self.TYPE_STR = bytes([self.TYPE]) self.repository = repository self.target = None # key location file path / repo obj - # Some commands write new chunks (e.g. rename) but don't take a --compression argument. This duplicates - # the default used by those commands who do take a --compression argument. - self.compressor = Compressor("lz4") - self.decompress = self.compressor.decompress self.tam_required = True self.copy_crypt_key = False @@ -197,10 +195,10 @@ class KeyBase: """Return HMAC hash using the "id" HMAC key""" raise NotImplementedError - def encrypt(self, id, data, compress=True): + def encrypt(self, id, data): pass - def decrypt(self, id, data, decompress=True): + def decrypt(self, id, data): pass def assert_id(self, id, data): @@ -301,19 +299,12 @@ class PlaintextKey(KeyBase): def id_hash(self, data): return sha256(data).digest() - def encrypt(self, id, data, compress=True): - if compress: - data = self.compressor.compress(data) + def encrypt(self, id, data): return b"".join([self.TYPE_STR, data]) - def decrypt(self, id, data, decompress=True): + def decrypt(self, id, data): self.assert_type(data[0], id) - payload = memoryview(data)[1:] - if not decompress: - return payload - data = self.decompress(payload) - self.assert_id(id, data) - return data + return memoryview(data)[1:] def _tam_key(self, salt, context): return salt + context @@ -380,23 +371,16 @@ class AESKeyBase(KeyBase): logically_encrypted = True - def encrypt(self, id, data, compress=True): - if compress: - data = self.compressor.compress(data) + def encrypt(self, id, data): next_iv = self.nonce_manager.ensure_reservation(self.cipher.next_iv(), self.cipher.block_count(len(data))) return self.cipher.encrypt(data, header=self.TYPE_STR, iv=next_iv) - def decrypt(self, id, data, decompress=True): + def decrypt(self, id, data): self.assert_type(data[0], id) try: - payload = self.cipher.decrypt(data) + return self.cipher.decrypt(data) except IntegrityError as e: raise IntegrityError(f"Chunk {bin_to_hex(id)}: Could not decrypt [{str(e)}]") - if not decompress: - return payload - data = self.decompress(memoryview(payload)) - self.assert_id(id, data) - return data def init_from_given_data(self, *, crypt_key, id_key, chunk_seed): assert len(crypt_key) in (32 + 32, 32 + 128) @@ -804,19 +788,12 @@ class AuthenticatedKeyBase(AESKeyBase, FlexiKey): if manifest_data is not None: self.assert_type(manifest_data[0]) - def encrypt(self, id, data, compress=True): - if compress: - data = self.compressor.compress(data) + def encrypt(self, id, data): return b"".join([self.TYPE_STR, data]) - def decrypt(self, id, data, decompress=True): + def decrypt(self, id, data): self.assert_type(data[0], id) - payload = memoryview(data)[1:] - if not decompress: - return payload - data = self.decompress(payload) - self.assert_id(id, data) - return data + return memoryview(data)[1:] class AuthenticatedKey(ID_HMAC_SHA_256, AuthenticatedKeyBase): @@ -861,10 +838,15 @@ class AEADKeyBase(KeyBase): MAX_IV = 2**48 - 1 - def encrypt(self, id, data, compress=True): + def assert_id(self, id, data): + # note: assert_id(id, data) is not needed any more for the new AEAD crypto. + # we put the id into AAD when storing the chunk, so it gets into the authentication tag computation. + # when decrypting, we provide the id we **want** as AAD for the auth tag verification, so + # decrypting only succeeds if we got the ciphertext we wrote **for that chunk id**. + pass + + def encrypt(self, id, data): # to encrypt new data in this session we use always self.cipher and self.sessionid - if compress: - data = self.compressor.compress(data) reserved = b"\0" iv = self.cipher.next_iv() if iv > self.MAX_IV: # see the data-structures docs about why the IV range is enough @@ -873,7 +855,7 @@ class AEADKeyBase(KeyBase): header = self.TYPE_STR + reserved + iv_48bit + self.sessionid return self.cipher.encrypt(data, header=header, iv=iv, aad=id) - def decrypt(self, id, data, decompress=True): + def decrypt(self, id, data): # to decrypt existing data, we need to get a cipher configured for the sessionid and iv from header self.assert_type(data[0], id) iv_48bit = data[2:8] @@ -881,17 +863,9 @@ class AEADKeyBase(KeyBase): iv = int.from_bytes(iv_48bit, "big") cipher = self._get_cipher(sessionid, iv) try: - payload = cipher.decrypt(data, aad=id) + return cipher.decrypt(data, aad=id) except IntegrityError as e: raise IntegrityError(f"Chunk {bin_to_hex(id)}: Could not decrypt [{str(e)}]") - if not decompress: - return payload - data = self.decompress(memoryview(payload)) - # note: calling self.assert_id(id, data) is not needed any more for the new AEAD crypto. - # we put the id into AAD when storing the chunk, so it gets into the authentication tag computation. - # when decrypting, we provide the id we **want** as AAD for the auth tag verification, so - # decrypting only succeeds if we got the ciphertext we wrote **for that chunk id**. - return data def init_from_given_data(self, *, crypt_key, id_key, chunk_seed): assert len(crypt_key) in (32 + 32, 32 + 128) diff --git a/src/borg/crypto/keymanager.py b/src/borg/crypto/keymanager.py index ba3acdce9..1b6a49e63 100644 --- a/src/borg/crypto/keymanager.py +++ b/src/borg/crypto/keymanager.py @@ -7,6 +7,8 @@ from hashlib import sha256 from ..helpers import Error, yes, bin_to_hex, dash_open from ..manifest import Manifest, NoManifestError from ..repository import Repository +from ..repoobj import RepoObj + from .key import CHPOKeyfileKey, RepoKeyNotFoundError, KeyBlobStorage, identify_key @@ -40,10 +42,11 @@ class KeyManager: self.keyblob_storage = None try: - manifest_data = self.repository.get(Manifest.MANIFEST_ID) + manifest_chunk = self.repository.get(Manifest.MANIFEST_ID) except Repository.ObjectNotFound: raise NoManifestError + manifest_data = RepoObj.extract_crypted_data(manifest_chunk) key = identify_key(manifest_data) self.keyblob_storage = key.STORAGE if self.keyblob_storage == KeyBlobStorage.NO_STORAGE: diff --git a/src/borg/fuse.py b/src/borg/fuse.py index 388195c9e..c42930c41 100644 --- a/src/borg/fuse.py +++ b/src/borg/fuse.py @@ -241,12 +241,12 @@ class ItemCache: class FuseBackend: """Virtual filesystem based on archive(s) to provide information to fuse""" - def __init__(self, key, manifest, repository, args, decrypted_repository): - self.repository_uncached = repository + def __init__(self, manifest, args, decrypted_repository): self._args = args self.numeric_ids = args.numeric_ids self._manifest = manifest - self.key = key + self.repo_objs = manifest.repo_objs + self.repository_uncached = manifest.repository # Maps inode numbers to Item instances. This is used for synthetic inodes, i.e. file-system objects that are # made up and are not contained in the archives. For example archive directories or intermediate directories # not contained in archives. @@ -330,13 +330,7 @@ class FuseBackend: """Build FUSE inode hierarchy from archive metadata""" self.file_versions = {} # for versions mode: original path -> version t0 = time.perf_counter() - archive = Archive( - self.repository_uncached, - self.key, - self._manifest, - archive_name, - consider_part_files=self._args.consider_part_files, - ) + archive = Archive(self._manifest, archive_name, consider_part_files=self._args.consider_part_files) strip_components = self._args.strip_components matcher = build_matcher(self._args.patterns, self._args.paths) hlm = HardLinkManager(id_type=bytes, info_type=str) # hlid -> path @@ -447,9 +441,9 @@ class FuseBackend: class FuseOperations(llfuse.Operations, FuseBackend): """Export archive as a FUSE filesystem""" - def __init__(self, key, repository, manifest, args, decrypted_repository): + def __init__(self, manifest, args, decrypted_repository): llfuse.Operations.__init__(self) - FuseBackend.__init__(self, key, manifest, repository, args, decrypted_repository) + FuseBackend.__init__(self, manifest, args, decrypted_repository) self.decrypted_repository = decrypted_repository data_cache_capacity = int(os.environ.get("BORG_MOUNT_DATA_CACHE_ENTRIES", os.cpu_count() or 1)) logger.debug("mount data cache capacity: %d chunks", data_cache_capacity) @@ -688,7 +682,7 @@ class FuseOperations(llfuse.Operations, FuseBackend): # evict fully read chunk from cache del self.data_cache[id] else: - data = self.key.decrypt(id, self.repository_uncached.get(id)) + _, data = self.repo_objs.parse(id, self.repository_uncached.get(id)) if offset + n < len(data): # chunk was only partially read, cache it self.data_cache[id] = data diff --git a/src/borg/helpers/parseformat.py b/src/borg/helpers/parseformat.py index 9af35f741..d1508aef7 100644 --- a/src/borg/helpers/parseformat.py +++ b/src/borg/helpers/parseformat.py @@ -673,7 +673,7 @@ class ArchiveFormatter(BaseFormatter): if self._archive is None or self._archive.id != self.id: from ..archive import Archive - self._archive = Archive(self.repository, self.key, self.manifest, self.name, iec=self.iec) + self._archive = Archive(self.manifest, self.name, iec=self.iec) return self._archive def get_meta(self, key, rs): diff --git a/src/borg/manifest.py b/src/borg/manifest.py index cccc17fdc..3e9d935c1 100644 --- a/src/borg/manifest.py +++ b/src/borg/manifest.py @@ -17,6 +17,7 @@ from .helpers.datastruct import StableDict from .helpers.parseformat import bin_to_hex from .helpers.time import parse_timestamp from .helpers.errors import Error +from .repoobj import RepoObj class NoManifestError(Error): @@ -164,10 +165,11 @@ class Manifest: MANIFEST_ID = b"\0" * 32 - def __init__(self, key, repository, item_keys=None): + def __init__(self, key, repository, item_keys=None, ro_cls=RepoObj): self.archives = Archives() self.config = {} self.key = key + self.repo_objs = ro_cls(key) self.repository = repository self.item_keys = frozenset(item_keys) if item_keys is not None else ITEM_KEYS self.tam_verified = False @@ -182,7 +184,7 @@ class Manifest: return parse_timestamp(self.timestamp) @classmethod - def load(cls, repository, operations, key=None, force_tam_not_required=False): + def load(cls, repository, operations, key=None, force_tam_not_required=False, *, ro_cls=RepoObj): from .item import ManifestItem from .crypto.key import key_factory, tam_required_file, tam_required from .repository import Repository @@ -192,14 +194,14 @@ class Manifest: except Repository.ObjectNotFound: raise NoManifestError if not key: - key = key_factory(repository, cdata) - manifest = cls(key, repository) - data = key.decrypt(cls.MANIFEST_ID, cdata) + key = key_factory(repository, cdata, ro_cls=ro_cls) + manifest = cls(key, repository, ro_cls=ro_cls) + _, data = manifest.repo_objs.parse(cls.MANIFEST_ID, cdata) manifest_dict, manifest.tam_verified = key.unpack_and_verify_manifest( data, force_tam_not_required=force_tam_not_required ) m = ManifestItem(internal_dict=manifest_dict) - manifest.id = key.id_hash(data) + manifest.id = manifest.repo_objs.id_hash(data) if m.get("version") not in (1, 2): raise ValueError("Invalid manifest version") manifest.archives.set_raw_dict(m.archives) @@ -219,7 +221,7 @@ class Manifest: logger.debug("Manifest is TAM verified and says TAM is *not* required, updating security database...") os.unlink(tam_required_file(repository)) manifest.check_repository_compatibility(operations) - return manifest, key + return manifest def check_repository_compatibility(self, operations): for operation in operations: @@ -272,5 +274,5 @@ class Manifest: ) self.tam_verified = True data = self.key.pack_and_authenticate_metadata(manifest.as_dict()) - self.id = self.key.id_hash(data) - self.repository.put(self.MANIFEST_ID, self.key.encrypt(self.MANIFEST_ID, data)) + self.id = self.repo_objs.id_hash(data) + self.repository.put(self.MANIFEST_ID, self.repo_objs.format(self.MANIFEST_ID, {}, data)) diff --git a/src/borg/remote.py b/src/borg/remote.py index 962f785fd..9eb10efd1 100644 --- a/src/borg/remote.py +++ b/src/borg/remote.py @@ -1283,7 +1283,7 @@ def cache_if_remote(repository, *, decrypted_cache=False, pack=None, unpack=None """ Return a Repository(No)Cache for *repository*. - If *decrypted_cache* is a key object, then get and get_many will return a tuple + If *decrypted_cache* is a repo_objs object, then get and get_many will return a tuple (csize, plaintext) instead of the actual data in the repository. The cache will store decrypted data, which increases CPU efficiency (by avoiding repeatedly decrypting and more importantly MAC and ID checking cached objects). @@ -1292,7 +1292,7 @@ def cache_if_remote(repository, *, decrypted_cache=False, pack=None, unpack=None if decrypted_cache and (pack or unpack or transform): raise ValueError("decrypted_cache and pack/unpack/transform are incompatible") elif decrypted_cache: - key = decrypted_cache + repo_objs = decrypted_cache # 32 bit csize, 64 bit (8 byte) xxh64 cache_struct = struct.Struct("=I8s") compressor = Compressor("lz4") @@ -1311,8 +1311,8 @@ def cache_if_remote(repository, *, decrypted_cache=False, pack=None, unpack=None return csize, compressor.decompress(compressed) def transform(id_, data): - csize = len(data) - decrypted = key.decrypt(id_, data) + meta, decrypted = repo_objs.parse(id_, data) + csize = meta.get("csize", len(data)) return csize, decrypted if isinstance(repository, RemoteRepository) or force_cache: diff --git a/src/borg/repoobj.py b/src/borg/repoobj.py new file mode 100644 index 000000000..cc696a49b --- /dev/null +++ b/src/borg/repoobj.py @@ -0,0 +1,129 @@ +from struct import Struct + +from borg.helpers import msgpack +from borg.compress import Compressor, LZ4_COMPRESSOR + + +class RepoObj: + meta_len_hdr = Struct(" bytes: + # used for crypto type detection + offs = cls.meta_len_hdr.size + meta_len = cls.meta_len_hdr.unpack(data[:offs])[0] + return data[offs + meta_len :] + + def __init__(self, key): + self.key = key + # Some commands write new chunks (e.g. rename) but don't take a --compression argument. This duplicates + # the default used by those commands who do take a --compression argument. + self.compressor = LZ4_COMPRESSOR + self.decompress = Compressor("lz4").decompress + + def id_hash(self, data: bytes) -> bytes: + return self.key.id_hash(data) + + def format(self, id: bytes, meta: dict, data: bytes, compress: bool = True, size: int = None) -> bytes: + assert isinstance(id, bytes) + assert isinstance(meta, dict) + assert isinstance(data, (bytes, memoryview)) + assert compress or size is not None + if compress: + assert size is None or size == len(data) + size = len(data) if size is None else size + data_compressed = self.compressor.compress(data) # TODO: compressor also adds compressor type/level bytes + else: + assert isinstance(size, int) + data_compressed = data # is already compressed + meta = dict(meta) # make a copy, so call arg is not modified + meta["size"] = size + meta["csize"] = len(data_compressed) + # meta["ctype"] = ... + # meta["clevel"] = ... + data_encrypted = self.key.encrypt(id, data_compressed) + meta_packed = msgpack.packb(meta) + meta_encrypted = self.key.encrypt(id, meta_packed) + hdr = self.meta_len_hdr.pack(len(meta_encrypted)) + return hdr + meta_encrypted + data_encrypted + + def parse_meta(self, id: bytes, cdata: bytes) -> dict: + # when calling parse_meta, enough cdata needs to be supplied to completely contain the + # meta_len_hdr and the encrypted, packed metadata. it is allowed to provide more cdata. + assert isinstance(id, bytes) + assert isinstance(cdata, bytes) + obj = memoryview(cdata) + offs = self.meta_len_hdr.size + hdr = obj[:offs] + len_meta_encrypted = self.meta_len_hdr.unpack(hdr)[0] + assert offs + len_meta_encrypted <= len(obj) + meta_encrypted = obj[offs : offs + len_meta_encrypted] + meta_packed = self.key.decrypt(id, meta_encrypted) + meta = msgpack.unpackb(meta_packed) + return meta + + def parse(self, id: bytes, cdata: bytes, decompress: bool = True) -> tuple[dict, bytes]: + assert isinstance(id, bytes) + assert isinstance(cdata, bytes) + obj = memoryview(cdata) + offs = self.meta_len_hdr.size + hdr = obj[:offs] + len_meta_encrypted = self.meta_len_hdr.unpack(hdr)[0] + assert offs + len_meta_encrypted <= len(obj) + meta_encrypted = obj[offs : offs + len_meta_encrypted] + offs += len_meta_encrypted + meta_packed = self.key.decrypt(id, meta_encrypted) + meta = msgpack.unpackb(meta_packed) + data_encrypted = obj[offs:] + data_compressed = self.key.decrypt(id, data_encrypted) + if decompress: + data = self.decompress(data_compressed) # TODO: decompressor still needs type/level bytes + self.key.assert_id(id, data) + else: + data = data_compressed + return meta, data + + +class RepoObj1: # legacy + @classmethod + def extract_crypted_data(cls, data: bytes) -> bytes: + # used for crypto type detection + return data + + def __init__(self, key): + self.key = key + self.compressor = LZ4_COMPRESSOR + self.decompress = Compressor("lz4").decompress + + def id_hash(self, data: bytes) -> bytes: + return self.key.id_hash(data) + + def format(self, id: bytes, meta: dict, data: bytes, compress: bool = True, size: int = None) -> bytes: + assert isinstance(id, bytes) + assert meta == {} + assert isinstance(data, (bytes, memoryview)) + assert compress or size is not None + assert compress or size is not None + if compress: + assert size is None + size = len(data) + data_compressed = self.compressor.compress(data) # TODO: compressor also adds compressor type/level bytes + else: + assert isinstance(size, int) + data_compressed = data # is already compressed + data_encrypted = self.key.encrypt(id, data_compressed) + return data_encrypted + + def parse(self, id: bytes, cdata: bytes, decompress: bool = True) -> tuple[dict, bytes]: + assert isinstance(id, bytes) + assert isinstance(cdata, bytes) + meta = {} + data_compressed = self.key.decrypt(id, cdata) + meta["csize"] = len(data_compressed) + if decompress: + data = self.decompress(data_compressed) # TODO: decompressor still needs type/level bytes + self.key.assert_id(id, data) + meta["size"] = len(data) + else: + data = data_compressed + return meta, data diff --git a/src/borg/testsuite/archive.py b/src/borg/testsuite/archive.py index c156c7641..8ddfbb522 100644 --- a/src/borg/testsuite/archive.py +++ b/src/borg/testsuite/archive.py @@ -110,8 +110,8 @@ class ArchiveTimestampTestCase(BaseTestCase): def _test_timestamp_parsing(self, isoformat, expected): repository = Mock() key = PlaintextKey(repository) - manifest = Manifest(repository, key) - a = Archive(repository, key, manifest, "test", create=True) + manifest = Manifest(key, repository) + a = Archive(manifest, "test", create=True) a.metadata = ArchiveItem(time=isoformat) self.assert_equal(a.ts, expected) diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py index 4dd8831cd..a188f075b 100644 --- a/src/borg/testsuite/archiver.py +++ b/src/borg/testsuite/archiver.py @@ -314,8 +314,8 @@ class ArchiverTestCaseBase(BaseTestCase): def open_archive(self, name): repository = Repository(self.repository_path, exclusive=True) with repository: - manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) - archive = Archive(repository, key, manifest, name) + manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) + archive = Archive(manifest, name) return archive, repository def open_repository(self): @@ -1660,7 +1660,7 @@ class ArchiverTestCase(ArchiverTestCaseBase): self.cmd(f"--repo={self.repository_location}", "extract", "test.4", "--dry-run") # Make sure both archives have been renamed with Repository(self.repository_path) as repository: - manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) + manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) self.assert_equal(len(manifest.archives), 2) self.assert_in("test.3", manifest.archives) self.assert_in("test.4", manifest.archives) @@ -1784,8 +1784,8 @@ class ArchiverTestCase(ArchiverTestCaseBase): self.cmd(f"--repo={self.repository_location}", "rcreate", "--encryption=none") self.create_src_archive("test") with Repository(self.repository_path, exclusive=True) as repository: - manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) - archive = Archive(repository, key, manifest, "test") + manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) + archive = Archive(manifest, "test") for item in archive.iter_items(): if item.path.endswith("testsuite/archiver.py"): repository.delete(item.chunks[-1].id) @@ -1803,8 +1803,8 @@ class ArchiverTestCase(ArchiverTestCaseBase): self.cmd(f"--repo={self.repository_location}", "rcreate", "--encryption=none") self.create_src_archive("test") with Repository(self.repository_path, exclusive=True) as repository: - manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) - archive = Archive(repository, key, manifest, "test") + manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) + archive = Archive(manifest, "test") id = archive.metadata.items[0] repository.put(id, b"corrupted items metadata stream chunk") repository.commit(compact=False) @@ -1952,12 +1952,12 @@ class ArchiverTestCase(ArchiverTestCaseBase): self.cmd(f"--repo={self.repository_location}", "create", "--dry-run", "test", "input") # Make sure no archive has been created with Repository(self.repository_path) as repository: - manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) + manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) self.assert_equal(len(manifest.archives), 0) def add_unknown_feature(self, operation): with Repository(self.repository_path, exclusive=True) as repository: - manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) + manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) manifest.config["feature_flags"] = {operation.value: {"mandatory": ["unknown-feature"]}} manifest.write() repository.commit(compact=False) @@ -2034,8 +2034,8 @@ class ArchiverTestCase(ArchiverTestCaseBase): with Repository(self.repository_path, exclusive=True) as repository: if path_prefix: repository._location = Location(self.repository_location) - manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) - with Cache(repository, key, manifest) as cache: + manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) + with Cache(repository, manifest) as cache: cache.begin_txn() cache.cache_config.mandatory_features = {"unknown-feature"} cache.commit() @@ -2059,8 +2059,8 @@ class ArchiverTestCase(ArchiverTestCaseBase): with Repository(self.repository_path, exclusive=True) as repository: if path_prefix: repository._location = Location(self.repository_location) - manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) - with Cache(repository, key, manifest) as cache: + manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) + with Cache(repository, manifest) as cache: assert cache.cache_config.mandatory_features == set() def test_progress_on(self): @@ -3060,11 +3060,11 @@ class ArchiverTestCase(ArchiverTestCaseBase): self.cmd(f"--repo={self.repository_location}", "check") # Then check that the cache on disk matches exactly what's in the repo. with self.open_repository() as repository: - manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) - with Cache(repository, key, manifest, sync=False) as cache: + manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) + with Cache(repository, manifest, sync=False) as cache: original_chunks = cache.chunks Cache.destroy(repository) - with Cache(repository, key, manifest) as cache: + with Cache(repository, manifest) as cache: correct_chunks = cache.chunks assert original_chunks is not correct_chunks seen = set() @@ -3080,8 +3080,8 @@ class ArchiverTestCase(ArchiverTestCaseBase): self.cmd(f"--repo={self.repository_location}", "rcreate", RK_ENCRYPTION) self.cmd(f"--repo={self.repository_location}", "create", "test", "input") with self.open_repository() as repository: - manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) - with Cache(repository, key, manifest, sync=False) as cache: + manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) + with Cache(repository, manifest, sync=False) as cache: cache.begin_txn() cache.chunks.incref(list(cache.chunks.iteritems())[0][0]) cache.commit() @@ -3966,7 +3966,8 @@ class ArchiverCheckTestCase(ArchiverTestCaseBase): def test_manifest_rebuild_duplicate_archive(self): archive, repository = self.open_archive("archive1") - key = archive.key + repo_objs = archive.repo_objs + with repository: manifest = repository.get(Manifest.MANIFEST_ID) corrupted_manifest = manifest + b"corrupted!" @@ -3983,8 +3984,8 @@ class ArchiverCheckTestCase(ArchiverTestCaseBase): "version": 2, } ) - archive_id = key.id_hash(archive) - repository.put(archive_id, key.encrypt(archive_id, archive)) + archive_id = repo_objs.id_hash(archive) + repository.put(archive_id, repo_objs.format(archive_id, {}, archive)) repository.commit(compact=False) self.cmd(f"--repo={self.repository_location}", "check", exit_code=1) self.cmd(f"--repo={self.repository_location}", "check", "--repair", exit_code=0) @@ -4042,45 +4043,43 @@ class ArchiverCheckTestCase(ArchiverTestCaseBase): class ManifestAuthenticationTest(ArchiverTestCaseBase): def spoof_manifest(self, repository): with repository: - _, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) - repository.put( + manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) + cdata = manifest.repo_objs.format( Manifest.MANIFEST_ID, - key.encrypt( - Manifest.MANIFEST_ID, - msgpack.packb( - { - "version": 1, - "archives": {}, - "config": {}, - "timestamp": (datetime.now(tz=timezone.utc) + timedelta(days=1)).isoformat( - timespec="microseconds" - ), - } - ), + {}, + msgpack.packb( + { + "version": 1, + "archives": {}, + "config": {}, + "timestamp": (datetime.now(tz=timezone.utc) + timedelta(days=1)).isoformat( + timespec="microseconds" + ), + } ), ) + repository.put(Manifest.MANIFEST_ID, cdata) repository.commit(compact=False) def test_fresh_init_tam_required(self): self.cmd(f"--repo={self.repository_location}", "rcreate", RK_ENCRYPTION) repository = Repository(self.repository_path, exclusive=True) with repository: - manifest, key = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) - repository.put( + manifest = Manifest.load(repository, Manifest.NO_OPERATION_CHECK) + cdata = manifest.repo_objs.format( Manifest.MANIFEST_ID, - key.encrypt( - Manifest.MANIFEST_ID, - msgpack.packb( - { - "version": 1, - "archives": {}, - "timestamp": (datetime.now(tz=timezone.utc) + timedelta(days=1)).isoformat( - timespec="microseconds" - ), - } - ), + {}, + msgpack.packb( + { + "version": 1, + "archives": {}, + "timestamp": (datetime.now(tz=timezone.utc) + timedelta(days=1)).isoformat( + timespec="microseconds" + ), + } ), ) + repository.put(Manifest.MANIFEST_ID, cdata) repository.commit(compact=False) with pytest.raises(TAMRequiredError): diff --git a/src/borg/testsuite/cache.py b/src/borg/testsuite/cache.py index 04eff12a6..ac34c9c4c 100644 --- a/src/borg/testsuite/cache.py +++ b/src/borg/testsuite/cache.py @@ -9,7 +9,6 @@ from .hashindex import H from .key import TestKey from ..archive import Statistics from ..cache import AdHocCache -from ..compress import CompressionSpec from ..crypto.key import AESOCBRepoKey from ..hashindex import ChunkIndex, CacheSynchronizer from ..manifest import Manifest @@ -167,17 +166,16 @@ class TestAdHocCache: def key(self, repository, monkeypatch): monkeypatch.setenv("BORG_PASSPHRASE", "test") key = AESOCBRepoKey.create(repository, TestKey.MockArgs()) - key.compressor = CompressionSpec("none").compressor return key @pytest.fixture def manifest(self, repository, key): Manifest(key, repository).write() - return Manifest.load(repository, key=key, operations=Manifest.NO_OPERATION_CHECK)[0] + return Manifest.load(repository, key=key, operations=Manifest.NO_OPERATION_CHECK) @pytest.fixture def cache(self, repository, key, manifest): - return AdHocCache(repository, key, manifest) + return AdHocCache(manifest) def test_does_not_contain_manifest(self, cache): assert not cache.seen_chunk(Manifest.MANIFEST_ID) diff --git a/src/borg/testsuite/key.py b/src/borg/testsuite/key.py index 2ef710357..c0417990b 100644 --- a/src/borg/testsuite/key.py +++ b/src/borg/testsuite/key.py @@ -8,6 +8,7 @@ import pytest from ..crypto.key import bin_to_hex from ..crypto.key import PlaintextKey, AuthenticatedKey, Blake2AuthenticatedKey from ..crypto.key import RepoKey, KeyfileKey, Blake2RepoKey, Blake2KeyfileKey +from ..crypto.key import AEADKeyBase from ..crypto.key import AESOCBRepoKey, AESOCBKeyfileKey, CHPORepoKey, CHPOKeyfileKey from ..crypto.key import Blake2AESOCBRepoKey, Blake2AESOCBKeyfileKey, Blake2CHPORepoKey, Blake2CHPOKeyfileKey from ..crypto.key import ID_HMAC_SHA_256, ID_BLAKE2b_256 @@ -42,15 +43,8 @@ class TestKey: F84MsMMiqpbz4KVICeBZhfAaTPs4W7BC63qml0ZXJhdGlvbnPOAAGGoKRzYWx02gAgLENQ 2uVCoR7EnAoiRzn8J+orbojKtJlNCnQ31SSC8rendmVyc2lvbgE=""".strip() - keyfile2_cdata = unhexlify( - re.sub( - r"\W", - "", - """ - 0055f161493fcfc16276e8c31493c4641e1eb19a79d0326fad0291e5a9c98e5933 - 00000000000003e8d21eaf9b86c297a8cd56432e1915bb - """, - ) + keyfile2_cdata = bytes.fromhex( + "003be7d57280d1a42add9f3f36ea363bbc5e9349ad01ddec0634a54dd02959e70500000000000003ec063d2cbcacba6b" ) keyfile2_id = unhexlify("c3fbf14bc001ebcc3cd86e696c13482ed071740927cd7cbe1b01b4bfcee49314") @@ -69,7 +63,7 @@ class TestKey: qkPqtDDxs2j/T7+ndmVyc2lvbgE=""".strip() keyfile_blake2_cdata = bytes.fromhex( - "04fdf9475cf2323c0ba7a99ddc011064f2e7d039f539f2e448" "0e6f5fc6ff9993d604040404040404098c8cee1c6db8c28947" + "04d6040f5ef80e0a8ac92badcbe3dee83b7a6b53d5c9a58c4eed14964cb10ef591040404040404040d1e65cc1f435027" ) # Verified against b2sum. Entire string passed to BLAKE2, including the padded 64 byte key contained in # keyfile_blake2_key_file above is @@ -224,7 +218,8 @@ class TestKey: data = bytearray(self.keyfile2_cdata) id = bytearray(key.id_hash(data)) # corrupt chunk id id[12] = 0 - key.decrypt(id, data) + plaintext = key.decrypt(id, data) + key.assert_id(id, plaintext) def test_roundtrip(self, key): repository = key.repository @@ -237,45 +232,18 @@ class TestKey: decrypted = loaded_key.decrypt(id, encrypted) assert decrypted == plaintext - def test_decrypt_decompress(self, key): - plaintext = b"123456789" - id = key.id_hash(plaintext) - encrypted = key.encrypt(id, plaintext) - assert key.decrypt(id, encrypted, decompress=False) != plaintext - assert key.decrypt(id, encrypted) == plaintext - def test_assert_id(self, key): plaintext = b"123456789" id = key.id_hash(plaintext) key.assert_id(id, plaintext) id_changed = bytearray(id) id_changed[0] ^= 1 - with pytest.raises(IntegrityError): - key.assert_id(id_changed, plaintext) - plaintext_changed = plaintext + b"1" - with pytest.raises(IntegrityError): - key.assert_id(id, plaintext_changed) - - def test_getting_wrong_chunk_fails(self, key): - # for the new AEAD crypto, we provide the chunk id as AAD when encrypting/authenticating, - # we provide the id **we want** as AAD when authenticating/decrypting the data we got from the repo. - # only if the id used for encrypting matches the id we want, the AEAD crypto authentication will succeed. - # thus, there is no need any more for calling self._assert_id() for the new crypto. - # the old crypto as well as plaintext and authenticated modes still need to call self._assert_id(). - plaintext_wanted = b"123456789" - id_wanted = key.id_hash(plaintext_wanted) - ciphertext_wanted = key.encrypt(id_wanted, plaintext_wanted) - plaintext_other = b"xxxxxxxxx" - id_other = key.id_hash(plaintext_other) - ciphertext_other = key.encrypt(id_other, plaintext_other) - # both ciphertexts are authentic and decrypting them should succeed: - key.decrypt(id_wanted, ciphertext_wanted) - key.decrypt(id_other, ciphertext_other) - # but if we wanted the one and got the other, it must fail. - # the new crypto will fail due to AEAD auth failure, - # the old crypto and plaintext, authenticated modes will fail due to ._assert_id() check failing: - with pytest.raises(IntegrityErrorBase): - key.decrypt(id_wanted, ciphertext_other) + if not isinstance(key, AEADKeyBase): + with pytest.raises(IntegrityError): + key.assert_id(id_changed, plaintext) + plaintext_changed = plaintext + b"1" + with pytest.raises(IntegrityError): + key.assert_id(id, plaintext_changed) def test_authenticated_encrypt(self, monkeypatch): monkeypatch.setenv("BORG_PASSPHRASE", "test") @@ -285,8 +253,8 @@ class TestKey: plaintext = b"123456789" id = key.id_hash(plaintext) authenticated = key.encrypt(id, plaintext) - # 0x07 is the key TYPE, \x00ff identifies no compression / unknown level. - assert authenticated == b"\x07\x00\xff" + plaintext + # 0x07 is the key TYPE. + assert authenticated == b"\x07" + plaintext def test_blake2_authenticated_encrypt(self, monkeypatch): monkeypatch.setenv("BORG_PASSPHRASE", "test") @@ -296,8 +264,8 @@ class TestKey: plaintext = b"123456789" id = key.id_hash(plaintext) authenticated = key.encrypt(id, plaintext) - # 0x06 is the key TYPE, 0x00ff identifies no compression / unknown level. - assert authenticated == b"\x06\x00\xff" + plaintext + # 0x06 is the key TYPE. + assert authenticated == b"\x06" + plaintext class TestTAM: diff --git a/src/borg/testsuite/remote.py b/src/borg/testsuite/remote.py index 348f6f202..95375dc09 100644 --- a/src/borg/testsuite/remote.py +++ b/src/borg/testsuite/remote.py @@ -9,8 +9,8 @@ import pytest from ..remote import SleepingBandwidthLimiter, RepositoryCache, cache_if_remote from ..repository import Repository from ..crypto.key import PlaintextKey -from ..compress import CompressionSpec from ..helpers import IntegrityError +from ..repoobj import RepoObj from .hashindex import H from .key import TestKey @@ -160,35 +160,38 @@ class TestRepositoryCache: def key(self, repository, monkeypatch): monkeypatch.setenv("BORG_PASSPHRASE", "test") key = PlaintextKey.create(repository, TestKey.MockArgs()) - key.compressor = CompressionSpec("none").compressor return key - def _put_encrypted_object(self, key, repository, data): - id_ = key.id_hash(data) - repository.put(id_, key.encrypt(id_, data)) + @pytest.fixture + def repo_objs(self, key): + return RepoObj(key) + + def _put_encrypted_object(self, repo_objs, repository, data): + id_ = repo_objs.id_hash(data) + repository.put(id_, repo_objs.format(id_, {}, data)) return id_ @pytest.fixture - def H1(self, key, repository): - return self._put_encrypted_object(key, repository, b"1234") + def H1(self, repo_objs, repository): + return self._put_encrypted_object(repo_objs, repository, b"1234") @pytest.fixture - def H2(self, key, repository): - return self._put_encrypted_object(key, repository, b"5678") + def H2(self, repo_objs, repository): + return self._put_encrypted_object(repo_objs, repository, b"5678") @pytest.fixture - def H3(self, key, repository): - return self._put_encrypted_object(key, repository, bytes(100)) + def H3(self, repo_objs, repository): + return self._put_encrypted_object(repo_objs, repository, bytes(100)) @pytest.fixture - def decrypted_cache(self, key, repository): - return cache_if_remote(repository, decrypted_cache=key, force_cache=True) + def decrypted_cache(self, repo_objs, repository): + return cache_if_remote(repository, decrypted_cache=repo_objs, force_cache=True) def test_cache_corruption(self, decrypted_cache: RepositoryCache, H1, H2, H3): list(decrypted_cache.get_many([H1, H2, H3])) iterator = decrypted_cache.get_many([H1, H2, H3]) - assert next(iterator) == (7, b"1234") + assert next(iterator) == (6, b"1234") with open(decrypted_cache.key_filename(H2), "a+b") as fd: fd.seek(-1, io.SEEK_END) @@ -198,4 +201,4 @@ class TestRepositoryCache: fd.truncate() with pytest.raises(IntegrityError): - assert next(iterator) == (7, b"5678") + assert next(iterator) == (26, b"5678") diff --git a/src/borg/testsuite/repoobj.py b/src/borg/testsuite/repoobj.py new file mode 100644 index 000000000..b48876c2d --- /dev/null +++ b/src/borg/testsuite/repoobj.py @@ -0,0 +1,59 @@ +import pytest + +from ..crypto.key import PlaintextKey +from ..repository import Repository +from ..repoobj import RepoObj, RepoObj1 + + +@pytest.fixture +def repository(tmpdir): + return Repository(tmpdir, create=True) + + +@pytest.fixture +def key(repository): + return PlaintextKey(repository) + + +def test_format_parse_roundtrip(key): + repo_objs = RepoObj(key) + data = b"foobar" * 10 + id = repo_objs.id_hash(data) + meta = {"custom": "something"} # size and csize are computed automatically + cdata = repo_objs.format(id, meta, data) + + got_meta = repo_objs.parse_meta(id, cdata) + assert got_meta["size"] == len(data) + assert got_meta["csize"] < len(data) + assert got_meta["custom"] == "something" + + got_meta, got_data = repo_objs.parse(id, cdata) + assert got_meta["size"] == len(data) + assert got_meta["csize"] < len(data) + assert got_meta["custom"] == "something" + assert data == got_data + + edata = repo_objs.extract_crypted_data(cdata) + compressor = repo_objs.compressor + key = repo_objs.key + assert edata.startswith(bytes((key.TYPE, compressor.ID[0], compressor.level))) + + +def test_format_parse_roundtrip_borg1(key): # legacy + repo_objs = RepoObj1(key) + data = b"foobar" * 10 + id = repo_objs.id_hash(data) + meta = {} # borg1 does not support this kind of metadata + cdata = repo_objs.format(id, meta, data) + + # borg1 does not support separate metadata and borg2 does not invoke parse_meta for borg1 repos + + got_meta, got_data = repo_objs.parse(id, cdata) + assert got_meta["size"] == len(data) + assert got_meta["csize"] < len(data) + assert data == got_data + + edata = repo_objs.extract_crypted_data(cdata) + compressor = repo_objs.compressor + key = repo_objs.key + assert edata.startswith(bytes((key.TYPE, compressor.ID[0], compressor.level))) From 754c58379945488043adadd8e43dd2c4747bba5d Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 4 Sep 2022 21:50:27 +0200 Subject: [PATCH 02/14] decompression dispatching based on meta dict --- src/borg/repoobj.py | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/borg/repoobj.py b/src/borg/repoobj.py index cc696a49b..3979fa6a9 100644 --- a/src/borg/repoobj.py +++ b/src/borg/repoobj.py @@ -1,7 +1,7 @@ from struct import Struct -from borg.helpers import msgpack -from borg.compress import Compressor, LZ4_COMPRESSOR +from .helpers import msgpack +from .compress import Compressor, LZ4_COMPRESSOR class RepoObj: @@ -19,28 +19,40 @@ class RepoObj: # Some commands write new chunks (e.g. rename) but don't take a --compression argument. This duplicates # the default used by those commands who do take a --compression argument. self.compressor = LZ4_COMPRESSOR - self.decompress = Compressor("lz4").decompress def id_hash(self, data: bytes) -> bytes: return self.key.id_hash(data) - def format(self, id: bytes, meta: dict, data: bytes, compress: bool = True, size: int = None) -> bytes: + def format( + self, + id: bytes, + meta: dict, + data: bytes, + compress: bool = True, + size: int = None, + ctype: int = None, + clevel: int = None, + ) -> bytes: assert isinstance(id, bytes) assert isinstance(meta, dict) + meta = dict(meta) # make a copy, so call arg is not modified assert isinstance(data, (bytes, memoryview)) - assert compress or size is not None + assert compress or size is not None and ctype is not None and clevel is not None if compress: assert size is None or size == len(data) size = len(data) if size is None else size data_compressed = self.compressor.compress(data) # TODO: compressor also adds compressor type/level bytes + ctype = data_compressed[0] + clevel = data_compressed[1] else: assert isinstance(size, int) + assert isinstance(ctype, int) + assert isinstance(clevel, int) data_compressed = data # is already compressed - meta = dict(meta) # make a copy, so call arg is not modified meta["size"] = size meta["csize"] = len(data_compressed) - # meta["ctype"] = ... - # meta["clevel"] = ... + meta["ctype"] = ctype + meta["clevel"] = clevel data_encrypted = self.key.encrypt(id, data_compressed) meta_packed = msgpack.packb(meta) meta_encrypted = self.key.encrypt(id, meta_packed) @@ -77,7 +89,12 @@ class RepoObj: data_encrypted = obj[offs:] data_compressed = self.key.decrypt(id, data_encrypted) if decompress: - data = self.decompress(data_compressed) # TODO: decompressor still needs type/level bytes + ctype = meta["ctype"] + clevel = meta["clevel"] + compr_hdr = bytes((ctype, clevel)) + compressor_cls, compression_level = Compressor.detect(compr_hdr) + compressor = compressor_cls(level=compression_level) + data = compressor.decompress(data_compressed) # TODO: decompressor still needs type/level bytes self.key.assert_id(id, data) else: data = data_compressed @@ -93,7 +110,6 @@ class RepoObj1: # legacy def __init__(self, key): self.key = key self.compressor = LZ4_COMPRESSOR - self.decompress = Compressor("lz4").decompress def id_hash(self, data: bytes) -> bytes: return self.key.id_hash(data) @@ -106,7 +122,6 @@ class RepoObj1: # legacy assert compress or size is not None if compress: assert size is None - size = len(data) data_compressed = self.compressor.compress(data) # TODO: compressor also adds compressor type/level bytes else: assert isinstance(size, int) @@ -120,8 +135,12 @@ class RepoObj1: # legacy meta = {} data_compressed = self.key.decrypt(id, cdata) meta["csize"] = len(data_compressed) + compressor_cls, compression_level = Compressor.detect(data_compressed[:2]) + compressor = compressor_cls(level=compression_level) + meta["ctype"] = compressor.ID[0] + meta["clevel"] = compressor.level if decompress: - data = self.decompress(data_compressed) # TODO: decompressor still needs type/level bytes + data = compressor.decompress(data_compressed) # TODO: decompressor still needs type/level bytes self.key.assert_id(id, data) meta["size"] = len(data) else: From b6cbf045ff8ce606993631ac303c8fc130410f20 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sun, 4 Sep 2022 22:34:58 +0200 Subject: [PATCH 03/14] add a test for borg 1 -> 2 repo objects transformation --- src/borg/archive.py | 6 ++++-- src/borg/repoobj.py | 9 ++++---- src/borg/testsuite/remote.py | 4 ++-- src/borg/testsuite/repoobj.py | 40 +++++++++++++++++++++++++++++++++-- 4 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/borg/archive.py b/src/borg/archive.py index 490128f7d..d377871bc 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -2269,8 +2269,10 @@ class ArchiveRecreater: overwrite = self.recompress if self.recompress and not self.always_recompress and chunk_id in self.cache.chunks: # Check if this chunk is already compressed the way we want it - _, old_chunk = self.repo_objs.parse(chunk_id, self.repository.get(chunk_id), decompress=False) - compressor_cls, level = Compressor.detect(old_chunk) + old_meta, old_data = self.repo_objs.parse(chunk_id, self.repository.get(chunk_id), decompress=False) + # TODO simplify code below + compr_hdr = bytes((old_meta["ctype"], old_meta["clevel"])) + compressor_cls, level = Compressor.detect(compr_hdr) if ( compressor_cls.name == self.repo_objs.compressor.decide(data).name and level == self.repo_objs.compressor.level diff --git a/src/borg/repoobj.py b/src/borg/repoobj.py index 3979fa6a9..3f49c11c1 100644 --- a/src/borg/repoobj.py +++ b/src/borg/repoobj.py @@ -44,11 +44,12 @@ class RepoObj: data_compressed = self.compressor.compress(data) # TODO: compressor also adds compressor type/level bytes ctype = data_compressed[0] clevel = data_compressed[1] + data_compressed = data_compressed[2:] # strip the type/level bytes else: assert isinstance(size, int) assert isinstance(ctype, int) assert isinstance(clevel, int) - data_compressed = data # is already compressed + data_compressed = data # is already compressed, is NOT prefixed by type/level bytes meta["size"] = size meta["csize"] = len(data_compressed) meta["ctype"] = ctype @@ -94,10 +95,10 @@ class RepoObj: compr_hdr = bytes((ctype, clevel)) compressor_cls, compression_level = Compressor.detect(compr_hdr) compressor = compressor_cls(level=compression_level) - data = compressor.decompress(data_compressed) # TODO: decompressor still needs type/level bytes + data = compressor.decompress(compr_hdr + data_compressed) # TODO: decompressor still needs type/level bytes self.key.assert_id(id, data) else: - data = data_compressed + data = data_compressed # does not include the type/level bytes return meta, data @@ -125,7 +126,7 @@ class RepoObj1: # legacy data_compressed = self.compressor.compress(data) # TODO: compressor also adds compressor type/level bytes else: assert isinstance(size, int) - data_compressed = data # is already compressed + data_compressed = data # is already compressed, must include type/level bytes data_encrypted = self.key.encrypt(id, data_compressed) return data_encrypted diff --git a/src/borg/testsuite/remote.py b/src/borg/testsuite/remote.py index 95375dc09..35f2b6df3 100644 --- a/src/borg/testsuite/remote.py +++ b/src/borg/testsuite/remote.py @@ -191,7 +191,7 @@ class TestRepositoryCache: list(decrypted_cache.get_many([H1, H2, H3])) iterator = decrypted_cache.get_many([H1, H2, H3]) - assert next(iterator) == (6, b"1234") + assert next(iterator) == (4, b"1234") with open(decrypted_cache.key_filename(H2), "a+b") as fd: fd.seek(-1, io.SEEK_END) @@ -201,4 +201,4 @@ class TestRepositoryCache: fd.truncate() with pytest.raises(IntegrityError): - assert next(iterator) == (26, b"5678") + assert next(iterator) == (4, b"5678") diff --git a/src/borg/testsuite/repoobj.py b/src/borg/testsuite/repoobj.py index b48876c2d..b7b452bac 100644 --- a/src/borg/testsuite/repoobj.py +++ b/src/borg/testsuite/repoobj.py @@ -3,6 +3,7 @@ import pytest from ..crypto.key import PlaintextKey from ..repository import Repository from ..repoobj import RepoObj, RepoObj1 +from ..compress import LZ4 @pytest.fixture @@ -34,9 +35,8 @@ def test_format_parse_roundtrip(key): assert data == got_data edata = repo_objs.extract_crypted_data(cdata) - compressor = repo_objs.compressor key = repo_objs.key - assert edata.startswith(bytes((key.TYPE, compressor.ID[0], compressor.level))) + assert edata.startswith(bytes((key.TYPE,))) def test_format_parse_roundtrip_borg1(key): # legacy @@ -57,3 +57,39 @@ def test_format_parse_roundtrip_borg1(key): # legacy compressor = repo_objs.compressor key = repo_objs.key assert edata.startswith(bytes((key.TYPE, compressor.ID[0], compressor.level))) + + +def test_borg1_borg2_transition(key): + # borg transfer reads borg 1.x repo objects (without decompressing them), + # writes borg 2 repo objects (giving already compressed data to avoid compression). + meta = {} # borg1 does not support this kind of metadata + data = b"foobar" * 10 + len_data = len(data) + repo_objs1 = RepoObj1(key) + id = repo_objs1.id_hash(data) + borg1_cdata = repo_objs1.format(id, meta, data) + meta1, compr_data1 = repo_objs1.parse(id, borg1_cdata, decompress=False) # borg transfer avoids (de)compression + # in borg 1, we can only get this metadata after decrypting the whole chunk (and we do not have "size" here): + assert meta1["ctype"] == LZ4.ID[0] # default compression + assert meta1["clevel"] == 0xFF # lz4 does not know levels (yet?) + assert meta1["csize"] < len_data # lz4 should make it smaller + + repo_objs2 = RepoObj(key) + # note: as we did not decompress, we do not have "size" and we need to get it from somewhere else. + # here, we just use len_data. for borg transfer, we also know the size from another metadata source. + borg2_cdata = repo_objs2.format( + id, meta1, compr_data1[2:], compress=False, size=len_data, ctype=meta1["ctype"], clevel=meta1["clevel"] + ) + meta2, data2 = repo_objs2.parse(id, borg2_cdata) + assert data2 == data + assert meta2["ctype"] == LZ4.ID[0] + assert meta2["clevel"] == 0xFF + assert meta2["csize"] == meta1["csize"] - 2 # borg2 does not store the type/level bytes there + assert meta2["size"] == len_data + + meta2 = repo_objs2.parse_meta(id, borg2_cdata) + # now, in borg 2, we have nice and separately decrypted metadata (no need to decrypt the whole chunk): + assert meta2["ctype"] == LZ4.ID[0] + assert meta2["clevel"] == 0xFF + assert meta2["csize"] == meta1["csize"] - 2 # borg2 does not store the type/level bytes there + assert meta2["size"] == len_data From 1e156ca02bc1a1d97bd1b99080d8bc5afc973e0c Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Mon, 5 Sep 2022 02:53:28 +0200 Subject: [PATCH 04/14] fix upgrader --- src/borg/archive.py | 15 +++++----- src/borg/archiver/transfer_cmd.py | 5 ++-- src/borg/cache.py | 16 +++++----- src/borg/repoobj.py | 5 +++- src/borg/testsuite/archive.py | 6 ++-- src/borg/testsuite/archiver.py | 3 +- src/borg/testsuite/cache.py | 10 +++---- src/borg/upgrade.py | 49 ++++++++++++++++--------------- 8 files changed, 58 insertions(+), 51 deletions(-) diff --git a/src/borg/archive.py b/src/borg/archive.py index d377871bc..53aae4497 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -370,7 +370,7 @@ class CacheChunkBuffer(ChunkBuffer): self.stats = stats def write_chunk(self, chunk): - id_, _ = self.cache.add_chunk(self.key.id_hash(chunk), chunk, self.stats, wait=False) + id_, _ = self.cache.add_chunk(self.key.id_hash(chunk), {}, chunk, stats=self.stats, wait=False) self.cache.repository.async_response(wait=False) return id_ @@ -415,7 +415,7 @@ def archive_put_items(chunk_ids, *, repo_objs, cache=None, stats=None, add_refer data = msgpack.packb(chunk_ids[i : i + IDS_PER_CHUNK]) id = repo_objs.id_hash(data) if cache is not None and stats is not None: - cache.add_chunk(id, data, stats) + cache.add_chunk(id, {}, data, stats=stats) elif add_reference is not None: cdata = repo_objs.format(id, {}, data) add_reference(id, len(data), cdata) @@ -667,7 +667,7 @@ Duration: {0.duration} data = self.key.pack_and_authenticate_metadata(metadata.as_dict(), context=b"archive") self.id = self.repo_objs.id_hash(data) try: - self.cache.add_chunk(self.id, data, self.stats) + self.cache.add_chunk(self.id, {}, data, stats=self.stats) except IntegrityError as err: err_msg = str(err) # hack to avoid changing the RPC protocol by introducing new (more specific) exception class @@ -967,7 +967,7 @@ Duration: {0.duration} del metadata.items data = msgpack.packb(metadata.as_dict()) new_id = self.key.id_hash(data) - self.cache.add_chunk(new_id, data, self.stats) + self.cache.add_chunk(new_id, {}, data, stats=self.stats) self.manifest.archives[self.name] = (new_id, metadata.time) self.cache.chunk_decref(self.id, self.stats) self.id = new_id @@ -1233,7 +1233,7 @@ class ChunksProcessor: def chunk_processor(chunk): chunk_id, data = cached_hash(chunk, self.key.id_hash) - chunk_entry = cache.add_chunk(chunk_id, data, stats, wait=False) + chunk_entry = cache.add_chunk(chunk_id, {}, data, stats=stats, wait=False) self.cache.repository.async_response(wait=False) return chunk_entry @@ -2269,8 +2269,7 @@ class ArchiveRecreater: overwrite = self.recompress if self.recompress and not self.always_recompress and chunk_id in self.cache.chunks: # Check if this chunk is already compressed the way we want it - old_meta, old_data = self.repo_objs.parse(chunk_id, self.repository.get(chunk_id), decompress=False) - # TODO simplify code below + old_meta = self.repo_objs.parse_meta(chunk_id, self.repository.get(chunk_id)) compr_hdr = bytes((old_meta["ctype"], old_meta["clevel"])) compressor_cls, level = Compressor.detect(compr_hdr) if ( @@ -2279,7 +2278,7 @@ class ArchiveRecreater: ): # Stored chunk has the same compression method and level as we wanted overwrite = False - chunk_entry = self.cache.add_chunk(chunk_id, data, target.stats, overwrite=overwrite, wait=False) + chunk_entry = self.cache.add_chunk(chunk_id, {}, data, stats=target.stats, overwrite=overwrite, wait=False) self.cache.repository.async_response(wait=False) self.seen_chunks.add(chunk_entry.id) return chunk_entry diff --git a/src/borg/archiver/transfer_cmd.py b/src/borg/archiver/transfer_cmd.py index c6c6a8776..e8de9e1f2 100644 --- a/src/borg/archiver/transfer_cmd.py +++ b/src/borg/archiver/transfer_cmd.py @@ -70,9 +70,10 @@ class TransferMixIn: cdata = other_repository.get(chunk_id) # keep compressed payload same, avoid decompression / recompression meta, data = other_manifest.repo_objs.parse(chunk_id, cdata, decompress=False) - data = upgrader.upgrade_compressed_chunk(chunk=data) + meta, data = upgrader.upgrade_compressed_chunk(meta, data) + chunk_entry = cache.add_chunk( - chunk_id, data, archive.stats, wait=False, compress=False, size=size + chunk_id, meta, data, stats=archive.stats, wait=False, compress=False, size=size ) cache.repository.async_response(wait=False) chunks.append(chunk_entry) diff --git a/src/borg/cache.py b/src/borg/cache.py index 3c09ab037..37c67f4d9 100644 --- a/src/borg/cache.py +++ b/src/borg/cache.py @@ -943,18 +943,18 @@ class LocalCache(CacheStatsMixin): self.cache_config.ignored_features.update(repo_features - my_features) self.cache_config.mandatory_features.update(repo_features & my_features) - def add_chunk(self, id, chunk, stats, *, overwrite=False, wait=True, compress=True, size=None): + def add_chunk(self, id, meta, data, *, stats, overwrite=False, wait=True, compress=True, size=None): if not self.txn_active: self.begin_txn() if size is None and compress: - size = len(chunk) # chunk is still uncompressed + size = len(data) # data is still uncompressed refcount = self.seen_chunk(id, size) if refcount and not overwrite: return self.chunk_incref(id, stats) if size is None: raise ValueError("when giving compressed data for a new chunk, the uncompressed size must be given also") - data = self.repo_objs.format(id, {}, chunk, compress=compress, size=size) - self.repository.put(id, data, wait=wait) + cdata = self.repo_objs.format(id, meta, data, compress=compress, size=size) + self.repository.put(id, cdata, wait=wait) self.chunks.add(id, 1, size) stats.update(size, not refcount) return ChunkListEntry(id, size) @@ -1115,19 +1115,19 @@ Chunk index: {0.total_unique_chunks:20d} unknown""" def memorize_file(self, hashed_path, path_hash, st, ids): pass - def add_chunk(self, id, chunk, stats, *, overwrite=False, wait=True, compress=True, size=None): + def add_chunk(self, id, meta, data, *, stats, overwrite=False, wait=True, compress=True, size=None): assert not overwrite, "AdHocCache does not permit overwrites — trying to use it for recreate?" if not self._txn_active: self.begin_txn() if size is None and compress: - size = len(chunk) # chunk is still uncompressed + size = len(data) # data is still uncompressed if size is None: raise ValueError("when giving compressed data for a chunk, the uncompressed size must be given also") refcount = self.seen_chunk(id, size) if refcount: return self.chunk_incref(id, stats, size=size) - data = self.repo_objs.format(id, {}, chunk, compress=compress) - self.repository.put(id, data, wait=wait) + cdata = self.repo_objs.format(id, meta, data, compress=compress) + self.repository.put(id, cdata, wait=wait) self.chunks.add(id, 1, size) stats.update(size, not refcount) return ChunkListEntry(id, size) diff --git a/src/borg/repoobj.py b/src/borg/repoobj.py index 3f49c11c1..f133e52f6 100644 --- a/src/borg/repoobj.py +++ b/src/borg/repoobj.py @@ -92,10 +92,13 @@ class RepoObj: if decompress: ctype = meta["ctype"] clevel = meta["clevel"] + csize = meta["csize"] # for obfuscation purposes, data_compressed may be longer than csize compr_hdr = bytes((ctype, clevel)) compressor_cls, compression_level = Compressor.detect(compr_hdr) compressor = compressor_cls(level=compression_level) - data = compressor.decompress(compr_hdr + data_compressed) # TODO: decompressor still needs type/level bytes + data = compressor.decompress( + compr_hdr + data_compressed[:csize] + ) # TODO: decompressor still needs type/level bytes self.key.assert_id(id, data) else: data = data_compressed # does not include the type/level bytes diff --git a/src/borg/testsuite/archive.py b/src/borg/testsuite/archive.py index 8ddfbb522..0a98e386b 100644 --- a/src/borg/testsuite/archive.py +++ b/src/borg/testsuite/archive.py @@ -101,9 +101,9 @@ class MockCache: self.objects = {} self.repository = self.MockRepo() - def add_chunk(self, id, chunk, stats=None, wait=True): - self.objects[id] = chunk - return id, len(chunk) + def add_chunk(self, id, meta, data, stats=None, wait=True): + self.objects[id] = data + return id, len(data) class ArchiveTimestampTestCase(BaseTestCase): diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py index a188f075b..6c2080b18 100644 --- a/src/borg/testsuite/archiver.py +++ b/src/borg/testsuite/archiver.py @@ -4014,7 +4014,8 @@ class ArchiverCheckTestCase(ArchiverTestCaseBase): for item in archive.iter_items(): if item.path.endswith("testsuite/archiver.py"): chunk = item.chunks[-1] - data = repository.get(chunk.id) + b"1234" + data = repository.get(chunk.id) + data = data[0:100] + b"x" + data[101:] repository.put(chunk.id, data) break repository.commit(compact=False) diff --git a/src/borg/testsuite/cache.py b/src/borg/testsuite/cache.py index ac34c9c4c..3699bf3a7 100644 --- a/src/borg/testsuite/cache.py +++ b/src/borg/testsuite/cache.py @@ -187,14 +187,14 @@ class TestAdHocCache: def test_does_not_overwrite(self, cache): with pytest.raises(AssertionError): - cache.add_chunk(H(1), b"5678", Statistics(), overwrite=True) + cache.add_chunk(H(1), {}, b"5678", stats=Statistics(), overwrite=True) def test_seen_chunk_add_chunk_size(self, cache): - assert cache.add_chunk(H(1), b"5678", Statistics()) == (H(1), 4) + assert cache.add_chunk(H(1), {}, b"5678", stats=Statistics()) == (H(1), 4) def test_deletes_chunks_during_lifetime(self, cache, repository): """E.g. checkpoint archives""" - cache.add_chunk(H(5), b"1010", Statistics()) + cache.add_chunk(H(5), {}, b"1010", stats=Statistics()) assert cache.seen_chunk(H(5)) == 1 cache.chunk_decref(H(5), Statistics()) assert not cache.seen_chunk(H(5)) @@ -216,10 +216,10 @@ class TestAdHocCache: assert not hasattr(cache, "chunks") def test_incref_after_add_chunk(self, cache): - assert cache.add_chunk(H(3), b"5678", Statistics()) == (H(3), 4) + assert cache.add_chunk(H(3), {}, b"5678", stats=Statistics()) == (H(3), 4) assert cache.chunk_incref(H(3), Statistics()) == (H(3), 4) def test_existing_incref_after_add_chunk(self, cache): """This case occurs with part files, see Archive.chunk_file.""" - assert cache.add_chunk(H(1), b"5678", Statistics()) == (H(1), 4) + assert cache.add_chunk(H(1), {}, b"5678", stats=Statistics()) == (H(1), 4) assert cache.chunk_incref(H(1), Statistics()) == (H(1), 4) diff --git a/src/borg/upgrade.py b/src/borg/upgrade.py index 213fced59..223066879 100644 --- a/src/borg/upgrade.py +++ b/src/borg/upgrade.py @@ -19,8 +19,8 @@ class UpgraderNoOp: def upgrade_item(self, *, item): return item - def upgrade_compressed_chunk(self, *, chunk): - return chunk + def upgrade_compressed_chunk(self, *, meta, data): + return meta, data def upgrade_archive_metadata(self, *, metadata): new_metadata = {} @@ -98,33 +98,36 @@ class UpgraderFrom12To20: assert all(key in new_item for key in REQUIRED_ITEM_KEYS) return new_item - def upgrade_compressed_chunk(self, *, chunk): - def upgrade_zlib_and_level(chunk): - if ZLIB_legacy.detect(chunk): - ctype = ZLIB.ID - chunk = ctype + level + bytes(chunk) # get rid of the legacy: prepend separate type/level bytes + def upgrade_compressed_chunk(self, *, meta, data): + # meta/data was parsed via RepoObj1.parse, which returns data **including** the ctype/clevel bytes prefixed + def upgrade_zlib_and_level(meta, data): + if ZLIB_legacy.detect(data): + ctype = ZLIB.ID[0] + data = bytes(data) # ZLIB_legacy has no ctype/clevel prefix else: - ctype = bytes(chunk[0:1]) - chunk = ctype + level + bytes(chunk[2:]) # keep type same, but set level - return chunk + ctype = data[0] + data = bytes(data[2:]) # strip ctype/clevel bytes + meta["ctype"] = ctype + meta["clevel"] = level + meta["csize"] = len(data) # we may have stripped some prefixed ctype/clevel bytes + return meta, data - ctype = chunk[0:1] - level = b"\xFF" # FF means unknown compression level + ctype = data[0] + level = 0xFF # means unknown compression level if ctype == ObfuscateSize.ID: # in older borg, we used unusual byte order - old_header_fmt = Struct(">I") - new_header_fmt = ObfuscateSize.header_fmt - length = ObfuscateSize.header_len - size_bytes = chunk[2 : 2 + length] - size = old_header_fmt.unpack(size_bytes) - size_bytes = new_header_fmt.pack(size) - compressed = chunk[2 + length :] - compressed = upgrade_zlib_and_level(compressed) - chunk = ctype + level + size_bytes + compressed + borg1_header_fmt = Struct(">I") + hlen = borg1_header_fmt.size + csize_bytes = data[2 : 2 + hlen] + csize = borg1_header_fmt.unpack(csize_bytes) + compressed = data[2 + hlen : 2 + hlen + csize] + meta, compressed = upgrade_zlib_and_level(meta, compressed) + osize = len(data) - 2 - hlen - csize # amount of 0x00 bytes appended for obfuscation + data = compressed + bytes(osize) else: - chunk = upgrade_zlib_and_level(chunk) - return chunk + meta, data = upgrade_zlib_and_level(meta, data) + return meta, data def upgrade_archive_metadata(self, *, metadata): new_metadata = {} From 4c9ed2a6c6e4b05e758fc7c0bb52019630427135 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Wed, 7 Sep 2022 14:28:54 +0200 Subject: [PATCH 05/14] refactor compressors to new api legacy: add/remove ctype/clevel bytes prefix of compressed data new: use a separate metadata dict compressors: use an int as ID, not a len 1 bytestring --- src/borg/archive.py | 2 +- src/borg/compress.pyx | 258 +++++++++++++++++++-------------- src/borg/remote.py | 14 +- src/borg/repoobj.py | 42 +++--- src/borg/testsuite/compress.py | 141 ++++++++++-------- src/borg/testsuite/repoobj.py | 8 +- src/borg/upgrade.py | 2 +- 7 files changed, 267 insertions(+), 200 deletions(-) diff --git a/src/borg/archive.py b/src/borg/archive.py index 53aae4497..ef41df413 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -2273,7 +2273,7 @@ class ArchiveRecreater: compr_hdr = bytes((old_meta["ctype"], old_meta["clevel"])) compressor_cls, level = Compressor.detect(compr_hdr) if ( - compressor_cls.name == self.repo_objs.compressor.decide(data).name + compressor_cls.name == self.repo_objs.compressor.decide({}, data).name and level == self.repo_objs.compressor.level ): # Stored chunk has the same compression method and level as we wanted diff --git a/src/borg/compress.pyx b/src/borg/compress.pyx index 824b11402..ea2ad0f63 100644 --- a/src/borg/compress.pyx +++ b/src/borg/compress.pyx @@ -56,22 +56,18 @@ cdef class CompressorBase: also handles compression format auto detection and adding/stripping the ID header (which enable auto detection). """ - ID = b'\xFF' # reserved and not used - # overwrite with a unique 1-byte bytestring in child classes + ID = 0xFF # reserved and not used + # overwrite with a unique 1-byte bytestring in child classes name = 'baseclass' @classmethod def detect(cls, data): - return data.startswith(cls.ID) + return data and data[0] == cls.ID - def __init__(self, level=255, **kwargs): + def __init__(self, level=255, legacy_mode=False, **kwargs): assert 0 <= level <= 255 self.level = level - if self.ID is not None: - self.id_level = self.ID + bytes((level, )) # level 255 means "unknown level" - assert len(self.id_level) == 2 - else: - self.id_level = None + self.legacy_mode = legacy_mode # True: support prefixed ctype/clevel bytes def decide(self, data): """ @@ -86,24 +82,48 @@ cdef class CompressorBase: """ return self - def compress(self, data): + def compress(self, meta, data): """ - Compress *data* (bytes) and return bytes result. Prepend the ID bytes of this compressor, - which is needed so that the correct decompressor can be used for decompression. + Compress *data* (bytes) and return compression metadata and compressed bytes. """ - # add id_level bytes - return self.id_level + data + if self.legacy_mode: + return None, bytes((self.ID, self.level)) + data + else: + meta["ctype"] = self.ID + meta["clevel"] = self.level + meta["csize"] = len(data) + return meta, data - def decompress(self, data): + def decompress(self, meta, data): """ Decompress *data* (preferably a memoryview, bytes also acceptable) and return bytes result. - The leading Compressor ID bytes need to be present. + + Legacy mode: The leading Compressor ID bytes need to be present. Only handles input generated by _this_ Compressor - for a general purpose decompression method see *Compressor.decompress*. """ - # strip id_level bytes - return data[2:] + if self.legacy_mode: + assert meta is None + meta = {} + meta["ctype"] = data[0] + meta["clevel"] = data[1] + meta["csize"] = len(data) + return meta, data[2:] + else: + assert isinstance(meta, dict) + assert "ctype" in meta + assert "clevel" in meta + return meta, data + + def check_fix_size(self, meta, data): + if "size" in meta: + assert meta["size"] == len(data) + elif self.legacy_mode: + meta["size"] = len(data) + else: + pass # raise ValueError("size not present and not in legacy mode") + cdef class DecidingCompressor(CompressorBase): """ @@ -112,12 +132,12 @@ cdef class DecidingCompressor(CompressorBase): """ name = 'decidebaseclass' - def __init__(self, level=255, **kwargs): - super().__init__(level=level, **kwargs) + def __init__(self, level=255, legacy_mode=False, **kwargs): + super().__init__(level=level, legacy_mode=legacy_mode, **kwargs) - def _decide(self, data): + def _decide(self, meta, data): """ - Decides what to do with *data*. Returns (compressor, compressed_data). + Decides what to do with *data*. Returns (compressor, meta, compressed_data). *compressed_data* can be the result of *data* being processed by *compressor*, if that is generated as a side-effect of the decision process, or None otherwise. @@ -127,47 +147,50 @@ cdef class DecidingCompressor(CompressorBase): """ raise NotImplementedError - def decide(self, data): - return self._decide(data)[0] + def decide(self, meta, data): + return self._decide(meta, data)[0] - def decide_compress(self, data): + def decide_compress(self, meta, data): """ Decides what to do with *data* and handle accordingly. Returns (compressor, compressed_data). *compressed_data* is the result of *data* being processed by *compressor*. """ - compressor, compressed_data = self._decide(data) + compressor, (meta, compressed_data) = self._decide(meta, data) if compressed_data is None: - compressed_data = compressor.compress(data) + meta, compressed_data = compressor.compress(meta, data) if compressor is self: # call super class to add ID bytes - return self, super().compress(compressed_data) + return self, super().compress(meta, compressed_data) - return compressor, compressed_data + return compressor, (meta, compressed_data) - def compress(self, data): - return self.decide_compress(data)[1] + def compress(self, meta, data): + meta["size"] = len(data) + return self.decide_compress(meta, data)[1] class CNONE(CompressorBase): """ none - no compression, just pass through data """ - ID = b'\x00' + ID = 0x00 name = 'none' - def __init__(self, level=255, **kwargs): - super().__init__(level=level, **kwargs) # no defined levels for CNONE, so just say "unknown" + def __init__(self, level=255, legacy_mode=False, **kwargs): + super().__init__(level=level, legacy_mode=legacy_mode, **kwargs) # no defined levels for CNONE, so just say "unknown" - def compress(self, data): - return super().compress(data) + def compress(self, meta, data): + meta["size"] = len(data) + return super().compress(meta, data) - def decompress(self, data): - data = super().decompress(data) + def decompress(self, meta, data): + meta, data = super().decompress(meta, data) if not isinstance(data, bytes): data = bytes(data) - return data + self.check_fix_size(meta, data) + return meta, data class LZ4(DecidingCompressor): @@ -179,13 +202,13 @@ class LZ4(DecidingCompressor): - wrapper releases CPython's GIL to support multithreaded code - uses safe lz4 methods that never go beyond the end of the output buffer """ - ID = b'\x01' + ID = 0x01 name = 'lz4' - def __init__(self, level=255, **kwargs): - super().__init__(level=level, **kwargs) # no defined levels for LZ4, so just say "unknown" + def __init__(self, level=255, legacy_mode=False, **kwargs): + super().__init__(level=level, legacy_mode=legacy_mode, **kwargs) # no defined levels for LZ4, so just say "unknown" - def _decide(self, idata): + def _decide(self, meta, idata): """ Decides what to do with *data*. Returns (compressor, lz4_data). @@ -206,12 +229,12 @@ class LZ4(DecidingCompressor): raise Exception('lz4 compress failed') # only compress if the result actually is smaller if osize < isize: - return self, dest[:osize] + return self, (meta, dest[:osize]) else: - return NONE_COMPRESSOR, None + return NONE_COMPRESSOR, (meta, None) - def decompress(self, idata): - idata = super().decompress(idata) + def decompress(self, meta, data): + meta, idata = super().decompress(meta, data) if not isinstance(idata, bytes): idata = bytes(idata) # code below does not work with memoryview cdef int isize = len(idata) @@ -237,23 +260,25 @@ class LZ4(DecidingCompressor): raise DecompressionError('lz4 decompress failed') # likely the buffer was too small, get a bigger one: osize = int(1.5 * osize) - return dest[:rsize] + data = dest[:rsize] + self.check_fix_size(meta, data) + return meta, data class LZMA(DecidingCompressor): """ lzma compression / decompression """ - ID = b'\x02' + ID = 0x02 name = 'lzma' - def __init__(self, level=6, **kwargs): - super().__init__(level=level, **kwargs) + def __init__(self, level=6, legacy_mode=False, **kwargs): + super().__init__(level=level, legacy_mode=legacy_mode, **kwargs) self.level = level if lzma is None: raise ValueError('No lzma support found.') - def _decide(self, data): + def _decide(self, meta, data): """ Decides what to do with *data*. Returns (compressor, lzma_data). @@ -262,14 +287,16 @@ class LZMA(DecidingCompressor): # we do not need integrity checks in lzma, we do that already lzma_data = lzma.compress(data, preset=self.level, check=lzma.CHECK_NONE) if len(lzma_data) < len(data): - return self, lzma_data + return self, (meta, lzma_data) else: - return NONE_COMPRESSOR, None + return NONE_COMPRESSOR, (meta, None) - def decompress(self, data): - data = super().decompress(data) + def decompress(self, meta, data): + meta, data = super().decompress(meta, data) try: - return lzma.decompress(data) + data = lzma.decompress(data) + self.check_fix_size(meta, data) + return meta, data except lzma.LZMAError as e: raise DecompressionError(str(e)) from None @@ -279,14 +306,14 @@ class ZSTD(DecidingCompressor): # This is a NOT THREAD SAFE implementation. # Only ONE python context must be created at a time. # It should work flawlessly as long as borg will call ONLY ONE compression job at time. - ID = b'\x03' + ID = 0x03 name = 'zstd' - def __init__(self, level=3, **kwargs): - super().__init__(level=level, **kwargs) + def __init__(self, level=3, legacy_mode=False, **kwargs): + super().__init__(level=level, legacy_mode=legacy_mode, **kwargs) self.level = level - def _decide(self, idata): + def _decide(self, meta, idata): """ Decides what to do with *data*. Returns (compressor, zstd_data). @@ -308,12 +335,12 @@ class ZSTD(DecidingCompressor): raise Exception('zstd compress failed: %s' % ZSTD_getErrorName(osize)) # only compress if the result actually is smaller if osize < isize: - return self, dest[:osize] + return self, (meta, dest[:osize]) else: - return NONE_COMPRESSOR, None + return NONE_COMPRESSOR, (meta, None) - def decompress(self, idata): - idata = super().decompress(idata) + def decompress(self, meta, data): + meta, idata = super().decompress(meta, data) if not isinstance(idata, bytes): idata = bytes(idata) # code below does not work with memoryview cdef int isize = len(idata) @@ -337,21 +364,23 @@ class ZSTD(DecidingCompressor): raise DecompressionError('zstd decompress failed: %s' % ZSTD_getErrorName(rsize)) if rsize != osize: raise DecompressionError('zstd decompress failed: size mismatch') - return dest[:osize] + data = dest[:osize] + self.check_fix_size(meta, data) + return meta, data class ZLIB(DecidingCompressor): """ zlib compression / decompression (python stdlib) """ - ID = b'\x05' + ID = 0x05 name = 'zlib' - def __init__(self, level=6, **kwargs): - super().__init__(level=level, **kwargs) + def __init__(self, level=6, legacy_mode=False, **kwargs): + super().__init__(level=level, legacy_mode=legacy_mode, **kwargs) self.level = level - def _decide(self, data): + def _decide(self, meta, data): """ Decides what to do with *data*. Returns (compressor, zlib_data). @@ -359,14 +388,16 @@ class ZLIB(DecidingCompressor): """ zlib_data = zlib.compress(data, self.level) if len(zlib_data) < len(data): - return self, zlib_data + return self, (meta, zlib_data) else: - return NONE_COMPRESSOR, None + return NONE_COMPRESSOR, (meta, None) - def decompress(self, data): - data = super().decompress(data) + def decompress(self, meta, data): + meta, data = super().decompress(meta, data) try: - return zlib.decompress(data) + data = zlib.decompress(data) + self.check_fix_size(meta, data) + return meta, data except zlib.error as e: raise DecompressionError(str(e)) from None @@ -382,7 +413,7 @@ class ZLIB_legacy(CompressorBase): Newer borg uses the ZLIB class that has separate ID bytes (as all the other compressors) and does not need this hack. """ - ID = b'\x08' # not used here, see detect() + ID = 0x08 # not used here, see detect() # avoid all 0x.8 IDs elsewhere! name = 'zlib_legacy' @@ -398,14 +429,14 @@ class ZLIB_legacy(CompressorBase): super().__init__(level=level, **kwargs) self.level = level - def compress(self, data): + def compress(self, meta, data): # note: for compatibility no super call, do not add ID bytes - return zlib.compress(data, self.level) + return None, zlib.compress(data, self.level) - def decompress(self, data): + def decompress(self, meta, data): # note: for compatibility no super call, do not strip ID bytes try: - return zlib.decompress(data) + return meta, zlib.decompress(data) except zlib.error as e: raise DecompressionError(str(e)) from None @@ -425,7 +456,7 @@ class Auto(CompressorBase): super().__init__() self.compressor = compressor - def _decide(self, data): + def _decide(self, meta, data): """ Decides what to do with *data*. Returns (compressor, compressed_data). @@ -448,33 +479,33 @@ class Auto(CompressorBase): Note: While it makes no sense, the expensive compressor may well be set to the LZ4 compressor. """ - compressor, compressed_data = LZ4_COMPRESSOR.decide_compress(data) + compressor, (meta, compressed_data) = LZ4_COMPRESSOR.decide_compress(meta, data) # compressed_data includes the compression type header, while data does not yet ratio = len(compressed_data) / (len(data) + 2) if ratio < 0.97: - return self.compressor, compressed_data + return self.compressor, (meta, compressed_data) else: - return compressor, compressed_data + return compressor, (meta, compressed_data) - def decide(self, data): - return self._decide(data)[0] + def decide(self, meta, data): + return self._decide(meta, data)[0] - def compress(self, data): - compressor, cheap_compressed_data = self._decide(data) + def compress(self, meta, data): + compressor, (cheap_meta, cheap_compressed_data) = self._decide(dict(meta), data) if compressor in (LZ4_COMPRESSOR, NONE_COMPRESSOR): # we know that trying to compress with expensive compressor is likely pointless, # so we fallback to return the cheap compressed data. - return cheap_compressed_data + return cheap_meta, cheap_compressed_data # if we get here, the decider decided to try the expensive compressor. # we also know that the compressed data returned by the decider is lz4 compressed. - expensive_compressed_data = compressor.compress(data) + expensive_meta, expensive_compressed_data = compressor.compress(dict(meta), data) ratio = len(expensive_compressed_data) / len(cheap_compressed_data) if ratio < 0.99: # the expensive compressor managed to squeeze the data significantly better than lz4. - return expensive_compressed_data + return expensive_meta, expensive_compressed_data else: # otherwise let's just store the lz4 data, which decompresses extremely fast. - return cheap_compressed_data + return cheap_meta, cheap_compressed_data def decompress(self, data): raise NotImplementedError @@ -487,14 +518,14 @@ class ObfuscateSize(CompressorBase): """ Meta-Compressor that obfuscates the compressed data size. """ - ID = b'\x04' + ID = 0x04 name = 'obfuscate' header_fmt = Struct(' bytes: return self.key.id_hash(data) @@ -126,7 +123,7 @@ class RepoObj1: # legacy assert compress or size is not None if compress: assert size is None - data_compressed = self.compressor.compress(data) # TODO: compressor also adds compressor type/level bytes + meta, data_compressed = self.compressor.compress(meta, data) else: assert isinstance(size, int) data_compressed = data # is already compressed, must include type/level bytes @@ -136,17 +133,16 @@ class RepoObj1: # legacy def parse(self, id: bytes, cdata: bytes, decompress: bool = True) -> tuple[dict, bytes]: assert isinstance(id, bytes) assert isinstance(cdata, bytes) - meta = {} data_compressed = self.key.decrypt(id, cdata) - meta["csize"] = len(data_compressed) compressor_cls, compression_level = Compressor.detect(data_compressed[:2]) - compressor = compressor_cls(level=compression_level) - meta["ctype"] = compressor.ID[0] - meta["clevel"] = compressor.level + compressor = compressor_cls(level=compression_level, legacy_mode=True) if decompress: - data = compressor.decompress(data_compressed) # TODO: decompressor still needs type/level bytes + meta, data = compressor.decompress(None, data_compressed) self.key.assert_id(id, data) - meta["size"] = len(data) else: + meta = {} + meta["ctype"] = compressor.ID + meta["clevel"] = compressor.level data = data_compressed + meta["csize"] = len(data_compressed) return meta, data diff --git a/src/borg/testsuite/compress.py b/src/borg/testsuite/compress.py index ad383ab0f..cfb16a85a 100644 --- a/src/borg/testsuite/compress.py +++ b/src/borg/testsuite/compress.py @@ -29,19 +29,19 @@ def test_get_compressor(): def test_cnull(): c = get_compressor(name="none") - cdata = c.compress(data) - assert len(cdata) > len(data) + meta, cdata = c.compress({}, data) + assert len(cdata) >= len(data) assert data in cdata # it's not compressed and just in there 1:1 - assert data == c.decompress(cdata) - assert data == Compressor(**params).decompress(cdata) # autodetect + assert data == c.decompress(meta, cdata)[1] + assert data == Compressor(**params).decompress(meta, cdata)[1] # autodetect def test_lz4(): c = get_compressor(name="lz4") - cdata = c.compress(data) + meta, cdata = c.compress({}, data) assert len(cdata) < len(data) - assert data == c.decompress(cdata) - assert data == Compressor(**params).decompress(cdata) # autodetect + assert data == c.decompress(meta, cdata)[1] + assert data == Compressor(**params).decompress(meta, cdata)[1] # autodetect def test_lz4_buffer_allocation(monkeypatch): @@ -51,56 +51,56 @@ def test_lz4_buffer_allocation(monkeypatch): data = os.urandom(5 * 2**20) * 10 # 50MiB badly compressible data assert len(data) == 50 * 2**20 c = Compressor("lz4") - cdata = c.compress(data) - assert len(cdata) > len(data) - assert data == c.decompress(cdata) + meta, cdata = c.compress({}, data) + assert len(cdata) >= len(data) + assert data == c.decompress(meta, cdata)[1] def test_zlib(): c = get_compressor(name="zlib") - cdata = c.compress(data) + meta, cdata = c.compress({}, data) assert len(cdata) < len(data) - assert data == c.decompress(cdata) - assert data == Compressor(**params).decompress(cdata) # autodetect + assert data == c.decompress(meta, cdata)[1] + assert data == Compressor(**params).decompress(meta, cdata)[1] # autodetect def test_lzma(): if lzma is None: pytest.skip("No lzma support found.") c = get_compressor(name="lzma") - cdata = c.compress(data) + meta, cdata = c.compress({}, data) assert len(cdata) < len(data) - assert data == c.decompress(cdata) - assert data == Compressor(**params).decompress(cdata) # autodetect + assert data == c.decompress(meta, cdata)[1] + assert data == Compressor(**params).decompress(meta, cdata)[1] # autodetect def test_zstd(): c = get_compressor(name="zstd") - cdata = c.compress(data) + meta, cdata = c.compress({}, data) assert len(cdata) < len(data) - assert data == c.decompress(cdata) - assert data == Compressor(**params).decompress(cdata) # autodetect + assert data == c.decompress(meta, cdata)[1] + assert data == Compressor(**params).decompress(meta, cdata)[1] # autodetect def test_autodetect_invalid(): with pytest.raises(ValueError): - Compressor(**params).decompress(b"\xff\xfftotalcrap") + Compressor(**params, legacy_mode=True).decompress({}, b"\xff\xfftotalcrap") with pytest.raises(ValueError): - Compressor(**params).decompress(b"\x08\x00notreallyzlib") + Compressor(**params, legacy_mode=True).decompress({}, b"\x08\x00notreallyzlib") def test_zlib_legacy_compat(): # for compatibility reasons, we do not add an extra header for zlib, # nor do we expect one when decompressing / autodetecting for level in range(10): - c = get_compressor(name="zlib_legacy", level=level) - cdata1 = c.compress(data) + c = get_compressor(name="zlib_legacy", level=level, legacy_mode=True) + meta1, cdata1 = c.compress({}, data) cdata2 = zlib.compress(data, level) assert cdata1 == cdata2 - data2 = c.decompress(cdata2) - assert data == data2 - data2 = Compressor(**params).decompress(cdata2) + meta2, data2 = c.decompress({}, cdata2) assert data == data2 + # _, data2 = Compressor(**params).decompress({}, cdata2) + # assert data == data2 def test_compressor(): @@ -122,7 +122,17 @@ def test_compressor(): ] for params in params_list: c = Compressor(**params) - assert data == c.decompress(c.compress(data)) + meta_c, data_compressed = c.compress({}, data) + assert "ctype" in meta_c + assert "clevel" in meta_c + assert meta_c["csize"] == len(data_compressed) + assert meta_c["size"] == len(data) + meta_d, data_decompressed = c.decompress(meta_c, data_compressed) + assert data == data_decompressed + assert "ctype" in meta_d + assert "clevel" in meta_d + assert meta_d["csize"] == len(data_compressed) + assert meta_d["size"] == len(data) def test_auto(): @@ -130,72 +140,89 @@ def test_auto(): compressor_lz4 = CompressionSpec("lz4").compressor compressor_zlib = CompressionSpec("zlib,9").compressor data = bytes(500) - compressed_auto_zlib = compressor_auto_zlib.compress(data) - compressed_lz4 = compressor_lz4.compress(data) - compressed_zlib = compressor_zlib.compress(data) + meta, compressed_auto_zlib = compressor_auto_zlib.compress({}, data) + _, compressed_lz4 = compressor_lz4.compress({}, data) + _, compressed_zlib = compressor_zlib.compress({}, data) ratio = len(compressed_zlib) / len(compressed_lz4) - assert Compressor.detect(compressed_auto_zlib)[0] == ZLIB if ratio < 0.99 else LZ4 + assert meta["ctype"] == ZLIB.ID if ratio < 0.99 else LZ4.ID + assert meta["clevel"] == 9 if ratio < 0.99 else 255 + assert meta["csize"] == len(compressed_auto_zlib) data = b"\x00\xb8\xa3\xa2-O\xe1i\xb6\x12\x03\xc21\xf3\x8a\xf78\\\x01\xa5b\x07\x95\xbeE\xf8\xa3\x9ahm\xb1~" - compressed = compressor_auto_zlib.compress(data) - assert Compressor.detect(compressed)[0] == CNONE + meta, compressed = compressor_auto_zlib.compress(dict(meta), data) + assert meta["ctype"] == CNONE.ID + assert meta["clevel"] == 255 + assert meta["csize"] == len(compressed) def test_obfuscate(): compressor = CompressionSpec("obfuscate,1,none").compressor data = bytes(10000) - compressed = compressor.compress(data) - # 2 id bytes compression, 2 id bytes obfuscator. 4 length bytes - assert len(data) + 8 <= len(compressed) <= len(data) * 101 + 8 + _, compressed = compressor.compress({}, data) + assert len(data) <= len(compressed) <= len(data) * 101 # compressing 100 times the same data should give at least 50 different result sizes - assert len({len(compressor.compress(data)) for i in range(100)}) > 50 + assert len({len(compressor.compress({}, data)[1]) for i in range(100)}) > 50 cs = CompressionSpec("obfuscate,2,lz4") assert isinstance(cs.inner.compressor, LZ4) compressor = cs.compressor data = bytes(10000) - compressed = compressor.compress(data) - # 2 id bytes compression, 2 id bytes obfuscator. 4 length bytes + _, compressed = compressor.compress({}, data) min_compress, max_compress = 0.2, 0.001 # estimate compression factor outer boundaries - assert max_compress * len(data) + 8 <= len(compressed) <= min_compress * len(data) * 1001 + 8 + assert max_compress * len(data) <= len(compressed) <= min_compress * len(data) * 1001 # compressing 100 times the same data should give multiple different result sizes - assert len({len(compressor.compress(data)) for i in range(100)}) > 10 + assert len({len(compressor.compress({}, data)[1]) for i in range(100)}) > 10 cs = CompressionSpec("obfuscate,6,zstd,3") assert isinstance(cs.inner.compressor, ZSTD) compressor = cs.compressor data = bytes(10000) - compressed = compressor.compress(data) - # 2 id bytes compression, 2 id bytes obfuscator. 4 length bytes + _, compressed = compressor.compress({}, data) min_compress, max_compress = 0.2, 0.001 # estimate compression factor outer boundaries - assert max_compress * len(data) + 8 <= len(compressed) <= min_compress * len(data) * 10000001 + 8 + assert max_compress * len(data) <= len(compressed) <= min_compress * len(data) * 10000001 # compressing 100 times the same data should give multiple different result sizes - assert len({len(compressor.compress(data)) for i in range(100)}) > 90 + assert len({len(compressor.compress({}, data)[1]) for i in range(100)}) > 90 cs = CompressionSpec("obfuscate,2,auto,zstd,10") assert isinstance(cs.inner.compressor, Auto) compressor = cs.compressor data = bytes(10000) - compressed = compressor.compress(data) - # 2 id bytes compression, 2 id bytes obfuscator. 4 length bytes + _, compressed = compressor.compress({}, data) min_compress, max_compress = 0.2, 0.001 # estimate compression factor outer boundaries - assert max_compress * len(data) + 8 <= len(compressed) <= min_compress * len(data) * 1001 + 8 + assert max_compress * len(data) <= len(compressed) <= min_compress * len(data) * 1001 # compressing 100 times the same data should give multiple different result sizes - assert len({len(compressor.compress(data)) for i in range(100)}) > 10 + assert len({len(compressor.compress({}, data)[1]) for i in range(100)}) > 10 cs = CompressionSpec("obfuscate,110,none") assert isinstance(cs.inner.compressor, CNONE) compressor = cs.compressor data = bytes(1000) - compressed = compressor.compress(data) - # N blocks + 2 id bytes obfuscator. 4 length bytes - # The 'none' compressor also adds 2 id bytes - assert 6 + 2 + 1000 <= len(compressed) <= 6 + 2 + 1000 + 1024 + _, compressed = compressor.compress({}, data) + assert 1000 <= len(compressed) <= 1000 + 1024 data = bytes(1100) - compressed = compressor.compress(data) - # N blocks + 2 id bytes obfuscator. 4 length bytes - # The 'none' compressor also adds 2 id bytes - assert 6 + 2 + 1100 <= len(compressed) <= 6 + 2 + 1100 + 1024 + _, compressed = compressor.compress({}, data) + assert 1100 <= len(compressed) <= 1100 + 1024 + + +def test_obfuscate_meta(): + compressor = CompressionSpec("obfuscate,3,lz4").compressor + meta_in = {} + data = bytes(10000) + meta_out, compressed = compressor.compress(meta_in, data) + assert "ctype" not in meta_in # do not modify dict of caller + assert "ctype" in meta_out + assert meta_out["ctype"] == LZ4.ID + assert "clevel" in meta_out + assert meta_out["clevel"] == 0xFF + assert "csize" in meta_out + csize = meta_out["csize"] + assert csize == len(compressed) # this is the overall size + assert "psize" in meta_out + psize = meta_out["psize"] + assert 0 < psize < 100 + assert csize - psize >= 0 # there is a obfuscation trailer + trailer = compressed[psize:] + assert not trailer or set(trailer) == {0} # trailer is all-zero-bytes def test_compression_specs(): diff --git a/src/borg/testsuite/repoobj.py b/src/borg/testsuite/repoobj.py index b7b452bac..b67b56e7c 100644 --- a/src/borg/testsuite/repoobj.py +++ b/src/borg/testsuite/repoobj.py @@ -56,7 +56,7 @@ def test_format_parse_roundtrip_borg1(key): # legacy edata = repo_objs.extract_crypted_data(cdata) compressor = repo_objs.compressor key = repo_objs.key - assert edata.startswith(bytes((key.TYPE, compressor.ID[0], compressor.level))) + assert edata.startswith(bytes((key.TYPE, compressor.ID, compressor.level))) def test_borg1_borg2_transition(key): @@ -70,7 +70,7 @@ def test_borg1_borg2_transition(key): borg1_cdata = repo_objs1.format(id, meta, data) meta1, compr_data1 = repo_objs1.parse(id, borg1_cdata, decompress=False) # borg transfer avoids (de)compression # in borg 1, we can only get this metadata after decrypting the whole chunk (and we do not have "size" here): - assert meta1["ctype"] == LZ4.ID[0] # default compression + assert meta1["ctype"] == LZ4.ID # default compression assert meta1["clevel"] == 0xFF # lz4 does not know levels (yet?) assert meta1["csize"] < len_data # lz4 should make it smaller @@ -82,14 +82,14 @@ def test_borg1_borg2_transition(key): ) meta2, data2 = repo_objs2.parse(id, borg2_cdata) assert data2 == data - assert meta2["ctype"] == LZ4.ID[0] + assert meta2["ctype"] == LZ4.ID assert meta2["clevel"] == 0xFF assert meta2["csize"] == meta1["csize"] - 2 # borg2 does not store the type/level bytes there assert meta2["size"] == len_data meta2 = repo_objs2.parse_meta(id, borg2_cdata) # now, in borg 2, we have nice and separately decrypted metadata (no need to decrypt the whole chunk): - assert meta2["ctype"] == LZ4.ID[0] + assert meta2["ctype"] == LZ4.ID assert meta2["clevel"] == 0xFF assert meta2["csize"] == meta1["csize"] - 2 # borg2 does not store the type/level bytes there assert meta2["size"] == len_data diff --git a/src/borg/upgrade.py b/src/borg/upgrade.py index 223066879..95d2b8e49 100644 --- a/src/borg/upgrade.py +++ b/src/borg/upgrade.py @@ -102,7 +102,7 @@ class UpgraderFrom12To20: # meta/data was parsed via RepoObj1.parse, which returns data **including** the ctype/clevel bytes prefixed def upgrade_zlib_and_level(meta, data): if ZLIB_legacy.detect(data): - ctype = ZLIB.ID[0] + ctype = ZLIB.ID data = bytes(data) # ZLIB_legacy has no ctype/clevel prefix else: ctype = data[0] From cf333cef91816d4fdab5540297726716457fe8d6 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Wed, 7 Sep 2022 20:24:50 +0200 Subject: [PATCH 06/14] upgrader fixes for new api --- src/borg/archiver/transfer_cmd.py | 11 +++++++++-- src/borg/cache.py | 6 ++++-- src/borg/upgrade.py | 4 ++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/borg/archiver/transfer_cmd.py b/src/borg/archiver/transfer_cmd.py index e8de9e1f2..663a9244a 100644 --- a/src/borg/archiver/transfer_cmd.py +++ b/src/borg/archiver/transfer_cmd.py @@ -71,9 +71,16 @@ class TransferMixIn: # keep compressed payload same, avoid decompression / recompression meta, data = other_manifest.repo_objs.parse(chunk_id, cdata, decompress=False) meta, data = upgrader.upgrade_compressed_chunk(meta, data) - chunk_entry = cache.add_chunk( - chunk_id, meta, data, stats=archive.stats, wait=False, compress=False, size=size + chunk_id, + meta, + data, + stats=archive.stats, + wait=False, + compress=False, + size=size, + ctype=meta["ctype"], + clevel=meta["clevel"], ) cache.repository.async_response(wait=False) chunks.append(chunk_entry) diff --git a/src/borg/cache.py b/src/borg/cache.py index 37c67f4d9..fc438d50a 100644 --- a/src/borg/cache.py +++ b/src/borg/cache.py @@ -943,7 +943,9 @@ class LocalCache(CacheStatsMixin): self.cache_config.ignored_features.update(repo_features - my_features) self.cache_config.mandatory_features.update(repo_features & my_features) - def add_chunk(self, id, meta, data, *, stats, overwrite=False, wait=True, compress=True, size=None): + def add_chunk( + self, id, meta, data, *, stats, overwrite=False, wait=True, compress=True, size=None, ctype=None, clevel=None + ): if not self.txn_active: self.begin_txn() if size is None and compress: @@ -953,7 +955,7 @@ class LocalCache(CacheStatsMixin): return self.chunk_incref(id, stats) if size is None: raise ValueError("when giving compressed data for a new chunk, the uncompressed size must be given also") - cdata = self.repo_objs.format(id, meta, data, compress=compress, size=size) + cdata = self.repo_objs.format(id, meta, data, compress=compress, size=size, ctype=ctype, clevel=clevel) self.repository.put(id, cdata, wait=wait) self.chunks.add(id, 1, size) stats.update(size, not refcount) diff --git a/src/borg/upgrade.py b/src/borg/upgrade.py index 95d2b8e49..6a9fd4460 100644 --- a/src/borg/upgrade.py +++ b/src/borg/upgrade.py @@ -19,7 +19,7 @@ class UpgraderNoOp: def upgrade_item(self, *, item): return item - def upgrade_compressed_chunk(self, *, meta, data): + def upgrade_compressed_chunk(self, meta, data): return meta, data def upgrade_archive_metadata(self, *, metadata): @@ -98,7 +98,7 @@ class UpgraderFrom12To20: assert all(key in new_item for key in REQUIRED_ITEM_KEYS) return new_item - def upgrade_compressed_chunk(self, *, meta, data): + def upgrade_compressed_chunk(self, meta, data): # meta/data was parsed via RepoObj1.parse, which returns data **including** the ctype/clevel bytes prefixed def upgrade_zlib_and_level(meta, data): if ZLIB_legacy.detect(data): From e827f98c4572edc3dc5025f508664894a57effdf Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Wed, 7 Sep 2022 21:10:54 +0200 Subject: [PATCH 07/14] transfer: add test bit hard to test the 1.2 -> 2.0 transfer, but we can at least test the 2.0 -> 2.0 "NoOp" transfer. --- src/borg/testsuite/archiver.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/borg/testsuite/archiver.py b/src/borg/testsuite/archiver.py index 6c2080b18..414d32e15 100644 --- a/src/borg/testsuite/archiver.py +++ b/src/borg/testsuite/archiver.py @@ -3765,6 +3765,32 @@ id: 2 / e29442 3506da 4e1ea7 / 25f62a 5a3d41 - 02 key = msgpack.unpackb(a2b_base64(repository.load_key())) assert key["algorithm"] == "argon2 chacha20-poly1305" + def test_transfer(self): + def check_repo(repo_option): + listing = self.cmd(repo_option, "rlist", "--short") + assert "arch1" in listing + assert "arch2" in listing + listing = self.cmd(repo_option, "list", "--short", "arch1") + assert "file1" in listing + assert "dir2/file2" in listing + self.cmd(repo_option, "check") + + self.create_test_files() + repo1 = f"--repo={self.repository_location}1" + repo2 = f"--repo={self.repository_location}2" + other_repo1 = f"--other-repo={self.repository_location}1" + + self.cmd(repo1, "rcreate", RK_ENCRYPTION) + self.cmd(repo1, "create", "arch1", "input") + self.cmd(repo1, "create", "arch2", "input") + check_repo(repo1) + + self.cmd(repo2, "rcreate", RK_ENCRYPTION, other_repo1) + self.cmd(repo2, "transfer", other_repo1, "--dry-run") + self.cmd(repo2, "transfer", other_repo1) + self.cmd(repo2, "transfer", other_repo1, "--dry-run") + check_repo(repo2) + @unittest.skipUnless("binary" in BORG_EXES, "no borg.exe available") class ArchiverTestCaseBinary(ArchiverTestCase): From b64427c48081f298d354d6b301f1d19eba485dd0 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 8 Sep 2022 12:38:55 +0200 Subject: [PATCH 08/14] simplify: iter_objects always returns (..., size, data) data might be None (if read_data is False). also removed the include_data argument, not needed any more. --- src/borg/repository.py | 29 +++++++++++++---------------- src/borg/testsuite/repository.py | 6 +++--- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/borg/repository.py b/src/borg/repository.py index 6ed1a2689..243db2a62 100644 --- a/src/borg/repository.py +++ b/src/borg/repository.py @@ -830,7 +830,7 @@ class Repository: freeable_ratio * 100.0, freeable_space, ) - for tag, key, offset, data in self.io.iter_objects(segment, include_data=True): + for tag, key, offset, _, data in self.io.iter_objects(segment): if tag == TAG_COMMIT: continue in_index = self.index.get(key) @@ -961,7 +961,7 @@ class Repository: def _update_index(self, segment, objects, report=None): """some code shared between replay_segments and check""" self.segments[segment] = 0 - for tag, key, offset, size in objects: + for tag, key, offset, size, _ in objects: if tag in (TAG_PUT2, TAG_PUT): try: # If this PUT supersedes an older PUT, mark the old segment for compaction and count the free space @@ -1011,7 +1011,7 @@ class Repository: return self.compact[segment] = 0 - for tag, key, offset, size in self.io.iter_objects(segment, read_data=False): + for tag, key, offset, size, _ in self.io.iter_objects(segment, read_data=False): if tag in (TAG_PUT2, TAG_PUT): in_index = self.index.get(key) if not in_index or (in_index.segment, in_index.offset) != (segment, offset): @@ -1165,8 +1165,8 @@ class Repository: if segment is not None and current_segment > segment: break try: - for tag, key, current_offset, data in self.io.iter_objects( - segment=current_segment, offset=offset or 0, include_data=True + for tag, key, current_offset, _, data in self.io.iter_objects( + segment=current_segment, offset=offset or 0 ): if offset is not None and current_offset > offset: break @@ -1229,10 +1229,10 @@ class Repository: start_segment, start_offset, _ = (0, 0, 0) if at_start else self.index[marker] result = [] for segment, filename in self.io.segment_iterator(start_segment): - obj_iterator = self.io.iter_objects(segment, start_offset, read_data=False, include_data=False) + obj_iterator = self.io.iter_objects(segment, start_offset, read_data=False) while True: try: - tag, id, offset, size = next(obj_iterator) + tag, id, offset, size, _ = next(obj_iterator) except (StopIteration, IntegrityError): # either end-of-segment or an error - we can not seek to objects at # higher offsets than one that has an error in the header fields. @@ -1458,7 +1458,7 @@ class LoggedIO: seen_commit = False while True: try: - tag, key, offset, _ = next(iterator) + tag, key, offset, _, _ = next(iterator) except IntegrityError: return False except StopIteration: @@ -1560,15 +1560,13 @@ class LoggedIO: fd.seek(0) return fd.read(MAGIC_LEN) - def iter_objects(self, segment, offset=0, include_data=False, read_data=True): + def iter_objects(self, segment, offset=0, read_data=True): """ Return object iterator for *segment*. - If read_data is False then include_data must be False as well. - See the _read() docstring about confidence in the returned data. - The iterator returns four-tuples of (tag, key, offset, data|size). + The iterator returns five-tuples of (tag, key, offset, size, data). """ fd = self.get_fd(segment) fd.seek(offset) @@ -1584,10 +1582,9 @@ class LoggedIO: size, tag, key, data = self._read( fd, header, segment, offset, (TAG_PUT2, TAG_DELETE, TAG_COMMIT, TAG_PUT), read_data=read_data ) - if include_data: - yield tag, key, offset, data - else: - yield tag, key, offset, size - header_size(tag) # corresponds to len(data) + # tuple[3]: corresponds to len(data) == length of the full chunk payload (meta_len+enc_meta+enc_data) + # tuple[4]: data will be None if read_data is False. + yield tag, key, offset, size - header_size(tag), data assert size >= 0 offset += size # we must get the fd via get_fd() here again as we yielded to our caller and it might diff --git a/src/borg/testsuite/repository.py b/src/borg/testsuite/repository.py index 45669ff69..ecb6000c7 100644 --- a/src/borg/testsuite/repository.py +++ b/src/borg/testsuite/repository.py @@ -60,7 +60,7 @@ class RepositoryTestCaseBase(BaseTestCase): H_trans[None] = -1 # key == None appears in commits tag_trans = {TAG_PUT2: "put2", TAG_PUT: "put", TAG_DELETE: "del", TAG_COMMIT: "comm"} for segment, fn in self.repository.io.segment_iterator(): - for tag, key, offset, size in self.repository.io.iter_objects(segment): + for tag, key, offset, size, _ in self.repository.io.iter_objects(segment): print("%s%s H(%d) -> %s[%d..+%d]" % (label, tag_trans[tag], H_trans[key], fn, offset, size)) print() @@ -372,7 +372,7 @@ class RepositoryCommitTestCase(RepositoryTestCaseBase): self.repo_dump("d1 cc") last_segment = self.repository.io.get_latest_segment() - 1 num_deletes = 0 - for tag, key, offset, size in self.repository.io.iter_objects(last_segment): + for tag, key, offset, size, _ in self.repository.io.iter_objects(last_segment): if tag == TAG_DELETE: assert key == H(1) num_deletes += 1 @@ -384,7 +384,7 @@ class RepositoryCommitTestCase(RepositoryTestCaseBase): assert last_segment not in self.repository.compact assert not self.repository.io.segment_exists(last_segment) for segment, _ in self.repository.io.segment_iterator(): - for tag, key, offset, size in self.repository.io.iter_objects(segment): + for tag, key, offset, size, _ in self.repository.io.iter_objects(segment): assert tag != TAG_DELETE assert key != H(1) # after compaction, there should be no empty shadowed_segments lists left over. From 74ffceabf4ce833122fd368ea24afb6a46b160f4 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 8 Sep 2022 13:17:59 +0200 Subject: [PATCH 09/14] simplify: read_data param of io.read() is not used (yet) --- src/borg/repository.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/borg/repository.py b/src/borg/repository.py index 243db2a62..d2229d8b7 100644 --- a/src/borg/repository.py +++ b/src/borg/repository.py @@ -1653,13 +1653,13 @@ class LoggedIO: h.update(d) return h.digest() - def read(self, segment, offset, id, read_data=True, *, expected_size=None): + def read(self, segment, offset, id, *, read_data=True, expected_size=None): """ Read entry from *segment* at *offset* with *id*. - If read_data is False the size of the entry is returned instead. See the _read() docstring about confidence in the returned data. """ + assert read_data is True # False is not used (yet) if segment == self.segment and self._write_fd: self._write_fd.sync() fd = self.get_fd(segment) @@ -1675,7 +1675,7 @@ class LoggedIO: raise IntegrityError( f"size from repository index: {expected_size} != " f"size from entry header: {data_size_from_header}" ) - return data if read_data else data_size_from_header + return data def _read(self, fd, header, segment, offset, acceptable_tags, read_data=True): """ From 106abbe4d94e86a6aab74307765fcbeab14b91a5 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 8 Sep 2022 18:31:56 +0200 Subject: [PATCH 10/14] new read_data param for repository.get() and .get_many() True (default): return full chunk (client can decrypt meta and data) False: return enough so client can decrypt only the meta --- src/borg/remote.py | 24 +-- src/borg/repository.py | 43 +++++- src/borg/testsuite/repository.py | 249 ++++++++++++++++++------------- 3 files changed, 195 insertions(+), 121 deletions(-) diff --git a/src/borg/remote.py b/src/borg/remote.py index 63bdbf2f2..b21e37495 100644 --- a/src/borg/remote.py +++ b/src/borg/remote.py @@ -1001,12 +1001,12 @@ This problem will go away as soon as the server has been upgraded to 1.0.7+. def flags_many(self, ids, mask=0xFFFFFFFF, value=None): """actual remoting is done via self.call in the @api decorator""" - def get(self, id): - for resp in self.get_many([id]): + def get(self, id, read_data=True): + for resp in self.get_many([id], read_data=read_data): return resp - def get_many(self, ids, is_preloaded=False): - yield from self.call_many("get", [{"id": id} for id in ids], is_preloaded=is_preloaded) + def get_many(self, ids, read_data=True, is_preloaded=False): + yield from self.call_many("get", [{"id": id, "read_data": read_data} for id in ids], is_preloaded=is_preloaded) @api(since=parse_version("1.0.0")) def put(self, id, data, wait=True): @@ -1148,11 +1148,11 @@ class RepositoryNoCache: def __exit__(self, exc_type, exc_val, exc_tb): self.close() - def get(self, key): - return next(self.get_many([key], cache=False)) + def get(self, key, read_data=True): + return next(self.get_many([key], read_data=read_data, cache=False)) - def get_many(self, keys, cache=True): - for key, data in zip(keys, self.repository.get_many(keys)): + def get_many(self, keys, read_data=True, cache=True): + for key, data in zip(keys, self.repository.get_many(keys, read_data=read_data)): yield self.transform(key, data) def log_instrumentation(self): @@ -1250,9 +1250,11 @@ class RepositoryCache(RepositoryNoCache): self.cache.clear() shutil.rmtree(self.basedir) - def get_many(self, keys, cache=True): + def get_many(self, keys, read_data=True, cache=True): + # TODO: this currently always requests the full chunk from self.repository (read_data=True). + # It could use different cache keys depending on read_data and cache full vs. meta-only chunks. unknown_keys = [key for key in keys if key not in self.cache] - repository_iterator = zip(unknown_keys, self.repository.get_many(unknown_keys)) + repository_iterator = zip(unknown_keys, self.repository.get_many(unknown_keys, read_data=True)) for key in keys: if key in self.cache: file = self.key_filename(key) @@ -1269,7 +1271,7 @@ class RepositoryCache(RepositoryNoCache): else: # slow path: eviction during this get_many removed this key from the cache t0 = time.perf_counter() - data = self.repository.get(key) + data = self.repository.get(key, read_data=True) self.slow_lat += time.perf_counter() - t0 transformed = self.add_entry(key, data, cache) self.slow_misses += 1 diff --git a/src/borg/repository.py b/src/borg/repository.py index d2229d8b7..2d5cd132c 100644 --- a/src/borg/repository.py +++ b/src/borg/repository.py @@ -25,6 +25,7 @@ from .locking import Lock, LockError, LockErrorT from .logger import create_logger from .manifest import Manifest from .platform import SaveFile, SyncFile, sync_dir, safe_fadvise +from .repoobj import RepoObj from .checksums import crc32, StreamingXXH64 from .crypto.file_integrity import IntegrityCheckedFile, FileIntegrityError @@ -1268,18 +1269,18 @@ class Repository: def flags_many(self, ids, mask=0xFFFFFFFF, value=None): return [self.flags(id_, mask, value) for id_ in ids] - def get(self, id): + def get(self, id, read_data=True): if not self.index: self.index = self.open_index(self.get_transaction_id()) try: in_index = NSIndexEntry(*((self.index[id] + (None,))[:3])) # legacy: index entries have no size element - return self.io.read(in_index.segment, in_index.offset, id, expected_size=in_index.size) + return self.io.read(in_index.segment, in_index.offset, id, expected_size=in_index.size, read_data=read_data) except KeyError: raise self.ObjectNotFound(id, self.path) from None - def get_many(self, ids, is_preloaded=False): + def get_many(self, ids, read_data=True, is_preloaded=False): for id_ in ids: - yield self.get(id_) + yield self.get(id_, read_data=read_data) def put(self, id, data, wait=True): """put a repo object @@ -1659,13 +1660,12 @@ class LoggedIO: See the _read() docstring about confidence in the returned data. """ - assert read_data is True # False is not used (yet) if segment == self.segment and self._write_fd: self._write_fd.sync() fd = self.get_fd(segment) fd.seek(offset) header = fd.read(self.header_fmt.size) - size, tag, key, data = self._read(fd, header, segment, offset, (TAG_PUT2, TAG_PUT), read_data) + size, tag, key, data = self._read(fd, header, segment, offset, (TAG_PUT2, TAG_PUT), read_data=read_data) if id != key: raise IntegrityError( "Invalid segment entry header, is not for wanted id [segment {}, offset {}]".format(segment, offset) @@ -1686,6 +1686,11 @@ class LoggedIO: PUT2 tags, read_data == False: crc32 check (header) PUT tags, read_data == True: crc32 check (header+data) PUT tags, read_data == False: crc32 check can not be done, all data obtained must be considered informational + + read_data == False behaviour: + PUT2 tags: return enough of the chunk so that the client is able to decrypt the metadata, + do not read, but just seek over the data. + PUT tags: return None and just seek over the data. """ def check_crc32(wanted, header, *data): @@ -1746,7 +1751,31 @@ class LoggedIO: f"expected {self.ENTRY_HASH_SIZE}, got {len(entry_hash)} bytes" ) check_crc32(crc, header, key, entry_hash) - if not read_data: # seek over data + if not read_data: + if tag == TAG_PUT2: + # PUT2 is only used in new repos and they also have different RepoObj layout, + # supporting separately encrypted metadata and data. + # In this case, we return enough bytes so the client can decrypt the metadata + # and seek over the rest (over the encrypted data). + meta_len_size = RepoObj.meta_len_hdr.size + meta_len = fd.read(meta_len_size) + length -= meta_len_size + if len(meta_len) != meta_len_size: + raise IntegrityError( + f"Segment entry meta length short read [segment {segment}, offset {offset}]: " + f"expected {meta_len_size}, got {len(meta_len)} bytes" + ) + ml = RepoObj.meta_len_hdr.unpack(meta_len)[0] + meta = fd.read(ml) + length -= ml + if len(meta) != ml: + raise IntegrityError( + f"Segment entry meta short read [segment {segment}, offset {offset}]: " + f"expected {ml}, got {len(meta)} bytes" + ) + data = meta_len + meta # shortened chunk - enough so the client can decrypt the metadata + # we do not have a checksum for this data, but the client's AEAD crypto will check it. + # in any case, we see over the remainder of the chunk oldpos = fd.tell() seeked = fd.seek(length, os.SEEK_CUR) - oldpos if seeked != length: diff --git a/src/borg/testsuite/repository.py b/src/borg/testsuite/repository.py index ecb6000c7..d88ca6351 100644 --- a/src/borg/testsuite/repository.py +++ b/src/borg/testsuite/repository.py @@ -15,6 +15,7 @@ from ..helpers import msgpack from ..locking import Lock, LockFailed from ..remote import RemoteRepository, InvalidRPCMethod, PathNotAllowed, handle_remote_line from ..repository import Repository, LoggedIO, MAGIC, MAX_DATA_SIZE, TAG_DELETE, TAG_PUT2, TAG_PUT, TAG_COMMIT +from ..repoobj import RepoObj from . import BaseTestCase from .hashindex import H @@ -22,6 +23,29 @@ from .hashindex import H UNSPECIFIED = object() # for default values where we can't use None +def fchunk(data, meta=b""): + # create a raw chunk that has valid RepoObj layout, but does not use encryption or compression. + meta_len = RepoObj.meta_len_hdr.pack(len(meta)) + assert isinstance(data, bytes) + chunk = meta_len + meta + data + return chunk + + +def pchunk(chunk): + # parse data and meta from a raw chunk made by fchunk + meta_len_size = RepoObj.meta_len_hdr.size + meta_len = chunk[:meta_len_size] + meta_len = RepoObj.meta_len_hdr.unpack(meta_len)[0] + meta = chunk[meta_len_size : meta_len_size + meta_len] + data = chunk[meta_len_size + meta_len :] + return data, meta + + +def pdchunk(chunk): + # parse only data from a raw chunk made by fchunk + return pchunk(chunk)[0] + + class RepositoryTestCaseBase(BaseTestCase): key_size = 32 exclusive = True @@ -46,12 +70,12 @@ class RepositoryTestCaseBase(BaseTestCase): self.repository = self.open(exclusive=exclusive) def add_keys(self): - self.repository.put(H(0), b"foo") - self.repository.put(H(1), b"bar") - self.repository.put(H(3), b"bar") + self.repository.put(H(0), fchunk(b"foo")) + self.repository.put(H(1), fchunk(b"bar")) + self.repository.put(H(3), fchunk(b"bar")) self.repository.commit(compact=False) - self.repository.put(H(1), b"bar2") - self.repository.put(H(2), b"boo") + self.repository.put(H(1), fchunk(b"bar2")) + self.repository.put(H(2), fchunk(b"boo")) self.repository.delete(H(3)) def repo_dump(self, label=None): @@ -68,9 +92,9 @@ class RepositoryTestCaseBase(BaseTestCase): class RepositoryTestCase(RepositoryTestCaseBase): def test1(self): for x in range(100): - self.repository.put(H(x), b"SOMEDATA") + self.repository.put(H(x), fchunk(b"SOMEDATA")) key50 = H(50) - self.assert_equal(self.repository.get(key50), b"SOMEDATA") + self.assert_equal(pdchunk(self.repository.get(key50)), b"SOMEDATA") self.repository.delete(key50) self.assert_raises(Repository.ObjectNotFound, lambda: self.repository.get(key50)) self.repository.commit(compact=False) @@ -80,55 +104,66 @@ class RepositoryTestCase(RepositoryTestCaseBase): for x in range(100): if x == 50: continue - self.assert_equal(repository2.get(H(x)), b"SOMEDATA") + self.assert_equal(pdchunk(repository2.get(H(x))), b"SOMEDATA") def test2(self): """Test multiple sequential transactions""" - self.repository.put(H(0), b"foo") - self.repository.put(H(1), b"foo") + self.repository.put(H(0), fchunk(b"foo")) + self.repository.put(H(1), fchunk(b"foo")) self.repository.commit(compact=False) self.repository.delete(H(0)) - self.repository.put(H(1), b"bar") + self.repository.put(H(1), fchunk(b"bar")) self.repository.commit(compact=False) - self.assert_equal(self.repository.get(H(1)), b"bar") + self.assert_equal(pdchunk(self.repository.get(H(1))), b"bar") + + def test_read_data(self): + meta, data = b"meta", b"data" + meta_len = RepoObj.meta_len_hdr.pack(len(meta)) + chunk_complete = meta_len + meta + data + chunk_short = meta_len + meta + self.repository.put(H(0), chunk_complete) + self.repository.commit(compact=False) + self.assert_equal(self.repository.get(H(0)), chunk_complete) + self.assert_equal(self.repository.get(H(0), read_data=True), chunk_complete) + self.assert_equal(self.repository.get(H(0), read_data=False), chunk_short) def test_consistency(self): """Test cache consistency""" - self.repository.put(H(0), b"foo") - self.assert_equal(self.repository.get(H(0)), b"foo") - self.repository.put(H(0), b"foo2") - self.assert_equal(self.repository.get(H(0)), b"foo2") - self.repository.put(H(0), b"bar") - self.assert_equal(self.repository.get(H(0)), b"bar") + self.repository.put(H(0), fchunk(b"foo")) + self.assert_equal(pdchunk(self.repository.get(H(0))), b"foo") + self.repository.put(H(0), fchunk(b"foo2")) + self.assert_equal(pdchunk(self.repository.get(H(0))), b"foo2") + self.repository.put(H(0), fchunk(b"bar")) + self.assert_equal(pdchunk(self.repository.get(H(0))), b"bar") self.repository.delete(H(0)) self.assert_raises(Repository.ObjectNotFound, lambda: self.repository.get(H(0))) def test_consistency2(self): """Test cache consistency2""" - self.repository.put(H(0), b"foo") - self.assert_equal(self.repository.get(H(0)), b"foo") + self.repository.put(H(0), fchunk(b"foo")) + self.assert_equal(pdchunk(self.repository.get(H(0))), b"foo") self.repository.commit(compact=False) - self.repository.put(H(0), b"foo2") - self.assert_equal(self.repository.get(H(0)), b"foo2") + self.repository.put(H(0), fchunk(b"foo2")) + self.assert_equal(pdchunk(self.repository.get(H(0))), b"foo2") self.repository.rollback() - self.assert_equal(self.repository.get(H(0)), b"foo") + self.assert_equal(pdchunk(self.repository.get(H(0))), b"foo") def test_overwrite_in_same_transaction(self): """Test cache consistency2""" - self.repository.put(H(0), b"foo") - self.repository.put(H(0), b"foo2") + self.repository.put(H(0), fchunk(b"foo")) + self.repository.put(H(0), fchunk(b"foo2")) self.repository.commit(compact=False) - self.assert_equal(self.repository.get(H(0)), b"foo2") + self.assert_equal(pdchunk(self.repository.get(H(0))), b"foo2") def test_single_kind_transactions(self): # put - self.repository.put(H(0), b"foo") + self.repository.put(H(0), fchunk(b"foo")) self.repository.commit(compact=False) self.repository.close() # replace self.repository = self.open() with self.repository: - self.repository.put(H(0), b"bar") + self.repository.put(H(0), fchunk(b"bar")) self.repository.commit(compact=False) # delete self.repository = self.open() @@ -138,7 +173,7 @@ class RepositoryTestCase(RepositoryTestCaseBase): def test_list(self): for x in range(100): - self.repository.put(H(x), b"SOMEDATA") + self.repository.put(H(x), fchunk(b"SOMEDATA")) self.repository.commit(compact=False) all = self.repository.list() self.assert_equal(len(all), 100) @@ -152,7 +187,7 @@ class RepositoryTestCase(RepositoryTestCaseBase): def test_scan(self): for x in range(100): - self.repository.put(H(x), b"SOMEDATA") + self.repository.put(H(x), fchunk(b"SOMEDATA")) self.repository.commit(compact=False) all = self.repository.scan() assert len(all) == 100 @@ -168,14 +203,14 @@ class RepositoryTestCase(RepositoryTestCaseBase): assert all[x] == H(x) def test_max_data_size(self): - max_data = b"x" * MAX_DATA_SIZE - self.repository.put(H(0), max_data) - self.assert_equal(self.repository.get(H(0)), max_data) - self.assert_raises(IntegrityError, lambda: self.repository.put(H(1), max_data + b"x")) + max_data = b"x" * (MAX_DATA_SIZE - RepoObj.meta_len_hdr.size) + self.repository.put(H(0), fchunk(max_data)) + self.assert_equal(pdchunk(self.repository.get(H(0))), max_data) + self.assert_raises(IntegrityError, lambda: self.repository.put(H(1), fchunk(max_data + b"x"))) def test_set_flags(self): id = H(0) - self.repository.put(id, b"") + self.repository.put(id, fchunk(b"")) self.assert_equal(self.repository.flags(id), 0x00000000) # init == all zero self.repository.flags(id, mask=0x00000001, value=0x00000001) self.assert_equal(self.repository.flags(id), 0x00000001) @@ -188,7 +223,7 @@ class RepositoryTestCase(RepositoryTestCaseBase): def test_get_flags(self): id = H(0) - self.repository.put(id, b"") + self.repository.put(id, fchunk(b"")) self.assert_equal(self.repository.flags(id), 0x00000000) # init == all zero self.repository.flags(id, mask=0xC0000003, value=0x80000001) self.assert_equal(self.repository.flags(id, mask=0x00000001), 0x00000001) @@ -199,7 +234,7 @@ class RepositoryTestCase(RepositoryTestCaseBase): def test_flags_many(self): ids_flagged = [H(0), H(1)] ids_default_flags = [H(2), H(3)] - [self.repository.put(id, b"") for id in ids_flagged + ids_default_flags] + [self.repository.put(id, fchunk(b"")) for id in ids_flagged + ids_default_flags] self.repository.flags_many(ids_flagged, mask=0xFFFFFFFF, value=0xDEADBEEF) self.assert_equal(list(self.repository.flags_many(ids_default_flags)), [0x00000000, 0x00000000]) self.assert_equal(list(self.repository.flags_many(ids_flagged)), [0xDEADBEEF, 0xDEADBEEF]) @@ -207,8 +242,8 @@ class RepositoryTestCase(RepositoryTestCaseBase): self.assert_equal(list(self.repository.flags_many(ids_flagged, mask=0x0000FFFF)), [0x0000BEEF, 0x0000BEEF]) def test_flags_persistence(self): - self.repository.put(H(0), b"default") - self.repository.put(H(1), b"one one zero") + self.repository.put(H(0), fchunk(b"default")) + self.repository.put(H(1), fchunk(b"one one zero")) # we do not set flags for H(0), so we can later check their default state. self.repository.flags(H(1), mask=0x00000007, value=0x00000006) self.repository.commit(compact=False) @@ -227,38 +262,39 @@ class LocalRepositoryTestCase(RepositoryTestCaseBase): def _assert_sparse(self): # The superseded 123456... PUT - assert self.repository.compact[0] == 41 + 8 + 9 + assert self.repository.compact[0] == 41 + 8 + len(fchunk(b"123456789")) # a COMMIT assert self.repository.compact[1] == 9 # The DELETE issued by the superseding PUT (or issued directly) assert self.repository.compact[2] == 41 self.repository._rebuild_sparse(0) - assert self.repository.compact[0] == 41 + 8 + 9 + assert self.repository.compact[0] == 41 + 8 + len(fchunk(b"123456789")) # 9 is chunk or commit? def test_sparse1(self): - self.repository.put(H(0), b"foo") - self.repository.put(H(1), b"123456789") + self.repository.put(H(0), fchunk(b"foo")) + self.repository.put(H(1), fchunk(b"123456789")) self.repository.commit(compact=False) - self.repository.put(H(1), b"bar") + self.repository.put(H(1), fchunk(b"bar")) self._assert_sparse() def test_sparse2(self): - self.repository.put(H(0), b"foo") - self.repository.put(H(1), b"123456789") + self.repository.put(H(0), fchunk(b"foo")) + self.repository.put(H(1), fchunk(b"123456789")) self.repository.commit(compact=False) self.repository.delete(H(1)) self._assert_sparse() def test_sparse_delete(self): - self.repository.put(H(0), b"1245") + ch0 = fchunk(b"1245") + self.repository.put(H(0), ch0) self.repository.delete(H(0)) self.repository.io._write_fd.sync() # The on-line tracking works on a per-object basis... - assert self.repository.compact[0] == 41 + 8 + 41 + 4 + assert self.repository.compact[0] == 41 + 8 + 41 + len(ch0) self.repository._rebuild_sparse(0) # ...while _rebuild_sparse can mark whole segments as completely sparse (which then includes the segment magic) - assert self.repository.compact[0] == 41 + 8 + 41 + 4 + len(MAGIC) + assert self.repository.compact[0] == 41 + 8 + 41 + len(ch0) + len(MAGIC) self.repository.commit(compact=True) assert 0 not in [segment for segment, _ in self.repository.io.segment_iterator()] @@ -266,7 +302,7 @@ class LocalRepositoryTestCase(RepositoryTestCaseBase): def test_uncommitted_garbage(self): # uncommitted garbage should be no problem, it is cleaned up automatically. # we just have to be careful with invalidation of cached FDs in LoggedIO. - self.repository.put(H(0), b"foo") + self.repository.put(H(0), fchunk(b"foo")) self.repository.commit(compact=False) # write some crap to a uncommitted segment file last_segment = self.repository.io.get_latest_segment() @@ -276,7 +312,7 @@ class LocalRepositoryTestCase(RepositoryTestCaseBase): # usually, opening the repo and starting a transaction should trigger a cleanup. self.repository = self.open() with self.repository: - self.repository.put(H(0), b"bar") # this may trigger compact_segments() + self.repository.put(H(0), fchunk(b"bar")) # this may trigger compact_segments() self.repository.commit(compact=True) # the point here is that nothing blows up with an exception. @@ -363,8 +399,8 @@ class RepositoryCommitTestCase(RepositoryTestCaseBase): assert not io.is_committed_segment(io.get_latest_segment()) def test_moved_deletes_are_tracked(self): - self.repository.put(H(1), b"1") - self.repository.put(H(2), b"2") + self.repository.put(H(1), fchunk(b"1")) + self.repository.put(H(2), fchunk(b"2")) self.repository.commit(compact=False) self.repo_dump("p1 p2 c") self.repository.delete(H(1)) @@ -378,7 +414,7 @@ class RepositoryCommitTestCase(RepositoryTestCaseBase): num_deletes += 1 assert num_deletes == 1 assert last_segment in self.repository.compact - self.repository.put(H(3), b"3") + self.repository.put(H(3), fchunk(b"3")) self.repository.commit(compact=True) self.repo_dump("p3 cc") assert last_segment not in self.repository.compact @@ -393,7 +429,7 @@ class RepositoryCommitTestCase(RepositoryTestCaseBase): def test_shadowed_entries_are_preserved(self): get_latest_segment = self.repository.io.get_latest_segment - self.repository.put(H(1), b"1") + self.repository.put(H(1), fchunk(b"1")) # This is the segment with our original PUT of interest put_segment = get_latest_segment() self.repository.commit(compact=False) @@ -401,7 +437,7 @@ class RepositoryCommitTestCase(RepositoryTestCaseBase): # We now delete H(1), and force this segment to not be compacted, which can happen # if it's not sparse enough (symbolized by H(2) here). self.repository.delete(H(1)) - self.repository.put(H(2), b"1") + self.repository.put(H(2), fchunk(b"1")) delete_segment = get_latest_segment() # We pretend these are mostly dense (not sparse) and won't be compacted @@ -426,7 +462,7 @@ class RepositoryCommitTestCase(RepositoryTestCaseBase): assert H(1) not in self.repository def test_shadow_index_rollback(self): - self.repository.put(H(1), b"1") + self.repository.put(H(1), fchunk(b"1")) self.repository.delete(H(1)) assert self.repository.shadow_index[H(1)] == [0] self.repository.commit(compact=True) @@ -440,7 +476,7 @@ class RepositoryCommitTestCase(RepositoryTestCaseBase): assert self.repository.shadow_index[H(1)] == [4] self.repository.rollback() self.repo_dump("r") - self.repository.put(H(2), b"1") + self.repository.put(H(2), fchunk(b"1")) # After the rollback segment 4 shouldn't be considered anymore assert self.repository.shadow_index[H(1)] == [] # because the delete is considered unstable @@ -459,19 +495,19 @@ class RepositoryAppendOnlyTestCase(RepositoryTestCaseBase): def segments_in_repository(): return len(list(self.repository.io.segment_iterator())) - self.repository.put(H(0), b"foo") + self.repository.put(H(0), fchunk(b"foo")) self.repository.commit(compact=False) self.repository.append_only = False assert segments_in_repository() == 2 - self.repository.put(H(0), b"foo") + self.repository.put(H(0), fchunk(b"foo")) self.repository.commit(compact=True) # normal: compact squashes the data together, only one segment assert segments_in_repository() == 2 self.repository.append_only = True assert segments_in_repository() == 2 - self.repository.put(H(0), b"foo") + self.repository.put(H(0), fchunk(b"foo")) self.repository.commit(compact=False) # append only: does not compact, only new segments written assert segments_in_repository() == 4 @@ -485,7 +521,7 @@ class RepositoryFreeSpaceTestCase(RepositoryTestCaseBase): self.reopen() with self.repository: - self.repository.put(H(0), b"foobar") + self.repository.put(H(0), fchunk(b"foobar")) with pytest.raises(Repository.InsufficientFreeSpaceError): self.repository.commit(compact=False) assert os.path.exists(self.repository.path) @@ -500,45 +536,52 @@ class RepositoryFreeSpaceTestCase(RepositoryTestCaseBase): class QuotaTestCase(RepositoryTestCaseBase): def test_tracking(self): assert self.repository.storage_quota_use == 0 - self.repository.put(H(1), bytes(1234)) - assert self.repository.storage_quota_use == 1234 + 41 + 8 - self.repository.put(H(2), bytes(5678)) - assert self.repository.storage_quota_use == 1234 + 5678 + 2 * (41 + 8) + ch1 = fchunk(bytes(1234)) + self.repository.put(H(1), ch1) + assert self.repository.storage_quota_use == len(ch1) + 41 + 8 + ch2 = fchunk(bytes(5678)) + self.repository.put(H(2), ch2) + assert self.repository.storage_quota_use == len(ch1) + len(ch2) + 2 * (41 + 8) self.repository.delete(H(1)) - assert self.repository.storage_quota_use == 1234 + 5678 + 2 * (41 + 8) # we have not compacted yet + assert self.repository.storage_quota_use == len(ch1) + len(ch2) + 2 * (41 + 8) # we have not compacted yet self.repository.commit(compact=False) - assert self.repository.storage_quota_use == 1234 + 5678 + 2 * (41 + 8) # we have not compacted yet + assert self.repository.storage_quota_use == len(ch1) + len(ch2) + 2 * (41 + 8) # we have not compacted yet self.reopen() with self.repository: # Open new transaction; hints and thus quota data is not loaded unless needed. - self.repository.put(H(3), b"") + ch3 = fchunk(b"") + self.repository.put(H(3), ch3) self.repository.delete(H(3)) - assert self.repository.storage_quota_use == 1234 + 5678 + 3 * (41 + 8) # we have not compacted yet + assert self.repository.storage_quota_use == len(ch1) + len(ch2) + len(ch3) + 3 * ( + 41 + 8 + ) # we have not compacted yet self.repository.commit(compact=True) - assert self.repository.storage_quota_use == 5678 + 41 + 8 + assert self.repository.storage_quota_use == len(ch2) + 41 + 8 def test_exceed_quota(self): assert self.repository.storage_quota_use == 0 self.repository.storage_quota = 80 - self.repository.put(H(1), b"") - assert self.repository.storage_quota_use == 41 + 8 + ch1 = fchunk(b"x" * 7) + self.repository.put(H(1), ch1) + assert self.repository.storage_quota_use == len(ch1) + 41 + 8 self.repository.commit(compact=False) with pytest.raises(Repository.StorageQuotaExceeded): - self.repository.put(H(2), b"") - assert self.repository.storage_quota_use == (41 + 8) * 2 + ch2 = fchunk(b"y" * 13) + self.repository.put(H(2), ch2) + assert self.repository.storage_quota_use == len(ch1) + len(ch2) + (41 + 8) * 2 # check ch2!? with pytest.raises(Repository.StorageQuotaExceeded): self.repository.commit(compact=False) - assert self.repository.storage_quota_use == (41 + 8) * 2 + assert self.repository.storage_quota_use == len(ch1) + len(ch2) + (41 + 8) * 2 # check ch2!? self.reopen() with self.repository: self.repository.storage_quota = 150 # Open new transaction; hints and thus quota data is not loaded unless needed. - self.repository.put(H(1), b"") + self.repository.put(H(1), ch1) assert ( - self.repository.storage_quota_use == (41 + 8) * 2 + self.repository.storage_quota_use == len(ch1) * 2 + (41 + 8) * 2 ) # we have 2 puts for H(1) here and not yet compacted. self.repository.commit(compact=True) - assert self.repository.storage_quota_use == 41 + 8 # now we have compacted. + assert self.repository.storage_quota_use == len(ch1) + 41 + 8 # now we have compacted. class NonceReservation(RepositoryTestCaseBase): @@ -586,13 +629,13 @@ class NonceReservation(RepositoryTestCaseBase): class RepositoryAuxiliaryCorruptionTestCase(RepositoryTestCaseBase): def setUp(self): super().setUp() - self.repository.put(H(0), b"foo") + self.repository.put(H(0), fchunk(b"foo")) self.repository.commit(compact=False) self.repository.close() def do_commit(self): with self.repository: - self.repository.put(H(0), b"fox") + self.repository.put(H(0), fchunk(b"fox")) self.repository.commit(compact=False) def test_corrupted_hints(self): @@ -648,7 +691,7 @@ class RepositoryAuxiliaryCorruptionTestCase(RepositoryTestCaseBase): # Data corruption is detected due to mismatching checksums # and fixed by rebuilding the index. assert len(self.repository) == 1 - assert self.repository.get(H(0)) == b"foo" + assert pdchunk(self.repository.get(H(0))) == b"foo" def test_index_corrupted_without_integrity(self): self._corrupt_index() @@ -684,17 +727,17 @@ class RepositoryAuxiliaryCorruptionTestCase(RepositoryTestCaseBase): with self.repository: # No issues accessing the repository assert len(self.repository) == 1 - assert self.repository.get(H(0)) == b"foo" + assert pdchunk(self.repository.get(H(0))) == b"foo" def _subtly_corrupted_hints_setup(self): with self.repository: self.repository.append_only = True assert len(self.repository) == 1 - assert self.repository.get(H(0)) == b"foo" - self.repository.put(H(1), b"bar") - self.repository.put(H(2), b"baz") + assert pdchunk(self.repository.get(H(0))) == b"foo" + self.repository.put(H(1), fchunk(b"bar")) + self.repository.put(H(2), fchunk(b"baz")) self.repository.commit(compact=False) - self.repository.put(H(2), b"bazz") + self.repository.put(H(2), fchunk(b"bazz")) self.repository.commit(compact=False) hints_path = os.path.join(self.repository.path, "hints.5") @@ -711,14 +754,14 @@ class RepositoryAuxiliaryCorruptionTestCase(RepositoryTestCaseBase): self._subtly_corrupted_hints_setup() with self.repository: self.repository.append_only = False - self.repository.put(H(3), b"1234") + self.repository.put(H(3), fchunk(b"1234")) # Do a compaction run. Succeeds, since the failed checksum prompted a rebuild of the index+hints. self.repository.commit(compact=True) assert len(self.repository) == 4 - assert self.repository.get(H(0)) == b"foo" - assert self.repository.get(H(1)) == b"bar" - assert self.repository.get(H(2)) == b"bazz" + assert pdchunk(self.repository.get(H(0))) == b"foo" + assert pdchunk(self.repository.get(H(1))) == b"bar" + assert pdchunk(self.repository.get(H(2))) == b"bazz" def test_subtly_corrupted_hints_without_integrity(self): self._subtly_corrupted_hints_setup() @@ -726,7 +769,7 @@ class RepositoryAuxiliaryCorruptionTestCase(RepositoryTestCaseBase): os.unlink(integrity_path) with self.repository: self.repository.append_only = False - self.repository.put(H(3), b"1234") + self.repository.put(H(3), fchunk(b"1234")) # Do a compaction run. Fails, since the corrupted refcount was not detected and leads to an assertion failure. with pytest.raises(AssertionError) as exc_info: self.repository.commit(compact=True) @@ -748,12 +791,12 @@ class RepositoryCheckTestCase(RepositoryTestCaseBase): def get_objects(self, *ids): for id_ in ids: - self.repository.get(H(id_)) + pdchunk(self.repository.get(H(id_))) def add_objects(self, segments): for ids in segments: for id_ in ids: - self.repository.put(H(id_), b"data") + self.repository.put(H(id_), fchunk(b"data")) self.repository.commit(compact=False) def get_head(self): @@ -859,8 +902,8 @@ class RepositoryCheckTestCase(RepositoryTestCaseBase): self.assert_equal({1, 2, 3, 4, 5, 6}, self.list_objects()) def test_crash_before_compact(self): - self.repository.put(H(0), b"data") - self.repository.put(H(0), b"data2") + self.repository.put(H(0), fchunk(b"data")) + self.repository.put(H(0), fchunk(b"data2")) # Simulate a crash before compact with patch.object(Repository, "compact_segments") as compact: self.repository.commit(compact=True) @@ -868,12 +911,12 @@ class RepositoryCheckTestCase(RepositoryTestCaseBase): self.reopen() with self.repository: self.check(repair=True) - self.assert_equal(self.repository.get(H(0)), b"data2") + self.assert_equal(pdchunk(self.repository.get(H(0))), b"data2") class RepositoryHintsTestCase(RepositoryTestCaseBase): def test_hints_persistence(self): - self.repository.put(H(0), b"data") + self.repository.put(H(0), fchunk(b"data")) self.repository.delete(H(0)) self.repository.commit(compact=False) shadow_index_expected = self.repository.shadow_index @@ -884,7 +927,7 @@ class RepositoryHintsTestCase(RepositoryTestCaseBase): self.reopen() with self.repository: # see also do_compact() - self.repository.put(H(42), b"foobar") # this will call prepare_txn() and load the hints data + self.repository.put(H(42), fchunk(b"foobar")) # this will call prepare_txn() and load the hints data # check if hints persistence worked: self.assert_equal(shadow_index_expected, self.repository.shadow_index) self.assert_equal(compact_expected, self.repository.compact) @@ -892,7 +935,7 @@ class RepositoryHintsTestCase(RepositoryTestCaseBase): self.assert_equal(segments_expected, self.repository.segments) def test_hints_behaviour(self): - self.repository.put(H(0), b"data") + self.repository.put(H(0), fchunk(b"data")) self.assert_equal(self.repository.shadow_index, {}) assert len(self.repository.compact) == 0 self.repository.delete(H(0)) @@ -901,7 +944,7 @@ class RepositoryHintsTestCase(RepositoryTestCaseBase): self.assert_in(H(0), self.repository.shadow_index) self.assert_equal(len(self.repository.shadow_index[H(0)]), 1) self.assert_in(0, self.repository.compact) # segment 0 can be compacted - self.repository.put(H(42), b"foobar") # see also do_compact() + self.repository.put(H(42), fchunk(b"foobar")) # see also do_compact() self.repository.commit(compact=True, threshold=0.0) # compact completely! # nothing to compact any more! no info left about stuff that does not exist any more: self.assert_not_in(H(0), self.repository.shadow_index) @@ -1041,13 +1084,13 @@ class RemoteLegacyFree(RepositoryTestCaseBase): def test_legacy_free(self): # put - self.repository.put(H(0), b"foo") + self.repository.put(H(0), fchunk(b"foo")) self.repository.commit(compact=False) self.repository.close() # replace self.repository = self.open() with self.repository: - self.repository.put(H(0), b"bar") + self.repository.put(H(0), fchunk(b"bar")) self.repository.commit(compact=False) # delete self.repository = self.open() From 06eab6a22871c17605b4ed37904299979f3e9268 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 8 Sep 2022 19:38:18 +0200 Subject: [PATCH 11/14] RepositoryCache: cache complete and meta-only chunks separately --- src/borg/remote.py | 28 +++++++++------ src/borg/testsuite/remote.py | 68 ++++++++++++++++++++++++++++-------- 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/src/borg/remote.py b/src/borg/remote.py index b21e37495..de0002ba2 100644 --- a/src/borg/remote.py +++ b/src/borg/remote.py @@ -1191,6 +1191,12 @@ class RepositoryCache(RepositoryNoCache): available_space = shutil.disk_usage(self.basedir).free self.size_limit = int(min(available_space * 0.25, 2**31)) + def prefixed_key(self, key, complete): + # just prefix another byte telling whether this key refers to a complete chunk + # or a without-data-metadata-only chunk (see also read_data param). + prefix = b"\x01" if complete else b"\x00" + return prefix + key + def key_filename(self, key): return os.path.join(self.basedir, bin_to_hex(key)) @@ -1204,12 +1210,13 @@ class RepositoryCache(RepositoryNoCache): os.unlink(file) self.evictions += 1 - def add_entry(self, key, data, cache): + def add_entry(self, key, data, cache, complete): transformed = self.transform(key, data) if not cache: return transformed packed = self.pack(transformed) - file = self.key_filename(key) + pkey = self.prefixed_key(key, complete=complete) + file = self.key_filename(pkey) try: with open(file, "wb") as fd: fd.write(packed) @@ -1225,7 +1232,7 @@ class RepositoryCache(RepositoryNoCache): raise else: self.size += len(packed) - self.cache.add(key) + self.cache.add(pkey) if self.size > self.size_limit: self.backoff() return transformed @@ -1253,27 +1260,28 @@ class RepositoryCache(RepositoryNoCache): def get_many(self, keys, read_data=True, cache=True): # TODO: this currently always requests the full chunk from self.repository (read_data=True). # It could use different cache keys depending on read_data and cache full vs. meta-only chunks. - unknown_keys = [key for key in keys if key not in self.cache] - repository_iterator = zip(unknown_keys, self.repository.get_many(unknown_keys, read_data=True)) + unknown_keys = [key for key in keys if self.prefixed_key(key, complete=read_data) not in self.cache] + repository_iterator = zip(unknown_keys, self.repository.get_many(unknown_keys, read_data=read_data)) for key in keys: - if key in self.cache: - file = self.key_filename(key) + pkey = self.prefixed_key(key, complete=read_data) + if pkey in self.cache: + file = self.key_filename(pkey) with open(file, "rb") as fd: self.hits += 1 yield self.unpack(fd.read()) else: for key_, data in repository_iterator: if key_ == key: - transformed = self.add_entry(key, data, cache) + transformed = self.add_entry(key, data, cache, complete=read_data) self.misses += 1 yield transformed break else: # slow path: eviction during this get_many removed this key from the cache t0 = time.perf_counter() - data = self.repository.get(key, read_data=True) + data = self.repository.get(key, read_data=read_data) self.slow_lat += time.perf_counter() - t0 - transformed = self.add_entry(key, data, cache) + transformed = self.add_entry(key, data, cache, complete=read_data) self.slow_misses += 1 yield transformed # Consume any pending requests diff --git a/src/borg/testsuite/remote.py b/src/borg/testsuite/remote.py index 35f2b6df3..b5cb2e642 100644 --- a/src/borg/testsuite/remote.py +++ b/src/borg/testsuite/remote.py @@ -12,6 +12,7 @@ from ..crypto.key import PlaintextKey from ..helpers import IntegrityError from ..repoobj import RepoObj from .hashindex import H +from .repository import fchunk, pdchunk from .key import TestKey @@ -74,9 +75,9 @@ class TestRepositoryCache: def repository(self, tmpdir): self.repository_location = os.path.join(str(tmpdir), "repository") with Repository(self.repository_location, exclusive=True, create=True) as repository: - repository.put(H(1), b"1234") - repository.put(H(2), b"5678") - repository.put(H(3), bytes(100)) + repository.put(H(1), fchunk(b"1234")) + repository.put(H(2), fchunk(b"5678")) + repository.put(H(3), fchunk(bytes(100))) yield repository @pytest.fixture @@ -85,19 +86,55 @@ class TestRepositoryCache: def test_simple(self, cache: RepositoryCache): # Single get()s are not cached, since they are used for unique objects like archives. - assert cache.get(H(1)) == b"1234" + assert pdchunk(cache.get(H(1))) == b"1234" assert cache.misses == 1 assert cache.hits == 0 - assert list(cache.get_many([H(1)])) == [b"1234"] + assert [pdchunk(ch) for ch in cache.get_many([H(1)])] == [b"1234"] assert cache.misses == 2 assert cache.hits == 0 - assert list(cache.get_many([H(1)])) == [b"1234"] + assert [pdchunk(ch) for ch in cache.get_many([H(1)])] == [b"1234"] assert cache.misses == 2 assert cache.hits == 1 - assert cache.get(H(1)) == b"1234" + assert pdchunk(cache.get(H(1))) == b"1234" + assert cache.misses == 2 + assert cache.hits == 2 + + def test_meta(self, cache: RepositoryCache): + # same as test_simple, but not reading the chunk data (metadata only). + # Single get()s are not cached, since they are used for unique objects like archives. + assert pdchunk(cache.get(H(1), read_data=False)) == b"" + assert cache.misses == 1 + assert cache.hits == 0 + + assert [pdchunk(ch) for ch in cache.get_many([H(1)], read_data=False)] == [b""] + assert cache.misses == 2 + assert cache.hits == 0 + + assert [pdchunk(ch) for ch in cache.get_many([H(1)], read_data=False)] == [b""] + assert cache.misses == 2 + assert cache.hits == 1 + + assert pdchunk(cache.get(H(1), read_data=False)) == b"" + assert cache.misses == 2 + assert cache.hits == 2 + + def test_mixed(self, cache: RepositoryCache): + assert [pdchunk(ch) for ch in cache.get_many([H(1)], read_data=False)] == [b""] + assert cache.misses == 1 + assert cache.hits == 0 + + assert [pdchunk(ch) for ch in cache.get_many([H(1)], read_data=True)] == [b"1234"] + assert cache.misses == 2 + assert cache.hits == 0 + + assert [pdchunk(ch) for ch in cache.get_many([H(1)], read_data=False)] == [b""] + assert cache.misses == 2 + assert cache.hits == 1 + + assert [pdchunk(ch) for ch in cache.get_many([H(1)], read_data=True)] == [b"1234"] assert cache.misses == 2 assert cache.hits == 2 @@ -105,11 +142,11 @@ class TestRepositoryCache: def query_size_limit(): cache.size_limit = 0 - assert list(cache.get_many([H(1), H(2)])) == [b"1234", b"5678"] + assert [pdchunk(ch) for ch in cache.get_many([H(1), H(2)])] == [b"1234", b"5678"] assert cache.misses == 2 assert cache.evictions == 0 iterator = cache.get_many([H(1), H(3), H(2)]) - assert next(iterator) == b"1234" + assert pdchunk(next(iterator)) == b"1234" # Force cache to back off qsl = cache.query_size_limit @@ -120,11 +157,11 @@ class TestRepositoryCache: assert cache.evictions == 2 assert H(1) not in cache.cache assert H(2) not in cache.cache - assert next(iterator) == bytes(100) + assert pdchunk(next(iterator)) == bytes(100) assert cache.slow_misses == 0 # Since H(2) was in the cache when we called get_many(), but has # been evicted during iterating the generator, it will be a slow miss. - assert next(iterator) == b"5678" + assert pdchunk(next(iterator)) == b"5678" assert cache.slow_misses == 1 def test_enospc(self, cache: RepositoryCache): @@ -145,16 +182,16 @@ class TestRepositoryCache: pass iterator = cache.get_many([H(1), H(2), H(3)]) - assert next(iterator) == b"1234" + assert pdchunk(next(iterator)) == b"1234" with patch("builtins.open", enospc_open): - assert next(iterator) == b"5678" + assert pdchunk(next(iterator)) == b"5678" assert cache.enospc == 1 # We didn't patch query_size_limit which would set size_limit to some low # value, so nothing was actually evicted. assert cache.evictions == 0 - assert next(iterator) == bytes(100) + assert pdchunk(next(iterator)) == bytes(100) @pytest.fixture def key(self, repository, monkeypatch): @@ -193,7 +230,8 @@ class TestRepositoryCache: iterator = decrypted_cache.get_many([H1, H2, H3]) assert next(iterator) == (4, b"1234") - with open(decrypted_cache.key_filename(H2), "a+b") as fd: + pkey = decrypted_cache.prefixed_key(H2, complete=True) + with open(decrypted_cache.key_filename(pkey), "a+b") as fd: fd.seek(-1, io.SEEK_END) corrupted = (int.from_bytes(fd.read(), "little") ^ 2).to_bytes(1, "little") fd.seek(-1, io.SEEK_END) From b28d6ee657b5affa16d92640eca7096b49d45258 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 8 Sep 2022 19:56:37 +0200 Subject: [PATCH 12/14] recompress: only read metadata to check for ctype/clevel --- src/borg/archive.py | 2 +- src/borg/remote.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/borg/archive.py b/src/borg/archive.py index ef41df413..f30597c1c 100644 --- a/src/borg/archive.py +++ b/src/borg/archive.py @@ -2269,7 +2269,7 @@ class ArchiveRecreater: overwrite = self.recompress if self.recompress and not self.always_recompress and chunk_id in self.cache.chunks: # Check if this chunk is already compressed the way we want it - old_meta = self.repo_objs.parse_meta(chunk_id, self.repository.get(chunk_id)) + old_meta = self.repo_objs.parse_meta(chunk_id, self.repository.get(chunk_id, read_data=False)) compr_hdr = bytes((old_meta["ctype"], old_meta["clevel"])) compressor_cls, level = Compressor.detect(compr_hdr) if ( diff --git a/src/borg/remote.py b/src/borg/remote.py index de0002ba2..dd7861122 100644 --- a/src/borg/remote.py +++ b/src/borg/remote.py @@ -1258,7 +1258,6 @@ class RepositoryCache(RepositoryNoCache): shutil.rmtree(self.basedir) def get_many(self, keys, read_data=True, cache=True): - # TODO: this currently always requests the full chunk from self.repository (read_data=True). # It could use different cache keys depending on read_data and cache full vs. meta-only chunks. unknown_keys = [key for key in keys if self.prefixed_key(key, complete=read_data) not in self.cache] repository_iterator = zip(unknown_keys, self.repository.get_many(unknown_keys, read_data=read_data)) From 5afe94883ad710c84edabdd302de1a44d2654a1a Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 8 Sep 2022 21:52:19 +0200 Subject: [PATCH 13/14] segment entry payload, metadata length: 16bit is enough guess we will not have much more than size, csize, psize, ctype, clevel. --- src/borg/repoobj.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/borg/repoobj.py b/src/borg/repoobj.py index 7278e6efc..14a575de5 100644 --- a/src/borg/repoobj.py +++ b/src/borg/repoobj.py @@ -5,7 +5,7 @@ from .compress import Compressor, LZ4_COMPRESSOR, get_compressor class RepoObj: - meta_len_hdr = Struct(" bytes: From c8830cde441d95fff7cfd93ca184cd60ca4078fa Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 8 Sep 2022 21:52:32 +0200 Subject: [PATCH 14/14] update docs --- docs/internals/data-structures.rst | 63 +++++++++++++++++++++-------- docs/internals/encryption-aead.odg | Bin 22708 -> 27294 bytes docs/internals/encryption-aead.png | Bin 139662 -> 149428 bytes 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/docs/internals/data-structures.rst b/docs/internals/data-structures.rst index f337eb18a..fc3ba97ce 100644 --- a/docs/internals/data-structures.rst +++ b/docs/internals/data-structures.rst @@ -77,7 +77,7 @@ don't have a particular meaning (except for the Manifest_). Normally the keys are computed like this:: - key = id = id_hash(unencrypted_data) + key = id = id_hash(plaintext_data) # plain = not encrypted, not compressed, not obfuscated The id_hash function depends on the :ref:`encryption mode `. @@ -98,15 +98,15 @@ followed by a number of log entries. Each log entry consists of (in this order): * crc32 checksum (uint32): - for PUT2: CRC32(size + tag + key + digest) - - for PUT: CRC32(size + tag + key + data) + - for PUT: CRC32(size + tag + key + payload) - for DELETE: CRC32(size + tag + key) - for COMMIT: CRC32(size + tag) * size (uint32) of the entry (including the whole header) * tag (uint8): PUT(0), DELETE(1), COMMIT(2) or PUT2(3) * key (256 bit) - only for PUT/PUT2/DELETE -* data (size - 41 bytes) - only for PUT -* xxh64 digest (64 bit) = XXH64(size + tag + key + data) - only for PUT2 -* data (size - 41 - 8 bytes) - only for PUT2 +* payload (size - 41 bytes) - only for PUT +* xxh64 digest (64 bit) = XXH64(size + tag + key + payload) - only for PUT2 +* payload (size - 41 - 8 bytes) - only for PUT2 PUT2 is new since repository version 2. For new log entries PUT2 is used. PUT is still supported to read version 1 repositories, but not generated any more. @@ -116,7 +116,7 @@ version 2+. Those files are strictly append-only and modified only once. When an object is written to the repository a ``PUT`` entry is written -to the file containing the object id and data. If an object is deleted +to the file containing the object id and payload. If an object is deleted a ``DELETE`` entry is appended with the object id. A ``COMMIT`` tag is written when a repository transaction is @@ -130,13 +130,42 @@ partial/uncommitted transaction. The size of individual segments is limited to 4 GiB, since the offset of entries within segments is stored in a 32-bit unsigned integer in the repository index. -Objects -~~~~~~~ +Objects / Payload structure +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +All data (the manifest, archives, archive item stream chunks and file data +chunks) is compressed, optionally obfuscated and encrypted. This produces some +additional metadata (size and compression information), which is separately +serialized and also encrypted. + +See :ref:`data-encryption` for a graphic outlining the anatomy of the encryption in Borg. +What you see at the bottom there is done twice: once for the data and once for the metadata. + +An object (the payload part of a segment file log entry) must be like: + +- length of encrypted metadata (16bit unsigned int) +- encrypted metadata (incl. encryption header), when decrypted: + + - msgpacked dict with: + + - ctype (compression type 0..255) + - clevel (compression level 0..255) + - csize (overall compressed (and maybe obfuscated) data size) + - psize (only when obfuscated: payload size without the obfuscation trailer) + - size (uncompressed size of the data) +- encrypted data (incl. encryption header), when decrypted: + + - compressed data (with an optional all-zero-bytes obfuscation trailer) + +This new, more complex repo v2 object format was implemented to be able to efficiently +query the metadata without having to read, transfer and decrypt the (usually much bigger) +data part. + +The metadata is encrypted to not disclose potentially sensitive information that could be +used for e.g. fingerprinting attacks. + +The compression `ctype` and `clevel` is explained in :ref:`data-compression`. -All objects (the manifest, archives, archive item streams chunks and file data -chunks) are encrypted and/or compressed. See :ref:`data-encryption` for a -graphic outlining the anatomy of an object in Borg. The `type` for compression -is explained in :ref:`data-compression`. Index, hints and integrity ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -855,7 +884,7 @@ For each borg invocation, a new sessionkey is derived from the borg key material and the 48bit IV starts from 0 again (both ciphers internally add a 32bit counter to our IV, so we'll just count up by 1 per chunk). -The chunk layout is best seen at the bottom of this diagram: +The encryption layout is best seen at the bottom of this diagram: .. figure:: encryption-aead.png :figwidth: 100% @@ -954,14 +983,14 @@ representation of the repository id. Compression ----------- -Borg supports the following compression methods, each identified by a type -byte: +Borg supports the following compression methods, each identified by a ctype value +in the range between 0 and 255 (and augmented by a clevel 0..255 value for the +compression level): - none (no compression, pass through data 1:1), identified by 0x00 - lz4 (low compression, but super fast), identified by 0x01 - zstd (level 1-22 offering a wide range: level 1 is lower compression and high - speed, level 22 is higher compression and lower speed) - since borg 1.1.4, - identified by 0x03 + speed, level 22 is higher compression and lower speed) - identified by 0x03 - zlib (level 0-9, level 0 is no compression [but still adding zlib overhead], level 1 is low, level 9 is high compression), identified by 0x05 - lzma (level 0-9, level 0 is low, level 9 is high compression), identified diff --git a/docs/internals/encryption-aead.odg b/docs/internals/encryption-aead.odg index a28a63b21010ae97d80d2e46b32409d951ef7478..6b9153a334fe86ce3c6d30bf1e3624571294a45c 100644 GIT binary patch literal 27294 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 KP6sxB)cy})cn_Qa literal 22708 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 A-v9sr diff --git a/docs/internals/encryption-aead.png b/docs/internals/encryption-aead.png index 1bcfbd17824caafceb51effd5813e97f76dbb567..5b062c844653425b3182a64e8a47c265fe251c05 100644 GIT binary patch literal 149428 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 #Ha-<>AO5cMfaCmy~H+O&LvXysw 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�}7R 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