diff options
author | Matthias Goergens <matthias.goergens@gmail.com> | 2023-03-27 18:43:26 +0800 |
---|---|---|
committer | Nikolaus Rath <Nikolaus@rath.org> | 2023-03-28 21:31:18 +0100 |
commit | 055478f11dd4f3d46653f89ffe63f7c5a400b114 (patch) | |
tree | 1aafb895d6870b701143b3cccbab33c813408e4f | |
parent | b509964f7cc7260c16abb41bf4ccabdce08c6bce (diff) | |
download | fuse-055478f11dd4f3d46653f89ffe63f7c5a400b114.tar.gz |
Fix `auto_unmount` to work without `allow_other`
In
https://github.com/libfuse/libfuse/blob/77d662459a0fcdf358d515477d33795837e859d5/util/fusermount.c#L1219
`open` is executed as root which does not have access to the mount
point if `allow_other` was not used and the real user id is not 0. Since
`allow_other` usually cannot be specified by unprivileged users,
`auto_unmount` has no effect for unprivileged users.
In this commit, we work around this limitation:
We first try to open the mountpoint as root, and if we get `EACCES`, we
retry as the user who started fusermount, and see if we get `ENOTCONN`.
In my testing, I found that `setfsuid` and `setfsgid` don't work to get
around the lack of `allow_other`. (Sorry, I don't know enough about the
Linux kernel to tell whether that's significant.) As a workaround, I
decided to use `setresuid` and `setresgid` in a forked child process,
and communicate via its exit status.
Please give feedback on correctness, style and suggest tests.
Fixes https://github.com/libfuse/libfuse/issues/586
-rw-r--r-- | util/fusermount.c | 47 |
1 files changed, 46 insertions, 1 deletions
diff --git a/util/fusermount.c b/util/fusermount.c index 6e72f0d..f752bfd 100644 --- a/util/fusermount.c +++ b/util/fusermount.c @@ -1190,6 +1190,40 @@ static int send_fd(int sock_fd, int fd) return 0; } +static int mnt_is_ENOTCONN_for_owner(const char *mnt) +{ + int pid = fork(); + if(pid == -1) { + perror("fuse: mnt_is_ENOTCONN_for_owner can't fork"); + _exit(EXIT_FAILURE); + } else if(pid == 0) { + uid_t uid = getuid(); + gid_t gid = getgid(); + if(setresgid(gid, gid, gid) == -1) { + perror("fuse: can't set resgid"); + _exit(EXIT_FAILURE); + } + if(setresuid(uid, uid, uid) == -1) { + perror("fuse: can't set resuid"); + _exit(EXIT_FAILURE); + } + + int fd = open(mnt, O_RDONLY); + if(fd == -1 && errno == ENOTCONN) + _exit(EXIT_SUCCESS); + else + _exit(EXIT_FAILURE); + } else { + int status; + int res = waitpid(pid, &status, 0); + if (res == -1) { + perror("fuse: waiting for child failed"); + _exit(EXIT_FAILURE); + } + return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS; + } +} + /* The parent fuse process has died: decide whether to auto_unmount. * * In the normal case (umount or fusermount -u), the filesystem @@ -1225,10 +1259,21 @@ static int should_auto_unmount(const char *mnt, const char *type) goto out; fd = open(mnt, O_RDONLY); + if (fd != -1) { close(fd); } else { - result = errno == ENOTCONN; + switch(errno) { + case ENOTCONN: + result = 1; + break; + case EACCES: + result = mnt_is_ENOTCONN_for_owner(mnt); + break; + default: + result = 0; + break; + } } out: free(copy); |