Re: [PATCH] erofs-utils: lib: fix ztailpacking fallback across lclusters
Zhiguo Niu <[email protected]> Mon, 20 Jul 2026 19:42:53 +0800
| Newsgroups | org.ozlabs.lists.linux-erofs |
|---|---|
| Message-ID | <CAHJ8P3JK5gKS=RH2b=waY7g1AJ7S6ZHzDV0asg46pkzHsWzMHw@mail.gmail.com> |
Yifan Zhao <[email protected]> 于2026年7月9日周四 20:36写道: > > With ztailpacking, the final compressed pcluster is first stored as > inline data. If the inode metadata area cannot hold it, mkfs falls back > to a normal tail block and drops the inline pcluster marker. > > The current fallback path assumes that the inline tail pcluster belongs > to the EOF lcluster. That is not always true: the tail pcluster can > start in the previous lcluster and end at EOF, while its raw size still > fits in one block. In that case, patching the EOF lcluster is > semantically wrong. > > Let's keep raw tail data whenever it fits in one block, and convert the > corresponding lcluster index to PLAIN during fallback. > > Reported-by: Alberto Salvia Novella <[email protected]> > Closes: https://github.com/erofs/erofs-utils/issues/51 > Assisted-by: Codex:GPT-5.5 > Signed-off-by: Yifan Zhao <[email protected]> > --- Hi Yifan, I tested this patch to focus on the issue fixed by commit 277a42502a7a, and it passed. But I have some questions: > include/erofs/internal.h | 5 +- > lib/compress.c | 109 ++++++++++++++++++++++++++++----------- > 2 files changed, 83 insertions(+), 31 deletions(-) > > diff --git a/include/erofs/internal.h b/include/erofs/internal.h > index 2cc9cc8..bdde41f 100644 > --- a/include/erofs/internal.h > +++ b/include/erofs/internal.h > @@ -212,8 +212,11 @@ struct erofs_diskbuf; > > enum erofs_idata_type { > EROFS_IDATA_TYPE_RAW, > - EROFS_IDATA_TYPE_COMPRESSED_DEFAULT, > + EROFS_IDATA_TYPE_COMPRESSED, > + /* compressed idata follows a final 2B compacted index pack */ > EROFS_IDATA_TYPE_COMPRESSED_END_OF_2B, > + /* compressed idata follows a final single-entry 4B pack after a 2B pack */ > + EROFS_IDATA_TYPE_COMPRESSED_4B1_PREV2B, > }; > > #define EROFS_I_BLKADDR_DEV_ID_BIT 48 > diff --git a/lib/compress.c b/lib/compress.c > index f7ad5a1..ec90f65 100644 > --- a/lib/compress.c > +++ b/lib/compress.c > @@ -483,7 +483,7 @@ static int z_erofs_fill_inline_data(struct erofs_inode *inode, void *data, > { > inode->z_advise |= Z_EROFS_ADVISE_INLINE_PCLUSTER; > inode->idata_size = len; > - inode->idata_type = EROFS_IDATA_TYPE_COMPRESSED_DEFAULT; > + inode->idata_type = EROFS_IDATA_TYPE_COMPRESSED; > > inode->idata = malloc(inode->idata_size); > if (!inode->idata) > @@ -664,7 +664,7 @@ frag_packing: > ictx->fragemitted = true; > /* tailpcluster should be less than 1 block */ > } else if (may_inline && len == e->length && compressedsize < blksz) { > - if (ctx->clusterofs + len <= blksz) { Shouldn't this condition restrict the `tail pcluster` so that it corresponds to only eof`lcluster`? and we drop the inline pcluster just when eof_tailraw is not null. Thanks! > + if (len <= blksz) { > inode->eof_tailraw = malloc(len); > if (!inode->eof_tailraw) > return -ENOMEM; > @@ -962,6 +962,14 @@ int z_erofs_convert_to_compacted_format(struct erofs_inode *inode, > dummy_head = true; > } > > + if (inode->idata_size) { > + if (compacted_2b && !compacted_4b_end) > + inode->idata_type = EROFS_IDATA_TYPE_COMPRESSED_END_OF_2B; > + else if (compacted_2b && compacted_4b_end == 1) > + inode->idata_type = > + EROFS_IDATA_TYPE_COMPRESSED_4B1_PREV2B; > + } > + > /* generate compacted_4b_initial */ > while (compacted_4b_initial) { > in = parse_legacy_indexes(cv, 2, in); > @@ -974,8 +982,6 @@ int z_erofs_convert_to_compacted_format(struct erofs_inode *inode, > > /* generate compacted_2b */ > if (compacted_2b) { > - if (!compacted_4b_end && inode->idata_size) > - inode->idata_type = EROFS_IDATA_TYPE_COMPRESSED_END_OF_2B; > do { > in = parse_legacy_indexes(cv, 16, in); > out = write_compacted_indexes(out, cv, &blkaddr, > @@ -1205,11 +1211,65 @@ out: > return metabuf; > } > > +static void z_erofs_patch_tail_compacted_index(struct erofs_inode *inode, > + bool previous, > + unsigned int type) > +{ > + const unsigned int totalidx = BLK_ROUND_UP(inode->sbi, inode->i_size); > + const unsigned int lobits = max_t(unsigned int, inode->z_lclusterbits, > + ilog2(Z_EROFS_LI_D0_CBLKCNT) + 1U); > + u8 *base = inode->compressmeta; > + u8 *pack = base + inode->extent_isize; > + unsigned int bitpos, bitoff; > + u8 *out; > + u32 v; > + > + DBG_BUGON(!totalidx); > + DBG_BUGON(inode->z_lclusterbits > 14); > + > + if (inode->idata_type == EROFS_IDATA_TYPE_COMPRESSED_END_OF_2B) { > + pack -= 32; > + bitpos = 14 * (previous ? 14 : 15); > + goto out; > + } > + > + /* Last compacted index pack is 4B */ > + pack -= 8; > + if (!(totalidx & 1)) { > + bitpos = previous ? 0 : 16; > + goto out; > + } > + > + if (!previous) { > + bitpos = 0; > + goto out; > + } > + > + /* Second to last compacted index pack is 2B */ > + if (inode->idata_type == EROFS_IDATA_TYPE_COMPRESSED_4B1_PREV2B) { > + pack -= 32; > + bitpos = 14 * 15; > + } else { > + pack -= 8; > + bitpos = 16; > + } > +out: > + DBG_BUGON(pack < base); > + bitoff = bitpos & 7; > + out = pack + bitpos / 8; > + v = get_unaligned_le32(out); > + v &= ~(Z_EROFS_LI_LCLUSTER_TYPE_MASK << (lobits + bitoff)); > + v |= type << (lobits + bitoff); > + put_unaligned_le32(v, out); > +} > + > void z_erofs_drop_inline_pcluster(struct erofs_inode *inode) > { > struct erofs_sb_info *sbi = inode->sbi; > const unsigned int type = Z_EROFS_LCLUSTER_TYPE_PLAIN; > struct z_erofs_map_header *h = inode->compressmeta; > + erofs_off_t rawstart; > + erofs_blk_t head_lcn, eof_lcn; > > h->h_advise = cpu_to_le16(le16_to_cpu(h->h_advise) & > ~Z_EROFS_ADVISE_INLINE_PCLUSTER); > @@ -1218,38 +1278,27 @@ void z_erofs_drop_inline_pcluster(struct erofs_inode *inode) > if (!inode->eof_tailraw) > return; > DBG_BUGON(inode->idata_type == EROFS_IDATA_TYPE_RAW); > + DBG_BUGON(!inode->i_size); > + DBG_BUGON(inode->eof_tailrawsize > erofs_blksiz(sbi)); > + DBG_BUGON(inode->eof_tailrawsize > inode->i_size); > + > + rawstart = inode->i_size - inode->eof_tailrawsize; > + head_lcn = rawstart >> sbi->blkszbits; > + eof_lcn = (inode->i_size - 1) >> sbi->blkszbits; > + DBG_BUGON(head_lcn != eof_lcn && head_lcn + 1 != eof_lcn); > > - /* patch the EOF lcluster to uncompressed type first */ > + /* patch the tail pcluster head to uncompressed type first */ > if (inode->datalayout == EROFS_INODE_COMPRESSED_FULL) { > struct z_erofs_lcluster_index *di = > - (inode->compressmeta + inode->extent_isize) - > - sizeof(struct z_erofs_lcluster_index); > + (void *)((u8 *)inode->compressmeta + > + Z_EROFS_LEGACY_MAP_HEADER_SIZE + > + head_lcn * > + sizeof(struct z_erofs_lcluster_index)); > > di->di_advise = cpu_to_le16(type); > } else if (inode->datalayout == EROFS_INODE_COMPRESSED_COMPACT) { > - /* handle the last compacted 4B/2B pack */ > - unsigned int lclusterbits = inode->z_lclusterbits; > - unsigned int lobits, eofs, base, pos, v; > - u8 *out; > - > - lobits = max(lclusterbits, ilog2(Z_EROFS_LI_D0_CBLKCNT) + 1U); > - > - if (inode->idata_type == EROFS_IDATA_TYPE_COMPRESSED_DEFAULT) { > - eofs = inode->extent_isize - > - (4 << (BLK_ROUND_UP(sbi, inode->i_size) & 1)); > - base = round_down(eofs, 8); > - pos = 16 /* encodebits */ * ((eofs - base) / 4); > - out = inode->compressmeta + base + pos / 8; > - } else { > - out = inode->compressmeta + inode->extent_isize - > - sizeof(__le32) - sizeof(__le16); > - lobits = 16 - 14 /* encodebits */ + lobits; > - } > - > - v = (get_unaligned_le16(out) & (BIT(lobits) - 1)) | > - (type << lobits); > - *out = v & 0xff; > - *(out + 1) = v >> 8; > + z_erofs_patch_tail_compacted_index(inode, head_lcn != eof_lcn, > + type); > } else { > DBG_BUGON(1); > return; > -- > 2.47.3 > >