From 5f329a3a728be757f082b868de3cbfe3972044f0 Mon Sep 17 00:00:00 2001 From: Mostyn Bramley-Moore Date: Mon, 29 May 2023 12:01:47 +0200 Subject: [PATCH] 7zip reader: add support for zstandard compression in 7z archives Zstandard support is not yet available in 7-Zip, though it is planned for a future release: https://sourceforge.net/p/sevenzip/feature-requests/1580/ The compression ID used here (4F71101) is copied from a popular 7-Zip fork, which added ZSTD support back in 2017, and is mentioned in the upstream 7-Zip ticket linked above: https://github.com/mcmilk/7-Zip-zstd.git Note that this does not add write support for Zstandard compression in 7z archives. Relates to #1656. --- README.md | 2 +- libarchive/archive_read_support_format_7zip.c | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 40407623..37973be2 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ Currently, the library automatically detects and reads the following formats: * ZIPX archives (with support for bzip2, ppmd8, lzma and xz compressed entries) * GNU and BSD 'ar' archives * 'mtree' format - * 7-Zip archives + * 7-Zip archives (including archives that use zstandard compression) * Microsoft CAB format * LHA and LZH archives * RAR and RAR 5.0 archives (with some limitations due to RAR's proprietary status) diff --git a/libarchive/archive_read_support_format_7zip.c b/libarchive/archive_read_support_format_7zip.c index 0ba4bee3..a0553c7e 100644 --- a/libarchive/archive_read_support_format_7zip.c +++ b/libarchive/archive_read_support_format_7zip.c @@ -41,6 +41,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_ZLIB_H #include #endif +#ifdef HAVE_ZSTD_H +#include +#endif #include "archive.h" #include "archive_entry.h" @@ -82,6 +85,8 @@ __FBSDID("$FreeBSD$"); #define _7Z_ARMTHUMB 0x03030701 #define _7Z_SPARC 0x03030805 +#define _7Z_ZSTD 0x4F71101 /* Copied from https://github.com/mcmilk/7-Zip-zstd.git */ + /* * 7-Zip header property IDs. */ @@ -277,6 +282,11 @@ struct _7zip { #ifdef HAVE_ZLIB_H z_stream stream; int stream_valid; +#endif + /* Decoding Zstandard data. */ +#if HAVE_ZSTD_H + ZSTD_DStream *zstd_dstream; + int zstdstream_valid; #endif /* Decoding PPMd data. */ int ppmd7_stat; @@ -1027,6 +1037,7 @@ init_decompression(struct archive_read *a, struct _7zip *zip, case _7Z_COPY: case _7Z_BZ2: case _7Z_DEFLATE: + case _7Z_ZSTD: case _7Z_PPMD: if (coder2 != NULL) { if (coder2->codec != _7Z_X86 && @@ -1222,6 +1233,22 @@ init_decompression(struct archive_read *a, struct _7zip *zip, "BZ2 codec is unsupported"); return (ARCHIVE_FAILED); #endif + case _7Z_ZSTD: + { +#if defined(HAVE_ZSTD_H) + if (zip->zstdstream_valid) { + ZSTD_freeDStream(zip->zstd_dstream); + zip->zstdstream_valid = 0; + } + zip->zstd_dstream = ZSTD_createDStream(); + zip->zstdstream_valid = 1; + break; +#else + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, + "ZSTD codec is unsupported"); + return (ARCHIVE_FAILED); +#endif + } case _7Z_DEFLATE: #ifdef HAVE_ZLIB_H if (zip->stream_valid) @@ -1487,6 +1514,22 @@ decompress(struct archive_read *a, struct _7zip *zip, t_avail_in = zip->stream.avail_in; t_avail_out = zip->stream.avail_out; break; +#endif +#ifdef HAVE_ZSTD_H + case _7Z_ZSTD: + { + ZSTD_inBuffer input = { t_next_in, t_avail_in, 0 }; // src, size, pos + ZSTD_outBuffer output = { t_next_out, t_avail_out, 0 }; // dst, size, pos + + size_t const zret = ZSTD_decompressStream(zip->zstd_dstream, &output, &input); + if (ZSTD_isError(zret)) { + archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Zstd decompression failed: %s", ZSTD_getErrorName(zret)); + return ARCHIVE_FAILED; + } + t_avail_in -= input.pos; + t_avail_out -= output.pos; + break; + } #endif case _7Z_PPMD: {