erofs-utils: fix the previous pcluster CBLKCNT missing for big pcluster dedupe
authorGao Xiang <hsiangkao@linux.alibaba.com>
Thu, 21 Sep 2023 03:24:17 +0000 (11:24 +0800)
committerGao Xiang <hsiangkao@linux.alibaba.com>
Thu, 21 Sep 2023 03:32:02 +0000 (11:32 +0800)
Similar to 876bec09e48a ("erofs-utils: lib: fix missing CBLKCNT for
big pcluster dedupe"), the previous CBLKCNT cannot be dropped due to
the extent shortening process.

It may cause data corruption on specific data patterns only if both
big pcluster and dedupe features are enabled.

Link: https://lore.kernel.org/r/20230921032417.82739-1-hsiangkao@linux.alibaba.com
Fixes: f3f9a2ce3137 ("erofs-utils: mkfs: introduce global compressed data deduplication")
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
lib/compress.c

index 81f277ad372620bd95f14719e9bd3259733df4dd..f6dc12af24f3cbb515c08eebbf035acbe847dda3 100644 (file)
@@ -170,6 +170,7 @@ static int z_erofs_compress_dedupe(struct z_erofs_vle_compress_ctx *ctx,
                                   unsigned int *len)
 {
        struct erofs_inode *inode = ctx->inode;
+       const unsigned int lclustermask = (1 << inode->z_logical_clusterbits) - 1;
        struct erofs_sb_info *sbi = inode->sbi;
        int ret = 0;
 
@@ -205,22 +206,32 @@ static int z_erofs_compress_dedupe(struct z_erofs_vle_compress_ctx *ctx,
                 * decompresssion could be done as another try in practice.
                 */
                if (dctx.e.compressedblks > 1 &&
-                   (ctx->clusterofs + ctx->e.length - delta) % erofs_blksiz(sbi) +
-                       dctx.e.length < 2 * erofs_blksiz(sbi))
+                   ((ctx->clusterofs + ctx->e.length - delta) & lclustermask) +
+                       dctx.e.length < 2 * (lclustermask + 1))
                        break;
 
-               /* fall back to noncompact indexes for deduplication */
-               inode->z_advise &= ~Z_EROFS_ADVISE_COMPACTED_2B;
-               inode->datalayout = EROFS_INODE_COMPRESSED_FULL;
-               erofs_sb_set_dedupe(sbi);
-
                if (delta) {
                        DBG_BUGON(delta < 0);
                        DBG_BUGON(!ctx->e.length);
+
+                       /*
+                        * For big pcluster dedupe, if we decide to shorten the
+                        * previous big pcluster, make sure that the previous
+                        * CBLKCNT is still kept.
+                        */
+                       if (ctx->e.compressedblks > 1 &&
+                           (ctx->clusterofs & lclustermask) + ctx->e.length
+                               - delta < 2 * (lclustermask + 1))
+                               break;
                        ctx->e.partial = true;
                        ctx->e.length -= delta;
                }
 
+               /* fall back to noncompact indexes for deduplication */
+               inode->z_advise &= ~Z_EROFS_ADVISE_COMPACTED_2B;
+               inode->datalayout = EROFS_INODE_COMPRESSED_FULL;
+               erofs_sb_set_dedupe(sbi);
+
                sbi->saved_by_deduplication +=
                        dctx.e.compressedblks * erofs_blksiz(sbi);
                erofs_dbg("Dedupe %u %scompressed data (delta %d) to %u of %u blocks",