[PATCH v2] erofs-utils: mkfs: fix to handle last compacted_2B pack in z_erofs_drop_inline_pcluster

Zhiguo Niu <[email protected]>
Newsgroups org.ozlabs.lists.linux-erofs
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.