diff --git a/contrib/zip/.gitignore b/contrib/zip/.gitignore index a7904a1ef..49b2cb2fd 100644 --- a/contrib/zip/.gitignore +++ b/contrib/zip/.gitignore @@ -1,6 +1,7 @@ /build/ /test/build/ /xcodeproj/ +.vscode/ # Object files *.o @@ -54,3 +55,4 @@ zip.dir/ test/test.exe.vcxproj.filters test/test.exe.vcxproj test/test.exe.dir/ + diff --git a/contrib/zip/CMakeLists.txt b/contrib/zip/CMakeLists.txt index b46dbb1db..77916d2e1 100644 --- a/contrib/zip/CMakeLists.txt +++ b/contrib/zip/CMakeLists.txt @@ -1,10 +1,14 @@ -cmake_minimum_required(VERSION 2.8) -project(zip) -enable_language(C) +cmake_minimum_required(VERSION 3.0) + +project(zip + LANGUAGES C + VERSION "0.1.15") set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) +option(CMAKE_DISABLE_TESTING "Disable test creation" OFF) + if (MSVC) - # Use secure functions by defaualt and suppress warnings about "deprecated" functions + # Use secure functions by default and suppress warnings about "deprecated" functions set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1") @@ -12,28 +16,80 @@ elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Werror -pedantic") + if(ENABLE_COVERAGE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") + endif() endif (MSVC) # zip set(SRC src/miniz.h src/zip.h src/zip.c) add_library(${PROJECT_NAME} ${SRC}) -target_include_directories(${PROJECT_NAME} INTERFACE src) +target_include_directories(${PROJECT_NAME} PUBLIC + $ + $ +) # test if (NOT CMAKE_DISABLE_TESTING) enable_testing() add_subdirectory(test) find_package(Sanitizers) - add_sanitizers(${PROJECT_NAME} test.exe) - add_sanitizers(${PROJECT_NAME} test_miniz.exe) + add_sanitizers(${PROJECT_NAME} ${test_out} ${test_miniz_out}) endif() +#### +# Installation (https://github.com/forexample/package-example) { + +set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}") +set(INCLUDE_INSTALL_DIR "include") + +set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") + +# Configuration +set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake") +set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}Config.cmake") +set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") +set(NAMESPACE "${PROJECT_NAME}::") + +# Include module with fuction 'write_basic_package_version_file' +include(CMakePackageConfigHelpers) + +# Note: PROJECT_VERSION is used as a VERSION +write_basic_package_version_file( + "${VERSION_CONFIG}" COMPATIBILITY SameMajorVersion +) + +# Use variables: +# * TARGETS_EXPORT_NAME +# * PROJECT_NAME +configure_package_config_file( + "cmake/Config.cmake.in" + "${PROJECT_CONFIG}" + INSTALL_DESTINATION "${CONFIG_INSTALL_DIR}" +) + +install( + FILES "${PROJECT_CONFIG}" "${VERSION_CONFIG}" + DESTINATION "${CONFIG_INSTALL_DIR}" +) + +install( + EXPORT "${TARGETS_EXPORT_NAME}" + NAMESPACE "${NAMESPACE}" + DESTINATION "${CONFIG_INSTALL_DIR}" +) + +# } + install(TARGETS ${PROJECT_NAME} + EXPORT ${TARGETS_EXPORT_NAME} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib - COMPONENT library) -install(FILES ${PROJECT_SOURCE_DIR}/src/zip.h DESTINATION include) + INCLUDES DESTINATION ${INCLUDE_INSTALL_DIR} +) +install(FILES ${PROJECT_SOURCE_DIR}/src/zip.h DESTINATION ${INCLUDE_INSTALL_DIR}/zip) # uninstall target (https://gitlab.kitware.com/cmake/community/wikis/FAQ#can-i-do-make-uninstall-with-cmake) if(NOT TARGET uninstall) @@ -45,3 +101,12 @@ if(NOT TARGET uninstall) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake) endif() + +find_package(Doxygen) +if(DOXYGEN_FOUND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY) + add_custom_target(doc + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" VERBATIM) +endif() diff --git a/contrib/zip/README.md b/contrib/zip/README.md index d5fb8cd20..14eb9a34c 100644 --- a/contrib/zip/README.md +++ b/contrib/zip/README.md @@ -71,7 +71,7 @@ int arg = 2; zip_extract("foo.zip", "/tmp", on_extract_entry, &arg); ``` -* Extract a zip entry into memory. +* Extract a zip entry into memory. ```c void *buf = NULL; size_t bufsize; @@ -89,7 +89,7 @@ zip_close(zip); free(buf); ``` -* Extract a zip entry into memory (no internal allocation). +* Extract a zip entry into memory (no internal allocation). ```c unsigned char *buf; size_t bufsize; @@ -110,7 +110,7 @@ zip_close(zip); free(buf); ``` -* Extract a zip entry into memory using callback. +* Extract a zip entry into memory using callback. ```c struct buffer_t { char *data; @@ -144,7 +144,7 @@ free(buf.data); ``` -* Extract a zip entry into a file. +* Extract a zip entry into a file. ```c struct zip_t *zip = zip_open("foo.zip", 0, 'r'); { @@ -157,7 +157,7 @@ struct zip_t *zip = zip_open("foo.zip", 0, 'r'); zip_close(zip); ``` -* List of all zip entries +* List of all zip entries ```c struct zip_t *zip = zip_open("foo.zip", 0, 'r'); int i, n = zip_total_entries(zip); @@ -174,7 +174,7 @@ for (i = 0; i < n; ++i) { zip_close(zip); ``` -## Bindings +# Bindings Compile zip library as a dynamic library. ```shell $ mkdir build diff --git a/contrib/zip/appveyor.yml b/contrib/zip/appveyor.yml index 0be6373ca..ea17f5de9 100644 --- a/contrib/zip/appveyor.yml +++ b/contrib/zip/appveyor.yml @@ -1,4 +1,4 @@ -version: zip-0.1.9.{build} +version: zip-0.1.15.{build} build_script: - cmd: >- cd c:\projects\zip diff --git a/contrib/zip/src/miniz.h b/contrib/zip/src/miniz.h index 2c27a94d8..c4fcfb83e 100644 --- a/contrib/zip/src/miniz.h +++ b/contrib/zip/src/miniz.h @@ -221,6 +221,7 @@ #ifndef MINIZ_HEADER_INCLUDED #define MINIZ_HEADER_INCLUDED +#include #include // Defines to completely disable specific portions of miniz.c: @@ -284,7 +285,8 @@ /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ #if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) #if MINIZ_X86_OR_X64_CPU -/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */ +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient + * integer loads and stores from unaligned addresses. */ #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define MINIZ_UNALIGNED_USE_MEMCPY #else @@ -354,6 +356,44 @@ enum { MZ_FIXED = 4 }; +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or + * modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + // Method #define MZ_DEFLATED 8 @@ -696,6 +736,7 @@ typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); struct mz_zip_internal_state_tag; typedef struct mz_zip_internal_state_tag mz_zip_internal_state; @@ -707,13 +748,27 @@ typedef enum { MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 } mz_zip_mode; -typedef struct mz_zip_archive_tag { +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +typedef struct { mz_uint64 m_archive_size; mz_uint64 m_central_directory_file_ofs; - mz_uint m_total_files; - mz_zip_mode m_zip_mode; - mz_uint m_file_offset_alignment; + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; mz_alloc_func m_pAlloc; mz_free_func m_pFree; @@ -722,6 +777,7 @@ typedef struct mz_zip_archive_tag { mz_file_read_func m_pRead; mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; void *m_pIO_opaque; mz_zip_internal_state *m_pState; @@ -1263,6 +1319,9 @@ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); #endif // #ifndef MINIZ_NO_ZLIB_APIS +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + #ifdef __cplusplus } #endif @@ -1311,6 +1370,11 @@ typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) #endif +#define MZ_READ_LE64(p) \ + (((mz_uint64)MZ_READ_LE32(p)) | \ + (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) \ + << 32U)) + #ifdef _MSC_VER #define MZ_FORCEINLINE __forceinline #elif defined(__GNUC__) @@ -4160,6 +4224,17 @@ enum { MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + // Central directory header record offsets MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, @@ -4199,6 +4274,31 @@ enum { MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 }; typedef struct { @@ -4211,7 +4311,24 @@ struct mz_zip_internal_state_tag { mz_zip_array m_central_dir; mz_zip_array m_central_dir_offsets; mz_zip_array m_sorted_central_dir_offsets; + + /* The flags passed in when the archive is initially opened. */ + uint32_t m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. + */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 + * will also be slammed to true too, even if we didn't find a zip64 end of + * central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write + * helpers. */ MZ_FILE *m_pFile; + mz_uint64 m_file_archive_start_ofs; + void *m_pMem; size_t m_mem_size; size_t m_mem_capacity; @@ -4363,6 +4480,13 @@ static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, #endif /* #ifndef MINIZ_NO_STDIO */ #endif /* #ifndef MINIZ_NO_TIME */ +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, + mz_zip_error err_num) { + if (pZip) + pZip->m_last_error = err_num; + return MZ_FALSE; +} + static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) { (void)flags; @@ -4480,127 +4604,346 @@ mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { } } -static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, - mz_uint32 flags) { - mz_uint cdir_size, num_this_disk, cdir_disk_index; - mz_uint64 cdir_ofs; +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, + mz_uint32 record_sig, + mz_uint32 record_size, + mz_int64 *pOfs) { mz_int64 cur_file_ofs; - const mz_uint8 *p; mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; - mz_bool sort_central_dir = - ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); - // Basic sanity checks - reject files which are too small, and check the first - // 4 bytes of the file to make sure a local header is there. - if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) return MZ_FALSE; - // Find the end of central directory record by scanning the file from the end - // towards the beginning. + + /* Find the record by scanning the file from the end towards the beginning. */ cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); for (;;) { int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) return MZ_FALSE; - for (i = n - 4; i >= 0; --i) - if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) - break; + + for (i = n - 4; i >= 0; --i) { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) + break; + } + } + if (i >= 0) { cur_file_ofs += i; break; } + + /* Give up if we've searched the entire file, or we've gone back "too far" + * (~64kb) */ if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= - (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) + (MZ_UINT16_MAX + record_size))) return MZ_FALSE; + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); } - // Read and verify the end of central directory record. + + *pOfs = cur_file_ofs; + return MZ_TRUE; +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, + mz_uint flags) { + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, + cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8 *p; + + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = + ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32 + [(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + + mz_uint32 zip64_end_of_central_dir_header_u32 + [(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8 *pZip64_end_of_central_dir = + (mz_uint8 *)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first + * 4 bytes of the file to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig( + pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != - MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || - ((pZip->m_total_files = - MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != - MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS))) - return MZ_FALSE; + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) { + if (pZip->m_pRead(pZip->m_pIO_opaque, + cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, + pZip64_locator, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) { + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) { + zip64_end_of_central_dir_ofs = MZ_READ_LE64( + pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > + (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, + pZip64_end_of_central_dir, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } + } + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = + MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) { + mz_uint32 zip64_total_num_of_disks = + MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64( + pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64( + pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64( + pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = + MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < + (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = + (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough + * for millions of files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = + MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) - return MZ_FALSE; + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); - if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < - pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; + if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); - cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) - return MZ_FALSE; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); pZip->m_central_directory_file_ofs = cdir_ofs; if (pZip->m_total_files) { mz_uint i, n; - - // Read the entire central directory into a heap block, and allocate another - // heap block to hold the unsorted central dir file record offsets, and - // another to hold the sorted indices. + /* Read the entire central directory into a heap block, and allocate another + * heap block to hold the unsorted central dir file record offsets, and + * possibly another to hold the sorted indices. */ if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) - return MZ_FALSE; + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (sort_central_dir) { if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) - return MZ_FALSE; + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) - return MZ_FALSE; + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); - // Now create an index into the central directory file records, do some - // basic sanity checking on each record, and check for zip64 entries (which - // are not yet supported). + /* Now create an index into the central directory file records, do some + * basic sanity checking on each record */ p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { - mz_uint total_header_size, comp_size, decomp_size, disk_index; + mz_uint total_header_size, disk_index, bit_flags, filename_size, + ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) - return MZ_FALSE; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + if (sort_central_dir) MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); - if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && - (decomp_size != comp_size)) || - (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || - (comp_size == 0xFFFFFFFF)) - return MZ_FALSE; + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && + (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == + MZ_UINT32_MAX)) { + /* Attempt to find zip64 extended information field in the entry's extra + * data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) { + const mz_uint8 *pExtra_data; + void *buf = NULL; + + if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > + n) { + buf = MZ_MALLOC(ext_data_size); + if (buf == NULL) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (pZip->m_pRead(pZip->m_pIO_opaque, + cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + filename_size, + buf, ext_data_size) != ext_data_size) { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (mz_uint8 *)buf; + } else { + pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + } + + do { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > + extra_size_remaining) { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { + /* Ok, the archive didn't have any zip64 headers but it uses a + * zip64 extended information field so mark it as zip64 anyway + * (this can occur with infozip's zip util when it reads + * compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = + extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + + MZ_FREE(buf); + } + } + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext + * data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) { + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && + (decomp_size != comp_size)) || + (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); - if ((disk_index != num_this_disk) && (disk_index != 1)) - return MZ_FALSE; - if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + - MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) - return MZ_FALSE; + if ((disk_index == MZ_UINT16_MAX) || + ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) - return MZ_FALSE; + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + n -= total_header_size; p += total_header_size; } diff --git a/contrib/zip/src/zip.c b/contrib/zip/src/zip.c index ff3a8fe1e..1abcfd8fd 100644 --- a/contrib/zip/src/zip.c +++ b/contrib/zip/src/zip.c @@ -24,7 +24,6 @@ ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \ (P)[1] == ':') #define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0) -#define ISSLASH(C) ((C) == '/' || (C) == '\\') #else @@ -48,7 +47,7 @@ int symlink(const char *target, const char *linkpath); // needed on Linux #endif #ifndef ISSLASH -#define ISSLASH(C) ((C) == '/') +#define ISSLASH(C) ((C) == '/' || (C) == '\\') #endif #define CLEANUP(ptr) \ @@ -78,26 +77,34 @@ static const char *base_name(const char *name) { return base; } -static int mkpath(const char *path) { - char const *p; +static int mkpath(char *path) { + char *p; char npath[MAX_PATH + 1]; int len = 0; int has_device = HAS_DEVICE(path); memset(npath, 0, MAX_PATH + 1); - -#ifdef _WIN32 - // only on windows fix the path - npath[0] = path[0]; - npath[1] = path[1]; - len = 2; -#endif // _WIN32 - + if (has_device) { + // only on windows + npath[0] = path[0]; + npath[1] = path[1]; + len = 2; + } for (p = path + len; *p && len < MAX_PATH; p++) { if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) { - if (MKDIR(npath) == -1) - if (errno != EEXIST) +#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ + defined(__MINGW32__) +#else + if ('\\' == *p) { + *p = '/'; + } +#endif + + if (MKDIR(npath) == -1) { + if (errno != EEXIST) { return -1; + } + } } npath[len++] = *p; } @@ -279,7 +286,14 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { zip->entry.header_offset = zip->archive.m_archive_size; memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8)); zip->entry.method = 0; + + // UNIX or APPLE +#if MZ_PLATFORM == 3 || MZ_PLATFORM == 19 + // regular file with rw-r--r-- persmissions + zip->entry.external_attr = (mz_uint32)(0100644) << 16; +#else zip->entry.external_attr = 0; +#endif num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pzip); @@ -660,7 +674,7 @@ ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) { } if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index, - buf, bufsize, 0, NULL, 0)) { + buf, bufsize, 0, NULL, 0)) { return -1; } @@ -670,10 +684,7 @@ ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) { int zip_entry_fread(struct zip_t *zip, const char *filename) { mz_zip_archive *pzip = NULL; mz_uint idx; -#if defined(_MSC_VER) -#else mz_uint32 xattr = 0; -#endif mz_zip_archive_file_stat info; if (!zip) { @@ -875,12 +886,19 @@ int zip_extract(const char *zipname, const char *dir, goto out; } - if ((((info.m_version_made_by >> 8) == 3) || ((info.m_version_made_by >> 8) == 19)) // if zip is produced on Unix or macOS (3 and 19 from section 4.4.2.2 of zip standard) - && info.m_external_attr & (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 is directory) + if ((((info.m_version_made_by >> 8) == 3) || + ((info.m_version_made_by >> 8) == + 19)) // if zip is produced on Unix or macOS (3 and 19 from + // section 4.4.2.2 of zip standard) + && info.m_external_attr & + (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 + // is directory) #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ defined(__MINGW32__) -#else - if (info.m_uncomp_size > MAX_PATH || !mz_zip_reader_extract_to_mem_no_alloc(&zip_archive, i, symlink_to, MAX_PATH, 0, NULL, 0)) { +#else + if (info.m_uncomp_size > MAX_PATH || + !mz_zip_reader_extract_to_mem_no_alloc(&zip_archive, i, symlink_to, + MAX_PATH, 0, NULL, 0)) { goto out; } symlink_to[info.m_uncomp_size] = '\0'; diff --git a/contrib/zip/src/zip.h b/contrib/zip/src/zip.h index 5f39df50a..a48d64d6d 100644 --- a/contrib/zip/src/zip.h +++ b/contrib/zip/src/zip.h @@ -20,241 +20,240 @@ extern "C" { #endif #if !defined(_SSIZE_T_DEFINED) && !defined(_SSIZE_T_DEFINED_) && \ - !defined(_SSIZE_T) && !defined(_SSIZE_T_) && !defined(__ssize_t_defined) -#define _SSIZE_T + !defined(__DEFINED_ssize_t) && !defined(__ssize_t_defined) && \ + !defined(_SSIZE_T) && !defined(_SSIZE_T_) + // 64-bit Windows is the only mainstream platform // where sizeof(long) != sizeof(void*) #ifdef _WIN64 -typedef long long ssize_t; /* byte count or error */ +typedef long long ssize_t; /* byte count or error */ #else -typedef long ssize_t; /* byte count or error */ +typedef long ssize_t; /* byte count or error */ #endif + +#define _SSIZE_T_DEFINED +#define _SSIZE_T_DEFINED_ +#define __DEFINED_ssize_t +#define __ssize_t_defined +#define _SSIZE_T +#define _SSIZE_T_ + #endif #ifndef MAX_PATH #define MAX_PATH 32767 /* # chars in a path name including NULL */ #endif +/** + * @mainpage + * + * Documenation for @ref zip. + */ + +/** + * @addtogroup zip + * @{ + */ + +/** + * Default zip compression level. + */ + #define ZIP_DEFAULT_COMPRESSION_LEVEL 6 -/* - This data structure is used throughout the library to represent zip archive - - forward declaration. -*/ +/** + * @struct zip_t + * + * This data structure is used throughout the library to represent zip archive - + * forward declaration. + */ struct zip_t; -/* - Opens zip archive with compression level using the given mode. - - Args: - zipname: zip archive file name. - level: compression level (0-9 are the standard zlib-style levels). - mode: file access mode. - 'r': opens a file for reading/extracting (the file must exists). - 'w': creates an empty file for writing. - 'a': appends to an existing archive. - - Returns: - The zip archive handler or NULL on error -*/ +/** + * Opens zip archive with compression level using the given mode. + * + * @param zipname zip archive file name. + * @param level compression level (0-9 are the standard zlib-style levels). + * @param mode file access mode. + * - 'r': opens a file for reading/extracting (the file must exists). + * - 'w': creates an empty file for writing. + * - 'a': appends to an existing archive. + * + * @return the zip archive handler or NULL on error + */ extern struct zip_t *zip_open(const char *zipname, int level, char mode); -/* - Closes the zip archive, releases resources - always finalize. - - Args: - zip: zip archive handler. -*/ +/** + * Closes the zip archive, releases resources - always finalize. + * + * @param zip zip archive handler. + */ extern void zip_close(struct zip_t *zip); -/* - Opens an entry by name in the zip archive. - For zip archive opened in 'w' or 'a' mode the function will append - a new entry. In readonly mode the function tries to locate the entry - in global dictionary. - - Args: - zip: zip archive handler. - entryname: an entry name in local dictionary. - - Returns: - The return code - 0 on success, negative number (< 0) on error. -*/ +/** + * Opens an entry by name in the zip archive. + * + * For zip archive opened in 'w' or 'a' mode the function will append + * a new entry. In readonly mode the function tries to locate the entry + * in global dictionary. + * + * @param zip zip archive handler. + * @param entryname an entry name in local dictionary. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ extern int zip_entry_open(struct zip_t *zip, const char *entryname); -/* - Opens a new entry by index in the zip archive. - This function is only valid if zip archive was opened in 'r' (readonly) mode. - - Args: - zip: zip archive handler. - index: index in local dictionary. - - Returns: - The return code - 0 on success, negative number (< 0) on error. -*/ +/** + * Opens a new entry by index in the zip archive. + * + * This function is only valid if zip archive was opened in 'r' (readonly) mode. + * + * @param zip zip archive handler. + * @param index index in local dictionary. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ extern int zip_entry_openbyindex(struct zip_t *zip, int index); -/* - Closes a zip entry, flushes buffer and releases resources. - - Args: - zip: zip archive handler. - - Returns: - The return code - 0 on success, negative number (< 0) on error. -*/ +/** + * Closes a zip entry, flushes buffer and releases resources. + * + * @param zip zip archive handler. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ extern int zip_entry_close(struct zip_t *zip); -/* - Returns a local name of the current zip entry. - The main difference between user's entry name and local entry name - is optional relative path. - Following .ZIP File Format Specification - the path stored MUST not contain - a drive or device letter, or a leading slash. - All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' - for compatibility with Amiga and UNIX file systems etc. - - Args: - zip: zip archive handler. - - Returns: - The pointer to the current zip entry name, or NULL on error. -*/ +/** + * Returns a local name of the current zip entry. + * + * The main difference between user's entry name and local entry name + * is optional relative path. + * Following .ZIP File Format Specification - the path stored MUST not contain + * a drive or device letter, or a leading slash. + * All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' + * for compatibility with Amiga and UNIX file systems etc. + * + * @param zip: zip archive handler. + * + * @return the pointer to the current zip entry name, or NULL on error. + */ extern const char *zip_entry_name(struct zip_t *zip); -/* - Returns an index of the current zip entry. - - Args: - zip: zip archive handler. - - Returns: - The index on success, negative number (< 0) on error. -*/ +/** + * Returns an index of the current zip entry. + * + * @param zip zip archive handler. + * + * @return the index on success, negative number (< 0) on error. + */ extern int zip_entry_index(struct zip_t *zip); -/* - Determines if the current zip entry is a directory entry. - - Args: - zip: zip archive handler. - - Returns: - The return code - 1 (true), 0 (false), negative number (< 0) on error. -*/ +/** + * Determines if the current zip entry is a directory entry. + * + * @param zip zip archive handler. + * + * @return the return code - 1 (true), 0 (false), negative number (< 0) on + * error. + */ extern int zip_entry_isdir(struct zip_t *zip); -/* - Returns an uncompressed size of the current zip entry. - - Args: - zip: zip archive handler. - - Returns: - The uncompressed size in bytes. -*/ +/** + * Returns an uncompressed size of the current zip entry. + * + * @param zip zip archive handler. + * + * @return the uncompressed size in bytes. + */ extern unsigned long long zip_entry_size(struct zip_t *zip); -/* - Returns CRC-32 checksum of the current zip entry. - - Args: - zip: zip archive handler. - - Returns: - The CRC-32 checksum. -*/ +/** + * Returns CRC-32 checksum of the current zip entry. + * + * @param zip zip archive handler. + * + * @return the CRC-32 checksum. + */ extern unsigned int zip_entry_crc32(struct zip_t *zip); -/* - Compresses an input buffer for the current zip entry. - - Args: - zip: zip archive handler. - buf: input buffer. - bufsize: input buffer size (in bytes). - - Returns: - The return code - 0 on success, negative number (< 0) on error. -*/ +/** + * Compresses an input buffer for the current zip entry. + * + * @param zip zip archive handler. + * @param buf input buffer. + * @param bufsize input buffer size (in bytes). + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ extern int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize); -/* - Compresses a file for the current zip entry. - - Args: - zip: zip archive handler. - filename: input file. - - Returns: - The return code - 0 on success, negative number (< 0) on error. -*/ +/** + * Compresses a file for the current zip entry. + * + * @param zip zip archive handler. + * @param filename input file. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ extern int zip_entry_fwrite(struct zip_t *zip, const char *filename); -/* - Extracts the current zip entry into output buffer. - The function allocates sufficient memory for a output buffer. - - Args: - zip: zip archive handler. - buf: output buffer. - bufsize: output buffer size (in bytes). - - Note: - - remember to release memory allocated for a output buffer. - - for large entries, please take a look at zip_entry_extract function. - - Returns: - The return code - the number of bytes actually read on success. - Otherwise a -1 on error. -*/ +/** + * Extracts the current zip entry into output buffer. + * + * The function allocates sufficient memory for a output buffer. + * + * @param zip zip archive handler. + * @param buf output buffer. + * @param bufsize output buffer size (in bytes). + * + * @note remember to release memory allocated for a output buffer. + * for large entries, please take a look at zip_entry_extract function. + * + * @return the return code - the number of bytes actually read on success. + * Otherwise a -1 on error. + */ extern ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize); -/* - Extracts the current zip entry into a memory buffer using no memory - allocation. +/** + * Extracts the current zip entry into a memory buffer using no memory + * allocation. + * + * @param zip zip archive handler. + * @param buf preallocated output buffer. + * @param bufsize output buffer size (in bytes). + * + * @note ensure supplied output buffer is large enough. + * zip_entry_size function (returns uncompressed size for the current + * entry) can be handy to estimate how big buffer is needed. for large + * entries, please take a look at zip_entry_extract function. + * + * @return the return code - the number of bytes actually read on success. + * Otherwise a -1 on error (e.g. bufsize is not large enough). + */ +extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, + size_t bufsize); - Args: - zip: zip archive handler. - buf: preallocated output buffer. - bufsize: output buffer size (in bytes). - - Note: - - ensure supplied output buffer is large enough. - - zip_entry_size function (returns uncompressed size for the current entry) - can be handy to estimate how big buffer is needed. - - for large entries, please take a look at zip_entry_extract function. - - Returns: - The return code - the number of bytes actually read on success. - Otherwise a -1 on error (e.g. bufsize is not large enough). -*/ -extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize); - -/* - Extracts the current zip entry into output file. - - Args: - zip: zip archive handler. - filename: output file. - - Returns: - The return code - 0 on success, negative number (< 0) on error. -*/ +/** + * Extracts the current zip entry into output file. + * + * @param zip zip archive handler. + * @param filename output file. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ extern int zip_entry_fread(struct zip_t *zip, const char *filename); -/* - Extracts the current zip entry using a callback function (on_extract). - - Args: - zip: zip archive handler. - on_extract: callback function. - arg: opaque pointer (optional argument, - which you can pass to the on_extract callback) - - Returns: - The return code - 0 on success, negative number (< 0) on error. +/** + * Extracts the current zip entry using a callback function (on_extract). + * + * @param zip zip archive handler. + * @param on_extract callback function. + * @param arg opaque pointer (optional argument, which you can pass to the + * on_extract callback) + * + * @return the return code - 0 on success, negative number (< 0) on error. */ extern int zip_entry_extract(struct zip_t *zip, @@ -262,53 +261,49 @@ zip_entry_extract(struct zip_t *zip, const void *data, size_t size), void *arg); -/* - Returns the number of all entries (files and directories) in the zip archive. - - Args: - zip: zip archive handler. - - Returns: - The return code - the number of entries on success, - negative number (< 0) on error. -*/ +/** + * Returns the number of all entries (files and directories) in the zip archive. + * + * @param zip zip archive handler. + * + * @return the return code - the number of entries on success, negative number + * (< 0) on error. + */ extern int zip_total_entries(struct zip_t *zip); -/* - Creates a new archive and puts files into a single zip archive. - - Args: - zipname: zip archive file. - filenames: input files. - len: number of input files. - - Returns: - The return code - 0 on success, negative number (< 0) on error. -*/ +/** + * Creates a new archive and puts files into a single zip archive. + * + * @param zipname zip archive file. + * @param filenames input files. + * @param len: number of input files. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ extern int zip_create(const char *zipname, const char *filenames[], size_t len); -/* - Extracts a zip archive file into directory. - - If on_extract_entry is not NULL, the callback will be called after - successfully extracted each zip entry. - Returning a negative value from the callback will cause abort and return an - error. The last argument (void *arg) is optional, which you can use to pass - data to the on_extract_entry callback. - - Args: - zipname: zip archive file. - dir: output directory. - on_extract_entry: on extract callback. - arg: opaque pointer. - - Returns: - The return code - 0 on success, negative number (< 0) on error. -*/ +/** + * Extracts a zip archive file into directory. + * + * If on_extract_entry is not NULL, the callback will be called after + * successfully extracted each zip entry. + * Returning a negative value from the callback will cause abort and return an + * error. The last argument (void *arg) is optional, which you can use to pass + * data to the on_extract_entry callback. + * + * @param zipname zip archive file. + * @param dir output directory. + * @param on_extract_entry on extract callback. + * @param arg opaque pointer. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ extern int zip_extract(const char *zipname, const char *dir, int (*on_extract_entry)(const char *filename, void *arg), void *arg); +/** @} */ + #ifdef __cplusplus } #endif diff --git a/contrib/zip/test/CMakeLists.txt b/contrib/zip/test/CMakeLists.txt index 9b2a8db10..cc060b00f 100644 --- a/contrib/zip/test/CMakeLists.txt +++ b/contrib/zip/test/CMakeLists.txt @@ -1,19 +1,16 @@ cmake_minimum_required(VERSION 2.8) -if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") - if(ENABLE_COVERAGE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g ") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") - endif() -endif () - # test -include_directories(../src) -add_executable(test.exe test.c ../src/zip.c) -add_executable(test_miniz.exe test_miniz.c) +set(test_out test.out) +set(test_miniz_out test_miniz.out) -add_test(NAME test COMMAND test.exe) -add_test(NAME test_miniz COMMAND test_miniz.exe) +add_executable(${test_out} test.c) +target_link_libraries(${test_out} zip) +add_executable(${test_miniz_out} test_miniz.c) +target_link_libraries(${test_miniz_out} zip) + +add_test(NAME ${test_out} COMMAND ${test_out}) +add_test(NAME ${test_miniz_out} COMMAND ${test_miniz_out}) + +set(test_out ${test_out} PARENT_SCOPE) +set(test_miniz_out ${test_miniz_out} PARENT_SCOPE) diff --git a/contrib/zip/test/test.c b/contrib/zip/test/test.c index 454430533..a9b2ddab1 100644 --- a/contrib/zip/test/test.c +++ b/contrib/zip/test/test.c @@ -29,6 +29,8 @@ #define XFILE "7.txt\0" #define XMODE 0100777 +#define UNIXMODE 0100644 + #define UNUSED(x) (void)x static int total_entries = 0; @@ -102,7 +104,8 @@ static void test_read(void) { assert(0 == zip_entry_close(zip)); free(buf); buf = NULL; - + bufsize = 0; + assert(0 == zip_entry_open(zip, "test/test-2.txt")); assert(strlen(TESTDATA2) == zip_entry_size(zip)); assert(CRC32DATA2 == zip_entry_crc32(zip)); @@ -131,7 +134,8 @@ static void test_read(void) { assert(0 == zip_entry_close(zip)); free(buf); buf = NULL; - + bufsize = 0; + buftmp = strlen(TESTDATA1); buf = calloc(buftmp, sizeof(char)); assert(0 == zip_entry_open(zip, "test/test-1.txt")); @@ -433,6 +437,35 @@ static void test_mtime(void) { remove(ZIPNAME); } +static void test_unix_permissions(void) { +#if defined(_WIN64) || defined(_WIN32) || defined(__WIN32__) +#else + // UNIX or APPLE + struct MZ_FILE_STAT_STRUCT file_stats; + + remove(ZIPNAME); + + struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w'); + assert(zip != NULL); + + assert(0 == zip_entry_open(zip, RFILE)); + assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1))); + assert(0 == zip_entry_close(zip)); + + zip_close(zip); + + remove(RFILE); + + assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL)); + + assert(0 == MZ_FILE_STAT(RFILE, &file_stats)); + assert(UNIXMODE == file_stats.st_mode); + + remove(RFILE); + remove(ZIPNAME); +#endif +} + int main(int argc, char *argv[]) { UNUSED(argc); UNUSED(argv); @@ -453,6 +486,7 @@ int main(int argc, char *argv[]) { test_write_permissions(); test_exe_permissions(); test_mtime(); + test_unix_permissions(); remove(ZIPNAME); return 0; diff --git a/contrib/zip/test/test_miniz.c b/contrib/zip/test/test_miniz.c index ebc0564dc..babcaecdb 100644 --- a/contrib/zip/test/test_miniz.c +++ b/contrib/zip/test/test_miniz.c @@ -23,16 +23,39 @@ int main(int argc, char *argv[]) { uint step = 0; int cmp_status; uLong src_len = (uLong)strlen(s_pStr); - uLong cmp_len = compressBound(src_len); uLong uncomp_len = src_len; + uLong cmp_len; uint8 *pCmp, *pUncomp; + size_t sz; uint total_succeeded = 0; (void)argc, (void)argv; printf("miniz.c version: %s\n", MZ_VERSION); do { + pCmp = (uint8 *)tdefl_compress_mem_to_heap(s_pStr, src_len, &cmp_len, 0); + if (!pCmp) { + printf("tdefl_compress_mem_to_heap failed\n"); + return EXIT_FAILURE; + } + if (src_len <= cmp_len) { + printf("tdefl_compress_mem_to_heap failed: from %u to %u bytes\n", + (mz_uint32)uncomp_len, (mz_uint32)cmp_len); + free(pCmp); + return EXIT_FAILURE; + } + + sz = tdefl_compress_mem_to_mem(pCmp, cmp_len, s_pStr, src_len, 0); + if (sz != cmp_len) { + printf("tdefl_compress_mem_to_mem failed: expected %u, got %u\n", + (mz_uint32)cmp_len, (mz_uint32)sz); + free(pCmp); + return EXIT_FAILURE; + } + // Allocate buffers to hold compressed and uncompressed data. + free(pCmp); + cmp_len = compressBound(src_len); pCmp = (mz_uint8 *)malloc((size_t)cmp_len); pUncomp = (mz_uint8 *)malloc((size_t)src_len); if ((!pCmp) || (!pUncomp)) {