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.
This commit is contained in:
Mostyn Bramley-Moore 2023-05-29 12:01:47 +02:00 committed by Martin Matuška
parent 7911ce4de9
commit 5f329a3a72
2 changed files with 44 additions and 1 deletions

View File

@ -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)

View File

@ -41,6 +41,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#endif
#ifdef HAVE_ZSTD_H
#include <zstd.h>
#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:
{