Repository: borgbackup/borg

Issue:
fuse: pass default_options
llfuse's documentation recommends using `llfuse.default_options`.

I've tried this, and it appears to work.

In particular "default_permissions" might be useful for performance reasons.

I'd be happy to provide a PR for this, if you agree that it makes sense.


Discussion:
```
>>> import llfuse
>>> llfuse.default_options
frozenset({'default_permissions', 'no_splice_read', 'nonempty', 'splice_move', 'splice_write', 'big_writes'}) 
```

Not sure what they all mean, whether they make sense for us or since when default_options is available in llfuse.

So, double check that please and then do a PR. :)
`default_options` is there since release-0.42~61(1d8c8e0): https://github.com/python-llfuse/python-llfuse/commit/1d8c8e0b40a09496074f76e6d0da036422eec10b.  Do you want to bump the required version, or should be duplicate it for older versions?

See https://github.com/python-llfuse/python-llfuse/pull/12 about the documentation for its values.

`default_permissions` seems to be crucial: without it, I can access a mount with `-o uid=1001,umask=077` as user 1000!
Hmm, somehow embarassing that the default_options must be explicitly given and are not the default(*)...

Same for the default_permissions.

(*) in fact, they are the default value for the init argument. but that's not practically helpful as in many cases one needs to pass other options there, so the argument default is not used.
@blueyed can you evaluate whether this means a security or performance issue for:
- borg 1.1.x (x < 6) or borg 1.0.y - no uid, gid, umask options implemented there
- borg >= 1.1.6 (with new uid, gid, umask options)
related PRs: #3769 (#3843 is the fwd port to master, no backport to 1.0 yet)
@blueyed do not bump the required version, but use something like:

```
default_options = frozenset(...)  # copied from llfuse, to support pre-0.42 llfuse
default_options = getattr(llfuse, 'default_options', default_options)
...
```
@blueyed you said `In particular "default_permissions" might be useful for performance reasons.`.

Did you mean security reasons? If not, can you explain the performance impact?
By now I would say "security reasons", but initially I've read something along that otherwise the program would have to do checks, which might be slower then.
borg's fuse driver does not do any checks however, so it would be slower probably, but for good reasons.  The other options should improve performance though I guess.
considering the PRs have been merged into master and 1.1-maint, guess we can close this?
ehrm, looks like I misinterpreted my own comment https://github.com/borgbackup/borg/issues/3903#issuecomment-397272669 - "related" did not mean this issue has been fixed there, just related stuff...

reopening...

Resolving patch:
diff --git a/src/borg/archiver.py b/src/borg/archiver.py
--- a/src/borg/archiver.py
+++ b/src/borg/archiver.py
@@ -2760,14 +2760,18 @@ def define_archive_filters_group(subparser, *, sort_by=True, first_last=True):
         To allow a regular user to use fstab entries, add the ``user`` option:
         ``/path/to/repo /mnt/point fuse.borgfs defaults,noauto,user 0 0``
 
-        For mount options, see the fuse(8) manual page. Additional mount options
-        supported by borg:
+        For FUSE configuration and mount options, see the mount.fuse(8) manual page.
+
+        Additional mount options supported by borg:
 
         - versions: when used with a repository mount, this gives a merged, versioned
           view of the files in the archives. EXPERIMENTAL, layout may change in future.
         - allow_damaged_files: by default damaged files (where missing chunks were
           replaced with runs of zeros by borg check ``--repair``) are not readable and
           return EIO (I/O error). Set this option to read such files.
+        - ignore_permissions: for security reasons the "default_permissions" mount
+          option is internally enforced by borg. "ignore_permissions" can be given to
+          not enforce "default_permissions".
 
         The BORG_MOUNT_DATA_CACHE_ENTRIES environment variable is meant for advanced users
         to tweak the performance. It sets the number of cached data chunks; additional
diff --git a/src/borg/fuse.py b/src/borg/fuse.py
--- a/src/borg/fuse.py
+++ b/src/borg/fuse.py
@@ -308,9 +308,20 @@ def pop_option(options, key, present, not_present, wanted_type, int_base=0):
             else:
                 return not_present
 
-        options = ['fsname=borgfs', 'ro']
+        # default_permissions enables permission checking by the kernel. Without
+        # this, any umask (or uid/gid) would not have an effect and this could
+        # cause security issues if used with allow_other mount option.
+        # When not using allow_other or allow_root, access is limited to the
+        # mounting user anyway.
+        options = ['fsname=borgfs', 'ro', 'default_permissions']
         if mount_options:
             options.extend(mount_options.split(','))
+        ignore_permissions = pop_option(options, 'ignore_permissions', True, False, bool)
+        if ignore_permissions:
+            # in case users have a use-case that requires NOT giving "default_permissions",
+            # this is enabled by the custom "ignore_permissions" mount option which just
+            # removes "default_permissions" again:
+            pop_option(options, 'default_permissions', True, False, bool)
         self.allow_damaged_files = pop_option(options, 'allow_damaged_files', True, False, bool)
         self.versions = pop_option(options, 'versions', True, False, bool)
         self.uid_forced = pop_option(options, 'uid', None, None, int)


Test patch: