From 7c578e3ede0e59f2afc9407d3c14d425fc15edf2 Mon Sep 17 00:00:00 2001 From: LemonBoy Date: Thu, 13 Jun 2024 20:22:29 +0200 Subject: [PATCH] Use updated miniz version The new miniz version correctly computes the archive starting offset. --- src/miniz.h | 373 +++++++++++++++----------------------------- src/zip.c | 319 ++++++++++++++++++------------------- src/zip.h | 10 ++ test/CMakeLists.txt | 6 + test/test_offset.c | 96 ++++++++++++ 5 files changed, 388 insertions(+), 416 deletions(-) create mode 100644 test/test_offset.c diff --git a/src/miniz.h b/src/miniz.h index a292a048..70a62f87 100644 --- a/src/miniz.h +++ b/src/miniz.h @@ -1,7 +1,7 @@ #ifndef MINIZ_EXPORT #define MINIZ_EXPORT #endif -/* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP +/* miniz.c 3.0.2 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: @@ -740,7 +740,9 @@ extern "C" { /* Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly * slower, and raw/dynamic blocks will be output more frequently). */ +#ifndef TDEFL_LESS_MEMORY #define TDEFL_LESS_MEMORY 0 +#endif /* tdefl_init() compression flags logically OR'd together (low 12 bits contain * the max. number of probes per dictionary search): */ @@ -1352,7 +1354,6 @@ typedef enum { typedef struct { mz_uint64 m_archive_size; mz_uint64 m_central_directory_file_ofs; - mz_uint64 m_archive_ofs; /* We only support up to UINT32_MAX files in zip64 mode. */ mz_uint32 m_total_files; @@ -1425,11 +1426,6 @@ MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size); -MINIZ_EXPORT mz_bool mz_zip_reader_init_file_v2_rpb(mz_zip_archive *pZip, - const char *pFilename, - mz_uint flags, - mz_uint64 file_start_ofs, - mz_uint64 archive_size); /* Read an archive from an already opened FILE, beginning at the current file * position. */ @@ -1695,8 +1691,6 @@ MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, const char *pFilename, mz_uint flags); -MINIZ_EXPORT mz_bool mz_zip_writer_init_from_reader_v2_noreopen( - mz_zip_archive *pZip, const char *pFilename, mz_uint flags); /* Adds the contents of a memory buffer to an archive. These functions record * the current local time into the archive. */ @@ -1735,9 +1729,9 @@ MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback( mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void *callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, - mz_uint level_and_flags, mz_uint32 ext_attributes, - const char *user_extra_data_local, mz_uint user_extra_data_local_len, - const char *user_extra_data_central, mz_uint user_extra_data_central_len); + mz_uint level_and_flags, const char *user_extra_data_local, + mz_uint user_extra_data_local_len, const char *user_extra_data_central, + mz_uint user_extra_data_central_len); #ifndef MINIZ_NO_STDIO /* Adds the contents of a disk file to an archive. This function also records @@ -1747,15 +1741,14 @@ MINIZ_EXPORT mz_bool mz_zip_writer_add_read_buf_callback( * just set to MZ_DEFAULT_COMPRESSION. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_file( mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, - const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, - mz_uint32 ext_attributes); + const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); /* Like mz_zip_writer_add_file(), except the file data is read from the * specified FILE stream. */ MINIZ_EXPORT mz_bool mz_zip_writer_add_cfile( mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, - mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes, + mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data_local, mz_uint user_extra_data_local_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len); #endif @@ -1833,6 +1826,8 @@ MINIZ_EXPORT void *mz_zip_extract_archive_file_to_heap_v2( #endif #endif /* MINIZ_NO_ARCHIVE_APIS */ + +#ifndef MINIZ_HEADER_FILE_ONLY /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software @@ -2145,21 +2140,17 @@ mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { } int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, - const unsigned char *pSource, mz_ulong pSource_len, - int level) { + const unsigned char *pSource, mz_ulong source_len, int level) { int status; mz_stream stream; memset(&stream, 0, sizeof(stream)); -#if defined(__MINGW32__) || defined(__MINGW64__) || defined(__WATCOMC__) /* In case mz_ulong is 64-bits (argh I hate longs). */ -#else - if ((mz_uint64)(pSource_len | *pDest_len) > 0xFFFFFFFFU) + if ((mz_uint64)(source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; -#endif stream.next_in = pSource; - stream.avail_in = (mz_uint32)pSource_len; + stream.avail_in = (mz_uint32)source_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; @@ -2403,12 +2394,9 @@ int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, int status; memset(&stream, 0, sizeof(stream)); -#if defined(__MINGW32__) || defined(__MINGW64__) || defined(__WATCOMC__) /* In case mz_ulong is 64-bits (argh I hate longs). */ -#else if ((mz_uint64)(*pSource_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; -#endif stream.next_in = pSource; stream.avail_in = (mz_uint32)*pSource_len; @@ -4953,9 +4941,17 @@ extern "C" { #else #include -#if defined(_MSC_VER) || defined(__MINGW64__) +#if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) +#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN +#endif +#ifndef __cplusplus +#define MICROSOFT_WINDOWS_WINBASE_H_DEFINE_INTERLOCKED_CPLUSPLUS_OVERLOADS 0 +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif #include static WCHAR *mz_utf8z_to_widechar(const char *str) { @@ -4969,51 +4965,37 @@ static FILE *mz_fopen(const char *pFilename, const char *pMode) { WCHAR *wFilename = mz_utf8z_to_widechar(pFilename); WCHAR *wMode = mz_utf8z_to_widechar(pMode); FILE *pFile = NULL; -#ifdef ZIP_ENABLE_SHARABLE_FILE_OPEN - pFile = _wfopen(wFilename, wMode); -#else errno_t err = _wfopen_s(&pFile, wFilename, wMode); -#endif free(wFilename); free(wMode); -#ifdef ZIP_ENABLE_SHARABLE_FILE_OPEN - return pFile; -#else return err ? NULL : pFile; -#endif } static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { WCHAR *wPath = mz_utf8z_to_widechar(pPath); WCHAR *wMode = mz_utf8z_to_widechar(pMode); FILE *pFile = NULL; -#ifdef ZIP_ENABLE_SHARABLE_FILE_OPEN - pFile = _wfreopen(wPath, wMode, pStream); -#else errno_t err = _wfreopen_s(&pFile, wPath, wMode, pStream); -#endif free(wPath); free(wMode); -#ifdef ZIP_ENABLE_SHARABLE_FILE_OPEN - return pFile; -#else return err ? NULL : pFile; -#endif } -static int mz_stat64(const char *path, struct __stat64 *buffer) { +#if defined(__MINGW32__) +static int mz_stat(const char *path, struct _stat *buffer) { WCHAR *wPath = mz_utf8z_to_widechar(path); - int res = _wstat64(wPath, buffer); + int res = _wstat(wPath, buffer); free(wPath); return res; } - -static int mz_mkdir(const char *pDirname) { - WCHAR *wDirname = mz_utf8z_to_widechar(pDirname); - int res = _wmkdir(wDirname); - free(wDirname); +#else +static int mz_stat64(const char *path, struct __stat64 *buffer) { + WCHAR *wPath = mz_utf8z_to_widechar(path); + int res = _wstat64(wPath, buffer); + free(wPath); return res; } +#endif #ifndef MINIZ_NO_TIME #include @@ -5024,14 +5006,18 @@ static int mz_mkdir(const char *pDirname) { #define MZ_FWRITE fwrite #define MZ_FTELL64 _ftelli64 #define MZ_FSEEK64 _fseeki64 +#if defined(__MINGW32__) +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT mz_stat +#else #define MZ_FILE_STAT_STRUCT _stat64 #define MZ_FILE_STAT mz_stat64 +#endif #define MZ_FFLUSH fflush #define MZ_FREOPEN mz_freopen #define MZ_DELETE_FILE remove -#define MZ_MKDIR(d) mz_mkdir(d) -#elif defined(__MINGW32__) || defined(__WATCOMC__) +#elif defined(__WATCOMC__) #ifndef MINIZ_NO_TIME #include #endif @@ -5046,7 +5032,6 @@ static int mz_mkdir(const char *pDirname) { #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove -#define MZ_MKDIR(d) _mkdir(d) #elif defined(__TINYC__) #ifndef MINIZ_NO_TIME @@ -5063,11 +5048,6 @@ static int mz_mkdir(const char *pDirname) { #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove -#if defined(_WIN32) || defined(_WIN64) -#define MZ_MKDIR(d) _mkdir(d) -#else -#define MZ_MKDIR(d) mkdir(d, 0755) -#endif #elif defined(__USE_LARGEFILE64) /* gcc, clang */ #ifndef MINIZ_NO_TIME @@ -5084,9 +5064,9 @@ static int mz_mkdir(const char *pDirname) { #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) #define MZ_DELETE_FILE remove -#define MZ_MKDIR(d) mkdir(d, 0755) -#elif defined(__APPLE__) || defined(__FreeBSD__) +#elif defined(__APPLE__) || defined(__FreeBSD__) || \ + (defined(__linux__) && defined(__x86_64__)) #ifndef MINIZ_NO_TIME #include #endif @@ -5101,7 +5081,6 @@ static int mz_mkdir(const char *pDirname) { #define MZ_FFLUSH fflush #define MZ_FREOPEN(p, m, s) freopen(p, m, s) #define MZ_DELETE_FILE remove -#define MZ_MKDIR(d) mkdir(d, 0755) #else #pragma message( \ @@ -5125,17 +5104,9 @@ static int mz_mkdir(const char *pDirname) { #define MZ_FFLUSH fflush #define MZ_FREOPEN(f, m, s) freopen(f, m, s) #define MZ_DELETE_FILE remove -#define MZ_MKDIR(d) mkdir(d, 0755) #endif /* #ifdef _MSC_VER */ #endif /* #ifdef MINIZ_NO_STDIO */ -#ifndef CHMOD -// Upon successful completion, a value of 0 is returned. -// Otherwise, a value of -1 is returned and errno is set to indicate the error. -// int chmod(const char *path, mode_t mode); -#define CHMOD(f, m) chmod(f, m) -#endif - #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) /* Various ZIP archive enums. To completely avoid cross platform compiler @@ -5601,7 +5572,7 @@ static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, /* 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) >= - (MZ_UINT16_MAX + record_size))) + ((mz_uint64)(MZ_UINT16_MAX) + record_size))) return MZ_FALSE; cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); @@ -5611,6 +5582,20 @@ static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, return MZ_TRUE; } +static mz_bool mz_zip_reader_eocd64_valid(mz_zip_archive *pZip, uint64_t offset, + uint8_t *buf) { + if (pZip->m_pRead(pZip->m_pIO_opaque, offset, buf, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) { + if (MZ_READ_LE32(buf + MZ_ZIP64_ECDH_SIG_OFS) == + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) { + return MZ_TRUE; + } + } + + return MZ_FALSE; +} + 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, @@ -5666,25 +5651,38 @@ static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, 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_pState->m_zip64 = MZ_TRUE; } } } + if (pZip->m_pState->m_zip64) { + /* Try locating the EOCD64 right before the EOCD64 locator. This works even + * when the effective start of the zip header is not yet known. */ + if (cur_file_ofs < MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + zip64_end_of_central_dir_ofs = cur_file_ofs - + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE - + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE; + + if (!mz_zip_reader_eocd64_valid(pZip, zip64_end_of_central_dir_ofs, + pZip64_end_of_central_dir)) { + /* That failed, try reading where the locator tells us to. */ + 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 (!mz_zip_reader_eocd64_valid(pZip, zip64_end_of_central_dir_ofs, + pZip64_end_of_central_dir)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + } + } + 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); @@ -5755,7 +5753,7 @@ static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); - if ((cdir_ofs + (mz_uint64)cdir_size) > eocd_ofs) + if (eocd_ofs < cdir_ofs + cdir_size) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); /* The end of central dir follows the central dir, unless the zip file has @@ -5770,8 +5768,15 @@ static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE; } - pZip->m_archive_ofs = archive_ofs; - pZip->m_central_directory_file_ofs = archive_ofs + cdir_ofs; + /* Update the archive start position, but only if not specified. */ + if ((pZip->m_zip_type == MZ_ZIP_TYPE_FILE || + pZip->m_zip_type == MZ_ZIP_TYPE_CFILE) && + pZip->m_pState->m_file_archive_start_ofs == 0) { + pZip->m_pState->m_file_archive_start_ofs = archive_ofs; + pZip->m_archive_size -= archive_ofs; + } + + pZip->m_central_directory_file_ofs = cdir_ofs; if (pZip->m_total_files) { mz_uint i, n; @@ -5791,7 +5796,7 @@ static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); } - if (pZip->m_pRead(pZip->m_pIO_opaque, archive_ofs + cdir_ofs, + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); @@ -5841,8 +5846,7 @@ static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); if (pZip->m_pRead(pZip->m_pIO_opaque, - archive_ofs + cdir_ofs + - MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size, buf, ext_data_size) != ext_data_size) { MZ_FREE(buf); @@ -6120,60 +6124,6 @@ mz_bool mz_zip_reader_init_file_v2(mz_zip_archive *pZip, const char *pFilename, return MZ_TRUE; } -mz_bool mz_zip_reader_init_file_v2_rpb(mz_zip_archive *pZip, - const char *pFilename, mz_uint flags, - mz_uint64 file_start_ofs, - mz_uint64 archive_size) { - mz_uint64 file_size; - MZ_FILE *pFile; - - if ((!pZip) || (!pFilename) || - ((archive_size) && - (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) - return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - - pFile = MZ_FOPEN(pFilename, "r+b"); - if (!pFile) - return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED); - - file_size = archive_size; - if (!file_size) { - if (MZ_FSEEK64(pFile, 0, SEEK_END)) { - MZ_FCLOSE(pFile); - return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED); - } - - file_size = MZ_FTELL64(pFile); - } - - /* TODO: Better sanity check archive_size and the # of actual remaining bytes - */ - - if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) { - MZ_FCLOSE(pFile); - return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); - } - - if (!mz_zip_reader_init_internal(pZip, flags)) { - MZ_FCLOSE(pFile); - return MZ_FALSE; - } - - pZip->m_zip_type = MZ_ZIP_TYPE_FILE; - pZip->m_pRead = mz_zip_file_read_func; - pZip->m_pIO_opaque = pZip; - pZip->m_pState->m_pFile = pFile; - pZip->m_archive_size = file_size; - pZip->m_pState->m_file_archive_start_ofs = file_start_ofs; - - if (!mz_zip_reader_read_central_dir(pZip, flags)) { - mz_zip_reader_end_internal(pZip, MZ_FALSE); - return MZ_FALSE; - } - - return MZ_TRUE; -} - mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags) { mz_uint64 cur_file_ofs; @@ -6646,7 +6596,7 @@ static mz_bool mz_zip_reader_extract_to_mem_no_alloc1( if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); - cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + cur_file_ofs += (mz_uint64)(MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) @@ -6887,7 +6837,7 @@ mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); - cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + cur_file_ofs += (mz_uint64)(MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) @@ -7118,8 +7068,7 @@ mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, pState->out_blk_remain = 0; /* Read and parse the local directory entry. */ - pState->cur_file_ofs = - pZip->m_archive_ofs + pState->file_stat.m_local_header_ofs; + pState->cur_file_ofs = pState->file_stat.m_local_header_ofs; if (pZip->m_pRead(pZip->m_pIO_opaque, pState->cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) { @@ -7135,7 +7084,7 @@ mz_zip_reader_extract_iter_new(mz_zip_archive *pZip, mz_uint file_index, } pState->cur_file_ofs += - MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + (mz_uint64)(MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); if ((pState->cur_file_ofs + pState->file_stat.m_comp_size) > @@ -8203,88 +8152,6 @@ mz_bool mz_zip_writer_init_from_reader_v2(mz_zip_archive *pZip, return MZ_TRUE; } -mz_bool mz_zip_writer_init_from_reader_v2_noreopen(mz_zip_archive *pZip, - const char *pFilename, - mz_uint flags) { - mz_zip_internal_state *pState; - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - - if (flags & MZ_ZIP_FLAG_WRITE_ZIP64) { - /* We don't support converting a non-zip64 file to zip64 - this seems like - * more trouble than it's worth. (What about the existing 32-bit data - * descriptors that could follow the compressed data?) */ - if (!pZip->m_pState->m_zip64) - return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - } - - /* No sense in trying to write to an archive that's already at the support max - * size */ - if (pZip->m_pState->m_zip64) { - if (pZip->m_total_files == MZ_UINT32_MAX) - return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); - } else { - if (pZip->m_total_files == MZ_UINT16_MAX) - return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); - - if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + - MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX) - return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE); - } - - pState = pZip->m_pState; - - if (pState->m_pFile) { -#ifdef MINIZ_NO_STDIO - (void)pFilename; - return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); -#else - if (pZip->m_pIO_opaque != pZip) - return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - - if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE) { - if (!pFilename) - return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - } - - pZip->m_pWrite = mz_zip_file_write_func; - pZip->m_pNeeds_keepalive = NULL; -#endif /* #ifdef MINIZ_NO_STDIO */ - } else if (pState->m_pMem) { - /* Archive lives in a memory block. Assume it's from the heap that we can - * resize using the realloc callback. */ - if (pZip->m_pIO_opaque != pZip) - return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - - pState->m_mem_capacity = pState->m_mem_size; - pZip->m_pWrite = mz_zip_heap_write_func; - pZip->m_pNeeds_keepalive = NULL; - } - /* Archive is being read via a user provided read function - make sure the - user has specified a write function too. */ - else if (!pZip->m_pWrite) - return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER); - - /* Start writing new files at the archive's current central directory - * location. */ - /* TODO: We could add a flag that lets the user start writing immediately - * AFTER the existing central dir - this would be safer. */ - pZip->m_archive_size = pZip->m_central_directory_file_ofs; - pZip->m_central_directory_file_ofs = 0; - - /* Clear the sorted central dir offsets, they aren't useful or maintained now. - */ - /* Even though we're now in write mode, files can still be extracted and - * verified, but file locates will be slow. */ - /* TODO: We could easily maintain the sorted central directory offsets. */ - mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets); - - pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; - - return MZ_TRUE; -} - mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) { return mz_zip_writer_init_from_reader_v2(pZip, pFilename, 0); @@ -8579,6 +8446,8 @@ mz_bool mz_zip_writer_add_mem_ex_v2( time(&cur_time); mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); } +#else + (void)last_modified; #endif /* #ifndef MINIZ_NO_TIME */ if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { @@ -8818,12 +8687,12 @@ mz_bool mz_zip_writer_add_read_buf_callback( mz_zip_archive *pZip, const char *pArchive_name, mz_file_read_func read_callback, void *callback_opaque, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, - mz_uint level_and_flags, mz_uint32 ext_attributes, - const char *user_extra_data, mz_uint user_extra_data_len, - const char *user_extra_data_central, mz_uint user_extra_data_central_len) { + mz_uint level_and_flags, const char *user_extra_data, + mz_uint user_extra_data_len, const char *user_extra_data_central, + mz_uint user_extra_data_central_len) { mz_uint16 gen_flags; mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; - mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; mz_uint64 local_dir_header_ofs, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; size_t archive_name_size; @@ -8907,6 +8776,8 @@ mz_bool mz_zip_writer_add_read_buf_callback( if (pFile_time) { mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date); } +#else + (void)pFile_time; #endif if (max_size <= 3) @@ -9212,20 +9083,20 @@ static size_t mz_file_read_func_stdio(void *pOpaque, mz_uint64 file_ofs, mz_bool mz_zip_writer_add_cfile( mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 max_size, const MZ_TIME_T *pFile_time, const void *pComment, - mz_uint16 comment_size, mz_uint level_and_flags, mz_uint32 ext_attributes, + mz_uint16 comment_size, mz_uint level_and_flags, const char *user_extra_data, mz_uint user_extra_data_len, const char *user_extra_data_central, mz_uint user_extra_data_central_len) { return mz_zip_writer_add_read_buf_callback( pZip, pArchive_name, mz_file_read_func_stdio, pSrc_file, max_size, - pFile_time, pComment, comment_size, level_and_flags, ext_attributes, - user_extra_data, user_extra_data_len, user_extra_data_central, + pFile_time, pComment, comment_size, level_and_flags, user_extra_data, + user_extra_data_len, user_extra_data_central, user_extra_data_central_len); } mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, - mz_uint16 comment_size, mz_uint level_and_flags, - mz_uint32 ext_attributes) { + mz_uint16 comment_size, + mz_uint level_and_flags) { MZ_FILE *pSrc_file = NULL; mz_uint64 uncomp_size = 0; MZ_TIME_T file_modified_time; @@ -9248,9 +9119,9 @@ mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, uncomp_size = MZ_FTELL64(pSrc_file); MZ_FSEEK64(pSrc_file, 0, SEEK_SET); - status = mz_zip_writer_add_cfile( - pZip, pArchive_name, pSrc_file, uncomp_size, pFile_time, pComment, - comment_size, level_and_flags, ext_attributes, NULL, 0, NULL, 0); + status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, + pFile_time, pComment, comment_size, + level_and_flags, NULL, 0, NULL, 0); MZ_FCLOSE(pSrc_file); @@ -9433,9 +9304,9 @@ mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS); local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS); - src_archive_bytes_remaining = local_header_filename_size + - local_header_extra_len + - src_file_stat.m_comp_size; + src_archive_bytes_remaining = src_file_stat.m_comp_size + + local_header_filename_size + + local_header_extra_len; /* Try to find a zip64 extended information field */ if ((local_header_extra_len) && @@ -9780,11 +9651,10 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { central_dir_size = 0; if (pZip->m_total_files) { /* Write central directory */ - central_dir_ofs = pZip->m_archive_size - pZip->m_archive_ofs; + central_dir_ofs = pZip->m_archive_size; central_dir_size = pState->m_central_dir.m_size; pZip->m_central_directory_file_ofs = central_dir_ofs; - if (pZip->m_pWrite(pZip->m_pIO_opaque, - pZip->m_archive_ofs + central_dir_ofs, + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED); @@ -9794,8 +9664,7 @@ mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { if (pState->m_zip64) { /* Write zip64 end of central directory header */ - mz_uint64 rel_ofs_to_zip64_ecdr = - pZip->m_archive_size - pZip->m_archive_ofs; + mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size; MZ_CLEAR_ARR(hdr); MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, @@ -10261,3 +10130,5 @@ mz_bool mz_zip_end(mz_zip_archive *pZip) { #endif #endif /*#ifndef MINIZ_NO_ARCHIVE_APIS*/ + +#endif // MINIZ_HEADER_FILE_ONLY diff --git a/src/zip.c b/src/zip.c index eb7d1106..4bbe304c 100644 --- a/src/zip.c +++ b/src/zip.c @@ -13,19 +13,19 @@ #include #include -#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ +#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ defined(__MINGW32__) /* Win32, DOS, MSVC, MSVS */ #include -#define HAS_DEVICE(P) \ - ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \ +#define HAS_DEVICE(P) \ + ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \ (P)[1] == ':') #define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0) #else -#include // needed for symlink() +#include // needed for symlink() #endif @@ -63,12 +63,12 @@ #define ISSLASH(C) ((C) == '/' || (C) == '\\') #endif -#define CLEANUP(ptr) \ - do { \ - if (ptr) { \ - free((void *)ptr); \ - ptr = NULL; \ - } \ +#define CLEANUP(ptr) \ + do { \ + if (ptr) { \ + free((void *)ptr); \ + ptr = NULL; \ + } \ } while (0) #define UNX_IFDIR 0040000 /* Unix directory */ @@ -172,8 +172,7 @@ static const char *zip_basename(const char *name) { } /* If NAME is all slashes, arrange to return `/'. */ - if (*base == '\0' && ISSLASH(*name) && all_slashes) - --base; + if (*base == '\0' && ISSLASH(*name) && all_slashes) --base; return base; } @@ -193,7 +192,7 @@ static int zip_mkpath(char *path) { } for (p = path + len; *p && len < MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE; p++) { if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) { -#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ +#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ defined(__MINGW32__) #else if ('\\' == *p) { @@ -201,7 +200,7 @@ static int zip_mkpath(char *path) { } #endif - if (MZ_MKDIR(npath) == -1) { + if (mkdir(npath) == -1) { if (errno != EEXIST) { return ZIP_EMKDIR; } @@ -275,7 +274,7 @@ static char *zip_name_normalize(char *name, char *const nname, size_t len) { if (ISSLASH(c)) { if (ncpy > 0 && !zip_strchr_match(&nname[offn], ncpy, '.')) { offn += ncpy; - nname[offn++] = c; // append '/' + nname[offn++] = c; // append '/' } ncpy = 0; } else { @@ -304,7 +303,7 @@ static int zip_archive_truncate(mz_zip_archive *pzip) { if (pzip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) { if (pState->m_pFile) { int fd = fileno(pState->m_pFile); - return ftruncate(fd, file_size); + return ftruncate(fd, pState->m_file_archive_start_ofs + file_size); } } return 0; @@ -379,12 +378,12 @@ static int zip_archive_extract(mz_zip_archive *zip_archive, const char *dir, 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) + 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) || \ + (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 > MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE || @@ -410,7 +409,7 @@ static int zip_archive_extract(mz_zip_archive *zip_archive, const char *dir, } #if defined(_MSC_VER) || defined(PS4) - (void)xattr; // unused + (void)xattr; // unused #else xattr = (info.m_external_attr >> 16) & 0xFFFF; if (xattr > 0 && xattr <= MZ_UINT16_MAX) { @@ -597,7 +596,6 @@ static int zip_index_update(struct zip_entry_mark_t *entry_mark, static int zip_entry_finalize(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, const ssize_t n) { - ssize_t i = 0; mz_uint64 *local_header_ofs_array = (mz_uint64 *)calloc(n, sizeof(mz_uint64)); if (!local_header_ofs_array) { @@ -717,7 +715,7 @@ static ssize_t zip_file_move(MZ_FILE *m_pFile, const mz_uint64 to, static ssize_t zip_files_move(struct zip_t *zip, mz_uint64 writen_num, mz_uint64 read_num, size_t length) { ssize_t n = 0; - const size_t page_size = 1 << 12; // 4K + const size_t page_size = 1 << 12; // 4K mz_zip_internal_state *pState = zip->archive.m_pState; mz_uint8 *move_buf = (mz_uint8 *)calloc(1, page_size); @@ -953,8 +951,7 @@ struct zip_t *zip_openwitherror(const char *zipname, int level, char mode, goto cleanup; } - if (level < 0) - level = MZ_DEFAULT_LEVEL; + if (level < 0) level = MZ_DEFAULT_LEVEL; if ((level & 0xF) > MZ_UBER_COMPRESSION) { // Wrong compression level *errnum = ZIP_EINVLVL; @@ -971,50 +968,56 @@ struct zip_t *zip_openwitherror(const char *zipname, int level, char mode, zip->level = (mz_uint)level; zip->entry.index = -1; switch (mode) { - case 'w': - // Create a new archive. - if (!mz_zip_writer_init_file_v2(&(zip->archive), zipname, 0, - MZ_ZIP_FLAG_WRITE_ZIP64)) { - // Cannot initialize zip_archive writer - *errnum = ZIP_EWINIT; - goto cleanup; - } - break; - - case 'r': - if (!mz_zip_reader_init_file_v2( - &(zip->archive), zipname, - zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { - // An archive file does not exist or cannot initialize - // zip_archive reader - *errnum = ZIP_ERINIT; - goto cleanup; - } - break; - - case 'a': - case 'd': - if (!mz_zip_reader_init_file_v2_rpb( - &(zip->archive), zipname, - zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { - // An archive file does not exist or cannot initialize - // zip_archive reader - *errnum = ZIP_ERINIT; - goto cleanup; - } - if ((mode == 'a' || mode == 'd')) { - if (!mz_zip_writer_init_from_reader_v2_noreopen(&(zip->archive), zipname, - 0)) { + case 'w': + // Create a new archive. + if (!mz_zip_writer_init_file_v2(&(zip->archive), zipname, 0, + MZ_ZIP_FLAG_WRITE_ZIP64)) { + // Cannot initialize zip_archive writer + *errnum = ZIP_EWINIT; + goto cleanup; + } + break; + + case 'r': + if (!mz_zip_reader_init_file_v2( + &(zip->archive), zipname, + zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) { + // An archive file does not exist or cannot initialize + // zip_archive reader + *errnum = ZIP_ERINIT; + goto cleanup; + } + break; + + case 'a': + case 'd': { + MZ_FILE *fp = MZ_FOPEN(zipname, "r+b"); + if (!fp) { + *errnum = ZIP_EOPNFILE; + goto cleanup; + } + if (!mz_zip_reader_init_cfile( + &(zip->archive), fp, 0, + zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { + // An archive file does not exist or cannot initialize zip_archive + // reader + *errnum = ZIP_ERINIT; + fclose(fp); + goto cleanup; + } + if (!mz_zip_writer_init_from_reader_v2(&(zip->archive), zipname, 0)) { *errnum = ZIP_EWRINIT; + fclose(fp); mz_zip_reader_end(&(zip->archive)); goto cleanup; } - } - break; + // The file pointer is now owned by the archive object. + zip->archive.m_zip_type = MZ_ZIP_TYPE_FILE; + } break; - default: - *errnum = ZIP_EINVMODE; - goto cleanup; + default: + *errnum = ZIP_EINVMODE; + goto cleanup; } return zip; @@ -1055,6 +1058,16 @@ int zip_is64(struct zip_t *zip) { return (int)zip->archive.m_pState->m_zip64; } +int zip_get_archive_offset(struct zip_t *zip, uint64_t *offset) { + if (!zip || !zip->archive.m_pState) { + // zip_t handler or zip state is not initialized + return ZIP_ENOINIT; + } + + *offset = mz_zip_get_archive_file_start_offset(&zip->archive); + return 0; +} + static int _zip_entry_open(struct zip_t *zip, const char *entryname, int case_sensitive) { size_t entrylen = 0; @@ -1071,8 +1084,7 @@ static int _zip_entry_open(struct zip_t *zip, const char *entryname, return ZIP_ENOINIT; } - local_dir_header_ofs = - zip->archive.m_archive_size - zip->archive.m_archive_ofs; + local_dir_header_ofs = zip->archive.m_archive_size; if (!entryname) { return ZIP_EINVENTNAME; @@ -1145,8 +1157,8 @@ static int _zip_entry_open(struct zip_t *zip, const char *entryname, zip->entry.comp_size = 0; zip->entry.uncomp_size = 0; zip->entry.uncomp_crc32 = MZ_CRC32_INIT; - zip->entry.dir_offset = zip->archive.m_archive_size - pzip->m_archive_ofs; - zip->entry.header_offset = zip->archive.m_archive_size - pzip->m_archive_ofs; + zip->entry.dir_offset = zip->archive.m_archive_size; + 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 = level ? MZ_DEFLATED : 0; @@ -1172,8 +1184,7 @@ static int _zip_entry_open(struct zip_t *zip, const char *entryname, goto cleanup; } - if (!mz_zip_writer_write_zeros(pzip, - pzip->m_archive_ofs + zip->entry.dir_offset, + if (!mz_zip_writer_write_zeros(pzip, zip->entry.dir_offset, num_alignment_padding_bytes)) { // Cannot memset zip entry header err = ZIP_EMEMSET; @@ -1206,8 +1217,7 @@ static int _zip_entry_open(struct zip_t *zip, const char *entryname, zip->entry.header_offset = zip->entry.dir_offset + num_alignment_padding_bytes; - if (pzip->m_pWrite(pzip->m_pIO_opaque, - pzip->m_archive_ofs + zip->entry.header_offset, + if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset, zip->entry.header, sizeof(zip->entry.header)) != sizeof(zip->entry.header)) { // Cannot write zip entry header @@ -1222,9 +1232,8 @@ static int _zip_entry_open(struct zip_t *zip, const char *entryname, zip->entry.dir_offset += num_alignment_padding_bytes + sizeof(zip->entry.header); - if (pzip->m_pWrite(pzip->m_pIO_opaque, - pzip->m_archive_ofs + zip->entry.dir_offset, - zip->entry.name, entrylen) != entrylen) { + if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, zip->entry.name, + entrylen) != entrylen) { // Cannot write data to zip entry err = ZIP_EWRTENT; goto cleanup; @@ -1232,8 +1241,7 @@ static int _zip_entry_open(struct zip_t *zip, const char *entryname, zip->entry.dir_offset += entrylen; - if (pzip->m_pWrite(pzip->m_pIO_opaque, - pzip->m_archive_ofs + zip->entry.dir_offset, extra_data, + if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, extra_data, extra_size) != extra_size) { // Cannot write ZIP64 data to zip entry err = ZIP_EWRTENT; @@ -1243,8 +1251,7 @@ static int _zip_entry_open(struct zip_t *zip, const char *entryname, if (level) { zip->entry.state.m_pZip = pzip; - zip->entry.state.m_cur_archive_file_ofs = - pzip->m_archive_ofs + zip->entry.dir_offset; + zip->entry.state.m_cur_archive_file_ofs = zip->entry.dir_offset; zip->entry.state.m_comp_size = 0; if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback, @@ -1369,8 +1376,7 @@ int zip_entry_close(struct zip_t *zip) { goto cleanup; } zip->entry.comp_size = zip->entry.state.m_comp_size; - zip->entry.dir_offset = - zip->entry.state.m_cur_archive_file_ofs - pzip->m_archive_ofs; + zip->entry.dir_offset = zip->entry.state.m_cur_archive_file_ofs; zip->entry.method = MZ_DEFLATED; } @@ -1384,9 +1390,9 @@ int zip_entry_close(struct zip_t *zip) { MZ_WRITE_LE64(local_dir_footer + 8, zip->entry.comp_size); MZ_WRITE_LE64(local_dir_footer + 16, zip->entry.uncomp_size); - if (pzip->m_pWrite( - pzip->m_pIO_opaque, pzip->m_archive_ofs + zip->entry.dir_offset, - local_dir_footer, local_dir_footer_size) != local_dir_footer_size) { + if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, + local_dir_footer, + local_dir_footer_size) != local_dir_footer_size) { // Cannot write zip entry header err = ZIP_EWRTHDR; goto cleanup; @@ -1422,7 +1428,7 @@ int zip_entry_close(struct zip_t *zip) { } pzip->m_total_files++; - pzip->m_archive_size = pzip->m_archive_ofs + zip->entry.dir_offset; + pzip->m_archive_size = zip->entry.dir_offset; cleanup: if (zip) { @@ -1508,8 +1514,7 @@ int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { level = zip->level & 0xF; if (!level) { - if ((pzip->m_pWrite(pzip->m_pIO_opaque, - pzip->m_archive_ofs + zip->entry.dir_offset, buf, + if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, buf, bufsize) != bufsize)) { // Cannot write buffer return ZIP_EWRTENT; @@ -1550,25 +1555,18 @@ int zip_entry_fwrite(struct zip_t *zip, const char *filename) { } #if defined(_WIN32) || defined(__WIN32__) || defined(DJGPP) - (void)modes; // unused + (void)modes; // unused #else /* Initialize with permission bits--which are not implementation-optional */ modes = file_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); - if (S_ISDIR(file_stat.st_mode)) - modes |= UNX_IFDIR; - if (S_ISREG(file_stat.st_mode)) - modes |= UNX_IFREG; - if (S_ISLNK(file_stat.st_mode)) - modes |= UNX_IFLNK; - if (S_ISBLK(file_stat.st_mode)) - modes |= UNX_IFBLK; - if (S_ISCHR(file_stat.st_mode)) - modes |= UNX_IFCHR; - if (S_ISFIFO(file_stat.st_mode)) - modes |= UNX_IFIFO; - if (S_ISSOCK(file_stat.st_mode)) - modes |= UNX_IFSOCK; + if (S_ISDIR(file_stat.st_mode)) modes |= UNX_IFDIR; + if (S_ISREG(file_stat.st_mode)) modes |= UNX_IFREG; + if (S_ISLNK(file_stat.st_mode)) modes |= UNX_IFLNK; + if (S_ISBLK(file_stat.st_mode)) modes |= UNX_IFBLK; + if (S_ISCHR(file_stat.st_mode)) modes |= UNX_IFCHR; + if (S_ISFIFO(file_stat.st_mode)) modes |= UNX_IFIFO; + if (S_ISSOCK(file_stat.st_mode)) modes |= UNX_IFSOCK; zip->entry.external_attr = (modes << 16) | !(file_stat.st_mode & S_IWUSR); if ((file_stat.st_mode & S_IFMT) == S_IFDIR) { zip->entry.external_attr |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; @@ -1677,7 +1675,7 @@ int zip_entry_fread(struct zip_t *zip, const char *filename) { } #if defined(_MSC_VER) || defined(PS4) - (void)xattr; // unused + (void)xattr; // unused #else if (!mz_zip_reader_file_stat(pzip, idx, &info)) { // Cannot get information about zip archive; @@ -1911,8 +1909,7 @@ struct zip_t *zip_cstream_openwitherror(FILE *stream, int level, char mode, goto cleanup; } - if (level < 0) - level = MZ_DEFAULT_LEVEL; + if (level < 0) level = MZ_DEFAULT_LEVEL; if ((level & 0xF) > MZ_UBER_COMPRESSION) { // Wrong compression level *errnum = ZIP_EINVLVL; @@ -1928,50 +1925,49 @@ struct zip_t *zip_cstream_openwitherror(FILE *stream, int level, char mode, zip->level = (mz_uint)level; switch (mode) { - case 'w': - // Create a new archive. - if (!mz_zip_writer_init_cfile(&(zip->archive), stream, - MZ_ZIP_FLAG_WRITE_ZIP64)) { - // Cannot initialize zip_archive writer - *errnum = ZIP_EWINIT; - goto cleanup; - } - break; - - case 'r': - if (!mz_zip_reader_init_cfile( - &(zip->archive), stream, 0, - zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { - // An archive file does not exist or cannot initialize - // zip_archive reader - *errnum = ZIP_ERINIT; - goto cleanup; - } - break; - - case 'a': - case 'd': - if (!mz_zip_reader_init_cfile( - &(zip->archive), stream, 0, - zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { - // An archive file does not exist or cannot initialize - // zip_archive reader - *errnum = ZIP_ERINIT; - goto cleanup; - } - if ((mode == 'a' || mode == 'd')) { - if (!mz_zip_writer_init_from_reader_v2_noreopen(&(zip->archive), NULL, - 0)) { - *errnum = ZIP_EWRINIT; - mz_zip_reader_end(&(zip->archive)); + case 'w': + // Create a new archive. + if (!mz_zip_writer_init_cfile(&(zip->archive), stream, + MZ_ZIP_FLAG_WRITE_ZIP64)) { + // Cannot initialize zip_archive writer + *errnum = ZIP_EWINIT; goto cleanup; } - } - break; + break; - default: - *errnum = ZIP_EINVMODE; - goto cleanup; + case 'r': + if (!mz_zip_reader_init_cfile( + &(zip->archive), stream, 0, + zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { + // An archive file does not exist or cannot initialize + // zip_archive reader + *errnum = ZIP_ERINIT; + goto cleanup; + } + break; + + case 'a': + case 'd': + if (!mz_zip_reader_init_cfile( + &(zip->archive), stream, 0, + zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { + // An archive file does not exist or cannot initialize + // zip_archive reader + *errnum = ZIP_ERINIT; + goto cleanup; + } + if ((mode == 'a' || mode == 'd')) { + if (!mz_zip_writer_init_from_reader_v2(&(zip->archive), NULL, 0)) { + *errnum = ZIP_EWRINIT; + mz_zip_reader_end(&(zip->archive)); + goto cleanup; + } + } + break; + + default: + *errnum = ZIP_EINVMODE; + goto cleanup; } return zip; @@ -2025,35 +2021,28 @@ int zip_create(const char *zipname, const char *filenames[], size_t len) { } #if defined(_WIN32) || defined(__WIN32__) || defined(DJGPP) - (void)modes; // unused + (void)modes; // unused #else /* Initialize with permission bits--which are not implementation-optional */ modes = file_stat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); - if (S_ISDIR(file_stat.st_mode)) - modes |= UNX_IFDIR; - if (S_ISREG(file_stat.st_mode)) - modes |= UNX_IFREG; - if (S_ISLNK(file_stat.st_mode)) - modes |= UNX_IFLNK; - if (S_ISBLK(file_stat.st_mode)) - modes |= UNX_IFBLK; - if (S_ISCHR(file_stat.st_mode)) - modes |= UNX_IFCHR; - if (S_ISFIFO(file_stat.st_mode)) - modes |= UNX_IFIFO; - if (S_ISSOCK(file_stat.st_mode)) - modes |= UNX_IFSOCK; + if (S_ISDIR(file_stat.st_mode)) modes |= UNX_IFDIR; + if (S_ISREG(file_stat.st_mode)) modes |= UNX_IFREG; + if (S_ISLNK(file_stat.st_mode)) modes |= UNX_IFLNK; + if (S_ISBLK(file_stat.st_mode)) modes |= UNX_IFBLK; + if (S_ISCHR(file_stat.st_mode)) modes |= UNX_IFCHR; + if (S_ISFIFO(file_stat.st_mode)) modes |= UNX_IFIFO; + if (S_ISSOCK(file_stat.st_mode)) modes |= UNX_IFSOCK; ext_attributes = (modes << 16) | !(file_stat.st_mode & S_IWUSR); if ((file_stat.st_mode & S_IFMT) == S_IFDIR) { ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG; } #endif - if (!mz_zip_writer_add_file(&zip_archive, zip_basename(name), name, "", 0, - ZIP_DEFAULT_COMPRESSION_LEVEL, - ext_attributes)) { + if (!mz_zip_writer_add_file( + &zip_archive, zip_basename(name), name, "", 0, + ZIP_DEFAULT_COMPRESSION_LEVEL | ext_attributes)) { // Cannot add file to zip_archive err = ZIP_ENOFILE; break; diff --git a/src/zip.h b/src/zip.h index 92056d5c..0f870cb3 100644 --- a/src/zip.h +++ b/src/zip.h @@ -162,6 +162,16 @@ extern ZIP_EXPORT void zip_close(struct zip_t *zip); */ extern ZIP_EXPORT int zip_is64(struct zip_t *zip); +/** + * Returns the offset in the stream where the zip header is located. + * + * @param zip zip archive handler. + * @param offset zip header offset. + * + * @return the return code - 0 if successful, negative number (< 0) on error. + */ +extern ZIP_EXPORT int zip_get_archive_offset(struct zip_t *zip, uint64_t *offset); + /** * Opens an entry by name in the zip archive. * diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fc8bb1df..1aef111c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -49,3 +49,9 @@ add_executable(${test_open_out} test_open.c) target_link_libraries(${test_open_out} zip) add_test(NAME ${test_open_out} COMMAND ${test_open_out}) add_sanitizers(${test_open_out}) + +set(test_offset_out test_offset.out) +add_executable(${test_offset_out} test_offset.c) +target_link_libraries(${test_offset_out} zip) +add_test(NAME ${test_offset_out} COMMAND ${test_offset_out}) +add_sanitizers(${test_offset_out}) \ No newline at end of file diff --git a/test/test_offset.c b/test/test_offset.c new file mode 100644 index 00000000..187eefa0 --- /dev/null +++ b/test/test_offset.c @@ -0,0 +1,96 @@ +#include +#include + +#include + +#include "minunit.h" + +#if defined(_WIN32) || defined(_WIN64) +#define MKTEMP _mktemp +#define UNLINK _unlink +#else +#define MKTEMP mkstemp +#define UNLINK unlink +#endif + +static char ZIPNAME[L_tmpnam + 1] = {0}; + +#define HEADERDATA1 "this precedes the zip header" +#define TESTDATA1 "Some test data 1...\0" +#define TESTDATA2 "Some test data 2...\0" +#define CRC32DATA1 2220805626 +#define CRC32DATA2 2532008468 + +void test_setup(void) { + strncpy(ZIPNAME, "z-XXXXXX\0", L_tmpnam); + MKTEMP(ZIPNAME); + + FILE *fp = fopen(ZIPNAME, "wb"); + mu_check(fp != NULL); + fwrite(HEADERDATA1, 1, strlen(HEADERDATA1), fp); + + struct zip_t *zip = zip_cstream_open(fp, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w'); + mu_check(zip != NULL); + + mu_assert_int_eq(0, zip_entry_open(zip, "test/test-1.txt")); + mu_assert_int_eq(0, zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1))); + mu_assert_int_eq(0, zip_entry_close(zip)); + + zip_close(zip); + fclose(fp); + + zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'a'); + mu_check(zip != NULL); + + mu_assert_int_eq(0, zip_entry_open(zip, "test/test-2.txt")); + mu_assert_int_eq(0, zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2))); + mu_assert_int_eq(0, zip_entry_close(zip)); + + zip_close(zip); +} + +void test_teardown(void) { remove(ZIPNAME); } + +MU_TEST(test_read) { + char *buf = NULL; + ssize_t bufsize; + size_t buftmp; + uint64_t archive_offset; + + struct zip_t *zip = zip_open(ZIPNAME, 0, 'r'); + mu_check(zip != NULL); + + zip_get_archive_offset(zip, &archive_offset); + mu_assert_int_eq(strlen(HEADERDATA1), archive_offset); + + mu_assert_int_eq(2, zip_entries_total(zip)); + + mu_assert_int_eq(0, zip_entry_open(zip, "test/test-1.txt")); + mu_assert_int_eq(strlen(TESTDATA1), zip_entry_size(zip)); + mu_check(CRC32DATA1 == zip_entry_crc32(zip)); + bufsize = zip_entry_read(zip, (void **)&buf, &buftmp); + mu_assert_int_eq(strlen(TESTDATA1), bufsize); + mu_assert_int_eq((size_t)bufsize, buftmp); + mu_assert_int_eq(0, strncmp(buf, TESTDATA1, bufsize)); + mu_assert_int_eq(0, zip_entry_close(zip)); + zip_close(zip); + free(buf); + buf = NULL; +} + +MU_TEST_SUITE(test_offset_suite) { + MU_SUITE_CONFIGURE(&test_setup, &test_teardown); + + MU_RUN_TEST(test_read); +} + +#define UNUSED(x) (void)x + +int main(int argc, char *argv[]) { + UNUSED(argc); + UNUSED(argv); + + MU_RUN_SUITE(test_offset_suite); + MU_REPORT(); + return MU_EXIT_CODE; +} \ No newline at end of file