[PATCH v2 2/3] erofs-utils: fuse: add multiple device support
Gao Xiang
hsiangkao at linux.alibaba.com
Fri Nov 12 23:31:27 AEDT 2021
Keep in sync with the latest kernel
dfeab2e95a75 ("erofs: add multiple device support")
Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
dump/main.c | 2 +-
fuse/main.c | 10 ++++++
include/erofs/defs.h | 32 +++++++++++++++++++
include/erofs/internal.h | 19 +++++++++++-
include/erofs_fs.h | 22 +++++++++++--
lib/data.c | 67 +++++++++++++++++++++++++++++++++-------
lib/super.c | 43 ++++++++++++++++++++++++--
7 files changed, 177 insertions(+), 18 deletions(-)
diff --git a/dump/main.c b/dump/main.c
index 401e6841aefb..912d98f70525 100644
--- a/dump/main.c
+++ b/dump/main.c
@@ -609,7 +609,7 @@ static void erofsdump_show_superblock(void)
fprintf(stdout, "Filesystem magic number: 0x%04X\n",
EROFS_SUPER_MAGIC_V1);
fprintf(stdout, "Filesystem blocks: %llu\n",
- sbi.blocks | 0ULL);
+ sbi.total_blocks | 0ULL);
fprintf(stdout, "Filesystem inode metadata start block: %u\n",
sbi.meta_blkaddr);
fprintf(stdout, "Filesystem shared xattr metadata start block: %u\n",
diff --git a/fuse/main.c b/fuse/main.c
index 8137421b1dea..21939cd15923 100644
--- a/fuse/main.c
+++ b/fuse/main.c
@@ -113,6 +113,7 @@ static struct options {
static const struct fuse_opt option_spec[] = {
OPTION("--dbglevel=%u", debug_lvl),
OPTION("--help", show_help),
+ FUSE_OPT_KEY("--device=", 1),
FUSE_OPT_END
};
@@ -148,7 +149,15 @@ static void erofsfuse_dumpcfg(void)
static int optional_opt_func(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
+ int ret;
+
switch (key) {
+ case 1:
+ ret = blob_open_ro(arg);
+ if (ret)
+ return -1;
+ ++sbi.extra_devices;
+ return 0;
case FUSE_OPT_KEY_NONOPT:
if (fusecfg.mountpoint)
return -1; /* Too many args */
@@ -237,6 +246,7 @@ int main(int argc, char *argv[])
ret = fuse_main(args.argc, args.argv, &erofs_ops, NULL);
err_dev_close:
+ blob_closeall();
dev_close();
err_fuse_free_args:
fuse_opt_free_args(&args);
diff --git a/include/erofs/defs.h b/include/erofs/defs.h
index 96bbb6574ff3..5283f5d3c6f3 100644
--- a/include/erofs/defs.h
+++ b/include/erofs/defs.h
@@ -252,6 +252,38 @@ static inline u32 get_unaligned_le32(const u8 *p)
(n) & (1ULL << 1) ? 1 : 0 \
)
+static inline unsigned int fls_long(unsigned long x)
+{
+ return x ? sizeof(x) * 8 - __builtin_clz(x) : 0;
+}
+
+/**
+ * __roundup_pow_of_two() - round up to nearest power of two
+ * @n: value to round up
+ */
+static inline __attribute__((const))
+unsigned long __roundup_pow_of_two(unsigned long n)
+{
+ return 1UL << fls_long(n - 1);
+}
+
+/**
+ * roundup_pow_of_two - round the given value up to nearest power of two
+ * @n: parameter
+ *
+ * round the given value up to the nearest power of two
+ * - the result is undefined when n == 0
+ * - this can be used to initialise global variables from constant data
+ */
+#define roundup_pow_of_two(n) \
+( \
+ __builtin_constant_p(n) ? ( \
+ ((n) == 1) ? 1 : \
+ (1UL << (ilog2((n) - 1) + 1)) \
+ ) : \
+ __roundup_pow_of_two(n) \
+ )
+
#ifndef __always_inline
#define __always_inline inline
#endif
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index f84e6b4f125d..974c069baa4f 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -60,8 +60,16 @@ typedef u32 erofs_blk_t;
struct erofs_buffer_head;
+struct erofs_device_info {
+ u32 blocks;
+ u32 mapped_blkaddr;
+};
+
struct erofs_sb_info {
- u64 blocks;
+ struct erofs_device_info *devs;
+
+ u64 total_blocks;
+ u64 primarydevice_blocks;
erofs_blk_t meta_blkaddr;
erofs_blk_t xattr_blkaddr;
@@ -84,6 +92,8 @@ struct erofs_sb_info {
u16 lz4_max_distance;
u32 checksum;
+ u16 extra_devices;
+ u16 device_id_mask;
};
/* global sbi */
@@ -112,6 +122,7 @@ EROFS_FEATURE_FUNCS(lz4_0padding, incompat, INCOMPAT_LZ4_0PADDING)
EROFS_FEATURE_FUNCS(compr_cfgs, incompat, INCOMPAT_COMPR_CFGS)
EROFS_FEATURE_FUNCS(big_pcluster, incompat, INCOMPAT_BIG_PCLUSTER)
EROFS_FEATURE_FUNCS(chunked_file, incompat, INCOMPAT_CHUNKED_FILE)
+EROFS_FEATURE_FUNCS(device_table, incompat, INCOMPAT_DEVICE_TABLE)
EROFS_FEATURE_FUNCS(sb_chksum, compat, COMPAT_SB_CHKSUM)
#define EROFS_I_EA_INITED (1 << 0)
@@ -257,6 +268,7 @@ struct erofs_map_blocks {
erofs_off_t m_pa, m_la;
u64 m_plen, m_llen;
+ unsigned short m_deviceid;
unsigned int m_flags;
erofs_blk_t index;
};
@@ -267,6 +279,11 @@ struct erofs_map_blocks {
*/
#define EROFS_GET_BLOCKS_FIEMAP 0x0002
+struct erofs_map_dev {
+ erofs_off_t m_pa;
+ unsigned int m_deviceid;
+};
+
/* super.c */
int erofs_read_superblock(void);
diff --git a/include/erofs_fs.h b/include/erofs_fs.h
index 4291970753a8..9a918775750c 100644
--- a/include/erofs_fs.h
+++ b/include/erofs_fs.h
@@ -22,14 +22,27 @@
#define EROFS_FEATURE_INCOMPAT_COMPR_CFGS 0x00000002
#define EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER 0x00000002
#define EROFS_FEATURE_INCOMPAT_CHUNKED_FILE 0x00000004
+#define EROFS_FEATURE_INCOMPAT_DEVICE_TABLE 0x00000008
#define EROFS_ALL_FEATURE_INCOMPAT \
(EROFS_FEATURE_INCOMPAT_LZ4_0PADDING | \
EROFS_FEATURE_INCOMPAT_COMPR_CFGS | \
EROFS_FEATURE_INCOMPAT_BIG_PCLUSTER | \
- EROFS_FEATURE_INCOMPAT_CHUNKED_FILE)
+ EROFS_FEATURE_INCOMPAT_CHUNKED_FILE | \
+ EROFS_FEATURE_INCOMPAT_DEVICE_TABLE)
#define EROFS_SB_EXTSLOT_SIZE 16
+struct erofs_deviceslot {
+ union {
+ u8 uuid[16]; /* used for device manager later */
+ u8 userdata[64]; /* digest(sha256), etc. */
+ } u;
+ __le32 blocks; /* total fs blocks of this device */
+ __le32 mapped_blkaddr; /* map starting at mapped_blkaddr */
+ u8 reserved[56];
+};
+#define EROFS_DEVT_SLOT_SIZE sizeof(struct erofs_deviceslot)
+
/* erofs on-disk super block (currently 128 bytes) */
struct erofs_super_block {
__le32 magic; /* file system magic number */
@@ -55,7 +68,9 @@ struct erofs_super_block {
/* customized sliding window size instead of 64k by default */
__le16 lz4_max_distance;
} __packed u1;
- __u8 reserved2[42];
+ __le16 extra_devices; /* # of devices besides the primary device */
+ __le16 devt_slotoff; /* startoff = devt_slotoff * devt_slotsize */
+ __u8 reserved2[38];
};
/*
@@ -239,7 +254,7 @@ static inline unsigned int erofs_xattr_entry_size(struct erofs_xattr_entry *e)
/* 8-byte inode chunk indexes */
struct erofs_inode_chunk_index {
__le16 advise; /* always 0, don't care for now */
- __le16 device_id; /* back-end storage id, always 0 for now */
+ __le16 device_id; /* back-end storage id (with bits masked) */
__le32 blkaddr; /* start block address of this inode chunk */
};
@@ -404,6 +419,7 @@ static inline void erofs_check_ondisk_layout_definitions(void)
/* keep in sync between 2 index structures for better extendibility */
BUILD_BUG_ON(sizeof(struct erofs_inode_chunk_index) !=
sizeof(struct z_erofs_vle_decompressed_index));
+ BUILD_BUG_ON(sizeof(struct erofs_deviceslot) != 128);
BUILD_BUG_ON(BIT(Z_EROFS_VLE_DI_CLUSTER_TYPE_BITS) <
Z_EROFS_VLE_CLUSTER_TYPE_MAX - 1);
diff --git a/lib/data.c b/lib/data.c
index b83cbff3e731..c90b66fca628 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -72,6 +72,7 @@ int erofs_map_blocks(struct erofs_inode *inode,
erofs_off_t pos;
int err = 0;
+ map->m_deviceid = 0;
if (map->m_la >= inode->i_size) {
/* leave out-of-bound access unmapped */
map->m_flags = 0;
@@ -118,14 +119,8 @@ int erofs_map_blocks(struct erofs_inode *inode,
map->m_flags = 0;
break;
default:
- /* only one device is supported for now */
- if (idx->device_id) {
- erofs_err("invalid device id %u @ %" PRIu64 " for nid %llu",
- le16_to_cpu(idx->device_id),
- chunknr, vi->nid | 0ULL);
- err = -EFSCORRUPTED;
- goto out;
- }
+ map->m_deviceid = le16_to_cpu(idx->device_id) &
+ sbi.device_id_mask;
map->m_pa = blknr_to_addr(le32_to_cpu(idx->blkaddr));
map->m_flags = EROFS_MAP_MAPPED;
break;
@@ -135,12 +130,42 @@ out:
return err;
}
+int erofs_map_dev(struct erofs_sb_info *sbi, struct erofs_map_dev *map)
+{
+ struct erofs_device_info *dif;
+ int id;
+
+ if (map->m_deviceid) {
+ if (sbi->extra_devices < map->m_deviceid)
+ return -ENODEV;
+ } else if (sbi->extra_devices) {
+ for (id = 0; id < sbi->extra_devices; ++id) {
+ erofs_off_t startoff, length;
+
+ dif = sbi->devs + id;
+
+ if (!dif->mapped_blkaddr)
+ continue;
+ startoff = blknr_to_addr(dif->mapped_blkaddr);
+ length = blknr_to_addr(dif->blocks);
+
+ if (map->m_pa >= startoff &&
+ map->m_pa < startoff + length) {
+ map->m_pa -= startoff;
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
erofs_off_t size, erofs_off_t offset)
{
struct erofs_map_blocks map = {
.index = UINT_MAX,
};
+ struct erofs_map_dev mdev;
int ret;
erofs_off_t ptr = offset;
@@ -155,6 +180,14 @@ static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
DBG_BUGON(map.m_plen != map.m_llen);
+ mdev = (struct erofs_map_dev) {
+ .m_deviceid = map.m_deviceid,
+ .m_pa = map.m_pa,
+ };
+ ret = erofs_map_dev(&sbi, &mdev);
+ if (ret)
+ return ret;
+
/* trim extent */
eend = min(offset + size, map.m_la + map.m_llen);
DBG_BUGON(ptr < map.m_la);
@@ -172,11 +205,12 @@ static int erofs_read_raw_data(struct erofs_inode *inode, char *buffer,
}
if (ptr > map.m_la) {
- map.m_pa += ptr - map.m_la;
+ mdev.m_pa += ptr - map.m_la;
map.m_la = ptr;
}
- ret = dev_read(0, estart, map.m_pa, eend - map.m_la);
+ ret = dev_read(mdev.m_deviceid, estart, mdev.m_pa,
+ eend - map.m_la);
if (ret < 0)
return -EIO;
ptr = eend;
@@ -191,6 +225,7 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
struct erofs_map_blocks map = {
.index = UINT_MAX,
};
+ struct erofs_map_dev mdev;
bool partial;
unsigned int algorithmformat, bufsize;
char *raw = NULL;
@@ -205,6 +240,16 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
if (ret)
break;
+ /* no device id here, thus it will always succeed */
+ mdev = (struct erofs_map_dev) {
+ .m_pa = map.m_pa,
+ };
+ ret = erofs_map_dev(&sbi, &mdev);
+ if (ret) {
+ DBG_BUGON(1);
+ break;
+ }
+
/*
* trim to the needed size if the returned extent is quite
* larger than requested, and set up partial flag as well.
@@ -240,7 +285,7 @@ static int z_erofs_read_data(struct erofs_inode *inode, char *buffer,
break;
}
}
- ret = dev_read(0, raw, map.m_pa, map.m_plen);
+ ret = dev_read(mdev.m_deviceid, raw, mdev.m_pa, map.m_plen);
if (ret < 0)
break;
diff --git a/lib/super.c b/lib/super.c
index bf4d4318a321..3ccc551f18cf 100644
--- a/lib/super.c
+++ b/lib/super.c
@@ -23,6 +23,45 @@ static bool check_layout_compatibility(struct erofs_sb_info *sbi,
return true;
}
+static int erofs_init_devices(struct erofs_sb_info *sbi,
+ struct erofs_super_block *dsb)
+{
+ unsigned int ondisk_extradevs, i;
+ erofs_off_t pos;
+
+ sbi->total_blocks = sbi->primarydevice_blocks;
+
+ if (!erofs_sb_has_device_table())
+ ondisk_extradevs = 0;
+ else
+ ondisk_extradevs = le16_to_cpu(dsb->extra_devices);
+
+ if (ondisk_extradevs != sbi->extra_devices) {
+ erofs_err("extra devices don't match (ondisk %u, given %u)",
+ ondisk_extradevs, sbi->extra_devices);
+ return -EINVAL;
+ }
+ if (!ondisk_extradevs)
+ return 0;
+
+ sbi->device_id_mask = roundup_pow_of_two(ondisk_extradevs + 1) - 1;
+ sbi->devs = calloc(ondisk_extradevs, sizeof(*sbi->devs));
+ pos = le16_to_cpu(dsb->devt_slotoff) * EROFS_DEVT_SLOT_SIZE;
+ for (i = 0; i < ondisk_extradevs; ++i) {
+ struct erofs_deviceslot dis;
+ int ret;
+
+ ret = dev_read(0, &dis, pos, sizeof(dis));
+ if (ret < 0)
+ return ret;
+
+ sbi->devs[i].mapped_blkaddr = dis.mapped_blkaddr;
+ sbi->total_blocks += dis.blocks;
+ pos += EROFS_DEVT_SLOT_SIZE;
+ }
+ return 0;
+}
+
int erofs_read_superblock(void)
{
char data[EROFS_BLKSIZ];
@@ -56,7 +95,7 @@ int erofs_read_superblock(void)
if (!check_layout_compatibility(&sbi, dsb))
return ret;
- sbi.blocks = le32_to_cpu(dsb->blocks);
+ sbi.primarydevice_blocks = le32_to_cpu(dsb->blocks);
sbi.meta_blkaddr = le32_to_cpu(dsb->meta_blkaddr);
sbi.xattr_blkaddr = le32_to_cpu(dsb->xattr_blkaddr);
sbi.islotbits = EROFS_ISLOTBITS;
@@ -68,5 +107,5 @@ int erofs_read_superblock(void)
sbi.build_time_nsec = le32_to_cpu(dsb->build_time_nsec);
memcpy(&sbi.uuid, dsb->uuid, sizeof(dsb->uuid));
- return 0;
+ return erofs_init_devices(&sbi, dsb);
}
--
2.24.4
More information about the Linux-erofs
mailing list