[PATCH] erofs-utils: support ztailpacking with encoded extents
Saksham
aghi.saksham5 at gmail.com
Tue Mar 31 05:29:11 AEDT 2026
Currently, ztailpacking (inline pclusters for compressed files) is only
supported for the legacy compact and full index layouts. When the
encoded extents layout (a.k.a. non-compact indexes, Layout 2) is used,
the mkfs process explicitly skips the encoded extents path if
ztailpacking is enabled (idata_size > 0), and there is a TODO in
z_erofs_write_indexes to address this.
This patch implements support for ztailpacking within the encoded
extents framework. The encoded extents layout is more flexible and
robust, especially for 48-bit physical addresses or complex extent
mappings. Supporting ztailpacking here allows for better storage
efficiency even when using advanced features.
Key changes:
1. lib/compress.c:
- z_erofs_write_extents() is updated to correctly track physical
continuity (pend) when inlined extents are encountered. An inlined
extent breaks the contiguous physical block sequence, so pend is
reset to EROFS_NULL_ADDR.
- z_erofs_write_indexes() now allows the encoded extents path even if
idata_size is non-zero. It still falls back to legacy indexes if
the encoded format is larger (returns -EAGAIN).
2. lib/zmap.c:
- z_erofs_map_blocks_ext() is updated to handle the
Z_EROFS_ADVISE_INLINE_PCLUSTER advise bit. For the tail-packed
extent, it calculates the absolute disk offset by adding the
metadata size to the inode's metadata start position.
- It correctly sets EROFS_MAP_META and EROFS_MAP_ENCODED flags for
inlined extents.
- When EROFS_GET_BLOCKS_FINDTAIL is passed, it correctly initializes
vi->z_fragmentoff and vi->z_idata_size from the encoded extent
data.
- z_erofs_fill_inode_lazy() is updated to trigger a FINDTAIL map
operation when an encoded-extent-based inode has the inline
pcluster bit set. This ensures all internal metadata is correctly
loaded upon the first access.
With this patch, mkfs.erofs can now generate images that combine
the benefits of encoded extents and tail-packing, as verified by
dump.erofs and content validation.
Signed-off-by: Saksham <aghi.saksham5 at gmail.com>
---
lib/compress.c | 15 ++++++++++++---
lib/zmap.c | 31 +++++++++++++++++++++++++++++--
2 files changed, 41 insertions(+), 5 deletions(-)
diff --git a/lib/compress.c b/lib/compress.c
index 4a0d890..7f723f0 100644
--- a/lib/compress.c
+++ b/lib/compress.c
@@ -1068,10 +1068,16 @@ static void *z_erofs_write_extents(struct z_erofs_compress_ictx *ctx)
pstart_hi |= (ei->e.pstart > UINT32_MAX);
if ((ei->e.pstart | ei->e.plen) & ((1U << sbi->blkszbits) - 1))
unaligned_data = true;
- if (pend != ei->e.pstart)
+
+ /*
+ * Inlined extents (ztailpacking) or non-contiguous pclusters
+ * break physical continuity.
+ */
+ if (ei->e.inlined || pend != ei->e.pstart)
pend = EROFS_NULL_ADDR;
else
pend += ei->e.plen;
+
if (ei->e.length != 1 << lclusterbits) {
if (ei->list.next != &ctx->extents ||
ei->e.length > 1 << lclusterbits)
@@ -1149,8 +1155,11 @@ static void *z_erofs_write_indexes(struct z_erofs_compress_ictx *ctx)
struct z_erofs_extent_item *ei, *n;
void *metabuf;
- /* TODO: support writing encoded extents for ztailpacking later. */
- if (erofs_sb_has_48bit(sbi) && !inode->idata_size) {
+ /*
+ * Encoded extents (a.k.a. non-compact indexes) are preferred for
+ * 48-bit physical addresses or when ztailpacking is used.
+ */
+ if (erofs_sb_has_48bit(sbi) || inode->idata_size) {
metabuf = z_erofs_write_extents(ctx);
if (metabuf != ERR_PTR(-EAGAIN)) {
if (IS_ERR(metabuf))
diff --git a/lib/zmap.c b/lib/zmap.c
index 0e7af4e..f98512f 100644
--- a/lib/zmap.c
+++ b/lib/zmap.c
@@ -618,11 +618,28 @@ static int z_erofs_map_blocks_ext(struct erofs_inode *vi,
if (lstart < lend) {
map->m_la = lstart;
- if (last && (vi->z_advise & Z_EROFS_ADVISE_FRAGMENT_PCLUSTER)) {
- map->m_flags = EROFS_MAP_FRAGMENT;
+ if (flags & EROFS_GET_BLOCKS_FINDTAIL)
+ vi->z_tailextent_headlcn = lstart >> vi->z_lclusterbits;
+
+ if (last && (vi->z_advise & (Z_EROFS_ADVISE_FRAGMENT_PCLUSTER |
+ Z_EROFS_ADVISE_INLINE_PCLUSTER))) {
+ if (vi->z_advise & Z_EROFS_ADVISE_INLINE_PCLUSTER) {
+ map->m_flags |= EROFS_MAP_META;
+ map->m_pa = pos + vi->z_extents * recsz;
+ if (recsz <= 4)
+ map->m_pa += 8;
+ } else {
+ map->m_flags = EROFS_MAP_FRAGMENT;
+ }
vi->z_fragmentoff = map->m_plen;
if (recsz > offsetof(struct z_erofs_extent, pstart_lo))
vi->z_fragmentoff |= map->m_pa << 32;
+
+ if (vi->z_advise & Z_EROFS_ADVISE_INLINE_PCLUSTER) {
+ vi->z_fragmentoff = map->m_pa;
+ vi->z_idata_size = map->m_plen &
+ Z_EROFS_EXTENT_PLEN_MASK;
+ }
} else if (map->m_plen & Z_EROFS_EXTENT_PLEN_MASK) {
map->m_flags |= EROFS_MAP_MAPPED |
EROFS_MAP_FULL_MAPPED | EROFS_MAP_ENCODED;
@@ -677,6 +694,16 @@ static int z_erofs_fill_inode_lazy(struct erofs_inode *vi)
(vi->z_advise & Z_EROFS_ADVISE_EXTENTS)) {
vi->z_extents = le32_to_cpu(h->h_extents_lo) |
((u64)le16_to_cpu(h->h_extents_hi) << 32);
+ if (vi->z_advise & Z_EROFS_ADVISE_INLINE_PCLUSTER) {
+ struct erofs_map_blocks map = {
+ .buf = __EROFS_BUF_INITIALIZER
+ };
+
+ err = z_erofs_map_blocks_ext(vi, &map,
+ EROFS_GET_BLOCKS_FINDTAIL);
+ if (err < 0)
+ goto out_put_metabuf;
+ }
goto done;
}
vi->z_algorithmtype[0] = h->h_algorithmtype & 15;
--
2.53.0
More information about the Linux-erofs
mailing list