From 39170479aa8aff98d031224ca441cab8840c1642 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Thu, 15 Sep 2016 20:31:57 +0200 Subject: [PATCH] fuse: fix hardlinks in versions view, fixes #1599 --- src/borg/fuse.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/borg/fuse.py b/src/borg/fuse.py index a8ad62dc3..f20ae899c 100644 --- a/src/borg/fuse.py +++ b/src/borg/fuse.py @@ -15,7 +15,7 @@ logger = create_logger() from .archive import Archive -from .helpers import daemonize, safe_encode +from .helpers import daemonize from .item import Item from .lrucache import LRUCache @@ -136,6 +136,7 @@ def _create_dir(self, parent): def process_archive(self, archive, prefix=[]): """Build fuse inode hierarchy from archive metadata """ + self.file_versions = {} # for versions mode: original path -> version unpacker = msgpack.Unpacker() for key, chunk in zip(archive.metadata.items, self.repository.get_many(archive.metadata.items)): _, data = self.key.decrypt(key, chunk) @@ -155,7 +156,6 @@ def process_archive(self, archive, prefix=[]): self.items[inode] = item continue segments = prefix + os.fsencode(os.path.normpath(item.path)).split(b'/') - del item.path parent = 1 for segment in segments[:-1]: parent = self.process_inner(segment, parent) @@ -169,14 +169,31 @@ def file_version(item): ident = adler32(chunkid, ident) return ident + def make_versioned_name(name, version, add_dir=False): + if add_dir: + # add intermediate directory with same name as filename + path_fname = name.rsplit(b'/', 1) + name += b'/' + path_fname[-1] + return name + os.fsencode('.%08x' % version) + if self.versions and not is_dir: parent = self.process_inner(name, parent) version = file_version(item) if version is not None: - name += safe_encode('.%08x' % version) + # regular file, with contents - maybe a hardlink master + name = make_versioned_name(name, version) + self.file_versions[item.path] = version + del item.path # safe some space if 'source' in item and stat.S_ISREG(item.mode): - inode = self._find_inode(item.source, prefix) + # a hardlink, no contents, is the hardlink master + source = item.source + if self.versions: + # adjust source name with version + version = self.file_versions[source] + source = os.fsdecode(make_versioned_name(os.fsencode(source), version, add_dir=True)) + name = make_versioned_name(name, version) + inode = self._find_inode(source, prefix) item = self.cache.get(inode) item.nlink = item.get('nlink', 1) + 1 self.items[inode] = item