[PATCH] erofs-utils: fix partial decompression for libdeflate

Gao Xiang hsiangkao at linux.alibaba.com
Fri Aug 9 20:56:36 AEST 2024


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 at linux.alibaba.com>
---
 lib/decompress.c | 42 +++++++++++++++++++++++++++++-------------
 1 file changed, 29 insertions(+), 13 deletions(-)

diff --git a/lib/decompress.c b/lib/decompress.c
index 1b44a18..3f553a8 100644
--- a/lib/decompress.c
+++ b/lib/decompress.c
@@ -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;
-- 
2.43.5



More information about the Linux-erofs mailing list