With ztailpacking enabled, the current process assumes that a compacted_4b_end
always exists in the compacted pack. However, in some specific files, the
compacted pack may not have a compacted_4b_end. This leads to an incorrect
modification of the last compacted_2B entry, resulting in incorrect modification
of its clusterofs. In subsequent fsck operations, incorrect parameters will
affect the decompression of the penultimate pcluster.

This patch also handle the last compacted_2B pack correctly.

Fixes: a7c1f0575ef8 ("erofs-utils: lib: refine tailpcluster compression 
approach")

Signed-off-by: Zhiguo Niu <[email protected]>
Suggested-by: Gao Xiang <[email protected]>
---
v2: modity according to Xiang's suggestions
---
 include/erofs/defs.h     |  5 +++++
 include/erofs/internal.h |  8 ++++++-
 lib/compress.c           | 55 ++++++++++++++++++++++++++++++------------------
 lib/inode.c              |  5 +++--
 4 files changed, 49 insertions(+), 24 deletions(-)

diff --git a/include/erofs/defs.h b/include/erofs/defs.h
index 5724c27..6b7952c 100644
--- a/include/erofs/defs.h
+++ b/include/erofs/defs.h
@@ -218,6 +218,11 @@ typedef int64_t         s64;
 #define get_unaligned(ptr)     __get_unaligned_t(typeof(*(ptr)), (ptr))
 #define put_unaligned(val, ptr) __put_unaligned_t(typeof(*(ptr)), (val), (ptr))
 
+static inline u16 get_unaligned_le16(const void *p)
+{
+       return le16_to_cpu(__get_unaligned_t(__le16, p));
+}
+
 static inline u32 get_unaligned_le32(const void *p)
 {
        return le32_to_cpu(__get_unaligned_t(__le32, p));
diff --git a/include/erofs/internal.h b/include/erofs/internal.h
index 450e264..2cc9cc8 100644
--- a/include/erofs/internal.h
+++ b/include/erofs/internal.h
@@ -210,6 +210,12 @@ struct erofs_diskbuf;
 #define EROFS_INODE_DATA_SOURCE_RESVSP         3
 #define EROFS_INODE_DATA_SOURCE_REBUILD_BLOB   4
 
+enum erofs_idata_type {
+       EROFS_IDATA_TYPE_RAW,
+       EROFS_IDATA_TYPE_COMPRESSED_DEFAULT,
+       EROFS_IDATA_TYPE_COMPRESSED_END_OF_2B,
+};
+
 #define EROFS_I_BLKADDR_DEV_ID_BIT             48
 
 struct erofs_inode {
@@ -262,7 +268,7 @@ struct erofs_inode {
        unsigned short idata_size;
        char datasource;
        bool in_metabox;
-       bool compressed_idata;
+       char idata_type;
        bool lazy_tailblock;
        bool opaque;
        /* OVL: non-merge dir that may contain whiteout entries */
diff --git a/lib/compress.c b/lib/compress.c
index 62d2672..03ed4d7 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->compressed_idata = !raw;
+       inode->idata_type = EROFS_IDATA_TYPE_COMPRESSED_DEFAULT;
 
        inode->idata = malloc(inode->idata_size);
        if (!inode->idata)
@@ -973,14 +973,17 @@ int z_erofs_convert_to_compacted_format(struct 
erofs_inode *inode,
        DBG_BUGON(compacted_4b_initial);
 
        /* generate compacted_2b */
-       while (compacted_2b) {
-               in = parse_legacy_indexes(cv, 16, in);
-               out = write_compacted_indexes(out, cv, &blkaddr,
-                                             2, logical_clusterbits, false,
-                                             &dummy_head, big_pcluster);
-               compacted_2b -= 16;
+       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,
+                                                     2, logical_clusterbits, 
false,
+                                                     &dummy_head, 
big_pcluster);
+                       compacted_2b -= 16;
+               } while(compacted_2b);
        }
-       DBG_BUGON(compacted_2b);
 
        /* generate compacted_4b_end */
        while (compacted_4b_end > 1) {
@@ -1210,10 +1213,11 @@ void z_erofs_drop_inline_pcluster(struct erofs_inode 
*inode)
 
        h->h_advise = cpu_to_le16(le16_to_cpu(h->h_advise) &
                                  ~Z_EROFS_ADVISE_INLINE_PCLUSTER);
+       DBG_BUGON(inode->idata_size != le16_to_cpu(h->h_idata_size));
        h->h_idata_size = 0;
        if (!inode->eof_tailraw)
                return;
-       DBG_BUGON(inode->compressed_idata != true);
+       DBG_BUGON(inode->idata_type == EROFS_IDATA_TYPE_RAW);
 
        /* patch the EOF lcluster to uncompressed type first */
        if (inode->datalayout == EROFS_INODE_COMPRESSED_FULL) {
@@ -1223,19 +1227,28 @@ void z_erofs_drop_inline_pcluster(struct erofs_inode 
*inode)
 
                di->di_advise = cpu_to_le16(type);
        } else if (inode->datalayout == EROFS_INODE_COMPRESSED_COMPACT) {
-               /* handle the last compacted 4B pack */
-               unsigned int eofs, base, pos, v, lo;
+               /* handle the last compacted pack */
+               unsigned int lclusterbits = inode->z_lclusterbits;
+               unsigned int lobits, eofs, base, pos, v;
                u8 *out;
 
-               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;
-               lo = erofs_blkoff(sbi, get_unaligned_le32(out + pos / 8));
-               v = (type << sbi->blkszbits) | lo;
-               out[pos / 8] = v & 0xff;
-               out[pos / 8 + 1] = v >> 8;
+               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 - 4 - 2;
+                       lobits = 16 - 14 /* encodebits */ + lobits;
+               }
+               v = (get_unaligned_le16(out) & (BIT(lobits) - 1)) |
+                       (type << lobits);
+               *out = v & 0xff;
+               *(out + 1) = v >> 8;
+
        } else {
                DBG_BUGON(1);
                return;
@@ -1244,7 +1257,7 @@ void z_erofs_drop_inline_pcluster(struct erofs_inode 
*inode)
        /* replace idata with prepared uncompressed data */
        inode->idata = inode->eof_tailraw;
        inode->idata_size = inode->eof_tailrawsize;
-       inode->compressed_idata = false;
+       inode->idata_type = EROFS_IDATA_TYPE_RAW;
        inode->eof_tailraw = NULL;
 }
 
diff --git a/lib/inode.c b/lib/inode.c
index 95fd93b..ce419c5 100644
--- a/lib/inode.c
+++ b/lib/inode.c
@@ -1043,7 +1043,7 @@ noinline:
                if (is_inode_layout_compression(inode)) {
                        DBG_BUGON(!params->ztailpacking);
                        erofs_dbg("Inline %scompressed data (%u bytes) to %s",
-                                 inode->compressed_idata ? "" : "un",
+                                 inode->idata_type == EROFS_IDATA_TYPE_RAW ? 
"un": "",
                                  inode->idata_size, inode->i_srcpath);
                        erofs_sb_set_ztailpacking(sbi);
                } else {
@@ -1149,7 +1149,8 @@ static int erofs_write_tail_end(struct erofs_importer *im,
                pos = erofs_btell(bh, true) - erofs_blksiz(sbi);
 
                /* 0'ed data should be padded at head for 0padding conversion */
-               h0 = erofs_sb_has_lz4_0padding(sbi) && inode->compressed_idata;
+               h0 = erofs_sb_has_lz4_0padding(sbi) &&
+                       inode->idata_type != EROFS_IDATA_TYPE_RAW;
                DBG_BUGON(inode->idata_size > erofs_blksiz(sbi));
 
                iov[h0] = (struct iovec) { .iov_base = inode->idata,
-- 
1.9.1


Reply via email to