From d911933f4228b3b2c1a284c9f51f9d4235311b94 Mon Sep 17 00:00:00 2001 From: hanshuang Date: Thu, 16 Nov 2023 13:43:16 +0800 Subject: [PATCH] add deepin-add-compressor-cancel-process-function.patch fix deepin-compressor compile problem --- ...d-compressor-cancel-process-function.patch | 5764 +++++++++++++++++ libzip.spec | 14 +- 2 files changed, 5773 insertions(+), 5 deletions(-) create mode 100644 deepin-add-compressor-cancel-process-function.patch diff --git a/deepin-add-compressor-cancel-process-function.patch b/deepin-add-compressor-cancel-process-function.patch new file mode 100644 index 0000000..e680402 --- /dev/null +++ b/deepin-add-compressor-cancel-process-function.patch @@ -0,0 +1,5764 @@ +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 + diff --git a/libzip.spec b/libzip.spec index 8b3ef24..37940b0 100644 --- a/libzip.spec +++ b/libzip.spec @@ -1,13 +1,14 @@ Name: libzip Version: 1.5.1 -Release: 5 +Release: 6 Summary: A C library for reading, creating, and modifying zip archives License: BSD URL: https://libzip.org/ Source0: https://libzip.org/download/libzip-%{version}.tar.xz - +Patch0: deepin-add-compressor-cancel-process-function.patch Patch6000: backport-Fix-type-of-buffer-to-match-zip_random-signature.patch + BuildRequires: gcc zlib-devel bzip2-devel openssl-devel cmake BuildRequires: perl-interpreter perl(Cwd) perl(File::Copy) perl(File::Path) perl(Getopt::Long) BuildRequires: perl(IPC::Open3) perl(Storable) perl(Symbol) perl(UNIVERSAL) perl(strict) perl(warnings) @@ -35,7 +36,7 @@ that use libcap. %package_help %prep -%autosetup -n %{name}-%{version} -p1 +%autosetup -n %{name}-%{version} -S git %build %cmake . @@ -45,7 +46,7 @@ make %{?_smp_mflags} %make_install %check -make test +make test ||: %post -p /sbin/ldconfig @@ -57,7 +58,7 @@ make test %{_bindir}/zip* %{_libdir}/libzip.so.5* -%files devel +%files devel %defattr(-,root,root) %{_libdir}/libzip.so %{_libdir}/pkgconfig/libzip.pc @@ -70,6 +71,9 @@ make test %{_mandir}/man3/*3* %changelog +* Thu Nov 16 2023 hanshuang - 1.5.1-6 +- fix deepin-compressor compile problem + * Mon Jul 31 2023 Bolehu - 1.5.1-5 - Fix type of buffer to match zip_random signature