| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- /*
- zip_source_compress.c -- (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 <libzip@nih.at>
- 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 <stdlib.h>
- #include <string.h>
- #include "zipint.h"
- struct context {
- zip_error_t error;
- bool end_of_input;
- bool end_of_stream;
- bool can_store;
- bool is_stored; /* only valid if end_of_stream is true */
- bool compress;
- zip_int32_t method;
- zip_uint64_t size;
- zip_int64_t first_read;
- zip_uint8_t buffer[BUFSIZE];
- zip_compression_algorithm_t *algorithm;
- void *ud;
- };
- struct implementation {
- zip_uint16_t method;
- zip_compression_algorithm_t *compress;
- zip_compression_algorithm_t *decompress;
- };
- static struct implementation implementations[] = {
- {ZIP_CM_DEFLATE, &zip_algorithm_deflate_compress, &zip_algorithm_deflate_decompress},
- #if defined(HAVE_LIBBZ2)
- {ZIP_CM_BZIP2, &zip_algorithm_bzip2_compress, &zip_algorithm_bzip2_decompress},
- #endif
- #if defined(HAVE_LIBLZMA)
- {ZIP_CM_LZMA, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress},
- /* Disabled - because 7z isn't able to unpack ZIP+LZMA2
- archives made this way - and vice versa.
- {ZIP_CM_LZMA2, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress},
- */
- {ZIP_CM_XZ, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress},
- #endif
- #if defined(HAVE_LIBZSTD)
- {ZIP_CM_ZSTD, &zip_algorithm_zstd_compress, &zip_algorithm_zstd_decompress},
- #endif
- };
- static size_t implementations_size = sizeof(implementations) / sizeof(implementations[0]);
- static zip_source_t *compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, int compression_flags);
- static zip_int64_t compress_callback(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t);
- static void context_free(struct context *ctx);
- static struct context *context_new(zip_int32_t method, bool compress, int compression_flags, zip_compression_algorithm_t *algorithm);
- static zip_int64_t compress_read(zip_source_t *, struct context *, void *, zip_uint64_t);
- zip_compression_algorithm_t *
- _zip_get_compression_algorithm(zip_int32_t method, bool compress) {
- size_t i;
- zip_uint16_t real_method = ZIP_CM_ACTUAL(method);
- for (i = 0; i < implementations_size; i++) {
- if (implementations[i].method == real_method) {
- if (compress) {
- return implementations[i].compress;
- }
- else {
- return implementations[i].decompress;
- }
- }
- }
- return NULL;
- }
- ZIP_EXTERN int
- zip_compression_method_supported(zip_int32_t method, int compress) {
- if (method == ZIP_CM_STORE) {
- return 1;
- }
- return _zip_get_compression_algorithm(method, compress) != NULL;
- }
- zip_source_t *
- zip_source_compress(zip_t *za, zip_source_t *src, zip_int32_t method, int compression_flags) {
- return compression_source_new(za, src, method, true, compression_flags);
- }
- zip_source_t *
- zip_source_decompress(zip_t *za, zip_source_t *src, zip_int32_t method) {
- return compression_source_new(za, src, method, false, 0);
- }
- static zip_source_t *
- compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, int compression_flags) {
- struct context *ctx;
- zip_source_t *s2;
- zip_compression_algorithm_t *algorithm = NULL;
- if (src == NULL) {
- zip_error_set(&za->error, ZIP_ER_INVAL, 0);
- return NULL;
- }
- if ((algorithm = _zip_get_compression_algorithm(method, compress)) == NULL) {
- zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
- return NULL;
- }
- if ((ctx = context_new(method, compress, compression_flags, algorithm)) == NULL) {
- zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
- return NULL;
- }
- if ((s2 = zip_source_layered(za, src, compress_callback, ctx)) == NULL) {
- context_free(ctx);
- return NULL;
- }
- return s2;
- }
- static struct context *
- context_new(zip_int32_t method, bool compress, int compression_flags, zip_compression_algorithm_t *algorithm) {
- struct context *ctx;
- if ((ctx = (struct context *)malloc(sizeof(*ctx))) == NULL) {
- return NULL;
- }
- zip_error_init(&ctx->error);
- ctx->can_store = compress ? ZIP_CM_IS_DEFAULT(method) : false;
- ctx->algorithm = algorithm;
- ctx->method = method;
- ctx->compress = compress;
- ctx->end_of_input = false;
- ctx->end_of_stream = false;
- ctx->is_stored = false;
- if ((ctx->ud = ctx->algorithm->allocate(ZIP_CM_ACTUAL(method), compression_flags, &ctx->error)) == NULL) {
- zip_error_fini(&ctx->error);
- free(ctx);
- return NULL;
- }
- return ctx;
- }
- static void
- context_free(struct context *ctx) {
- if (ctx == NULL) {
- return;
- }
- ctx->algorithm->deallocate(ctx->ud);
- zip_error_fini(&ctx->error);
- free(ctx);
- }
- static zip_int64_t
- compress_read(zip_source_t *src, struct context *ctx, void *data, zip_uint64_t len) {
- zip_compression_status_t ret;
- bool end;
- zip_int64_t n;
- zip_uint64_t out_offset;
- zip_uint64_t out_len;
- if (zip_error_code_zip(&ctx->error) != ZIP_ER_OK) {
- return -1;
- }
- if (len == 0 || ctx->end_of_stream) {
- return 0;
- }
- out_offset = 0;
- end = false;
- while (!end && out_offset < len) {
- out_len = len - out_offset;
- ret = ctx->algorithm->process(ctx->ud, (zip_uint8_t *)data + out_offset, &out_len);
- if (ret != ZIP_COMPRESSION_ERROR) {
- out_offset += out_len;
- }
- switch (ret) {
- case ZIP_COMPRESSION_END:
- ctx->end_of_stream = true;
- if (!ctx->end_of_input) {
- /* TODO: garbage after stream, or compression ended before all data read */
- }
- if (ctx->first_read < 0) {
- /* we got end of processed stream before reading any input data */
- zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
- end = true;
- break;
- }
- if (ctx->can_store && (zip_uint64_t)ctx->first_read <= out_offset) {
- ctx->is_stored = true;
- ctx->size = (zip_uint64_t)ctx->first_read;
- memcpy(data, ctx->buffer, ctx->size);
- return (zip_int64_t)ctx->size;
- }
- end = true;
- break;
- case ZIP_COMPRESSION_OK:
- break;
- case ZIP_COMPRESSION_NEED_DATA:
- if (ctx->end_of_input) {
- /* TODO: error: stream not ended, but no more input */
- end = true;
- break;
- }
- if ((n = zip_source_read(src, ctx->buffer, sizeof(ctx->buffer))) < 0) {
- _zip_error_set_from_source(&ctx->error, src);
- end = true;
- break;
- }
- else if (n == 0) {
- ctx->end_of_input = true;
- ctx->algorithm->end_of_input(ctx->ud);
- if (ctx->first_read < 0) {
- ctx->first_read = 0;
- }
- }
- else {
- if (ctx->first_read >= 0) {
- /* we overwrote a previously filled ctx->buffer */
- ctx->can_store = false;
- }
- else {
- ctx->first_read = n;
- }
- ctx->algorithm->input(ctx->ud, ctx->buffer, (zip_uint64_t)n);
- }
- break;
- case ZIP_COMPRESSION_ERROR:
- /* error set by algorithm */
- if (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) {
- zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
- }
- end = true;
- break;
- }
- }
- if (out_offset > 0) {
- ctx->can_store = false;
- ctx->size += out_offset;
- return (zip_int64_t)out_offset;
- }
- return (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) ? 0 : -1;
- }
- static zip_int64_t
- compress_callback(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
- struct context *ctx;
- ctx = (struct context *)ud;
- switch (cmd) {
- case ZIP_SOURCE_OPEN: {
- zip_stat_t st;
- zip_file_attributes_t attributes;
-
- ctx->size = 0;
- ctx->end_of_input = false;
- ctx->end_of_stream = false;
- ctx->is_stored = false;
- ctx->first_read = -1;
-
- if (zip_source_stat(src, &st) < 0 || zip_source_get_file_attributes(src, &attributes) < 0) {
- _zip_error_set_from_source(&ctx->error, src);
- return -1;
- }
- if (!ctx->algorithm->start(ctx->ud, &st, &attributes)) {
- return -1;
- }
- return 0;
- }
- case ZIP_SOURCE_READ:
- return compress_read(src, ctx, data, len);
- case ZIP_SOURCE_CLOSE:
- if (!ctx->algorithm->end(ctx->ud)) {
- return -1;
- }
- return 0;
- case ZIP_SOURCE_STAT: {
- zip_stat_t *st;
- st = (zip_stat_t *)data;
- if (ctx->compress) {
- if (ctx->end_of_stream) {
- st->comp_method = ctx->is_stored ? ZIP_CM_STORE : ZIP_CM_ACTUAL(ctx->method);
- st->comp_size = ctx->size;
- st->valid |= ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD;
- }
- else {
- st->valid &= ~(ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD);
- }
- }
- else {
- st->comp_method = ZIP_CM_STORE;
- st->valid |= ZIP_STAT_COMP_METHOD;
- if (ctx->end_of_stream) {
- st->size = ctx->size;
- st->valid |= ZIP_STAT_SIZE;
- }
- }
- }
- return 0;
- case ZIP_SOURCE_ERROR:
- return zip_error_to_data(&ctx->error, data, len);
- case ZIP_SOURCE_FREE:
- context_free(ctx);
- return 0;
- case ZIP_SOURCE_GET_FILE_ATTRIBUTES: {
- zip_file_attributes_t *attributes = (zip_file_attributes_t *)data;
- if (len < sizeof(*attributes)) {
- zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
- return -1;
- }
- attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED | ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS;
- attributes->version_needed = ctx->algorithm->version_needed;
- attributes->general_purpose_bit_mask = ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK;
- attributes->general_purpose_bit_flags = (ctx->is_stored ? 0 : ctx->algorithm->general_purpose_bit_flags(ctx->ud));
- return sizeof(*attributes);
- }
- case ZIP_SOURCE_SUPPORTS:
- return ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, -1);
- default:
- zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
- return -1;
- }
- }
|