summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Goergens <matthias.goergens@gmail.com>2023-03-27 18:43:26 +0800
committerNikolaus Rath <Nikolaus@rath.org>2023-03-28 21:31:18 +0100
commit055478f11dd4f3d46653f89ffe63f7c5a400b114 (patch)
tree1aafb895d6870b701143b3cccbab33c813408e4f
parentb509964f7cc7260c16abb41bf4ccabdce08c6bce (diff)
downloadfuse-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.c47
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);