This implements metadata compression for mkfs.erofs. It introduces a new command-line option `-m <size>[:algorithm]`. If specified, all inode metadata will be kept in a special inode called `the metabox inode`.
The metabox inode will be compressed using the specified physical cluster size <size>. For example: $ mkfs.erofs -zlzma -C1048576 -m 4096 foo.erofs foo will compress metadata in 4096-byte pclusters. EROFS supports multiple algorithms within a filesystem, so metadata can be compressed with a different algorithm. For instance, LZMA may be too slow for metadata, but users may still want to save metadata space: $ mkfs.erofs -zlzma -C1048576 -m4096:lz4hc,12 foo.erofs foo Or $ mkfs.erofs -zlzma:lz4hc,12 -C1048576 -m4096:1 foo.erofs foo will compress metadata in 4096-byte pclusters using LZ4HC level 12. Signed-off-by: Gao Xiang <hsiang...@linux.alibaba.com> --- include/erofs/blobchunk.h | 3 +- include/erofs/compress.h | 11 ---- include/erofs/config.h | 2 + include/erofs/fragments.h | 5 ++ include/erofs/internal.h | 5 +- lib/Makefile.am | 5 +- lib/blobchunk.c | 6 +- lib/compress.c | 26 ++++++-- lib/config.c | 1 + lib/inode.c | 131 +++++++++++++++++++++++++------------- lib/liberofs_metabox.h | 19 ++++++ lib/metabox.c | 66 +++++++++++++++++++ mkfs/main.c | 62 ++++++++++++++++-- 13 files changed, 268 insertions(+), 74 deletions(-) create mode 100644 lib/liberofs_metabox.h create mode 100644 lib/metabox.c diff --git a/include/erofs/blobchunk.h b/include/erofs/blobchunk.h index 619155f7..ef067732 100644 --- a/include/erofs/blobchunk.h +++ b/include/erofs/blobchunk.h @@ -16,7 +16,8 @@ extern "C" struct erofs_blobchunk *erofs_get_unhashed_chunk(unsigned int device_id, erofs_blk_t blkaddr, erofs_off_t sourceoffset); -int erofs_blob_write_chunk_indexes(struct erofs_inode *inode, erofs_off_t off); +int erofs_write_chunk_indexes(struct erofs_inode *inode, struct erofs_vfile *vf, + erofs_off_t off); int erofs_blob_write_chunked_file(struct erofs_inode *inode, int fd, erofs_off_t startoff); int erofs_write_zero_inode(struct erofs_inode *inode); diff --git a/include/erofs/compress.h b/include/erofs/compress.h index 4731a8b4..d5b25197 100644 --- a/include/erofs/compress.h +++ b/include/erofs/compress.h @@ -30,17 +30,6 @@ int z_erofs_compress_exit(struct erofs_sb_info *sbi); const char *z_erofs_list_supported_algorithms(int i, unsigned int *mask); const struct erofs_algorithm *z_erofs_list_available_compressors(int *i); -static inline bool erofs_is_packed_inode(struct erofs_inode *inode) -{ - erofs_nid_t packed_nid = inode->sbi->packed_nid; - - if (inode->nid == EROFS_PACKED_NID_UNALLOCATED) { - DBG_BUGON(packed_nid != EROFS_PACKED_NID_UNALLOCATED); - return true; - } - return (packed_nid > 0 && inode->nid == packed_nid); -} - #ifdef __cplusplus } #endif diff --git a/include/erofs/config.h b/include/erofs/config.h index e4d2bb32..8c40fd1b 100644 --- a/include/erofs/config.h +++ b/include/erofs/config.h @@ -79,11 +79,13 @@ struct erofs_configure { struct erofs_compr_opts c_compr_opts[EROFS_MAX_COMPR_CFGS]; char c_force_inodeversion; char c_force_chunkformat; + u8 c_mkfs_metabox_algid; /* < 0, xattr disabled and INT_MAX, always use inline xattrs */ int c_inline_xattr_tolerance; u32 c_mkfs_pclustersize_max; u32 c_mkfs_pclustersize_def; u32 c_mkfs_pclustersize_packed; + s32 c_mkfs_pclustersize_metabox; u32 c_max_decompressed_extent_bytes; u64 c_unix_timestamp; u32 c_uid, c_gid; diff --git a/include/erofs/fragments.h b/include/erofs/fragments.h index 112f002e..7c7acf46 100644 --- a/include/erofs/fragments.h +++ b/include/erofs/fragments.h @@ -15,6 +15,11 @@ extern "C" extern const char *erofs_frags_packedname; #define EROFS_PACKED_INODE erofs_frags_packedname +static inline bool erofs_is_packed_inode(struct erofs_inode *inode) +{ + return inode->i_srcpath == EROFS_PACKED_INODE; +} + u32 z_erofs_fragments_tofh(struct erofs_inode *inode, int fd, erofs_off_t fpos); int erofs_fragment_findmatch(struct erofs_inode *inode, int fd, u32 tofh); diff --git a/include/erofs/internal.h b/include/erofs/internal.h index de6f4eae..e9b90995 100644 --- a/include/erofs/internal.h +++ b/include/erofs/internal.h @@ -83,11 +83,10 @@ struct erofs_xattr_prefix_item { u8 infix_len; }; -#define EROFS_PACKED_NID_UNALLOCATED -1 - struct erofs_mkfs_dfops; struct erofs_packed_inode; struct z_erofs_mgr; +struct erofs_metaboxmgr; struct erofs_sb_info { struct erofs_sb_lz4_info lz4; @@ -149,6 +148,7 @@ struct erofs_sb_info { #endif struct erofs_bufmgr *bmgr; struct z_erofs_mgr *zmgr; + struct erofs_metaboxmgr *m2gr; struct erofs_packed_inode *packedinode; struct erofs_buffer_head *bh_devt; bool useqpl; @@ -245,6 +245,7 @@ struct erofs_inode { /* inline tail-end packing size */ unsigned short idata_size; char datasource; + bool in_metabox; bool compressed_idata; bool lazy_tailblock; bool opaque; diff --git a/lib/Makefile.am b/lib/Makefile.am index 688403b7..0db81df7 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -28,7 +28,8 @@ noinst_HEADERS = $(top_srcdir)/include/erofs_fs.h \ $(top_srcdir)/include/erofs/fragments.h \ $(top_srcdir)/include/erofs/rebuild.h \ $(top_srcdir)/lib/liberofs_private.h \ - $(top_srcdir)/lib/liberofs_xxhash.h + $(top_srcdir)/lib/liberofs_xxhash.h \ + $(top_srcdir)/lib/liberofs_metabox.h noinst_HEADERS += compressor.h liberofs_la_SOURCES = config.c io.c cache.c super.c inode.c xattr.c exclude.c \ @@ -36,7 +37,7 @@ liberofs_la_SOURCES = config.c io.c cache.c super.c inode.c xattr.c exclude.c \ compress_hints.c hashmap.c sha256.c blobchunk.c dir.c \ fragments.c dedupe.c uuid_unparse.c uuid.c tar.c \ block_list.c rebuild.c diskbuf.c bitops.c dedupe_ext.c \ - vmdk.c + vmdk.c metabox.c liberofs_la_CFLAGS = -Wall ${libuuid_CFLAGS} -I$(top_srcdir)/include if ENABLE_LZ4 diff --git a/lib/blobchunk.c b/lib/blobchunk.c index bbc69cfb..157b9a9c 100644 --- a/lib/blobchunk.c +++ b/lib/blobchunk.c @@ -133,8 +133,8 @@ static int erofs_blob_hashmap_cmp(const void *a, const void *b, sizeof(ec1->sha256)); } -int erofs_blob_write_chunk_indexes(struct erofs_inode *inode, - erofs_off_t off) +int erofs_write_chunk_indexes(struct erofs_inode *inode, struct erofs_vfile *vf, + erofs_off_t off) { struct erofs_sb_info *sbi = inode->sbi; erofs_blk_t remaining_blks = BLK_ROUND_UP(sbi, inode->i_size); @@ -202,7 +202,7 @@ int erofs_blob_write_chunk_indexes(struct erofs_inode *inode, source_offset, zeroedlen); } off = roundup(off, unit); - return erofs_io_pwrite(&sbi->bdev, inode->chunkindexes, + return erofs_io_pwrite(vf, inode->chunkindexes, off, inode->extent_isize); } diff --git a/lib/compress.c b/lib/compress.c index 22fb5d6d..0bfad3f5 100644 --- a/lib/compress.c +++ b/lib/compress.c @@ -25,6 +25,7 @@ #ifdef EROFS_MT_ENABLED #include "erofs/workqueue.h" #endif +#include "liberofs_metabox.h" #define Z_EROFS_DESTBUF_SZ (Z_EROFS_PCLUSTER_MAX_SIZE + EROFS_MAX_BLOCK_SIZE * 2) @@ -459,6 +460,8 @@ static unsigned int z_erofs_get_max_pclustersize(struct erofs_inode *inode) { if (erofs_is_packed_inode(inode)) { return cfg.c_mkfs_pclustersize_packed; + } else if (erofs_is_metabox_inode(inode)) { + return cfg.c_mkfs_pclustersize_metabox; #ifndef NDEBUG } else if (cfg.c_random_pclusterblks) { unsigned int pclusterblks = @@ -567,7 +570,8 @@ static int __z_erofs_compress_one(struct z_erofs_compress_sctx *ctx, unsigned int len = ctx->tail - ctx->head; bool is_packed_inode = erofs_is_packed_inode(inode); bool tsg = (ctx->seg_idx + 1 >= ictx->seg_num), final = !ctx->remaining; - bool may_packing = (cfg.c_fragments && tsg && final && !is_packed_inode); + bool may_packing = (cfg.c_fragments && tsg && final && !is_packed_inode && + !erofs_is_metabox_inode(inode)); bool data_unaligned = ictx->data_unaligned; bool may_inline = (cfg.c_ztailpacking && !data_unaligned && tsg && final && !may_packing); @@ -1247,6 +1251,7 @@ int z_erofs_compress_segment(struct z_erofs_compress_sctx *ctx, struct z_erofs_compress_ictx *ictx = ctx->ictx; struct erofs_inode *inode = ictx->inode; bool frag = cfg.c_fragments && !erofs_is_packed_inode(inode) && + !erofs_is_metabox_inode(inode) && ctx->seg_idx >= ictx->seg_num - 1; int fd = ictx->fd; int ret; @@ -1764,8 +1769,9 @@ void *erofs_begin_compressed_file(struct erofs_inode *inode, int fd, u64 fpos) { struct erofs_sb_info *sbi = inode->sbi; struct z_erofs_compress_ictx *ictx; - bool all_fragments = cfg.c_all_fragments && - !erofs_is_packed_inode(inode); + bool frag = cfg.c_fragments && !erofs_is_packed_inode(inode) && + !erofs_is_metabox_inode(inode); + bool all_fragments = cfg.c_all_fragments && frag; int ret; /* initialize per-file compression setting */ @@ -1799,8 +1805,10 @@ void *erofs_begin_compressed_file(struct erofs_inode *inode, int fd, u64 fpos) return ERR_PTR(-ENOMEM); } ictx->fd = fd; - - ictx->ccfg = &sbi->zmgr->ccfg[inode->z_algorithmtype[0]]; + if (erofs_is_metabox_inode(inode)) + ictx->ccfg = &sbi->zmgr->ccfg[cfg.c_mkfs_metabox_algid]; + else + ictx->ccfg = &sbi->zmgr->ccfg[inode->z_algorithmtype[0]]; inode->z_algorithmtype[0] = ictx->ccfg->algorithmtype; inode->z_algorithmtype[1] = 0; ictx->data_unaligned = erofs_sb_has_48bit(sbi) && @@ -1809,7 +1817,7 @@ void *erofs_begin_compressed_file(struct erofs_inode *inode, int fd, u64 fpos) if (cfg.c_fragments && !cfg.c_dedupe && !ictx->data_unaligned) inode->z_advise |= Z_EROFS_ADVISE_INTERLACED_PCLUSTER; - if (cfg.c_fragments && !erofs_is_packed_inode(inode)) { + if (frag) { ictx->tofh = z_erofs_fragments_tofh(inode, fd, fpos); if (ictx == &g_ictx && cfg.c_fragdedupe != FRAGDEDUPE_OFF) { /* @@ -2107,6 +2115,12 @@ int z_erofs_compress_init(struct erofs_sb_info *sbi, struct erofs_buffer_head *s return -EINVAL; } + if (cfg.c_mkfs_pclustersize_metabox > (s32)cfg.c_mkfs_pclustersize_max) { + erofs_err("invalid pclustersize for the metabox file %u", + cfg.c_mkfs_pclustersize_metabox); + return -EINVAL; + } + if (sb_bh && erofs_sb_has_compr_cfgs(sbi)) { ret = z_erofs_build_compr_cfgs(sbi, sb_bh, max_dict_size); if (ret) diff --git a/lib/config.c b/lib/config.c index 848bc590..f7c6fbad 100644 --- a/lib/config.c +++ b/lib/config.c @@ -36,6 +36,7 @@ void erofs_init_configure(void) cfg.c_uid = -1; cfg.c_gid = -1; cfg.c_max_decompressed_extent_bytes = -1; + cfg.c_mkfs_pclustersize_metabox = -1; erofs_stdout_tty = isatty(STDOUT_FILENO); } diff --git a/lib/inode.c b/lib/inode.c index 5f50c09f..20498027 100644 --- a/lib/inode.c +++ b/lib/inode.c @@ -28,6 +28,12 @@ #include "erofs/blobchunk.h" #include "erofs/fragments.h" #include "liberofs_private.h" +#include "liberofs_metabox.h" + +static inline bool erofs_is_special_identifier(const char *path) +{ + return path == EROFS_PACKED_INODE || path == EROFS_METABOX_INODE; +} #define S_SHIFT 12 static unsigned char erofs_ftype_by_mode[S_IFMT >> S_SHIFT] = { @@ -143,7 +149,8 @@ unsigned int erofs_iput(struct erofs_inode *inode) free(inode->compressmeta); free(inode->eof_tailraw); erofs_remove_ihash(inode); - free(inode->i_srcpath); + if (!erofs_is_special_identifier(inode->i_srcpath)) + free(inode->i_srcpath); if (inode->datasource == EROFS_INODE_DATA_SOURCE_DISKBUF) { erofs_diskbuf_close(inode->i_diskbuf); @@ -330,24 +337,39 @@ static int write_dirblock(struct erofs_sb_info *sbi, return erofs_blk_write(sbi, buf, blkaddr, 1); } +#define EROFS_NID_UNALLOCATED -1ULL + erofs_nid_t erofs_lookupnid(struct erofs_inode *inode) { struct erofs_buffer_head *const bh = inode->bh; struct erofs_sb_info *sbi = inode->sbi; erofs_off_t off, meta_offset; + erofs_nid_t nid; - if (bh && (long long)inode->nid <= 0) { + if (bh && inode->nid == EROFS_NID_UNALLOCATED) { erofs_mapbh(NULL, bh->block); off = erofs_btell(bh, false); - meta_offset = erofs_pos(sbi, sbi->meta_blkaddr); - DBG_BUGON(off < meta_offset); - inode->nid = (off - meta_offset) >> EROFS_ISLOTBITS; - erofs_dbg("Assign nid %llu to file %s (mode %05o)", - inode->nid, inode->i_srcpath, inode->i_mode); + if (!inode->in_metabox) { + meta_offset = erofs_pos(sbi, sbi->meta_blkaddr); + DBG_BUGON(off < meta_offset); + } else { + meta_offset = 0; + } + + nid = (off - meta_offset) >> EROFS_ISLOTBITS; + inode->nid = nid | + (u64)inode->in_metabox << EROFS_DIRENT_NID_METABOX_BIT; + erofs_dbg("Assign nid %s%llu to file %s (mode %05o)", + inode->in_metabox ? "[M]" : "", nid, + inode->i_srcpath, inode->i_mode); + } + if (__erofs_unlikely(IS_ROOT(inode))) { + if (inode->in_metabox) + DBG_BUGON(!erofs_sb_has_48bit(sbi)); + else if (inode->nid > 0xffff) + return sbi->root_nid; } - if (__erofs_unlikely(IS_ROOT(inode)) && inode->nid > 0xffff) - return sbi->root_nid; return inode->nid; } @@ -533,6 +555,9 @@ int erofs_write_file_from_buffer(struct erofs_inode *inode, char *buf) /* rules to decide whether a file could be compressed or not */ static bool erofs_file_is_compressible(struct erofs_inode *inode) { + if (erofs_is_metabox_inode(inode) && + cfg.c_mkfs_pclustersize_metabox < 0) + return false; if (cfg.c_compress_hints_file) return z_erofs_apply_compress_hints(inode); return true; @@ -618,6 +643,8 @@ int erofs_iflush(struct erofs_inode *inode) struct erofs_sb_info *sbi = inode->sbi; struct erofs_buffer_head *bh = inode->bh; erofs_off_t off = erofs_iloc(inode); + struct erofs_bufmgr *ibmgr = inode->in_metabox ? + erofs_metabox_bmgr(sbi) : sbi->bmgr; union { struct erofs_inode_compact dic; struct erofs_inode_extended die; @@ -722,7 +749,7 @@ int erofs_iflush(struct erofs_inode *inode) .iov_len = inode->xattr_isize }; } - ret = erofs_io_pwritev(&sbi->bdev, iov, iovcnt, off); + ret = erofs_io_pwritev(ibmgr->vf, iov, iovcnt, off); free(xattrs); if (ret != inode->inode_isize + inode->xattr_isize) return ret < 0 ? ret : -EIO; @@ -730,10 +757,10 @@ int erofs_iflush(struct erofs_inode *inode) off += ret; if (inode->extent_isize) { if (inode->datalayout == EROFS_INODE_CHUNK_BASED) { - ret = erofs_blob_write_chunk_indexes(inode, off); + ret = erofs_write_chunk_indexes(inode, ibmgr->vf, off); } else { /* write compression metadata */ off = roundup(off, 8); - ret = erofs_io_pwrite(&sbi->bdev, inode->compressmeta, + ret = erofs_io_pwrite(ibmgr->vf, inode->compressmeta, off, inode->extent_isize); } if (ret != inode->extent_isize) @@ -799,14 +826,16 @@ static bool erofs_inode_need_48bit(struct erofs_inode *inode) static int erofs_prepare_inode_buffer(struct erofs_inode *inode) { - struct erofs_bufmgr *bmgr = inode->sbi->bmgr; + struct erofs_sb_info *sbi = inode->sbi; + struct erofs_bufmgr *bmgr = sbi->bmgr; + struct erofs_bufmgr *ibmgr = bmgr; unsigned int inodesize; struct erofs_buffer_head *bh, *ibh; DBG_BUGON(inode->bh || inode->bh_inline); if (erofs_inode_need_48bit(inode)) { - if (!erofs_sb_has_48bit(inode->sbi)) + if (!erofs_sb_has_48bit(sbi)) return -ENOSPC; if (inode->inode_isize == sizeof(struct erofs_inode_compact) && inode->i_nlink != 1) @@ -837,7 +866,13 @@ static int erofs_prepare_inode_buffer(struct erofs_inode *inode) inode->datalayout = EROFS_INODE_FLAT_PLAIN; } - bh = erofs_balloc(bmgr, INODE, inodesize, inode->idata_size); + if (!erofs_is_special_identifier(inode->i_srcpath) && + erofs_metabox_bmgr(sbi)) + inode->in_metabox = true; + + if (inode->in_metabox) + ibmgr = erofs_metabox_bmgr(sbi) ?: bmgr; + bh = erofs_balloc(ibmgr, INODE, inodesize, inode->idata_size); if (bh == ERR_PTR(-ENOSPC)) { int ret; @@ -850,7 +885,7 @@ noinline: ret = erofs_prepare_tail_block(inode); if (ret) return ret; - bh = erofs_balloc(bmgr, INODE, inodesize, 0); + bh = erofs_balloc(ibmgr, INODE, inodesize, 0); if (IS_ERR(bh)) return PTR_ERR(bh); DBG_BUGON(inode->bh_inline); @@ -862,7 +897,7 @@ noinline: erofs_dbg("Inline %scompressed data (%u bytes) to %s", inode->compressed_idata ? "" : "un", inode->idata_size, inode->i_srcpath); - erofs_sb_set_ztailpacking(inode->sbi); + erofs_sb_set_ztailpacking(sbi); } else { inode->datalayout = EROFS_INODE_FLAT_INLINE; erofs_dbg("Inline tail-end data (%u bytes) to %s", @@ -881,20 +916,24 @@ noinline: bh->fsprivate = erofs_igrab(inode); bh->op = &erofs_write_inode_bhops; inode->bh = bh; - inode->i_ino[0] = ++inode->sbi->inos; /* inode serial number */ + inode->i_ino[0] = ++sbi->inos; /* inode serial number */ return 0; } static int erofs_bh_flush_write_inline(struct erofs_buffer_head *bh) { struct erofs_inode *const inode = bh->fsprivate; + struct erofs_sb_info *sbi = inode->sbi; + struct erofs_bufmgr *ibmgr = inode->in_metabox ? + erofs_metabox_bmgr(sbi) : sbi->bmgr; const erofs_off_t off = erofs_btell(bh, false); int ret; - ret = erofs_dev_write(inode->sbi, inode->idata, off, inode->idata_size); - if (ret) + ret = erofs_io_pwrite(ibmgr->vf, inode->idata, off, inode->idata_size); + if (ret < 0) return ret; - + if (ret != inode->idata_size) + return -EIO; free(inode->idata); inode->idata = NULL; @@ -1003,7 +1042,7 @@ static bool erofs_should_use_inode_extended(struct erofs_inode *inode, return true; if (inode->i_nlink > USHRT_MAX) return true; - if (path != EROFS_PACKED_INODE && !cfg.c_ignore_mtime && + if (!erofs_is_special_identifier(path) && !cfg.c_ignore_mtime && !erofs_sb_has_48bit(inode->sbi) && inode->i_mtime != inode->sbi->epoch) return true; @@ -1033,7 +1072,7 @@ int erofs_droid_inode_fsconfig(struct erofs_inode *inode, if (!cfg.fs_config_file && !cfg.mount_point) return 0; /* avoid loading special inodes */ - if (path == EROFS_PACKED_INODE) + if (erofs_is_special_identifier(path)) return 0; if (!cfg.mount_point || @@ -1095,7 +1134,7 @@ int __erofs_fill_inode(struct erofs_inode *inode, struct stat *st, erofs_err("gid overflow @ %s", path); inode->i_gid += cfg.c_gid_offset; - if (path == EROFS_PACKED_INODE) { + if (erofs_is_special_identifier(path)) { inode->i_mtime = sbi->epoch + sbi->build_time; inode->i_mtime_nsec = sbi->fixed_nsec; return 0; @@ -1144,9 +1183,13 @@ static int erofs_fill_inode(struct erofs_inode *inode, struct stat *st, return -EINVAL; } - inode->i_srcpath = strdup(path); - if (!inode->i_srcpath) - return -ENOMEM; + if (erofs_is_special_identifier(path)) { + inode->i_srcpath = (char *)path; + } else { + inode->i_srcpath = strdup(path); + if (!inode->i_srcpath) + return -ENOMEM; + } if (erofs_should_use_inode_extended(inode, path)) { if (cfg.c_force_inodeversion == FORCE_INODE_COMPACT) { @@ -1181,6 +1224,7 @@ struct erofs_inode *erofs_new_inode(struct erofs_sb_info *sbi) inode->dev = sbi->dev; inode->i_count = 1; inode->datalayout = EROFS_INODE_FLAT_PLAIN; + inode->nid = EROFS_NID_UNALLOCATED; init_list_head(&inode->i_hash); init_list_head(&inode->i_subdirs); @@ -1224,22 +1268,28 @@ static struct erofs_inode *erofs_iget_from_srcpath(struct erofs_sb_info *sbi, return inode; } -static void erofs_fixup_meta_blkaddr(struct erofs_inode *rootdir) +static void erofs_fixup_meta_blkaddr(struct erofs_inode *root) { const erofs_off_t rootnid_maxoffset = 0xffff << EROFS_ISLOTBITS; - struct erofs_buffer_head *const bh = rootdir->bh; - struct erofs_sb_info *sbi = rootdir->sbi; - erofs_off_t off, meta_offset; + struct erofs_buffer_head *const bh = root->bh; + struct erofs_sb_info *sbi = root->sbi; + erofs_off_t meta_offset = 0; + erofs_off_t off; erofs_mapbh(NULL, bh->block); off = erofs_btell(bh, false); - - if (off > rootnid_maxoffset) - meta_offset = round_up(off - rootnid_maxoffset, erofs_blksiz(sbi)); - else - meta_offset = 0; + if (!root->in_metabox && off > rootnid_maxoffset) + meta_offset = round_up(off - rootnid_maxoffset, + erofs_blksiz(sbi)); + else if (root->in_metabox && !erofs_sb_has_48bit(sbi)) { + sbi->build_time = sbi->epoch; + sbi->epoch = max_t(s64, 0, (s64)sbi->build_time - UINT32_MAX); + sbi->build_time -= sbi->epoch; + erofs_sb_set_48bit(sbi); + } sbi->meta_blkaddr = erofs_blknr(sbi, meta_offset); - rootdir->nid = (off - meta_offset) >> EROFS_ISLOTBITS; + root->nid = ((off - meta_offset) >> EROFS_ISLOTBITS) | + ((u64)root->in_metabox << EROFS_DIRENT_NID_METABOX_BIT); } static int erofs_inode_reserve_data_blocks(struct erofs_inode *inode) @@ -2027,7 +2077,7 @@ struct erofs_inode *erofs_mkfs_build_special_from_fd(struct erofs_sb_info *sbi, if (IS_ERR(inode)) return inode; - if (name == EROFS_PACKED_INODE) { + if (erofs_is_special_identifier(name)) { st.st_uid = st.st_gid = 0; st.st_nlink = 0; } @@ -2038,11 +2088,6 @@ struct erofs_inode *erofs_mkfs_build_special_from_fd(struct erofs_sb_info *sbi, return ERR_PTR(ret); } - if (name == EROFS_PACKED_INODE) { - inode->sbi->packed_nid = EROFS_PACKED_NID_UNALLOCATED; - inode->nid = inode->sbi->packed_nid; - } - if (cfg.c_compr_opts[0].alg && erofs_file_is_compressible(inode)) { ictx = erofs_begin_compressed_file(inode, fd, 0); diff --git a/lib/liberofs_metabox.h b/lib/liberofs_metabox.h new file mode 100644 index 00000000..75b1e69e --- /dev/null +++ b/lib/liberofs_metabox.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0 */ +#ifndef __EROFS_LIB_LIBEROFS_METABOX_H +#define __EROFS_LIB_LIBEROFS_METABOX_H + +#include "erofs/internal.h" + +extern const char *erofs_metabox_identifier; +#define EROFS_METABOX_INODE erofs_metabox_identifier + +static inline bool erofs_is_metabox_inode(struct erofs_inode *inode) +{ + return inode->i_srcpath == EROFS_METABOX_INODE; +} + +int erofs_metabox_init(struct erofs_sb_info *sbi); +struct erofs_bufmgr *erofs_metabox_bmgr(struct erofs_sb_info *sbi); +int erofs_metabox_iflush(struct erofs_sb_info *sbi); + +#endif diff --git a/lib/metabox.c b/lib/metabox.c new file mode 100644 index 00000000..4b1a9c58 --- /dev/null +++ b/lib/metabox.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0 +#include <stdlib.h> +#include "erofs/cache.h" +#include "erofs/inode.h" +#include "liberofs_private.h" +#include "liberofs_metabox.h" + +const char *erofs_metabox_identifier = "metabox"; + +struct erofs_metaboxmgr { + struct erofs_vfile vf; + struct erofs_bufmgr *bmgr; +}; + +int erofs_metabox_init(struct erofs_sb_info *sbi) +{ + struct erofs_metaboxmgr *m2gr; + int ret; + + m2gr = malloc(sizeof(*m2gr)); + if (!m2gr) + return -ENOMEM; + + ret = erofs_tmpfile(); + if (ret < 0) + goto out_err; + + m2gr->vf = (struct erofs_vfile){ .fd = ret }; + m2gr->bmgr = erofs_buffer_init(sbi, 0, &m2gr->vf); + if (m2gr->bmgr) { + erofs_sb_set_metabox(sbi); + sbi->m2gr = m2gr; + return 0; + } + ret = -ENOMEM; +out_err: + free(m2gr); + return ret; +} + +struct erofs_bufmgr *erofs_metabox_bmgr(struct erofs_sb_info *sbi) +{ + return sbi->m2gr ? sbi->m2gr->bmgr : NULL; +} + +int erofs_metabox_iflush(struct erofs_sb_info *sbi) +{ + struct erofs_metaboxmgr *m2gr = sbi->m2gr; + struct erofs_inode *inode; + int err; + + if (!m2gr || !erofs_sb_has_metabox(sbi)) + return -EINVAL; + + err = erofs_bflush(m2gr->bmgr, NULL); + if (err) + return err; + + if (erofs_io_lseek(&m2gr->vf, 0, SEEK_END) <= 0) + return 0; + inode = erofs_mkfs_build_special_from_fd(sbi, m2gr->vf.fd, + EROFS_METABOX_INODE); + sbi->metabox_nid = erofs_lookupnid(inode); + erofs_iput(inode); + return 0; +} diff --git a/mkfs/main.c b/mkfs/main.c index 30804d10..e026596f 100644 --- a/mkfs/main.c +++ b/mkfs/main.c @@ -30,6 +30,7 @@ #include "erofs/rebuild.h" #include "../lib/liberofs_private.h" #include "../lib/liberofs_uuid.h" +#include "../lib/liberofs_metabox.h" #include "../lib/compressor.h" static struct option long_options[] = { @@ -159,6 +160,8 @@ static void usage(int argc, char **argv) " -C# specify the size of compress physical cluster in bytes\n" " -EX[,...] X=extended options\n" " -L volume-label set the volume label (maximum 15 bytes)\n" + " -m#[:X] enable metadata compression (# = physical cluster size in bytes;\n" + " X = another compression algorithm for metadata)\n" " -T# specify a fixed UNIX timestamp # as build time\n" " --all-time the timestamp is also applied to all files (default)\n" " --mkfs-time the timestamp is applied as build time only\n" @@ -237,10 +240,12 @@ static void version(void) } static unsigned int pclustersize_packed, pclustersize_max; +static int pclustersize_metabox = -1; static struct erofs_tarfile erofstar = { .global.xattrs = LIST_HEAD_INIT(erofstar.global.xattrs) }; static bool tar_mode, rebuild_mode, incremental_mode; +static u8 metabox_algorithmid; enum { EROFS_MKFS_DATA_IMPORT_DEFAULT, @@ -249,7 +254,7 @@ enum { EROFS_MKFS_DATA_IMPORT_SPARSE, } dataimport_mode; -static unsigned int rebuild_src_count; +static unsigned int rebuild_src_count, total_ccfgs; static LIST_HEAD(rebuild_src_list); static u8 fixeduuid[16]; static bool valid_fixeduuid; @@ -575,19 +580,19 @@ static int mkfs_parse_one_compress_alg(char *alg, static int mkfs_parse_compress_algs(char *algs) { - unsigned int i; char *s; int ret; - for (s = strtok(algs, ":"), i = 0; s; s = strtok(NULL, ":"), ++i) { - if (i >= EROFS_MAX_COMPR_CFGS - 1) { + for (s = strtok(algs, ":"); s; s = strtok(NULL, ":")) { + if (total_ccfgs >= EROFS_MAX_COMPR_CFGS - 1) { erofs_err("too many algorithm types"); return -EINVAL; } - ret = mkfs_parse_one_compress_alg(s, &cfg.c_compr_opts[i]); + ret = mkfs_parse_one_compress_alg(s, &cfg.c_compr_opts[total_ccfgs]); if (ret) return ret; + ++total_ccfgs; } return 0; } @@ -694,7 +699,7 @@ static int mkfs_parse_options_cfg(int argc, char *argv[]) bool quiet = false; bool has_timestamp = false; - while ((opt = getopt_long(argc, argv, "C:E:L:T:U:b:d:x:z:Vh", + while ((opt = getopt_long(argc, argv, "C:E:L:T:U:b:d:m:x:z:Vh", long_options, NULL)) != -1) { switch (opt) { case 'z': @@ -849,6 +854,25 @@ static int mkfs_parse_options_cfg(int argc, char *argv[]) } pclustersize_max = i; break; + case 'm': { + char *algid = strchr(optarg, ':'); + + if (algid) { + algid[0] = '\0'; + metabox_algorithmid = + strtoul(algid + 1, &endptr, 0); + if (*endptr != '\0') { + err = mkfs_parse_one_compress_alg(algid + 1, + &cfg.c_compr_opts[total_ccfgs]); + if (err) + return err; + metabox_algorithmid = total_ccfgs++; + } + } + pclustersize_metabox = atoi(optarg); + break; + } + case 11: i = strtol(optarg, &endptr, 0); if (*endptr != '\0') { @@ -1108,6 +1132,18 @@ static int mkfs_parse_options_cfg(int argc, char *argv[]) cfg.c_mkfs_pclustersize_packed = pclustersize_packed; } + if (pclustersize_metabox >= 0) { + if (pclustersize_metabox && + (pclustersize_metabox < erofs_blksiz(&g_sbi) || + pclustersize_metabox % erofs_blksiz(&g_sbi))) { + erofs_err("invalid pcluster size %u for the metabox inode", + pclustersize_metabox); + return -EINVAL; + } + cfg.c_mkfs_pclustersize_metabox = pclustersize_metabox; + cfg.c_mkfs_metabox_algid = metabox_algorithmid; + } + if (has_timestamp && cfg.c_timeinherit == TIMESTAMP_UNSPECIFIED) cfg.c_timeinherit = TIMESTAMP_FIXED; return 0; @@ -1334,6 +1370,15 @@ int main(int argc, char **argv) } } + if (cfg.c_mkfs_pclustersize_metabox >= 0) { + err = erofs_metabox_init(&g_sbi); + if (err) { + erofs_err("failed to initialize metabox: %s", + erofs_strerror(err)); + return 1; + } + } + #ifndef NDEBUG if (cfg.c_random_pclusterblks) srand(time(NULL)); @@ -1541,6 +1586,11 @@ int main(int argc, char **argv) } } + erofs_update_progressinfo("Handling metabox ..."); + erofs_metabox_iflush(&g_sbi); + if (err) + goto exit; + if ((cfg.c_fragments || cfg.c_extra_ea_name_prefixes) && erofs_sb_has_fragments(&g_sbi)) { erofs_update_progressinfo("Handling packed data ..."); -- 2.43.5