VeraCrypt
aboutsummaryrefslogtreecommitdiff
path: root/src/Common/libzip/zip_algorithm_xz.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/Common/libzip/zip_algorithm_xz.c')
-rw-r--r--src/Common/libzip/zip_algorithm_xz.c408
1 files changed, 408 insertions, 0 deletions
diff --git a/src/Common/libzip/zip_algorithm_xz.c b/src/Common/libzip/zip_algorithm_xz.c
new file mode 100644
index 00000000..d7a7142d
--- /dev/null
+++ b/src/Common/libzip/zip_algorithm_xz.c
@@ -0,0 +1,408 @@
+/*
+ zip_algorithm_xz.c -- LZMA/XZ (de)compression routines
+ Bazed on zip_algorithm_deflate.c -- deflate (de)compression routines
+ Copyright (C) 2017-2021 Dieter Baron and Thomas Klausner
+
+ This file is part of libzip, a library to manipulate ZIP archives.
+ The authors can be contacted at <info@libzip.org>
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 3. The names of the authors may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "zipint.h"
+
+#include <limits.h>
+#include <lzma.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+enum header_state { INCOMPLETE, OUTPUT, DONE };
+
+#define HEADER_BYTES_ZIP 9
+#define HEADER_MAGIC_LENGTH 4
+#define HEADER_MAGIC1_OFFSET 0
+#define HEADER_MAGIC2_OFFSET 2
+#define HEADER_SIZE_OFFSET 9
+#define HEADER_SIZE_LENGTH 8
+#define HEADER_PARAMETERS_LENGTH 5
+#define HEADER_LZMA_ALONE_LENGTH (HEADER_PARAMETERS_LENGTH + HEADER_SIZE_LENGTH)
+
+struct ctx {
+ zip_error_t *error;
+ bool compress;
+ zip_uint32_t compression_flags;
+ bool end_of_input;
+ lzma_stream zstr;
+ zip_uint16_t method;
+ /* header member is used for converting from zip to "lzma alone"
+ * format
+ *
+ * "lzma alone" file format starts with:
+ * 5 bytes lzma parameters
+ * 8 bytes uncompressed size
+ * compressed data
+ *
+ * zip archive on-disk format starts with
+ * 4 bytes magic (first two bytes vary, e.g. 0x0914 or 0x1002, next bytes are 0x0500)
+ * 5 bytes lzma parameters
+ * compressed data
+ *
+ * we read the data into a header of the form
+ * 4 bytes magic
+ * 5 bytes lzma parameters
+ * 8 bytes uncompressed size
+ */
+ zip_uint8_t header[HEADER_MAGIC_LENGTH + HEADER_LZMA_ALONE_LENGTH];
+ zip_uint8_t header_bytes_offset;
+ enum header_state header_state;
+ zip_uint64_t uncompresssed_size;
+};
+
+
+static zip_uint64_t
+maximum_compressed_size(zip_uint64_t uncompressed_size) {
+ /*
+ According to https://sourceforge.net/p/sevenzip/discussion/45797/thread/b6bd62f8/
+
+ 1) you can use
+ outSize = 1.10 * originalSize + 64 KB.
+ in most cases outSize is less then 1.02 from originalSize.
+ 2) You can try LZMA2, where
+ outSize can be = 1.001 * originalSize + 1 KB.
+ */
+ /* 13 bytes added for lzma alone header */
+ zip_uint64_t compressed_size = (zip_uint64_t)((double)uncompressed_size * 1.1) + 64 * 1024 + 13;
+
+ if (compressed_size < uncompressed_size) {
+ return ZIP_UINT64_MAX;
+ }
+ return compressed_size;
+}
+
+
+static void *
+allocate(bool compress, zip_uint32_t compression_flags, zip_error_t *error, zip_uint16_t method) {
+ struct ctx *ctx;
+
+ if ((ctx = (struct ctx *)malloc(sizeof(*ctx))) == NULL) {
+ zip_error_set(error, ZIP_ER_MEMORY, 0);
+ return NULL;
+ }
+
+ ctx->error = error;
+ ctx->compress = compress;
+ if (compression_flags <= 9) {
+ ctx->compression_flags = compression_flags;
+ } else {
+ ctx->compression_flags = 6; /* default value */
+ }
+ ctx->compression_flags |= LZMA_PRESET_EXTREME;
+ ctx->end_of_input = false;
+ memset(ctx->header, 0, sizeof(ctx->header));
+ ctx->header_bytes_offset = 0;
+ if (method == ZIP_CM_LZMA) {
+ ctx->header_state = INCOMPLETE;
+ }
+ else {
+ ctx->header_state = DONE;
+ }
+ memset(&ctx->zstr, 0, sizeof(ctx->zstr));
+ ctx->method = method;
+ return ctx;
+}
+
+
+static void *
+compress_allocate(zip_uint16_t method, zip_uint32_t compression_flags, zip_error_t *error) {
+ return allocate(true, compression_flags, error, method);
+}
+
+
+static void *
+decompress_allocate(zip_uint16_t method, zip_uint32_t compression_flags, zip_error_t *error) {
+ return allocate(false, compression_flags, error, method);
+}
+
+
+static void
+deallocate(void *ud) {
+ struct ctx *ctx = (struct ctx *)ud;
+ free(ctx);
+}
+
+
+static zip_uint16_t
+general_purpose_bit_flags(void *ud) {
+ struct ctx *ctx = (struct ctx *)ud;
+
+ if (!ctx->compress) {
+ return 0;
+ }
+
+ if (ctx->method == ZIP_CM_LZMA) {
+ /* liblzma always returns an EOS/EOPM marker, see
+ * https://sourceforge.net/p/lzmautils/discussion/708858/thread/84c5dbb9/#a5e4/3764 */
+ return 1 << 1;
+ }
+ return 0;
+}
+
+static int
+map_error(lzma_ret ret) {
+ switch (ret) {
+ case LZMA_DATA_ERROR:
+ case LZMA_UNSUPPORTED_CHECK:
+ return ZIP_ER_COMPRESSED_DATA;
+
+ case LZMA_MEM_ERROR:
+ return ZIP_ER_MEMORY;
+
+ case LZMA_OPTIONS_ERROR:
+ return ZIP_ER_INVAL;
+
+ default:
+ return ZIP_ER_INTERNAL;
+ }
+}
+
+
+static bool
+start(void *ud, zip_stat_t *st, zip_file_attributes_t *attributes) {
+ struct ctx *ctx = (struct ctx *)ud;
+ lzma_ret ret;
+
+ lzma_options_lzma opt_lzma;
+ lzma_lzma_preset(&opt_lzma, ctx->compression_flags);
+ lzma_filter filters[] = {
+ {.id = (ctx->method == ZIP_CM_LZMA ? LZMA_FILTER_LZMA1 : LZMA_FILTER_LZMA2), .options = &opt_lzma},
+ {.id = LZMA_VLI_UNKNOWN, .options = NULL},
+ };
+
+ ctx->zstr.avail_in = 0;
+ ctx->zstr.next_in = NULL;
+ ctx->zstr.avail_out = 0;
+ ctx->zstr.next_out = NULL;
+
+ if (ctx->compress) {
+ if (ctx->method == ZIP_CM_LZMA)
+ ret = lzma_alone_encoder(&ctx->zstr, filters[0].options);
+ else
+ ret = lzma_stream_encoder(&ctx->zstr, filters, LZMA_CHECK_CRC64);
+ }
+ else {
+ if (ctx->method == ZIP_CM_LZMA)
+ ret = lzma_alone_decoder(&ctx->zstr, UINT64_MAX);
+ else
+ ret = lzma_stream_decoder(&ctx->zstr, UINT64_MAX, LZMA_CONCATENATED);
+ }
+
+ if (ret != LZMA_OK) {
+ zip_error_set(ctx->error, map_error(ret), 0);
+ return false;
+ }
+
+ /* If general purpose bits 1 & 2 are both zero, write real uncompressed size in header. */
+ if ((attributes->valid & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS) && (attributes->general_purpose_bit_mask & 0x6) == 0x6 && (attributes->general_purpose_bit_flags & 0x06) == 0 && (st->valid & ZIP_STAT_SIZE)) {
+ ctx->uncompresssed_size = st->size;
+ }
+ else {
+ ctx->uncompresssed_size = ZIP_UINT64_MAX;
+ }
+
+ return true;
+}
+
+
+static bool
+end(void *ud) {
+ struct ctx *ctx = (struct ctx *)ud;
+
+ lzma_end(&ctx->zstr);
+ return true;
+}
+
+
+static bool
+input(void *ud, zip_uint8_t *data, zip_uint64_t length) {
+ struct ctx *ctx = (struct ctx *)ud;
+
+ if (length > UINT_MAX || ctx->zstr.avail_in > 0) {
+ zip_error_set(ctx->error, ZIP_ER_INVAL, 0);
+ return false;
+ }
+
+ /* For decompression of LZMA1: Have we read the full "lzma alone" header yet? */
+ if (ctx->method == ZIP_CM_LZMA && !ctx->compress && ctx->header_state == INCOMPLETE) {
+ /* if not, get more of the data */
+ zip_uint8_t got = (zip_uint8_t)ZIP_MIN(HEADER_BYTES_ZIP - ctx->header_bytes_offset, length);
+ (void)memcpy_s(ctx->header + ctx->header_bytes_offset, sizeof(ctx->header) - ctx->header_bytes_offset, data, got);
+ ctx->header_bytes_offset += got;
+ length -= got;
+ data += got;
+ /* Do we have a complete header now? */
+ if (ctx->header_bytes_offset == HEADER_BYTES_ZIP) {
+ Bytef empty_buffer[1];
+ zip_buffer_t *buffer;
+ /* check magic */
+ if (ctx->header[HEADER_MAGIC2_OFFSET] != 0x05 || ctx->header[HEADER_MAGIC2_OFFSET + 1] != 0x00) {
+ /* magic does not match */
+ zip_error_set(ctx->error, ZIP_ER_COMPRESSED_DATA, 0);
+ return false;
+ }
+ /* set size of uncompressed data in "lzma alone" header to "unknown" */
+ if ((buffer = _zip_buffer_new(ctx->header + HEADER_SIZE_OFFSET, HEADER_SIZE_LENGTH)) == NULL) {
+ zip_error_set(ctx->error, ZIP_ER_MEMORY, 0);
+ return false;
+ }
+ _zip_buffer_put_64(buffer, ctx->uncompresssed_size);
+ _zip_buffer_free(buffer);
+ /* Feed header into "lzma alone" decoder, for
+ * initialization; this should not produce output. */
+ ctx->zstr.next_in = (void *)(ctx->header + HEADER_MAGIC_LENGTH);
+ ctx->zstr.avail_in = HEADER_LZMA_ALONE_LENGTH;
+ ctx->zstr.total_in = 0;
+ ctx->zstr.next_out = empty_buffer;
+ ctx->zstr.avail_out = sizeof(*empty_buffer);
+ ctx->zstr.total_out = 0;
+ /* this just initializes the decoder and does not produce output, so it consumes the complete header */
+ if (lzma_code(&ctx->zstr, LZMA_RUN) != LZMA_OK || ctx->zstr.total_out > 0) {
+ zip_error_set(ctx->error, ZIP_ER_COMPRESSED_DATA, 0);
+ return false;
+ }
+ ctx->header_state = DONE;
+ }
+ }
+ ctx->zstr.avail_in = (uInt)length;
+ ctx->zstr.next_in = (Bytef *)data;
+
+ return true;
+}
+
+
+static void
+end_of_input(void *ud) {
+ struct ctx *ctx = (struct ctx *)ud;
+
+ ctx->end_of_input = true;
+}
+
+
+static zip_compression_status_t
+process(void *ud, zip_uint8_t *data, zip_uint64_t *length) {
+ struct ctx *ctx = (struct ctx *)ud;
+ uInt avail_out;
+ lzma_ret ret;
+ /* for compression of LZMA1 */
+ if (ctx->method == ZIP_CM_LZMA && ctx->compress) {
+ if (ctx->header_state == INCOMPLETE) {
+ /* write magic to output buffer */
+ ctx->header[0] = 0x09;
+ ctx->header[1] = 0x14;
+ ctx->header[2] = 0x05;
+ ctx->header[3] = 0x00;
+ /* generate lzma parameters into output buffer */
+ ctx->zstr.avail_out = HEADER_LZMA_ALONE_LENGTH;
+ ctx->zstr.next_out = ctx->header + HEADER_MAGIC_LENGTH;
+ ret = lzma_code(&ctx->zstr, LZMA_RUN);
+ if (ret != LZMA_OK || ctx->zstr.avail_out != 0) {
+ /* assume that the whole header will be provided with the first call to lzma_code */
+ return ZIP_COMPRESSION_ERROR;
+ }
+ ctx->header_state = OUTPUT;
+ }
+ if (ctx->header_state == OUTPUT) {
+ /* write header */
+ zip_uint8_t write_len = (zip_uint8_t)ZIP_MIN(HEADER_BYTES_ZIP - ctx->header_bytes_offset, *length);
+ (void)memcpy_s(data, *length, ctx->header + ctx->header_bytes_offset, write_len);
+ ctx->header_bytes_offset += write_len;
+ *length = write_len;
+ if (ctx->header_bytes_offset == HEADER_BYTES_ZIP) {
+ ctx->header_state = DONE;
+ }
+ return ZIP_COMPRESSION_OK;
+ }
+ }
+
+ avail_out = (uInt)ZIP_MIN(UINT_MAX, *length);
+ ctx->zstr.avail_out = avail_out;
+ ctx->zstr.next_out = (Bytef *)data;
+
+ ret = lzma_code(&ctx->zstr, ctx->end_of_input ? LZMA_FINISH : LZMA_RUN);
+ *length = avail_out - ctx->zstr.avail_out;
+
+ switch (ret) {
+ case LZMA_OK:
+ return ZIP_COMPRESSION_OK;
+
+ case LZMA_STREAM_END:
+ return ZIP_COMPRESSION_END;
+
+ case LZMA_BUF_ERROR:
+ if (ctx->zstr.avail_in == 0) {
+ return ZIP_COMPRESSION_NEED_DATA;
+ }
+
+ /* fallthrough */
+ default:
+ zip_error_set(ctx->error, map_error(ret), 0);
+ return ZIP_COMPRESSION_ERROR;
+ }
+}
+
+/* Version Required should be set to 63 (6.3) because this compression
+ method was only defined in appnote.txt version 6.3.8, but Winzip
+ does not unpack it if the value is not 20. */
+
+/* clang-format off */
+
+zip_compression_algorithm_t zip_algorithm_xz_compress = {
+ maximum_compressed_size,
+ compress_allocate,
+ deallocate,
+ general_purpose_bit_flags,
+ 20,
+ start,
+ end,
+ input,
+ end_of_input,
+ process
+};
+
+
+zip_compression_algorithm_t zip_algorithm_xz_decompress = {
+ maximum_compressed_size,
+ decompress_allocate,
+ deallocate,
+ general_purpose_bit_flags,
+ 20,
+ start,
+ end,
+ input,
+ end_of_input,
+ process
+};
+
+/* clang-format on */