diff --git a/ChangeLog.rst b/ChangeLog.rst index 3d5c05b..65f57d7 100644 --- a/ChangeLog.rst +++ b/ChangeLog.rst @@ -8,6 +8,10 @@ Unreleased Changes * The description of the FUSE_CAP_READDIRPLUS_AUTO flag has been improved. +* Allow open `/dev/fuse` file descriptors to be passed via mountpoints of the + special format `/dev/fd/%u`. This allows mounting to be handled by the parent + so the FUSE filesystem process can run fully unprivileged. + libfuse 3.2.6 (2018-08-31) ========================== diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index e6e3d8d..844e797 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -16,6 +16,7 @@ #include "fuse_kernel.h" #include "fuse_opt.h" #include "fuse_misc.h" +#include "mount_util.h" #include #include @@ -2891,6 +2892,24 @@ int fuse_session_mount(struct fuse_session *se, const char *mountpoint) close(fd); } while (fd >= 0 && fd <= 2); + /* + * To allow FUSE daemons to run without privileges, the caller may open + * /dev/fuse before launching the file system and pass on the file + * descriptor by specifying /dev/fd/N as the mount point. Note that the + * parent process takes care of performing the mount in this case. + */ + fd = fuse_mnt_parse_fuse_fd(mountpoint); + if (fd != -1) { + if (fcntl(fd, F_GETFD) == -1) { + fprintf(stderr, + "fuse: Invalid file descriptor /dev/fd/%u\n", + fd); + return -1; + } + se->fd = fd; + return 0; + } + /* Open channel */ fd = fuse_kern_mount(mountpoint, se->mo); if (fd == -1) @@ -2916,9 +2935,11 @@ int fuse_session_fd(struct fuse_session *se) void fuse_session_unmount(struct fuse_session *se) { - fuse_kern_unmount(se->mountpoint, se->fd); - free(se->mountpoint); - se->mountpoint = NULL; + if (se->mountpoint != NULL) { + fuse_kern_unmount(se->mountpoint, se->fd); + free(se->mountpoint); + se->mountpoint = NULL; + } } #ifdef linux diff --git a/lib/helper.c b/lib/helper.c index 07cef81..e1de362 100644 --- a/lib/helper.c +++ b/lib/helper.c @@ -15,6 +15,7 @@ #include "fuse_misc.h" #include "fuse_opt.h" #include "fuse_lowlevel.h" +#include "mount_util.h" #include #include @@ -147,7 +148,11 @@ static int fuse_helper_opt_proc(void *data, const char *arg, int key, switch (key) { case FUSE_OPT_KEY_NONOPT: if (!opts->mountpoint) { - char mountpoint[PATH_MAX]; + if (fuse_mnt_parse_fuse_fd(arg) != -1) { + return fuse_opt_add_opt(&opts->mountpoint, arg); + } + + char mountpoint[PATH_MAX] = ""; if (realpath(arg, mountpoint) == NULL) { fprintf(stderr, "fuse: bad mount point `%s': %s\n", diff --git a/lib/mount_util.c b/lib/mount_util.c index 56ed85a..95e038f 100644 --- a/lib/mount_util.c +++ b/lib/mount_util.c @@ -352,3 +352,16 @@ int fuse_mnt_check_fuseblk(void) fclose(f); return 0; } + +int fuse_mnt_parse_fuse_fd(const char *mountpoint) +{ + int fd = -1; + int len = 0; + + if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 && + len == strlen(mountpoint)) { + return fd; + } + + return -1; +} diff --git a/lib/mount_util.h b/lib/mount_util.h index 55c6c5e..0ef0fbe 100644 --- a/lib/mount_util.h +++ b/lib/mount_util.h @@ -15,3 +15,4 @@ int fuse_mnt_umount(const char *progname, const char *abs_mnt, const char *rel_mnt, int lazy); char *fuse_mnt_resolve_path(const char *progname, const char *orig); int fuse_mnt_check_fuseblk(void); +int fuse_mnt_parse_fuse_fd(const char *mountpoint);