[PATCH] erofs-utils: support --blobdev with block map

Ajay Rajera
2026-04-09T08:48:51Z

Add support for using --blobdev with the FORCE_INODE_BLOCK_MAP
chunk format. Previously this combination returned -EINVAL with
a TODO noting it could be implemented via deviceslot mapped_blkaddr.

This implements it by utilizing the deviceslot unified addressing
(uniaddr) to map external blob device blocks into a contiguous
address space, bypassing the need for 8-byte chunk indexes.

For block map format, chunk addresses are encoded as:
blkaddr = devs[0].uniaddr + erofs_blknr(data_offset)

At mount time, the kernel's erofs_map_dev() resolves these unified
addresses back to the correct external device transparently.

Changes:
- mkfs/main.c: remove the error blocking blobdev + block map
- lib/super.c: initialize uniaddr with primarydevice_blocks
- lib/blobchunk.c: encode unified addresses in 4-byte block maps
- lib/data.c: fix erofs_map_dev() to set m_deviceid when resolving
unified addresses, so userspace tools (fsck, dump) work correctly

Signed-off-by: Ajay Rajera <newajay.11r@gmail.com>
---
lib/blobchunk.c | 19 +++++++++++++++++--
lib/data.c | 1 +
lib/super.c | 5 +++++
mkfs/main.c | 9 ++++++---
4 files changed, 29 insertions(+), 5 deletions(-)

diff --git a/lib/blobchunk.c b/lib/blobchunk.c
index 96c161b..e666d2b 100644
--- a/lib/blobchunk.c
+++ b/lib/blobchunk.c
@@ -95,7 +95,14 @@ static struct erofs_blobchunk *erofs_blob_getchunk(struct erofs_sb_info *sbi,
chunk->device_id = 1;
else
chunk->device_id = 0;
- chunk->blkaddr = erofs_blknr(sbi, blkpos);
+
+ /* For block map with blobdev, use unified addressing */
+ if (sbi->extra_devices && cfg.c_force_chunkformat == FORCE_INODE_BLOCK_MAP) {
+ chunk->blkaddr = sbi->devs[0].uniaddr + erofs_blknr(sbi, blkpos);
+ chunk->device_id = 0; /* unified address space */
+ } else {
+ chunk->blkaddr = erofs_blknr(sbi, blkpos);
+ }

erofs_dbg("Writing chunk (%llu bytes) to %llu", chunksize | 0ULL,
chunk->blkaddr | 0ULL);
@@ -324,7 +331,7 @@ int erofs_blob_write_chunked_file(struct erofs_inode *inode, int fd,
chunksize = 1ULL << chunkbits;
count = DIV_ROUND_UP(inode->i_size, chunksize);

- if (sbi->extra_devices)
+ if (sbi->extra_devices && cfg.c_force_chunkformat != FORCE_INODE_BLOCK_MAP)
inode->u.chunkformat |= EROFS_CHUNK_FORMAT_INDEXES;
if (inode->u.chunkformat & EROFS_CHUNK_FORMAT_INDEXES)
unit = sizeof(struct erofs_inode_chunk_index);
@@ -498,6 +505,14 @@ int tarerofs_write_chunkes(struct erofs_inode *inode, erofs_off_t data_offset)
unit = sizeof(struct erofs_inode_chunk_index);
DBG_BUGON(erofs_blkoff(sbi, data_offset));
blkaddr = erofs_blknr(sbi, data_offset);
+ } else if (cfg.c_blobdev_path &&
+ cfg.c_force_chunkformat == FORCE_INODE_BLOCK_MAP) {
+ /* Block map with blobdev: use unified addressing via deviceslot */
+ device_id = 0; /* unified address space, no device_id needed */
+ unit = EROFS_BLOCK_MAP_ENTRY_SIZE;
+ DBG_BUGON(erofs_blkoff(sbi, data_offset));
+ /* Map to unified address space using device's uniaddr base */
+ blkaddr = sbi->devs[0].uniaddr + erofs_blknr(sbi, data_offset);
} else {
device_id = 0;
unit = EROFS_BLOCK_MAP_ENTRY_SIZE;
diff --git a/lib/data.c b/lib/data.c
index 6fd1389..5aeb0c1 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -184,6 +184,7 @@ int erofs_map_dev(struct erofs_sb_info *sbi, struct erofs_map_dev *map)
if (map->m_pa >= startoff &&
map->m_pa < startoff + length) {
map->m_pa -= startoff;
+ map->m_deviceid = id + 1;
break;
}
}
diff --git a/lib/super.c b/lib/super.c
index 088c9a0..55d5dd1 100644
--- a/lib/super.c
+++ b/lib/super.c
@@ -393,6 +393,11 @@ int erofs_mkfs_init_devices(struct erofs_sb_info *sbi, unsigned int devices)
sbi->bh_devt = bh;
sbi->devt_slotoff = erofs_btell(bh, false) / EROFS_DEVT_SLOT_SIZE;
sbi->extra_devices = devices;
+
+ /* Initialize uniaddr for block map with blobdev support */
+ if (devices > 0)
+ sbi->devs[0].uniaddr = sbi->primarydevice_blocks;
+
erofs_sb_set_device_table(sbi);
return 0;
}
diff --git a/mkfs/main.c b/mkfs/main.c
index b84d1b4..3cccd60 100644
--- a/mkfs/main.c
+++ b/mkfs/main.c
@@ -1565,11 +1565,14 @@ static int mkfs_parse_options_cfg(struct erofs_importer_params *params,
return -EINVAL;
}

- /* TODO: can be implemented with (deviceslot) mapped_blkaddr */
+ /* Blobdev with block map uses deviceslot unified addressing */
if (cfg.c_blobdev_path &&
cfg.c_force_chunkformat == FORCE_INODE_BLOCK_MAP) {
- erofs_err("--blobdev cannot work with block map currently");
- return -EINVAL;
+ /* Block map format can work with blobdev using unified addressing */
+ if (!g_sbi.extra_devices) {
+ erofs_err("--blobdev requires device table setup");
+ return -EINVAL;
+ }
}

if (optind >= argc) {
--
2.51.0.windows.1