[PATCH 04/19] fuse2fs: register block devices for use with iomap

Darrick J. Wong
2025-08-21T01:16:45Z

Register the ext4 block device with the kernel for use with iomap. For
now this is redundant with using fuseblk mode because the kernel
automatically registers any fuseblk devices, but eventually we'll go
back to regular fuse mode and we'll have to pin the bdev ourselves.
In theory this interface supports strange beasts where the metadata can
exist somewhere else entirely (or be made up by AI) while the file data
persists to real disks.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
misc/fuse2fs.c | 42 ++++++++++++++++++++++++++++++++++++++----
misc/fuse4fs.c | 44 ++++++++++++++++++++++++++++++++++++++++----
2 files changed, 78 insertions(+), 8 deletions(-)

diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c
index 5b17aadc006560..8bf0fbcff093a7 100644
--- a/misc/fuse2fs.c
+++ b/misc/fuse2fs.c
@@ -41,6 +41,7 @@
# define _FILE_OFFSET_BITS 64
#endif /* _FILE_OFFSET_BITS */
#include <fuse.h>
+#include <fuse_lowlevel.h>
#ifdef __SET_FOB_FOR_FUSE
# undef _FILE_OFFSET_BITS
#endif /* __SET_FOB_FOR_FUSE */
@@ -273,6 +274,7 @@ struct fuse2fs {
#ifdef HAVE_FUSE_IOMAP
enum fuse2fs_feature_toggle iomap_want;
enum fuse2fs_iomap_state iomap_state;
+ uint32_t iomap_dev;
#endif
unsigned int blockmask;
unsigned long offset;
@@ -5235,7 +5237,7 @@ static errcode_t fuse2fs_iomap_begin_extent(struct fuse2fs *ff, uint64_t ino,
}

/* Mapping overlaps startoff, report this. */
- iomap->dev = FUSE_IOMAP_DEV_NULL;
+ iomap->dev = ff->iomap_dev;
iomap->addr = FUSE2FS_FSB_TO_B(ff, extent.e_pblk);
iomap->offset = FUSE2FS_FSB_TO_B(ff, extent.e_lblk);
iomap->length = FUSE2FS_FSB_TO_B(ff, extent.e_len);
@@ -5268,13 +5270,14 @@ static int fuse2fs_iomap_begin_indirect(struct fuse2fs *ff, uint64_t ino,
if (err)
return translate_error(fs, ino, err);

- iomap->dev = FUSE_IOMAP_DEV_NULL;
iomap->offset = FUSE2FS_FSB_TO_B(ff, startoff);
iomap->flags |= FUSE_IOMAP_F_MERGED;
if (startblock) {
+ iomap->dev = ff->iomap_dev;
iomap->addr = FUSE2FS_FSB_TO_B(ff, startblock);
iomap->type = FUSE_IOMAP_TYPE_MAPPED;
} else {
+ iomap->dev = FUSE_IOMAP_DEV_NULL;
iomap->addr = FUSE_IOMAP_NULL_ADDR;
iomap->type = FUSE_IOMAP_TYPE_HOLE;
}
@@ -5487,11 +5490,36 @@ static off_t fuse2fs_max_size(struct fuse2fs *ff, off_t upper_limit)
return res;
}

+static int fuse2fs_iomap_config_devices(struct fuse2fs *ff)
+{
+ errcode_t err;
+ int fd;
+ int ret;
+
+ err = io_channel_get_fd(ff->fs->io, &fd);
+ if (err)
+ return translate_error(ff->fs, 0, err);
+
+ ret = fuse_fs_iomap_device_add(fd, 0);
+ if (ret < 0) {
+ dbg_printf(ff, "%s: cannot register iomap dev fd=%d, err=%d\n",
+ __func__, fd, -ret);
+ return translate_error(ff->fs, 0, -ret);
+ }
+
+ dbg_printf(ff, "%s: registered iomap dev fd=%d iomap_dev=%u\n",
+ __func__, fd, ff->iomap_dev);
+
+ ff->iomap_dev = ret;
+ return 0;
+}
+
static int op_iomap_config(uint64_t flags, off_t maxbytes,
struct fuse_iomap_config *cfg)
{
struct fuse2fs *ff = fuse2fs_get();
ext2_filsys fs;
+ int ret = 0;

FUSE2FS_CHECK_CONTEXT(ff);

@@ -5526,8 +5554,13 @@ static int op_iomap_config(uint64_t flags, off_t maxbytes,
cfg->flags |= FUSE_IOMAP_CONFIG_MAXBYTES;
cfg->s_maxbytes = fuse2fs_max_size(ff, maxbytes);

- fuse2fs_finish(ff, 0);
- return 0;
+ ret = fuse2fs_iomap_config_devices(ff);
+ if (ret)
+ goto out_unlock;
+
+out_unlock:
+ fuse2fs_finish(ff, ret);
+ return ret;
}
#endif /* HAVE_FUSE_IOMAP */

@@ -5853,6 +5886,7 @@ int main(int argc, char *argv[])
#ifdef HAVE_FUSE_IOMAP
.iomap_want = FT_DEFAULT,
.iomap_state = IOMAP_UNKNOWN,
+ .iomap_dev = FUSE_IOMAP_DEV_NULL,
#endif
};
errcode_t err;
diff --git a/misc/fuse4fs.c b/misc/fuse4fs.c
index 5876af19387c96..5debaf892b2113 100644
--- a/misc/fuse4fs.c
+++ b/misc/fuse4fs.c
@@ -269,6 +269,7 @@ struct fuse4fs {
#ifdef HAVE_FUSE_IOMAP
enum fuse4fs_feature_toggle iomap_want;
enum fuse4fs_iomap_state iomap_state;
+ uint32_t iomap_dev;
#endif
unsigned int blockmask;
unsigned long offset;
@@ -5644,7 +5645,7 @@ static errcode_t fuse4fs_iomap_begin_extent(struct fuse4fs *ff, uint64_t ino,
}

/* Mapping overlaps startoff, report this. */
- iomap->dev = FUSE_IOMAP_DEV_NULL;
+ iomap->dev = ff->iomap_dev;
iomap->addr = FUSE4FS_FSB_TO_B(ff, extent.e_pblk);
iomap->offset = FUSE4FS_FSB_TO_B(ff, extent.e_lblk);
iomap->length = FUSE4FS_FSB_TO_B(ff, extent.e_len);
@@ -5677,13 +5678,14 @@ static int fuse4fs_iomap_begin_indirect(struct fuse4fs *ff, uint64_t ino,
if (err)
return translate_error(fs, ino, err);

- iomap->dev = FUSE_IOMAP_DEV_NULL;
iomap->offset = FUSE4FS_FSB_TO_B(ff, startoff);
iomap->flags |= FUSE_IOMAP_F_MERGED;
if (startblock) {
+ iomap->dev = ff->iomap_dev;
iomap->addr = FUSE4FS_FSB_TO_B(ff, startblock);
iomap->type = FUSE_IOMAP_TYPE_MAPPED;
} else {
+ iomap->dev = FUSE_IOMAP_DEV_NULL;
iomap->addr = FUSE_IOMAP_NULL_ADDR;
iomap->type = FUSE_IOMAP_TYPE_HOLE;
}
@@ -5897,11 +5899,36 @@ static off_t fuse4fs_max_size(struct fuse4fs *ff, off_t upper_limit)
return res;
}

+static int fuse4fs_iomap_config_devices(struct fuse4fs *ff)
+{
+ errcode_t err;
+ int fd;
+ int ret;
+
+ err = io_channel_get_fd(ff->fs->io, &fd);
+ if (err)
+ return translate_error(ff->fs, 0, err);
+
+ ret = fuse_lowlevel_iomap_device_add(ff->fuse, fd, 0);
+ if (ret < 0) {
+ dbg_printf(ff, "%s: cannot register iomap dev fd=%d, err=%d\n",
+ __func__, fd, -ret);
+ return translate_error(ff->fs, 0, -ret);
+ }
+
+ dbg_printf(ff, "%s: registered iomap dev fd=%d iomap_dev=%u\n",
+ __func__, fd, ff->iomap_dev);
+
+ ff->iomap_dev = ret;
+ return 0;
+}
+
static void op_iomap_config(fuse_req_t req, uint64_t flags, uint64_t maxbytes)
{
struct fuse_iomap_config cfg = { };
struct fuse4fs *ff = fuse4fs_get(req);
ext2_filsys fs;
+ int ret = 0;

FUSE4FS_CHECK_CONTEXT(req);

@@ -5936,8 +5963,16 @@ static void op_iomap_config(fuse_req_t req, uint64_t flags, uint64_t maxbytes)
cfg.flags |= FUSE_IOMAP_CONFIG_MAXBYTES;
cfg.s_maxbytes = fuse4fs_max_size(ff, maxbytes);

- fuse4fs_finish(ff, 0);
- fuse_reply_iomap_config(req, &cfg);
+ ret = fuse4fs_iomap_config_devices(ff);
+ if (ret)
+ goto out_unlock;
+
+out_unlock:
+ fuse4fs_finish(ff, ret);
+ if (ret)
+ fuse_reply_err(req, -ret);
+ else
+ fuse_reply_iomap_config(req, &cfg);
}
#endif /* HAVE_FUSE_IOMAP */

@@ -6345,6 +6380,7 @@ int main(int argc, char *argv[])
#ifdef HAVE_FUSE_IOMAP
.iomap_want = FT_DEFAULT,
.iomap_state = IOMAP_UNKNOWN,
+ .iomap_dev = FUSE_IOMAP_DEV_NULL,
#endif
};
errcode_t err;