aboutsummaryrefslogtreecommitdiff
path: root/libarchive/archive_read_support_format_zip.c
diff options
context:
space:
mode:
Diffstat (limited to 'libarchive/archive_read_support_format_zip.c')
-rw-r--r--libarchive/archive_read_support_format_zip.c159
1 files changed, 157 insertions, 2 deletions
diff --git a/libarchive/archive_read_support_format_zip.c b/libarchive/archive_read_support_format_zip.c
index 21d41cc0f741..38ada70b5577 100644
--- a/libarchive/archive_read_support_format_zip.c
+++ b/libarchive/archive_read_support_format_zip.c
@@ -58,6 +58,9 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_read_support_format_zip.c 201102
#ifdef HAVE_LZMA_H
#include <lzma.h>
#endif
+#ifdef HAVE_ZSTD_H
+#include <zstd.h>
+#endif
#include "archive.h"
#include "archive_digest_private.h"
@@ -191,6 +194,11 @@ struct zip {
char bzstream_valid;
#endif
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ ZSTD_DStream *zstdstream;
+ char zstdstream_valid;
+#endif
+
IByteIn zipx_ppmd_stream;
ssize_t zipx_ppmd_read_compressed;
CPpmd8 ppmd8;
@@ -435,6 +443,7 @@ static const struct {
{17, "reserved"}, /* Reserved by PKWARE */
{18, "ibm-terse-new"}, /* File is compressed using IBM TERSE (new) */
{19, "ibm-lz777"},/* IBM LZ77 z Architecture (PFS) */
+ {93, "zstd"}, /* Zstandard (zstd) Compression */
{95, "xz"}, /* XZ compressed data */
{96, "jpeg"}, /* JPEG compressed data */
{97, "wav-pack"}, /* WavPack compressed data */
@@ -1144,7 +1153,8 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
(intmax_t)zip_entry->compressed_size);
ret = ARCHIVE_WARN;
}
- if (zip_entry->uncompressed_size == 0) {
+ if (zip_entry->uncompressed_size == 0 ||
+ zip_entry->uncompressed_size == 0xffffffff) {
zip_entry->uncompressed_size
= zip_entry_central_dir.uncompressed_size;
} else if (zip_entry->uncompressed_size
@@ -1186,7 +1196,7 @@ zip_read_local_file_header(struct archive_read *a, struct archive_entry *entry,
{
// symlink target string appeared to be compressed
int status = ARCHIVE_FATAL;
- const void *uncompressed_buffer;
+ const void *uncompressed_buffer = NULL;
switch (zip->entry->compression)
{
@@ -2238,6 +2248,140 @@ zip_read_data_zipx_bzip2(struct archive_read *a, const void **buff,
#endif
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+static int
+zipx_zstd_init(struct archive_read *a, struct zip *zip)
+{
+ size_t r;
+
+ /* Deallocate already existing Zstd decompression context if it
+ * exists. */
+ if(zip->zstdstream_valid) {
+ ZSTD_freeDStream(zip->zstdstream);
+ zip->zstdstream_valid = 0;
+ }
+
+ /* Allocate a new Zstd decompression context. */
+ zip->zstdstream = ZSTD_createDStream();
+
+ r = ZSTD_initDStream(zip->zstdstream);
+ if (ZSTD_isError(r)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Error initializing zstd decompressor: %s",
+ ZSTD_getErrorName(r));
+
+ return ARCHIVE_FAILED;
+ }
+
+ /* Mark the zstdstream field to be released in cleanup phase. */
+ zip->zstdstream_valid = 1;
+
+ /* (Re)allocate the buffer that will contain decompressed bytes. */
+ free(zip->uncompressed_buffer);
+
+ zip->uncompressed_buffer_size = ZSTD_DStreamOutSize();
+ zip->uncompressed_buffer =
+ (uint8_t*) malloc(zip->uncompressed_buffer_size);
+ if (zip->uncompressed_buffer == NULL) {
+ archive_set_error(&a->archive, ENOMEM,
+ "No memory for Zstd decompression");
+
+ return ARCHIVE_FATAL;
+ }
+
+ /* Initialization done. */
+ zip->decompress_init = 1;
+ return ARCHIVE_OK;
+}
+
+static int
+zip_read_data_zipx_zstd(struct archive_read *a, const void **buff,
+ size_t *size, int64_t *offset)
+{
+ struct zip *zip = (struct zip *)(a->format->data);
+ ssize_t bytes_avail = 0, in_bytes, to_consume;
+ const void *compressed_buff;
+ int r;
+ size_t ret;
+ uint64_t total_out;
+ ZSTD_outBuffer out;
+ ZSTD_inBuffer in;
+
+ (void) offset; /* UNUSED */
+
+ /* Initialize decompression context if we're here for the first time. */
+ if(!zip->decompress_init) {
+ r = zipx_zstd_init(a, zip);
+ if(r != ARCHIVE_OK)
+ return r;
+ }
+
+ /* Fetch more compressed bytes */
+ compressed_buff = __archive_read_ahead(a, 1, &bytes_avail);
+ if(bytes_avail < 0) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated zstd file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ in_bytes = zipmin(zip->entry_bytes_remaining, bytes_avail);
+ if(in_bytes < 1) {
+ /* zstd doesn't complain when caller feeds avail_in == 0.
+ * It will actually return success in this case, which is
+ * undesirable. This is why we need to make this check
+ * manually. */
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT,
+ "Truncated zstd file body");
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Setup buffer boundaries */
+ in.src = compressed_buff;
+ in.size = in_bytes;
+ in.pos = 0;
+ out = (ZSTD_outBuffer) { zip->uncompressed_buffer, zip->uncompressed_buffer_size, 0 };
+
+ /* Perform the decompression. */
+ ret = ZSTD_decompressStream(zip->zstdstream, &out, &in);
+ if (ZSTD_isError(ret)) {
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Error during zstd decompression: %s",
+ ZSTD_getErrorName(ret));
+ return (ARCHIVE_FATAL);
+ }
+
+ /* Check end of the stream. */
+ if (ret == 0) {
+ if ((in.pos == in.size) && (out.pos < out.size)) {
+ zip->end_of_entry = 1;
+ ZSTD_freeDStream(zip->zstdstream);
+ zip->zstdstream_valid = 0;
+ }
+ }
+
+ /* Update the pointers so decompressor can continue decoding. */
+ to_consume = in.pos;
+ __archive_read_consume(a, to_consume);
+
+ total_out = out.pos;
+
+ zip->entry_bytes_remaining -= to_consume;
+ zip->entry_compressed_bytes_read += to_consume;
+ zip->entry_uncompressed_bytes_read += total_out;
+
+ /* Give libarchive its due. */
+ *size = total_out;
+ *buff = zip->uncompressed_buffer;
+
+ /* Seek for optional marker, like in other entries. */
+ r = consume_optional_marker(a, zip);
+ if(r != ARCHIVE_OK)
+ return r;
+
+ return ARCHIVE_OK;
+}
+#endif
+
#ifdef HAVE_ZLIB_H
static int
zip_deflate_init(struct archive_read *a, struct zip *zip)
@@ -2858,6 +3002,11 @@ archive_read_format_zip_read_data(struct archive_read *a,
r = zip_read_data_zipx_xz(a, buff, size, offset);
break;
#endif
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ case 93: /* ZIPx Zstd compression. */
+ r = zip_read_data_zipx_zstd(a, buff, size, offset);
+ break;
+#endif
/* PPMd support is built-in, so we don't need any #if guards. */
case 98: /* ZIPx PPMd compression. */
r = zip_read_data_zipx_ppmd(a, buff, size, offset);
@@ -2948,6 +3097,12 @@ archive_read_format_zip_cleanup(struct archive_read *a)
}
#endif
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ if (zip->zstdstream_valid) {
+ ZSTD_freeDStream(zip->zstdstream);
+ }
+#endif
+
free(zip->uncompressed_buffer);
if (zip->ppmd8_valid)