[PATCH 02/16] fuse2fs: register block devices for use with iomap

Darrick J. Wong
2025-05-22T00:11:22Z

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 | 44 ++++++++++++++++++++++++++++++++++++++++----
1 file changed, 40 insertions(+), 4 deletions(-)

diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c
index f9eed078d91152..92a80753f4f1e8 100644
--- a/misc/fuse2fs.c
+++ b/misc/fuse2fs.c
@@ -36,6 +36,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 */
@@ -179,6 +180,7 @@ struct fuse2fs {
int blocklog;
#ifdef HAVE_FUSE_IOMAP
enum fuse2fs_iomap_state iomap_state;
+ uint32_t iomap_dev;
#endif
unsigned int blockmask;
int retcode;
@@ -4638,7 +4640,7 @@ static int op_fallocate(const char *path EXT2FS_ATTR((unused)), int mode,
static void handle_iomap_hole(struct fuse2fs *ff, struct fuse_iomap *iomap,
off_t pos, uint64_t count)
{
- iomap->dev = FUSE_IOMAP_DEV_FUSEBLK;
+ iomap->dev = ff->iomap_dev;
iomap->addr = FUSE_IOMAP_NULL_ADDR;
iomap->offset = pos;
iomap->length = count;
@@ -4815,7 +4817,7 @@ static errcode_t extent_iomap_begin(struct fuse2fs *ff, uint64_t ino,
}

/* Mapping overlaps startoff, report this. */
- iomap->dev = FUSE_IOMAP_DEV_FUSEBLK;
+ 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);
@@ -4846,7 +4848,7 @@ static int indirect_iomap_begin(struct fuse2fs *ff, uint64_t ino,
if (err)
return translate_error(fs, ino, err);

- iomap->dev = FUSE_IOMAP_DEV_FUSEBLK;
+ iomap->dev = ff->iomap_dev;
iomap->offset = pos;
iomap->flags |= FUSE_IOMAP_F_MERGED;
if (startblock) {
@@ -4884,7 +4886,7 @@ static int indirect_iomap_begin(struct fuse2fs *ff, uint64_t ino,
static int inline_iomap_begin(struct fuse2fs *ff, off_t pos, uint64_t count,
struct fuse_iomap *iomap)
{
- iomap->dev = FUSE_IOMAP_DEV_FUSEBLK;
+ iomap->dev = ff->iomap_dev;
iomap->addr = FUSE_IOMAP_NULL_ADDR;
iomap->offset = pos;
iomap->length = count;
@@ -4925,6 +4927,31 @@ static int fuse_iomap_begin_write(struct fuse2fs *ff, ext2_ino_t ino,
return -ENOSYS;
}

+static errcode_t config_iomap_devices(struct fuse_context *ctxt,
+ struct fuse2fs *ff)
+{
+ struct fuse_session *se = fuse_get_session(ctxt->fuse);
+ errcode_t err;
+ int fd;
+ int ret;
+
+ err = io_channel_fd(ff->fs->io, &fd);
+ if (err)
+ return err;
+
+ ret = fuse_lowlevel_notify_iomap_add_device(se, fd, &ff->iomap_dev);
+
+ dbg_printf(ff, "%s: registering iomap dev fd=%d ret=%d iomap_dev=%u\n",
+ __func__, fd, ret, ff->iomap_dev);
+
+ if (ret)
+ return ret;
+ if (ff->iomap_dev == FUSE_IOMAP_DEV_NULL)
+ return -EIO;
+
+ return 0;
+}
+
static int op_iomap_begin(const char *path, uint64_t nodeid, uint64_t attr_ino,
off_t pos, uint64_t count, uint32_t opflags,
struct fuse_iomap *read_iomap,
@@ -4951,6 +4978,14 @@ static int op_iomap_begin(const char *path, uint64_t nodeid, uint64_t attr_ino,
(unsigned long long)count,
opflags);

+ if (ff->iomap_dev == FUSE_IOMAP_DEV_NULL) {
+ err = config_iomap_devices(ctxt, ff);
+ if (err) {
+ ret = translate_error(fs, attr_ino, err);
+ goto out_unlock;
+ }
+ }
+
err = fuse2fs_read_inode(fs, attr_ino, &inode);
if (err) {
ret = translate_error(fs, attr_ino, err);
@@ -5285,6 +5320,7 @@ int main(int argc, char *argv[])
.magic = FUSE2FS_MAGIC,
#ifdef HAVE_FUSE_IOMAP
.iomap_state = IOMAP_UNKNOWN,
+ .iomap_dev = FUSE_IOMAP_DEV_NULL,
#endif
};
errcode_t err;