[PATCH 05/18] libfuse: add a lowlevel notification to add a new device to iomap

Darrick J. Wong
2025-09-16T00:43:06Z

Plumb in the pieces needed to attach block devices to a fuse+iomap mount
for use with iomap operations. This enables us to have filesystems
where the metadata could live somewhere else, but the actual file IO
goes to locally attached storage.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
include/fuse_kernel.h | 7 +++++++
include/fuse_lowlevel.h | 29 ++++++++++++++++++++++++++++
lib/fuse_lowlevel.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++
lib/fuse_versionscript | 2 ++
4 files changed, 87 insertions(+)

diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
index 111aef43e17245..b544d0700165e8 100644
--- a/include/fuse_kernel.h
+++ b/include/fuse_kernel.h
@@ -1122,6 +1122,13 @@ struct fuse_notify_retrieve_in {
uint64_t dummy4;
};

+#define FUSE_BACKING_TYPE_MASK (0xFF)
+#define FUSE_BACKING_TYPE_PASSTHROUGH (0)
+#define FUSE_BACKING_TYPE_IOMAP (1)
+#define FUSE_BACKING_MAX_TYPE (FUSE_BACKING_TYPE_IOMAP)
+
+#define FUSE_BACKING_FLAGS_ALL (FUSE_BACKING_TYPE_MASK)
+
struct fuse_backing_map {
int32_t fd;
uint32_t flags;
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index e2a81948811554..45e3cf8c79b91d 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -1998,6 +1998,35 @@ int fuse_lowlevel_notify_store(struct fuse_session *se, fuse_ino_t ino,
int fuse_lowlevel_notify_retrieve(struct fuse_session *se, fuse_ino_t ino,
size_t size, off_t offset, void *cookie);

+/**
+ * Attach an open file descriptor to a fuse+iomap mount. Currently must be
+ * a block device.
+ *
+ * Added in FUSE protocol version 7.99. If the kernel does not support
+ * this (or a newer) version, the function will return -ENOSYS and do
+ * nothing.
+ *
+ * @param se the session object
+ * @param fd file descriptor of an open block device
+ * @param flags flags for the operation; none defined so far
+ * @return positive nonzero device id on success, or negative errno on failure
+ */
+int fuse_lowlevel_iomap_device_add(struct fuse_session *se, int fd,
+ unsigned int flags);
+
+/**
+ * Detach an open file from a fuse+iomap mount. Must be a device id returned
+ * by fuse_lowlevel_iomap_device_add.
+ *
+ * Added in FUSE protocol version 7.99. If the kernel does not support
+ * this (or a newer) version, the function will return -ENOSYS and do
+ * nothing.
+ *
+ * @param se the session object
+ * @param device_id device index as returned by fuse_lowlevel_iomap_device_add
+ * @return 0 on success, or negative errno on failure
+ */
+int fuse_lowlevel_iomap_device_remove(struct fuse_session *se, int device_id);

/* ----------------------------------------------------------- *
* Utility functions *
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 89fcdb87db7dee..a3cc5491ff6a24 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -580,6 +580,55 @@ int fuse_passthrough_close(fuse_req_t req, int backing_id)
return ret;
}

+int fuse_lowlevel_iomap_device_add(struct fuse_session *se, int fd,
+ unsigned int flags)
+{
+ struct fuse_backing_map map = {
+ .fd = fd,
+ .flags = FUSE_BACKING_TYPE_IOMAP |
+ (flags & ~FUSE_BACKING_TYPE_MASK),
+ };
+ int ret;
+
+ if (!(se->conn.want_ext & FUSE_CAP_IOMAP))
+ return -ENOSYS;
+
+ ret = ioctl(se->fd, FUSE_DEV_IOC_BACKING_OPEN, &map);
+ if (ret == 0) {
+ /* not supposed to happen */
+ ret = -1;
+ errno = ERANGE;
+ }
+ if (ret < 0) {
+ int err = errno;
+
+ fuse_log(FUSE_LOG_ERR, "fuse: iomap_device_add: %s\n",
+ strerror(err));
+ return -err;
+ }
+
+ return ret;
+}
+
+int fuse_lowlevel_iomap_device_remove(struct fuse_session *se, int device_id)
+{
+ int ret;
+
+ if (!(se->conn.want_ext & FUSE_CAP_IOMAP))
+ return -ENOSYS;
+
+ ret = ioctl(se->fd, FUSE_DEV_IOC_BACKING_CLOSE, &device_id);
+ if (ret < 0) {
+ int err = errno;
+
+ fuse_log(FUSE_LOG_ERR, "fuse: iomap_device_remove: %s\n",
+ strerror(errno));
+ return -err;
+ }
+
+ return ret;
+}
+
int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f)
{
struct fuse_open_out arg;
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index eb4d2f350ec63c..e796100c5ee414 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -222,6 +222,8 @@ FUSE_3.99 {
global:
fuse_iomap_pure_overwrite;
fuse_reply_iomap_begin;
+ fuse_lowlevel_iomap_device_add;
+ fuse_lowlevel_iomap_device_remove;
} FUSE_3.18;