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",

Reply via email to