summaryrefslogtreecommitdiffstats
path: root/src/borg/fuse.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/borg/fuse.py')
-rw-r--r--src/borg/fuse.py70
1 files changed, 49 insertions, 21 deletions
diff --git a/src/borg/fuse.py b/src/borg/fuse.py
index eeb4c7f80..93530f892 100644
--- a/src/borg/fuse.py
+++ b/src/borg/fuse.py
@@ -14,7 +14,7 @@ from .logger import create_logger
logger = create_logger()
from .archive import Archive
-from .helpers import daemonize
+from .helpers import daemonize, safe_encode
from .item import Item
from .lrucache import LRUCache
@@ -50,6 +50,9 @@ class ItemCache:
class FuseOperations(llfuse.Operations):
"""Export archive as a fuse filesystem
"""
+
+ allow_damaged_files = False
+
def __init__(self, key, repository, manifest, archive, cached_repo):
super().__init__()
self._inode_count = 0
@@ -79,6 +82,32 @@ class FuseOperations(llfuse.Operations):
self.contents[1][os.fsencode(archive_name)] = archive_inode
self.pending_archives[archive_inode] = Archive(repository, key, manifest, archive_name)
+ def mount(self, mountpoint, mount_options, foreground=False):
+ """Mount filesystem on *mountpoint* with *mount_options*."""
+ options = ['fsname=borgfs', 'ro']
+ if mount_options:
+ options.extend(mount_options.split(','))
+ try:
+ options.remove('allow_damaged_files')
+ self.allow_damaged_files = True
+ except ValueError:
+ pass
+ llfuse.init(self, mountpoint, options)
+ if not foreground:
+ daemonize()
+
+ # If the file system crashes, we do not want to umount because in that
+ # case the mountpoint suddenly appears to become empty. This can have
+ # nasty consequences, imagine the user has e.g. an active rsync mirror
+ # job - seeing the mountpoint empty, rsync would delete everything in the
+ # mirror.
+ umount = False
+ try:
+ signal = fuse_main()
+ umount = (signal is None) # no crash and no signal -> umount request
+ finally:
+ llfuse.close(umount)
+
def process_archive(self, archive, prefix=[]):
"""Build fuse inode hierarchy from archive metadata
"""
@@ -88,6 +117,16 @@ class FuseOperations(llfuse.Operations):
unpacker.feed(data)
for item in unpacker:
item = Item(internal_dict=item)
+ try:
+ # This can happen if an archive was created with a command line like
+ # $ borg create ... dir1/file dir1
+ # In this case the code below will have created a default_dir inode for dir1 already.
+ inode = self._find_inode(safe_encode(item.path), prefix)
+ except KeyError:
+ pass
+ else:
+ self.items[inode] = item
+ continue
segments = prefix + os.fsencode(os.path.normpath(item.path)).split(b'/')
del item.path
num_segments = len(segments)
@@ -214,6 +253,15 @@ class FuseOperations(llfuse.Operations):
return self.getattr(inode)
def open(self, inode, flags, ctx=None):
+ if not self.allow_damaged_files:
+ item = self.get_item(inode)
+ if 'chunks_healthy' in item:
+ # Processed archive items don't carry the path anymore; for converting the inode
+ # to the path we'd either have to store the inverse of the current structure,
+ # or search the entire archive. So we just don't print it. It's easy to correlate anyway.
+ logger.warning('File has damaged (all-zero) chunks. Try running borg check --repair. '
+ 'Mount with allow_damaged_files to read damaged files.')
+ raise llfuse.FUSEError(errno.EIO)
return inode
def opendir(self, inode, ctx=None):
@@ -254,23 +302,3 @@ class FuseOperations(llfuse.Operations):
def readlink(self, inode, ctx=None):
item = self.get_item(inode)
return os.fsencode(item.source)
-
- def mount(self, mountpoint, extra_options, foreground=False):
- options = ['fsname=borgfs', 'ro']
- if extra_options:
- options.extend(extra_options.split(','))
- llfuse.init(self, mountpoint, options)
- if not foreground:
- daemonize()
-
- # If the file system crashes, we do not want to umount because in that
- # case the mountpoint suddenly appears to become empty. This can have
- # nasty consequences, imagine the user has e.g. an active rsync mirror
- # job - seeing the mountpoint empty, rsync would delete everything in the
- # mirror.
- umount = False
- try:
- signal = fuse_main()
- umount = (signal is None) # no crash and no signal -> umount request
- finally:
- llfuse.close(umount)