erofs-utils: fuse: fix partial decompression for libdeflate
authorGao Xiang <hsiangkao@linux.alibaba.com>
Fri, 9 Aug 2024 10:56:36 +0000 (18:56 +0800)
committerGao Xiang <hsiangkao@linux.alibaba.com>
Fri, 9 Aug 2024 16:19:14 +0000 (00:19 +0800)
Actually, libdeflate doesn't support partial decompression; therefore,
fix it by reallocating larger decompression buffers.

Although a better approach would be to obtain the exact decompressed
length instead for libdeflate decompressor, which requires more changes,
a quick fix is needed.

Fixes: 29b9e7140162 ("erofs-utils: fuse,fsck: add DEFLATE algorithm support")
Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Link: https://lore.kernel.org/r/20240809105636.3641536-1-hsiangkao@linux.alibaba.com
lib/decompress.c

index 1b44a18b1dfcf38b113524fa2a5f90eee3c304e4..3f553a817b17fbe69dc429de10303927d50d89f7 100644 (file)
@@ -247,32 +247,47 @@ static int z_erofs_decompress_deflate(struct z_erofs_decompress_req *rq)
        unsigned int inputmargin;
        struct libdeflate_decompressor *inf;
        enum libdeflate_result ret;
+       unsigned int decodedcapacity;
 
        inputmargin = z_erofs_fixup_insize(src, rq->inputsize);
        if (inputmargin >= rq->inputsize)
                return -EFSCORRUPTED;
 
-       if (rq->decodedskip) {
-               buff = malloc(rq->decodedlength);
+       decodedcapacity = rq->decodedlength << (4 * rq->partial_decoding);
+       if (rq->decodedskip || rq->partial_decoding) {
+               buff = malloc(decodedcapacity);
                if (!buff)
                        return -ENOMEM;
                dest = buff;
        }
 
        inf = libdeflate_alloc_decompressor();
-       if (!inf)
-               return -ENOMEM;
+       if (!inf) {
+               ret = -ENOMEM;
+               goto out_free_mem;
+       }
 
        if (rq->partial_decoding) {
-               ret = libdeflate_deflate_decompress(inf, src + inputmargin,
-                               rq->inputsize - inputmargin, dest,
-                               rq->decodedlength, &actual_out);
-               if (ret && ret != LIBDEFLATE_INSUFFICIENT_SPACE) {
-                       ret = -EIO;
-                       goto out_inflate_end;
+               while (1) {
+                       ret = libdeflate_deflate_decompress(inf, src + inputmargin,
+                                       rq->inputsize - inputmargin, dest,
+                                       decodedcapacity, &actual_out);
+                       if (ret == LIBDEFLATE_SUCCESS)
+                               break;
+                       if (ret != LIBDEFLATE_INSUFFICIENT_SPACE) {
+                               ret = -EIO;
+                               goto out_inflate_end;
+                       }
+                       decodedcapacity = decodedcapacity << 1;
+                       dest = realloc(buff, decodedcapacity);
+                       if (!dest) {
+                               ret = -ENOMEM;
+                               goto out_inflate_end;
+                       }
+                       buff = dest;
                }
 
-               if (actual_out != rq->decodedlength) {
+               if (actual_out < rq->decodedlength) {
                        ret = -EIO;
                        goto out_inflate_end;
                }
@@ -280,18 +295,19 @@ static int z_erofs_decompress_deflate(struct z_erofs_decompress_req *rq)
                ret = libdeflate_deflate_decompress(inf, src + inputmargin,
                                rq->inputsize - inputmargin, dest,
                                rq->decodedlength, NULL);
-               if (ret) {
+               if (ret != LIBDEFLATE_SUCCESS) {
                        ret = -EIO;
                        goto out_inflate_end;
                }
        }
 
-       if (rq->decodedskip)
+       if (rq->decodedskip || rq->partial_decoding)
                memcpy(rq->out, dest + rq->decodedskip,
                       rq->decodedlength - rq->decodedskip);
 
 out_inflate_end:
        libdeflate_free_decompressor(inf);
+out_free_mem:
        if (buff)
                free(buff);
        return ret;