[PATCH 03/10] unix_io: allow passing /dev/fd/XXX paths to the unixfd IO manager

Darrick J. Wong
2026-04-22T23:24:20Z

Commit 4ccf9e4fe165cf created a "unixfd" IO manager that allows someone
to choose the unixfd IO manager and then mount a filesystem from an
existing file descriptor by passing a string with the fd number as the
"device" name to ext2fs_open().

That was an unfortunate choice of naming, however, because that could
be mistaken for a relative path to a file whose name is an integer
number. Let's improve this by allowing callers to pass /dev/fd/XX
as the filesystem device name. The upcoming fuse4fs service patches
will employ this method to open a filesystem on a block device fd passed
into the secure container from a mount helper.

Cc: <linux-ext4@vger.kernel.org> # v1.43.2
Fixes: 4ccf9e4fe165cf ("libext2fs: add unixfd_io_manager")
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
lib/ext2fs/unix_io.c | 30 +++++++++++++++++++++++++++++-
1 file changed, 29 insertions(+), 1 deletion(-)

diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
index 79bc9219f9515b..a9b1fac62a0250 100644
--- a/lib/ext2fs/unix_io.c
+++ b/lib/ext2fs/unix_io.c
@@ -67,6 +67,7 @@
#ifdef HAVE_SYS_FILE_H
#include <sys/file.h>
#endif
+#include <limits.h>

#if defined(__linux__) && defined(_IO) && !defined(BLKROGET)
#define BLKROGET _IO(0x12, 94) /* Get read-only status (0 = read_write). */
@@ -1148,13 +1149,40 @@ static errcode_t unix_open_channel(const char *name, int fd,
return retval;
}

+#define DEV_FD_PATH "/dev/fd/"
+#define DEV_FD_PATHLEN (sizeof(DEV_FD_PATH) - 1)
+
+static int possible_unixfd_pathname(const char *path)
+{
+ return strncmp(DEV_FD_PATH, path, DEV_FD_PATHLEN) == 0;
+}
+
static errcode_t unixfd_open(const char *str_fd, int flags,
io_channel *channel)
{
int fd;
int fd_flags;

- fd = atoi(str_fd);
+ /*
+ * The caller should provide a path in the form "/dev/fd/XX",
+ * but the shorthand form "XX" is allowed for legacy reasons.
+ */
+ if (possible_unixfd_pathname(str_fd)) {
+ char *endptr;
+ long maybe_fd;
+
+ errno = 0;
+ maybe_fd = strtol(str_fd + DEV_FD_PATHLEN, &endptr, 10);
+ if (errno)
+ return errno;
+ if (*endptr != 0)
+ return EINVAL;
+ if (maybe_fd < 0 || maybe_fd > INT_MAX)
+ return EINVAL;
+ fd = maybe_fd;
+ } else {
+ fd = atoi(str_fd);
+ }
#if defined(HAVE_FCNTL)
fd_flags = fcntl(fd, F_GETFL);
if (fd_flags == -1)