Package: libfuse2 Version: 2.9.9-6+b1 Tags: patch upstream fixed-upstream Hello Laszlo,
I have a slightly unusual request. I know libfuse 2.x is a legacy in maintenance mode slated for removal eventually. Unfortunately, 2/3 of fuse users still use the 2.x branch so it seems like we will have to support it a bit longer than expected. You may have heared about Linux and namespaces and this is an area where fuse < 3.3 falls short. While we think of mount as a privileged operation in need of a setuid helper, you can perform an unprivileged mount operation on standard Debian systems by employing user and mount namespaces now. Getting there is a bit non-trivial, so let me explain how it works with fuse >= 3.3. Typically, the goal is to perform the mount operation in a different namespaces from the namespace running the fuse userspace driver. Currently, one invokes a fuse driver with a mount point and it first tries to mount directly and failing that executes a setuid helper process fusermount to perform that operation with root rights. Both happen in the same namespace as the driver process, which is very much not what we want here. When launching the fusermount process, it opens /dev/fuse, mounts it and communicates the fuse connection via a unix domain socket using SCM_RIGHTS back to the driver process. So what we really need here is a way of passing such a fuse connection to a fuse driver and having it not perform the mount itself (neither directly nor via fusermount). This need was recognized and since fuse 3.3, the path /dev/fd/N is treated differently when passed as a mountpoint. Upon seeing such a path, it'll skip all mounting and just use the referenced file descriptor. Thus a process in a namespace can open a fuse connection there, mount it and send back the file descriptor (again using SCM_RIGHTS) to some other process outside the namespace that ultimately launches the fuse driver while inheriting that file descriptor. Unfortunately, this currently requires fuse >= 3.3 and e.g. fuse2fs from e2fsprogs still uses fuse 2.x. Last time Ted Ts'o was inquired about porting to 3.x, he was unenthusiastic and when I spent a look, it is quite non-trivial as the API changed a lot. It's not just fuse2fs that is affected but still 2/3 of drivers. On the flip side, backporting this function into fuse 2.x was relatively easy and the chances of breaking something are fairly low as that path is intentionally so special that people will only use it for this very functionality. So I think we should backport this into fuse 2.x. This is not to say that we should stop porting drivers to the 3.x API. It also slightly improves compatibility between 2.x and 3.x, so that seems like a sane trade-off to me. Do you agree? I'm attaching a patch ready to be dropped into debian/patches for your convenience. Helmut
From: Helmut Grohne <hel...@subdivi.de> Subject: backport support for /dev/fd/NN Origin: https://github.com/libfuse/libfuse/commit/64e11073b9347fcf9c6d1eea143763ba9e946f70 Forwarded: not-needed This is a backport of 64e11073b9347fcf9c6d1eea143763ba9e946f70 ("Allow passing `/dev/fuse` file descriptor from parent process") and enables fuse 2.9 to perform the unprivileged part in much the same way as fuse >= 3.3 does. Since fuse changed significantly, this is a reimplementation of the original idea on the older code base. An alternative to applying this patch is porting users of this library to 3.x. In reality, progress has been slow and very many packages still use 2.x. While such porting is a good thing in principle, it also is a huge task when a relatively simple alternative is available. --- fuse-2.9.9.orig/lib/mount.c +++ fuse-2.9.9/lib/mount.c @@ -575,6 +575,15 @@ return 0; } +static int parse_fuse_fd(const char *mountpoint) +{ + int fd = -1, len = 0; + + if (sscanf(mountpoint, "/dev/fd/%u%n", &fd, &len) == 1 && len == strlen(mountpoint)) + return fd; + return -1; +} + int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) { struct mount_opts mo; @@ -604,6 +613,15 @@ if (mo.mtab_opts && fuse_opt_add_opt(&mnt_opts, mo.mtab_opts) == -1) goto out; + res = parse_fuse_fd(mountpoint); + if (res >= 0) { + if (fcntl(res, F_GETFD) == -1) { + fprintf(stderr, "fuse: Invalid filesystem /dev/fd/%u\n", res); + res = -1; + } + goto out; + } + res = fuse_mount_sys(mountpoint, &mo, mnt_opts); if (res == -2) { if (mo.fusermount_opts && --- fuse-2.9.9.orig/lib/helper.c +++ fuse-2.9.9/lib/helper.c @@ -105,7 +105,11 @@ case FUSE_OPT_KEY_NONOPT: if (!hopts->mountpoint) { + int fd, len=0; char mountpoint[PATH_MAX]; + /* fuse_kern_mount matches /dev/fd/N */ + if (sscanf(arg, "/dev/fd/%u%n", &fd, &len) == 1 && len == strlen(arg)) + return fuse_opt_add_opt(&hopts->mountpoint, arg); if (realpath(arg, mountpoint) == NULL) { fprintf(stderr, "fuse: bad mount point `%s': %s\n",