From 33a5aed090f3224d699ee0787bf2dd3c2b111645 Mon Sep 17 00:00:00 2001 From: guoqinglan Date: Fri, 5 Feb 2021 13:34:41 +0800 Subject: [PATCH] add deepin compressor cancel process function --- CMakeLists.txt | 6 + cmake-zipconf.h.in | 1 + lib/CMakeLists.txt | 41 +- lib/zip.h | 37 +- lib/zip_algorithm_bzip2.c | 10 +- lib/zip_algorithm_deflate.c | 15 +- lib/zip_algorithm_xz.c | 249 +++++++ lib/zip_close.c | 136 +++- lib/zip_delete.c | 2 +- lib/zip_dirent.c | 122 +++- lib/zip_get_encryption_implementation.c | 13 +- lib/zip_mkstempm.c | 93 +++ lib/zip_open.c | 40 +- lib/zip_pkware.c | 112 +++ lib/zip_progress.c | 172 ++++- lib/zip_random_unix.c | 54 +- ...ource_file.c => zip_source_accept_empty.c} | 35 +- lib/zip_source_buffer.c | 56 +- lib/zip_source_compress.c | 37 +- lib/zip_source_crc.c | 3 +- lib/zip_source_file.h | 90 +++ lib/zip_source_file_common.c | 378 ++++++++++ lib/zip_source_file_stdio.c | 208 ++++++ lib/zip_source_file_stdio.h | 47 ++ lib/zip_source_file_stdio_named.c | 313 +++++++++ lib/zip_source_file_win32.c | 230 +++++++ lib/zip_source_file_win32.h | 74 ++ lib/zip_source_file_win32_ansi.c | 81 +++ lib/zip_source_file_win32_named.c | 266 +++++++ lib/zip_source_file_win32_utf16.c | 108 +++ lib/zip_source_file_win32_utf8.c | 73 ++ lib/zip_source_filep.c | 651 ------------------ lib/zip_source_get_file_attributes.c | 104 +++ lib/zip_source_pkware_decode.c | 221 ++++++ lib/zip_source_pkware_encode.c | 249 +++++++ lib/zip_source_seek.c | 7 +- lib/zip_source_supports.c | 2 +- lib/zip_source_window.c | 38 +- lib/zip_source_winzip_aes_encode.c | 17 +- lib/zip_source_zip_new.c | 38 +- lib/zip_stat_index.c | 9 +- lib/zip_string.c | 2 +- lib/zip_utf-8.c | 2 +- lib/zipint.h | 55 +- 44 files changed, 3651 insertions(+), 846 deletions(-) create mode 100644 lib/zip_algorithm_xz.c create mode 100644 lib/zip_mkstempm.c create mode 100644 lib/zip_pkware.c rename lib/{zip_source_file.c => zip_source_accept_empty.c} (65%) create mode 100644 lib/zip_source_file.h create mode 100644 lib/zip_source_file_common.c create mode 100644 lib/zip_source_file_stdio.c create mode 100644 lib/zip_source_file_stdio.h create mode 100644 lib/zip_source_file_stdio_named.c create mode 100644 lib/zip_source_file_win32.c create mode 100644 lib/zip_source_file_win32.h create mode 100644 lib/zip_source_file_win32_ansi.c create mode 100644 lib/zip_source_file_win32_named.c create mode 100644 lib/zip_source_file_win32_utf16.c create mode 100644 lib/zip_source_file_win32_utf8.c delete mode 100644 lib/zip_source_filep.c create mode 100644 lib/zip_source_get_file_attributes.c create mode 100644 lib/zip_source_pkware_decode.c create mode 100644 lib/zip_source_pkware_encode.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 69e6548..ca6ffe1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -337,6 +337,12 @@ ELSEIF(LONG_LONG_LIBZIP EQUAL 8) SET(ZIP_UINT64_T "unsigned long long") ENDIF() +if(HAVE_NULLABLE) + set(ZIP_NULLABLE_DEFINES) +else() + set(ZIP_NULLABLE_DEFINES "#define _Nullable +#define _Nonnull") +endif() # write out config file CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/cmake-config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/cmake-zipconf.h.in ${CMAKE_CURRENT_BINARY_DIR}/zipconf.h) diff --git a/cmake-zipconf.h.in b/cmake-zipconf.h.in index 6ff4fe3..5777fa7 100644 --- a/cmake-zipconf.h.in +++ b/cmake-zipconf.h.in @@ -14,6 +14,7 @@ #cmakedefine LIBZIP_VERSION_MICRO @PACKAGE_VERSION_MICRO@ #cmakedefine ZIP_STATIC +${ZIP_NULLABLE_DEFINES} ${LIBZIP_TYPES_INCLUDE} diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 92c4bec..f15a65c 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -110,6 +110,7 @@ SET(LIBZIP_SOURCES zip_name_locate.c zip_new.c zip_open.c + zip_pkware.c zip_progress.c zip_rename.c zip_replace.c @@ -119,6 +120,7 @@ SET(LIBZIP_SOURCES zip_set_file_comment.c zip_set_file_compression.c zip_set_name.c + zip_source_accept_empty.c zip_source_begin_write.c zip_source_begin_write_cloning.c zip_source_buffer.c @@ -128,14 +130,18 @@ SET(LIBZIP_SOURCES zip_source_compress.c zip_source_crc.c zip_source_error.c - zip_source_filep.c + zip_source_file_common.c + zip_source_file_stdio.c +# zip_source_filep.c zip_source_free.c zip_source_function.c - zip_source_get_compression_flags.c + # zip_source_get_compression_flags.c + zip_source_get_file_attributes.c zip_source_is_deleted.c zip_source_layered.c zip_source_open.c - zip_source_pkware.c + zip_source_pkware_decode.c + zip_source_pkware_encode.c zip_source_read.c zip_source_remove.c zip_source_rollback_write.c @@ -175,7 +181,7 @@ IF(WIN32) ENDIF() ELSE(WIN32) SET(LIBZIP_OPSYS_FILES - zip_source_file.c + #zip_source_file.c ) ENDIF(WIN32) @@ -193,6 +199,11 @@ IF(HAVE_LIBBZ2) SET(LIBZIP_OPTIONAL_FILES zip_algorithm_bzip2.c) ENDIF() +if(HAVE_LIBLZMA) + target_sources(zip PRIVATE zip_algorithm_xz.c) + target_link_libraries(zip PRIVATE LibLZMA::LibLZMA) +endif() + IF(HAVE_COMMONCRYPTO) SET(LIBZIP_OPTIONAL_FILES ${LIBZIP_OPTIONAL_FILES} zip_crypto_commoncrypto.c ) @@ -210,6 +221,28 @@ IF(HAVE_CRYPTO) ENDIF() ADD_LIBRARY(zip ${LIBZIP_SOURCES} ${LIBZIP_EXTRA_FILES} ${LIBZIP_OPTIONAL_FILES} ${LIBZIP_OPSYS_FILES}) + +if(WIN32) + target_sources(zip PRIVATE + zip_source_file_win32.c + zip_source_file_win32_named.c + zip_source_file_win32_utf16.c + zip_source_file_win32_utf8.c + ) + if(CMAKE_SYSTEM_NAME MATCHES WindowsPhone OR CMAKE_SYSTEM_NAME MATCHES WindowsStore) + target_sources(zip PRIVATE zip_random_uwp.c) + else() + target_sources(zip PRIVATE zip_source_file_win32_ansi.c zip_random_win32.c) + target_link_libraries(zip PRIVATE advapi32) + endif() +else(WIN32) + target_sources(zip PRIVATE + zip_mkstempm.c + zip_source_file_stdio_named.c + zip_random_unix.c + ) +endif(WIN32) + SET_TARGET_PROPERTIES(zip PROPERTIES VERSION 5.0 SOVERSION 5) TARGET_LINK_LIBRARIES(zip ${ZLIB_LIBRARY} ${OPTIONAL_LIBRARY}) INSTALL(TARGETS zip diff --git a/lib/zip.h b/lib/zip.h index 2d83a99..5215b3e 100644 --- a/lib/zip.h +++ b/lib/zip.h @@ -134,6 +134,7 @@ extern "C" { #define ZIP_ER_INUSE 29 /* N Resource still in use */ #define ZIP_ER_TELL 30 /* S Tell error */ #define ZIP_ER_COMPRESSED_DATA 31 /* N Compressed data invalid */ +#define ZIP_ER_CANCELLED 32 /* N Operation cancelled */ /* type of system error value */ @@ -228,8 +229,11 @@ enum zip_source_cmd { ZIP_SOURCE_TELL_WRITE, /* get write position */ ZIP_SOURCE_SUPPORTS, /* check whether source supports command */ ZIP_SOURCE_REMOVE, /* remove file */ - ZIP_SOURCE_GET_COMPRESSION_FLAGS, /* get compression flags, internal only */ - ZIP_SOURCE_BEGIN_WRITE_CLONING /* like ZIP_SOURCE_BEGIN_WRITE, but keep part of original file */ + ZIP_SOURCE_RESERVED_1, /* previously used internally */ + + ZIP_SOURCE_BEGIN_WRITE_CLONING, /* like ZIP_SOURCE_BEGIN_WRITE, but keep part of original file */ + ZIP_SOURCE_ACCEPT_EMPTY, /* whether empty files are valid archives */ + ZIP_SOURCE_GET_FILE_ATTRIBUTES /* get additional file attributes */ }; typedef enum zip_source_cmd zip_source_cmd_t; @@ -275,7 +279,7 @@ typedef struct zip_source_args_seek zip_source_args_seek_t; struct zip_error { int zip_err; /* libzip error code (ZIP_ER_*) */ int sys_err; /* copy of errno (E*) or zlib error code */ - char *str; /* string representation or NULL */ + char *_Nullable str; /* string representation or NULL */ }; #define ZIP_STAT_NAME 0x0001u @@ -290,7 +294,7 @@ struct zip_error { struct zip_stat { zip_uint64_t valid; /* which fields have valid values */ - const char *name; /* name of the file */ + const char *_Nullable name; /* name of the file */ zip_uint64_t index; /* index within archive */ zip_uint64_t size; /* size of file (uncompressed) */ zip_uint64_t comp_size; /* size of file (compressed) */ @@ -302,10 +306,26 @@ struct zip_stat { }; struct zip_buffer_fragment { - zip_uint8_t *data; + zip_uint8_t *_Nonnull data; zip_uint64_t length; }; +struct zip_file_attributes { + zip_uint64_t valid; /* which fields have valid values */ + zip_uint8_t version; /* version of this struct, currently 1 */ + zip_uint8_t host_system; /* host system on which file was created */ + zip_uint8_t ascii; /* flag whether file is ASCII text */ + zip_uint8_t version_needed; /* minimum version needed to extract file */ + zip_uint32_t external_file_attributes; /* external file attributes (host-system specific) */ + zip_uint16_t general_purpose_bit_flags; /* general purpose big flags, only some bits are honored */ + zip_uint16_t general_purpose_bit_mask; /* which bits in general_purpose_bit_flags are valid */ +}; + +#define ZIP_FILE_ATTRIBUTES_HOST_SYSTEM 0x0001u +#define ZIP_FILE_ATTRIBUTES_ASCII 0x0002u +#define ZIP_FILE_ATTRIBUTES_VERSION_NEEDED 0x0004u +#define ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES 0x0008u +#define ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS 0x0010u struct zip; struct zip_file; struct zip_source; @@ -313,6 +333,7 @@ struct zip_source; typedef struct zip zip_t; typedef struct zip_error zip_error_t; typedef struct zip_file zip_file_t; +typedef struct zip_file_attributes zip_file_attributes_t; typedef struct zip_source zip_source_t; typedef struct zip_stat zip_stat_t; typedef struct zip_buffer_fragment zip_buffer_fragment_t; @@ -321,6 +342,7 @@ typedef zip_uint32_t zip_flags_t; typedef zip_int64_t (*zip_source_callback)(void *, void *, zip_uint64_t, zip_source_cmd_t); typedef void (*zip_progress_callback)(zip_t *, double, void *); +typedef int (*zip_cancel_callback)(zip_t *_Nonnull, void *_Nullable); #ifndef ZIP_DISABLE_DEPRECATED typedef void (*zip_progress_callback_t)(double); @@ -359,6 +381,7 @@ ZIP_EXTERN zip_int64_t zip_error_to_data(const zip_error_t *, void *, zip_uint64 ZIP_EXTERN int zip_fclose(zip_file_t *); ZIP_EXTERN zip_t *zip_fdopen(int, int, int *); ZIP_EXTERN zip_int64_t zip_file_add(zip_t *, const char *, zip_source_t *, zip_flags_t); +ZIP_EXTERN void zip_file_attributes_init(zip_file_attributes_t *_Nonnull); ZIP_EXTERN void zip_file_error_clear(zip_file_t *); ZIP_EXTERN int zip_file_extra_field_delete(zip_t *, zip_uint64_t, zip_uint16_t, zip_flags_t); ZIP_EXTERN int zip_file_extra_field_delete_by_id(zip_t *, zip_uint64_t, zip_uint16_t, zip_uint16_t, zip_flags_t); @@ -393,6 +416,7 @@ ZIP_EXTERN zip_int64_t zip_name_locate(zip_t *, const char *, zip_flags_t); ZIP_EXTERN zip_t *zip_open(const char *, int, int *); ZIP_EXTERN zip_t *zip_open_from_source(zip_source_t *, int, zip_error_t *); ZIP_EXTERN int zip_register_progress_callback_with_state(zip_t *, double, zip_progress_callback, void (*)(void *), void *); +ZIP_EXTERN int zip_register_cancel_callback_with_state(zip_t *_Nonnull, zip_cancel_callback _Nullable, void (*_Nullable)(void *_Nullable), void *_Nullable); ZIP_EXTERN int zip_set_archive_comment(zip_t *, const char *, zip_uint16_t); ZIP_EXTERN int zip_set_archive_flag(zip_t *, zip_flags_t, int); ZIP_EXTERN int zip_set_default_password(zip_t *, const char *); @@ -409,10 +433,11 @@ ZIP_EXTERN zip_error_t *zip_source_error(zip_source_t *); ZIP_EXTERN zip_source_t *zip_source_file(zip_t *, const char *, zip_uint64_t, zip_int64_t); ZIP_EXTERN zip_source_t *zip_source_file_create(const char *, zip_uint64_t, zip_int64_t, zip_error_t *); ZIP_EXTERN zip_source_t *zip_source_filep(zip_t *, FILE *, zip_uint64_t, zip_int64_t); -ZIP_EXTERN zip_source_t *zip_source_filep_create(FILE *, zip_uint64_t, zip_int64_t, zip_error_t *); +ZIP_EXTERN zip_source_t *_Nullable zip_source_filep_create(FILE *_Nonnull, zip_uint64_t, zip_int64_t, zip_error_t *_Nullable); ZIP_EXTERN void zip_source_free(zip_source_t *); ZIP_EXTERN zip_source_t *zip_source_function(zip_t *, zip_source_callback, void *); ZIP_EXTERN zip_source_t *zip_source_function_create(zip_source_callback, void *, zip_error_t *); +ZIP_EXTERN int zip_source_get_file_attributes(zip_source_t *_Nonnull, zip_file_attributes_t *_Nonnull); ZIP_EXTERN int zip_source_is_deleted(zip_source_t *); ZIP_EXTERN void zip_source_keep(zip_source_t *); ZIP_EXTERN zip_int64_t zip_source_make_command_bitmap(zip_source_cmd_t, ...); diff --git a/lib/zip_algorithm_bzip2.c b/lib/zip_algorithm_bzip2.c index f2fb162..ea7b9c6 100644 --- a/lib/zip_algorithm_bzip2.c +++ b/lib/zip_algorithm_bzip2.c @@ -90,8 +90,8 @@ deallocate(void *ud) { } -static int -compression_flags(void *ud) { +static zip_uint16_t +general_purpose_bit_flags(void *ud) { return 0; } @@ -247,7 +247,8 @@ process(void *ud, zip_uint8_t *data, zip_uint64_t *length) { zip_compression_algorithm_t zip_algorithm_bzip2_compress = { compress_allocate, deallocate, - compression_flags, + general_purpose_bit_flags, + 46, start, end, input, @@ -259,7 +260,8 @@ zip_compression_algorithm_t zip_algorithm_bzip2_compress = { zip_compression_algorithm_t zip_algorithm_bzip2_decompress = { decompress_allocate, deallocate, - compression_flags, + general_purpose_bit_flags, + 46, start, end, input, diff --git a/lib/zip_algorithm_deflate.c b/lib/zip_algorithm_deflate.c index 2a1c904..618055c 100644 --- a/lib/zip_algorithm_deflate.c +++ b/lib/zip_algorithm_deflate.c @@ -51,6 +51,7 @@ allocate(bool compress, int compression_flags, zip_error_t *error) { struct ctx *ctx; if ((ctx = (struct ctx *)malloc(sizeof(*ctx))) == NULL) { + zip_error_set(error, ZIP_ET_SYS, errno); return NULL; } @@ -90,8 +91,8 @@ deallocate(void *ud) { } -static int -compression_flags(void *ud) { +static zip_uint16_t +general_purpose_bit_flags(void *ud) { struct ctx *ctx = (struct ctx *)ud; if (!ctx->compress) { @@ -99,10 +100,10 @@ compression_flags(void *ud) { } if (ctx->compression_flags < 3) { - return 2; + return 2 << 1; } else if (ctx->compression_flags > 7) { - return 1; + return 1 << 1; } return 0; } @@ -224,7 +225,8 @@ process(void *ud, zip_uint8_t *data, zip_uint64_t *length) { zip_compression_algorithm_t zip_algorithm_deflate_compress = { compress_allocate, deallocate, - compression_flags, + general_purpose_bit_flags, + 20, start, end, input, @@ -236,7 +238,8 @@ zip_compression_algorithm_t zip_algorithm_deflate_compress = { zip_compression_algorithm_t zip_algorithm_deflate_decompress = { decompress_allocate, deallocate, - compression_flags, + general_purpose_bit_flags, + 20, start, end, input, diff --git a/lib/zip_algorithm_xz.c b/lib/zip_algorithm_xz.c new file mode 100644 index 0000000..945ab4e --- /dev/null +++ b/lib/zip_algorithm_xz.c @@ -0,0 +1,249 @@ +/* + zip_algorithm_xz.c -- XZ (de)compression routines + Bazed on zip_algorithm_deflate.c -- deflate (de)compression routines + Copyright (C) 2017-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 "zipint.h" + +#include +#include +#include +#include + +struct ctx { + zip_error_t *error; + bool compress; + zip_uint32_t compression_flags; + bool end_of_input; + lzma_stream zstr; + zip_uint16_t method; +}; + + +static void * +allocate(bool compress, int compression_flags, zip_error_t *error, zip_uint16_t method) { + struct ctx *ctx; + + if (compression_flags < 0) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((ctx = (struct ctx *)malloc(sizeof(*ctx))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + ctx->error = error; + ctx->compress = compress; + ctx->compression_flags = (zip_uint32_t)compression_flags; + ctx->compression_flags |= LZMA_PRESET_EXTREME; + ctx->end_of_input = false; + memset(&ctx->zstr, 0, sizeof(ctx->zstr)); + ctx->method = method; + return ctx; +} + + +static void * +compress_allocate(zip_uint16_t method, int compression_flags, zip_error_t *error) { + return allocate(true, compression_flags, error, method); +} + + +static void * +decompress_allocate(zip_uint16_t method, int 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; */ + return 0; +} + +static int +map_error(lzma_ret ret) { + switch (ret) { + 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) { + 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; + } + + 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; + } + + 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; + lzma_ret ret; + + ctx->zstr.avail_out = (uInt)ZIP_MIN(UINT_MAX, *length); + ctx->zstr.next_out = (Bytef *)data; + + ret = lzma_code(&ctx->zstr, ctx->end_of_input ? LZMA_FINISH : LZMA_RUN); + *length = *length - 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; + } +} + +/* clang-format off */ + +zip_compression_algorithm_t zip_algorithm_xz_compress = { + compress_allocate, + deallocate, + general_purpose_bit_flags, + 63, + start, + end, + input, + end_of_input, + process +}; + + +zip_compression_algorithm_t zip_algorithm_xz_decompress = { + decompress_allocate, + deallocate, + general_purpose_bit_flags, + 63, + start, + end, + input, + end_of_input, + process +}; + +/* clang-format on */ diff --git a/lib/zip_close.c b/lib/zip_close.c index c46e1b3..d62b6db 100644 --- a/lib/zip_close.c +++ b/lib/zip_close.c @@ -51,10 +51,11 @@ #endif -static int add_data(zip_t *, zip_source_t *, zip_dirent_t *); +static int add_data(zip_t *, zip_source_t *, zip_dirent_t *, zip_uint32_t); static int copy_data(zip_t *, zip_uint64_t); static int copy_source(zip_t *, zip_source_t *, zip_int64_t); static int write_cdir(zip_t *, const zip_filelist_t *, zip_uint64_t); +static int write_data_descriptor(zip_t *za, const zip_dirent_t *dirent, int is_zip64); ZIP_EXTERN int zip_close(zip_t *za) { @@ -73,8 +74,10 @@ zip_close(zip_t *za) { if (survivors == 0) { if ((za->open_flags & ZIP_TRUNCATE) || changed) { if (zip_source_remove(za->src) < 0) { - _zip_error_set_from_source(&za->error, za->src); - return -1; + if (!((zip_error_code_zip(zip_source_error(za->src)) == ZIP_ER_REMOVE) && (zip_error_code_system(zip_source_error(za->src)) == ENOENT))) { + _zip_error_set_from_source(&za->error, za->src); + return -1; + } } } zip_discard(za); @@ -158,14 +161,23 @@ zip_close(zip_t *za) { } } - _zip_progress_start(za->progress); + if (_zip_progress_start(za->progress) != 0) { + zip_error_set(&za->error, ZIP_ER_CANCELLED, 0); + zip_source_rollback_write(za->src); + free(filelist); + return -1; + } error = 0; for (j = 0; j < survivors; j++) { int new_data; zip_entry_t *entry; zip_dirent_t *de; - _zip_progress_subrange(za->progress, (double)j / (double)survivors, (double)(j + 1) / (double)survivors); + if (_zip_progress_subrange(za->progress, (double)j / (double)survivors, (double)(j + 1) / (double)survivors) != 0) { + zip_error_set(&za->error, ZIP_ER_CANCELLED, 0); + error = 1; + break; + } i = filelist[j].idx; entry = za->entry + i; @@ -193,6 +205,7 @@ zip_close(zip_t *za) { } if ((off = zip_source_tell_write(za->src)) < 0) { + _zip_error_set_from_source(&za->error, za->src); error = 1; break; } @@ -210,7 +223,7 @@ zip_close(zip_t *za) { } /* add_data writes dirent */ - if (add_data(za, zs ? zs : entry->source, de) < 0) { + if (add_data(za, zs ? zs : entry->source, de, entry->changes ? entry->changes->changed : 0) < 0) { error = 1; if (zs) zip_source_free(zs); @@ -222,8 +235,11 @@ zip_close(zip_t *za) { else { zip_uint64_t offset; - /* when copying data, all sizes are known -> no data descriptor needed */ - de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR; + if (de->encryption_method != ZIP_EM_TRAD_PKWARE) { + /* when copying data, all sizes are known -> no data descriptor needed */ + /* except for PKWare encryption, where removing the data descriptor breaks password validation */ + de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR; + } if (_zip_dirent_write(za, de, ZIP_FL_LOCAL) < 0) { error = 1; break; @@ -234,13 +250,20 @@ zip_close(zip_t *za) { } if (zip_source_seek(za->src, (zip_int64_t)offset, SEEK_SET) < 0) { _zip_error_set_from_source(&za->error, za->src); - error = 1; + error = 1; break; } if (copy_data(za, de->comp_size) < 0) { - error = 1; + error = 1; break; } + + if (de->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) { + if (write_data_descriptor(za, de, _zip_dirent_needs_zip64(de, 0)) < 0) { + error = 1; + break; + } + } } } @@ -256,10 +279,9 @@ zip_close(zip_t *za) { _zip_error_set_from_source(&za->error, za->src); error = 1; } + _zip_progress_end(za->progress); } - _zip_progress_end(za->progress); - if (error) { zip_source_rollback_write(za->src); return -1; @@ -272,14 +294,14 @@ zip_close(zip_t *za) { static int -add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) { +add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { zip_int64_t offstart, offdata, offend, data_length; - struct zip_stat st; + zip_stat_t st; + zip_file_attributes_t attributes; zip_source_t *src_final, *src_tmp; int ret; int is_zip64; zip_flags_t flags; - zip_int8_t compression_flags; bool needs_recompress, needs_decompress, needs_crc, needs_compress, needs_reencrypt, needs_decrypt, needs_encrypt; if (zip_source_stat(src, &st) < 0) { @@ -443,6 +465,9 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) { zip_source_free(src_final); return -1; } + if (de->encryption_method == ZIP_EM_TRAD_PKWARE) { + de->bitflags |= ZIP_GPBF_DATA_DESCRIPTOR; + } zip_source_free(src_final); src_final = src_tmp; @@ -461,7 +486,7 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) { ret = -1; } - if ((compression_flags = zip_source_get_compression_flags(src_final)) < 0) { + if (zip_source_get_file_attributes(src_final, &attributes) != 0) { _zip_error_set_from_source(&za->error, src_final); ret = -1; } @@ -497,8 +522,7 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) { de->crc = st.crc; de->uncomp_size = st.size; de->comp_size = (zip_uint64_t)(offend - offdata); - de->bitflags = (zip_uint16_t)((de->bitflags & (zip_uint16_t)~6) | ((zip_uint8_t)compression_flags << 1)); - _zip_dirent_set_version_needed(de, (flags & ZIP_FL_FORCE_ZIP64) != 0); + _zip_dirent_apply_attributes(de, &attributes, (flags & ZIP_FL_FORCE_ZIP64) != 0, changed); if ((ret = _zip_dirent_write(za, de, flags)) < 0) return -1; @@ -514,38 +538,54 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) { return -1; } + if (de->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) { + if (write_data_descriptor(za, de, is_zip64) < 0) { + return -1; + } + } + return 0; } static int copy_data(zip_t *za, zip_uint64_t len) { - zip_uint8_t buf[BUFSIZE]; + DEFINE_BYTE_ARRAY(buf, BUFSIZE); size_t n; double total = (double)len; + if (!byte_array_init(buf, BUFSIZE)) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + while (len > 0) { - n = len > sizeof(buf) ? sizeof(buf) : len; + n = len > BUFSIZE ? BUFSIZE : len; if (_zip_read(za->src, buf, n, &za->error) < 0) { + byte_array_fini(buf); return -1; } if (_zip_write(za, buf, n) < 0) { + byte_array_fini(buf); return -1; } len -= n; - _zip_progress_update(za->progress, (total - (double)len) / total); + if (_zip_progress_update(za->progress, (total - (double)len) / total) != 0) { + zip_error_set(&za->error, ZIP_ER_CANCELLED, 0); + return -1; + } } + byte_array_fini(buf); return 0; } - static int copy_source(zip_t *za, zip_source_t *src, zip_int64_t data_length) { - zip_uint8_t buf[BUFSIZE]; + DEFINE_BYTE_ARRAY(buf, BUFSIZE); zip_int64_t n, current; int ret; @@ -554,16 +594,25 @@ copy_source(zip_t *za, zip_source_t *src, zip_int64_t data_length) { return -1; } + if (!byte_array_init(buf, BUFSIZE)) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + ret = 0; current = 0; - while ((n = zip_source_read(src, buf, sizeof(buf))) > 0) { + while ((n = zip_source_read(src, buf, BUFSIZE)) > 0) { if (_zip_write(za, buf, (zip_uint64_t)n) < 0) { ret = -1; break; } - if (n == sizeof(buf) && za->progress && data_length > 0) { + if (n == BUFSIZE && za->progress && data_length > 0) { current += n; - _zip_progress_update(za->progress, (double)current / (double)data_length); + if (_zip_progress_update(za->progress, (double)current / (double)data_length) != 0) { + zip_error_set(&za->error, ZIP_ER_CANCELLED, 0); + ret = -1; + break; + } } } @@ -572,6 +621,8 @@ copy_source(zip_t *za, zip_source_t *src, zip_int64_t data_length) { ret = -1; } + byte_array_fini(buf); + zip_source_close(src); return ret; @@ -624,3 +675,36 @@ _zip_changed(const zip_t *za, zip_uint64_t *survivorsp) { return changed; } +static int +write_data_descriptor(zip_t *za, const zip_dirent_t *de, int is_zip64) { + zip_buffer_t *buffer = _zip_buffer_new(NULL, MAX_DATA_DESCRIPTOR_LENGTH); + int ret = 0; + + if (buffer == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + + _zip_buffer_put(buffer, DATADES_MAGIC, 4); + _zip_buffer_put_32(buffer, de->crc); + if (is_zip64) { + _zip_buffer_put_64(buffer, de->comp_size); + _zip_buffer_put_64(buffer, de->uncomp_size); + } + else { + _zip_buffer_put_32(buffer, (zip_uint32_t)de->comp_size); + _zip_buffer_put_32(buffer, (zip_uint32_t)de->uncomp_size); + } + + if (!_zip_buffer_ok(buffer)) { + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + ret = -1; + } + else { + ret = _zip_write(za, _zip_buffer_data(buffer), _zip_buffer_offset(buffer)); + } + + _zip_buffer_free(buffer); + + return ret; +} diff --git a/lib/zip_delete.c b/lib/zip_delete.c index ae8f5a6..a407378 100644 --- a/lib/zip_delete.c +++ b/lib/zip_delete.c @@ -53,7 +53,7 @@ zip_delete(zip_t *za, zip_uint64_t idx) { return -1; } - if (!_zip_hash_delete(za->names, (const zip_uint8_t *)name, &za->error)) { + if (!_zip_hash_delete(za->names, (const zip_uint8_t *)name, &za->error)) { return -1; } diff --git a/lib/zip_dirent.c b/lib/zip_dirent.c index a6dbfac..5164d21 100644 --- a/lib/zip_dirent.c +++ b/lib/zip_dirent.c @@ -523,29 +523,54 @@ _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, boo return -1; } - if (zde->uncomp_size == ZIP_UINT32_MAX) + if (zde->uncomp_size == ZIP_UINT32_MAX) { zde->uncomp_size = _zip_buffer_get_64(ef_buffer); + } else if (local) { /* From appnote.txt: This entry in the Local header MUST include BOTH original and compressed file size fields. */ (void)_zip_buffer_skip(ef_buffer, 8); /* error is caught by _zip_buffer_eof() call */ } - if (zde->comp_size == ZIP_UINT32_MAX) + if (zde->comp_size == ZIP_UINT32_MAX) { zde->comp_size = _zip_buffer_get_64(ef_buffer); + } if (!local) { - if (zde->offset == ZIP_UINT32_MAX) + if (zde->offset == ZIP_UINT32_MAX) { zde->offset = _zip_buffer_get_64(ef_buffer); - if (zde->disk_number == ZIP_UINT16_MAX) + } + if (zde->disk_number == ZIP_UINT16_MAX) { zde->disk_number = _zip_buffer_get_32(ef_buffer); + } } if (!_zip_buffer_eof(ef_buffer)) { - zip_error_set(error, ZIP_ER_INCONS, 0); - _zip_buffer_free(ef_buffer); - if (!from_buffer) { - _zip_buffer_free(buffer); + /* accept additional fields if values match */ + bool ok = true; + switch (got_len) { + case 28: + _zip_buffer_set_offset(ef_buffer, 24); + if (zde->disk_number != _zip_buffer_get_32(ef_buffer)) { + ok = false; + } + /* fallthrough */ + case 24: + _zip_buffer_set_offset(ef_buffer, 0); + if ((zde->uncomp_size != _zip_buffer_get_64(ef_buffer)) || (zde->comp_size != _zip_buffer_get_64(ef_buffer)) || (zde->offset != _zip_buffer_get_64(ef_buffer))) { + ok = false; + } + break; + + default: + ok = false; + } + if (!ok) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_buffer_free(ef_buffer); + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; } - return -1; } _zip_buffer_free(ef_buffer); } @@ -573,7 +598,7 @@ _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, boo zde->extra_fields = _zip_ef_remove_internal(zde->extra_fields); - return (zip_int64_t)(size + variable_size); + return (zip_int64_t)size + (zip_int64_t)variable_size; } @@ -868,9 +893,9 @@ _zip_dirent_write(zip_t *za, zip_dirent_t *de, zip_flags_t flags) { _zip_buffer_put(buffer, (flags & ZIP_FL_LOCAL) ? LOCAL_MAGIC : CENTRAL_MAGIC, 4); if ((flags & ZIP_FL_LOCAL) == 0) { - _zip_buffer_put_16(buffer, (zip_uint16_t)(is_really_zip64 ? 45 : de->version_madeby)); + _zip_buffer_put_16(buffer, de->version_madeby); } - _zip_buffer_put_16(buffer, (zip_uint16_t)(is_really_zip64 ? 45 : de->version_needed)); + _zip_buffer_put_16(buffer, ZIP_MAX(is_really_zip64 ? 45 : 0, de->version_needed)); _zip_buffer_put_16(buffer, de->bitflags); if (is_winzip_aes) { _zip_buffer_put_16(buffer, ZIP_CM_WINZIP_AES); @@ -978,7 +1003,7 @@ _zip_dirent_write(zip_t *za, zip_dirent_t *de, zip_flags_t flags) { } -static time_t +time_t _zip_d2u_time(zip_uint16_t dtime, zip_uint16_t ddate) { struct tm tm; @@ -1066,20 +1091,32 @@ _zip_get_dirent(zip_t *za, zip_uint64_t idx, zip_flags_t flags, zip_error_t *err void _zip_u2d_time(time_t intime, zip_uint16_t *dtime, zip_uint16_t *ddate) { - struct tm *tm; + struct tm *tpm; - tm = localtime(&intime); - if (tm->tm_year < 80) { - tm->tm_year = 80; +#ifdef HAVE_LOCALTIME_R + struct tm tm; + tpm = localtime_r(&intime, &tm); +#else + tpm = localtime(&intime); +#endif + if (tpm == NULL) { + /* if localtime() fails, return an arbitrary date (1980-01-01 00:00:00) */ + *ddate = (1 << 5) + 1; + *dtime = 0; + return; + } + if (tpm->tm_year < 80) { + tpm->tm_year = 80; } - *ddate = (zip_uint16_t)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); - *dtime = (zip_uint16_t)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); + *ddate = (zip_uint16_t)(((tpm->tm_year + 1900 - 1980) << 9) + ((tpm->tm_mon + 1) << 5) + tpm->tm_mday); + *dtime = (zip_uint16_t)(((tpm->tm_hour) << 11) + ((tpm->tm_min) << 5) + ((tpm->tm_sec) >> 1)); return; } + void _zip_dirent_set_version_needed(zip_dirent_t *de, bool force_zip64) { zip_uint16_t length; @@ -1114,3 +1151,50 @@ _zip_dirent_set_version_needed(zip_dirent_t *de, bool force_zip64) { de->version_needed = 10; } +void +_zip_dirent_apply_attributes(zip_dirent_t *de, zip_file_attributes_t *attributes, bool force_zip64, zip_uint32_t changed) { + zip_uint16_t length; + + if (attributes->valid & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS) { + zip_uint16_t mask = attributes->general_purpose_bit_mask & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK; + de->bitflags = (de->bitflags & ~mask) | (attributes->general_purpose_bit_flags & mask); + } + if (attributes->valid & ZIP_FILE_ATTRIBUTES_ASCII) { + de->int_attrib = (de->int_attrib & ~0x1) | (attributes->ascii ? 1 : 0); + } + /* manually set attributes are preferred over attributes provided by source */ + if ((changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES)) { + de->ext_attrib = attributes->external_file_attributes; + } + + if (de->comp_method == ZIP_CM_LZMA) { + de->version_needed = 63; + } + else if (de->encryption_method == ZIP_EM_AES_128 || de->encryption_method == ZIP_EM_AES_192 || de->encryption_method == ZIP_EM_AES_256) { + de->version_needed = 51; + } + else if (de->comp_method == ZIP_CM_BZIP2) { + de->version_needed = 46; + } + else if (force_zip64 || _zip_dirent_needs_zip64(de, 0)) { + de->version_needed = 45; + } + else if (de->comp_method == ZIP_CM_DEFLATE || de->encryption_method == ZIP_EM_TRAD_PKWARE) { + de->version_needed = 20; + } + else if ((length = _zip_string_length(de->filename)) > 0 && de->filename->raw[length - 1] == '/') { + de->version_needed = 20; + } + else { + de->version_needed = 10; + } + + if (attributes->valid & ZIP_FILE_ATTRIBUTES_VERSION_NEEDED) { + de->version_needed = ZIP_MAX(de->version_needed, attributes->version_needed); + } + + de->version_madeby = 63 | (de->version_madeby & 0xff00); + if ((changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_HOST_SYSTEM)) { + de->version_madeby = (de->version_madeby & 0xff) | (zip_uint16_t)(attributes->host_system << 8); + } +} \ No newline at end of file diff --git a/lib/zip_get_encryption_implementation.c b/lib/zip_get_encryption_implementation.c index ba459d4..e7f91c6 100644 --- a/lib/zip_get_encryption_implementation.c +++ b/lib/zip_get_encryption_implementation.c @@ -39,10 +39,7 @@ zip_encryption_implementation _zip_get_encryption_implementation(zip_uint16_t em, int operation) { switch (em) { case ZIP_EM_TRAD_PKWARE: - if (operation == ZIP_CODEC_ENCODE) { - return NULL; - } - return zip_source_pkware; + return operation == ZIP_CODEC_DECODE ? zip_source_pkware_decode : zip_source_pkware_encode; #if defined(HAVE_CRYPTO) case ZIP_EM_AES_128: @@ -55,3 +52,11 @@ _zip_get_encryption_implementation(zip_uint16_t em, int operation) { return NULL; } } + +ZIP_EXTERN int +zip_encryption_method_supported(zip_uint16_t method, int encode) { + if (method == ZIP_EM_NONE) { + return 1; + } + return _zip_get_encryption_implementation(method, encode ? ZIP_CODEC_ENCODE : ZIP_CODEC_DECODE) != NULL; +} diff --git a/lib/zip_mkstempm.c b/lib/zip_mkstempm.c new file mode 100644 index 0000000..c1afade --- /dev/null +++ b/lib/zip_mkstempm.c @@ -0,0 +1,93 @@ +/* + zip_mkstempm.c -- mkstemp replacement that accepts a mode argument + Copyright (C) 2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 +#include +#include +#include + +#include "zipint.h" + +/* + * create temporary file with same permissions as previous one; + * or default permissions if there is no previous file + */ +int +_zip_mkstempm(char *path, int mode) { + int fd; + char *start, *end, *xs; + + int xcnt = 0; + + end = path + strlen(path); + start = end - 1; + while (start >= path && *start == 'X') { + xcnt++; + start--; + } + + if (xcnt == 0) { + errno = EINVAL; + return -1; + } + + start++; + + for (;;) { + zip_uint32_t value = zip_random_uint32(); + + xs = start; + + while (xs < end) { + char digit = value % 36; + if (digit < 10) { + *(xs++) = digit + '0'; + } + else { + *(xs++) = digit - 10 + 'a'; + } + value /= 36; + } + + if ((fd = open(path, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, mode == -1 ? 0666 : (mode_t)mode)) >= 0) { + if (mode != -1) { + /* open() honors umask(), which we don't want in this case */ + (void)chmod(path, (mode_t)mode); + } + return fd; + } + if (errno != EEXIST) { + return -1; + } + } +} diff --git a/lib/zip_open.c b/lib/zip_open.c index 1886c9d..a2ca54a 100644 --- a/lib/zip_open.c +++ b/lib/zip_open.c @@ -40,12 +40,7 @@ #include "zipint.h" -typedef enum { - EXISTS_ERROR = -1, - EXISTS_NOT = 0, - EXISTS_EMPTY, - EXISTS_NONEMPTY, -} exists_t; +typedef enum { EXISTS_ERROR = -1, EXISTS_NOT = 0, EXISTS_OK } exists_t; static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error); static zip_int64_t _zip_checkcons(zip_t *za, zip_cdir_t *cdir, zip_error_t *error); static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len); @@ -56,6 +51,16 @@ static zip_cdir_t *_zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error); static zip_cdir_t *_zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error); +ZIP_EXTERN int +zip_archive_set_tempdir(zip_t *za, const char *tempdir) +{ + (void)(za); + (void)(tempdir); + + return 0; +} + + ZIP_EXTERN zip_t * zip_open(const char *fn, int _flags, int *zep) { @@ -174,20 +179,16 @@ _zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) { } len = st.size; - /* treat empty files as empty archives */ - if (len == 0) { - if ((za = _zip_allocate_new(src, flags, error)) == NULL) { - zip_source_free(src); - return NULL; - } - - return za; - } if ((za = _zip_allocate_new(src, flags, error)) == NULL) { return NULL; } + /* treat empty files as empty archives */ + if (len == 0 && zip_source_accept_empty(src)) { + return za; + } + if ((cdir = _zip_find_central_dir(za, len)) == NULL) { _zip_error_copy(error, &za->error); /* keep src so discard does not get rid of it */ @@ -541,7 +542,7 @@ _zip_file_exists(zip_source_t *src, zip_error_t *error) { return EXISTS_ERROR; } - return (st.valid & ZIP_STAT_SIZE) && st.size == 0 ? EXISTS_EMPTY : EXISTS_NONEMPTY; + return EXISTS_OK; } @@ -726,7 +727,8 @@ _zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offse eocd_disk = _zip_buffer_get_16(buffer); eocd_offset = _zip_buffer_get_64(buffer); - if (eocd_offset > ZIP_INT64_MAX || eocd_offset + EOCD64LEN < eocd_offset) { + /* valid seek value for start of EOCD */ + if (eocd_offset > ZIP_INT64_MAX) { zip_error_set(error, ZIP_ER_SEEK, EFBIG); return NULL; } @@ -838,6 +840,10 @@ _zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offse return NULL; } + if (nentry > size / CDENTRYSIZE) { + zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } if ((cd = _zip_cdir_new(nentry, error)) == NULL) return NULL; diff --git a/lib/zip_pkware.c b/lib/zip_pkware.c new file mode 100644 index 0000000..eb12b1e --- /dev/null +++ b/lib/zip_pkware.c @@ -0,0 +1,112 @@ +/* + zip_pkware.c -- Traditional PKWARE de/encryption backend routines + Copyright (C) 2009-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 +#include + +#include "zipint.h" + +#define PKWARE_KEY0 305419896 +#define PKWARE_KEY1 591751049 +#define PKWARE_KEY2 878082192 + + +static void +update_keys(zip_pkware_keys_t *keys, zip_uint8_t b) { + keys->key[0] = (zip_uint32_t)crc32(keys->key[0] ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL; + keys->key[1] = (keys->key[1] + (keys->key[0] & 0xff)) * 134775813 + 1; + b = (zip_uint8_t)(keys->key[1] >> 24); + keys->key[2] = (zip_uint32_t)crc32(keys->key[2] ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL; +} + + +static zip_uint8_t +crypt_byte(zip_pkware_keys_t *keys) { + zip_uint16_t tmp; + tmp = (zip_uint16_t)(keys->key[2] | 2); + tmp = (zip_uint16_t)(((zip_uint32_t)tmp * (tmp ^ 1)) >> 8); + return (zip_uint8_t)tmp; +} + + +void +_zip_pkware_keys_reset(zip_pkware_keys_t *keys) { + keys->key[0] = PKWARE_KEY0; + keys->key[1] = PKWARE_KEY1; + keys->key[2] = PKWARE_KEY2; +} + + +void +_zip_pkware_encrypt(zip_pkware_keys_t *keys, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len) { + zip_uint64_t i; + zip_uint8_t b; + zip_uint8_t tmp; + + for (i = 0; i < len; i++) { + b = in[i]; + + if (out != NULL) { + tmp = crypt_byte(keys); + update_keys(keys, b); + b ^= tmp; + out[i] = b; + } + else { + /* during initialization, we're only interested in key updates */ + update_keys(keys, b); + } + } +} + + +void +_zip_pkware_decrypt(zip_pkware_keys_t *keys, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len) { + zip_uint64_t i; + zip_uint8_t b; + zip_uint8_t tmp; + + for (i = 0; i < len; i++) { + b = in[i]; + + /* during initialization, we're only interested in key updates */ + if (out != NULL) { + tmp = crypt_byte(keys); + b ^= tmp; + out[i] = b; + } + + update_keys(keys, b); + } +} diff --git a/lib/zip_progress.c b/lib/zip_progress.c index 46c8bb8..ddb47c7 100644 --- a/lib/zip_progress.c +++ b/lib/zip_progress.c @@ -1,6 +1,6 @@ /* zip_progress.c -- progress reporting - Copyright (C) 2017 Dieter Baron and Thomas Klausner + Copyright (C) 2017-2020 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -40,10 +40,14 @@ struct zip_progress { zip_t *za; - zip_progress_callback callback; - void (*ud_free)(void *); - void *ud; + zip_progress_callback callback_progress; + void (*ud_progress_free)(void *); + void *ud_progress; + + zip_cancel_callback callback_cancel; + void (*ud_cancel_free)(void *); + void *ud_cancel; double precision; @@ -54,6 +58,11 @@ struct zip_progress { double end; /* end of sub-progress section */ }; +static void _zip_progress_free_cancel_callback(zip_progress_t *progress); +static void _zip_progress_free_progress_callback(zip_progress_t *progress); +static zip_progress_t *_zip_progress_new(zip_t *za); +static void _zip_progress_set_cancel_callback(zip_progress_t *progress, zip_cancel_callback callback, void (*ud_free)(void *), void *ud); +static void _zip_progress_set_progress_callback(zip_progress_t *progress, double precision, zip_progress_callback callback, void (*ud_free)(void *), void *ud); void _zip_progress_end(zip_progress_t *progress) { @@ -67,16 +76,15 @@ _zip_progress_free(zip_progress_t *progress) { return; } - if (progress->ud_free) { - progress->ud_free(progress->ud); - } + _zip_progress_free_progress_callback(progress); + _zip_progress_free_cancel_callback(progress); free(progress); } -zip_progress_t * -_zip_progress_new(zip_t *za, double precision, zip_progress_callback callback, void (*ud_free)(void *), void *ud) { +static zip_progress_t * +_zip_progress_new(zip_t *za) { zip_progress_t *progress = (zip_progress_t *)malloc(sizeof(*progress)); if (progress == NULL) { @@ -85,67 +93,169 @@ _zip_progress_new(zip_t *za, double precision, zip_progress_callback callback, v } progress->za = za; - progress->callback = callback; - progress->ud_free = ud_free; - progress->ud = ud; - progress->precision = precision; + + progress->callback_progress = NULL; + progress->ud_progress_free = NULL; + progress->ud_progress = NULL; + progress->precision = 0.0; + + progress->callback_cancel = NULL; + progress->ud_cancel_free = NULL; + progress->ud_cancel = NULL; return progress; } +static void +_zip_progress_free_progress_callback(zip_progress_t *progress) { + if (progress->ud_progress_free) { + progress->ud_progress_free(progress->ud_progress); + } + + progress->callback_progress = NULL; + progress->ud_progress = NULL; + progress->ud_progress_free = NULL; +} + +static void +_zip_progress_free_cancel_callback(zip_progress_t *progress) { + if (progress->ud_cancel_free) { + progress->ud_cancel_free(progress->ud_cancel); + } + + progress->callback_cancel = NULL; + progress->ud_cancel = NULL; + progress->ud_cancel_free = NULL; +} + +static void +_zip_progress_set_progress_callback(zip_progress_t *progress, double precision, zip_progress_callback callback, void (*ud_free)(void *), void *ud) { + _zip_progress_free_progress_callback(progress); + + progress->callback_progress = callback; + progress->ud_progress_free = ud_free; + progress->ud_progress = ud; + progress->precision = precision; +} void +_zip_progress_set_cancel_callback(zip_progress_t *progress, zip_cancel_callback callback, void (*ud_free)(void *), void *ud) { + _zip_progress_free_cancel_callback(progress); + + progress->callback_cancel = callback; + progress->ud_cancel_free = ud_free; + progress->ud_cancel = ud; +} + +int _zip_progress_start(zip_progress_t *progress) { if (progress == NULL) { - return; + return 0; + } + + if (progress->callback_progress != NULL) { + progress->last_update = 0.0; + progress->callback_progress(progress->za, 0.0, progress->ud_progress); + } + + if (progress->callback_cancel != NULL) { + if (progress->callback_cancel(progress->za, progress->ud_cancel)) { + return -1; + } } - progress->last_update = 0.0; - progress->callback(progress->za, 0.0, progress->ud); + return 0; } -void +int _zip_progress_subrange(zip_progress_t *progress, double start, double end) { if (progress == NULL) { - return; + return 0; } progress->start = start; progress->end = end; - _zip_progress_update(progress, 0.0); + return _zip_progress_update(progress, 0.0); } -void +int _zip_progress_update(zip_progress_t *progress, double sub_current) { double current; if (progress == NULL) { - return; + return 0; } - current = ZIP_MIN(ZIP_MAX(sub_current, 0.0), 1.0) * (progress->end - progress->start) + progress->start; + if (progress->callback_progress != NULL) { + current = ZIP_MIN(ZIP_MAX(sub_current, 0.0), 1.0) * (progress->end - progress->start) + progress->start; + + if (current - progress->last_update > progress->precision) { + progress->callback_progress(progress->za, current, progress->ud_progress); + progress->last_update = current; + } + } - if (current - progress->last_update > progress->precision) { - progress->callback(progress->za, current, progress->ud); - progress->last_update = current; + if (progress->callback_cancel != NULL) { + if (progress->callback_cancel(progress->za, progress->ud_cancel)) { + return -1; + } } + + return 0; } ZIP_EXTERN int zip_register_progress_callback_with_state(zip_t *za, double precision, zip_progress_callback callback, void (*ud_free)(void *), void *ud) { - zip_progress_t *progress = NULL; - if (callback != NULL) { - if ((progress = _zip_progress_new(za, precision, callback, ud_free, ud)) == NULL) { - return -1; + if (za->progress == NULL) { + if ((za->progress = _zip_progress_new(za)) == NULL) { + return -1; + } + } + + _zip_progress_set_progress_callback(za->progress, precision, callback, ud_free, ud); + } + else { + if (za->progress != NULL) { + if (za->progress->callback_cancel == NULL) { + _zip_progress_free(za->progress); + za->progress = NULL; + } + else { + _zip_progress_free_progress_callback(za->progress); + } } } - _zip_progress_free(za->progress); - za->progress = progress; + return 0; +} + + +ZIP_EXTERN int +zip_register_cancel_callback_with_state(zip_t *za, zip_cancel_callback callback, void (*ud_free)(void *), void *ud) { + if (callback != NULL) { + if (za->progress == NULL) { + if ((za->progress = _zip_progress_new(za)) == NULL) { + return -1; + } + } + + _zip_progress_set_cancel_callback(za->progress, callback, ud_free, ud); + } + else { + if (za->progress != NULL) { + if (za->progress->callback_progress == NULL) { + _zip_progress_free(za->progress); + za->progress = NULL; + } + else { + _zip_progress_free_cancel_callback(za->progress); + } + } + } return 0; } diff --git a/lib/zip_random_unix.c b/lib/zip_random_unix.c index 2c59847..1d0c967 100644 --- a/lib/zip_random_unix.c +++ b/lib/zip_random_unix.c @@ -1,6 +1,6 @@ /* zip_random_unix.c -- fill the user's buffer with random stuff (Unix version) - Copyright (C) 2016-2017 Dieter Baron and Thomas Klausner + Copyright (C) 2016-2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -33,11 +33,37 @@ #include "zipint.h" +#ifdef HAVE_CRYPTO +#include "zip_crypto.h" +#endif + +#ifdef HAVE_ARC4RANDOM + +#include + +#ifndef HAVE_SECURE_RANDOM +ZIP_EXTERN bool +zip_secure_random(zip_uint8_t *buffer, zip_uint16_t length) { + arc4random_buf(buffer, length); + return true; +} +#endif + +#ifndef HAVE_RANDOM_UINT32 +zip_uint32_t +zip_random_uint32(void) { + return arc4random(); +} +#endif + +#else /* HAVE_ARC4RANDOM */ + +#ifndef HAVE_SECURE_RANDOM #include #include ZIP_EXTERN bool -zip_random(zip_uint8_t *buffer, zip_uint16_t length) { +zip_secure_random(zip_uint8_t *buffer, zip_uint16_t length) { int fd; if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { @@ -52,3 +78,27 @@ zip_random(zip_uint8_t *buffer, zip_uint16_t length) { close(fd); return true; } +#endif + +#ifndef HAVE_RANDOM_UINT32 +#include + +zip_uint32_t +zip_random_uint32(void) { + static bool seeded = false; + + zip_uint32_t value; + + if (zip_secure_random((zip_uint8_t *)&value, sizeof(value))) { + return value; + } + + if (!seeded) { + srandom((unsigned int)time(NULL)); + } + + return (zip_uint32_t)random(); +} +#endif + +#endif /* HAVE_ARC4RANDOM */ diff --git a/lib/zip_source_file.c b/lib/zip_source_accept_empty.c similarity index 65% rename from lib/zip_source_file.c rename to lib/zip_source_accept_empty.c index 77376f4..e6d5151 100644 --- a/lib/zip_source_file.c +++ b/lib/zip_source_accept_empty.c @@ -1,6 +1,6 @@ /* - zip_source_file.c -- create data source from file - Copyright (C) 1999-2016 Dieter Baron and Thomas Klausner + zip_source_accept_empty.c -- if empty source is a valid archive + Copyright (C) 2019 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -32,30 +32,21 @@ */ -#include - #include "zipint.h" -#ifdef _WIN32 -#error This file is incompatible with Windows, use zip_source_win32utf8.c instead. -#error Something probably went wrong with configure/cmake. -#endif - -ZIP_EXTERN zip_source_t * -zip_source_file(zip_t *za, const char *fname, zip_uint64_t start, zip_int64_t len) { - if (za == NULL) - return NULL; - return zip_source_file_create(fname, start, len, &za->error); -} +bool +zip_source_accept_empty(zip_source_t *src) { + int ret; - -ZIP_EXTERN zip_source_t * -zip_source_file_create(const char *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { - if (fname == NULL || length < -1) { - zip_error_set(error, ZIP_ER_INVAL, 0); - return NULL; + if ((zip_source_supports(src) & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ACCEPT_EMPTY)) == 0) { + if (ZIP_SOURCE_IS_LAYERED(src)) { + return zip_source_accept_empty(src->src); + } + return true; } - return _zip_source_file_or_p(fname, NULL, start, length, NULL, error); + ret = (int)_zip_source_call(src, NULL, 0, ZIP_SOURCE_ACCEPT_EMPTY); + + return ret != 0; } diff --git a/lib/zip_source_buffer.c b/lib/zip_source_buffer.c index d2bc8ad..0e5eab3 100644 --- a/lib/zip_source_buffer.c +++ b/lib/zip_source_buffer.c @@ -61,6 +61,7 @@ typedef struct buffer buffer_t; struct read_data { zip_error_t error; time_t mtime; + zip_file_attributes_t attributes; buffer_t *in; buffer_t *out; }; @@ -79,29 +80,41 @@ static zip_int64_t buffer_write(buffer_t *buffer, const zip_uint8_t *data, zip_u static zip_int64_t read_data(void *, void *, zip_uint64_t, zip_source_cmd_t); +zip_source_t *zip_source_buffer_with_attributes_create(const void *data, zip_uint64_t len, int freep, zip_file_attributes_t *attributes, zip_error_t *error); +zip_source_t *zip_source_buffer_fragment_with_attributes_create(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int freep, zip_file_attributes_t *attributes, zip_error_t *error); ZIP_EXTERN zip_source_t * zip_source_buffer(zip_t *za, const void *data, zip_uint64_t len, int freep) { if (za == NULL) return NULL; - return zip_source_buffer_create(data, len, freep, &za->error); + return zip_source_buffer_with_attributes_create(data, len, freep, NULL, &za->error); } ZIP_EXTERN zip_source_t * zip_source_buffer_create(const void *data, zip_uint64_t len, int freep, zip_error_t *error) { + return zip_source_buffer_with_attributes_create(data, len, freep, NULL, error); +} + + +zip_source_t * +zip_source_buffer_with_attributes_create(const void *data, zip_uint64_t len, int freep, zip_file_attributes_t *attributes, zip_error_t *error) { zip_buffer_fragment_t fragment; - if (data == NULL && len > 0) { - zip_error_set(error, ZIP_ER_INVAL, 0); - return NULL; + if (data == NULL) { + if (len > 0) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return zip_source_buffer_fragment_with_attributes_create(NULL, 0, freep, attributes, error); } fragment.data = (zip_uint8_t *)data; fragment.length = len; - return zip_source_buffer_fragment_create(&fragment, 1, freep, error); + return zip_source_buffer_fragment_with_attributes_create(&fragment, 1, freep, attributes, error); } @@ -111,12 +124,17 @@ zip_source_buffer_fragment(zip_t *za, const zip_buffer_fragment_t *fragments, zi return NULL; } - return zip_source_buffer_fragment_create(fragments, nfragments, freep, &za->error); + return zip_source_buffer_fragment_with_attributes_create(fragments, nfragments, freep, NULL, &za->error); } ZIP_EXTERN zip_source_t * zip_source_buffer_fragment_create(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int freep, zip_error_t *error) { + return zip_source_buffer_fragment_with_attributes_create(fragments, nfragments, freep, NULL, error); +} + +zip_source_t * +zip_source_buffer_fragment_with_attributes_create(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int freep, zip_file_attributes_t *attributes, zip_error_t *error) { struct read_data *ctx; zip_source_t *zs; buffer_t *buffer; @@ -139,6 +157,12 @@ zip_source_buffer_fragment_create(const zip_buffer_fragment_t *fragments, zip_ui ctx->in = buffer; ctx->out = NULL; ctx->mtime = time(NULL); + if (attributes) { + memcpy(&ctx->attributes, attributes, sizeof(ctx->attributes)); + } + else { + zip_file_attributes_init(&ctx->attributes); + } zip_error_init(&ctx->error); if ((zs = zip_source_function_create(read_data, ctx, error)) == NULL) { @@ -151,6 +175,11 @@ zip_source_buffer_fragment_create(const zip_buffer_fragment_t *fragments, zip_ui } +zip_source_t * +zip_source_buffer_with_attributes(zip_t *za, const void *data, zip_uint64_t len, int freep, zip_file_attributes_t *attributes) { + return zip_source_buffer_with_attributes_create(data, len, freep, attributes, &za->error); +} + static zip_int64_t read_data(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { struct read_data *ctx = (struct read_data *)state; @@ -190,6 +219,17 @@ read_data(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { free(ctx); return 0; + case ZIP_SOURCE_GET_FILE_ATTRIBUTES: { + if (len < sizeof(ctx->attributes)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + + memcpy(data, &ctx->attributes, sizeof(ctx->attributes)); + + return sizeof(ctx->attributes); + } + case ZIP_SOURCE_OPEN: ctx->in->offset = 0; ctx->in->current_fragment = 0; @@ -246,7 +286,7 @@ read_data(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { } case ZIP_SOURCE_SUPPORTS: - return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_BEGIN_WRITE_CLONING, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_WRITE, -1); + return zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_BEGIN_WRITE_CLONING, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_WRITE, -1); case ZIP_SOURCE_TELL: if (ctx->in->offset > ZIP_INT64_MAX) { @@ -457,7 +497,7 @@ buffer_new(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int } buffer->nfragments = j; buffer->first_owned_fragment = free_data ? 0 : buffer->nfragments; - buffer->fragment_offsets[nfragments] = offset; + buffer->fragment_offsets[buffer->nfragments] = offset; buffer->size = offset; } diff --git a/lib/zip_source_compress.c b/lib/zip_source_compress.c index a1ac3e0..ab6ea72 100644 --- a/lib/zip_source_compress.c +++ b/lib/zip_source_compress.c @@ -67,6 +67,16 @@ static struct implementation implementations[] = { #if defined(HAVE_LIBBZ2) {ZIP_CM_BZIP2, &zip_algorithm_bzip2_compress, &zip_algorithm_bzip2_decompress}, #endif +#if defined(HAVE_LIBLZMA) + /* Disabled - because 7z isn't able to unpack ZIP+LZMA ZIP+LZMA2 + archives made this way - and vice versa. + + {ZIP_CM_LZMA, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress}, + {ZIP_CM_LZMA2, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress}, + */ + {ZIP_CM_XZ, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress}, +#endif + }; static size_t implementations_size = sizeof(implementations) / sizeof(implementations[0]); @@ -96,10 +106,10 @@ get_algorithm(zip_int32_t method, bool compress) { return NULL; } -bool -zip_compression_method_supported(zip_int32_t method, bool compress) { +ZIP_EXTERN int +zip_compression_method_supported(zip_int32_t method, int compress) { if (method == ZIP_CM_STORE) { - return true; + return 1; } return get_algorithm(method, compress) != NULL; } @@ -347,9 +357,6 @@ compress_callback(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip } return 0; - case ZIP_SOURCE_GET_COMPRESSION_FLAGS: - return ctx->is_stored ? 0 : ctx->algorithm->compression_flags(ctx->ud); - case ZIP_SOURCE_ERROR: return zip_error_to_data(&ctx->error, data, len); @@ -357,8 +364,24 @@ compress_callback(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip 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_COMPRESSION_FLAGS, -1); + 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); diff --git a/lib/zip_source_crc.c b/lib/zip_source_crc.c index 8797dfe..4033395 100644 --- a/lib/zip_source_crc.c +++ b/lib/zip_source_crc.c @@ -148,7 +148,6 @@ crc_read(zip_source_t *src, void *_ctx, void *data, zip_uint64_t len, zip_source st->comp_method = ZIP_CM_STORE; st->encryption_method = ZIP_EM_NONE; st->valid |= ZIP_STAT_SIZE | ZIP_STAT_CRC | ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD | ZIP_STAT_ENCRYPTION_METHOD; - ; } return 0; } @@ -168,7 +167,7 @@ crc_read(zip_source_t *src, void *_ctx, void *data, zip_uint64_t len, zip_source return -1; } - return mask & ~zip_source_make_command_bitmap(ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_GET_COMPRESSION_FLAGS, -1); + return mask & ~zip_source_make_command_bitmap(ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_GET_FILE_ATTRIBUTES, -1); } case ZIP_SOURCE_SEEK: { diff --git a/lib/zip_source_file.h b/lib/zip_source_file.h new file mode 100644 index 0000000..43a4645 --- /dev/null +++ b/lib/zip_source_file.h @@ -0,0 +1,90 @@ +/* + zip_source_file.h -- header for common file operations + Copyright (C) 2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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. +*/ + +struct zip_source_file_stat { + zip_uint64_t size; /* must be valid for regular files */ + time_t mtime; /* must always be valid, is initialized to current time */ + bool exists; /* must always be vaild */ + bool regular_file; /* must always be valid */ +}; + +typedef struct zip_source_file_context zip_source_file_context_t; +typedef struct zip_source_file_operations zip_source_file_operations_t; +typedef struct zip_source_file_stat zip_source_file_stat_t; + +struct zip_source_file_context { + zip_error_t error; /* last error information */ + zip_int64_t supports; + + /* reading */ + char *fname; /* name of file to read from */ + void *f; /* file to read from */ + zip_stat_t st; /* stat information passed in */ + zip_file_attributes_t attributes; /* additional file attributes */ + zip_error_t stat_error; /* error returned for stat */ + zip_uint64_t start; /* start offset of data to read */ + zip_uint64_t len; /* length of the file, 0 for up to EOF */ + zip_uint64_t offset; /* current offset relative to start (0 is beginning of part we read) */ + + /* writing */ + char *tmpname; + void *fout; + + zip_source_file_operations_t *ops; + void *ops_userdata; +}; + + +/* The following methods must be implemented to support each feature: + - close, read, seek, and stat must always be implemented. + - To support specifying the file by name, open, and strdup must be implemented. + - For write support, the file must be specified by name and close, commit_write, create_temp_output, remove, rollback_write, and tell must be implemented. + - create_temp_output_cloning is always optional. */ + +struct zip_source_file_operations { + void (*close)(zip_source_file_context_t *ctx); + zip_int64_t (*commit_write)(zip_source_file_context_t *ctx); + zip_int64_t (*create_temp_output)(zip_source_file_context_t *ctx); + zip_int64_t (*create_temp_output_cloning)(zip_source_file_context_t *ctx, zip_uint64_t len); + bool (*open)(zip_source_file_context_t *ctx); + zip_int64_t (*read)(zip_source_file_context_t *ctx, void *buf, zip_uint64_t len); + zip_int64_t (*remove)(zip_source_file_context_t *ctx); + void (*rollback_write)(zip_source_file_context_t *ctx); + bool (*seek)(zip_source_file_context_t *ctx, void *f, zip_int64_t offset, int whence); + bool (*stat)(zip_source_file_context_t *ctx, zip_source_file_stat_t *st); + char *(*string_duplicate)(zip_source_file_context_t *ctx, const char *); + zip_int64_t (*tell)(zip_source_file_context_t *ctx, void *f); + zip_int64_t (*write)(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len); +}; + +zip_source_t *zip_source_file_common_new(const char *fname, void *file, zip_uint64_t start, zip_int64_t len, const zip_stat_t *st, zip_source_file_operations_t *ops, void *ops_userdata, zip_error_t *error); diff --git a/lib/zip_source_file_common.c b/lib/zip_source_file_common.c new file mode 100644 index 0000000..74e2100 --- /dev/null +++ b/lib/zip_source_file_common.c @@ -0,0 +1,378 @@ +/* + zip_source_file_common.c -- create data source from file + Copyright (C) 1999-2019 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 +#include +#include + +#include "zipint.h" + +#include "zip_source_file.h" + +static zip_int64_t read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd); + +static void +zip_source_file_stat_init(zip_source_file_stat_t *st) { + st->size = 0; + st->mtime = time(NULL); + st->exists = false; + st->regular_file = false; +} + +zip_source_t * +zip_source_file_common_new(const char *fname, void *file, zip_uint64_t start, zip_int64_t len, const zip_stat_t *st, zip_source_file_operations_t *ops, void *ops_userdata, zip_error_t *error) { + zip_source_file_context_t *ctx; + zip_source_t *zs; + zip_source_file_stat_t sb; + + if (ops == NULL) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + if (ops->close == NULL || ops->read == NULL || ops->seek == NULL || ops->stat == NULL) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + return NULL; + } + + if (ops->write != NULL && (ops->commit_write == NULL || ops->create_temp_output == NULL || ops->remove == NULL || ops->rollback_write == NULL || ops->tell == NULL)) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + return NULL; + } + + if (fname != NULL) { + if (ops->open == NULL || ops->string_duplicate == NULL) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + return NULL; + } + } + else if (file == NULL) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + if (len < 0) { + len = 0; + } + + if (start > ZIP_INT64_MAX || start + (zip_uint64_t)len < start) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + if ((ctx = (zip_source_file_context_t *)malloc(sizeof(zip_source_file_context_t))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + ctx->ops = ops; + ctx->ops_userdata = ops_userdata; + ctx->fname = NULL; + if (fname) { + if ((ctx->fname = ops->string_duplicate(ctx, fname)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + free(ctx); + return NULL; + } + } + ctx->f = file; + ctx->start = start; + ctx->len = (zip_uint64_t)len; + if (st) { + memcpy(&ctx->st, st, sizeof(ctx->st)); + ctx->st.name = NULL; + ctx->st.valid &= ~ZIP_STAT_NAME; + } + else { + zip_stat_init(&ctx->st); + } + + if (ctx->len > 0) { + ctx->st.size = ctx->len; + ctx->st.valid |= ZIP_STAT_SIZE; + } + + zip_error_init(&ctx->stat_error); + + ctx->tmpname = NULL; + ctx->fout = NULL; + + zip_error_init(&ctx->error); + zip_file_attributes_init(&ctx->attributes); + + ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1); + + zip_source_file_stat_init(&sb); + if (!ops->stat(ctx, &sb)) { + _zip_error_copy(error, &ctx->error); + free(ctx->fname); + free(ctx); + return NULL; + } + + if (!sb.exists) { + if (ctx->fname && ctx->start == 0 && ctx->len == 0 && ops->write != NULL) { + ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE; + /* zip_open_from_source checks for this to detect non-existing files */ + zip_error_set(&ctx->stat_error, ZIP_ER_READ, ENOENT); + } + else { + zip_error_set(&ctx->stat_error, ZIP_ER_READ, ENOENT); + free(ctx->fname); + free(ctx); + return NULL; + } + } + else { + if ((ctx->st.valid & ZIP_STAT_MTIME) == 0) { + ctx->st.mtime = sb.mtime; + ctx->st.valid |= ZIP_STAT_MTIME; + } + if (sb.regular_file) { + ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE; + + if (ctx->start + ctx->len > sb.size) { + zip_error_set(error, ZIP_ER_INVAL, 0); + free(ctx->fname); + free(ctx); + return NULL; + } + + if (ctx->len == 0) { + ctx->len = sb.size - ctx->start; + ctx->st.size = ctx->len; + ctx->st.valid |= ZIP_STAT_SIZE; + + /* when using a partial file, don't allow writing */ + if (ctx->fname && start == 0 && ops->write != NULL) { + ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE; + } + } + } + + ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_GET_FILE_ATTRIBUTES); + } + + ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ACCEPT_EMPTY); + if (ops->create_temp_output_cloning != NULL) { + if (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE)) { + ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE_CLONING); + } + } + + if ((zs = zip_source_function_create(read_file, ctx, error)) == NULL) { + free(ctx->fname); + free(ctx); + return NULL; + } + + return zs; +} + + +static zip_int64_t +read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { + zip_source_file_context_t *ctx; + char *buf; + + ctx = (zip_source_file_context_t *)state; + buf = (char *)data; + + switch (cmd) { + case ZIP_SOURCE_ACCEPT_EMPTY: + return 0; + + case ZIP_SOURCE_BEGIN_WRITE: + /* write support should not be set if fname is NULL */ + if (ctx->fname == NULL) { + zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0); + return -1; + } + return ctx->ops->create_temp_output(ctx); + + case ZIP_SOURCE_BEGIN_WRITE_CLONING: + /* write support should not be set if fname is NULL */ + if (ctx->fname == NULL) { + zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0); + return -1; + } + return ctx->ops->create_temp_output_cloning(ctx, len); + + case ZIP_SOURCE_CLOSE: + if (ctx->fname) { + ctx->ops->close(ctx); + ctx->f = NULL; + } + return 0; + + case ZIP_SOURCE_COMMIT_WRITE: { + zip_int64_t ret = ctx->ops->commit_write(ctx); + ctx->fout = NULL; + if (ret == 0) { + free(ctx->tmpname); + ctx->tmpname = NULL; + } + return ret; + } + + case ZIP_SOURCE_ERROR: + return zip_error_to_data(&ctx->error, data, len); + + case ZIP_SOURCE_FREE: + free(ctx->fname); + free(ctx->tmpname); + if (ctx->f) { + ctx->ops->close(ctx); + } + free(ctx); + return 0; + + case ZIP_SOURCE_GET_FILE_ATTRIBUTES: + if (len < sizeof(ctx->attributes)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + memcpy(data, &ctx->attributes, sizeof(ctx->attributes)); + return sizeof(ctx->attributes); + + case ZIP_SOURCE_OPEN: + if (ctx->fname) { + if (ctx->ops->open(ctx) == false) { + return -1; + } + } + + if (ctx->start > 0) { // TODO: rewind on re-open + if (ctx->ops->seek(ctx, ctx->f, (zip_int64_t)ctx->start, SEEK_SET) == false) { + /* TODO: skip by reading */ + return -1; + } + } + ctx->offset = 0; + return 0; + + case ZIP_SOURCE_READ: { + zip_int64_t i; + zip_uint64_t n; + + if (ctx->len > 0) { + n = ZIP_MIN(ctx->len - ctx->offset, len); + } + else { + n = len; + } + + if ((i = ctx->ops->read(ctx, buf, n)) < 0) { + zip_error_set(&ctx->error, ZIP_ER_READ, errno); + return -1; + } + ctx->offset += (zip_uint64_t)i; + + return i; + } + + case ZIP_SOURCE_REMOVE: + return ctx->ops->remove(ctx); + + case ZIP_SOURCE_ROLLBACK_WRITE: + ctx->ops->rollback_write(ctx); + ctx->fout = NULL; + free(ctx->tmpname); + ctx->tmpname = NULL; + return 0; + + case ZIP_SOURCE_SEEK: { + zip_int64_t new_offset = zip_source_seek_compute_offset(ctx->offset, ctx->len, data, len, &ctx->error); + + if (new_offset < 0) { + return -1; + } + + /* The actual offset inside the file must be representable as zip_int64_t. */ + if (new_offset > ZIP_INT64_MAX - (zip_int64_t)ctx->start) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, EOVERFLOW); + return -1; + } + + ctx->offset = (zip_uint64_t)new_offset; + + if (ctx->ops->seek(ctx, ctx->f, (zip_int64_t)(ctx->offset + ctx->start), SEEK_SET) == false) { + return -1; + } + return 0; + } + + case ZIP_SOURCE_SEEK_WRITE: { + zip_source_args_seek_t *args; + + args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error); + if (args == NULL) { + return -1; + } + + if (ctx->ops->seek(ctx, ctx->fout, args->offset, args->whence) == false) { + return -1; + } + return 0; + } + + case ZIP_SOURCE_STAT: { + if (len < sizeof(ctx->st)) + return -1; + + if (zip_error_code_zip(&ctx->stat_error) != 0) { + zip_error_set(&ctx->error, zip_error_code_zip(&ctx->stat_error), zip_error_code_system(&ctx->stat_error)); + return -1; + } + + memcpy(data, &ctx->st, sizeof(ctx->st)); + return sizeof(ctx->st); + } + + case ZIP_SOURCE_SUPPORTS: + return ctx->supports; + + case ZIP_SOURCE_TELL: + return (zip_int64_t)ctx->offset; + + case ZIP_SOURCE_TELL_WRITE: + return ctx->ops->tell(ctx, ctx->fout); + + case ZIP_SOURCE_WRITE: + return ctx->ops->write(ctx, data, len); + + default: + zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); + return -1; + } +} diff --git a/lib/zip_source_file_stdio.c b/lib/zip_source_file_stdio.c new file mode 100644 index 0000000..4a22f78 --- /dev/null +++ b/lib/zip_source_file_stdio.c @@ -0,0 +1,208 @@ +/* + zip_source_file_stdio.c -- read-only stdio file source implementation + Copyright (C) 2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 "zipint.h" + +#include "zip_source_file.h" +#include "zip_source_file_stdio.h" + +#include +#include +#include +#include + +#ifdef _WIN32 +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif +#endif + +/* clang-format off */ +static zip_source_file_operations_t ops_stdio_read = { + _zip_stdio_op_close, + NULL, + NULL, + NULL, + NULL, + _zip_stdio_op_read, + NULL, + NULL, + _zip_stdio_op_seek, + _zip_stdio_op_stat, + NULL, + _zip_stdio_op_tell, + NULL +}; +/* clang-format on */ + + +ZIP_EXTERN zip_source_t * +zip_source_filep(zip_t *za, FILE *file, zip_uint64_t start, zip_int64_t len) { + if (za == NULL) { + return NULL; + } + + return zip_source_filep_create(file, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_filep_create(FILE *file, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { + if (file == NULL || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return zip_source_file_common_new(NULL, file, start, length, NULL, &ops_stdio_read, NULL, error); +} + + +void +_zip_stdio_op_close(zip_source_file_context_t *ctx) { + fclose((FILE *)ctx->f); +} + + +zip_int64_t +_zip_stdio_op_read(zip_source_file_context_t *ctx, void *buf, zip_uint64_t len) { + size_t i; + if (len > SIZE_MAX) { + len = SIZE_MAX; + } + + if ((i = fread(buf, 1, (size_t)len, ctx->f)) == 0) { + if (ferror((FILE *)ctx->f)) { + zip_error_set(&ctx->error, ZIP_ER_READ, errno); + return -1; + } + } + + return (zip_int64_t)i; +} + + +bool +_zip_stdio_op_seek(zip_source_file_context_t *ctx, void *f, zip_int64_t offset, int whence) { +#if ZIP_FSEEK_MAX > ZIP_INT64_MAX + if (offset > ZIP_FSEEK_MAX || offset < ZIP_FSEEK_MIN) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, EOVERFLOW); + return false; + } +#endif + + if (fseeko((FILE *)f, (off_t)offset, whence) < 0) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, errno); + return false; + } + return true; +} + + +bool +_zip_stdio_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st) { + struct stat sb; + + int ret; + + if (ctx->fname) { + ret = stat(ctx->fname, &sb); + } + else { + ret = fstat(fileno((FILE *)ctx->f), &sb); + } + + if (ret < 0) { + if (errno == ENOENT) { + st->exists = false; + return true; + } + zip_error_set(&ctx->error, ZIP_ER_READ, errno); + return false; + } + + st->size = (zip_uint64_t)sb.st_size; + st->mtime = sb.st_mtime; + + st->regular_file = S_ISREG(sb.st_mode); + st->exists = true; + + /* We're using UNIX file API, even on Windows; thus, we supply external file attributes with Unix values. */ + /* TODO: This could be improved on Windows by providing Windows-specific file attributes */ + ctx->attributes.valid = ZIP_FILE_ATTRIBUTES_HOST_SYSTEM | ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES; + ctx->attributes.host_system = ZIP_OPSYS_UNIX; + ctx->attributes.external_file_attributes = (((zip_uint32_t)sb.st_mode) << 16) | ((sb.st_mode & S_IWUSR) ? 0 : 1); + + return true; +} + + +zip_int64_t +_zip_stdio_op_tell(zip_source_file_context_t *ctx, void *f) { + off_t offset = ftello((FILE *)f); + + if (offset < 0) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, errno); + } + + return offset; +} + + +/* + * fopen replacement that sets the close-on-exec flag + * some implementations support an fopen 'e' flag for that, + * but e.g. macOS doesn't. + */ +FILE * +_zip_fopen_close_on_exec(const char *name, bool writeable) { + int fd; + int flags; + FILE *fp; + + flags = O_CLOEXEC; + if (writeable) { + flags |= O_RDWR; + } + else { + flags |= O_RDONLY; + } + + /* mode argument needed on Windows */ + if ((fd = open(name, flags, 0666)) < 0) { + return NULL; + } + if ((fp = fdopen(fd, writeable ? "r+b" : "rb")) == NULL) { + return NULL; + } + return fp; +} diff --git a/lib/zip_source_file_stdio.h b/lib/zip_source_file_stdio.h new file mode 100644 index 0000000..1bf698c --- /dev/null +++ b/lib/zip_source_file_stdio.h @@ -0,0 +1,47 @@ +#ifndef _HAD_ZIP_SOURCE_FILE_STDIO_H +#define _HAD_ZIP_SOURCE_FILE_STDIO_H + +/* + zip_source_file_stdio.h -- common header for stdio file implementation + Copyright (C) 2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 + +void _zip_stdio_op_close(zip_source_file_context_t *ctx); +zip_int64_t _zip_stdio_op_read(zip_source_file_context_t *ctx, void *buf, zip_uint64_t len); +bool _zip_stdio_op_seek(zip_source_file_context_t *ctx, void *f, zip_int64_t offset, int whence); +bool _zip_stdio_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st); +zip_int64_t _zip_stdio_op_tell(zip_source_file_context_t *ctx, void *f); + +FILE *_zip_fopen_close_on_exec(const char *name, bool writeable); + +#endif /* _HAD_ZIP_SOURCE_FILE_STDIO_H */ diff --git a/lib/zip_source_file_stdio_named.c b/lib/zip_source_file_stdio_named.c new file mode 100644 index 0000000..dae8177 --- /dev/null +++ b/lib/zip_source_file_stdio_named.c @@ -0,0 +1,313 @@ +/* + zip_source_file_stdio_named.c -- source for stdio file opened by name + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 "zipint.h" + +#include "zip_source_file.h" +#include "zip_source_file_stdio.h" + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#ifdef HAVE_CLONEFILE +#include +#include +#define CAN_CLONE +#endif +#ifdef HAVE_FICLONERANGE +#include +#include +#define CAN_CLONE +#endif + +static zip_int64_t _zip_stdio_op_commit_write(zip_source_file_context_t *ctx); +static zip_int64_t _zip_stdio_op_create_temp_output(zip_source_file_context_t *ctx); +#ifdef CAN_CLONE +static zip_int64_t _zip_stdio_op_create_temp_output_cloning(zip_source_file_context_t *ctx, zip_uint64_t offset); +#endif +static bool _zip_stdio_op_open(zip_source_file_context_t *ctx); +static zip_int64_t _zip_stdio_op_remove(zip_source_file_context_t *ctx); +static void _zip_stdio_op_rollback_write(zip_source_file_context_t *ctx); +static char *_zip_stdio_op_strdup(zip_source_file_context_t *ctx, const char *string); +static zip_int64_t _zip_stdio_op_write(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len); + +/* clang-format off */ +static zip_source_file_operations_t ops_stdio_named = { + _zip_stdio_op_close, + _zip_stdio_op_commit_write, + _zip_stdio_op_create_temp_output, +#ifdef CAN_CLONE + _zip_stdio_op_create_temp_output_cloning, +#else + NULL, +#endif + _zip_stdio_op_open, + _zip_stdio_op_read, + _zip_stdio_op_remove, + _zip_stdio_op_rollback_write, + _zip_stdio_op_seek, + _zip_stdio_op_stat, + _zip_stdio_op_strdup, + _zip_stdio_op_tell, + _zip_stdio_op_write +}; +/* clang-format on */ + +ZIP_EXTERN zip_source_t * +zip_source_file(zip_t *za, const char *fname, zip_uint64_t start, zip_int64_t len) { + if (za == NULL) + return NULL; + + return zip_source_file_create(fname, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_file_create(const char *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { + if (fname == NULL || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return zip_source_file_common_new(fname, NULL, start, length, NULL, &ops_stdio_named, NULL, error); +} + + +static zip_int64_t +_zip_stdio_op_commit_write(zip_source_file_context_t *ctx) { + if (fclose(ctx->fout) < 0) { + zip_error_set(&ctx->error, ZIP_ER_WRITE, errno); + return -1; + } + if (rename(ctx->tmpname, ctx->fname) < 0) { + zip_error_set(&ctx->error, ZIP_ER_RENAME, errno); + return -1; + } + + return 0; +} + + +static zip_int64_t +_zip_stdio_op_create_temp_output(zip_source_file_context_t *ctx) { + char *temp; + int tfd; + int mode; + FILE *tfp; + struct stat st; + + if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return -1; + } + + if (stat(ctx->fname, &st) == 0) { + mode = st.st_mode; + } + else { + mode = -1; + } + + sprintf(temp, "%s.XXXXXX", ctx->fname); + + if ((tfd = _zip_mkstempm(temp, mode)) == -1) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return -1; + } + + if ((tfp = fdopen(tfd, "r+b")) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + close(tfd); + (void)remove(temp); + free(temp); + return -1; + } + + ctx->fout = tfp; + ctx->tmpname = temp; + + return 0; +} + +#ifdef CAN_CLONE +static zip_int64_t +_zip_stdio_op_create_temp_output_cloning(zip_source_file_context_t *ctx, zip_uint64_t offset) { + char *temp; + FILE *tfp; + + if (offset > ZIP_OFF_MAX) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, E2BIG); + return -1; + } + + if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return -1; + } + sprintf(temp, "%s.XXXXXX", ctx->fname); + +#ifdef HAVE_CLONEFILE +#ifndef __clang_analyzer__ + /* we can't use mkstemp, since clonefile insists on creating the file */ + if (mktemp(temp) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return -1; + } +#endif + + if (clonefile(ctx->fname, temp, 0) < 0) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return -1; + } + if ((tfp = _zip_fopen_close_on_exec(temp, true)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + (void)remove(temp); + free(temp); + return -1; + } +#else + { + int fd; + struct file_clone_range range; + struct stat st; + + if (fstat(fileno(ctx->f), &st) < 0) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return -1; + } + + if ((fd = mkstemp(temp)) < 0) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + free(temp); + return -1; + } + + range.src_fd = fileno(ctx->f); + range.src_offset = 0; + range.src_length = ((offset + st.st_blksize - 1) / st.st_blksize) * st.st_blksize; + if (range.src_length > st.st_size) { + range.src_length = 0; + } + range.dest_offset = 0; + if (ioctl(fd, FICLONERANGE, &range) < 0) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + (void)close(fd); + (void)remove(temp); + free(temp); + return -1; + } + + if ((tfp = fdopen(fd, "r+b")) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + (void)close(fd); + (void)remove(temp); + free(temp); + return -1; + } + } +#endif + + if (ftruncate(fileno(tfp), (off_t)offset) < 0) { + (void)fclose(tfp); + (void)remove(temp); + free(temp); + return -1; + } + if (fseeko(tfp, (off_t)offset, SEEK_SET) < 0) { + (void)fclose(tfp); + (void)remove(temp); + free(temp); + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); + } + + ctx->fout = tfp; + ctx->tmpname = temp; + + return 0; +} +#endif + +static bool +_zip_stdio_op_open(zip_source_file_context_t *ctx) { + if ((ctx->f = _zip_fopen_close_on_exec(ctx->fname, false)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_OPEN, errno); + return false; + } + return true; +} + + +static zip_int64_t +_zip_stdio_op_remove(zip_source_file_context_t *ctx) { + if (remove(ctx->fname) < 0) { + zip_error_set(&ctx->error, ZIP_ER_REMOVE, errno); + return -1; + } + return 0; +} + + +static void +_zip_stdio_op_rollback_write(zip_source_file_context_t *ctx) { + if (ctx->fout) { + fclose(ctx->fout); + } + (void)remove(ctx->tmpname); +} + +static char * +_zip_stdio_op_strdup(zip_source_file_context_t *ctx, const char *string) { + return strdup(string); +} + + +static zip_int64_t +_zip_stdio_op_write(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len) { + size_t ret; + + clearerr((FILE *)ctx->fout); + ret = fwrite(data, 1, len, (FILE *)ctx->fout); + if (ret != len || ferror((FILE *)ctx->fout)) { + zip_error_set(&ctx->error, ZIP_ER_WRITE, errno); + return -1; + } + + return (zip_int64_t)ret; +} diff --git a/lib/zip_source_file_win32.c b/lib/zip_source_file_win32.c new file mode 100644 index 0000000..2fe838f --- /dev/null +++ b/lib/zip_source_file_win32.c @@ -0,0 +1,230 @@ +/* + zip_source_file_win32.c -- read-only Windows file source implementation + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 "zip_source_file_win32.h" + +static bool _zip_win32_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st); + +static bool _zip_stat_win32(zip_source_file_context_t *ctx, zip_source_file_stat_t *st, HANDLE h); + +static zip_source_file_operations_t ops_win32_read = { + _zip_win32_op_close, + NULL, + NULL, + NULL, + NULL, + _zip_win32_op_read, + NULL, + NULL, + _zip_win32_op_seek, + _zip_win32_op_stat, + NULL, + _zip_win32_op_tell, + NULL +}; + + +ZIP_EXTERN zip_source_t * +zip_source_win32handle(zip_t *za, HANDLE h, zip_uint64_t start, zip_int64_t len) { + if (za == NULL) { + return NULL; + } + + return zip_source_win32handle_create(h, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_win32handle_create(HANDLE h, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { + if (h == INVALID_HANDLE_VALUE || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return zip_source_file_common_new(NULL, h, start, length, NULL, &ops_win32_read, NULL, error); +} + + +void +_zip_win32_op_close(zip_source_file_context_t *ctx) { + CloseHandle((HANDLE)ctx->f); +} + + +zip_int64_t +_zip_win32_op_read(zip_source_file_context_t *ctx, void *buf, zip_uint64_t len) { + DWORD i; + + /* TODO: cap len to "DWORD_MAX" */ + if (!ReadFile((HANDLE)ctx->f, buf, (DWORD)len, &i, NULL)) { + zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + return (zip_int64_t)i; +} + + +bool +_zip_win32_op_seek(zip_source_file_context_t *ctx, void *f, zip_int64_t offset, int whence) { + LARGE_INTEGER li; + DWORD method; + + switch (whence) { + case SEEK_SET: + method = FILE_BEGIN; + break; + case SEEK_END: + method = FILE_END; + break; + case SEEK_CUR: + method = FILE_CURRENT; + break; + default: + zip_error_set(&ctx->error, ZIP_ER_SEEK, EINVAL); + return -1; + } + + li.QuadPart = (LONGLONG)offset; + if (!SetFilePointerEx((HANDLE)f, li, NULL, method)) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, _zip_win32_error_to_errno(GetLastError())); + return false; + } + + return true; +} + + +static bool +_zip_win32_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st) { + return _zip_stat_win32(ctx, st, (HANDLE)ctx->f); +} + + +zip_int64_t +_zip_win32_op_tell(zip_source_file_context_t *ctx, void *f) { + LARGE_INTEGER zero; + LARGE_INTEGER new_offset; + + zero.QuadPart = 0; + if (!SetFilePointerEx((HANDLE)f, zero, &new_offset, FILE_CURRENT)) { + zip_error_set(&ctx->error, ZIP_ER_SEEK, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + return (zip_int64_t)new_offset.QuadPart; +} + + +int +_zip_win32_error_to_errno(DWORD win32err) { + /* Note: This list isn't exhaustive, but should cover common cases. */ + switch (win32err) { + case ERROR_INVALID_PARAMETER: + return EINVAL; + case ERROR_FILE_NOT_FOUND: + return ENOENT; + case ERROR_INVALID_HANDLE: + return EBADF; + case ERROR_ACCESS_DENIED: + return EACCES; + case ERROR_FILE_EXISTS: + return EEXIST; + case ERROR_TOO_MANY_OPEN_FILES: + return EMFILE; + case ERROR_DISK_FULL: + return ENOSPC; + default: + return 10000 + win32err; + } +} + + +static bool +_zip_stat_win32(zip_source_file_context_t *ctx, zip_source_file_stat_t *st, HANDLE h) { + FILETIME mtimeft; + time_t mtime; + LARGE_INTEGER size; + + if (!GetFileTime(h, NULL, NULL, &mtimeft)) { + zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError())); + return false; + } + if (_zip_filetime_to_time_t(mtimeft, &mtime) < 0) { + zip_error_set(&ctx->error, ZIP_ER_READ, ERANGE); + return false; + } + + st->exists = true; + st->mtime = mtime; + + if (GetFileType(h) == FILE_TYPE_DISK) { + st->regular_file = 1; + + if (!GetFileSizeEx(h, &size)) { + zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(GetLastError())); + return false; + } + + st->size = (zip_uint64_t)size.QuadPart; + } + + /* TODO: fill in ctx->attributes */ + + return true; +} + + +bool +_zip_filetime_to_time_t(FILETIME ft, time_t *t) { + /* + Inspired by http://stackoverflow.com/questions/6161776/convert-windows-filetime-to-second-in-unix-linux + */ + const zip_int64_t WINDOWS_TICK = 10000000LL; + const zip_int64_t SEC_TO_UNIX_EPOCH = 11644473600LL; + ULARGE_INTEGER li; + zip_int64_t secs; + time_t temp; + + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + secs = (li.QuadPart / WINDOWS_TICK - SEC_TO_UNIX_EPOCH); + + temp = (time_t)secs; + if (secs != (zip_int64_t)temp) { + return false; + } + + *t = temp; + return true; +} diff --git a/lib/zip_source_file_win32.h b/lib/zip_source_file_win32.h new file mode 100644 index 0000000..5092e63 --- /dev/null +++ b/lib/zip_source_file_win32.h @@ -0,0 +1,74 @@ +#ifndef _HAD_ZIP_SOURCE_FILE_WIN32_H +#define _HAD_ZIP_SOURCE_FILE_WIN32_H + +/* + zip_source_file_win32.h -- common header for Windows file implementation + Copyright (C) 2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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. +*/ + +/* 0x0501 => Windows XP; needs to be at least this value because of GetFileSizeEx */ +#if !defined(MS_UWP) && !defined(_WIN32_WINNT) +#define _WIN32_WINNT 0x0501 +#endif + +#include + +#include + +#include "zipint.h" + +#include "zip_source_file.h" + +struct zip_win32_file_operations { + char *(*allocate_tempname)(const char *name, size_t extra_chars, size_t *lengthp); + HANDLE (__stdcall *create_file)(const void *name, DWORD access, DWORD share_mode, PSECURITY_ATTRIBUTES security_attributes, DWORD creation_disposition, DWORD file_attributes, HANDLE template_file); + BOOL (__stdcall *delete_file)(const void *name); + DWORD (__stdcall *get_file_attributes)(const void *name); + BOOL (__stdcall *get_file_attributes_ex)(const void *name, GET_FILEEX_INFO_LEVELS info_level, void *information); + void (*make_tempname)(char *buf, size_t len, const char *name, zip_uint32_t i); + BOOL (__stdcall *move_file)(const void *from, const void *to, DWORD flags); + BOOL (__stdcall *set_file_attributes)(const void *name, DWORD attributes); + char *(*string_duplicate)(const char *string); +}; + +typedef struct zip_win32_file_operations zip_win32_file_operations_t; + +extern zip_source_file_operations_t _zip_source_file_win32_named_ops; + +void _zip_win32_op_close(zip_source_file_context_t *ctx); +zip_int64_t _zip_win32_op_read(zip_source_file_context_t *ctx, void *buf, zip_uint64_t len); +bool _zip_win32_op_seek(zip_source_file_context_t *ctx, void *f, zip_int64_t offset, int whence); +zip_int64_t _zip_win32_op_tell(zip_source_file_context_t *ctx, void *f); + +bool _zip_filetime_to_time_t(FILETIME ft, time_t *t); +int _zip_win32_error_to_errno(DWORD win32err); + +#endif /* _HAD_ZIP_SOURCE_FILE_WIN32_H */ diff --git a/lib/zip_source_file_win32_ansi.c b/lib/zip_source_file_win32_ansi.c new file mode 100644 index 0000000..573b74e --- /dev/null +++ b/lib/zip_source_file_win32_ansi.c @@ -0,0 +1,81 @@ +/* + zip_source_file_win32_ansi.c -- source for Windows file opened by ANSI name + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 "zip_source_file_win32.h" + +static char *ansi_allocate_tempname(const char *name, size_t extra_chars, size_t *lengthp); +static void ansi_make_tempname(char *buf, size_t len, const char *name, zip_uint32_t i); + +zip_win32_file_operations_t ops_ansi = { + ansi_allocate_tempname, + CreateFileA, + DeleteFileA, + GetFileAttributesA, + GetFileAttributesExA, + ansi_make_tempname, + MoveFileExA, + SetFileAttributesA, + strdup +}; + +ZIP_EXTERN zip_source_t * +zip_source_win32a(zip_t *za, const char *fname, zip_uint64_t start, zip_int64_t len) { + if (za == NULL) + return NULL; + + return zip_source_win32a_create(fname, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_win32a_create(const char *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { + if (fname == NULL || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + return zip_source_file_common_new(fname, NULL, start, length, NULL, &_zip_source_file_win32_named_ops, &ops_ansi, error); +} + + +static char * +ansi_allocate_tempname(const char *name, size_t extra_chars, size_t *lengthp) { + *lengthp = strlen(name) + extra_chars; + return (char *)malloc(*lengthp); +} + + +static void +ansi_make_tempname(char *buf, size_t len, const char *name, zip_uint32_t i) { + snprintf(buf, len, "%s.%08x", name, i); +} diff --git a/lib/zip_source_file_win32_named.c b/lib/zip_source_file_win32_named.c new file mode 100644 index 0000000..9591b26 --- /dev/null +++ b/lib/zip_source_file_win32_named.c @@ -0,0 +1,266 @@ +/* + zip_source_file_win32_named.c -- source for Windows file opened by name + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 "zip_source_file_win32.h" + +static zip_int64_t _zip_win32_named_op_commit_write(zip_source_file_context_t *ctx); +static zip_int64_t _zip_win32_named_op_create_temp_output(zip_source_file_context_t *ctx); +static bool _zip_win32_named_op_open(zip_source_file_context_t *ctx); +static zip_int64_t _zip_win32_named_op_remove(zip_source_file_context_t *ctx); +static void _zip_win32_named_op_rollback_write(zip_source_file_context_t *ctx); +static bool _zip_win32_named_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st); +static char *_zip_win32_named_op_string_duplicate(zip_source_file_context_t *ctx, const char *string); +static zip_int64_t _zip_win32_named_op_write(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len); + +static HANDLE win32_named_open(zip_source_file_context_t *ctx, const char *name, bool temporary, PSECURITY_ATTRIBUTES security_attributes); + +/* clang-format off */ +zip_source_file_operations_t _zip_source_file_win32_named_ops = { + _zip_win32_op_close, + _zip_win32_named_op_commit_write, + _zip_win32_named_op_create_temp_output, + NULL, + _zip_win32_named_op_open, + _zip_win32_op_read, + _zip_win32_named_op_remove, + _zip_win32_named_op_rollback_write, + _zip_win32_op_seek, + _zip_win32_named_op_stat, + _zip_win32_named_op_string_duplicate, + _zip_win32_op_tell, + _zip_win32_named_op_write +}; +/* clang-format on */ + +static zip_int64_t +_zip_win32_named_op_commit_write(zip_source_file_context_t *ctx) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + if (!CloseHandle((HANDLE)ctx->fout)) { + zip_error_set(&ctx->error, ZIP_ER_WRITE, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + DWORD attributes = file_ops->get_file_attributes(ctx->tmpname); + if (attributes == INVALID_FILE_ATTRIBUTES) { + zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + if (attributes & FILE_ATTRIBUTE_TEMPORARY) { + if (!file_ops->set_file_attributes(ctx->tmpname, attributes & ~FILE_ATTRIBUTE_TEMPORARY)) { + zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + } + + if (!file_ops->move_file(ctx->tmpname, ctx->fname, MOVEFILE_REPLACE_EXISTING)) { + zip_error_set(&ctx->error, ZIP_ER_RENAME, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + return 0; +} + +static zip_int64_t +_zip_win32_named_op_create_temp_output(zip_source_file_context_t *ctx) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + zip_uint32_t value, i; + HANDLE th = INVALID_HANDLE_VALUE; + void *temp = NULL; + PSECURITY_DESCRIPTOR psd = NULL; + PSECURITY_ATTRIBUTES psa = NULL; + SECURITY_ATTRIBUTES sa; + SECURITY_INFORMATION si; + DWORD success; + PACL dacl = NULL; + char *tempname = NULL; + size_t tempname_size = 0; + + if ((HANDLE)ctx->f != INVALID_HANDLE_VALUE && GetFileType((HANDLE)ctx->f) == FILE_TYPE_DISK) { + si = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION; + success = GetSecurityInfo((HANDLE)ctx->f, SE_FILE_OBJECT, si, NULL, NULL, &dacl, NULL, &psd); + if (success == ERROR_SUCCESS) { + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.bInheritHandle = FALSE; + sa.lpSecurityDescriptor = psd; + psa = &sa; + } + } + + #ifndef MS_UWP + value = GetTickCount(); +#else + value = (zip_uint32_t)(GetTickCount64() & 0xffffffff); +#endif + + if ((tempname = file_ops->allocate_tempname(ctx->fname, 10, &tempname_size)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return -1; + } + + for (i = 0; i < 1024 && th == INVALID_HANDLE_VALUE; i++) { + file_ops->make_tempname(tempname, tempname_size, ctx->fname, value + i); + + th = win32_named_open(ctx, tempname, true, psa); + if (th == INVALID_HANDLE_VALUE && GetLastError() != ERROR_FILE_EXISTS) + break; + } + + if (th == INVALID_HANDLE_VALUE) { + free(tempname); + LocalFree(psd); + zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + LocalFree(psd); + ctx->fout = th; + ctx->tmpname = tempname; + + return 0; +} + + +static bool +_zip_win32_named_op_open(zip_source_file_context_t *ctx) { + HANDLE h = win32_named_open(ctx, ctx->fname, false, NULL); + + if (h == INVALID_HANDLE_VALUE) { + return false; + } + + ctx->f = h; + return true; +} + + +static zip_int64_t +_zip_win32_named_op_remove(zip_source_file_context_t *ctx) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + if (!file_ops->delete_file(ctx->fname)) { + zip_error_set(&ctx->error, ZIP_ER_REMOVE, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + return 0; +} + + +static void +_zip_win32_named_op_rollback_write(zip_source_file_context_t *ctx) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + if (ctx->fout) { + CloseHandle((HANDLE)ctx->fout); + } + file_ops->delete_file(ctx->tmpname); +} + + +static bool +_zip_win32_named_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + WIN32_FILE_ATTRIBUTE_DATA file_attributes; + + if (!file_ops->get_file_attributes_ex(ctx->fname, GetFileExInfoStandard, &file_attributes)) { + DWORD error = GetLastError(); + if (error == ERROR_FILE_NOT_FOUND) { + st->exists = false; + return true; + } + zip_error_set(&ctx->error, ZIP_ER_READ, _zip_win32_error_to_errno(error)); + return false; + } + + st->exists = true; + st->regular_file = true; /* TODO: Is this always right? How to determine without a HANDLE? */ + if (!_zip_filetime_to_time_t(file_attributes.ftLastWriteTime, &st->mtime)) { + zip_error_set(&ctx->error, ZIP_ER_READ, ERANGE); + return false; + } + st->size = ((zip_uint64_t)file_attributes.nFileSizeHigh << 32) | file_attributes.nFileSizeLow; + + /* TODO: fill in ctx->attributes */ + + return true; +} + + +static char * +_zip_win32_named_op_string_duplicate(zip_source_file_context_t *ctx, const char *string) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + return file_ops->string_duplicate(string); +} + + +static zip_int64_t +_zip_win32_named_op_write(zip_source_file_context_t *ctx, const void *data, zip_uint64_t len) { + DWORD ret; + if (!WriteFile((HANDLE)ctx->fout, data, (DWORD)len, &ret, NULL) || ret != len) { + zip_error_set(&ctx->error, ZIP_ER_WRITE, _zip_win32_error_to_errno(GetLastError())); + return -1; + } + + return (zip_int64_t)ret; +} + + +static HANDLE +win32_named_open(zip_source_file_context_t *ctx, const char *name, bool temporary, PSECURITY_ATTRIBUTES security_attributes) { + zip_win32_file_operations_t *file_ops = (zip_win32_file_operations_t *)ctx->ops_userdata; + + DWORD access = GENERIC_READ; + DWORD share_mode = FILE_SHARE_READ | FILE_SHARE_WRITE; + DWORD creation_disposition = OPEN_EXISTING; + DWORD file_attributes = FILE_ATTRIBUTE_NORMAL; + + if (temporary) { + access = GENERIC_READ | GENERIC_WRITE; + share_mode = FILE_SHARE_READ; + creation_disposition = CREATE_NEW; + file_attributes = FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY; + } + + HANDLE h = file_ops->create_file(name, access, share_mode, security_attributes, creation_disposition, file_attributes, NULL); + + if (h == INVALID_HANDLE_VALUE) { + zip_error_set(&ctx->error, ZIP_ER_OPEN, _zip_win32_error_to_errno(GetLastError())); + } + + return h; +} diff --git a/lib/zip_source_file_win32_utf16.c b/lib/zip_source_file_win32_utf16.c new file mode 100644 index 0000000..3fa0aa1 --- /dev/null +++ b/lib/zip_source_file_win32_utf16.c @@ -0,0 +1,108 @@ +/* + zip_source_file_win32_utf16.c -- source for Windows file opened by UTF-16 name + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 "zip_source_file_win32.h" + +static char *utf16_allocate_tempname(const char *name, size_t extra_chars, size_t *lengthp); +static HANDLE __stdcall utf16_create_file(const char *name, DWORD access, DWORD share_mode, PSECURITY_ATTRIBUTES security_attributes, DWORD creation_disposition, DWORD file_attributes, HANDLE template_file); +static void utf16_make_tempname(char *buf, size_t len, const char *name, zip_uint32_t i); +static char *utf16_strdup(const char *string); + +zip_win32_file_operations_t ops_utf16 = { + utf16_allocate_tempname, + utf16_create_file, + DeleteFileW, + GetFileAttributesW, + GetFileAttributesExW, + utf16_make_tempname, + MoveFileExW, + SetFileAttributesW, + utf16_strdup +}; + +ZIP_EXTERN zip_source_t * +zip_source_win32w(zip_t *za, const wchar_t *fname, zip_uint64_t start, zip_int64_t len) { + if (za == NULL) + return NULL; + + return zip_source_win32w_create(fname, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_win32w_create(const wchar_t *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { + if (fname == NULL || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + + return zip_source_file_common_new((const char *)fname, NULL, start, length, NULL, &_zip_source_file_win32_named_ops, &ops_utf16, error); +} + + +static char * +utf16_allocate_tempname(const char *name, size_t extra_chars, size_t *lengthp) { + *lengthp = wcslen((const wchar_t *)name) + extra_chars; + return (char *)malloc(*lengthp * sizeof(wchar_t)); +} + + +static HANDLE __stdcall +utf16_create_file(const char *name, DWORD access, DWORD share_mode, PSECURITY_ATTRIBUTES security_attributes, DWORD creation_disposition, DWORD file_attributes, HANDLE template_file) { +#ifdef MS_UWP + CREATEFILE2_EXTENDED_PARAMETERS extParams = {0}; + extParams.dwFileAttributes = file_attributes; + extParams.dwFileFlags = FILE_FLAG_RANDOM_ACCESS; + extParams.dwSecurityQosFlags = SECURITY_ANONYMOUS; + extParams.dwSize = sizeof(extParams); + extParams.hTemplateFile = template_file; + extParams.lpSecurityAttributes = security_attributes; + + return CreateFile2((const wchar_t *)name, access, share_mode, creation_disposition, &extParams); +#else + return CreateFileW((const wchar_t *)name, access, share_mode, security_attributes, creation_disposition, file_attributes, template_file); +#endif +} + + +static void +utf16_make_tempname(char *buf, size_t len, const char *name, zip_uint32_t i) { + _snwprintf((wchar_t *)buf, len, L"%s.%08x", (const wchar_t *)name, i); +} + + +static char * +utf16_strdup(const char *string) { + return (char *)_wcsdup((const wchar_t *)string); +} diff --git a/lib/zip_source_file_win32_utf8.c b/lib/zip_source_file_win32_utf8.c new file mode 100644 index 0000000..4f258bc --- /dev/null +++ b/lib/zip_source_file_win32_utf8.c @@ -0,0 +1,73 @@ +/* + zip_source_file_win32_ansi.c -- source for Windows file opened by UTF-8 name + Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 "zip_source_file_win32.h" + +ZIP_EXTERN zip_source_t * +zip_source_file(zip_t *za, const char *fname, zip_uint64_t start, zip_int64_t len) { + if (za == NULL) { + return NULL; + } + + return zip_source_file_create(fname, start, len, &za->error); +} + + +ZIP_EXTERN zip_source_t * +zip_source_file_create(const char *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { + int size; + wchar_t *wfname; + zip_source_t *source; + + if (fname == NULL || length < -1) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + + /* Convert fname from UTF-8 to Windows-friendly UTF-16. */ + size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, fname, -1, NULL, 0); + if (size == 0) { + zip_error_set(error, ZIP_ER_INVAL, 0); + return NULL; + } + if ((wfname = (wchar_t *)malloc(sizeof(wchar_t) * size)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, fname, -1, wfname, size); + + source = zip_source_win32w_create(wfname, start, length, error); + + free(wfname); + return source; +} diff --git a/lib/zip_source_filep.c b/lib/zip_source_filep.c deleted file mode 100644 index 9fc9a01..0000000 --- a/lib/zip_source_filep.c +++ /dev/null @@ -1,651 +0,0 @@ -/* - zip_source_filep.c -- create data source from FILE * - Copyright (C) 1999-2017 Dieter Baron and Thomas Klausner - - This file is part of libzip, a library to manipulate ZIP archives. - The authors can be contacted 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 -#include -#include -#include - -#include "zipint.h" - -#ifdef HAVE_UNISTD_H -#include -#endif - -#ifdef HAVE_CLONEFILE -#include -#include -#define CAN_CLONE -#endif -#ifdef HAVE_FICLONERANGE -#include -#include -#define CAN_CLONE -#endif - -#ifdef _WIN32 -/* WIN32 needs for _O_BINARY */ -#include -#endif - -/* Windows sys/types.h does not provide these */ -#ifndef S_ISREG -#define S_ISREG(m) (((m)&S_IFMT) == S_IFREG) -#endif -#if defined(S_IXUSR) && defined(S_IRWXG) && defined(S_IRWXO) -#define _SAFE_MASK (S_IXUSR | S_IRWXG | S_IRWXO) -#elif defined(_S_IWRITE) -#define _SAFE_MASK (_S_IWRITE) -#else -#error do not know safe values for umask, please report this -#endif - -#ifdef _MSC_VER -/* MSVC doesn't have mode_t */ -typedef int mode_t; -#endif - -struct read_file { - zip_error_t error; /* last error information */ - zip_int64_t supports; - - /* reading */ - char *fname; /* name of file to read from */ - FILE *f; /* file to read from */ - struct zip_stat st; /* stat information passed in */ - zip_error_t stat_error; /* error returned for stat */ - zip_uint64_t start; /* start offset of data to read */ - zip_uint64_t end; /* end offset of data to read relative to start, 0 for up to EOF */ - zip_uint64_t current; /* current offset relative to start (0 is beginning of part we read) */ - - /* writing */ - char *tmpname; - FILE *fout; -}; - -static zip_int64_t read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd); -static int create_temp_output(struct read_file *ctx); -#ifdef CAN_CLONE -static zip_int64_t create_temp_output_cloning(struct read_file *ctx, zip_uint64_t offset); -#endif -static int _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error); -static int _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error); - - -ZIP_EXTERN zip_source_t * -zip_source_filep(zip_t *za, FILE *file, zip_uint64_t start, zip_int64_t len) { - if (za == NULL) - return NULL; - - return zip_source_filep_create(file, start, len, &za->error); -} - - -ZIP_EXTERN zip_source_t * -zip_source_filep_create(FILE *file, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { - if (file == NULL || length < -1) { - zip_error_set(error, ZIP_ER_INVAL, 0); - return NULL; - } - - return _zip_source_file_or_p(NULL, file, start, length, NULL, error); -} - - -zip_source_t * -_zip_source_file_or_p(const char *fname, FILE *file, zip_uint64_t start, zip_int64_t len, const zip_stat_t *st, zip_error_t *error) { - struct read_file *ctx; - zip_source_t *zs; - struct stat sb; - bool stat_valid; - - if (file == NULL && fname == NULL) { - zip_error_set(error, ZIP_ER_INVAL, 0); - return NULL; - } - - if (len < 0) { - len = 0; - } - - if (start > ZIP_INT64_MAX || start + (zip_uint64_t)len < start) { - zip_error_set(error, ZIP_ER_INVAL, 0); - return NULL; - } - - if ((ctx = (struct read_file *)malloc(sizeof(struct read_file))) == NULL) { - zip_error_set(error, ZIP_ER_MEMORY, 0); - return NULL; - } - - ctx->fname = NULL; - if (fname) { - if ((ctx->fname = strdup(fname)) == NULL) { - zip_error_set(error, ZIP_ER_MEMORY, 0); - free(ctx); - return NULL; - } - } - ctx->f = file; - ctx->start = start; - ctx->end = (zip_uint64_t)len; - if (st) { - memcpy(&ctx->st, st, sizeof(ctx->st)); - ctx->st.name = NULL; - ctx->st.valid &= ~ZIP_STAT_NAME; - } - else { - zip_stat_init(&ctx->st); - } - - if (ctx->end > 0) { - ctx->st.size = ctx->end; - ctx->st.valid |= ZIP_STAT_SIZE; - } - - zip_error_init(&ctx->stat_error); - - ctx->tmpname = NULL; - ctx->fout = NULL; - - zip_error_init(&ctx->error); - - ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1); - - if (ctx->fname) { - stat_valid = stat(ctx->fname, &sb) >= 0; - - if (!stat_valid) { - if (ctx->start == 0 && ctx->end == 0) { - ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE; - } - } - } - else { - stat_valid = fstat(fileno(ctx->f), &sb) >= 0; - } - - if (!stat_valid) { - zip_error_set(&ctx->stat_error, ZIP_ER_READ, errno); - } - else { - if ((ctx->st.valid & ZIP_STAT_MTIME) == 0) { - ctx->st.mtime = sb.st_mtime; - ctx->st.valid |= ZIP_STAT_MTIME; - } - if (S_ISREG(sb.st_mode)) { - ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE; - - if (ctx->start + ctx->end > (zip_uint64_t)sb.st_size) { - zip_error_set(error, ZIP_ER_INVAL, 0); - free(ctx->fname); - free(ctx); - return NULL; - } - - if (ctx->end == 0) { - ctx->st.size = (zip_uint64_t)sb.st_size - ctx->start; - ctx->st.valid |= ZIP_STAT_SIZE; - - if (ctx->fname && start == 0) { - ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE; - } - } - } - } - -#ifdef CAN_CLONE - if (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE)) { - ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE_CLONING); - } -#endif - - if ((zs = zip_source_function_create(read_file, ctx, error)) == NULL) { - free(ctx->fname); - free(ctx); - return NULL; - } - - return zs; -} - - -static int -create_temp_output(struct read_file *ctx) { - char *temp; - int tfd; - mode_t mask; - FILE *tfp; - - if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) { - zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); - return -1; - } - sprintf(temp, "%s.XXXXXX", ctx->fname); - - mask = umask(_SAFE_MASK); - if ((tfd = mkstemp(temp)) == -1) { - zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - umask(mask); - free(temp); - return -1; - } - umask(mask); - - if ((tfp = fdopen(tfd, "r+b")) == NULL) { - zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - close(tfd); - (void)remove(temp); - free(temp); - return -1; - } - -#ifdef _WIN32 - /* - According to Pierre Joye, Windows in some environments per - default creates text files, so force binary mode. - */ - _setmode(_fileno(tfp), _O_BINARY); -#endif - - ctx->fout = tfp; - ctx->tmpname = temp; - - return 0; -} - -#ifdef CAN_CLONE -zip_int64_t static create_temp_output_cloning(struct read_file *ctx, zip_uint64_t offset) { - char *temp; - FILE *tfp; - - if (offset > ZIP_OFF_MAX) { - zip_error_set(&ctx->error, ZIP_ER_SEEK, E2BIG); - return -1; - } - - if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) { - zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); - return -1; - } - sprintf(temp, "%s.XXXXXX", ctx->fname); - -#ifdef HAVE_CLONEFILE -#ifndef __clang_analyzer__ - /* we can't use mkstemp, since clonefile insists on creating the file */ - if (mktemp(temp) == NULL) { - zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - free(temp); - return -1; - } -#endif - - if (clonefile(ctx->fname, temp, 0) < 0) { - zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - free(temp); - return -1; - } - if ((tfp = fopen(temp, "r+b")) == NULL) { - zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - (void)remove(temp); - free(temp); - return -1; - } -#else - { - int fd; - struct file_clone_range range; - struct stat st; - - if (fstat(fileno(ctx->f), &st) < 0) { - zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - return -1; - } - - if ((fd = mkstemp(temp)) < 0) { - zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - free(temp); - return -1; - } - - range.src_fd = fileno(ctx->f); - range.src_offset = 0; - range.src_length = ((offset + st.st_blksize - 1) / st.st_blksize) * st.st_blksize; - if (range.src_length > st.st_size) { - range.src_length = 0; - } - range.dest_offset = 0; - if (ioctl(fd, FICLONERANGE, &range) < 0) { - zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - (void)close(fd); - (void)remove(temp); - free(temp); - return -1; - } - - if ((tfp = fdopen(fd, "r+b")) == NULL) { - zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - (void)close(fd); - (void)remove(temp); - free(temp); - return -1; - } - } -#endif - - if (ftruncate(fileno(tfp), (off_t)offset) < 0) { - (void)fclose(tfp); - (void)remove(temp); - free(temp); - return -1; - } - if (fseeko(tfp, (off_t)offset, SEEK_SET) < 0) { - (void)fclose(tfp); - (void)remove(temp); - free(temp); - zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno); - } - - ctx->fout = tfp; - ctx->tmpname = temp; - - return 0; -} -#endif - - -static zip_int64_t -read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { - struct read_file *ctx; - char *buf; - zip_uint64_t n; - size_t i; - - ctx = (struct read_file *)state; - buf = (char *)data; - - switch (cmd) { - case ZIP_SOURCE_BEGIN_WRITE: - if (ctx->fname == NULL) { - zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); - return -1; - } - return create_temp_output(ctx); - -#ifdef CAN_CLONE - case ZIP_SOURCE_BEGIN_WRITE_CLONING: - if (ctx->fname == NULL) { - zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); - return -1; - } - return create_temp_output_cloning(ctx, len); -#endif - - case ZIP_SOURCE_COMMIT_WRITE: { - mode_t mask; - - if (fclose(ctx->fout) < 0) { - ctx->fout = NULL; - zip_error_set(&ctx->error, ZIP_ER_WRITE, errno); - } - ctx->fout = NULL; - if (rename(ctx->tmpname, ctx->fname) < 0) { - zip_error_set(&ctx->error, ZIP_ER_RENAME, errno); - return -1; - } - mask = umask(022); - umask(mask); - /* not much we can do if chmod fails except make the whole commit fail */ - (void)chmod(ctx->fname, 0666 & ~mask); - free(ctx->tmpname); - ctx->tmpname = NULL; - return 0; - } - - case ZIP_SOURCE_CLOSE: - if (ctx->fname) { - fclose(ctx->f); - ctx->f = NULL; - } - return 0; - - case ZIP_SOURCE_ERROR: - return zip_error_to_data(&ctx->error, data, len); - - case ZIP_SOURCE_FREE: - free(ctx->fname); - free(ctx->tmpname); - if (ctx->f) - fclose(ctx->f); - free(ctx); - return 0; - - case ZIP_SOURCE_OPEN: - if (ctx->fname) { - if ((ctx->f = fopen(ctx->fname, "rb")) == NULL) { - zip_error_set(&ctx->error, ZIP_ER_OPEN, errno); - return -1; - } - } - - if (ctx->start > 0) { - if (_zip_fseek_u(ctx->f, ctx->start, SEEK_SET, &ctx->error) < 0) { - /* TODO: skip by reading */ - return -1; - } - } - ctx->current = 0; - return 0; - - case ZIP_SOURCE_READ: - if (ctx->end > 0) { - n = ctx->end - ctx->current; - if (n > len) { - n = len; - } - } - else { - n = len; - } - - if (n > SIZE_MAX) - n = SIZE_MAX; - - if ((i = fread(buf, 1, (size_t)n, ctx->f)) == 0) { - if (ferror(ctx->f)) { - zip_error_set(&ctx->error, ZIP_ER_READ, errno); - return -1; - } - } - ctx->current += i; - - return (zip_int64_t)i; - - case ZIP_SOURCE_REMOVE: - if (remove(ctx->fname) < 0) { - zip_error_set(&ctx->error, ZIP_ER_REMOVE, errno); - return -1; - } - return 0; - - case ZIP_SOURCE_ROLLBACK_WRITE: - if (ctx->fout) { - fclose(ctx->fout); - ctx->fout = NULL; - } - (void)remove(ctx->tmpname); - free(ctx->tmpname); - ctx->tmpname = NULL; - return 0; - - case ZIP_SOURCE_SEEK: { - zip_int64_t new_current; - int need_seek; - zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error); - - if (args == NULL) - return -1; - - need_seek = 1; - - switch (args->whence) { - case SEEK_SET: - new_current = args->offset; - break; - - case SEEK_END: - if (ctx->end == 0) { - if (_zip_fseek(ctx->f, args->offset, SEEK_END, &ctx->error) < 0) { - return -1; - } - if ((new_current = ftello(ctx->f)) < 0) { - zip_error_set(&ctx->error, ZIP_ER_SEEK, errno); - return -1; - } - new_current -= (zip_int64_t)ctx->start; - need_seek = 0; - } - else { - new_current = (zip_int64_t)ctx->end + args->offset; - } - break; - - case SEEK_CUR: - new_current = (zip_int64_t)ctx->current + args->offset; - break; - - default: - zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); - return -1; - } - - if (new_current < 0 || (ctx->end != 0 && (zip_uint64_t)new_current > ctx->end) || (zip_uint64_t)new_current + ctx->start < ctx->start) { - zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); - return -1; - } - - ctx->current = (zip_uint64_t)new_current; - - if (need_seek) { - if (_zip_fseek_u(ctx->f, ctx->current + ctx->start, SEEK_SET, &ctx->error) < 0) { - return -1; - } - } - return 0; - } - - case ZIP_SOURCE_SEEK_WRITE: { - zip_source_args_seek_t *args; - - args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error); - if (args == NULL) { - return -1; - } - - if (_zip_fseek(ctx->fout, args->offset, args->whence, &ctx->error) < 0) { - return -1; - } - return 0; - } - - case ZIP_SOURCE_STAT: { - if (len < sizeof(ctx->st)) - return -1; - - if (zip_error_code_zip(&ctx->stat_error) != 0) { - zip_error_set(&ctx->error, zip_error_code_zip(&ctx->stat_error), zip_error_code_system(&ctx->stat_error)); - return -1; - } - - memcpy(data, &ctx->st, sizeof(ctx->st)); - return sizeof(ctx->st); - } - - case ZIP_SOURCE_SUPPORTS: - return ctx->supports; - - case ZIP_SOURCE_TELL: - return (zip_int64_t)ctx->current; - - case ZIP_SOURCE_TELL_WRITE: { - off_t ret = ftello(ctx->fout); - - if (ret < 0) { - zip_error_set(&ctx->error, ZIP_ER_TELL, errno); - return -1; - } - return ret; - } - - case ZIP_SOURCE_WRITE: { - size_t ret; - - clearerr(ctx->fout); - ret = fwrite(data, 1, len, ctx->fout); - if (ret != len || ferror(ctx->fout)) { - zip_error_set(&ctx->error, ZIP_ER_WRITE, errno); - return -1; - } - - return (zip_int64_t)ret; - } - - default: - zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); - return -1; - } -} - - -static int -_zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error) { - if (offset > ZIP_INT64_MAX) { - zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW); - return -1; - } - return _zip_fseek(f, (zip_int64_t)offset, whence, error); -} - - -static int -_zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error) { - if (offset > ZIP_FSEEK_MAX || offset < ZIP_FSEEK_MIN) { - zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW); - return -1; - } - if (fseeko(f, (off_t)offset, whence) < 0) { - zip_error_set(error, ZIP_ER_SEEK, errno); - return -1; - } - return 0; -} diff --git a/lib/zip_source_get_file_attributes.c b/lib/zip_source_get_file_attributes.c new file mode 100644 index 0000000..acbede1 --- /dev/null +++ b/lib/zip_source_get_file_attributes.c @@ -0,0 +1,104 @@ +/* + zip_source_get_file_attributes.c -- get attributes for file from source + Copyright (C) 2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 "zipint.h" + +ZIP_EXTERN void +zip_file_attributes_init(zip_file_attributes_t *attributes) { + attributes->valid = 0; + attributes->version = 1; +} + +int +zip_source_get_file_attributes(zip_source_t *src, zip_file_attributes_t *attributes) { + if (src->source_closed) { + return -1; + } + if (attributes == NULL) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); + return -1; + } + + zip_file_attributes_init(attributes); + + if (src->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_GET_FILE_ATTRIBUTES)) { + if (_zip_source_call(src, attributes, sizeof(*attributes), ZIP_SOURCE_GET_FILE_ATTRIBUTES) < 0) { + return -1; + } + } + + if (ZIP_SOURCE_IS_LAYERED(src)) { + zip_file_attributes_t lower_attributes; + + if (zip_source_get_file_attributes(src->src, &lower_attributes) < 0) { + _zip_error_set_from_source(&src->error, src->src); + return -1; + } + + if ((lower_attributes.valid & ZIP_FILE_ATTRIBUTES_HOST_SYSTEM) && (attributes->valid & ZIP_FILE_ATTRIBUTES_HOST_SYSTEM) == 0) { + attributes->host_system = lower_attributes.host_system; + attributes->valid |= ZIP_FILE_ATTRIBUTES_HOST_SYSTEM; + } + if ((lower_attributes.valid & ZIP_FILE_ATTRIBUTES_ASCII) && (attributes->valid & ZIP_FILE_ATTRIBUTES_ASCII) == 0) { + attributes->ascii = lower_attributes.ascii; + attributes->valid |= ZIP_FILE_ATTRIBUTES_ASCII; + } + if ((lower_attributes.valid & ZIP_FILE_ATTRIBUTES_VERSION_NEEDED)) { + if (attributes->valid & ZIP_FILE_ATTRIBUTES_VERSION_NEEDED) { + attributes->version_needed = ZIP_MAX(lower_attributes.version_needed, attributes->version_needed); + } + else { + attributes->version_needed = lower_attributes.version_needed; + attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED; + } + } + if ((lower_attributes.valid & ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES) && (attributes->valid & ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES) == 0) { + attributes->external_file_attributes = lower_attributes.external_file_attributes; + attributes->valid |= ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES; + } + if ((lower_attributes.valid & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS)) { + if (attributes->valid & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS) { + attributes->general_purpose_bit_flags &= ~lower_attributes.general_purpose_bit_mask; + attributes->general_purpose_bit_flags |= lower_attributes.general_purpose_bit_flags & lower_attributes.general_purpose_bit_mask; + attributes->general_purpose_bit_mask |= lower_attributes.general_purpose_bit_mask; + } + else { + attributes->valid |= ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS; + attributes->general_purpose_bit_flags = lower_attributes.general_purpose_bit_flags; + attributes->general_purpose_bit_mask = lower_attributes.general_purpose_bit_mask; + } + } + } + + return 0; +} diff --git a/lib/zip_source_pkware_decode.c b/lib/zip_source_pkware_decode.c new file mode 100644 index 0000000..d5489a5 --- /dev/null +++ b/lib/zip_source_pkware_decode.c @@ -0,0 +1,221 @@ +/* + zip_source_pkware_decode.c -- Traditional PKWARE decryption routines + Copyright (C) 2009-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 +#include + +#include "zipint.h" + +struct trad_pkware { + char *password; + zip_pkware_keys_t keys; + zip_error_t error; +}; + + +static int decrypt_header(zip_source_t *, struct trad_pkware *); +static zip_int64_t pkware_decrypt(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t); +static struct trad_pkware *trad_pkware_new(const char *password, zip_error_t *error); +static void trad_pkware_free(struct trad_pkware *); + + +zip_source_t * +zip_source_pkware_decode(zip_t *za, zip_source_t *src, zip_uint16_t em, int flags, const char *password) { + struct trad_pkware *ctx; + zip_source_t *s2; + + if (password == NULL || src == NULL || em != ZIP_EM_TRAD_PKWARE) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + if (flags & ZIP_CODEC_ENCODE) { + zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); + return NULL; + } + + if ((ctx = trad_pkware_new(password, &za->error)) == NULL) { + return NULL; + } + + if ((s2 = zip_source_layered(za, src, pkware_decrypt, ctx)) == NULL) { + trad_pkware_free(ctx); + return NULL; + } + + return s2; +} + + +static int +decrypt_header(zip_source_t *src, struct trad_pkware *ctx) { + zip_uint8_t header[ZIP_CRYPTO_PKWARE_HEADERLEN]; + struct zip_stat st; + zip_int64_t n; + + if ((n = zip_source_read(src, header, ZIP_CRYPTO_PKWARE_HEADERLEN)) < 0) { + _zip_error_set_from_source(&ctx->error, src); + return -1; + } + + if (n != ZIP_CRYPTO_PKWARE_HEADERLEN) { + zip_error_set(&ctx->error, ZIP_ER_EOF, 0); + return -1; + } + + _zip_pkware_decrypt(&ctx->keys, header, header, ZIP_CRYPTO_PKWARE_HEADERLEN); + + if (zip_source_stat(src, &st)) { + /* stat failed, skip password validation */ + return 0; + } + + /* password verification - two ways: + * mtime - InfoZIP way, to avoid computing complete CRC before encrypting data + * CRC - old PKWare way + */ + + bool ok = false; + + if (st.valid & ZIP_STAT_MTIME) { + unsigned short dostime, dosdate; + _zip_u2d_time(st.mtime, &dostime, &dosdate); + if (header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] == dostime >> 8) { + ok = true; + } + } + + if (st.valid & ZIP_STAT_CRC) { + if (header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] == st.crc >> 24) { + ok = true; + } + } + + if (!ok && ((st.valid & (ZIP_STAT_MTIME | ZIP_STAT_CRC)) != 0)) { + zip_error_set(&ctx->error, ZIP_ER_WRONGPASSWD, 0); + return -1; + } + + return 0; +} + + +static zip_int64_t +pkware_decrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { + struct trad_pkware *ctx; + zip_int64_t n; + + ctx = (struct trad_pkware *)ud; + + switch (cmd) { + case ZIP_SOURCE_OPEN: + _zip_pkware_keys_reset(&ctx->keys); + _zip_pkware_decrypt(&ctx->keys, NULL, (const zip_uint8_t *)ctx->password, strlen(ctx->password)); + if (decrypt_header(src, ctx) < 0) { + return -1; + } + return 0; + + case ZIP_SOURCE_READ: + if ((n = zip_source_read(src, data, len)) < 0) { + _zip_error_set_from_source(&ctx->error, src); + return -1; + } + + _zip_pkware_decrypt(&ctx->keys, (zip_uint8_t *)data, (zip_uint8_t *)data, (zip_uint64_t)n); + return n; + + case ZIP_SOURCE_CLOSE: + return 0; + + case ZIP_SOURCE_STAT: { + zip_stat_t *st; + + st = (zip_stat_t *)data; + + st->encryption_method = ZIP_EM_NONE; + st->valid |= ZIP_STAT_ENCRYPTION_METHOD; + if (st->valid & ZIP_STAT_COMP_SIZE) { + st->comp_size -= ZIP_CRYPTO_PKWARE_HEADERLEN; + } + + return 0; + } + + case ZIP_SOURCE_SUPPORTS: + return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, -1); + + case ZIP_SOURCE_ERROR: + return zip_error_to_data(&ctx->error, data, len); + + case ZIP_SOURCE_FREE: + trad_pkware_free(ctx); + return 0; + + default: + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } +} + + +static struct trad_pkware * +trad_pkware_new(const char *password, zip_error_t *error) { + struct trad_pkware *ctx; + + if ((ctx = (struct trad_pkware *)malloc(sizeof(*ctx))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if ((ctx->password = strdup(password)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + free(ctx); + return NULL; + } + + zip_error_init(&ctx->error); + + return ctx; +} + + +static void +trad_pkware_free(struct trad_pkware *ctx) { + if (ctx == NULL) { + return; + } + + free(ctx->password); + free(ctx); +} diff --git a/lib/zip_source_pkware_encode.c b/lib/zip_source_pkware_encode.c new file mode 100644 index 0000000..1e8f42c --- /dev/null +++ b/lib/zip_source_pkware_encode.c @@ -0,0 +1,249 @@ +/* + zip_source_pkware_encode.c -- Traditional PKWARE encryption routines + Copyright (C) 2009-2020 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted 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 +#include + +#include "zipint.h" + +struct trad_pkware { + char *password; + zip_pkware_keys_t keys; + zip_buffer_t *buffer; + bool eof; + zip_error_t error; +}; + + +static int encrypt_header(zip_source_t *, struct trad_pkware *); +static zip_int64_t pkware_encrypt(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t); +static void trad_pkware_free(struct trad_pkware *); +static struct trad_pkware *trad_pkware_new(const char *password, zip_error_t *error); + + +zip_source_t * +zip_source_pkware_encode(zip_t *za, zip_source_t *src, zip_uint16_t em, int flags, const char *password) { + struct trad_pkware *ctx; + zip_source_t *s2; + + if (password == NULL || src == NULL || em != ZIP_EM_TRAD_PKWARE) { + zip_error_set(&za->error, ZIP_ER_INVAL, 0); + return NULL; + } + if (!(flags & ZIP_CODEC_ENCODE)) { + zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); + return NULL; + } + + if ((ctx = trad_pkware_new(password, &za->error)) == NULL) { + return NULL; + } + + if ((s2 = zip_source_layered(za, src, pkware_encrypt, ctx)) == NULL) { + trad_pkware_free(ctx); + return NULL; + } + + return s2; +} + + +static int +encrypt_header(zip_source_t *src, struct trad_pkware *ctx) { + struct zip_stat st; + unsigned short dostime, dosdate; + zip_uint8_t *header; + + if (zip_source_stat(src, &st) != 0) { + _zip_error_set_from_source(&ctx->error, src); + return -1; + } + + _zip_u2d_time(st.mtime, &dostime, &dosdate); + + if ((ctx->buffer = _zip_buffer_new(NULL, ZIP_CRYPTO_PKWARE_HEADERLEN)) == NULL) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return -1; + } + + header = _zip_buffer_data(ctx->buffer); + + /* generate header from random bytes and mtime + see appnote.iz, XIII. Decryption, Step 2, last paragraph */ + if (!zip_secure_random(header, ZIP_CRYPTO_PKWARE_HEADERLEN - 1)) { + zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0); + _zip_buffer_free(ctx->buffer); + ctx->buffer = NULL; + return -1; + } + header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] = (zip_uint8_t)((dostime >> 8) & 0xff); + + _zip_pkware_encrypt(&ctx->keys, header, header, ZIP_CRYPTO_PKWARE_HEADERLEN); + + return 0; +} + + +static zip_int64_t +pkware_encrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t length, zip_source_cmd_t cmd) { + struct trad_pkware *ctx; + zip_int64_t n; + zip_uint64_t buffer_n; + + ctx = (struct trad_pkware *)ud; + + switch (cmd) { + case ZIP_SOURCE_OPEN: + ctx->eof = false; + + /* initialize keys */ + _zip_pkware_keys_reset(&ctx->keys); + _zip_pkware_encrypt(&ctx->keys, NULL, (const zip_uint8_t *)ctx->password, strlen(ctx->password)); + + if (encrypt_header(src, ctx) < 0) { + return -1; + } + return 0; + + case ZIP_SOURCE_READ: + buffer_n = 0; + + if (ctx->buffer) { + /* write header values to data */ + buffer_n = _zip_buffer_read(ctx->buffer, data, length); + data = (zip_uint8_t *)data + buffer_n; + length -= buffer_n; + + if (_zip_buffer_eof(ctx->buffer)) { + _zip_buffer_free(ctx->buffer); + ctx->buffer = NULL; + } + } + + if (ctx->eof) { + return (zip_int64_t)buffer_n; + } + + if ((n = zip_source_read(src, data, length)) < 0) { + _zip_error_set_from_source(&ctx->error, src); + return -1; + } + + _zip_pkware_encrypt(&ctx->keys, (zip_uint8_t *)data, (zip_uint8_t *)data, (zip_uint64_t)n); + + if ((zip_uint64_t)n < length) { + ctx->eof = true; + } + + return (zip_int64_t)buffer_n + n; + + case ZIP_SOURCE_CLOSE: + _zip_buffer_free(ctx->buffer); + ctx->buffer = NULL; + return 0; + + case ZIP_SOURCE_STAT: { + zip_stat_t *st; + + st = (zip_stat_t *)data; + st->encryption_method = ZIP_EM_TRAD_PKWARE; + st->valid |= ZIP_STAT_ENCRYPTION_METHOD; + if (st->valid & ZIP_STAT_COMP_SIZE) { + st->comp_size += ZIP_CRYPTO_PKWARE_HEADERLEN; + } + + return 0; + } + + case ZIP_SOURCE_GET_FILE_ATTRIBUTES: { + zip_file_attributes_t *attributes = (zip_file_attributes_t *)data; + if (length < sizeof(*attributes)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED; + attributes->version_needed = 20; + + return 0; + } + + case ZIP_SOURCE_SUPPORTS: + return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_GET_FILE_ATTRIBUTES, -1); + + case ZIP_SOURCE_ERROR: + return zip_error_to_data(&ctx->error, data, length); + + case ZIP_SOURCE_FREE: + trad_pkware_free(ctx); + return 0; + + default: + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } +} + + +static struct trad_pkware * +trad_pkware_new(const char *password, zip_error_t *error) { + struct trad_pkware *ctx; + + if ((ctx = (struct trad_pkware *)malloc(sizeof(*ctx))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return NULL; + } + + if ((ctx->password = strdup(password)) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + free(ctx); + return NULL; + } + ctx->buffer = NULL; + zip_error_init(&ctx->error); + + return ctx; +} + + +static void +trad_pkware_free(struct trad_pkware *ctx) { + if (ctx == NULL) { + return; + } + + free(ctx->password); + _zip_buffer_free(ctx->buffer); + zip_error_fini(&ctx->error); + free(ctx); +} diff --git a/lib/zip_source_seek.c b/lib/zip_source_seek.c index aed53b8..4a71bfb 100644 --- a/lib/zip_source_seek.c +++ b/lib/zip_source_seek.c @@ -50,7 +50,12 @@ zip_source_seek(zip_source_t *src, zip_int64_t offset, int whence) { args.offset = offset; args.whence = whence; - return (_zip_source_call(src, &args, sizeof(args), ZIP_SOURCE_SEEK) < 0 ? -1 : 0); + if (_zip_source_call(src, &args, sizeof(args), ZIP_SOURCE_SEEK) < 0) { + return -1; + } + + src->eof = 0; + return 0; } diff --git a/lib/zip_source_supports.c b/lib/zip_source_supports.c index a47f293..1d3ceba 100644 --- a/lib/zip_source_supports.c +++ b/lib/zip_source_supports.c @@ -38,7 +38,7 @@ zip_int64_t -zip_source_supports(zip_source_t *src) { + zip_source_supports(zip_source_t *src) { return src->supports; } diff --git a/lib/zip_source_window.c b/lib/zip_source_window.c index f4701a0..b65dfda 100644 --- a/lib/zip_source_window.c +++ b/lib/zip_source_window.c @@ -48,7 +48,7 @@ struct window { zip_uint64_t offset; /* offset in src for next read */ zip_stat_t stat; - zip_int8_t compression_flags; + zip_file_attributes_t attributes; zip_error_t error; zip_int64_t supports; bool needs_seek; @@ -64,7 +64,7 @@ zip_source_window(zip_t *za, zip_source_t *src, zip_uint64_t start, zip_uint64_t zip_source_t * -_zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_uint64_t length, zip_stat_t *st, zip_int8_t compression_flags, zip_t *source_archive, zip_uint64_t source_index, zip_error_t *error) { +_zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_uint64_t length, zip_stat_t *st, zip_file_attributes_t *attributes, zip_t *source_archive, zip_uint64_t source_index, zip_error_t *error) { struct window *ctx; if (src == NULL || start + length < start || (source_archive == NULL && source_index != 0)) { @@ -80,11 +80,16 @@ _zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_uint64_t lengt ctx->start = start; ctx->end = start + length; zip_stat_init(&ctx->stat); - ctx->compression_flags = compression_flags; + if (attributes != NULL) { + memcpy(&ctx->attributes, attributes, sizeof(ctx->attributes)); + } + else { + zip_file_attributes_init(&ctx->attributes); + } ctx->source_archive = source_archive; ctx->source_index = source_index; zip_error_init(&ctx->error); - ctx->supports = (zip_source_supports(src) & ZIP_SOURCE_SUPPORTS_SEEKABLE) | (zip_source_make_command_bitmap(ZIP_SOURCE_GET_COMPRESSION_FLAGS, ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1)); + ctx->supports = (zip_source_supports(src) & ZIP_SOURCE_SUPPORTS_SEEKABLE) | (zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1)); ctx->needs_seek = (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK)) ? true : false; if (st) { @@ -121,7 +126,6 @@ window_read(zip_source_t *src, void *_ctx, void *data, zip_uint64_t len, zip_sou struct window *ctx; zip_int64_t ret; zip_uint64_t n, i; - char b[8192]; ctx = (struct window *)_ctx; @@ -154,18 +158,30 @@ window_read(zip_source_t *src, void *_ctx, void *data, zip_uint64_t len, zip_sou } if (!ctx->needs_seek) { + DEFINE_BYTE_ARRAY(b, BUFSIZE); + + if (!byte_array_init(b, BUFSIZE)) { + zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); + return -1; + } + for (n = 0; n < ctx->start; n += (zip_uint64_t)ret) { - i = (ctx->start - n > sizeof(b) ? sizeof(b) : ctx->start - n); + i = (ctx->start - n > BUFSIZE ? BUFSIZE : ctx->start - n); if ((ret = zip_source_read(src, b, i)) < 0) { _zip_error_set_from_source(&ctx->error, src); + byte_array_fini(b); return -1; } if (ret == 0) { zip_error_set(&ctx->error, ZIP_ER_EOF, 0); + byte_array_fini(b); return -1; } } + + byte_array_fini(b); } + ctx->offset = ctx->start; return 0; @@ -220,8 +236,14 @@ window_read(zip_source_t *src, void *_ctx, void *data, zip_uint64_t len, zip_sou return 0; } - case ZIP_SOURCE_GET_COMPRESSION_FLAGS: - return ctx->compression_flags; + case ZIP_SOURCE_GET_FILE_ATTRIBUTES: + if (len < sizeof(ctx->attributes)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + + memcpy(data, &ctx->attributes, sizeof(ctx->attributes)); + return sizeof(ctx->attributes); case ZIP_SOURCE_SUPPORTS: return ctx->supports; diff --git a/lib/zip_source_winzip_aes_encode.c b/lib/zip_source_winzip_aes_encode.c index 12ed1b2..853ead4 100644 --- a/lib/zip_source_winzip_aes_encode.c +++ b/lib/zip_source_winzip_aes_encode.c @@ -83,7 +83,7 @@ zip_source_winzip_aes_encode(zip_t *za, zip_source_t *src, zip_uint16_t encrypti static int encrypt_header(zip_source_t *src, struct winzip_aes *ctx) { zip_uint16_t salt_length = SALT_LENGTH(ctx->encryption_method); - if (!zip_random(ctx->data, salt_length)) { + if (!zip_secure_random(ctx->data, salt_length)) { zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0); return -1; } @@ -163,6 +163,7 @@ winzip_aes_encrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t length, /* TODO: return partial read? */ return -1; } + buffer_n += _zip_buffer_read(ctx->buffer, (zip_uint8_t *)data + ret, length - (zip_uint64_t)ret); } return (zip_int64_t)(buffer_n + (zip_uint64_t)ret); @@ -183,8 +184,20 @@ winzip_aes_encrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t length, return 0; } + case ZIP_SOURCE_GET_FILE_ATTRIBUTES: { + zip_file_attributes_t *attributes = (zip_file_attributes_t *)data; + if (length < sizeof(*attributes)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED; + attributes->version_needed = 51; + + return 0; + } + case ZIP_SOURCE_SUPPORTS: - return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, -1); + return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_GET_FILE_ATTRIBUTES, -1); case ZIP_SOURCE_ERROR: return zip_error_to_data(&ctx->error, data, length); diff --git a/lib/zip_source_zip_new.c b/lib/zip_source_zip_new.c index a5cfee3..e27fde7 100644 --- a/lib/zip_source_zip_new.c +++ b/lib/zip_source_zip_new.c @@ -36,15 +36,19 @@ #include "zipint.h" +static void _zip_file_attributes_from_dirent(zip_file_attributes_t *attributes, zip_dirent_t *de); zip_source_t * _zip_source_zip_new(zip_t *za, zip_t *srcza, zip_uint64_t srcidx, zip_flags_t flags, zip_uint64_t start, zip_uint64_t len, const char *password) { zip_source_t *src, *s2; - struct zip_stat st; + zip_stat_t st; + zip_file_attributes_t attributes; + zip_dirent_t *de; bool partial_data, needs_crc, needs_decrypt, needs_decompress; - if (za == NULL) + if (za == NULL) { return NULL; + } if (srcza == NULL || srcidx >= srcza->nentry) { zip_error_set(&za->error, ZIP_ER_INVAL, 0); @@ -61,8 +65,9 @@ _zip_source_zip_new(zip_t *za, zip_t *srcza, zip_uint64_t srcidx, zip_flags_t fl return NULL; } - if (flags & ZIP_FL_ENCRYPTED) + if (flags & ZIP_FL_ENCRYPTED) { flags |= ZIP_FL_COMPRESSED; + } if ((start > 0 || len > 0) && (flags & ZIP_FL_COMPRESSED)) { zip_error_set(&za->error, ZIP_ER_INVAL, 0); @@ -95,8 +100,13 @@ _zip_source_zip_new(zip_t *za, zip_t *srcza, zip_uint64_t srcidx, zip_flags_t fl } } + if ((de = _zip_get_dirent(srcza, srcidx, flags, &za->error)) == NULL) { + return NULL; + } + _zip_file_attributes_from_dirent(&attributes, de); + if (st.comp_size == 0) { - return zip_source_buffer(za, NULL, 0, 0); + return zip_source_buffer_with_attributes(za, NULL, 0, 0, &attributes); } if (partial_data && !needs_decrypt && !needs_decompress) { @@ -108,17 +118,12 @@ _zip_source_zip_new(zip_t *za, zip_t *srcza, zip_uint64_t srcidx, zip_flags_t fl st2.mtime = st.mtime; st2.valid = ZIP_STAT_SIZE | ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD | ZIP_STAT_MTIME; - if ((src = _zip_source_window_new(srcza->src, start, len, &st2, 0, srcza, srcidx, &za->error)) == NULL) { + if ((src = _zip_source_window_new(srcza->src, start, len, &st2, &attributes, srcza, srcidx, &za->error)) == NULL) { return NULL; } } else { - zip_dirent_t *de; - - if ((de = _zip_get_dirent(srcza, srcidx, flags, &za->error)) == NULL) { - return NULL; - } - if ((src = _zip_source_window_new(srcza->src, 0, st.comp_size, &st, (de->bitflags >> 1) & 3, srcza, srcidx, &za->error)) == NULL) { + if ((src = _zip_source_window_new(srcza->src, 0, st.comp_size, &st, &attributes, srcza, srcidx, &za->error)) == NULL) { return NULL; } } @@ -173,3 +178,14 @@ _zip_source_zip_new(zip_t *za, zip_t *srcza, zip_uint64_t srcidx, zip_flags_t fl return src; } + +static void +_zip_file_attributes_from_dirent(zip_file_attributes_t *attributes, zip_dirent_t *de) { + zip_file_attributes_init(attributes); + attributes->valid = ZIP_FILE_ATTRIBUTES_ASCII | ZIP_FILE_ATTRIBUTES_HOST_SYSTEM | ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES | ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS; + attributes->ascii = de->int_attrib & 1; + attributes->host_system = de->version_madeby >> 8; + attributes->external_file_attributes = de->ext_attrib; + attributes->general_purpose_bit_flags = de->bitflags; + attributes->general_purpose_bit_mask = ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK; +} diff --git a/lib/zip_stat_index.c b/lib/zip_stat_index.c index 109c118..1a6ff71 100644 --- a/lib/zip_stat_index.c +++ b/lib/zip_stat_index.c @@ -48,10 +48,17 @@ zip_stat_index(zip_t *za, zip_uint64_t index, zip_flags_t flags, zip_stat_t *st) if ((flags & ZIP_FL_UNCHANGED) == 0 && ZIP_ENTRY_DATA_CHANGED(za->entry + index)) { - if (zip_source_stat(za->entry[index].source, st) < 0) { + zip_entry_t *entry = za->entry + index; + + if (zip_source_stat(entry->source, st) < 0) { zip_error_set(&za->error, ZIP_ER_CHANGED, 0); return -1; } + + if (entry->changes->changed & ZIP_DIRENT_LAST_MOD) { + st->mtime = de->last_mod; + st->valid |= ZIP_STAT_MTIME; + } } else { zip_stat_init(st); diff --git a/lib/zip_string.c b/lib/zip_string.c index 293766c..6533228 100644 --- a/lib/zip_string.c +++ b/lib/zip_string.c @@ -145,7 +145,7 @@ _zip_string_new(const zip_uint8_t *raw, zip_uint16_t length, zip_flags_t flags, return NULL; } - if ((s->raw = (zip_uint8_t *)malloc((size_t)(length + 1))) == NULL) { + if ((s->raw = (zip_uint8_t *)malloc((size_t)length + 1)) == NULL) { free(s); return NULL; } diff --git a/lib/zip_utf-8.c b/lib/zip_utf-8.c index 8f02f88..0776294 100644 --- a/lib/zip_utf-8.c +++ b/lib/zip_utf-8.c @@ -39,7 +39,7 @@ static const zip_uint16_t _cp437_to_unicode[256] = { /* 0x00 - 0x0F */ - 0x2007, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, + 0x0000, 0x263A, 0x263B, 0x2665, 0x2666, 0x2663, 0x2660, 0x2022, 0x25D8, 0x25CB, 0x25D9, 0x2642, 0x2640, 0x266A, 0x266B, 0x263C, /* 0x10 - 0x1F */ 0x25BA, 0x25C4, 0x2195, 0x203C, 0x00B6, 0x00A7, 0x25AC, 0x21A8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221F, 0x2194, 0x25B2, 0x25BC, diff --git a/lib/zipint.h b/lib/zipint.h index 3c60ece..35553cd 100644 --- a/lib/zipint.h +++ b/lib/zipint.h @@ -65,6 +65,9 @@ #define BUFSIZE 8192 #define EFZIP64SIZE 28 #define EF_WINZIP_AES_SIZE 7 +#define MAX_DATA_DESCRIPTOR_LENGTH 24 + +#define ZIP_CRYPTO_PKWARE_HEADERLEN 12 #define ZIP_CM_REPLACED_DEFAULT (-2) #define ZIP_CM_WINZIP_AES 99 /* Winzip AES encrypted */ @@ -91,6 +94,7 @@ /* according to unzip-6.0's zipinfo.c, this corresponds to a directory with rwx permissions for everyone */ #define ZIP_EXT_ATTRIB_DEFAULT_DIR (0040777u << 16) +#define ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK 0x0836 #define ZIP_MAX(a, b) ((a) > (b) ? (a) : (b)) #define ZIP_MIN(a, b) ((a) < (b) ? (a) : (b)) @@ -124,7 +128,9 @@ struct zip_compression_algorithm { void (*deallocate)(void *ctx); /* get compression specific general purpose bitflags */ - int (*compression_flags)(void *ctx); + zip_uint16_t (*general_purpose_bit_flags)(void *ctx); + /* minimum version needed when using this algorithm */ + zip_uint8_t version_needed; /* start processing */ bool (*start)(void *ctx); @@ -146,8 +152,9 @@ extern zip_compression_algorithm_t zip_algorithm_bzip2_compress; extern zip_compression_algorithm_t zip_algorithm_bzip2_decompress; extern zip_compression_algorithm_t zip_algorithm_deflate_compress; extern zip_compression_algorithm_t zip_algorithm_deflate_decompress; +extern zip_compression_algorithm_t zip_algorithm_xz_compress; +extern zip_compression_algorithm_t zip_algorithm_xz_decompress; -bool zip_compression_method_supported(zip_int32_t method, bool compress); /* This API is not final yet, but we need it internally, so it's private for now. */ @@ -163,13 +170,14 @@ zip_source_t *zip_source_crc(zip_t *, zip_source_t *, int); zip_source_t *zip_source_decompress(zip_t *za, zip_source_t *src, zip_int32_t cm); zip_source_t *zip_source_layered(zip_t *, zip_source_t *, zip_source_layered_callback, void *); zip_source_t *zip_source_layered_create(zip_source_t *src, zip_source_layered_callback cb, void *ud, zip_error_t *error); -zip_source_t *zip_source_pkware(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); +zip_source_t *zip_source_pkware_decode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); +zip_source_t *zip_source_pkware_encode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); int zip_source_remove(zip_source_t *); zip_int64_t zip_source_supports(zip_source_t *src); zip_source_t *zip_source_window(zip_t *, zip_source_t *, zip_uint64_t, zip_uint64_t); zip_source_t *zip_source_winzip_aes_decode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); zip_source_t *zip_source_winzip_aes_encode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *); - +zip_source_t *zip_source_buffer_with_attributes(zip_t *za, const void *data, zip_uint64_t len, int freep, zip_file_attributes_t *attributes); /* error source for layered sources */ @@ -367,6 +375,21 @@ struct zip_string { }; +/* byte array */ + +/* For performance, we usually keep 8k byte arrays on the stack. + However, there are (embedded) systems with a stack size of 12k; + for those, use malloc()/free() */ + +#ifdef ZIP_ALLOCATE_BUFFER +#define DEFINE_BYTE_ARRAY(buf, size) zip_uint8_t *buf +#define byte_array_init(buf, size) (((buf) = (zip_uint8_t *)malloc(size)) != NULL) +#define byte_array_fini(buf) (free(buf)) +#else +#define DEFINE_BYTE_ARRAY(buf, size) zip_uint8_t buf[size] +#define byte_array_init(buf, size) (1) +#define byte_array_fini(buf) ((void)0) +#endif /* bounds checked access to memory buffer */ struct zip_buffer { @@ -390,6 +413,11 @@ typedef struct zip_filelist zip_filelist_t; struct _zip_winzip_aes; typedef struct _zip_winzip_aes zip_winzip_aes_t; +struct _zip_pkware_keys { + zip_uint32_t key[3]; +}; +typedef struct _zip_pkware_keys zip_pkware_keys_t; + extern const char *const _zip_err_str[]; extern const int _zip_nerr_str; extern const int _zip_err_type[]; @@ -449,6 +477,7 @@ zip_cdir_t *_zip_cdir_new(zip_uint64_t, zip_error_t *); zip_int64_t _zip_cdir_write(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivors); void _zip_deregister_source(zip_t *za, zip_source_t *src); +void _zip_dirent_apply_attributes(zip_dirent_t *, zip_file_attributes_t *, bool, zip_uint32_t); zip_dirent_t *_zip_dirent_clone(const zip_dirent_t *); void _zip_dirent_free(zip_dirent_t *); void _zip_dirent_finalize(zip_dirent_t *); @@ -506,12 +535,12 @@ zip_t *_zip_open(zip_source_t *, unsigned int, zip_error_t *); void _zip_progress_end(zip_progress_t *progress); void _zip_progress_free(zip_progress_t *progress); -zip_progress_t *_zip_progress_new(zip_t *za, double precision, zip_progress_callback callback, void (*ud_free)(void *), void *ud); -void _zip_progress_start(zip_progress_t *progress); -void _zip_progress_subrange(zip_progress_t *progress, double start, double end); -void _zip_progress_update(zip_progress_t *progress, double value); +int _zip_progress_start(zip_progress_t *progress); +int _zip_progress_subrange(zip_progress_t *progress, double start, double end); +int _zip_progress_update(zip_progress_t *progress, double value); -ZIP_EXTERN bool zip_random(zip_uint8_t *buffer, zip_uint16_t length); +ZIP_EXTERN bool zip_secure_random(zip_uint8_t *buffer, zip_uint16_t length); +zip_uint32_t zip_random_uint32(void); int _zip_read(zip_source_t *src, zip_uint8_t *data, zip_uint64_t length, zip_error_t *error); int _zip_read_at_offset(zip_source_t *src, zip_uint64_t offset, unsigned char *b, size_t length, zip_error_t *error); @@ -522,6 +551,7 @@ int _zip_register_source(zip_t *za, zip_source_t *src); void _zip_set_open_error(int *zep, const zip_error_t *err, int ze); +bool zip_source_accept_empty(zip_source_t *src); zip_int64_t _zip_source_call(zip_source_t *src, void *data, zip_uint64_t length, zip_source_cmd_t command); bool _zip_source_eof(zip_source_t *); zip_source_t *_zip_source_file_or_p(const char *, FILE *, zip_uint64_t, zip_int64_t, const zip_stat_t *, zip_error_t *error); @@ -530,7 +560,7 @@ bool _zip_source_had_error(zip_source_t *); void _zip_source_invalidate(zip_source_t *src); zip_source_t *_zip_source_new(zip_error_t *error); int _zip_source_set_source_archive(zip_source_t *, zip_t *); -zip_source_t *_zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_uint64_t length, zip_stat_t *st, zip_int8_t compression_flags, zip_t *source_archive, zip_uint64_t source_index, zip_error_t *error); +zip_source_t *_zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_uint64_t length, zip_stat_t *st, zip_file_attributes_t *attributes, zip_t *source_archive, zip_uint64_t source_index, zip_error_t *error); zip_source_t *_zip_source_zip_new(zip_t *, zip_t *, zip_uint64_t, zip_flags_t, zip_uint64_t, zip_uint64_t, const char *); int _zip_stat_merge(zip_stat_t *dst, const zip_stat_t *src, zip_error_t *error); @@ -547,6 +577,11 @@ bool _zip_winzip_aes_finish(zip_winzip_aes_t *ctx, zip_uint8_t *hmac); void _zip_winzip_aes_free(zip_winzip_aes_t *ctx); zip_winzip_aes_t *_zip_winzip_aes_new(const zip_uint8_t *password, zip_uint64_t password_length, const zip_uint8_t *salt, zip_uint16_t key_size, zip_uint8_t *password_verify, zip_error_t *error); +void _zip_pkware_encrypt(zip_pkware_keys_t *keys, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len); +void _zip_pkware_decrypt(zip_pkware_keys_t *keys, zip_uint8_t *out, const zip_uint8_t *in, zip_uint64_t len); +zip_pkware_keys_t *_zip_pkware_keys_new(zip_error_t *error); +void _zip_pkware_keys_free(zip_pkware_keys_t *keys); +void _zip_pkware_keys_reset(zip_pkware_keys_t *keys); int _zip_changed(const zip_t *, zip_uint64_t *); const char *_zip_get_name(zip_t *, zip_uint64_t, zip_flags_t, zip_error_t *); int _zip_local_header_read(zip_t *, int); -- 2.28.0