diff --git a/src/borg/archiver.py b/src/borg/archiver.py index 098208167..ea70f412a 100644 --- a/src/borg/archiver.py +++ b/src/borg/archiver.py @@ -347,6 +347,20 @@ def do_transfer(self, args, *, def upgrade_item(item): """upgrade item as needed, get rid of legacy crap""" + if item.get('hardlink_master', True) and 'source' not in item and hardlinkable(item.mode): + item._dict['hlid'] = hlid = hashlib.sha256(item._dict['path']) + hardlink_masters[hlid] = (item._dict.get('chunks'), item._dict.get('chunks_healthy')) + elif 'source' in item and hardlinkable(item.mode): + item._dict['hlid'] = hlid = hashlib.sha256(item._dict['source']) + chunks, chunks_healthy = hardlink_masters.get(hlid, (None, None)) + if chunks is not None: + item._dict['chunks'] = chunks + for chunk_id, _, _ in chunks: + cache.chunk_incref(chunk_id, archive.stats) + if chunks_healthy is not None: + item._dict['chunks_healthy'] = chunks + item._dict.pop('source') # not used for hardlinks any more, replaced by hlid + item._dict.pop('hardlink_master', None) # not used for hardlinks any more, replaced by hlid item._dict.pop('acl', None) # remove remnants of bug in attic <= 0.13 item.get_size(memorize=True) # if not already present: compute+remember size for items with chunks return item @@ -371,6 +385,7 @@ def upgrade_compressed_chunk(chunk): else: if not dry_run: print(f"{name}: copying archive to destination repo...") + hardlink_masters = {} 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 for item in other_archive.iter_items(): diff --git a/src/borg/constants.py b/src/borg/constants.py index 0b2ef16a1..13eb8bd23 100644 --- a/src/borg/constants.py +++ b/src/borg/constants.py @@ -1,5 +1,5 @@ # this set must be kept complete, otherwise the RobustUnpacker might malfunction: -ITEM_KEYS = frozenset(['path', 'source', 'rdev', 'chunks', 'chunks_healthy', 'hardlink_master', +ITEM_KEYS = frozenset(['path', 'source', 'rdev', 'chunks', 'chunks_healthy', 'hardlink_master', 'hlid', 'mode', 'user', 'group', 'uid', 'gid', 'mtime', 'atime', 'ctime', 'birthtime', 'size', 'xattrs', 'bsdflags', 'acl_nfs4', 'acl_access', 'acl_default', 'acl_extended', 'part']) diff --git a/src/borg/item.pyx b/src/borg/item.pyx index 48debf183..0b2598ffe 100644 --- a/src/borg/item.pyx +++ b/src/borg/item.pyx @@ -181,7 +181,8 @@ class Item(PropDict): # compatibility note: this is a new feature, in old archives size will be missing. size = PropDict._make_property('size', int) - hardlink_master = PropDict._make_property('hardlink_master', bool) + hlid = PropDict._make_property('hlid', bytes) # hard link id: same value means same hard link. + hardlink_master = PropDict._make_property('hardlink_master', bool) # legacy chunks = PropDict._make_property('chunks', (list, type(None)), 'list or None') chunks_healthy = PropDict._make_property('chunks_healthy', (list, type(None)), 'list or None')