From 107796758b5b3948efecd31253e4d44386195d29 Mon Sep 17 00:00:00 2001 From: SaraP Date: Mon, 25 Aug 2025 11:06:55 +0200 Subject: [PATCH] libzip 1.11.4 : - aggiornamento alla versione 1.11.4. --- compat.h | 77 +++-- config_x32.h | 7 +- config_x64.h | 7 +- libzip.rc | 32 +- libzip.vcxproj | 2 + zip_add.c | 2 +- zip_add_dir.c | 2 +- zip_add_entry.c | 20 +- zip_algorithm_deflate.c | 6 +- zip_buffer.c | 5 +- zip_close.c | 160 ++++++--- zip_crypto.h | 2 +- zip_crypto_commoncrypto.h | 2 +- zip_crypto_gnutls.h | 2 +- zip_crypto_mbedtls.h | 2 +- zip_crypto_openssl.h | 2 +- zip_crypto_win.c | 5 +- zip_crypto_win.h | 2 +- zip_delete.c | 2 +- zip_dir_add.c | 2 +- zip_dirent.c | 355 +++++++++++++------ zip_discard.c | 2 +- zip_entry.c | 2 +- zip_err_str.c | 9 + zip_error.c | 2 +- zip_error_clear.c | 2 +- zip_error_get.c | 2 +- zip_error_get_sys_type.c | 2 +- zip_error_strerror.c | 14 +- zip_error_to_str.c | 2 +- zip_extra_field.c | 2 +- zip_extra_field_api.c | 6 +- zip_fclose.c | 5 +- zip_fdopen.c | 2 +- zip_file_add.c | 2 +- zip_file_error_clear.c | 2 +- zip_file_error_get.c | 2 +- zip_file_get_comment.c | 2 +- zip_file_get_external_attributes.c | 2 +- zip_file_get_offset.c | 2 +- zip_file_rename.c | 2 +- zip_file_replace.c | 8 +- zip_file_set_comment.c | 2 +- zip_file_set_encryption.c | 73 ++-- zip_file_set_external_attributes.c | 2 +- zip_file_set_mtime.c | 40 ++- zip_file_strerror.c | 2 +- zip_fopen.c | 2 +- zip_fopen_encrypted.c | 2 +- zip_fopen_index.c | 2 +- zip_fopen_index_encrypted.c | 2 +- zip_fread.c | 8 +- zip_fseek.c | 10 +- zip_ftell.c | 8 +- zip_get_archive_comment.c | 2 +- zip_get_archive_flag.c | 2 +- zip_get_encryption_implementation.c | 2 +- zip_get_file_comment.c | 2 +- zip_get_name.c | 2 +- zip_get_num_entries.c | 2 +- zip_get_num_files.c | 2 +- zip_hash.c | 2 +- zip_io_util.c | 4 +- zip_libzip_version.c | 2 +- zip_memdup.c | 4 +- zip_new.c | 5 +- zip_open.c | 509 +++++++++++++++------------- zip_pkware.c | 2 +- zip_progress.c | 4 +- zip_random_win32.c | 3 +- zip_realloc.c | 62 ++++ zip_rename.c | 2 +- zip_replace.c | 2 +- zip_set_archive_comment.c | 2 +- zip_set_archive_flag.c | 5 +- zip_set_default_password.c | 2 +- zip_set_file_comment.c | 2 +- zip_set_file_compression.c | 31 +- zip_set_name.c | 2 +- zip_source_accept_empty.c | 2 +- zip_source_begin_write.c | 2 +- zip_source_begin_write_cloning.c | 2 +- zip_source_buffer.c | 28 +- zip_source_call.c | 2 +- zip_source_close.c | 2 +- zip_source_commit_write.c | 2 +- zip_source_compress.c | 41 ++- zip_source_crc.c | 2 +- zip_source_error.c | 2 +- zip_source_file.h | 7 +- zip_source_file_common.c | 24 +- zip_source_file_stdio.c | 17 +- zip_source_file_stdio.h | 2 +- zip_source_file_win32.c | 4 +- zip_source_file_win32.h | 11 +- zip_source_file_win32_ansi.c | 71 +++- zip_source_file_win32_named.c | 29 +- zip_source_file_win32_utf16.c | 67 +++- zip_source_file_win32_utf8.c | 4 +- zip_source_free.c | 2 +- zip_source_get_dostime.c | 72 ++++ zip_source_get_file_attributes.c | 5 +- zip_source_is_deleted.c | 2 +- zip_source_layered.c | 2 +- zip_source_open.c | 2 +- zip_source_pass_to_lower_layer.c | 4 +- zip_source_pkware_decode.c | 43 ++- zip_source_pkware_encode.c | 44 ++- zip_source_read.c | 2 +- zip_source_remove.c | 2 +- zip_source_rollback_write.c | 2 +- zip_source_seek.c | 2 +- zip_source_seek_write.c | 2 +- zip_source_stat.c | 2 +- zip_source_supports.c | 2 +- zip_source_tell.c | 2 +- zip_source_tell_write.c | 2 +- zip_source_window.c | 42 ++- zip_source_winzip_aes_decode.c | 2 +- zip_source_winzip_aes_encode.c | 2 +- zip_source_write.c | 2 +- zip_source_zip.c | 2 +- zip_source_zip_new.c | 26 +- zip_stat.c | 2 +- zip_stat_index.c | 12 +- zip_stat_init.c | 2 +- zip_strerror.c | 2 +- zip_string.c | 22 +- zip_unchange_all.c | 2 +- zip_unchange_archive.c | 2 +- zip_unchange_data.c | 2 +- zip_utf-8.c | 156 ++++++--- zip_winzip_aes.c | 2 +- zipint.h | 73 ++-- 134 files changed, 1564 insertions(+), 886 deletions(-) create mode 100644 zip_realloc.c create mode 100644 zip_source_get_dostime.c diff --git a/compat.h b/compat.h index 384a611..49d3aa3 100644 --- a/compat.h +++ b/compat.h @@ -3,7 +3,7 @@ /* compat.h -- compatibility defines. - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -123,14 +123,64 @@ typedef char bool; #endif #endif -#ifndef HAVE_FSEEKO -#define fseeko(s, o, w) (fseek((s), (long int)(o), (w))) + +#if defined(HAVE__FSEEKI64) && defined(HAVE__FSTAT64) && defined(HAVE__SEEK64) +/* Windows API using int64 */ +typedef zip_int64_t zip_off_t; +typedef struct _stat64 zip_os_stat_t; +#define zip_os_stat _stat64 +#define zip_os_fstat _fstat64 +#define zip_os_seek _fseeki64 +#define ZIP_FSEEK_MAX ZIP_INT64_MAX +#define ZIP_FSEEK_MIN ZIP_INT64_MIN +#else + +/* Normal API */ +#include +typedef struct stat zip_os_stat_t; +#define zip_os_fstat fstat +#define zip_os_stat stat + +#if defined(HAVE_FTELLO) && defined(HAVE_FSEEKO) +/* Using off_t */ +typedef off_t zip_off_t; +#if SIZEOF_OFF_T == 8 +#define ZIP_OFF_MAX ZIP_INT64_MAX +#define ZIP_OFF_MIN ZIP_INT64_MIN +#elif SIZEOF_OFF_T == 4 +#define ZIP_OFF_MAX ZIP_INT32_MAX +#define ZIP_OFF_MIN ZIP_INT32_MIN +#elif SIZEOF_OFF_T == 2 +#define ZIP_OFF_MAX ZIP_INT16_MAX +#define ZIP_OFF_MIN ZIP_INT16_MIN +#else +#error unsupported size of off_t +#endif + +#define ZIP_FSEEK_MAX ZIP_OFF_MAX +#define ZIP_FSEEK_MIN ZIP_OFF_MIN + +#define zip_os_fseek fseeko +#define zip_os_ftell ftello +#else + +/* Using long */ +typedef long zip_off_t; +#include +#define ZIP_FSEEK_MAX LONG_MAX +#define ZIP_FSEEK_MIN LONG_MIN + +#define zip_os_fseek fseek +#define zip_os_ftell ftell +#endif + #endif #ifndef HAVE_FTELLO #define ftello(s) ((long)ftell((s))) #endif + #ifdef HAVE_LOCALTIME_S #ifdef _WIN32 /* Windows is incompatible to the C11 standard, hurray! */ @@ -179,27 +229,6 @@ typedef char bool; #endif #endif -#if SIZEOF_OFF_T == 8 -#define ZIP_OFF_MAX ZIP_INT64_MAX -#define ZIP_OFF_MIN ZIP_INT64_MIN -#elif SIZEOF_OFF_T == 4 -#define ZIP_OFF_MAX ZIP_INT32_MAX -#define ZIP_OFF_MIN ZIP_INT32_MIN -#elif SIZEOF_OFF_T == 2 -#define ZIP_OFF_MAX ZIP_INT16_MAX -#define ZIP_OFF_MIN ZIP_INT16_MIN -#else -#error unsupported size of off_t -#endif - -#if defined(HAVE_FTELLO) && defined(HAVE_FSEEKO) -#define ZIP_FSEEK_MAX ZIP_OFF_MAX -#define ZIP_FSEEK_MIN ZIP_OFF_MIN -#else -#include -#define ZIP_FSEEK_MAX LONG_MAX -#define ZIP_FSEEK_MIN LONG_MIN -#endif #ifndef SIZE_MAX #if SIZEOF_SIZE_T == 8 diff --git a/config_x32.h b/config_x32.h index deadfc3..64bab14 100644 --- a/config_x32.h +++ b/config_x32.h @@ -10,10 +10,13 @@ #define HAVE__DUP #define HAVE__FDOPEN #define HAVE__FILENO +#define HAVE__FSEEKI64 +#define HAVE__FSTAT64 #define HAVE__SETMODE #define HAVE__SNPRINTF #define HAVE__SNPRINTF_S #define HAVE__SNWPRINTF_S +#define HAVE__STAT64 #define HAVE__STRDUP #define HAVE__STRICMP #define HAVE__STRTOI64 @@ -29,6 +32,7 @@ /* #undef HAVE_FSEEKO */ /* #undef HAVE_FTELLO */ /* #undef HAVE_GETPROGNAME */ +/* #undef HAVE_GETSECURITYINFO */ /* #undef HAVE_GNUTLS */ /* #undef HAVE_LIBBZ2 */ /* #undef HAVE_LIBLZMA */ @@ -38,7 +42,6 @@ #define HAVE_MEMCPY_S /* #undef HAVE_MBEDTLS */ /* #undef HAVE_MKSTEMP */ -/* #undef HAVE_NULLABLE */ /* #undef HAVE_OPENSSL */ #define HAVE_SETMODE #define HAVE_SNPRINTF @@ -67,6 +70,6 @@ #define HAVE_SHARED /* END DEFINES */ #define PACKAGE "libzip" -#define VERSION "1.10.0" +#define VERSION "1.11.4" #endif /* HAD_CONFIG_H */ diff --git a/config_x64.h b/config_x64.h index f603147..4368a20 100644 --- a/config_x64.h +++ b/config_x64.h @@ -10,10 +10,13 @@ #define HAVE__DUP #define HAVE__FDOPEN #define HAVE__FILENO +#define HAVE__FSEEKI64 +#define HAVE__FSTAT64 #define HAVE__SETMODE #define HAVE__SNPRINTF #define HAVE__SNPRINTF_S #define HAVE__SNWPRINTF_S +#define HAVE__STAT64 #define HAVE__STRDUP #define HAVE__STRICMP #define HAVE__STRTOI64 @@ -29,6 +32,7 @@ /* #undef HAVE_FSEEKO */ /* #undef HAVE_FTELLO */ /* #undef HAVE_GETPROGNAME */ +#define HAVE_GETSECURITYINFO /* #undef HAVE_GNUTLS */ /* #undef HAVE_LIBBZ2 */ /* #undef HAVE_LIBLZMA */ @@ -38,7 +42,6 @@ #define HAVE_MEMCPY_S /* #undef HAVE_MBEDTLS */ /* #undef HAVE_MKSTEMP */ -/* #undef HAVE_NULLABLE */ /* #undef HAVE_OPENSSL */ #define HAVE_SETMODE #define HAVE_SNPRINTF @@ -67,6 +70,6 @@ #define HAVE_SHARED /* END DEFINES */ #define PACKAGE "libzip" -#define VERSION "1.10.0" +#define VERSION "1.11.4" #endif /* HAD_CONFIG_H */ diff --git a/libzip.rc b/libzip.rc index d9db623..b92c492 100644 --- a/libzip.rc +++ b/libzip.rc @@ -56,8 +56,8 @@ VS_VERSION_INFO$(NDEB64) VERSIONINFO #else VS_VERSION_INFO VERSIONINFO #endif - FILEVERSION 1,10,0,0 - PRODUCTVERSION 1,10,0,0 + FILEVERSION 1,11,4,0 + PRODUCTVERSION 1,11,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -73,12 +73,12 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "Libzip Release Version" - VALUE "FileVersion", "1.10.0.0" + VALUE "FileVersion", "1.11.4.0" VALUE "InternalName", "zipR64.dll" VALUE "LegalCopyright", "Copyright (C) 2021" VALUE "OriginalFilename", "zipR64.dll" VALUE "ProductName", "libzip" - VALUE "ProductVersion", "1.10.0.0" + VALUE "ProductVersion", "1.11.0.0" END END BLOCK "VarFileInfo" @@ -94,8 +94,8 @@ VS_VERSION_INFO$(NDEB32) VERSIONINFO #else VS_VERSION_INFO VERSIONINFO #endif - FILEVERSION 1,10,0,0 - PRODUCTVERSION 1,10,0,0 + FILEVERSION 1,11,4,0 + PRODUCTVERSION 1,11,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -111,11 +111,11 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "Libzip Release Version" - VALUE "FileVersion", "1.10.0.0" + VALUE "FileVersion", "1.11.4.0" VALUE "InternalName", "zipR32.dll" VALUE "OriginalFilename", "zipR32.dll" VALUE "ProductName", "libzip" - VALUE "ProductVersion", "1.10.0.0" + VALUE "ProductVersion", "1.11.0.0" END END BLOCK "VarFileInfo" @@ -131,8 +131,8 @@ VS_VERSION_INFO$(_DEB64) VERSIONINFO #else VS_VERSION_INFO VERSIONINFO #endif - FILEVERSION 1,10,0,0 - PRODUCTVERSION 1,10,0,0 + FILEVERSION 1,11,4,0 + PRODUCTVERSION 1,11,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -148,11 +148,11 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "Libzip Debug Version" - VALUE "FileVersion", "1.10.0.0" + VALUE "FileVersion", "1.11.4.0" VALUE "InternalName", "zipD64.dll" VALUE "OriginalFilename", "zipD64.dll" VALUE "ProductName", "libzip" - VALUE "ProductVersion", "1.10.0.0" + VALUE "ProductVersion", "1.11.0.0" END END BLOCK "VarFileInfo" @@ -168,8 +168,8 @@ VS_VERSION_INFO$(_DEB32) VERSIONINFO #else VS_VERSION_INFO VERSIONINFO #endif - FILEVERSION 1,10,0,0 - PRODUCTVERSION 1,10,0,0 + FILEVERSION 1,11,4,0 + PRODUCTVERSION 1,11,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -185,11 +185,11 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "Libzip Debug Version" - VALUE "FileVersion", "1.10.0.0" + VALUE "FileVersion", "1.11.4.0" VALUE "InternalName", "zipD32.dll" VALUE "OriginalFilename", "zipD32.dll" VALUE "ProductName", "libzip" - VALUE "ProductVersion", "1.10.0.0" + VALUE "ProductVersion", "1.11.0.0" END END BLOCK "VarFileInfo" diff --git a/libzip.vcxproj b/libzip.vcxproj index 38e189b..81608fe 100644 --- a/libzip.vcxproj +++ b/libzip.vcxproj @@ -284,6 +284,7 @@ copy $(TargetPath) \EgtProg\Dll64 + @@ -311,6 +312,7 @@ copy $(TargetPath) \EgtProg\Dll64 + diff --git a/zip_add.c b/zip_add.c index 9770139..a426a57 100644 --- a/zip_add.c +++ b/zip_add.c @@ -1,6 +1,6 @@ /* zip_add.c -- add file via callback function - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_add_dir.c b/zip_add_dir.c index c31fea3..2faaa1a 100644 --- a/zip_add_dir.c +++ b/zip_add_dir.c @@ -1,6 +1,6 @@ /* zip_add_dir.c -- add directory - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_add_entry.c b/zip_add_entry.c index bf12dd5..ccb4bf3 100644 --- a/zip_add_entry.c +++ b/zip_add_entry.c @@ -1,6 +1,6 @@ /* zip_add_entry.c -- create and init struct zip_entry - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -44,10 +44,7 @@ _zip_add_entry(zip_t *za) { zip_uint64_t idx; if (za->nentry + 1 >= za->nentry_alloc) { - zip_entry_t *rentries; - zip_uint64_t nalloc = za->nentry_alloc; - zip_uint64_t additional_entries = 2 * nalloc; - zip_uint64_t realloc_size; + zip_uint64_t additional_entries = 2 * za->nentry_alloc; if (additional_entries < 16) { additional_entries = 16; @@ -55,21 +52,10 @@ _zip_add_entry(zip_t *za) { else if (additional_entries > 1024) { additional_entries = 1024; } - /* neither + nor * overflows can happen: nentry_alloc * sizeof(struct zip_entry) < UINT64_MAX */ - nalloc += additional_entries; - realloc_size = sizeof(struct zip_entry) * (size_t)nalloc; - if (sizeof(struct zip_entry) * (size_t)za->nentry_alloc > realloc_size) { - zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + if (!ZIP_REALLOC(za->entry, za->nentry_alloc, additional_entries, &za->error)) { return -1; } - rentries = (zip_entry_t *)realloc(za->entry, sizeof(struct zip_entry) * (size_t)nalloc); - if (!rentries) { - zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - return -1; - } - za->entry = rentries; - za->nentry_alloc = nalloc; } idx = za->nentry++; diff --git a/zip_algorithm_deflate.c b/zip_algorithm_deflate.c index 3c85e20..402b7e8 100644 --- a/zip_algorithm_deflate.c +++ b/zip_algorithm_deflate.c @@ -1,6 +1,6 @@ /* zip_algorithm_deflate.c -- deflate (de)compression routines - Copyright (C) 2017-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2017-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -196,11 +196,11 @@ input(void *ud, zip_uint8_t *data, zip_uint64_t length) { } -static void -end_of_input(void *ud) { +static bool end_of_input(void *ud) { struct ctx *ctx = (struct ctx *)ud; ctx->end_of_input = true; + return ctx->zstr.avail_in != 0; } diff --git a/zip_buffer.c b/zip_buffer.c index e2103f0..de22bab 100644 --- a/zip_buffer.c +++ b/zip_buffer.c @@ -1,6 +1,6 @@ /* zip_buffer.c -- bounds checked access to memory buffer - Copyright (C) 2014-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2014-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -306,8 +306,7 @@ _zip_buffer_put_8(zip_buffer_t *buffer, zip_uint8_t i) { } -int -_zip_buffer_set_offset(zip_buffer_t *buffer, zip_uint64_t offset) { +int _zip_buffer_set_offset(zip_buffer_t *buffer, zip_uint64_t offset) { if (offset > buffer->size) { buffer->ok = false; return -1; diff --git a/zip_close.c b/zip_close.c index 6b33cbf..08692ef 100644 --- a/zip_close.c +++ b/zip_close.c @@ -1,6 +1,6 @@ /* zip_close.c -- close zip archive and update changes - Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -42,9 +42,9 @@ #endif -static int add_data(zip_t *, zip_source_t *, zip_dirent_t *, zip_uint32_t); +static int add_data(zip_t *, zip_source_t *, zip_dirent_t *); static int copy_data(zip_t *, zip_uint64_t); -static int copy_source(zip_t *, zip_source_t *, zip_int64_t); +static int copy_source(zip_t *, zip_source_t *, zip_source_t *, zip_int64_t); static int torrentzip_compare_names(const void *a, const void *b); static int write_cdir(zip_t *, const zip_filelist_t *, zip_uint64_t); static int write_data_descriptor(zip_t *za, const zip_dirent_t *dirent, int is_zip64); @@ -195,6 +195,12 @@ zip_close(zip_t *za) { break; } } + else if (entry->orig != NULL) { + if (!_zip_dirent_merge(entry->changes, entry->orig, ZIP_ENTRY_DATA_CHANGED(entry), &za->error)) { + error = 1; + break; + } + } de = entry->changes; if (_zip_read_local_ef(za, i) < 0) { @@ -225,7 +231,7 @@ zip_close(zip_t *za) { } /* add_data writes dirent */ - if (add_data(za, zs ? zs : entry->source, de, entry->changes ? entry->changes->changed : 0) < 0) { + if (add_data(za, zs ? zs : entry->source, de) < 0) { error = 1; if (zs) zip_source_free(zs); @@ -295,8 +301,7 @@ zip_close(zip_t *za) { } -static int -add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { +static int add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) { zip_int64_t offstart, offdata, offend, data_length; zip_stat_t st; zip_file_attributes_t attributes; @@ -305,19 +310,25 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { int is_zip64; zip_flags_t flags; bool needs_recompress, needs_decompress, needs_crc, needs_compress, needs_reencrypt, needs_decrypt, needs_encrypt; + bool dirent_changed; + bool have_dos_time = false; + time_t mtime_before_copy; if (zip_source_stat(src, &st) < 0) { zip_error_set_from_source(&za->error, src); return -1; } + de->bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR; + if ((st.valid & ZIP_STAT_COMP_METHOD) == 0) { st.valid |= ZIP_STAT_COMP_METHOD; st.comp_method = ZIP_CM_STORE; } - if (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != ZIP_CM_STORE) + if (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != ZIP_CM_STORE) { de->comp_method = st.comp_method; + } else if (de->comp_method == ZIP_CM_STORE && (st.valid & ZIP_STAT_SIZE)) { st.valid |= ZIP_STAT_COMP_SIZE; st.comp_size = st.size; @@ -372,14 +383,30 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { } } - if ((offstart = zip_source_tell_write(za->src)) < 0) { - zip_error_set_from_source(&za->error, za->src); - return -1; + if ((de->changed & ZIP_DIRENT_LAST_MOD) == 0) { + int ret2 = zip_source_get_dos_time(src, &de->last_mod); + if (ret2 < 0) { + zip_error_set_from_source(&za->error, src); + return -1; + } + if (ret2 == 1) { + have_dos_time = true; + } + else { + if (st.valid & ZIP_STAT_MTIME) { + mtime_before_copy = st.mtime; + } + else { + time(&mtime_before_copy); + } + if (_zip_u2d_time(mtime_before_copy, &de->last_mod, &za->error) < 0) { + return -1; + } + } } - /* as long as we don't support non-seekable output, clear data descriptor bit */ - de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR; - if ((is_zip64 = _zip_dirent_write(za, de, flags)) < 0) { + if ((offstart = zip_source_tell_write(za->src)) < 0) { + zip_error_set_from_source(&za->error, za->src); return -1; } @@ -396,6 +423,11 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { src_final = src; zip_source_keep(src_final); + if (!needs_decrypt && st.encryption_method == ZIP_EM_TRAD_PKWARE && (de->changed & ZIP_DIRENT_LAST_MOD)) { + /* PKWare encryption uses the last modification time for password verification, therefore we can't change it without re-encrypting. Ignoring the requested modification time change seems more sensible than failing to close the archive. */ + de->changed &= ~ZIP_DIRENT_LAST_MOD; + } + if (needs_decrypt) { zip_encryption_implementation impl; @@ -463,11 +495,7 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { /* PKWare encryption uses last_mod, make sure it gets the right value. */ if (de->changed & ZIP_DIRENT_LAST_MOD) { - zip_stat_t st_mtime; - zip_stat_init(&st_mtime); - st_mtime.valid = ZIP_STAT_MTIME; - st_mtime.mtime = de->last_mod; - if ((src_tmp = _zip_source_window_new(src_final, 0, -1, &st_mtime, 0, NULL, NULL, 0, true, &za->error)) == NULL) { + if ((src_tmp = _zip_source_window_new(src_final, 0, -1, NULL, 0, NULL, &de->last_mod, NULL, 0, true, &za->error)) == NULL) { zip_source_free(src_final); return -1; } @@ -484,22 +512,39 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { src_final = src_tmp; } + if (!ZIP_WANT_TORRENTZIP(za)) { + if (zip_source_get_file_attributes(src_final, &attributes) != 0) { + zip_error_set_from_source(&za->error, src_final); + zip_source_free(src_final); + return -1; + } + _zip_dirent_apply_attributes(de, &attributes, (flags & ZIP_FL_FORCE_ZIP64) != 0); + } - if ((offdata = zip_source_tell_write(za->src)) < 0) { - zip_error_set_from_source(&za->error, za->src); + /* as long as we don't support non-seekable output, clear data descriptor bit */ + if ((is_zip64 = _zip_dirent_write(za, de, flags)) < 0) { + zip_source_free(src_final); return -1; } - ret = copy_source(za, src_final, data_length); + if ((offdata = zip_source_tell_write(za->src)) < 0) { + zip_error_set_from_source(&za->error, za->src); + zip_source_free(src_final); + return -1; + } + + ret = copy_source(za, src_final, src, data_length); if (zip_source_stat(src_final, &st) < 0) { zip_error_set_from_source(&za->error, src_final); ret = -1; } - if (zip_source_get_file_attributes(src_final, &attributes) != 0) { - zip_error_set_from_source(&za->error, src_final); - ret = -1; + if (!ZIP_WANT_TORRENTZIP(za)) { + if (zip_source_get_file_attributes(src_final, &attributes) != 0) { + zip_error_set_from_source(&za->error, src_final); + ret = -1; + } } zip_source_free(src_final); @@ -513,44 +558,51 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { return -1; } - if (zip_source_seek_write(za->src, offstart, SEEK_SET) < 0) { - zip_error_set_from_source(&za->error, za->src); - return -1; - } - if ((st.valid & (ZIP_STAT_COMP_METHOD | ZIP_STAT_CRC | ZIP_STAT_SIZE)) != (ZIP_STAT_COMP_METHOD | ZIP_STAT_CRC | ZIP_STAT_SIZE)) { zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } - if ((de->changed & ZIP_DIRENT_LAST_MOD) == 0) { - if (st.valid & ZIP_STAT_MTIME) - de->last_mod = st.mtime; - else - time(&de->last_mod); - } + dirent_changed = ZIP_CM_ACTUAL(de->comp_method) != st.comp_method || de->crc != st.crc || de->uncomp_size != st.size || de->comp_size != (zip_uint64_t)(offend - offdata); de->comp_method = st.comp_method; de->crc = st.crc; de->uncomp_size = st.size; de->comp_size = (zip_uint64_t)(offend - offdata); - _zip_dirent_apply_attributes(de, &attributes, (flags & ZIP_FL_FORCE_ZIP64) != 0, changed); - if (ZIP_WANT_TORRENTZIP(za)) { - zip_dirent_torrentzip_normalize(de); + if (!ZIP_WANT_TORRENTZIP(za)) { + dirent_changed |= _zip_dirent_apply_attributes(de, &attributes, (flags & ZIP_FL_FORCE_ZIP64) != 0); + + if ((de->changed & ZIP_DIRENT_LAST_MOD) == 0 && !have_dos_time) { + if (st.valid & ZIP_STAT_MTIME) { + if (st.mtime != mtime_before_copy) { + if (_zip_u2d_time(st.mtime, &de->last_mod, &za->error) < 0) { + return -1; + } + dirent_changed = true; + } + } + } } - if ((ret = _zip_dirent_write(za, de, flags)) < 0) - return -1; + if (dirent_changed) { + if (zip_source_seek_write(za->src, offstart, SEEK_SET) < 0) { + zip_error_set_from_source(&za->error, za->src); + return -1; + } - if (is_zip64 != ret) { - /* Zip64 mismatch between preliminary file header written before data and final file header written afterwards */ - zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); - return -1; - } + if ((ret = _zip_dirent_write(za, de, flags)) < 0) + return -1; - if (zip_source_seek_write(za->src, offend, SEEK_SET) < 0) { - zip_error_set_from_source(&za->error, za->src); - return -1; + if (is_zip64 != ret) { + /* Zip64 mismatch between preliminary file header written before data and final file header written afterwards */ + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + return -1; + } + + if (zip_source_seek_write(za->src, offend, SEEK_SET) < 0) { + zip_error_set_from_source(&za->error, za->src); + return -1; + } } if (de->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) { @@ -600,7 +652,7 @@ copy_data(zip_t *za, zip_uint64_t len) { static int -copy_source(zip_t *za, zip_source_t *src, zip_int64_t data_length) { +copy_source(zip_t *za, zip_source_t *src, zip_source_t *src_for_length, zip_int64_t data_length) { DEFINE_BYTE_ARRAY(buf, BUFSIZE); zip_int64_t n, current; int ret; @@ -623,7 +675,13 @@ copy_source(zip_t *za, zip_source_t *src, zip_int64_t data_length) { break; } if (n == BUFSIZE && za->progress && data_length > 0) { - current += n; + zip_int64_t t; + t = zip_source_tell(src_for_length); + if (t >= 0) { + current = t; + } else { + current += n; + } if (_zip_progress_update(za->progress, (double)current / (double)data_length) != 0) { zip_error_set(&za->error, ZIP_ER_CANCELLED, 0); ret = -1; @@ -737,4 +795,4 @@ static int torrentzip_compare_names(const void *a, const void *b) { } return strcasecmp(aname, bname); -} \ No newline at end of file +} diff --git a/zip_crypto.h b/zip_crypto.h index 0d74d1a..805af52 100644 --- a/zip_crypto.h +++ b/zip_crypto.h @@ -1,6 +1,6 @@ /* zip_crypto.h -- crypto definitions - Copyright (C) 2017-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2017-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_crypto_commoncrypto.h b/zip_crypto_commoncrypto.h index 01828cc..82dafdb 100644 --- a/zip_crypto_commoncrypto.h +++ b/zip_crypto_commoncrypto.h @@ -1,6 +1,6 @@ /* zip_crypto_commoncrypto.h -- definitions for CommonCrypto wrapper. - Copyright (C) 2018 Dieter Baron and Thomas Klausner + Copyright (C) 2018-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_crypto_gnutls.h b/zip_crypto_gnutls.h index dc8b97a..a6fa508 100644 --- a/zip_crypto_gnutls.h +++ b/zip_crypto_gnutls.h @@ -1,6 +1,6 @@ /* zip_crypto_gnutls.h -- definitions for GnuTLS wrapper. - Copyright (C) 2018-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2018-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_crypto_mbedtls.h b/zip_crypto_mbedtls.h index 1151fff..30ce21c 100644 --- a/zip_crypto_mbedtls.h +++ b/zip_crypto_mbedtls.h @@ -1,6 +1,6 @@ /* zip_crypto_mbedtls.h -- definitions for mbedtls wrapper - Copyright (C) 2018-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2018-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_crypto_openssl.h b/zip_crypto_openssl.h index 198a907..e593ec5 100644 --- a/zip_crypto_openssl.h +++ b/zip_crypto_openssl.h @@ -1,6 +1,6 @@ /* zip_crypto_openssl.h -- definitions for OpenSSL wrapper. - Copyright (C) 2018-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2018-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_crypto_win.c b/zip_crypto_win.c index ee3ccc3..6d92303 100644 --- a/zip_crypto_win.c +++ b/zip_crypto_win.c @@ -1,6 +1,6 @@ /* zip_crypto_win.c -- Windows Crypto API wrapper. - Copyright (C) 2018-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2018-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -37,9 +37,6 @@ #include "zip_crypto.h" -#define WIN32_LEAN_AND_MEAN -#define NOCRYPT - #include #include diff --git a/zip_crypto_win.h b/zip_crypto_win.h index a533fe2..3f05b62 100644 --- a/zip_crypto_win.h +++ b/zip_crypto_win.h @@ -1,6 +1,6 @@ /* zip_crypto_win.h -- Windows Crypto API wrapper. - Copyright (C) 2018-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2018-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_delete.c b/zip_delete.c index 676c16b..4eefdd9 100644 --- a/zip_delete.c +++ b/zip_delete.c @@ -1,6 +1,6 @@ /* zip_delete.c -- delete file from zip archive - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_dir_add.c b/zip_dir_add.c index c010819..01d8ec9 100644 --- a/zip_dir_add.c +++ b/zip_dir_add.c @@ -1,6 +1,6 @@ /* zip_dir_add.c -- add directory - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_dirent.c b/zip_dirent.c index 45a2a6a..5ea8d6d 100644 --- a/zip_dirent.c +++ b/zip_dirent.c @@ -1,6 +1,6 @@ /* zip_dirent.c -- read directory entry (local or central), clean dirent - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -41,7 +41,7 @@ #include "zipint.h" -static zip_string_t *_zip_dirent_process_ef_utf_8(const zip_dirent_t *de, zip_uint16_t id, zip_string_t *str); +static zip_string_t *_zip_dirent_process_ef_utf_8(const zip_dirent_t *de, zip_uint16_t id, zip_string_t *str, bool check_consistency); static zip_extra_field_t *_zip_ef_utf8(zip_uint16_t, zip_string_t *, zip_error_t *); static bool _zip_dirent_process_winzip_aes(zip_dirent_t *de, zip_error_t *error); @@ -50,8 +50,9 @@ void _zip_cdir_free(zip_cdir_t *cd) { zip_uint64_t i; - if (!cd) + if (cd == NULL) { return; + } for (i = 0; i < cd->nentry; i++) _zip_entry_finalize(cd->entry + i); @@ -62,7 +63,7 @@ _zip_cdir_free(zip_cdir_t *cd) { zip_cdir_t * -_zip_cdir_new(zip_uint64_t nentry, zip_error_t *error) { +_zip_cdir_new(zip_error_t *error) { zip_cdir_t *cd; if ((cd = (zip_cdir_t *)malloc(sizeof(*cd))) == NULL) { @@ -76,43 +77,27 @@ _zip_cdir_new(zip_uint64_t nentry, zip_error_t *error) { cd->comment = NULL; cd->is_zip64 = false; - if (!_zip_cdir_grow(cd, nentry, error)) { - _zip_cdir_free(cd); - return NULL; - } - return cd; } bool _zip_cdir_grow(zip_cdir_t *cd, zip_uint64_t additional_entries, zip_error_t *error) { - zip_uint64_t i, new_alloc; - zip_entry_t *new_entry; + zip_uint64_t i; if (additional_entries == 0) { return true; } - new_alloc = cd->nentry_alloc + additional_entries; - - if (new_alloc < additional_entries || new_alloc > SIZE_MAX / sizeof(*(cd->entry))) { - zip_error_set(error, ZIP_ER_MEMORY, 0); + if (!ZIP_REALLOC(cd->entry, cd->nentry_alloc, additional_entries, error)) { return false; } - if ((new_entry = (zip_entry_t *)realloc(cd->entry, sizeof(*(cd->entry)) * (size_t)new_alloc)) == NULL) { - zip_error_set(error, ZIP_ER_MEMORY, 0); - return false; - } - - cd->entry = new_entry; - - for (i = cd->nentry; i < new_alloc; i++) { + for (i = cd->nentry; i < cd->nentry_alloc; i++) { _zip_entry_init(cd->entry + i); } - cd->nentry = cd->nentry_alloc = new_alloc; + cd->nentry = cd->nentry_alloc; return true; } @@ -126,8 +111,6 @@ _zip_cdir_write(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivor zip_buffer_t *buffer; zip_int64_t off; zip_uint64_t i; - bool is_zip64; - int ret; zip_uint32_t cdir_crc; if ((off = zip_source_tell_write(za->src)) < 0) { @@ -136,8 +119,6 @@ _zip_cdir_write(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivor } offset = (zip_uint64_t)off; - is_zip64 = false; - if (ZIP_WANT_TORRENTZIP(za)) { cdir_crc = (zip_uint32_t)crc32(0, NULL, 0); za->write_crc = &cdir_crc; @@ -146,10 +127,10 @@ _zip_cdir_write(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivor for (i = 0; i < survivors; i++) { zip_entry_t *entry = za->entry + filelist[i].idx; - if ((ret = _zip_dirent_write(za, entry->changes ? entry->changes : entry->orig, ZIP_FL_CENTRAL)) < 0) + if (_zip_dirent_write(za, entry->changes ? entry->changes : entry->orig, ZIP_FL_CENTRAL) < 0) { + za->write_crc = NULL; return -1; - if (ret) - is_zip64 = true; + } } za->write_crc = NULL; @@ -160,16 +141,12 @@ _zip_cdir_write(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivor } size = (zip_uint64_t)off - offset; - if (offset > ZIP_UINT32_MAX || survivors > ZIP_UINT16_MAX) { - is_zip64 = true; - } - if ((buffer = _zip_buffer_new(buf, sizeof(buf))) == NULL) { zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return -1; } - if (is_zip64) { + if (survivors > ZIP_UINT16_MAX || offset > ZIP_UINT32_MAX || size > ZIP_UINT32_MAX) { _zip_buffer_put(buffer, EOCD64_MAGIC, 4); _zip_buffer_put_64(buffer, EOCD64LEN - 12); _zip_buffer_put_16(buffer, 45); @@ -287,6 +264,55 @@ _zip_dirent_free(zip_dirent_t *zde) { } +bool +_zip_dirent_merge(zip_dirent_t *de, zip_dirent_t *de_orig, bool replacing_data, zip_error_t *error) { + if (!de->cloned) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + return false; + } + + if (!(de->changed & ZIP_DIRENT_ATTRIBUTES)) { + de->ext_attrib = de_orig->ext_attrib; + de->int_attrib = de_orig->int_attrib; + } + if (!(de->changed & ZIP_DIRENT_COMMENT)) { + de->comment = de_orig->comment; + } + if (!(de->changed & ZIP_DIRENT_COMP_METHOD)) { + if (replacing_data) { + de->comp_method = ZIP_CM_DEFAULT; + de->compression_level = 0; + } + else { + de->comp_method = de_orig->comp_method; + de->compression_level = de_orig->compression_level; + } + } + if (!(de->changed & ZIP_DIRENT_ENCRYPTION_METHOD)) { + if (replacing_data) { + de->encryption_method = ZIP_EM_NONE; + } + else { + de->encryption_method = de_orig->encryption_method; + } + } + if (!(de->changed & ZIP_DIRENT_EXTRA_FIELD)) { + de->extra_fields = de_orig->extra_fields; + } + if (!(de->changed & ZIP_DIRENT_FILENAME)) { + de->filename = de_orig->filename; + } + if (!(de->changed & ZIP_DIRENT_LAST_MOD)) { + de->last_mod = de_orig->last_mod; + } + if (!(de->changed & ZIP_DIRENT_PASSWORD)) { + de->password = de_orig->password; + } + + return true; +} + + void _zip_dirent_init(zip_dirent_t *de) { de->changed = 0; @@ -294,11 +320,13 @@ _zip_dirent_init(zip_dirent_t *de) { de->cloned = 0; de->crc_valid = true; + de->last_mod_mtime_valid = false; de->version_madeby = 63 | (ZIP_OPSYS_DEFAULT << 8); de->version_needed = 10; /* 1.0 */ de->bitflags = 0; de->comp_method = ZIP_CM_DEFAULT; - de->last_mod = 0; + de->last_mod.date = 0; + de->last_mod.time = 0; de->crc = 0; de->comp_size = 0; de->uncomp_size = 0; @@ -336,7 +364,7 @@ _zip_dirent_new(void) { } -/* _zip_dirent_read(zde, fp, bufp, left, localp, error): +/* Fills the zip directory entry zde. If buffer is non-NULL, data is taken from there; otherwise data is read from fp as needed. @@ -347,11 +375,12 @@ _zip_dirent_new(void) { */ zip_int64_t -_zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, bool local, zip_error_t *error) { +_zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, bool local, zip_uint64_t central_compressed_size, bool check_consistency, zip_error_t *error) { zip_uint8_t buf[CDENTRYSIZE]; - zip_uint16_t dostime, dosdate; zip_uint32_t size, variable_size; zip_uint16_t filename_len, comment_len, ef_len; + zip_string_t *utf8_string; + bool is_zip64 = false; bool from_buffer = (buffer != NULL); @@ -389,9 +418,8 @@ _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, boo zde->comp_method = _zip_buffer_get_16(buffer); /* convert to time_t */ - dostime = _zip_buffer_get_16(buffer); - dosdate = _zip_buffer_get_16(buffer); - zde->last_mod = _zip_d2u_time(dostime, dosdate); + zde->last_mod.time = _zip_buffer_get_16(buffer); + zde->last_mod.date = _zip_buffer_get_16(buffer); zde->crc = _zip_buffer_get_32(buffer); zde->comp_size = _zip_buffer_get_32(buffer); @@ -458,7 +486,7 @@ _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, boo if (filename_len) { zde->filename = _zip_read_string(buffer, src, filename_len, 1, error); - if (!zde->filename) { + if (zde->filename == NULL) { if (zip_error_code_zip(error) == ZIP_ER_EOF) { zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_VARIABLE_SIZE_OVERFLOW); } @@ -502,7 +530,7 @@ _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, boo if (comment_len) { zde->comment = _zip_read_string(buffer, src, comment_len, 0, error); - if (!zde->comment) { + if (zde->comment == NULL) { if (!from_buffer) { _zip_buffer_free(buffer); } @@ -519,8 +547,24 @@ _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, boo } } - zde->filename = _zip_dirent_process_ef_utf_8(zde, ZIP_EF_UTF_8_NAME, zde->filename); - zde->comment = _zip_dirent_process_ef_utf_8(zde, ZIP_EF_UTF_8_COMMENT, zde->comment); + if ((utf8_string = _zip_dirent_process_ef_utf_8(zde, ZIP_EF_UTF_8_NAME, zde->filename, check_consistency)) == NULL && zde->filename != NULL) { + zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_UTF8_FILENAME_MISMATCH); + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; + } + zde->filename = utf8_string; + if (!local) { + if ((utf8_string = _zip_dirent_process_ef_utf_8(zde, ZIP_EF_UTF_8_COMMENT, zde->comment, check_consistency)) == NULL && zde->comment != NULL) { + zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_UTF8_COMMENT_MISMATCH); + if (!from_buffer) { + _zip_buffer_free(buffer); + } + return -1; + } + zde->comment = utf8_string; + } /* Zip64 */ @@ -535,6 +579,7 @@ _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, boo return -1; } } + is_zip64 = true; } @@ -545,10 +590,40 @@ _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, boo } return -1; } + if (!from_buffer) { _zip_buffer_free(buffer); } + if (local && zde->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) { + zip_uint32_t df_crc; + zip_uint64_t df_comp_size, df_uncomp_size; + if (zip_source_seek(src, central_compressed_size, SEEK_CUR) != 0 || (buffer = _zip_buffer_new_from_source(src, MAX_DATA_DESCRIPTOR_LENGTH, buf, error)) == NULL) { + return -1; + } + if (memcmp(_zip_buffer_peek(buffer, MAGIC_LEN), DATADES_MAGIC, MAGIC_LEN) == 0) { + _zip_buffer_skip(buffer, MAGIC_LEN); + } + df_crc = _zip_buffer_get_32(buffer); + df_comp_size = is_zip64 ? _zip_buffer_get_64(buffer) : _zip_buffer_get_32(buffer); + df_uncomp_size = is_zip64 ? _zip_buffer_get_64(buffer) : _zip_buffer_get_32(buffer); + + if (!_zip_buffer_ok(buffer)) { + zip_error_set(error, ZIP_ER_INTERNAL, 0); + _zip_buffer_free(buffer); + return -1; + } + _zip_buffer_free(buffer); + + if ((zde->crc != 0 && zde->crc != df_crc) || (zde->comp_size != 0 && zde->comp_size != df_comp_size) || (zde->uncomp_size != 0 && zde->uncomp_size != df_uncomp_size)) { + zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_DATA_DESCRIPTOR_MISMATCH); + return -1; + } + zde->crc = df_crc; + zde->comp_size = df_comp_size; + zde->uncomp_size = df_uncomp_size; + } + /* zip_source_seek / zip_source_tell don't support values > ZIP_INT64_MAX */ if (zde->offset > ZIP_INT64_MAX) { zip_error_set(error, ZIP_ER_SEEK, EFBIG); @@ -564,7 +639,8 @@ _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, boo return (zip_int64_t)size + (zip_int64_t)variable_size; } -bool zip_dirent_process_ef_zip64(zip_dirent_t* zde, const zip_uint8_t* ef, zip_uint64_t got_len, bool local, zip_error_t* error) { +bool +zip_dirent_process_ef_zip64(zip_dirent_t *zde, const zip_uint8_t *ef, zip_uint64_t got_len, bool local, zip_error_t *error) { zip_buffer_t *ef_buffer; if ((ef_buffer = _zip_buffer_new((zip_uint8_t *)ef, got_len)) == NULL) { @@ -625,7 +701,7 @@ bool zip_dirent_process_ef_zip64(zip_dirent_t* zde, const zip_uint8_t* ef, zip_u static zip_string_t * -_zip_dirent_process_ef_utf_8(const zip_dirent_t *de, zip_uint16_t id, zip_string_t *str) { +_zip_dirent_process_ef_utf_8(const zip_dirent_t *de, zip_uint16_t id, zip_string_t *str, bool check_consistency) { zip_uint16_t ef_len; zip_uint32_t ef_crc; zip_buffer_t *buffer; @@ -648,6 +724,14 @@ _zip_dirent_process_ef_utf_8(const zip_dirent_t *de, zip_uint16_t id, zip_string zip_string_t *ef_str = _zip_string_new(_zip_buffer_get(buffer, len), len, ZIP_FL_ENC_UTF_8, NULL); if (ef_str != NULL) { + if (check_consistency) { + if (!_zip_string_equal(str, ef_str) && _zip_string_is_ascii(ef_str)) { + _zip_string_free(ef_str); + _zip_buffer_free(buffer); + return NULL; + } + } + _zip_string_free(str); str = ef_str; } @@ -688,18 +772,18 @@ _zip_dirent_process_winzip_aes(zip_dirent_t *de, zip_error_t *error) { crc_valid = true; switch (_zip_buffer_get_16(buffer)) { - case 1: - break; + case 1: + break; - case 2: - crc_valid = false; - /* TODO: When checking consistency, check that crc is 0. */ - break; - - default: - zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0); - _zip_buffer_free(buffer); - return false; + case 2: + crc_valid = false; + /* TODO: When checking consistency, check that crc is 0. */ + break; + + default: + zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0); + _zip_buffer_free(buffer); + return false; } /* vendor */ @@ -787,7 +871,7 @@ _zip_dirent_size(zip_source_t *src, zip_uint16_t flags, zip_error_t *error) { int _zip_dirent_write(zip_t *za, zip_dirent_t *de, zip_flags_t flags) { - zip_uint16_t dostime, dosdate; + zip_dostime_t dostime; zip_encoding_type_t com_enc, name_enc; zip_extra_field_t *ef; zip_extra_field_t *ef64; @@ -922,18 +1006,18 @@ _zip_dirent_write(zip_t *za, zip_dirent_t *de, zip_flags_t flags) { _zip_buffer_put_16(buffer, ZIP_CM_WINZIP_AES); } else { - _zip_buffer_put_16(buffer, (zip_uint16_t)de->comp_method); + _zip_buffer_put_16(buffer, (zip_uint16_t)ZIP_CM_ACTUAL(de->comp_method)); } if (ZIP_WANT_TORRENTZIP(za)) { - dostime = 0xbc00; - dosdate = 0x2198; + dostime.time = 0xbc00; + dostime.date = 0x2198; } else { - _zip_u2d_time(de->last_mod, &dostime, &dosdate); + dostime = de->last_mod; } - _zip_buffer_put_16(buffer, dostime); - _zip_buffer_put_16(buffer, dosdate); + _zip_buffer_put_16(buffer, dostime.time); + _zip_buffer_put_16(buffer, dostime.date); if (is_winzip_aes && de->uncomp_size < 20) { _zip_buffer_put_32(buffer, 0); @@ -1034,7 +1118,7 @@ _zip_dirent_write(zip_t *za, zip_dirent_t *de, zip_flags_t flags) { time_t -_zip_d2u_time(zip_uint16_t dtime, zip_uint16_t ddate) { +_zip_d2u_time(const zip_dostime_t *dtime) { struct tm tm; memset(&tm, 0, sizeof(tm)); @@ -1042,13 +1126,13 @@ _zip_d2u_time(zip_uint16_t dtime, zip_uint16_t ddate) { /* let mktime decide if DST is in effect */ tm.tm_isdst = -1; - tm.tm_year = ((ddate >> 9) & 127) + 1980 - 1900; - tm.tm_mon = ((ddate >> 5) & 15) - 1; - tm.tm_mday = ddate & 31; + tm.tm_year = ((dtime->date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dtime->date >> 5) & 15) - 1; + tm.tm_mday = dtime->date & 31; - tm.tm_hour = (dtime >> 11) & 31; - tm.tm_min = (dtime >> 5) & 63; - tm.tm_sec = (dtime << 1) & 62; + tm.tm_hour = (dtime->time >> 11) & 31; + tm.tm_min = (dtime->time >> 5) & 63; + tm.tm_sec = (dtime->time << 1) & 62; return mktime(&tm); } @@ -1119,72 +1203,101 @@ _zip_get_dirent(zip_t *za, zip_uint64_t idx, zip_flags_t flags, zip_error_t *err } -void -_zip_u2d_time(time_t intime, zip_uint16_t *dtime, zip_uint16_t *ddate) { +int +_zip_u2d_time(time_t intime, zip_dostime_t *dtime, zip_error_t *ze) { struct tm *tpm; struct tm tm; tpm = zip_localtime(&intime, &tm); if (tpm == NULL) { /* if localtime fails, return an arbitrary date (1980-01-01 00:00:00) */ - *ddate = (1 << 5) + 1; - *dtime = 0; - return; + dtime->date = (1 << 5) + 1; + dtime->time = 0; + if (ze) { + zip_error_set(ze, ZIP_ER_INVAL, errno); + } + return -1; } if (tpm->tm_year < 80) { tpm->tm_year = 80; } - *ddate = (zip_uint16_t)(((tpm->tm_year + 1900 - 1980) << 9) + ((tpm->tm_mon + 1) << 5) + tpm->tm_mday); - *dtime = (zip_uint16_t)(((tpm->tm_hour) << 11) + ((tpm->tm_min) << 5) + ((tpm->tm_sec) >> 1)); + dtime->date = (zip_uint16_t)(((tpm->tm_year + 1900 - 1980) << 9) + ((tpm->tm_mon + 1) << 5) + tpm->tm_mday); + dtime->time = (zip_uint16_t)(((tpm->tm_hour) << 11) + ((tpm->tm_min) << 5) + ((tpm->tm_sec) >> 1)); + + return 0; } -void -_zip_dirent_apply_attributes(zip_dirent_t *de, zip_file_attributes_t *attributes, bool force_zip64, zip_uint32_t changed) { +bool +_zip_dirent_apply_attributes(zip_dirent_t *de, zip_file_attributes_t *attributes, bool force_zip64) { zip_uint16_t length; + bool has_changed = false; if (attributes->valid & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS) { zip_uint16_t mask = attributes->general_purpose_bit_mask & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK; - de->bitflags = (de->bitflags & ~mask) | (attributes->general_purpose_bit_flags & mask); + zip_uint16_t bitflags = (de->bitflags & ~mask) | (attributes->general_purpose_bit_flags & mask); + if (de->bitflags != bitflags) { + de->bitflags = bitflags; + has_changed = true; + } } if (attributes->valid & ZIP_FILE_ATTRIBUTES_ASCII) { - de->int_attrib = (de->int_attrib & ~0x1) | (attributes->ascii ? 1 : 0); + zip_uint16_t int_attrib = (de->int_attrib & ~0x1) | (attributes->ascii ? 1 : 0); + if (de->int_attrib != int_attrib) { + de->int_attrib = int_attrib; + has_changed = true; + } } /* manually set attributes are preferred over attributes provided by source */ - if ((changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES)) { - de->ext_attrib = attributes->external_file_attributes; + if ((de->changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES)) { + if (de->ext_attrib != attributes->external_file_attributes) { + de->ext_attrib = attributes->external_file_attributes; + has_changed = true; + } } + zip_uint16_t version_needed; if (de->comp_method == ZIP_CM_LZMA) { - de->version_needed = 63; + version_needed = 63; } else if (de->encryption_method == ZIP_EM_AES_128 || de->encryption_method == ZIP_EM_AES_192 || de->encryption_method == ZIP_EM_AES_256) { - de->version_needed = 51; + version_needed = 51; } else if (de->comp_method == ZIP_CM_BZIP2) { - de->version_needed = 46; + version_needed = 46; } else if (force_zip64 || _zip_dirent_needs_zip64(de, 0)) { - de->version_needed = 45; + version_needed = 45; } else if (de->comp_method == ZIP_CM_DEFLATE || de->encryption_method == ZIP_EM_TRAD_PKWARE) { - de->version_needed = 20; + version_needed = 20; } else if ((length = _zip_string_length(de->filename)) > 0 && de->filename->raw[length - 1] == '/') { - de->version_needed = 20; + version_needed = 20; } else { - de->version_needed = 10; + version_needed = 10; } if (attributes->valid & ZIP_FILE_ATTRIBUTES_VERSION_NEEDED) { - de->version_needed = ZIP_MAX(de->version_needed, attributes->version_needed); + version_needed = ZIP_MAX(version_needed, attributes->version_needed); } - de->version_madeby = 63 | (de->version_madeby & 0xff00); - if ((changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_HOST_SYSTEM)) { - de->version_madeby = (de->version_madeby & 0xff) | (zip_uint16_t)(attributes->host_system << 8); + if (de->version_needed != version_needed) { + de->version_needed = version_needed; + has_changed = true; } + + zip_int16_t version_madeby = 63 | (de->version_madeby & 0xff00); + if ((de->changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_HOST_SYSTEM)) { + version_madeby = (version_madeby & 0xff) | (zip_uint16_t)(attributes->host_system << 8); + } + if (de->version_madeby != version_madeby) { + de->version_madeby = version_madeby; + has_changed = true; + } + + return has_changed; } @@ -1192,10 +1305,11 @@ _zip_dirent_apply_attributes(zip_dirent_t *de, zip_file_attributes_t *attributes Set values suitable for torrentzip. */ -void zip_dirent_torrentzip_normalize(zip_dirent_t *de) { +void +zip_dirent_torrentzip_normalize(zip_dirent_t *de) { de->version_madeby = 0; de->version_needed = 20; /* 2.0 */ - de->bitflags = 2; /* maximum compression */ + de->bitflags = 2; /* maximum compression */ de->comp_method = ZIP_CM_DEFLATE; de->compression_level = TORRENTZIP_COMPRESSION_FLAGS; de->disk_number = 0; @@ -1203,5 +1317,44 @@ void zip_dirent_torrentzip_normalize(zip_dirent_t *de) { de->ext_attrib = 0; /* last_mod, extra_fields, and comment are normalized in zip_dirent_write() directly */ - } + +int +zip_dirent_check_consistency(zip_dirent_t *dirent) { + if (dirent->comp_method == ZIP_CM_STORE) { + zip_uint64_t header_size = 0; + switch (dirent->encryption_method) { + case ZIP_EM_NONE: + break; + case ZIP_EM_TRAD_PKWARE: + header_size = 12; + break; + case ZIP_EM_AES_128: + header_size = 20; + break; + case ZIP_EM_AES_192: + header_size = 24; + break; + case ZIP_EM_AES_256: + header_size = 28; + break; + + default: + return 0; + } + if (dirent->uncomp_size + header_size < dirent->uncomp_size || dirent->comp_size != dirent->uncomp_size + header_size) { + return ZIP_ER_DETAIL_STORED_SIZE_MISMATCH; + } + } + return 0; +} + +time_t +zip_dirent_get_last_mod_mtime(zip_dirent_t *de) { + if (!de->last_mod_mtime_valid) { + de->last_mod_mtime = _zip_d2u_time(&de->last_mod); + de->last_mod_mtime_valid = true; + } + + return de->last_mod_mtime; +} \ No newline at end of file diff --git a/zip_discard.c b/zip_discard.c index d1dc4f8..841a80e 100644 --- a/zip_discard.c +++ b/zip_discard.c @@ -1,6 +1,6 @@ /* zip_discard.c -- discard and free struct zip - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_entry.c b/zip_entry.c index 35a36e4..dd25e61 100644 --- a/zip_entry.c +++ b/zip_entry.c @@ -1,6 +1,6 @@ /* zip_entry.c -- struct zip_entry helper functions - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_err_str.c b/zip_err_str.c index b7b28dd..b6a5891 100644 --- a/zip_err_str.c +++ b/zip_err_str.c @@ -47,6 +47,9 @@ const struct _zip_err_info _zip_err_str[] = { { S, "Tell error" }, { N, "Compressed data invalid" }, { N, "Operation cancelled" }, + { N, "Unexpected length of data" }, + { N, "Not allowed in torrentzip" }, + { N, "Possibly truncated or corrupted zip archive" }, }; const int _zip_err_str_count = sizeof(_zip_err_str)/sizeof(_zip_err_str[0]); @@ -72,6 +75,12 @@ const struct _zip_err_info _zip_err_details[] = { { E, "garbage at end of extra fields" }, { E, "extra field length is invalid" }, { E, "file length in header doesn't match actual file length" }, + { E, "compressed and uncompressed sizes don't match for stored file" }, + { E, "local header and data descriptor do not match" }, + { G, "EOCD64 and EOCD64 locator do not match" }, + { E, "UTF-8 filename is ASCII and doesn't match filename" }, + { E, "UTF-8 comment is ASCII and doesn't match comment" }, + { G, "garbage at end of compressed data" }, }; const int _zip_err_details_count = sizeof(_zip_err_details)/sizeof(_zip_err_details[0]); diff --git a/zip_error.c b/zip_error.c index c498e08..da910d4 100644 --- a/zip_error.c +++ b/zip_error.c @@ -1,6 +1,6 @@ /* zip_error.c -- zip_error_t helper functions - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_error_clear.c b/zip_error_clear.c index 94ff506..0406271 100644 --- a/zip_error_clear.c +++ b/zip_error_clear.c @@ -1,6 +1,6 @@ /* zip_error_clear.c -- clear zip error - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_error_get.c b/zip_error_get.c index c0418f0..2a5b3ea 100644 --- a/zip_error_get.c +++ b/zip_error_get.c @@ -1,6 +1,6 @@ /* zip_error_get.c -- get zip error - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_error_get_sys_type.c b/zip_error_get_sys_type.c index a22ffb0..973d26c 100644 --- a/zip_error_get_sys_type.c +++ b/zip_error_get_sys_type.c @@ -1,6 +1,6 @@ /* zip_error_get_sys_type.c -- return type of system error code - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_error_strerror.c b/zip_error_strerror.c index fe04cbb..5be54b3 100644 --- a/zip_error_strerror.c +++ b/zip_error_strerror.c @@ -1,6 +1,6 @@ /* zip_error_sterror.c -- get string representation of struct zip_error - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -49,6 +49,9 @@ zip_error_strerror(zip_error_t *err) { if (err->zip_err < 0 || err->zip_err >= _zip_err_str_count) { system_error_buffer = (char *)malloc(128); + if (system_error_buffer == NULL) { + return _zip_err_str[ZIP_ER_MEMORY].description; + } snprintf_s(system_error_buffer, 128, "Unknown error %d", err->zip_err); system_error_buffer[128 - 1] = '\0'; /* make sure string is NUL-terminated */ zip_error_string = NULL; @@ -61,6 +64,9 @@ zip_error_strerror(zip_error_t *err) { case ZIP_ET_SYS: { size_t len = strerrorlen_s(err->sys_err) + 1; system_error_buffer = malloc(len); + if (system_error_buffer == NULL) { + return _zip_err_str[ZIP_ER_MEMORY].description; + } strerror_s(system_error_buffer, len, err->sys_err); system_error_string = system_error_buffer; break; @@ -79,12 +85,18 @@ zip_error_strerror(zip_error_t *err) { } else if (error >= _zip_err_details_count) { system_error_buffer = (char *)malloc(128); + if (system_error_buffer == NULL) { + return _zip_err_str[ZIP_ER_MEMORY].description; + } snprintf_s(system_error_buffer, 128, "invalid detail error %u", error); system_error_buffer[128 - 1] = '\0'; /* make sure string is NUL-terminated */ system_error_string = system_error_buffer; } else if (_zip_err_details[error].type == ZIP_DETAIL_ET_ENTRY && index < MAX_DETAIL_INDEX) { system_error_buffer = (char *)malloc(128); + if (system_error_buffer == NULL) { + return _zip_err_str[ZIP_ER_MEMORY].description; + } snprintf_s(system_error_buffer, 128, "entry %d: %s", index, _zip_err_details[error].description); system_error_buffer[128 - 1] = '\0'; /* make sure string is NUL-terminated */ system_error_string = system_error_buffer; diff --git a/zip_error_to_str.c b/zip_error_to_str.c index b60b788..4186e3a 100644 --- a/zip_error_to_str.c +++ b/zip_error_to_str.c @@ -1,6 +1,6 @@ /* zip_error_to_str.c -- get string representation of zip error code - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_extra_field.c b/zip_extra_field.c index 7aed12a..5283704 100644 --- a/zip_extra_field.c +++ b/zip_extra_field.c @@ -1,6 +1,6 @@ /* zip_extra_field.c -- manipulate extra fields - Copyright (C) 2012-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2012-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_extra_field_api.c b/zip_extra_field_api.c index 560c71b..6f2b459 100644 --- a/zip_extra_field_api.c +++ b/zip_extra_field_api.c @@ -1,6 +1,6 @@ /* zip_extra_field_api.c -- public extra fields API functions - Copyright (C) 2012-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2012-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -56,10 +56,6 @@ zip_file_extra_field_delete(zip_t *za, zip_uint64_t idx, zip_uint16_t ef_idx, zi zip_error_set(&za->error, ZIP_ER_RDONLY, 0); return -1; } - if (ZIP_WANT_TORRENTZIP(za)) { - zip_error_set(&za->error, ZIP_ER_NOT_ALLOWED, 0); - return -1; - } if (_zip_file_extra_field_prepare_for_change(za, idx) < 0) return -1; diff --git a/zip_fclose.c b/zip_fclose.c index b820d98..4dd2994 100644 --- a/zip_fclose.c +++ b/zip_fclose.c @@ -1,6 +1,6 @@ /* zip_fclose.c -- close file in zip archive - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -41,6 +41,9 @@ ZIP_EXTERN int zip_fclose(zip_file_t *zf) { int ret; + if (zf == NULL) + return ZIP_ER_INVAL; + if (zf->src) zip_source_free(zf->src); diff --git a/zip_fdopen.c b/zip_fdopen.c index e72c55d..94fe6c7 100644 --- a/zip_fdopen.c +++ b/zip_fdopen.c @@ -1,6 +1,6 @@ /* zip_fdopen.c -- open read-only archive from file descriptor - Copyright (C) 2009-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_file_add.c b/zip_file_add.c index c2c41e1..5959d50 100644 --- a/zip_file_add.c +++ b/zip_file_add.c @@ -1,6 +1,6 @@ /* zip_file_add.c -- add file via callback function - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_file_error_clear.c b/zip_file_error_clear.c index a10bff8..eb9a614 100644 --- a/zip_file_error_clear.c +++ b/zip_file_error_clear.c @@ -1,6 +1,6 @@ /* zip_file_error_clear.c -- clear zip file error - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_file_error_get.c b/zip_file_error_get.c index b93117b..679aeef 100644 --- a/zip_file_error_get.c +++ b/zip_file_error_get.c @@ -1,6 +1,6 @@ /* zip_file_error_get.c -- get zip file error - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_file_get_comment.c b/zip_file_get_comment.c index fa998f0..ca04042 100644 --- a/zip_file_get_comment.c +++ b/zip_file_get_comment.c @@ -1,6 +1,6 @@ /* zip_file_get_comment.c -- get file comment - Copyright (C) 2006-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_file_get_external_attributes.c b/zip_file_get_external_attributes.c index a79bb3e..483557f 100644 --- a/zip_file_get_external_attributes.c +++ b/zip_file_get_external_attributes.c @@ -1,6 +1,6 @@ /* zip_file_get_external_attributes.c -- get opsys/external attributes - Copyright (C) 2013-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2013-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_file_get_offset.c b/zip_file_get_offset.c index 72f4880..c50def5 100644 --- a/zip_file_get_offset.c +++ b/zip_file_get_offset.c @@ -1,6 +1,6 @@ /* zip_file_get_offset.c -- get offset of file data in archive. - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_file_rename.c b/zip_file_rename.c index 9ac2581..03101e4 100644 --- a/zip_file_rename.c +++ b/zip_file_rename.c @@ -1,6 +1,6 @@ /* zip_file_rename.c -- rename file in zip archive - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_file_replace.c b/zip_file_replace.c index 4262d45..ce457ed 100644 --- a/zip_file_replace.c +++ b/zip_file_replace.c @@ -1,6 +1,6 @@ /* zip_file_replace.c -- replace file via callback function - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -83,6 +83,12 @@ _zip_file_replace(zip_t *za, zip_uint64_t idx, const char *name, zip_source_t *s return -1; } + /* delete all extra fields - these are usually data that are + * strongly coupled with the original data */ + if (zip_file_extra_field_delete(za, idx, ZIP_EXTRA_FIELD_ALL, ZIP_FL_CENTRAL | ZIP_FL_LOCAL) < 0) { + return -1; + } + /* does not change any name related data, so we can do it here; * needed for a double add of the same file name */ _zip_unchange_data(za->entry + idx); diff --git a/zip_file_set_comment.c b/zip_file_set_comment.c index 570f8e8..fb7b9ed 100644 --- a/zip_file_set_comment.c +++ b/zip_file_set_comment.c @@ -1,6 +1,6 @@ /* zip_file_set_comment.c -- set comment for file in archive - Copyright (C) 2006-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_file_set_encryption.c b/zip_file_set_encryption.c index 1cdcd4a..2af55d6 100644 --- a/zip_file_set_encryption.c +++ b/zip_file_set_encryption.c @@ -1,6 +1,6 @@ /* zip_file_set_encryption.c -- set encryption for file in archive - Copyright (C) 2016-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2016-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -40,7 +40,7 @@ ZIP_EXTERN int zip_file_set_encryption(zip_t *za, zip_uint64_t idx, zip_uint16_t method, const char *password) { zip_entry_t *e; - zip_uint16_t old_method; + char *our_password = NULL; if (idx >= za->nentry) { zip_error_set(&za->error, ZIP_ER_INVAL, 0); @@ -63,56 +63,33 @@ zip_file_set_encryption(zip_t *za, zip_uint64_t idx, zip_uint16_t method, const e = za->entry + idx; - old_method = (e->orig == NULL ? ZIP_EM_NONE : e->orig->encryption_method); - if (method == old_method && password == NULL) { - if (e->changes) { - if (e->changes->changed & ZIP_DIRENT_PASSWORD) { - _zip_crypto_clear(e->changes->password, strlen(e->changes->password)); - free(e->changes->password); - e->changes->password = (e->orig == NULL ? NULL : e->orig->password); - } - e->changes->changed &= ~(ZIP_DIRENT_ENCRYPTION_METHOD | ZIP_DIRENT_PASSWORD); - if (e->changes->changed == 0) { - _zip_dirent_free(e->changes); - e->changes = NULL; - } + if (e->changes == NULL) { + if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; } } + + if (password) { + if ((our_password = strdup(password)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; + } + } + + e->changes->encryption_method = method; + e->changes->changed |= ZIP_DIRENT_ENCRYPTION_METHOD; + if (password) { + e->changes->password = our_password; + e->changes->changed |= ZIP_DIRENT_PASSWORD; + } else { - char *our_password = NULL; - - if (password) { - if ((our_password = strdup(password)) == NULL) { - zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - return -1; - } - } - - if (e->changes == NULL) { - if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) { - if (our_password) { - _zip_crypto_clear(our_password, strlen(our_password)); - } - free(our_password); - zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - return -1; - } - } - - e->changes->encryption_method = method; - e->changes->changed |= ZIP_DIRENT_ENCRYPTION_METHOD; - if (password) { - e->changes->password = our_password; - e->changes->changed |= ZIP_DIRENT_PASSWORD; - } - else { - if (e->changes->changed & ZIP_DIRENT_PASSWORD) { - _zip_crypto_clear(e->changes->password, strlen(e->changes->password)); - free(e->changes->password); - e->changes->password = e->orig ? e->orig->password : NULL; - e->changes->changed &= ~ZIP_DIRENT_PASSWORD; - } + if (e->changes->changed & ZIP_DIRENT_PASSWORD) { + _zip_crypto_clear(e->changes->password, strlen(e->changes->password)); + free(e->changes->password); + e->changes->password = e->orig ? e->orig->password : NULL; + e->changes->changed &= ~ZIP_DIRENT_PASSWORD; } } diff --git a/zip_file_set_external_attributes.c b/zip_file_set_external_attributes.c index 2e0429b..2f9d30f 100644 --- a/zip_file_set_external_attributes.c +++ b/zip_file_set_external_attributes.c @@ -1,6 +1,6 @@ /* zip_file_set_external_attributes.c -- set external attributes for entry - Copyright (C) 2013-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2013-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_file_set_mtime.c b/zip_file_set_mtime.c index 4126f5a..e60f7a9 100644 --- a/zip_file_set_mtime.c +++ b/zip_file_set_mtime.c @@ -1,6 +1,6 @@ /* zip_file_set_mtime.c -- set modification time of entry. - Copyright (C) 2014-2022 Dieter Baron and Thomas Klausner + Copyright (C) 2014-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -33,19 +33,12 @@ #include "zipint.h" -ZIP_EXTERN int -zip_file_set_dostime(zip_t *za, zip_uint64_t idx, zip_uint16_t dtime, zip_uint16_t ddate, zip_flags_t flags) { - time_t mtime; - mtime = _zip_d2u_time(dtime, ddate); - return zip_file_set_mtime(za, idx, mtime, flags); -} - -ZIP_EXTERN int -zip_file_set_mtime(zip_t *za, zip_uint64_t idx, time_t mtime, zip_flags_t flags) { +static int zip_file_set_time(zip_t *za, zip_uint64_t idx, zip_uint16_t dtime, zip_uint16_t ddate, zip_flags_t flags, time_t *mtime) { zip_entry_t *e; - if (_zip_get_dirent(za, idx, 0, NULL) == NULL) + if (_zip_get_dirent(za, idx, 0, NULL) == NULL) { return -1; + } if (ZIP_IS_RDONLY(za)) { zip_error_set(&za->error, ZIP_ER_RDONLY, 0); @@ -70,8 +63,31 @@ zip_file_set_mtime(zip_t *za, zip_uint64_t idx, time_t mtime, zip_flags_t flags) } } - e->changes->last_mod = mtime; + e->changes->last_mod.time = dtime; + e->changes->last_mod.date = ddate; + if (mtime != NULL) { + e->changes->last_mod_mtime = *mtime; + e->changes->last_mod_mtime_valid = true; + } + else { + e->changes->last_mod_mtime_valid = false; + } e->changes->changed |= ZIP_DIRENT_LAST_MOD; return 0; } + +ZIP_EXTERN int zip_file_set_dostime(zip_t *za, zip_uint64_t idx, zip_uint16_t dtime, zip_uint16_t ddate, zip_flags_t flags) { + return zip_file_set_time(za, idx, dtime, ddate, flags, NULL); +} + + +ZIP_EXTERN int zip_file_set_mtime(zip_t *za, zip_uint64_t idx, time_t mtime, zip_flags_t flags) { + zip_dostime_t dostime; + + if (_zip_u2d_time(mtime, &dostime, &za->error) < 0) { + return -1; + } + + return zip_file_set_time(za, idx, dostime.time, dostime.date, flags, &mtime); +} diff --git a/zip_file_strerror.c b/zip_file_strerror.c index 5b5a009..5e896b4 100644 --- a/zip_file_strerror.c +++ b/zip_file_strerror.c @@ -1,6 +1,6 @@ /* zip_file_sterror.c -- get string representation of zip file error - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_fopen.c b/zip_fopen.c index e3cde9b..93217f3 100644 --- a/zip_fopen.c +++ b/zip_fopen.c @@ -1,6 +1,6 @@ /* zip_fopen.c -- open file in zip archive for reading - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_fopen_encrypted.c b/zip_fopen_encrypted.c index d5880dc..78143af 100644 --- a/zip_fopen_encrypted.c +++ b/zip_fopen_encrypted.c @@ -1,6 +1,6 @@ /* zip_fopen_encrypted.c -- open file for reading with password - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_fopen_index.c b/zip_fopen_index.c index a449b83..b6676b7 100644 --- a/zip_fopen_index.c +++ b/zip_fopen_index.c @@ -1,6 +1,6 @@ /* zip_fopen_index.c -- open file in zip archive for reading by index - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_fopen_index_encrypted.c b/zip_fopen_index_encrypted.c index 4048370..86d6977 100644 --- a/zip_fopen_index_encrypted.c +++ b/zip_fopen_index_encrypted.c @@ -1,6 +1,6 @@ /* zip_fopen_index_encrypted.c -- open file for reading by index w/ password - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_fread.c b/zip_fread.c index 5b7da46..17cec4f 100644 --- a/zip_fread.c +++ b/zip_fread.c @@ -1,6 +1,6 @@ /* zip_fread.c -- read from file - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -39,11 +39,13 @@ ZIP_EXTERN zip_int64_t zip_fread(zip_file_t *zf, void *outbuf, zip_uint64_t toread) { zip_int64_t n; - if (!zf) + if (zf == NULL) { return -1; + } - if (zf->error.zip_err != 0) + if (zf->error.zip_err != 0) { return -1; + } if (toread > ZIP_INT64_MAX) { zip_error_set(&zf->error, ZIP_ER_INVAL, 0); diff --git a/zip_fseek.c b/zip_fseek.c index e68ffd3..107a6f7 100644 --- a/zip_fseek.c +++ b/zip_fseek.c @@ -1,6 +1,6 @@ /* zip_fseek.c -- seek in file - Copyright (C) 2016-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2016-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -36,11 +36,13 @@ ZIP_EXTERN zip_int8_t zip_fseek(zip_file_t *zf, zip_int64_t offset, int whence) { - if (!zf) + if (zf == NULL) { return -1; + } - if (zf->error.zip_err != 0) + if (zf->error.zip_err != 0) { return -1; + } if (zip_source_seek(zf->src, offset, whence) < 0) { zip_error_set_from_source(&zf->error, zf->src); @@ -53,7 +55,7 @@ zip_fseek(zip_file_t *zf, zip_int64_t offset, int whence) { ZIP_EXTERN int zip_file_is_seekable(zip_file_t *zfile) { - if (!zfile) { + if (zfile == NULL) { return -1; } diff --git a/zip_ftell.c b/zip_ftell.c index bf3b03d..6299b2d 100644 --- a/zip_ftell.c +++ b/zip_ftell.c @@ -1,6 +1,6 @@ /* zip_ftell.c -- tell position in file - Copyright (C) 2016-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2016-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -38,11 +38,13 @@ ZIP_EXTERN zip_int64_t zip_ftell(zip_file_t *zf) { zip_int64_t res; - if (!zf) + if (zf == NULL) { return -1; + } - if (zf->error.zip_err != 0) + if (zf->error.zip_err != 0) { return -1; + } res = zip_source_tell(zf->src); if (res < 0) { diff --git a/zip_get_archive_comment.c b/zip_get_archive_comment.c index ea9a00a..b83e6bc 100644 --- a/zip_get_archive_comment.c +++ b/zip_get_archive_comment.c @@ -1,6 +1,6 @@ /* zip_get_archive_comment.c -- get archive comment - Copyright (C) 2006-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_get_archive_flag.c b/zip_get_archive_flag.c index fc200bd..80d5991 100644 --- a/zip_get_archive_flag.c +++ b/zip_get_archive_flag.c @@ -1,6 +1,6 @@ /* zip_get_archive_flag.c -- get archive global flag - Copyright (C) 2008-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2008-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_get_encryption_implementation.c b/zip_get_encryption_implementation.c index 72e48fe..28ad329 100644 --- a/zip_get_encryption_implementation.c +++ b/zip_get_encryption_implementation.c @@ -1,6 +1,6 @@ /* zip_get_encryption_implementation.c -- get encryption implementation - Copyright (C) 2009-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_get_file_comment.c b/zip_get_file_comment.c index d58e22b..0284aec 100644 --- a/zip_get_file_comment.c +++ b/zip_get_file_comment.c @@ -1,6 +1,6 @@ /* zip_get_file_comment.c -- get file comment - Copyright (C) 2006-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_get_name.c b/zip_get_name.c index 4828d78..d1c9453 100644 --- a/zip_get_name.c +++ b/zip_get_name.c @@ -1,6 +1,6 @@ /* zip_get_name.c -- get filename for a file in zip file - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_get_num_entries.c b/zip_get_num_entries.c index 667dc51..b2595de 100644 --- a/zip_get_num_entries.c +++ b/zip_get_num_entries.c @@ -1,6 +1,6 @@ /* zip_get_num_entries.c -- get number of entries in archive - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_get_num_files.c b/zip_get_num_files.c index 140e34f..16d7754 100644 --- a/zip_get_num_files.c +++ b/zip_get_num_files.c @@ -1,6 +1,6 @@ /* zip_get_num_files.c -- get number of files in archive - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_hash.c b/zip_hash.c index d3a954e..8479bec 100644 --- a/zip_hash.c +++ b/zip_hash.c @@ -1,6 +1,6 @@ /* zip_hash.c -- hash table string -> uint64 - Copyright (C) 2015-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2015-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_io_util.c b/zip_io_util.c index 9fcd10b..e346246 100644 --- a/zip_io_util.c +++ b/zip_io_util.c @@ -1,6 +1,6 @@ /* zip_io_util.c -- I/O helper functions - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -70,7 +70,7 @@ _zip_read_data(zip_buffer_t *buffer, zip_source_t *src, size_t length, bool nulp } r = (zip_uint8_t *)malloc(length + (nulp ? 1 : 0)); - if (!r) { + if (r == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); return NULL; } diff --git a/zip_libzip_version.c b/zip_libzip_version.c index 4200727..139b250 100644 --- a/zip_libzip_version.c +++ b/zip_libzip_version.c @@ -1,6 +1,6 @@ /* zip_libzip_version.c -- return run-time version of library - Copyright (C) 2017-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2017-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_memdup.c b/zip_memdup.c index 75d72c6..6ac9a5a 100644 --- a/zip_memdup.c +++ b/zip_memdup.c @@ -1,6 +1,6 @@ /* zip_memdup.c -- internal zip function, "strdup" with len - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -45,7 +45,7 @@ _zip_memdup(const void *mem, size_t len, zip_error_t *error) { return NULL; ret = malloc(len); - if (!ret) { + if (ret == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); return NULL; } diff --git a/zip_new.c b/zip_new.c index 4f69c8a..68e1588 100644 --- a/zip_new.c +++ b/zip_new.c @@ -1,6 +1,6 @@ /* zip_new.c -- create and init struct zip - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -46,7 +46,7 @@ _zip_new(zip_error_t *error) { zip_t *za; za = (zip_t *)malloc(sizeof(struct zip)); - if (!za) { + if (za == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); return NULL; } @@ -68,6 +68,7 @@ _zip_new(zip_error_t *error) { za->nopen_source = za->nopen_source_alloc = 0; za->open_source = NULL; za->progress = NULL; + za->torrent_mtime = 0; return za; } diff --git a/zip_open.c b/zip_open.c index ee7e9de..aee0da0 100644 --- a/zip_open.c +++ b/zip_open.c @@ -1,6 +1,6 @@ /* zip_open.c -- open zip archive by name - Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -31,7 +31,6 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include #include #include @@ -39,17 +38,30 @@ #include "zipint.h" -typedef enum { EXISTS_ERROR = -1, EXISTS_NOT = 0, EXISTS_OK } exists_t; +typedef enum { + EXISTS_ERROR = -1, + EXISTS_NOT = 0, + EXISTS_OK +} exists_t; +typedef enum { + CDIR_OK, + CDIR_INVALID, + CDIR_NOT_FOUND + +} cdir_status_t; + +static bool check_eocd(zip_cdir_t *cd, unsigned int flags, zip_error_t *error); +static bool check_magic(zip_uint64_t offset, zip_buffer_t *buffer, zip_uint64_t buffer_offset, zip_source_t *src, const char* magic); static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error); static zip_int64_t _zip_checkcons(zip_t *za, zip_cdir_t *cdir, zip_error_t *error); static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir); static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len); static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error); static int _zip_headercomp(const zip_dirent_t *, const zip_dirent_t *); -static const unsigned char *_zip_memmem(const unsigned char *, size_t, const unsigned char *, size_t); -static zip_cdir_t *_zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error); -static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error); -static zip_cdir_t *_zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error); +static bool _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_cdir_t **cdirp, zip_error_t *error); +static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error); +static cdir_status_t _zip_read_eocd64(zip_cdir_t *cdir, zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error); +static const unsigned char *find_eocd(zip_buffer_t *buffer, const unsigned char *last); ZIP_EXTERN zip_t * @@ -144,6 +156,27 @@ zip_open_from_source(zip_source_t *src, int _flags, zip_error_t *error) { } +static bool +_is_truncated_zip(zip_source_t *src) { + unsigned char data[4]; + /* check if the source is a truncated zip archive: true if yes, no + if not or can't be determined */ + if (zip_source_seek(src, 0, SEEK_SET) < 0) { + return false; + } + + if (zip_source_read(src, data, 4) != 4) { + return false; + } + + if (memcmp(data, LOCAL_MAGIC, 4) == 0) { + /* file starts with a ZIP local header signature */ + return true; + } + return false; +} + + zip_t * _zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) { zip_t *za; @@ -174,6 +207,12 @@ _zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) { if ((cdir = _zip_find_central_dir(za, len)) == NULL) { _zip_error_copy(error, &za->error); + if (zip_error_code_zip(&za->error) == ZIP_ER_NOZIP) { + /* not a zip - find out if it's truncated */ + if (_is_truncated_zip(src)) { + zip_error_set(error, ZIP_ER_TRUNCATED_ZIP, 0); + } + } /* keep src so discard does not get rid of it */ zip_source_keep(src); zip_discard(za); @@ -228,14 +267,14 @@ void _zip_set_open_error(int *zep, const zip_error_t *err, int ze) { if (err) { ze = zip_error_code_zip(err); - switch (zip_error_system_type(err)) { - case ZIP_ET_SYS: - case ZIP_ET_LIBZIP: - errno = zip_error_code_system(err); - break; - - default: - break; + switch (zip_error_system_type(err)) { + case ZIP_ET_SYS: + case ZIP_ET_LIBZIP: + errno = zip_error_code_system(err); + break; + + default: + break; } } @@ -250,37 +289,67 @@ _zip_set_open_error(int *zep, const zip_error_t *err, int ze) { Returns a struct zip_cdir which contains the central directory entries, or NULL if unsuccessful. */ -static zip_cdir_t * -_zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) { +static bool _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_cdir_t **cdirp, zip_error_t *error) { zip_cdir_t *cd; zip_uint16_t comment_len; zip_uint64_t i, left; zip_uint64_t eocd_offset = _zip_buffer_offset(buffer); zip_buffer_t *cd_buffer; + bool eocd64_found = false; - if (_zip_buffer_left(buffer) < EOCDLEN) { - /* not enough bytes left for comment */ - zip_error_set(error, ZIP_ER_NOZIP, 0); - return NULL; - } + *cdirp = NULL; - /* check for end-of-central-dir magic */ - if (memcmp(_zip_buffer_get(buffer, 4), EOCD_MAGIC, 4) != 0) { - zip_error_set(error, ZIP_ER_NOZIP, 0); - return NULL; + if ((cd = _zip_read_eocd(buffer, buf_offset, error)) == NULL) { + return false; } if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) { + eocd64_found = true; _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN); - cd = _zip_read_eocd64(za->src, buffer, buf_offset, za->flags, error); - } - else { - _zip_buffer_set_offset(buffer, eocd_offset); - cd = _zip_read_eocd(buffer, buf_offset, za->flags, error); + switch (_zip_read_eocd64(cd, za->src, buffer, buf_offset, za->flags, error)) { + case CDIR_OK: + break; + + case CDIR_INVALID: + _zip_cdir_free(cd); + return true; + + case CDIR_NOT_FOUND: + _zip_cdir_free(cd); + return false; + } } - if (cd == NULL) - return NULL; + if ((cd->eocd_disk != 0 || cd->this_disk != 0) && !eocd64_found && cd->eocd_disk != cd->this_disk) { + /* If the central directory doesn't start on this disk, we can't check that offset is valid. Check as much as we can instead. */ + if (cd->this_disk < cd->eocd_disk) { + /* Disks before the start of the central directory don't contain an EOCD. */ + _zip_cdir_free(cd); + return false; + } + if (cd->size <= cd->eocd_offset) { + /* The complete central directory would fit on this disk. */ + _zip_cdir_free(cd); + return false; + } + } + + if (!eocd64_found) { + if (cd->this_disk == 0 && cd->eocd_disk == 0 && cd->eocd_offset == 0 && cd->offset == 0 && cd->num_entries == 0) { + /* An empty archive doesn't contain central directory entries. */ + } + else if (!check_magic(cd->offset, buffer, buf_offset, za->src, CENTRAL_MAGIC)) { + _zip_cdir_free(cd); + return false; + } + } + + /* We accept this EOCD as valid and won't search for an earlier one if it is unusable. */ + + if (!check_eocd(cd, za->flags, error)) { + _zip_cdir_free(cd); + return true; + } _zip_buffer_set_offset(buffer, eocd_offset + 20); comment_len = _zip_buffer_get_16(buffer); @@ -289,7 +358,7 @@ _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_err /* cdir spans past EOCD record */ zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD); _zip_cdir_free(cd); - return NULL; + return true; } if (comment_len || (za->open_flags & ZIP_CHECKCONS)) { @@ -298,16 +367,21 @@ _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_err _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN); tail_len = _zip_buffer_left(buffer); - if (tail_len < comment_len || ((za->open_flags & ZIP_CHECKCONS) && tail_len != comment_len)) { - zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_COMMENT_LENGTH_INVALID); - _zip_cdir_free(cd); - return NULL; + if (tail_len != comment_len) { + if (za->open_flags & ZIP_CHECKCONS) { + zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_COMMENT_LENGTH_INVALID); + _zip_cdir_free(cd); + return true; + } + if (tail_len < comment_len) { + comment_len = tail_len; + } } if (comment_len) { if ((cd->comment = _zip_string_new(_zip_buffer_get(buffer, comment_len), comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) { _zip_cdir_free(cd); - return NULL; + return true; } } } @@ -320,12 +394,12 @@ _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_err if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID); _zip_cdir_free(cd); - return NULL; + return true; } if ((cd_buffer = _zip_buffer_new(data, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); _zip_cdir_free(cd); - return NULL; + return true; } } else { @@ -334,17 +408,22 @@ _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_err if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) { zip_error_set_from_source(error, za->src); _zip_cdir_free(cd); - return NULL; + return true; } /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */ if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) { zip_error_set(error, ZIP_ER_NOZIP, 0); _zip_cdir_free(cd); - return NULL; + return true; } } + if (!_zip_cdir_grow(cd, cd->num_entries, error)) { + _zip_cdir_free(cd); + _zip_buffer_free(cd_buffer); + return true; + } left = (zip_uint64_t)cd->size; i = 0; while (left > 0) { @@ -362,31 +441,32 @@ _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_err if (!_zip_cdir_grow(cd, 0x10000, error)) { _zip_cdir_free(cd); _zip_buffer_free(cd_buffer); - return NULL; + return true; } grown = true; } - if ((cd->entry[i].orig = _zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, error)) < 0) { - if (zip_error_code_zip(error) == ZIP_ER_INCONS) { - zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i)); - } - else if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) { + if ((cd->entry[i].orig = _zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, 0, za->open_flags & ZIP_CHECKCONS, error)) < 0) { + if (zip_error_code_zip(error) == ZIP_ER_INCONS) { + zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i)); + } + else if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) { zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_CDIR_ENTRY_INVALID, i)); } _zip_cdir_free(cd); _zip_buffer_free(cd_buffer); - return NULL; + return true; } i++; left -= (zip_uint64_t)entry_size; } + /* If we didn't fill all we grew, cd->num_entries was wrong. */ if (i != cd->nentry || left > 0) { zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_WRONG_ENTRIES_COUNT); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); - return NULL; + return true; } if (za->open_flags & ZIP_CHECKCONS) { @@ -401,7 +481,7 @@ _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_err if (offset < 0) { zip_error_set_from_source(error, za->src); _zip_cdir_free(cd); - return NULL; + return true; } ok = ((zip_uint64_t)offset == cd->offset + cd->size); } @@ -410,12 +490,32 @@ _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_err zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); - return NULL; + return true; } } _zip_buffer_free(cd_buffer); - return cd; + *cdirp = cd; + return true; +} + + +static bool check_magic(zip_uint64_t offset, zip_buffer_t *buffer, zip_uint64_t buffer_offset, zip_source_t *src, const char* magic) { + if (buffer_offset <= offset) { + zip_uint8_t* data; + if (_zip_buffer_set_offset(buffer, offset - buffer_offset) < 0 || (data = _zip_buffer_get(buffer, MAGIC_LEN)) == NULL) { + return false; + } + return memcmp(data, magic, MAGIC_LEN) == 0; + } + else { + zip_uint8_t data[MAGIC_LEN]; + + if (zip_source_seek(src, offset, SEEK_SET) < 0 || zip_source_read(src, data, MAGIC_LEN) != MAGIC_LEN) { + return false; + } + return memcmp(data, magic, MAGIC_LEN) == 0; + } } @@ -430,6 +530,7 @@ _zip_checkcons(zip_t *za, zip_cdir_t *cd, zip_error_t *error) { zip_uint64_t i; zip_uint64_t min, max, j; struct zip_dirent temp; + int detail; _zip_dirent_init(&temp); if (cd->nentry) { @@ -460,10 +561,10 @@ _zip_checkcons(zip_t *za, zip_cdir_t *cd, zip_error_t *error) { return -1; } - if (_zip_dirent_read(&temp, za->src, NULL, true, error) == -1) { - if (zip_error_code_zip(error) == ZIP_ER_INCONS) { - zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i)); - } + if (_zip_dirent_read(&temp, za->src, NULL, true, cd->entry[i].orig->comp_size, true, error) == -1) { + if (zip_error_code_zip(error) == ZIP_ER_INCONS) { + zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i)); + } _zip_dirent_finalize(&temp); return -1; } @@ -479,6 +580,11 @@ _zip_checkcons(zip_t *za, zip_cdir_t *cd, zip_error_t *error) { temp.extra_fields = NULL; _zip_dirent_finalize(&temp); + + if ((detail = zip_dirent_check_consistency(cd->entry[i].orig)) != 0) { + zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(detail, i)); + return -1; + } } return (max - min) < ZIP_INT64_MAX ? (zip_int64_t)(max - min) : ZIP_INT64_MAX; @@ -497,25 +603,23 @@ _zip_headercomp(const zip_dirent_t *central, const zip_dirent_t *local) { and global headers for the bitflags */ || (central->bitflags != local->bitflags) #endif - || (central->comp_method != local->comp_method) || (central->last_mod != local->last_mod) || !_zip_string_equal(central->filename, local->filename)) + || (central->comp_method != local->comp_method) || (central->last_mod.time != local->last_mod.time) || (central->last_mod.date != local->last_mod.date) || !_zip_string_equal(central->filename, local->filename)) return -1; if ((central->crc != local->crc) || (central->comp_size != local->comp_size) || (central->uncomp_size != local->uncomp_size)) { /* InfoZip stores valid values in local header even when data descriptor is used. This is in violation of the appnote. - macOS Archive sets the compressed size even when data descriptor is used ( but not the others), - also in violation of the appnote. - */ - /* if data descriptor is not used, the values must match */ + macOS Archive sets the compressed size even when data descriptor is used ( but not the others), + also in violation of the appnote. + */ + /* if data descriptor is not used, the values must match */ if ((local->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0) { return -1; - } - /* when using a data descriptor, the local header value must be zero or match */ - if ((local->crc != 0 && central->crc != local->crc) || - (local->comp_size != 0 && central->comp_size != local->comp_size) || - (local->uncomp_size != 0 && central->uncomp_size != local->uncomp_size)) { - return -1; - } + } + /* when using a data descriptor, the local header value must be zero or match */ + if ((local->crc != 0 && central->crc != local->crc) || (local->comp_size != 0 && central->comp_size != local->comp_size) || (local->uncomp_size != 0 && central->uncomp_size != local->uncomp_size)) { + return -1; + } } return 0; @@ -568,12 +672,10 @@ _zip_file_exists(zip_source_t *src, zip_error_t *error) { static zip_cdir_t * _zip_find_central_dir(zip_t *za, zip_uint64_t len) { - zip_cdir_t *cdir, *cdirnew; + zip_cdir_t *cdir; const zip_uint8_t *match; zip_int64_t buf_offset; zip_uint64_t buflen; - zip_int64_t a; - zip_int64_t best; zip_error_t error; zip_buffer_t *buffer; @@ -600,7 +702,6 @@ _zip_find_central_dir(zip_t *za, zip_uint64_t len) { return NULL; } - best = -1; cdir = NULL; if (buflen >= CDBUFSIZE) { /* EOCD64 locator is before EOCD, so leave place for it */ @@ -608,165 +709,139 @@ _zip_find_central_dir(zip_t *za, zip_uint64_t len) { } zip_error_set(&error, ZIP_ER_NOZIP, 0); - match = _zip_buffer_get(buffer, 0); - /* The size of buffer never greater than CDBUFSIZE. */ - while (_zip_buffer_left(buffer) >= EOCDLEN && (match = _zip_memmem(match, (size_t)_zip_buffer_left(buffer) - (EOCDLEN - 4), (const unsigned char *)EOCD_MAGIC, 4)) != NULL) { + match = NULL; + while ((match = find_eocd(buffer, match)) != NULL) { _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer))); - if ((cdirnew = _zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &error)) != NULL) { - if (cdir) { - if (best <= 0) { - best = _zip_checkcons(za, cdir, &error); - } - - a = _zip_checkcons(za, cdirnew, &error); - if (best < a) { - _zip_cdir_free(cdir); - cdir = cdirnew; - best = a; - } - else { - _zip_cdir_free(cdirnew); - } + if (_zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &cdir, &error)) { + if (cdir != NULL && (za->open_flags & ZIP_CHECKCONS) && _zip_checkcons(za, cdir, &error) < 0) { + _zip_cdir_free(cdir); + cdir = NULL; } - else { - cdir = cdirnew; - if (za->open_flags & ZIP_CHECKCONS) - best = _zip_checkcons(za, cdir, &error); - else { - best = 0; - } - } - cdirnew = NULL; + break; } - - match++; - _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer))); } _zip_buffer_free(buffer); - if (best < 0) { + if (cdir == NULL) { _zip_error_copy(&za->error, &error); - _zip_cdir_free(cdir); - return NULL; } - return cdir; } -static const unsigned char *_zip_memmem(const unsigned char *big, size_t biglen, const unsigned char *little, size_t littlelen) { +static const unsigned char * +find_eocd(zip_buffer_t *buffer, const unsigned char *last) { + const unsigned char *data = _zip_buffer_data(buffer); const unsigned char *p; - if (littlelen == 0) { - return big; + if (last == NULL) { + last = data + _zip_buffer_size(buffer) - MAGIC_LEN; } - - if (biglen < littlelen) { + else if (last == _zip_buffer_data(buffer)) { return NULL; } - - p = big; - while (true) { - p = (const unsigned char *)memchr(p, little[0], biglen - (littlelen - 1) - (size_t)(p - big)); - if (p == NULL) { - return NULL; - } - if (memcmp(p + 1, little + 1, littlelen - 1) == 0) { - return p; - } - p += 1; + else { + last -= 1; } + + for (p = last; p >= data; p -= 1) { + if (*p == EOCD_MAGIC[0]) { + if (memcmp(p, EOCD_MAGIC, MAGIC_LEN) == 0) { + return p; + } + } + } + + return NULL; } static zip_cdir_t * -_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) { +_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) { zip_cdir_t *cd; - zip_uint64_t i, nentry, size, offset, eocd_offset; if (_zip_buffer_left(buffer) < EOCDLEN) { zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD_LENGTH_INVALID); return NULL; } - eocd_offset = _zip_buffer_offset(buffer); - - _zip_buffer_get(buffer, 4); /* magic already verified */ - - if (_zip_buffer_get_32(buffer) != 0) { - zip_error_set(error, ZIP_ER_MULTIDISK, 0); + if ((cd = _zip_cdir_new(error)) == NULL) { return NULL; } + cd->eocd_offset = buf_offset + _zip_buffer_offset(buffer); + /* This function is only called where EOCD magic was found, so no need to check that here. */ + _zip_buffer_skip(buffer, MAGIC_LEN); + cd->is_zip64 = false; + cd->this_disk = _zip_buffer_get_16(buffer); + cd->eocd_disk = _zip_buffer_get_16(buffer); + /* number of cdir-entries on this disk */ - i = _zip_buffer_get_16(buffer); + cd->disk_entries = _zip_buffer_get_16(buffer); /* number of cdir-entries */ - nentry = _zip_buffer_get_16(buffer); - - if (nentry != i) { - zip_error_set(error, ZIP_ER_NOZIP, 0); - return NULL; - } - - size = _zip_buffer_get_32(buffer); - offset = _zip_buffer_get_32(buffer); - - if (offset + size < offset) { - zip_error_set(error, ZIP_ER_SEEK, EFBIG); - return NULL; - } - - if (offset + size > buf_offset + eocd_offset) { - /* cdir spans past EOCD record */ - zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD); - return NULL; - } - - if ((flags & ZIP_CHECKCONS) && offset + size != buf_offset + eocd_offset) { - zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID); - return NULL; - } - - if ((cd = _zip_cdir_new(nentry, error)) == NULL) - return NULL; - - cd->is_zip64 = false; - cd->size = size; - cd->offset = offset; + cd->num_entries = _zip_buffer_get_16(buffer); + cd->size = _zip_buffer_get_32(buffer); + cd->offset = _zip_buffer_get_32(buffer); return cd; } +static bool +check_eocd(zip_cdir_t *cd, unsigned int flags, zip_error_t *error) { + if (cd->disk_entries != cd->num_entries || cd->this_disk != 0 || cd->eocd_disk != 0) { + zip_error_set(error, ZIP_ER_MULTIDISK, 0); + return false; + } -static zip_cdir_t * -_zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) { - zip_cdir_t *cd; + if (cd->offset + cd->size < cd->offset) { + zip_error_set(error, ZIP_ER_SEEK, EFBIG); + return false; + } + if ((flags & ZIP_CHECKCONS) && cd->offset + cd->size != cd->eocd_offset) { + zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID); + return false; + } + + return true; +} + + +cdir_status_t _zip_read_eocd64(zip_cdir_t *cdir, zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) { zip_uint64_t offset; zip_uint8_t eocd[EOCD64LEN]; zip_uint64_t eocd_offset; zip_uint64_t size, nentry, i, eocdloc_offset; bool free_buffer; - zip_uint32_t num_disks, num_disks64, eocd_disk, eocd_disk64; + zip_uint32_t num_disks, eocd_disk, this_disk; eocdloc_offset = _zip_buffer_offset(buffer); _zip_buffer_get(buffer, 4); /* magic already verified */ - num_disks = _zip_buffer_get_16(buffer); - eocd_disk = _zip_buffer_get_16(buffer); + eocd_disk = _zip_buffer_get_32(buffer); eocd_offset = _zip_buffer_get_64(buffer); + num_disks = _zip_buffer_get_32(buffer); + + if (!check_magic(eocd_offset, buffer, buf_offset, src, EOCD64_MAGIC)) { + return CDIR_NOT_FOUND; + } + + if (num_disks != 1) { + zip_error_set(error, ZIP_ER_MULTIDISK, 0); + return CDIR_INVALID; + } /* valid seek value for start of EOCD */ if (eocd_offset > ZIP_INT64_MAX) { zip_error_set(error, ZIP_ER_SEEK, EFBIG); - return NULL; + return CDIR_INVALID; } /* does EOCD fit before EOCD locator? */ if (eocd_offset + EOCD64LEN > eocdloc_offset + buf_offset) { zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD); - return NULL; + return CDIR_INVALID; } /* make sure current position of buffer is beginning of EOCD */ @@ -777,10 +852,10 @@ _zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offse else { if (zip_source_seek(src, (zip_int64_t)eocd_offset, SEEK_SET) < 0) { zip_error_set_from_source(error, src); - return NULL; + return CDIR_INVALID; } if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) { - return NULL; + return CDIR_INVALID; } free_buffer = true; } @@ -790,7 +865,7 @@ _zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offse if (free_buffer) { _zip_buffer_free(buffer); } - return NULL; + return CDIR_INVALID; } /* size of EOCD */ @@ -802,47 +877,29 @@ _zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offse if (free_buffer) { _zip_buffer_free(buffer); } - return NULL; + return CDIR_INVALID; } _zip_buffer_get(buffer, 4); /* skip version made by/needed */ - num_disks64 = _zip_buffer_get_32(buffer); - eocd_disk64 = _zip_buffer_get_32(buffer); - - /* if eocd values are 0xffff, we have to use eocd64 values. - otherwise, if the values are not the same, it's inconsistent; - in any case, if the value is not 0, we don't support it */ - if (num_disks == 0xffff) { - num_disks = num_disks64; - } - if (eocd_disk == 0xffff) { - eocd_disk = eocd_disk64; - } - if ((flags & ZIP_CHECKCONS) && (eocd_disk != eocd_disk64 || num_disks != num_disks64)) { - zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_MISMATCH); + this_disk = _zip_buffer_get_32(buffer); + if (_zip_buffer_get_32(buffer) != eocd_disk) { + zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_LOCATOR_MISMATCH); if (free_buffer) { _zip_buffer_free(buffer); } - return NULL; - } - if (num_disks != 0 || eocd_disk != 0) { - zip_error_set(error, ZIP_ER_MULTIDISK, 0); - if (free_buffer) { - _zip_buffer_free(buffer); - } - return NULL; + return CDIR_INVALID; } - nentry = _zip_buffer_get_64(buffer); i = _zip_buffer_get_64(buffer); + nentry = _zip_buffer_get_64(buffer); if (nentry != i) { zip_error_set(error, ZIP_ER_MULTIDISK, 0); if (free_buffer) { _zip_buffer_free(buffer); } - return NULL; + return CDIR_INVALID; } size = _zip_buffer_get_64(buffer); @@ -854,7 +911,7 @@ _zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offse if (free_buffer) { _zip_buffer_free(buffer); } - return NULL; + return CDIR_INVALID; } if (free_buffer) { @@ -863,35 +920,33 @@ _zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offse if (offset > ZIP_INT64_MAX || offset + size < offset) { zip_error_set(error, ZIP_ER_SEEK, EFBIG); - return NULL; - } - if (offset + size > buf_offset + eocd_offset) { - /* cdir spans past EOCD record */ - zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD); - return NULL; - } - if ((flags & ZIP_CHECKCONS) && offset + size != buf_offset + eocd_offset) { - zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD); - return NULL; + return CDIR_INVALID; } if (nentry > size / CDENTRYSIZE) { zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_INVALID); - return NULL; + return CDIR_INVALID; } - if ((cd = _zip_cdir_new(nentry, error)) == NULL) - return NULL; + if ((cdir->size != 0xffffffff && cdir->size != size) || (cdir->offset != 0xffffffff && cdir->offset != offset) || (cdir->num_entries != 0xffff && cdir->num_entries != nentry) || (cdir->disk_entries != 0xffff && cdir->disk_entries != i) || (cdir->this_disk != 0xffff && cdir->this_disk != this_disk) || (cdir->eocd_disk != 0xffff && cdir->eocd_disk != eocd_disk)) { + zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_MISMATCH); + return CDIR_INVALID; + } - cd->is_zip64 = true; - cd->size = size; - cd->offset = offset; + cdir->is_zip64 = true; + cdir->size = size; + cdir->offset = offset; + cdir->disk_entries = i; + cdir->num_entries = nentry; + cdir->this_disk = this_disk; + cdir->eocd_disk = eocd_disk; - return cd; + return CDIR_OK; } -static int decode_hex(char c) { +static int +decode_hex(char c) { if (c >= '0' && c <= '9') { return c - '0'; } @@ -906,17 +961,17 @@ static int decode_hex(char c) { /* _zip_check_torrentzip: check whether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */ -static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir) { +static void +zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir) { zip_uint32_t crc_should; - char buf[8+1]; + char buf[8 + 1]; size_t i; if (cdir == NULL) { return; } - if (_zip_string_length(cdir->comment) != TORRENTZIP_SIGNATURE_LENGTH + TORRENTZIP_CRC_LENGTH - || strncmp((const char *)cdir->comment->raw, TORRENTZIP_SIGNATURE, TORRENTZIP_SIGNATURE_LENGTH) != 0) + if (_zip_string_length(cdir->comment) != TORRENTZIP_SIGNATURE_LENGTH + TORRENTZIP_CRC_LENGTH || strncmp((const char *)cdir->comment->raw, TORRENTZIP_SIGNATURE, TORRENTZIP_SIGNATURE_LENGTH) != 0) return; memcpy(buf, cdir->comment->raw + TORRENTZIP_SIGNATURE_LENGTH, TORRENTZIP_CRC_LENGTH); @@ -934,8 +989,8 @@ static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir) { { zip_stat_t st; - zip_source_t* src_window; - zip_source_t* src_crc; + zip_source_t *src_window; + zip_source_t *src_crc; zip_uint8_t buffer[512]; zip_int64_t ret; @@ -943,7 +998,7 @@ static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir) { st.valid |= ZIP_STAT_SIZE | ZIP_STAT_CRC; st.size = cdir->size; st.crc = crc_should; - if ((src_window = _zip_source_window_new(za->src, cdir->offset, cdir->size, &st, 0, NULL, NULL, 0, false, NULL)) == NULL) { + if ((src_window = _zip_source_window_new(za->src, cdir->offset, cdir->size, &st, 0, NULL, NULL, NULL, 0, false, NULL)) == NULL) { return; } if ((src_crc = zip_source_crc_create(src_window, 1, NULL)) == NULL) { diff --git a/zip_pkware.c b/zip_pkware.c index 6a8c9fc..ef3a30b 100644 --- a/zip_pkware.c +++ b/zip_pkware.c @@ -1,6 +1,6 @@ /* zip_pkware.c -- Traditional PKWARE de/encryption backend routines - Copyright (C) 2009-2020 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_progress.c b/zip_progress.c index e080514..5693d7a 100644 --- a/zip_progress.c +++ b/zip_progress.c @@ -1,6 +1,6 @@ /* zip_progress.c -- progress reporting - Copyright (C) 2017-2020 Dieter Baron and Thomas Klausner + Copyright (C) 2017-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -191,7 +191,7 @@ _zip_progress_update(zip_progress_t *progress, double sub_current) { if (progress->callback_progress != NULL) { current = ZIP_MIN(ZIP_MAX(sub_current, 0.0), 1.0) * (progress->end - progress->start) + progress->start; - if (current - progress->last_update > progress->precision) { + if (current - progress->last_update > progress->precision || (progress->last_update < 1 && current == 1)) { progress->callback_progress(progress->za, current, progress->ud_progress); progress->last_update = current; } diff --git a/zip_random_win32.c b/zip_random_win32.c index 966b9a5..209ca4b 100644 --- a/zip_random_win32.c +++ b/zip_random_win32.c @@ -1,6 +1,6 @@ /* zip_random_win32.c -- fill the user's buffer with random stuff (Windows version) - Copyright (C) 2016-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2016-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -74,6 +74,7 @@ zip_random_uint32(void) { if (!seeded) { srand((unsigned int)time(NULL)); + seeded = true; } return (zip_uint32_t)rand(); diff --git a/zip_realloc.c b/zip_realloc.c new file mode 100644 index 0000000..81948de --- /dev/null +++ b/zip_realloc.c @@ -0,0 +1,62 @@ +/* + zip_realloc.c -- reallocate with additional elements + Copyright (C) 2009-2025 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#include "zipint.h" + +bool zip_realloc(void **memory, zip_uint64_t *alloced_elements, zip_uint64_t element_size, zip_uint64_t additional_elements, zip_error_t *error) { + zip_uint64_t new_alloced_elements; + void *new_memory; + + if (additional_elements == 0) { + return true; + } + + new_alloced_elements = *alloced_elements + additional_elements; + + if (new_alloced_elements < additional_elements || new_alloced_elements > SIZE_MAX / element_size) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return false; + } + + if ((new_memory = realloc(*memory, (size_t)(new_alloced_elements * element_size))) == NULL) { + zip_error_set(error, ZIP_ER_MEMORY, 0); + return false; + } + + *memory = new_memory; + *alloced_elements = new_alloced_elements; + + return true; +} diff --git a/zip_rename.c b/zip_rename.c index c89b06c..b356de4 100644 --- a/zip_rename.c +++ b/zip_rename.c @@ -1,6 +1,6 @@ /* zip_rename.c -- rename file in zip archive - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_replace.c b/zip_replace.c index 96c083c..549306a 100644 --- a/zip_replace.c +++ b/zip_replace.c @@ -1,6 +1,6 @@ /* zip_replace.c -- replace file via callback function - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_set_archive_comment.c b/zip_set_archive_comment.c index 7d06688..2c75bda 100644 --- a/zip_set_archive_comment.c +++ b/zip_set_archive_comment.c @@ -1,6 +1,6 @@ /* zip_set_archive_comment.c -- set archive comment - Copyright (C) 2006-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_set_archive_flag.c b/zip_set_archive_flag.c index ec6a86d..7533ac5 100644 --- a/zip_set_archive_flag.c +++ b/zip_set_archive_flag.c @@ -1,6 +1,6 @@ /* zip_get_archive_flag.c -- set archive global flag - Copyright (C) 2008-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2008-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -57,7 +57,8 @@ zip_set_archive_flag(zip_t *za, zip_flags_t flag, int value) { return 0; } - if (ZIP_IS_RDONLY(za)) { + /* Allow removing ZIP_AFL_RDONLY if manually set, not if archive was opened read-only. */ + if (za->flags & ZIP_AFL_RDONLY) { zip_error_set(&za->error, ZIP_ER_RDONLY, 0); return -1; } diff --git a/zip_set_default_password.c b/zip_set_default_password.c index 4bab513..07b3a06 100644 --- a/zip_set_default_password.c +++ b/zip_set_default_password.c @@ -1,6 +1,6 @@ /* zip_set_default_password.c -- set default password for decryption - Copyright (C) 2009-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_set_file_comment.c b/zip_set_file_comment.c index 5d2b0b8..b587ae9 100644 --- a/zip_set_file_comment.c +++ b/zip_set_file_comment.c @@ -1,6 +1,6 @@ /* zip_set_file_comment.c -- set comment for file in archive - Copyright (C) 2006-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_set_file_compression.c b/zip_set_file_compression.c index a193bb7..45924d6 100644 --- a/zip_set_file_compression.c +++ b/zip_set_file_compression.c @@ -1,6 +1,6 @@ /* zip_set_file_compression.c -- set compression for file in archive - Copyright (C) 2012-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2012-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -38,7 +38,6 @@ ZIP_EXTERN int zip_set_file_compression(zip_t *za, zip_uint64_t idx, zip_int32_t method, zip_uint32_t flags) { zip_entry_t *e; - zip_int32_t old_method; if (idx >= za->nentry) { zip_error_set(&za->error, ZIP_ER_INVAL, 0); @@ -61,35 +60,21 @@ zip_set_file_compression(zip_t *za, zip_uint64_t idx, zip_int32_t method, zip_ui e = za->entry + idx; - old_method = (e->orig == NULL ? ZIP_CM_DEFAULT : e->orig->comp_method); - /* TODO: do we want to recompress if level is set? Only if it's * different than what bit flags tell us, but those are not * defined for all compression methods, or not directly mappable * to levels */ - if (method == old_method) { - if (e->changes) { - e->changes->changed &= ~ZIP_DIRENT_COMP_METHOD; - e->changes->compression_level = 0; - if (e->changes->changed == 0) { - _zip_dirent_free(e->changes); - e->changes = NULL; - } + if (e->changes == NULL) { + if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + return -1; } } - else { - if (e->changes == NULL) { - if ((e->changes = _zip_dirent_clone(e->orig)) == NULL) { - zip_error_set(&za->error, ZIP_ER_MEMORY, 0); - return -1; - } - } - e->changes->comp_method = method; - e->changes->compression_level = (zip_uint16_t)flags; - e->changes->changed |= ZIP_DIRENT_COMP_METHOD; - } + e->changes->comp_method = method; + e->changes->compression_level = (zip_uint16_t)flags; + e->changes->changed |= ZIP_DIRENT_COMP_METHOD; return 0; } diff --git a/zip_set_name.c b/zip_set_name.c index f1bf703..0087af1 100644 --- a/zip_set_name.c +++ b/zip_set_name.c @@ -1,6 +1,6 @@ /* zip_set_name.c -- rename helper function - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_accept_empty.c b/zip_source_accept_empty.c index e772aee..be31943 100644 --- a/zip_source_accept_empty.c +++ b/zip_source_accept_empty.c @@ -1,6 +1,6 @@ /* zip_source_accept_empty.c -- if empty source is a valid archive - Copyright (C) 2019-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2019-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_begin_write.c b/zip_source_begin_write.c index 4a9d5d5..2724335 100644 --- a/zip_source_begin_write.c +++ b/zip_source_begin_write.c @@ -1,6 +1,6 @@ /* zip_source_begin_write.c -- start a new file for writing - Copyright (C) 2014-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2014-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_begin_write_cloning.c b/zip_source_begin_write_cloning.c index df195fc..67d3475 100644 --- a/zip_source_begin_write_cloning.c +++ b/zip_source_begin_write_cloning.c @@ -1,6 +1,6 @@ /* zip_source_begin_write_cloning.c -- clone part of file for writing - Copyright (C) 2017-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2017-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_buffer.c b/zip_source_buffer.c index 5667293..0464cb0 100644 --- a/zip_source_buffer.c +++ b/zip_source_buffer.c @@ -1,6 +1,6 @@ /* zip_source_buffer.c -- create zip data source from buffer - Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -340,6 +340,7 @@ buffer_clone(buffer_t *buffer, zip_uint64_t offset, zip_error_t *error) { fragment_offset = offset - buffer->fragment_offsets[fragment]; if (fragment_offset == 0) { + /* We can't be at beginning of fragment zero if offset > 0. */ fragment--; fragment_offset = buffer->fragments[fragment].length; } @@ -427,32 +428,23 @@ buffer_free(buffer_t *buffer) { static bool buffer_grow_fragments(buffer_t *buffer, zip_uint64_t capacity, zip_error_t *error) { - zip_buffer_fragment_t *fragments; - zip_uint64_t *offsets; + zip_uint64_t additional_fragments; + zip_uint64_t offset_capacity = buffer->fragments_capacity + 1; - if (capacity < buffer->fragments_capacity) { + if (capacity <= buffer->fragments_capacity) { return true; } - zip_uint64_t fragments_size = sizeof(buffer->fragments[0]) * capacity; - zip_uint64_t offsets_size = sizeof(buffer->fragment_offsets[0]) * (capacity + 1); + additional_fragments = capacity - buffer->fragments_capacity; - if (capacity == ZIP_UINT64_MAX || fragments_size < capacity || fragments_size > SIZE_MAX|| offsets_size < capacity || offsets_size > SIZE_MAX) { - zip_error_set(error, ZIP_ER_MEMORY, 0); + if (!ZIP_REALLOC(buffer->fragments, buffer->fragments_capacity, additional_fragments, error)) { return false; } - - if ((fragments = realloc(buffer->fragments, (size_t)fragments_size)) == NULL) { - zip_error_set(error, ZIP_ER_MEMORY, 0); + /* The size of both buffer->fragments and buffer->fragment_offsets is stored in buffer->fragments_capacity, so use a temporary capacity variable here for reallocating buffer->fragment_offsets. */ + if (!ZIP_REALLOC(buffer->fragment_offsets, offset_capacity, additional_fragments, error)) { + buffer->fragments_capacity -= additional_fragments; return false; } - buffer->fragments = fragments; - if ((offsets = realloc(buffer->fragment_offsets, (size_t)offsets_size)) == NULL) { - zip_error_set(error, ZIP_ER_MEMORY, 0); - return false; - } - buffer->fragment_offsets = offsets; - buffer->fragments_capacity = capacity; return true; } diff --git a/zip_source_call.c b/zip_source_call.c index 8c98fc2..bc13c3b 100644 --- a/zip_source_call.c +++ b/zip_source_call.c @@ -1,6 +1,6 @@ /* zip_source_call.c -- invoke callback command on zip_source - Copyright (C) 2009-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_close.c b/zip_source_close.c index f4f3ff2..f1ae6f3 100644 --- a/zip_source_close.c +++ b/zip_source_close.c @@ -1,6 +1,6 @@ /* zip_source_close.c -- close zip_source (stop reading) - Copyright (C) 2009-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_commit_write.c b/zip_source_commit_write.c index d7f567b..ca7563c 100644 --- a/zip_source_commit_write.c +++ b/zip_source_commit_write.c @@ -1,6 +1,6 @@ /* zip_source_commit_write.c -- commit changes to file - Copyright (C) 2014-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2014-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_compress.c b/zip_source_compress.c index 3cf709f..3330796 100644 --- a/zip_source_compress.c +++ b/zip_source_compress.c @@ -1,6 +1,6 @@ /* zip_source_compress.c -- (de)compression routines - Copyright (C) 2017-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2017-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -44,6 +44,7 @@ struct context { bool can_store; bool is_stored; /* only valid if end_of_stream is true */ bool compress; + bool check_consistency; zip_int32_t method; zip_uint64_t size; @@ -86,11 +87,10 @@ static size_t implementations_size = sizeof(implementations) / sizeof(implementa static zip_source_t *compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, zip_uint32_t compression_flags); static zip_int64_t compress_callback(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t); static void context_free(struct context *ctx); -static struct context *context_new(zip_int32_t method, bool compress, zip_uint32_t compression_flags, zip_compression_algorithm_t *algorithm); +static struct context *context_new(zip_int32_t method, bool compress, zip_uint32_t compression_flags, zip_compression_algorithm_t *algorithm, bool check_consistency); static zip_int64_t compress_read(zip_source_t *, struct context *, void *, zip_uint64_t); -zip_compression_algorithm_t * -_zip_get_compression_algorithm(zip_int32_t method, bool compress) { +zip_compression_algorithm_t *_zip_get_compression_algorithm(zip_int32_t method, bool compress) { size_t i; zip_uint16_t real_method = ZIP_CM_ACTUAL(method); @@ -108,16 +108,14 @@ _zip_get_compression_algorithm(zip_int32_t method, bool compress) { return NULL; } -ZIP_EXTERN int -zip_compression_method_supported(zip_int32_t method, int compress) { +ZIP_EXTERN int zip_compression_method_supported(zip_int32_t method, int compress) { if (method == ZIP_CM_STORE) { return 1; } return _zip_get_compression_algorithm(method, compress) != NULL; } -zip_source_t * -zip_source_compress(zip_t *za, zip_source_t *src, zip_int32_t method, zip_uint32_t compression_flags) { +zip_source_t *zip_source_compress(zip_t *za, zip_source_t *src, zip_int32_t method, zip_uint32_t compression_flags) { return compression_source_new(za, src, method, true, compression_flags); } @@ -127,8 +125,7 @@ zip_source_decompress(zip_t *za, zip_source_t *src, zip_int32_t method) { } -static zip_source_t * -compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, zip_uint32_t compression_flags) { +static zip_source_t *compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, zip_uint32_t compression_flags) { struct context *ctx; zip_source_t *s2; zip_compression_algorithm_t *algorithm = NULL; @@ -143,7 +140,7 @@ compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool co return NULL; } - if ((ctx = context_new(method, compress, compression_flags, algorithm)) == NULL) { + if ((ctx = context_new(method, compress, compression_flags, algorithm, za->open_flags & ZIP_CHECKCONS)) == NULL) { zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return NULL; } @@ -157,8 +154,7 @@ compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool co } -static struct context * -context_new(zip_int32_t method, bool compress, zip_uint32_t compression_flags, zip_compression_algorithm_t *algorithm) { +static struct context *context_new(zip_int32_t method, bool compress, zip_uint32_t compression_flags, zip_compression_algorithm_t *algorithm, bool check_consistency) { struct context *ctx; if ((ctx = (struct context *)malloc(sizeof(*ctx))) == NULL) { @@ -172,6 +168,7 @@ context_new(zip_int32_t method, bool compress, zip_uint32_t compression_flags, z ctx->end_of_input = false; ctx->end_of_stream = false; ctx->is_stored = false; + ctx->check_consistency = check_consistency; if ((ctx->ud = ctx->algorithm->allocate(ZIP_CM_ACTUAL(method), compression_flags, &ctx->error)) == NULL) { zip_error_fini(&ctx->error); @@ -228,7 +225,23 @@ compress_read(zip_source_t *src, struct context *ctx, void *data, zip_uint64_t l ctx->end_of_stream = true; if (!ctx->end_of_input) { - /* TODO: garbage after stream, or compression ended before all data read */ + n = zip_source_read(src, ctx->buffer, 1); + if (n < 0) { + zip_error_set_from_source(&ctx->error, src); + end = true; + break; + } + else if (n == 0) { + ctx->end_of_input = true; + n = ctx->algorithm->end_of_input(ctx->ud) ? 1 : 0; + } + + if (n > 0 && ctx->check_consistency) { + /* garbage after stream, or compression ended before all data read */ + zip_error_set(&ctx->error, ZIP_ER_INCONS, ZIP_ER_DETAIL_COMPRESSED_DATA_TRAILING_GARBAGE); + end = true; + break; + } } if (ctx->first_read < 0) { diff --git a/zip_source_crc.c b/zip_source_crc.c index 435a084..931a729 100644 --- a/zip_source_crc.c +++ b/zip_source_crc.c @@ -1,6 +1,6 @@ /* zip_source_crc.c -- pass-through source that calculates CRC32 and size - Copyright (C) 2009-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_error.c b/zip_source_error.c index dc7fa20..774eec0 100644 --- a/zip_source_error.c +++ b/zip_source_error.c @@ -1,6 +1,6 @@ /* zip_source_error.c -- get last error from zip_source - Copyright (C) 2009-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_file.h b/zip_source_file.h index cca9fd2..de07aec 100644 --- a/zip_source_file.h +++ b/zip_source_file.h @@ -1,6 +1,9 @@ +#ifndef _HAD_ZIP_SOURCE_FILE_H +#define _HAD_ZIP_SOURCE_FILE_H + /* zip_source_file.h -- header for common file operations - Copyright (C) 2020-2022 Dieter Baron and Thomas Klausner + Copyright (C) 2020-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -88,3 +91,5 @@ struct zip_source_file_operations { }; zip_source_t *zip_source_file_common_new(const char *fname, void *file, zip_uint64_t start, zip_int64_t len, const zip_stat_t *st, zip_source_file_operations_t *ops, void *ops_userdata, zip_error_t *error); + +#endif /* _HAD_ZIP_SOURCE_FILE_H */ diff --git a/zip_source_file_common.c b/zip_source_file_common.c index a2a46a4..99f893e 100644 --- a/zip_source_file_common.c +++ b/zip_source_file_common.c @@ -1,6 +1,6 @@ /* zip_source_file_common.c -- create data source from file - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -54,6 +54,7 @@ zip_source_file_common_new(const char *fname, void *file, zip_uint64_t start, zi zip_source_file_context_t *ctx; zip_source_t *zs; zip_source_file_stat_t sb; + zip_uint64_t length; if (ops == NULL) { zip_error_set(error, ZIP_ER_INVAL, 0); @@ -82,10 +83,17 @@ zip_source_file_common_new(const char *fname, void *file, zip_uint64_t start, zi } if (len < 0) { - len = 0; + if (len == -1) { + len = ZIP_LENGTH_TO_END; + } + // TODO: return ZIP_ER_INVAL if len != ZIP_LENGTH_UNCHECKED? + length = 0; + } + else { + length = (zip_uint64_t)len; } - if (start > ZIP_INT64_MAX || start + (zip_uint64_t)len < start) { + if (start > ZIP_INT64_MAX || start + length < start) { zip_error_set(error, ZIP_ER_INVAL, 0); return NULL; } @@ -107,7 +115,7 @@ zip_source_file_common_new(const char *fname, void *file, zip_uint64_t start, zi } ctx->f = file; ctx->start = start; - ctx->len = (zip_uint64_t)len; + ctx->len = length; if (st) { (void)memcpy_s(&ctx->st, sizeof(ctx->st), st, sizeof(*st)); ctx->st.name = NULL; @@ -169,9 +177,11 @@ zip_source_file_common_new(const char *fname, void *file, zip_uint64_t start, zi } if (ctx->len == 0) { - ctx->len = sb.size - ctx->start; - ctx->st.size = ctx->len; - ctx->st.valid |= ZIP_STAT_SIZE; + if (len != ZIP_LENGTH_UNCHECKED) { + ctx->len = sb.size - ctx->start; + ctx->st.size = ctx->len; + ctx->st.valid |= ZIP_STAT_SIZE; + } /* when using a partial file, don't allow writing */ if (ctx->fname && start == 0 && ops->write != NULL) { diff --git a/zip_source_file_stdio.c b/zip_source_file_stdio.c index 7ed9167..6e98517 100644 --- a/zip_source_file_stdio.c +++ b/zip_source_file_stdio.c @@ -1,6 +1,6 @@ /* zip_source_file_stdio.c -- read-only stdio file source implementation - Copyright (C) 2020 Dieter Baron and Thomas Klausner + Copyright (C) 2020-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -39,7 +39,6 @@ #include #include #include -#include #ifdef _WIN32 #ifndef S_IWUSR @@ -78,7 +77,7 @@ zip_source_filep(zip_t *za, FILE *file, zip_uint64_t start, zip_int64_t len) { ZIP_EXTERN zip_source_t * zip_source_filep_create(FILE *file, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { - if (file == NULL || length < -1) { + if (file == NULL || length < ZIP_LENGTH_UNCHECKED) { zip_error_set(error, ZIP_ER_INVAL, 0); return NULL; } @@ -96,9 +95,11 @@ _zip_stdio_op_close(zip_source_file_context_t *ctx) { zip_int64_t _zip_stdio_op_read(zip_source_file_context_t *ctx, void *buf, zip_uint64_t len) { size_t i; +#if SIZE_MAX < ZIP_UINT64_MAX if (len > SIZE_MAX) { len = SIZE_MAX; } +#endif if ((i = fread(buf, 1, (size_t)len, ctx->f)) == 0) { if (ferror((FILE *)ctx->f)) { @@ -120,7 +121,7 @@ _zip_stdio_op_seek(zip_source_file_context_t *ctx, void *f, zip_int64_t offset, } #endif - if (fseeko((FILE *)f, (off_t)offset, whence) < 0) { + if (zip_os_fseek((FILE *)f, (zip_off_t)offset, whence) < 0) { zip_error_set(&ctx->error, ZIP_ER_SEEK, errno); return false; } @@ -130,15 +131,15 @@ _zip_stdio_op_seek(zip_source_file_context_t *ctx, void *f, zip_int64_t offset, bool _zip_stdio_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st) { - struct stat sb; + zip_os_stat_t sb; int ret; if (ctx->fname) { - ret = stat(ctx->fname, &sb); + ret = zip_os_stat(ctx->fname, &sb); } else { - ret = fstat(fileno((FILE *)ctx->f), &sb); + ret = zip_os_fstat(fileno((FILE *)ctx->f), &sb); } if (ret < 0) { @@ -168,7 +169,7 @@ _zip_stdio_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t *st) { zip_int64_t _zip_stdio_op_tell(zip_source_file_context_t *ctx, void *f) { - off_t offset = ftello((FILE *)f); + zip_off_t offset = zip_os_ftell((FILE *)f); if (offset < 0) { zip_error_set(&ctx->error, ZIP_ER_SEEK, errno); diff --git a/zip_source_file_stdio.h b/zip_source_file_stdio.h index 802e607..417a28c 100644 --- a/zip_source_file_stdio.h +++ b/zip_source_file_stdio.h @@ -3,7 +3,7 @@ /* zip_source_file_stdio.h -- common header for stdio file implementation - Copyright (C) 2020 Dieter Baron and Thomas Klausner + Copyright (C) 2020-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_file_win32.c b/zip_source_file_win32.c index 4708dc8..aa66976 100644 --- a/zip_source_file_win32.c +++ b/zip_source_file_win32.c @@ -1,6 +1,6 @@ /* zip_source_file_win32.c -- read-only Windows file source implementation - Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -69,7 +69,7 @@ zip_source_win32handle(zip_t *za, HANDLE h, zip_uint64_t start, zip_int64_t len) ZIP_EXTERN zip_source_t * zip_source_win32handle_create(HANDLE h, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { - if (h == INVALID_HANDLE_VALUE || length < -1) { + if (h == INVALID_HANDLE_VALUE || length < ZIP_LENGTH_UNCHECKED) { zip_error_set(error, ZIP_ER_INVAL, 0); return NULL; } diff --git a/zip_source_file_win32.h b/zip_source_file_win32.h index d86069e..da691f0 100644 --- a/zip_source_file_win32.h +++ b/zip_source_file_win32.h @@ -3,7 +3,7 @@ /* zip_source_file_win32.h -- common header for Windows file implementation - Copyright (C) 2020 Dieter Baron and Thomas Klausner + Copyright (C) 2020-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -59,6 +59,7 @@ struct zip_win32_file_operations { BOOL(__stdcall *move_file)(const void *from, const void *to, DWORD flags); BOOL(__stdcall *set_file_attributes)(const void *name, DWORD attributes); char *(*string_duplicate)(const char *string); + HANDLE(__stdcall *find_first_file)(const void *name, void *data); }; typedef struct zip_win32_file_operations zip_win32_file_operations_t; @@ -73,12 +74,4 @@ zip_int64_t _zip_win32_op_tell(zip_source_file_context_t *ctx, void *f); bool _zip_filetime_to_time_t(FILETIME ft, time_t *t); int _zip_win32_error_to_errno(DWORD win32err); -#ifdef __clang__ -#define DONT_WARN_INCOMPATIBLE_FN_PTR_BEGIN _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wincompatible-function-pointer-types\"") -#define DONT_WARN_INCOMPATIBLE_FN_PTR_END _Pragma("GCC diagnostic pop") -#else -#define DONT_WARN_INCOMPATIBLE_FN_PTR_BEGIN -#define DONT_WARN_INCOMPATIBLE_FN_PTR_END -#endif - #endif /* _HAD_ZIP_SOURCE_FILE_WIN32_H */ diff --git a/zip_source_file_win32_ansi.c b/zip_source_file_win32_ansi.c index 31ea48a..f10f8ae 100644 --- a/zip_source_file_win32_ansi.c +++ b/zip_source_file_win32_ansi.c @@ -1,6 +1,6 @@ /* zip_source_file_win32_ansi.c -- source for Windows file opened by ANSI name - Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -34,24 +34,28 @@ #include "zip_source_file_win32.h" static char *ansi_allocate_tempname(const char *name, size_t extra_chars, size_t *lengthp); +static HANDLE __stdcall ansi_create_file(const void *name, DWORD access, DWORD share_mode, PSECURITY_ATTRIBUTES security_attributes, DWORD creation_disposition, DWORD file_attributes, HANDLE template_file); +static BOOL __stdcall ansi_delete_file(const void *name); +static DWORD __stdcall ansi_get_file_attributes(const void *name); +static BOOL __stdcall ansi_get_file_attributes_ex(const void *name, GET_FILEEX_INFO_LEVELS info_level, void *information); static void ansi_make_tempname(char *buf, size_t len, const char *name, zip_uint32_t i); +static BOOL __stdcall ansi_move_file(const void *from, const void *to, DWORD flags); +static BOOL __stdcall ansi_set_file_attributes(const void *name, DWORD attributes); +static HANDLE __stdcall ansi_find_first_file(const void *name, void* data); /* clang-format off */ -DONT_WARN_INCOMPATIBLE_FN_PTR_BEGIN - zip_win32_file_operations_t ops_ansi = { ansi_allocate_tempname, - CreateFileA, - DeleteFileA, - GetFileAttributesA, - GetFileAttributesExA, + ansi_create_file, + ansi_delete_file, + ansi_get_file_attributes, + ansi_get_file_attributes_ex, ansi_make_tempname, - MoveFileExA, - SetFileAttributesA, - strdup + ansi_move_file, + ansi_set_file_attributes, + strdup, + ansi_find_first_file, }; - -DONT_WARN_INCOMPATIBLE_FN_PTR_END /* clang-format on */ ZIP_EXTERN zip_source_t * @@ -65,7 +69,7 @@ zip_source_win32a(zip_t *za, const char *fname, zip_uint64_t start, zip_int64_t ZIP_EXTERN zip_source_t * zip_source_win32a_create(const char *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { - if (fname == NULL || length < -1) { + if (fname == NULL || length < ZIP_LENGTH_UNCHECKED) { zip_error_set(error, ZIP_ER_INVAL, 0); return NULL; } @@ -80,8 +84,49 @@ ansi_allocate_tempname(const char *name, size_t extra_chars, size_t *lengthp) { return (char *)malloc(*lengthp); } +static HANDLE __stdcall +ansi_create_file(const void *name, DWORD access, DWORD share_mode, PSECURITY_ATTRIBUTES security_attributes, DWORD creation_disposition, DWORD file_attributes, HANDLE template_file) +{ + return CreateFileA((const char *)name, access, share_mode, security_attributes, creation_disposition, file_attributes, template_file); +} + +static BOOL __stdcall +ansi_delete_file(const void *name) +{ + return DeleteFileA((const char *)name); +} + +static DWORD __stdcall +ansi_get_file_attributes(const void *name) +{ + return GetFileAttributesA((const char *)name); +} + +static BOOL __stdcall +ansi_get_file_attributes_ex(const void *name, GET_FILEEX_INFO_LEVELS info_level, void *information) +{ + return GetFileAttributesExA((const char *)name, info_level, information); +} static void ansi_make_tempname(char *buf, size_t len, const char *name, zip_uint32_t i) { snprintf_s(buf, len, "%s.%08x", name, i); } + +static BOOL __stdcall +ansi_move_file(const void *from, const void *to, DWORD flags) +{ + return MoveFileExA((const char *)from, (const char *)to, flags); +} + +static BOOL __stdcall +ansi_set_file_attributes(const void *name, DWORD attributes) +{ + return SetFileAttributesA((const char *)name, attributes); +} + +static HANDLE __stdcall +ansi_find_first_file(const void *name, void *data) +{ + return FindFirstFileA((const char *)name, data); +} diff --git a/zip_source_file_win32_named.c b/zip_source_file_win32_named.c index 855e605..5219d6a 100644 --- a/zip_source_file_win32_named.c +++ b/zip_source_file_win32_named.c @@ -1,6 +1,6 @@ /* zip_source_file_win32_named.c -- source for Windows file opened by name - Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -99,25 +99,24 @@ _zip_win32_named_op_create_temp_output(zip_source_file_context_t *ctx) { zip_uint32_t value, i; HANDLE th = INVALID_HANDLE_VALUE; - PSECURITY_DESCRIPTOR psd = NULL; PSECURITY_ATTRIBUTES psa = NULL; + PSECURITY_DESCRIPTOR psd = NULL; +#ifdef HAVE_GETSECURITYINFO SECURITY_ATTRIBUTES sa; - SECURITY_INFORMATION si; - DWORD success; - PACL dacl = NULL; +#endif char *tempname = NULL; size_t tempname_size = 0; +#ifdef HAVE_GETSECURITYINFO if ((HANDLE)ctx->f != INVALID_HANDLE_VALUE && GetFileType((HANDLE)ctx->f) == FILE_TYPE_DISK) { - si = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION; - success = GetSecurityInfo((HANDLE)ctx->f, SE_FILE_OBJECT, si, NULL, NULL, &dacl, NULL, &psd); - if (success == ERROR_SUCCESS) { + if (GetSecurityInfo((HANDLE)ctx->f, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, NULL, NULL, &psd) == ERROR_SUCCESS) { sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.bInheritHandle = FALSE; sa.lpSecurityDescriptor = psd; psa = &sa; } } +#endif #ifndef MS_UWP value = GetTickCount(); @@ -210,8 +209,18 @@ _zip_win32_named_op_stat(zip_source_file_context_t *ctx, zip_source_file_stat_t st->regular_file = false; if (file_attributes.dwFileAttributes != INVALID_FILE_ATTRIBUTES) { - if ((file_attributes.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)) == 0) { - st->regular_file = true; + if ((file_attributes.dwFileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE)) == 0) { + if (file_attributes.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + WIN32_FIND_DATA find_data; + /* Deduplication on Windows replaces files with reparse points; + * accept them as regular files. */ + if (file_ops->find_first_file(ctx->fname, &find_data) != INVALID_HANDLE_VALUE) { + st->regular_file = (find_data.dwReserved0 == IO_REPARSE_TAG_DEDUP); + } + } + else { + st->regular_file = true; + } } } diff --git a/zip_source_file_win32_utf16.c b/zip_source_file_win32_utf16.c index b2f2341..73e2566 100644 --- a/zip_source_file_win32_utf16.c +++ b/zip_source_file_win32_utf16.c @@ -1,6 +1,6 @@ /* zip_source_file_win32_utf16.c -- source for Windows file opened by UTF-16 name - Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -34,26 +34,30 @@ #include "zip_source_file_win32.h" static char *utf16_allocate_tempname(const char *name, size_t extra_chars, size_t *lengthp); -static HANDLE __stdcall utf16_create_file(const char *name, DWORD access, DWORD share_mode, PSECURITY_ATTRIBUTES security_attributes, DWORD creation_disposition, DWORD file_attributes, HANDLE template_file); +static HANDLE __stdcall utf16_create_file(const void *name, DWORD access, DWORD share_mode, PSECURITY_ATTRIBUTES security_attributes, DWORD creation_disposition, DWORD file_attributes, HANDLE template_file); +static BOOL __stdcall utf16_delete_file(const void *name); +static DWORD __stdcall utf16_get_file_attributes(const void *name); +static BOOL __stdcall utf16_get_file_attributes_ex(const void *name, GET_FILEEX_INFO_LEVELS info_level, void *information); static void utf16_make_tempname(char *buf, size_t len, const char *name, zip_uint32_t i); +static BOOL __stdcall utf16_move_file(const void *from, const void *to, DWORD flags); +static BOOL __stdcall utf16_set_file_attributes(const void *name, DWORD attributes); static char *utf16_strdup(const char *string); +static HANDLE __stdcall utf16_find_first_file(const void *name, void* data); + /* clang-format off */ -DONT_WARN_INCOMPATIBLE_FN_PTR_BEGIN - zip_win32_file_operations_t ops_utf16 = { utf16_allocate_tempname, utf16_create_file, - DeleteFileW, - GetFileAttributesW, - GetFileAttributesExW, + utf16_delete_file, + utf16_get_file_attributes, + utf16_get_file_attributes_ex, utf16_make_tempname, - MoveFileExW, - SetFileAttributesW, - utf16_strdup + utf16_move_file, + utf16_set_file_attributes, + utf16_strdup, + utf16_find_first_file }; - -DONT_WARN_INCOMPATIBLE_FN_PTR_END /* clang-format on */ ZIP_EXTERN zip_source_t * @@ -67,7 +71,7 @@ zip_source_win32w(zip_t *za, const wchar_t *fname, zip_uint64_t start, zip_int64 ZIP_EXTERN zip_source_t * zip_source_win32w_create(const wchar_t *fname, zip_uint64_t start, zip_int64_t length, zip_error_t *error) { - if (fname == NULL || length < -1) { + if (fname == NULL || length < ZIP_LENGTH_UNCHECKED) { zip_error_set(error, ZIP_ER_INVAL, 0); return NULL; } @@ -84,7 +88,7 @@ utf16_allocate_tempname(const char *name, size_t extra_chars, size_t *lengthp) { } -static HANDLE __stdcall utf16_create_file(const char *name, DWORD access, DWORD share_mode, PSECURITY_ATTRIBUTES security_attributes, DWORD creation_disposition, DWORD file_attributes, HANDLE template_file) { +static HANDLE __stdcall utf16_create_file(const void *name, DWORD access, DWORD share_mode, PSECURITY_ATTRIBUTES security_attributes, DWORD creation_disposition, DWORD file_attributes, HANDLE template_file) { #ifdef MS_UWP CREATEFILE2_EXTENDED_PARAMETERS extParams = {0}; extParams.dwFileAttributes = file_attributes; @@ -100,14 +104,49 @@ static HANDLE __stdcall utf16_create_file(const char *name, DWORD access, DWORD #endif } +static BOOL __stdcall +utf16_delete_file(const void *name) +{ + return DeleteFileW((const wchar_t *)name); +} + +static DWORD __stdcall +utf16_get_file_attributes(const void *name) +{ + return GetFileAttributesW((const wchar_t *)name); +} + +static BOOL __stdcall +utf16_get_file_attributes_ex(const void *name, GET_FILEEX_INFO_LEVELS info_level, void *information) +{ + return GetFileAttributesExW((const wchar_t *)name, info_level, information); +} static void utf16_make_tempname(char *buf, size_t len, const char *name, zip_uint32_t i) { _snwprintf_s((wchar_t *)buf, len, len, L"%s.%08x", (const wchar_t *)name, i); } +static BOOL __stdcall +utf16_move_file(const void *from, const void *to, DWORD flags) +{ + return MoveFileExW((const wchar_t *)from, (const wchar_t *)to, flags); +} + +static BOOL __stdcall +utf16_set_file_attributes(const void *name, DWORD attributes) +{ + return SetFileAttributesW((const wchar_t *)name, attributes); +} static char * utf16_strdup(const char *string) { return (char *)_wcsdup((const wchar_t *)string); } + + +static HANDLE __stdcall +utf16_find_first_file(const void *name, void* data) +{ + return FindFirstFileW((const wchar_t *)name, data); +} diff --git a/zip_source_file_win32_utf8.c b/zip_source_file_win32_utf8.c index 687c30f..5c27df9 100644 --- a/zip_source_file_win32_utf8.c +++ b/zip_source_file_win32_utf8.c @@ -1,6 +1,6 @@ /* zip_source_file_win32_ansi.c -- source for Windows file opened by UTF-8 name - Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -49,7 +49,7 @@ zip_source_file_create(const char *fname, zip_uint64_t start, zip_int64_t length wchar_t *wfname; zip_source_t *source; - if (fname == NULL || length < -1) { + if (fname == NULL || length < ZIP_LENGTH_UNCHECKED) { zip_error_set(error, ZIP_ER_INVAL, 0); return NULL; } diff --git a/zip_source_free.c b/zip_source_free.c index 1a80040..df4b08d 100644 --- a/zip_source_free.c +++ b/zip_source_free.c @@ -1,6 +1,6 @@ /* zip_source_free.c -- free zip data source - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_get_dostime.c b/zip_source_get_dostime.c new file mode 100644 index 0000000..17f652b --- /dev/null +++ b/zip_source_get_dostime.c @@ -0,0 +1,72 @@ +/* + zip_source_get_dostime.c -- get modification time in DOS format from source + Copyright (C) 2024 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + +/* Returns -1 on error, 0 on no dostime available, 1 for dostime returned */ +int +zip_source_get_dos_time(zip_source_t *src, zip_dostime_t *dos_time) { + if (src->source_closed) { + return -1; + } + if (dos_time == NULL) { + zip_error_set(&src->error, ZIP_ER_INVAL, 0); + return -1; + } + + if (src->write_state == ZIP_SOURCE_WRITE_REMOVED) { + zip_error_set(&src->error, ZIP_ER_READ, ENOENT); + } + + if (zip_source_supports(src) & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_GET_DOS_TIME)) { + zip_int64_t n = _zip_source_call(src, dos_time, sizeof(*dos_time), ZIP_SOURCE_GET_DOS_TIME); + + if (n < 0) { + return -1; + } + else if (n == 0) { + return 0; + } + else if (n == sizeof(*dos_time)) { + return 1; + } + else { + zip_error_set(&src->error, ZIP_ER_INTERNAL, 0); + return -1; + } + } + else { + return 0; + } +} diff --git a/zip_source_get_file_attributes.c b/zip_source_get_file_attributes.c index 4771dc1..59c064f 100644 --- a/zip_source_get_file_attributes.c +++ b/zip_source_get_file_attributes.c @@ -1,6 +1,6 @@ /* zip_source_get_file_attributes.c -- get attributes for file from source - Copyright (C) 2020 Dieter Baron and Thomas Klausner + Copyright (C) 2020-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -39,8 +39,7 @@ zip_file_attributes_init(zip_file_attributes_t *attributes) { attributes->version = 1; } -int -zip_source_get_file_attributes(zip_source_t *src, zip_file_attributes_t *attributes) { +int zip_source_get_file_attributes(zip_source_t *src, zip_file_attributes_t *attributes) { if (src->source_closed) { return -1; } diff --git a/zip_source_is_deleted.c b/zip_source_is_deleted.c index 838aa90..d6016a0 100644 --- a/zip_source_is_deleted.c +++ b/zip_source_is_deleted.c @@ -1,6 +1,6 @@ /* zip_source_is_deleted.c -- was archive was removed? - Copyright (C) 2014-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2014-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_layered.c b/zip_source_layered.c index 62b78e6..be42819 100644 --- a/zip_source_layered.c +++ b/zip_source_layered.c @@ -1,6 +1,6 @@ /* zip_source_layered.c -- create layered source - Copyright (C) 2009-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_open.c b/zip_source_open.c index b34fa2c..9e42d0a 100644 --- a/zip_source_open.c +++ b/zip_source_open.c @@ -1,6 +1,6 @@ /* zip_source_open.c -- open zip_source (prepare for reading) - Copyright (C) 2009-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_pass_to_lower_layer.c b/zip_source_pass_to_lower_layer.c index 4a98222..391729e 100644 --- a/zip_source_pass_to_lower_layer.c +++ b/zip_source_pass_to_lower_layer.c @@ -1,6 +1,6 @@ /* zip_source_pass_to_lower_layer.c -- pass command to lower layer - Copyright (C) 2022 Dieter Baron and Thomas Klausner + Copyright (C) 2022-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -47,12 +47,12 @@ zip_int64_t zip_source_pass_to_lower_layer(zip_source_t *src, void *data, zip_ui case ZIP_SOURCE_ACCEPT_EMPTY: case ZIP_SOURCE_ERROR: + case ZIP_SOURCE_GET_DOS_TIME: case ZIP_SOURCE_READ: case ZIP_SOURCE_SEEK: case ZIP_SOURCE_TELL: return _zip_source_call(src, data, length, command); - case ZIP_SOURCE_BEGIN_WRITE: case ZIP_SOURCE_BEGIN_WRITE_CLONING: case ZIP_SOURCE_COMMIT_WRITE: diff --git a/zip_source_pkware_decode.c b/zip_source_pkware_decode.c index b4c482b..9c22a06 100644 --- a/zip_source_pkware_decode.c +++ b/zip_source_pkware_decode.c @@ -1,6 +1,6 @@ /* zip_source_pkware_decode.c -- Traditional PKWARE decryption routines - Copyright (C) 2009-2020 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -80,9 +80,9 @@ zip_source_pkware_decode(zip_t *za, zip_source_t *src, zip_uint16_t em, int flag static int decrypt_header(zip_source_t *src, struct trad_pkware *ctx) { zip_uint8_t header[ZIP_CRYPTO_PKWARE_HEADERLEN]; - struct zip_stat st; + zip_stat_t st; + zip_dostime_t dostime; zip_int64_t n; - bool ok = false; if ((n = zip_source_read(src, header, ZIP_CRYPTO_PKWARE_HEADERLEN)) < 0) { zip_error_set_from_source(&ctx->error, src); @@ -96,36 +96,35 @@ decrypt_header(zip_source_t *src, struct trad_pkware *ctx) { _zip_pkware_decrypt(&ctx->keys, header, header, ZIP_CRYPTO_PKWARE_HEADERLEN); - if (zip_source_stat(src, &st)) { - /* stat failed, skip password validation */ + if (zip_source_stat(src, &st) < 0 || (st.valid & ZIP_STAT_CRC) == 0) { + /* skip password validation */ return 0; } - /* password verification - two ways: - * mtime - InfoZIP way, to avoid computing complete CRC before encrypting data - * CRC - old PKWare way - */ + if (zip_source_get_dos_time(src, &dostime) <= 0) { + if ((st.valid & ZIP_STAT_MTIME) == 0) { + /* no date available, skip password validation */ + return 0; + } - if (st.valid & ZIP_STAT_MTIME) { - unsigned short dostime, dosdate; - _zip_u2d_time(st.mtime, &dostime, &dosdate); - if (header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] == dostime >> 8) { - ok = true; + if (_zip_u2d_time(st.mtime, &dostime, &ctx->error) < 0) { + return -1; } } - if (st.valid & ZIP_STAT_CRC) { - if (header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] == st.crc >> 24) { - ok = true; - } + /* + password verification - two ways: + - mtime - InfoZIP way, to avoid computing complete CRC before encrypting data + - CRC - old PKWare way + */ + if (header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] == dostime.time >> 8 + || header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] == st.crc >> 24) { + return 0; } - - if (!ok && ((st.valid & (ZIP_STAT_MTIME | ZIP_STAT_CRC)) != 0)) { + else { zip_error_set(&ctx->error, ZIP_ER_WRONGPASSWD, 0); return -1; } - - return 0; } diff --git a/zip_source_pkware_encode.c b/zip_source_pkware_encode.c index cf2d34c..26e1ce7 100644 --- a/zip_source_pkware_encode.c +++ b/zip_source_pkware_encode.c @@ -1,6 +1,6 @@ /* zip_source_pkware_encode.c -- Traditional PKWARE encryption routines - Copyright (C) 2009-2020 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -42,6 +42,7 @@ struct trad_pkware { zip_pkware_keys_t keys; zip_buffer_t *buffer; bool eof; + zip_dostime_t dostime; zip_error_t error; }; @@ -51,7 +52,6 @@ static zip_int64_t pkware_encrypt(zip_source_t *, void *, void *, zip_uint64_t, static void trad_pkware_free(struct trad_pkware *); static struct trad_pkware *trad_pkware_new(const char *password, zip_error_t *error); - zip_source_t * zip_source_pkware_encode(zip_t *za, zip_source_t *src, zip_uint16_t em, int flags, const char *password) { struct trad_pkware *ctx; @@ -67,9 +67,24 @@ zip_source_pkware_encode(zip_t *za, zip_source_t *src, zip_uint16_t em, int flag } if ((ctx = trad_pkware_new(password, &za->error)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return NULL; } + if (zip_source_get_dos_time(src, &ctx->dostime) <= 0) { + zip_stat_t st; + + if (zip_source_stat(src, &st) < 0) { + zip_error_set_from_source(&za->error, src); + trad_pkware_free(ctx); + return NULL; + } + if (_zip_u2d_time((st.valid & ZIP_STAT_MTIME) ? st.mtime : time(NULL), &ctx->dostime, &za->error) < 0) { + trad_pkware_free(ctx); + return NULL; + } + } + if ((s2 = zip_source_layered(za, src, pkware_encrypt, ctx)) == NULL) { trad_pkware_free(ctx); return NULL; @@ -81,17 +96,8 @@ zip_source_pkware_encode(zip_t *za, zip_source_t *src, zip_uint16_t em, int flag static int encrypt_header(zip_source_t *src, struct trad_pkware *ctx) { - struct zip_stat st; - unsigned short dostime, dosdate; zip_uint8_t *header; - if (zip_source_stat(src, &st) != 0) { - zip_error_set_from_source(&ctx->error, src); - return -1; - } - - _zip_u2d_time(st.mtime, &dostime, &dosdate); - if ((ctx->buffer = _zip_buffer_new(NULL, ZIP_CRYPTO_PKWARE_HEADERLEN)) == NULL) { zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); return -1; @@ -107,7 +113,7 @@ encrypt_header(zip_source_t *src, struct trad_pkware *ctx) { ctx->buffer = NULL; return -1; } - header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] = (zip_uint8_t)((dostime >> 8) & 0xff); + header[ZIP_CRYPTO_PKWARE_HEADERLEN - 1] = (zip_uint8_t)((ctx->dostime.time >> 8) & 0xff); _zip_pkware_encrypt(&ctx->keys, header, header, ZIP_CRYPTO_PKWARE_HEADERLEN); @@ -192,14 +198,24 @@ pkware_encrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t length, zip zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); return -1; } - attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED; + attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED | ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS; attributes->version_needed = 20; + attributes->general_purpose_bit_flags = ZIP_GPBF_DATA_DESCRIPTOR; + attributes->general_purpose_bit_mask = ZIP_GPBF_DATA_DESCRIPTOR; return 0; } + case ZIP_SOURCE_GET_DOS_TIME: + if (length < sizeof(ctx->dostime)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + (void)memcpy_s(data, sizeof(ctx->dostime), &ctx->dostime, sizeof(ctx->dostime)); + return sizeof(ctx->dostime); + case ZIP_SOURCE_SUPPORTS: - return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_GET_FILE_ATTRIBUTES, -1); + return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_GET_DOS_TIME, -1); case ZIP_SOURCE_ERROR: return zip_error_to_data(&ctx->error, data, length); diff --git a/zip_source_read.c b/zip_source_read.c index 0938fcb..910d4c3 100644 --- a/zip_source_read.c +++ b/zip_source_read.c @@ -1,6 +1,6 @@ /* zip_source_read.c -- read data from zip_source - Copyright (C) 2009-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_remove.c b/zip_source_remove.c index c1d73ab..06325b7 100644 --- a/zip_source_remove.c +++ b/zip_source_remove.c @@ -1,6 +1,6 @@ /* zip_source_remove.c -- remove empty archive - Copyright (C) 2014-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2014-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_rollback_write.c b/zip_source_rollback_write.c index ea1a151..5161325 100644 --- a/zip_source_rollback_write.c +++ b/zip_source_rollback_write.c @@ -1,6 +1,6 @@ /* zip_source_rollback_write.c -- discard changes - Copyright (C) 2014-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2014-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_seek.c b/zip_source_seek.c index e3baad5..42d6b0d 100644 --- a/zip_source_seek.c +++ b/zip_source_seek.c @@ -1,6 +1,6 @@ /* zip_source_seek.c -- seek to offset - Copyright (C) 2014-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2014-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_seek_write.c b/zip_source_seek_write.c index 34ae2f5..9e87fa4 100644 --- a/zip_source_seek_write.c +++ b/zip_source_seek_write.c @@ -1,6 +1,6 @@ /* zip_source_seek_write.c -- seek to offset for writing - Copyright (C) 2014-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2014-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_stat.c b/zip_source_stat.c index 05dcb84..7c8baca 100644 --- a/zip_source_stat.c +++ b/zip_source_stat.c @@ -1,6 +1,6 @@ /* zip_source_stat.c -- get meta information from zip_source - Copyright (C) 2009-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_supports.c b/zip_source_supports.c index 8fea2ae..e30ee75 100644 --- a/zip_source_supports.c +++ b/zip_source_supports.c @@ -1,6 +1,6 @@ /* zip_source_supports.c -- check for supported functions - Copyright (C) 2014-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2014-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_tell.c b/zip_source_tell.c index 49057ce..33ec475 100644 --- a/zip_source_tell.c +++ b/zip_source_tell.c @@ -1,6 +1,6 @@ /* zip_source_tell.c -- report current offset - Copyright (C) 2014-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2014-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_tell_write.c b/zip_source_tell_write.c index a5b0e53..6912af9 100644 --- a/zip_source_tell_write.c +++ b/zip_source_tell_write.c @@ -1,6 +1,6 @@ /* zip_source_tell_write.c -- report current offset for writing - Copyright (C) 2014-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2014-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_window.c b/zip_source_window.c index 524e27c..8b47246 100644 --- a/zip_source_window.c +++ b/zip_source_window.c @@ -1,6 +1,6 @@ /* zip_source_window.c -- return part of lower source - Copyright (C) 2012-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2012-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -51,6 +51,8 @@ struct window { zip_stat_t stat; zip_uint64_t stat_invalid; zip_file_attributes_t attributes; + zip_dostime_t dostime; + bool dostime_valid; zip_error_t error; zip_int64_t supports; bool needs_seek; @@ -61,12 +63,12 @@ static zip_int64_t window_read(zip_source_t *, void *, void *, zip_uint64_t, zip ZIP_EXTERN zip_source_t * zip_source_window_create(zip_source_t *src, zip_uint64_t start, zip_int64_t len, zip_error_t *error) { - return _zip_source_window_new(src, start, len, NULL, 0, NULL, NULL, 0, false, error); + return _zip_source_window_new(src, start, len, NULL, 0, NULL, NULL, NULL, 0, false, error); } zip_source_t * -_zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_int64_t length, zip_stat_t *st, zip_uint64_t st_invalid, zip_file_attributes_t *attributes, zip_t *source_archive, zip_uint64_t source_index, bool take_ownership, zip_error_t *error) { +_zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_int64_t length, zip_stat_t *st, zip_uint64_t st_invalid, zip_file_attributes_t *attributes, zip_dostime_t *dostime, zip_t *source_archive, zip_uint64_t source_index, bool take_ownership, zip_error_t *error) { zip_source_t* window_source; struct window *ctx; @@ -103,10 +105,17 @@ _zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_int64_t length else { zip_file_attributes_init(&ctx->attributes); } + if (dostime != NULL) { + ctx->dostime = *dostime; + ctx->dostime_valid = true; + } + else { + ctx->dostime_valid = false; + } ctx->source_archive = source_archive; ctx->source_index = source_index; zip_error_init(&ctx->error); - ctx->supports = (zip_source_supports(src) & (ZIP_SOURCE_SUPPORTS_SEEKABLE | ZIP_SOURCE_SUPPORTS_REOPEN)) | (zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, ZIP_SOURCE_FREE, -1)); + ctx->supports = (zip_source_supports(src) & (ZIP_SOURCE_SUPPORTS_SEEKABLE | ZIP_SOURCE_SUPPORTS_REOPEN)) | (zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_GET_DOS_TIME, ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, ZIP_SOURCE_FREE, -1)); ctx->needs_seek = (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK)) ? true : false; if (st) { @@ -309,6 +318,19 @@ window_read(zip_source_t *src, void *_ctx, void *data, zip_uint64_t len, zip_sou (void)memcpy_s(data, sizeof(ctx->attributes), &ctx->attributes, sizeof(ctx->attributes)); return sizeof(ctx->attributes); + case ZIP_SOURCE_GET_DOS_TIME: + if (len < sizeof(ctx->dostime)) { + zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); + return -1; + } + if (ctx->dostime_valid) { + (void)memcpy_s(data, sizeof(ctx->dostime), &ctx->dostime, sizeof(ctx->dostime)); + return sizeof(ctx->dostime); + } + else { + return 0; + } + case ZIP_SOURCE_SUPPORTS: return ctx->supports; @@ -323,7 +345,7 @@ window_read(zip_source_t *src, void *_ctx, void *data, zip_uint64_t len, zip_sou void _zip_deregister_source(zip_t *za, zip_source_t *src) { - unsigned int i; + zip_uint64_t i; for (i = 0; i < za->nopen_source; i++) { if (za->open_source[i] == src) { @@ -337,18 +359,10 @@ _zip_deregister_source(zip_t *za, zip_source_t *src) { int _zip_register_source(zip_t *za, zip_source_t *src) { - zip_source_t **open_source; - if (za->nopen_source + 1 >= za->nopen_source_alloc) { - unsigned int n; - n = za->nopen_source_alloc + 10; - open_source = (zip_source_t **)realloc(za->open_source, n * sizeof(zip_source_t *)); - if (open_source == NULL) { - zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + if (!ZIP_REALLOC(za->open_source, za->nopen_source_alloc, 10, &za->error)) { return -1; } - za->nopen_source_alloc = n; - za->open_source = open_source; } za->open_source[za->nopen_source++] = src; diff --git a/zip_source_winzip_aes_decode.c b/zip_source_winzip_aes_decode.c index ee53fb4..8830a00 100644 --- a/zip_source_winzip_aes_decode.c +++ b/zip_source_winzip_aes_decode.c @@ -1,6 +1,6 @@ /* zip_source_winzip_aes_decode.c -- Winzip AES decryption routines - Copyright (C) 2009-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_winzip_aes_encode.c b/zip_source_winzip_aes_encode.c index 87e2865..c704b12 100644 --- a/zip_source_winzip_aes_encode.c +++ b/zip_source_winzip_aes_encode.c @@ -1,6 +1,6 @@ /* zip_source_winzip_aes_encode.c -- Winzip AES encryption routines - Copyright (C) 2009-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2009-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_write.c b/zip_source_write.c index 24dde9b..20ca361 100644 --- a/zip_source_write.c +++ b/zip_source_write.c @@ -1,6 +1,6 @@ /* zip_source_write.c -- start a new file for writing - Copyright (C) 2014-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2014-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_zip.c b/zip_source_zip.c index faabf0d..0781986 100644 --- a/zip_source_zip.c +++ b/zip_source_zip.c @@ -1,6 +1,6 @@ /* zip_source_zip.c -- create data source from zip file - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2023 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_source_zip_new.c b/zip_source_zip_new.c index d2d66d4..3eb8a4c 100644 --- a/zip_source_zip_new.c +++ b/zip_source_zip_new.c @@ -1,6 +1,6 @@ /* zip_source_zip_new.c -- prepare data structures for zip_fopen/zip_source_zip - Copyright (C) 2012-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2012-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -53,6 +53,7 @@ ZIP_EXTERN zip_source_t *zip_source_zip_file_create(zip_t *srcza, zip_uint64_t s zip_flags_t stat_flags; zip_int64_t data_len; bool take_ownership = false; + bool empty_data = false; if (srcza == NULL || srcidx >= srcza->nentry || len < -1) { zip_error_set(error, ZIP_ER_INVAL, 0); @@ -147,15 +148,13 @@ ZIP_EXTERN zip_source_t *zip_source_zip_file_create(zip_t *srcza, zip_uint64_t s _zip_file_attributes_from_dirent(&attributes, de); have_comp_size = (st.valid & ZIP_STAT_COMP_SIZE) != 0; - if (compressed) { - if (have_comp_size && st.comp_size == 0) { - src = zip_source_buffer_with_attributes_create(NULL, 0, 0, &attributes, error); - } - else { - src = NULL; - } + if (needs_decrypt || needs_decompress) { + empty_data = (have_comp_size && st.comp_size == 0); } - else if (have_size && st.size == 0) { + else { + empty_data = (have_size && st.size == 0); + } + if (empty_data) { src = zip_source_buffer_with_attributes_create(NULL, 0, 0, &attributes, error); } else { @@ -201,7 +200,7 @@ ZIP_EXTERN zip_source_t *zip_source_zip_file_create(zip_t *srcza, zip_uint64_t s st2.valid |= ZIP_STAT_MTIME; } - if ((src = _zip_source_window_new(src, start, data_len, &st2, ZIP_STAT_NAME, &attributes, source_archive, source_index, take_ownership, error)) == NULL) { + if ((src = _zip_source_window_new(src, start, data_len, &st2, ZIP_STAT_NAME, &attributes, &de->last_mod, source_archive, source_index, take_ownership, error)) == NULL) { return NULL; } } @@ -220,7 +219,7 @@ ZIP_EXTERN zip_source_t *zip_source_zip_file_create(zip_t *srcza, zip_uint64_t s attributes and to have a source that positions the read offset properly before each read for multiple zip_file_t referring to the same underlying source */ - if ((src = _zip_source_window_new(srcza->src, 0, (zip_int64_t)st.comp_size, &st, ZIP_STAT_NAME, &attributes, srcza, srcidx, take_ownership, error)) == NULL) { + if ((src = _zip_source_window_new(srcza->src, 0, (zip_int64_t)st.comp_size, &st, ZIP_STAT_NAME, &attributes, &de->last_mod, srcza, srcidx, take_ownership, error)) == NULL) { return NULL; } } @@ -236,7 +235,7 @@ ZIP_EXTERN zip_source_t *zip_source_zip_file_create(zip_t *srcza, zip_uint64_t s attributes and to have a source that positions the read offset properly before each read for multiple zip_file_t referring to the same underlying source */ - if ((src = _zip_source_window_new(src, 0, data_len, &st, ZIP_STAT_NAME, &attributes, NULL, 0, take_ownership, error)) == NULL) { + if ((src = _zip_source_window_new(src, 0, data_len, &st, ZIP_STAT_NAME, &attributes, &de->last_mod, NULL, 0, take_ownership, error)) == NULL) { return NULL; } } @@ -254,6 +253,7 @@ ZIP_EXTERN zip_source_t *zip_source_zip_file_create(zip_t *srcza, zip_uint64_t s zip_encryption_implementation enc_impl; if ((enc_impl = _zip_get_encryption_implementation(st.encryption_method, ZIP_CODEC_DECODE)) == NULL) { + zip_source_free(src); zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0); return NULL; } @@ -290,7 +290,7 @@ ZIP_EXTERN zip_source_t *zip_source_zip_file_create(zip_t *srcza, zip_uint64_t s st2.valid = ZIP_STAT_SIZE; st2.size = (zip_uint64_t)data_len; } - s2 = _zip_source_window_new(src, start, data_len, &st2, ZIP_STAT_NAME, NULL, NULL, 0, true, error); + s2 = _zip_source_window_new(src, start, data_len, &st2, ZIP_STAT_NAME, NULL, NULL, NULL, 0, true, error); if (s2 == NULL) { zip_source_free(src); return NULL; diff --git a/zip_stat.c b/zip_stat.c index 51d8026..74fc5c3 100644 --- a/zip_stat.c +++ b/zip_stat.c @@ -1,6 +1,6 @@ /* zip_stat.c -- get information about file by name - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_stat_index.c b/zip_stat_index.c index da33c09..af9f0e5 100644 --- a/zip_stat_index.c +++ b/zip_stat_index.c @@ -1,6 +1,6 @@ /* zip_stat_index.c -- get information about file by index - Copyright (C) 1999-2020 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -77,7 +77,7 @@ zip_stat_index(zip_t *za, zip_uint64_t index, zip_flags_t flags, zip_stat_t *st) } if (entry->changes != NULL && entry->changes->changed & ZIP_DIRENT_LAST_MOD) { - st->mtime = de->last_mod; + st->mtime = zip_dirent_get_last_mod_mtime(de); st->valid |= ZIP_STAT_MTIME; } } @@ -86,7 +86,7 @@ zip_stat_index(zip_t *za, zip_uint64_t index, zip_flags_t flags, zip_stat_t *st) st->crc = de->crc; st->size = de->uncomp_size; - st->mtime = de->last_mod; + st->mtime = zip_dirent_get_last_mod_mtime(de); st->comp_size = de->comp_size; st->comp_method = (zip_uint16_t)de->comp_method; st->encryption_method = de->encryption_method; @@ -97,8 +97,12 @@ zip_stat_index(zip_t *za, zip_uint64_t index, zip_flags_t flags, zip_stat_t *st) } if ((za->ch_flags & ZIP_AFL_WANT_TORRENTZIP) && (flags & ZIP_FL_UNCHANGED) == 0) { + if (za->torrent_mtime == 0) { + zip_dostime_t dostime = {0xbc00, 0x2198}; + za->torrent_mtime = _zip_d2u_time(&dostime); + } st->comp_method = ZIP_CM_DEFLATE; - st->mtime = _zip_d2u_time(0xbc00, 0x2198); + st->mtime = za->torrent_mtime; st->valid |= ZIP_STAT_MTIME | ZIP_STAT_COMP_METHOD; st->valid &= ~ZIP_STAT_COMP_SIZE; } diff --git a/zip_stat_init.c b/zip_stat_init.c index 9c6088a..bd83565 100644 --- a/zip_stat_init.c +++ b/zip_stat_init.c @@ -1,6 +1,6 @@ /* zip_stat_init.c -- initialize struct zip_stat. - Copyright (C) 2006-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_strerror.c b/zip_strerror.c index 7d82793..681024a 100644 --- a/zip_strerror.c +++ b/zip_strerror.c @@ -1,6 +1,6 @@ /* zip_sterror.c -- get string representation of zip error - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_string.c b/zip_string.c index 1c96449..bb06c1d 100644 --- a/zip_string.c +++ b/zip_string.c @@ -1,6 +1,6 @@ /* zip_string.c -- string handling (with encoding) - Copyright (C) 2012-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2012-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -88,8 +88,10 @@ _zip_string_get(zip_string_t *string, zip_uint32_t *lenp, zip_flags_t flags, zip if ((flags & ZIP_FL_ENC_RAW) == 0) { /* start guessing */ - if (string->encoding == ZIP_ENCODING_UNKNOWN) - _zip_guess_encoding(string, ZIP_ENCODING_UNKNOWN); + if (string->encoding == ZIP_ENCODING_UNKNOWN) { + /* guess encoding, sets string->encoding */ + (void)_zip_guess_encoding(string, ZIP_ENCODING_UNKNOWN); + } if (((flags & ZIP_FL_ENC_STRICT) && string->encoding != ZIP_ENCODING_ASCII && string->encoding != ZIP_ENCODING_UTF8_KNOWN) || (string->encoding == ZIP_ENCODING_CP437)) { if (string->converted == NULL) { @@ -107,6 +109,20 @@ _zip_string_get(zip_string_t *string, zip_uint32_t *lenp, zip_flags_t flags, zip return string->raw; } +bool _zip_string_is_ascii(const zip_string_t *string) { + if (string->encoding != ZIP_ENCODING_ASCII) { + zip_uint16_t i; + + for (i = 0; i < string->length; i++) { + if (string->raw[i] & 0x80) { + return false; + } + } + } + + return true; +} + zip_uint16_t _zip_string_length(const zip_string_t *s) { diff --git a/zip_unchange_all.c b/zip_unchange_all.c index 34f3702..0b22c80 100644 --- a/zip_unchange_all.c +++ b/zip_unchange_all.c @@ -1,6 +1,6 @@ /* zip_unchange.c -- undo changes to all files in zip archive - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_unchange_archive.c b/zip_unchange_archive.c index 56a8e31..ddd617b 100644 --- a/zip_unchange_archive.c +++ b/zip_unchange_archive.c @@ -1,6 +1,6 @@ /* zip_unchange_archive.c -- undo global changes to ZIP archive - Copyright (C) 2006-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2006-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_unchange_data.c b/zip_unchange_data.c index 6bdecd1..30a7be7 100644 --- a/zip_unchange_data.c +++ b/zip_unchange_data.c @@ -1,6 +1,6 @@ /* zip_unchange_data.c -- undo helper function - Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zip_utf-8.c b/zip_utf-8.c index 678912f..7d671f6 100644 --- a/zip_utf-8.c +++ b/zip_utf-8.c @@ -1,6 +1,6 @@ /* zip_utf-8.c -- UTF-8 support functions for libzip - Copyright (C) 2011-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2011-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -101,70 +101,124 @@ _zip_guess_encoding(zip_string_t *str, zip_encoding_type_t expected_encoding) { zip_encoding_type_t enc; const zip_uint8_t *name; zip_uint32_t i, j, ulen; + bool can_be_ascii = true; + bool can_be_utf8 = true; + bool has_control_characters = false; - if (str == NULL) + if (str == NULL) { return ZIP_ENCODING_ASCII; + } name = str->raw; - if (str->encoding != ZIP_ENCODING_UNKNOWN) - enc = str->encoding; - else { - enc = ZIP_ENCODING_ASCII; - for (i = 0; i < str->length; i++) { - if ((name[i] > 31 && name[i] < 128) || name[i] == '\r' || name[i] == '\n' || name[i] == '\t') - continue; + if (str->encoding != ZIP_ENCODING_UNKNOWN) { + return str->encoding; + } - enc = ZIP_ENCODING_UTF8_GUESSED; - if ((name[i] & UTF_8_LEN_2_MASK) == UTF_8_LEN_2_MATCH) - ulen = 1; - else if ((name[i] & UTF_8_LEN_3_MASK) == UTF_8_LEN_3_MATCH) - ulen = 2; - else if ((name[i] & UTF_8_LEN_4_MASK) == UTF_8_LEN_4_MATCH) - ulen = 3; - else { - enc = ZIP_ENCODING_CP437; - break; + for (i = 0; i < str->length; i++) { + if (name[i] < 128) { + if (name[i] < 32 && name[i] != '\r' && name[i] != '\n' && name[i] != '\t') { + has_control_characters = true; } - - if (i + ulen >= str->length) { - enc = ZIP_ENCODING_CP437; - break; - } - - for (j = 1; j <= ulen; j++) { - if ((name[i + j] & UTF_8_CONTINUE_MASK) != UTF_8_CONTINUE_MATCH) { - enc = ZIP_ENCODING_CP437; - goto done; - } - } - i += ulen; + continue; } + + can_be_ascii = false; + if ((name[i] & UTF_8_LEN_2_MASK) == UTF_8_LEN_2_MATCH) { + ulen = 1; + } + else if ((name[i] & UTF_8_LEN_3_MASK) == UTF_8_LEN_3_MATCH) { + ulen = 2; + } + else if ((name[i] & UTF_8_LEN_4_MASK) == UTF_8_LEN_4_MATCH) { + ulen = 3; + } + else { + can_be_utf8 = false; + break; + } + + if (i + ulen >= str->length) { + can_be_utf8 = false; + break; + } + + for (j = 1; j <= ulen; j++) { + if ((name[i + j] & UTF_8_CONTINUE_MASK) != UTF_8_CONTINUE_MATCH) { + can_be_utf8 = false; + goto done; + } + } + i += ulen; + } + + done: + enc = ZIP_ENCODING_CP437; + + switch (expected_encoding) { + case ZIP_ENCODING_UTF8_KNOWN: + case ZIP_ENCODING_UTF8_GUESSED: + if (can_be_utf8) { + enc = ZIP_ENCODING_UTF8_KNOWN; + } + else { + enc = ZIP_ENCODING_ERROR; + } + break; + + case ZIP_ENCODING_ASCII: + if (can_be_ascii && !has_control_characters) { + enc = ZIP_ENCODING_ASCII; + } + else { + enc = ZIP_ENCODING_ERROR; + } + break; + + case ZIP_ENCODING_CP437: + enc = ZIP_ENCODING_CP437; + break; + + case ZIP_ENCODING_UNKNOWN: + if (can_be_ascii && !has_control_characters) { + /* only bytes from 0x20-0x7F */ + enc = ZIP_ENCODING_ASCII; + } + else if (can_be_ascii && has_control_characters) { + /* only bytes from 0x00-0x7F */ + enc = ZIP_ENCODING_CP437; + } + else if (can_be_utf8) { + /* contains bytes from 0x80-0xFF and is valid UTF-8 */ + enc = ZIP_ENCODING_UTF8_GUESSED; + } + else { + /* fallback */ + enc = ZIP_ENCODING_CP437; + } + break; + case ZIP_ENCODING_ERROR: + /* invalid, shouldn't happen */ + enc = ZIP_ENCODING_ERROR; + break; } -done: str->encoding = enc; - - if (expected_encoding != ZIP_ENCODING_UNKNOWN) { - if (expected_encoding == ZIP_ENCODING_UTF8_KNOWN && enc == ZIP_ENCODING_UTF8_GUESSED) - str->encoding = enc = ZIP_ENCODING_UTF8_KNOWN; - - if (expected_encoding != enc && enc != ZIP_ENCODING_ASCII) - return ZIP_ENCODING_ERROR; - } - return enc; } static zip_uint32_t _zip_unicode_to_utf8_len(zip_uint32_t codepoint) { - if (codepoint < 0x0080) + if (codepoint < 0x0080) { return 1; - if (codepoint < 0x0800) + } + if (codepoint < 0x0800) { return 2; - if (codepoint < 0x10000) + } + if (codepoint < 0x10000) { return 3; + } return 4; } @@ -201,14 +255,16 @@ _zip_cp437_to_utf8(const zip_uint8_t *const _cp437buf, zip_uint32_t len, zip_uin zip_uint32_t buflen, i, offset; if (len == 0) { - if (utf8_lenp) + if (utf8_lenp) { *utf8_lenp = 0; + } return NULL; } buflen = 1; - for (i = 0; i < len; i++) + for (i = 0; i < len; i++) { buflen += _zip_unicode_to_utf8_len(_cp437_to_unicode[cp437buf[i]]); + } if ((utf8buf = (zip_uint8_t *)malloc(buflen)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); @@ -216,11 +272,13 @@ _zip_cp437_to_utf8(const zip_uint8_t *const _cp437buf, zip_uint32_t len, zip_uin } offset = 0; - for (i = 0; i < len; i++) + for (i = 0; i < len; i++) { offset += _zip_unicode_to_utf8(_cp437_to_unicode[cp437buf[i]], utf8buf + offset); + } utf8buf[buflen - 1] = 0; - if (utf8_lenp) + if (utf8_lenp) { *utf8_lenp = buflen - 1; + } return utf8buf; } diff --git a/zip_winzip_aes.c b/zip_winzip_aes.c index ce26903..2c9874b 100644 --- a/zip_winzip_aes.c +++ b/zip_winzip_aes.c @@ -1,6 +1,6 @@ /* zip_winzip_aes.c -- Winzip AES de/encryption backend routines - Copyright (C) 2017-2021 Dieter Baron and Thomas Klausner + Copyright (C) 2017-2022 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at diff --git a/zipint.h b/zipint.h index e1761e1..296f3ec 100644 --- a/zipint.h +++ b/zipint.h @@ -3,7 +3,7 @@ /* zipint.h -- internal declarations. - Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at @@ -48,7 +48,7 @@ #define ZIP_DISABLE_DEPRECATED #endif -#include +#include "zip.h" #define CENTRAL_MAGIC "PK\1\2" #define LOCAL_MAGIC "PK\3\4" @@ -56,6 +56,7 @@ #define DATADES_MAGIC "PK\7\10" #define EOCD64LOC_MAGIC "PK\6\7" #define EOCD64_MAGIC "PK\6\6" +#define MAGIC_LEN 4 #define CDENTRYSIZE 46u #define LENTRYSIZE 30 #define MAXCOMLEN 65536 @@ -101,11 +102,14 @@ /* according to unzip-6.0's zipinfo.c, this corresponds to a directory with rwx permissions for everyone */ #define ZIP_EXT_ATTRIB_DEFAULT_DIR (0040777u << 16) -#define ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK 0x0836 +/* Allowed: Encryption specific bits, data descriptor, compression specific, UTF-8 filename */ +#define ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK 0x083e #define ZIP_MAX(a, b) ((a) > (b) ? (a) : (b)) #define ZIP_MIN(a, b) ((a) < (b) ? (a) : (b)) +#define ZIP_REALLOC(memory, alloced_elements, additional_elements, error) zip_realloc((void **)&memory, &alloced_elements, sizeof(*memory), additional_elements, error) + /* This section contains API that won't materialize like this. It's placed in the internal section, pending cleanup. */ @@ -151,7 +155,7 @@ struct zip_compression_algorithm { bool (*input)(void *ctx, zip_uint8_t *data, zip_uint64_t length); /* all input data has been provided */ - void (*end_of_input)(void *ctx); + bool (*end_of_input)(void *ctx); /* process input data, writing to data, which has room for length bytes, update length to number of bytes written */ zip_compression_status_t (*process)(void *ctx, zip_uint8_t *data, zip_uint64_t *length); @@ -229,12 +233,18 @@ extern const int _zip_err_details_count; #define ZIP_ER_DETAIL_CDIR_INVALID 11 /* G invalid value in central directory */ #define ZIP_ER_DETAIL_VARIABLE_SIZE_OVERFLOW 12 /* E variable size fields overflow header */ #define ZIP_ER_DETAIL_INVALID_UTF8_IN_FILENAME 13 /* E invalid UTF-8 in filename */ -#define ZIP_ER_DETAIL_INVALID_UTF8_IN_COMMENT 13 /* E invalid UTF-8 in comment */ -#define ZIP_ER_DETAIL_INVALID_ZIP64_EF 14 /* E invalid Zip64 extra field */ -#define ZIP_ER_DETAIL_INVALID_WINZIPAES_EF 14 /* E invalid WinZip AES extra field */ -#define ZIP_ER_DETAIL_EF_TRAILING_GARBAGE 15 /* E garbage at end of extra fields */ -#define ZIP_ER_DETAIL_INVALID_EF_LENGTH 16 /* E extra field length is invalid */ -#define ZIP_ER_DETAIL_INVALID_FILE_LENGTH 17 /* E file length in header doesn't match actual file length */ +#define ZIP_ER_DETAIL_INVALID_UTF8_IN_COMMENT 14 /* E invalid UTF-8 in comment */ +#define ZIP_ER_DETAIL_INVALID_ZIP64_EF 15 /* E invalid Zip64 extra field */ +#define ZIP_ER_DETAIL_INVALID_WINZIPAES_EF 16 /* E invalid WinZip AES extra field */ +#define ZIP_ER_DETAIL_EF_TRAILING_GARBAGE 17 /* E garbage at end of extra fields */ +#define ZIP_ER_DETAIL_INVALID_EF_LENGTH 18 /* E extra field length is invalid */ +#define ZIP_ER_DETAIL_INVALID_FILE_LENGTH 19 /* E file length in header doesn't match actual file length */ +#define ZIP_ER_DETAIL_STORED_SIZE_MISMATCH 20 /* E compressed and uncompressed sizes don't match for stored file */ +#define ZIP_ER_DETAIL_DATA_DESCRIPTOR_MISMATCH 21 /* E local header and data descriptor do not match */ +#define ZIP_ER_DETAIL_EOCD64_LOCATOR_MISMATCH 22 /* G EOCD64 and EOCD64 locator do not match */ +#define ZIP_ER_DETAIL_UTF8_FILENAME_MISMATCH 23 /* E UTF-8 filename is ASCII and doesn't match filename */ +#define ZIP_ER_DETAIL_UTF8_COMMENT_MISMATCH 24 /* E UTF-8 comment is ASCII and doesn't match comment */ +#define ZIP_ER_DETAIL_COMPRESSED_DATA_TRAILING_GARBAGE 25 /* G garbage at end of compressed data */ /* directory entry: general purpose bit flags */ @@ -270,6 +280,7 @@ struct zip_hash; struct zip_progress; typedef struct zip_cdir zip_cdir_t; +typedef struct zip_dostime zip_dostime_t; typedef struct zip_dirent zip_dirent_t; typedef struct zip_entry zip_entry_t; typedef struct zip_extra_field zip_extra_field_t; @@ -298,8 +309,8 @@ struct zip { zip_uint64_t nentry_alloc; /* number of entries allocated */ zip_entry_t *entry; /* entries */ - unsigned int nopen_source; /* number of open sources using archive */ - unsigned int nopen_source_alloc; /* number of sources allocated */ + zip_uint64_t nopen_source; /* number of open sources using archive */ + zip_uint64_t nopen_source_alloc; /* number of sources allocated */ zip_source_t **open_source; /* open sources using archive */ zip_hash_t *names; /* hash table for name lookup */ @@ -307,6 +318,7 @@ struct zip { zip_progress_t *progress; /* progress callback for zip_close() */ zip_uint32_t* write_crc; /* have _zip_write() compute CRC */ + time_t torrent_mtime; }; /* file in zip archive, part of API */ @@ -328,18 +340,24 @@ struct zip_file { #define ZIP_DIRENT_PASSWORD 0x0080u #define ZIP_DIRENT_ALL ZIP_UINT32_MAX +struct zip_dostime { + zip_uint16_t time; + zip_uint16_t date; +}; + struct zip_dirent { zip_uint32_t changed; bool local_extra_fields_read; /* whether we already read in local header extra fields */ bool cloned; /* whether this instance is cloned, and thus shares non-changed strings */ bool crc_valid; /* if CRC is valid (sometimes not for encrypted archives) */ + bool last_mod_mtime_valid; zip_uint16_t version_madeby; /* (c) version of creator */ zip_uint16_t version_needed; /* (cl) version needed to extract */ zip_uint16_t bitflags; /* (cl) general purpose bit flag */ zip_int32_t comp_method; /* (cl) compression method used (uint16 and ZIP_CM_DEFAULT (-1)) */ - time_t last_mod; /* (cl) time of last modification */ + zip_dostime_t last_mod; /* (cl) time of last modification */ zip_uint32_t crc; /* (cl) CRC-32 of uncompressed data */ zip_uint64_t comp_size; /* (cl) size of compressed data */ zip_uint64_t uncomp_size; /* (cl) size of uncompressed data */ @@ -354,6 +372,8 @@ struct zip_dirent { zip_uint32_t compression_level; /* level of compression to use (never valid in orig) */ zip_uint16_t encryption_method; /* encryption method, computed from other fields */ char *password; /* file specific encryption password */ + + time_t last_mod_mtime; /* cached last_mod in Unix time format */ }; /* zip archive central directory */ @@ -363,8 +383,13 @@ struct zip_cdir { zip_uint64_t nentry; /* number of entries */ zip_uint64_t nentry_alloc; /* number of entries allocated */ + zip_uint32_t this_disk; + zip_uint32_t eocd_disk; + zip_uint64_t disk_entries; /* number of entries on this disk */ + zip_uint64_t num_entries; /* number of entries on all disks */ zip_uint64_t size; /* size of central directory */ zip_uint64_t offset; /* offset of central directory in file */ + zip_uint64_t eocd_offset; /* offset of EOCD in file */ zip_string_t *comment; /* zip archive comment */ bool is_zip64; /* central directory in zip64 format */ }; @@ -526,20 +551,23 @@ zip_uint64_t _zip_buffer_size(zip_buffer_t *buffer); void _zip_cdir_free(zip_cdir_t *); bool _zip_cdir_grow(zip_cdir_t *cd, zip_uint64_t additional_entries, zip_error_t *error); -zip_cdir_t *_zip_cdir_new(zip_uint64_t, zip_error_t *); +zip_cdir_t *_zip_cdir_new(zip_error_t *); zip_int64_t _zip_cdir_write(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivors); -time_t _zip_d2u_time(zip_uint16_t, zip_uint16_t); +time_t _zip_d2u_time(const zip_dostime_t*); void _zip_deregister_source(zip_t *za, zip_source_t *src); -void _zip_dirent_apply_attributes(zip_dirent_t *, zip_file_attributes_t *, bool, zip_uint32_t); +bool _zip_dirent_apply_attributes(zip_dirent_t *, zip_file_attributes_t *, bool); +int zip_dirent_check_consistency(zip_dirent_t *dirent); zip_dirent_t *_zip_dirent_clone(const zip_dirent_t *); void _zip_dirent_free(zip_dirent_t *); void _zip_dirent_finalize(zip_dirent_t *); +time_t zip_dirent_get_last_mod_mtime(zip_dirent_t *de); void _zip_dirent_init(zip_dirent_t *); +bool _zip_dirent_merge(zip_dirent_t *de, zip_dirent_t *de_orig, bool replacing_data, zip_error_t *error); bool _zip_dirent_needs_zip64(const zip_dirent_t *, zip_flags_t); zip_dirent_t *_zip_dirent_new(void); bool zip_dirent_process_ef_zip64(zip_dirent_t * zde, const zip_uint8_t * ef, zip_uint64_t got_len, bool local, zip_error_t * error); -zip_int64_t _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, bool local, zip_error_t *error); +zip_int64_t _zip_dirent_read(zip_dirent_t *zde, zip_source_t *src, zip_buffer_t *buffer, bool local, zip_uint64_t central_compressed_size, bool check_consistency, zip_error_t *error); void _zip_dirent_set_version_needed(zip_dirent_t *de, bool force_zip64); void zip_dirent_torrentzip_normalize(zip_dirent_t *de); @@ -595,6 +623,8 @@ int _zip_progress_start(zip_progress_t *progress); int _zip_progress_subrange(zip_progress_t *progress, double start, double end); int _zip_progress_update(zip_progress_t *progress, double value); +bool zip_realloc(void **memory, zip_uint64_t *alloced_elements, zip_uint64_t element_size, zip_uint64_t additional_elements, zip_error_t *error); + /* this symbol is extern so it can be overridden for regression testing */ ZIP_EXTERN bool zip_secure_random(zip_uint8_t *buffer, zip_uint16_t length); zip_uint32_t zip_random_uint32(void); @@ -611,18 +641,21 @@ void _zip_set_open_error(int *zep, const zip_error_t *err, int ze); bool zip_source_accept_empty(zip_source_t *src); zip_int64_t _zip_source_call(zip_source_t *src, void *data, zip_uint64_t length, zip_source_cmd_t command); bool _zip_source_eof(zip_source_t *); +int zip_source_get_dos_time(zip_source_t *src, zip_dostime_t *dos_time); + zip_source_t *_zip_source_file_or_p(const char *, FILE *, zip_uint64_t, zip_int64_t, const zip_stat_t *, zip_error_t *error); bool _zip_source_had_error(zip_source_t *); void _zip_source_invalidate(zip_source_t *src); zip_source_t *_zip_source_new(zip_error_t *error); int _zip_source_set_source_archive(zip_source_t *, zip_t *); -zip_source_t *_zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_int64_t length, zip_stat_t *st, zip_uint64_t st_invalid, zip_file_attributes_t *attributes, zip_t *source_archive, zip_uint64_t source_index, bool take_ownership, zip_error_t *error); +zip_source_t *_zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_int64_t length, zip_stat_t *st, zip_uint64_t st_invalid, zip_file_attributes_t *attributes, zip_dostime_t *dostime, zip_t *source_archive, zip_uint64_t source_index, bool take_ownership, zip_error_t *error); int _zip_stat_merge(zip_stat_t *dst, const zip_stat_t *src, zip_error_t *error); int _zip_string_equal(const zip_string_t *a, const zip_string_t *b); void _zip_string_free(zip_string_t *string); zip_uint32_t _zip_string_crc32(const zip_string_t *string); const zip_uint8_t *_zip_string_get(zip_string_t *string, zip_uint32_t *lenp, zip_flags_t flags, zip_error_t *error); +bool _zip_string_is_ascii(const zip_string_t *string); zip_uint16_t _zip_string_length(const zip_string_t *string); zip_string_t *_zip_string_new(const zip_uint8_t *raw, zip_uint16_t length, zip_flags_t flags, zip_error_t *error); int _zip_string_write(zip_t *za, const zip_string_t *string); @@ -647,9 +680,9 @@ zip_t *_zip_new(zip_error_t *); zip_int64_t _zip_file_replace(zip_t *, zip_uint64_t, const char *, zip_source_t *, zip_flags_t); int _zip_set_name(zip_t *, zip_uint64_t, const char *, zip_flags_t); -void _zip_u2d_time(time_t, zip_uint16_t *, zip_uint16_t *); +int _zip_u2d_time(time_t, zip_dostime_t *, zip_error_t *); int _zip_unchange(zip_t *, zip_uint64_t, int); void _zip_unchange_data(zip_entry_t *); int _zip_write(zip_t *za, const void *data, zip_uint64_t length); -#endif /* zipint.h */ +#endif /* _HAD_ZIPINT_H */