Bug 1761563 - mount_local_access() in lib/fuse.c wrongly returns -EACCES (affects guestmount)
Summary: mount_local_access() in lib/fuse.c wrongly returns -EACCES (affects guestmount)
Keywords:
Status: NEW
Alias: None
Product: Virtualization Tools
Classification: Community
Component: libguestfs
Version: unspecified
Hardware: Unspecified
OS: Unspecified
unspecified
unspecified
Target Milestone: ---
Assignee: Richard W.M. Jones
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2019-10-14 17:51 UTC by Daniel Haid
Modified: 2025-10-17 12:52 UTC (History)
3 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed: 2025-10-17 00:10:58 UTC
Embargoed:


Attachments (Terms of Use)

Description Daniel Haid 2019-10-14 17:51:32 UTC
My expectation is that when any user (possibly unprivileged) uses guestmount he can access everything inside without any permission checks unless "-o default_permissions" is not given as an option.

This works fine for read(), but not for chdir(). Reproduce as follows:

unprivileged@testbox:~$ guestmount -v -x  --format=raw -a test.fs -m /dev/sda mnt

This will mount test.fs in mnt and give verbose information. Then:

unprivileged@testbox:~$ cd mnt
unprivileged@testbox:~$ mkdir test_dir
unprivileged@testbox:~$ chown root:root test_dir
unprivileged@testbox:~$ chmod 700 test_dir
unprivileged@testbox:~$ ls test_dir

Everything worked perfectly so far (as it should). Note in particular that the last command was able to read the contents of test_dir.

unprivileged@testbox:~$ cd test_dir
-bash: cd: test_dir/: Permission denied

This does not work. Output from guestmount:

libguestfs: /test_dir: testing access mask X_OK: caller UID:GID = 998:998, file UID:GID = 0:0, file mode = 40700, result = EACCESS

This can only come from the function mount_local_access() in lib/fuse.c

It seems to do its own permission checking. However, the fuse documentation says that the access function (here: mount_local_access) from fuse_operations is not called if the 'default_permissions' mount option is given.

So I do not understand what is going here. If -o default_permissions is given, the function is not called, but if it is not given we do not want to restrict the access anyway.

(It seems that this should be used if a fuse filesystem wants to implement *its own* particular access controls.)

My proposed fix is to remove the function completely or always return 0.

Indeed I found this old discussion where removing it completely was tried, but some test failed:

https://www.redhat.com/archives/libguestfs/2014-June/msg00080.html

It would be interesting to know what actually happened there.

Tested on version 1.40.2, but the code seems to be the same in the development tree.

Comment 1 Richard W.M. Jones 2019-10-14 18:43:11 UTC
As was discussed over and over again on IRC, please discuss this with
the upstream fuse community to find out what the correct course of
action is.

Comment 2 Daniel Haid 2019-10-14 18:48:33 UTC
What question or input exactly would you like to have from the fuse community here?

Comment 3 Richard W.M. Jones 2019-10-14 18:50:07 UTC
Some sort of technical explanation of why the .access method exists
if it's not useful, I guess.  Or why FUSE modules are supposed to be
parsing FUSE options?  I want to hear from the FUSE community what
the correct thing to do is.  This is hardly unreasonable.

Comment 4 pidpawel 2020-06-15 16:58:57 UTC
Hi!
First of all I have to say that I've not contacted the libfuse team and all I'm saying is based on my own research and thoughts.

It seems that the code actually using the `default_permissions` option resides here: https://github.com/torvalds/linux/blob/b3a9e3b9622ae10064826dccb4f7a52bd88c7407/fs/fuse/dir.c#L1139
So kernel will either (when `default_permissions` is supplied) issue a FUSE `getattr` and do it's own, regular file permission check or (when that option is not supplied), will forward the request to actual FUSE module to do whatever it wants.

Libfuse side: https://github.com/libfuse/libfuse/blob/4322cffbe992aa0768f5ce310ef18f5d5340485e/include/fuse.h#L588
To me it sounds like there's no additional meaning given to that method, other than "regular file systems had that option, you can have it too".

Let's look at that syscall description then: https://www.man7.org/linux/man-pages/man2/access.2.html
The key element from that part here is that `access` syscall is checked using the real UID/GID so programs started with setuid bit can check whether the user on behalf of whom they act does have permissions for that file too. 

That last bit sounds like the real use case for the `access` method in general. In particular though… I can't think of reason why it would be useful in `libguestfs`'s case. As per the example in the first message of this bug report - `libguestfs` neither checks nor enforces the user permissions during regular operations so I don't see a reason why it should report otherwise in the query type methods. Proposal to make it always return 0 seems reasonable to me.

Final bit of reasoning and my particular use case: I'm trying to run `debootstrap`/`multistrap` onto a `guestmount`ed file system (as a non-root user) and I'm getting exactly the same issue as in the first message - they're able to do all sorts of things with files, including changing the permissions, owners, etc,… but they do some (unreasonable in this case but nevertheless) access checks… which they fail and refuse to continue, which seems like a very unfortunate reason to fail (as the user mounting the file system already has all the permissions in the host system to modify the mounted files system's image).

P.S. Thanks for all the hard work!

Comment 5 Daniel Haid 2020-06-21 11:06:45 UTC
I have written to fuse-devel a few months ago, and there were some answers.

See: https://sourceforge.net/p/fuse/mailman/message/36785025/

See in particular the last message by Miklos Szeredi, who says:

   "When in doubt look at sshfs."

   And all sshfs' access function does is check X_OK and if there are
   *any* executable bits at all on a regular file:

       if (mask & X_OK) {
           err = sshfs.op->getattr(path, &stbuf, NULL);
           if (!err) {
               if (S_ISREG(stbuf.st_mode) &&
                   !(stbuf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
                   err = -EACCES;
           }
       }

I now believe this is the correct thing that libguestfs should also do.

Filtering out the exec bit on regular files if there is not *any*
executable bit makes them appear correctly in file managers and so on.

Maybe this also fixes this: https://www.redhat.com/archives/libguestfs/2014-June/msg00080.html

So I propose to remove the whole "if (fuse->uid != 0)" block in lib/fuse.c and replace it by

if (mask & X_OK)
  if (S_ISREG(statbuf.st_mode) &&
      !(statbuf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
    ok = 0;

Comment 6 Red Hat Bugzilla 2025-10-17 00:10:58 UTC
This product has been discontinued or is no longer tracked in Red Hat Bugzilla.

Comment 7 Alasdair Kergon 2025-10-17 12:52:13 UTC
Reopening because Virtualization Tools has not been discontinued.


Note You need to log in before you can comment on or make changes to this bug.