From 51a1be4517531df79c2c83acea05a52c24f5f38b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Podg=C3=B3rski?= Date: Sun, 30 Sep 2018 13:45:35 +0200 Subject: [PATCH] Set mtime (#76) --- src/miniz.h | 47 +++++++++------- src/zip.c | 32 +++-------- test/test.c | 62 +++++++++++++++++++-- test/test_miniz.c | 138 +++++++++++++++++++++++----------------------- 4 files changed, 160 insertions(+), 119 deletions(-) diff --git a/src/miniz.h b/src/miniz.h index 353f318..c8bda5b 100644 --- a/src/miniz.h +++ b/src/miniz.h @@ -4306,8 +4306,9 @@ static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) { return mktime(&tm); } -static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, - mz_uint16 *pDOS_date) { +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(time_t time, mz_uint16 *pDOS_time, + mz_uint16 *pDOS_date) { #ifdef _MSC_VER struct tm tm_struct; struct tm *tm = &tm_struct; @@ -4319,42 +4320,44 @@ static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, } #else struct tm *tm = localtime(&time); -#endif +#endif /* #ifdef _MSC_VER */ + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); } -#endif +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ #ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS static mz_bool mz_zip_get_file_modified_time(const char *pFilename, - mz_uint16 *pDOS_time, - mz_uint16 *pDOS_date) { -#ifdef MINIZ_NO_TIME - (void)pFilename; - *pDOS_date = *pDOS_time = 0; -#else + time_t *pTime) { struct MZ_FILE_STAT_STRUCT file_stat; - // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 - // bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. + + /* On Linux with x86 glibc, this call will fail on large files (I think >= + * 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ if (MZ_FILE_STAT(pFilename, &file_stat) != 0) return MZ_FALSE; - mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); -#endif // #ifdef MINIZ_NO_TIME + + *pTime = file_stat.st_mtime; + return MZ_TRUE; } +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ -#ifndef MINIZ_NO_TIME static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) { struct utimbuf t; + + memset(&t, 0, sizeof(t)); t.actime = access_time; t.modtime = modified_time; + return !utime(pFilename, &t); } -#endif // #ifndef MINIZ_NO_TIME -#endif // #ifndef MINIZ_NO_STDIO +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) { @@ -5354,8 +5357,9 @@ mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, if (MZ_FCLOSE(pFile) == EOF) return MZ_FALSE; #ifndef MINIZ_NO_TIME - if (status) + if (status) { mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); + } #endif return status; @@ -5795,7 +5799,7 @@ mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, { time_t cur_time; time(&cur_time); - mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); } #endif // #ifndef MINIZ_NO_TIME @@ -5938,6 +5942,7 @@ mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, mz_uint32 ext_attributes) { mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; mz_uint16 method = 0, dos_time = 0, dos_date = 0; + time_t file_modified_time; mz_uint64 local_dir_header_ofs, cur_archive_file_ofs, uncomp_size = 0, comp_size = 0; size_t archive_name_size; @@ -5974,8 +5979,10 @@ mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, comment_size + archive_name_size) > 0xFFFFFFFF)) return MZ_FALSE; - if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) + memset(&file_modified_time, 0, sizeof(file_modified_time)); + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) return MZ_FALSE; + mz_zip_time_t_to_dos_time(file_modified_time, &dos_time, &dos_date); pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); if (!pSrc_file) diff --git a/src/zip.c b/src/zip.c index d8b5b46..5403680 100644 --- a/src/zip.c +++ b/src/zip.c @@ -121,6 +121,7 @@ struct zip_entry_t { mz_zip_writer_add_state state; tdefl_compressor comp; mz_uint32 external_attr; + time_t m_time; }; struct zip_t { @@ -249,6 +250,7 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { zip->entry.header_offset = stats.m_local_header_ofs; zip->entry.method = stats.m_method; zip->entry.external_attr = stats.m_external_attr; + zip->entry.m_time = stats.m_time; return 0; } @@ -319,6 +321,8 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { } } + zip->entry.m_time = time(NULL); + return 0; cleanup: @@ -389,6 +393,7 @@ int zip_entry_openbyindex(struct zip_t *zip, int index) { zip->entry.header_offset = stats.m_local_header_ofs; zip->entry.method = stats.m_method; zip->entry.external_attr = stats.m_external_attr; + zip->entry.m_time = stats.m_time; return 0; } @@ -398,17 +403,9 @@ int zip_entry_close(struct zip_t *zip) { mz_uint level; tdefl_status done; mz_uint16 entrylen; - time_t t; mz_uint16 dos_time, dos_date; int status = -1; -#if defined(_MSC_VER) || defined(_WIN32) || defined(__WIN32__) || \ - defined(__MINGW32__) - struct tm tmb, *tm = (struct tm *)&tmb; -#else - struct tm *tm; -#endif - if (!zip) { // zip_t handler is not initialized goto cleanup; @@ -433,28 +430,13 @@ int zip_entry_close(struct zip_t *zip) { } entrylen = (mz_uint16)strlen(zip->entry.name); - t = time(NULL); - -#ifdef __STDC_LIB_EXT1__ - tm = localtime_s(&t, tm); -#else - tm = localtime(&t); -#endif - if (!tm) { - goto cleanup; - } - - dos_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + - ((tm->tm_sec) >> 1)); - dos_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + - ((tm->tm_mon + 1) << 5) + tm->tm_mday); - // no zip64 support yet if ((zip->entry.comp_size > 0xFFFFFFFF) || (zip->entry.offset > 0xFFFFFFFF)) { // No zip64 support, yet goto cleanup; } + mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date); if (!mz_zip_writer_create_local_dir_header( pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, @@ -485,6 +467,7 @@ int zip_entry_close(struct zip_t *zip) { cleanup: if (zip) { + zip->entry.m_time = 0; CLEANUP(zip->entry.name); } return status; @@ -592,6 +575,7 @@ int zip_entry_fwrite(struct zip_t *zip, const char *filename) { zip->entry.external_attr |= 0x01; } zip->entry.external_attr |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16); + zip->entry.m_time = file_stat.st_mtime; #if defined(_MSC_VER) || defined(__MINGW32__) if (fopen_s(&stream, filename, "rb")) diff --git a/test/test.c b/test/test.c index 7a54fc5..365368d 100644 --- a/test/test.c +++ b/test/test.c @@ -6,6 +6,14 @@ #include #include +#if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#else +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#endif + #define ZIPNAME "test.zip\0" #define TESTDATA1 "Some test data 1...\0" #define CRC32DATA1 2220805626 @@ -301,7 +309,7 @@ static void test_fwrite(void) { static void test_exe_permissions(void) { #if defined(_WIN32) || defined(__WIN32__) #else - struct stat file_stats; + struct MZ_FILE_STAT_STRUCT file_stats; const char *filenames[] = {XFILE}; FILE *f = fopen(XFILE, "w"); fclose(f); @@ -315,7 +323,7 @@ static void test_exe_permissions(void) { assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL)); - assert(0 == stat(XFILE, &file_stats)); + assert(0 == MZ_FILE_STAT(XFILE, &file_stats)); assert(XMODE == file_stats.st_mode); remove(XFILE); @@ -327,7 +335,7 @@ static void test_read_permissions(void) { #if defined(_MSC_VER) #else - struct stat file_stats; + struct MZ_FILE_STAT_STRUCT file_stats; const char *filenames[] = {RFILE}; FILE *f = fopen(RFILE, "w"); fclose(f); @@ -343,7 +351,7 @@ static void test_read_permissions(void) { assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL)); - assert(0 == stat(RFILE, &file_stats)); + assert(0 == MZ_FILE_STAT(RFILE, &file_stats)); assert(RMODE == file_stats.st_mode); chmod(RFILE, WMODE); @@ -356,7 +364,7 @@ static void test_write_permissions(void) { #if defined(_MSC_VER) #else - struct stat file_stats; + struct MZ_FILE_STAT_STRUCT file_stats; const char *filenames[] = {WFILE}; FILE *f = fopen(WFILE, "w"); fclose(f); @@ -370,7 +378,7 @@ static void test_write_permissions(void) { assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL)); - assert(0 == stat(WFILE, &file_stats)); + assert(0 == MZ_FILE_STAT(WFILE, &file_stats)); assert(WMODE == file_stats.st_mode); remove(WFILE); @@ -378,6 +386,47 @@ static void test_write_permissions(void) { #endif } +static void test_mtime(void) { + struct MZ_FILE_STAT_STRUCT file_stat1, file_stat2; + + const char *filename = WFILE; + FILE *stream = NULL; + struct zip_t *zip = NULL; +#if defined(_MSC_VER) + if (0 != fopen_s(&stream, filename, "w+")) +#else + if (!(stream = fopen(filename, "w+"))) +#endif + { + // Cannot open filename + fprintf(stdout, "Cannot open filename\n"); + assert(0 == -1); + } + fwrite(TESTDATA1, sizeof(char), strlen(TESTDATA1), stream); + assert(0 == fclose(stream)); + + memset(&file_stat1, 0, sizeof(file_stat1)); + memset(&file_stat2, 0, sizeof(file_stat2)); + zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w'); + assert(zip != NULL); + assert(0 == zip_entry_open(zip, filename)); + assert(0 == zip_entry_fwrite(zip, filename)); + assert(0 == zip_entry_close(zip)); + zip_close(zip); + + assert(0 == MZ_FILE_STAT(filename, &file_stat1)); + + remove(filename); + assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL)); + assert(0 == MZ_FILE_STAT(filename, &file_stat2)); + fprintf(stdout, "file_stat1.st_mtime: %lu\n", file_stat1.st_mtime); + fprintf(stdout, "file_stat2.st_mtime: %lu\n", file_stat2.st_mtime); + assert(labs(file_stat1.st_mtime - file_stat2.st_mtime) <= 1); + + remove(filename); + remove(ZIPNAME); +} + int main(int argc, char *argv[]) { UNUSED(argc); UNUSED(argv); @@ -397,6 +446,7 @@ int main(int argc, char *argv[]) { test_read_permissions(); test_write_permissions(); test_exe_permissions(); + test_mtime(); remove(ZIPNAME); return 0; diff --git a/test/test_miniz.c b/test/test_miniz.c index a0989aa..ebc0564 100644 --- a/test/test_miniz.c +++ b/test/test_miniz.c @@ -20,85 +20,85 @@ static const char *s_pStr = "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."; int main(int argc, char *argv[]) { - uint step = 0; - int cmp_status; - uLong src_len = (uLong)strlen(s_pStr); - uLong cmp_len = compressBound(src_len); - uLong uncomp_len = src_len; - uint8 *pCmp, *pUncomp; - uint total_succeeded = 0; - (void)argc, (void)argv; + uint step = 0; + int cmp_status; + uLong src_len = (uLong)strlen(s_pStr); + uLong cmp_len = compressBound(src_len); + uLong uncomp_len = src_len; + uint8 *pCmp, *pUncomp; + uint total_succeeded = 0; + (void)argc, (void)argv; - printf("miniz.c version: %s\n", MZ_VERSION); + printf("miniz.c version: %s\n", MZ_VERSION); - do { - // Allocate buffers to hold compressed and uncompressed data. - pCmp = (mz_uint8 *)malloc((size_t)cmp_len); - pUncomp = (mz_uint8 *)malloc((size_t)src_len); - if ((!pCmp) || (!pUncomp)) { - printf("Out of memory!\n"); - return EXIT_FAILURE; - } + do { + // Allocate buffers to hold compressed and uncompressed data. + pCmp = (mz_uint8 *)malloc((size_t)cmp_len); + pUncomp = (mz_uint8 *)malloc((size_t)src_len); + if ((!pCmp) || (!pUncomp)) { + printf("Out of memory!\n"); + return EXIT_FAILURE; + } - // Compress the string. - cmp_status = - compress(pCmp, &cmp_len, (const unsigned char *)s_pStr, src_len); - if (cmp_status != Z_OK) { - printf("compress() failed!\n"); - free(pCmp); - free(pUncomp); - return EXIT_FAILURE; - } + // Compress the string. + cmp_status = + compress(pCmp, &cmp_len, (const unsigned char *)s_pStr, src_len); + if (cmp_status != Z_OK) { + printf("compress() failed!\n"); + free(pCmp); + free(pUncomp); + return EXIT_FAILURE; + } - printf("Compressed from %u to %u bytes\n", (mz_uint32)src_len, - (mz_uint32)cmp_len); + printf("Compressed from %u to %u bytes\n", (mz_uint32)src_len, + (mz_uint32)cmp_len); - if (step) { - // Purposely corrupt the compressed data if fuzzy testing (this is a - // very crude fuzzy test). - uint n = 1 + (rand() % 3); - while (n--) { - uint i = rand() % cmp_len; - pCmp[i] ^= (rand() & 0xFF); - } - } + if (step) { + // Purposely corrupt the compressed data if fuzzy testing (this is a + // very crude fuzzy test). + uint n = 1 + (rand() % 3); + while (n--) { + uint i = rand() % cmp_len; + pCmp[i] ^= (rand() & 0xFF); + } + } - // Decompress. - cmp_status = uncompress(pUncomp, &uncomp_len, pCmp, cmp_len); - total_succeeded += (cmp_status == Z_OK); - - if (step) { - printf("Simple fuzzy test: step %u total_succeeded: %u\n", step, - total_succeeded); - } else { - if (cmp_status != Z_OK) { - printf("uncompress failed!\n"); - free(pCmp); - free(pUncomp); - return EXIT_FAILURE; - } - - printf("Decompressed from %u to %u bytes\n", (mz_uint32)cmp_len, - (mz_uint32)uncomp_len); - - // Ensure uncompress() returned the expected data. - if ((uncomp_len != src_len) || - (memcmp(pUncomp, s_pStr, (size_t)src_len))) { - printf("Decompression failed!\n"); - free(pCmp); - free(pUncomp); - return EXIT_FAILURE; - } - } + // Decompress. + cmp_status = uncompress(pUncomp, &uncomp_len, pCmp, cmp_len); + total_succeeded += (cmp_status == Z_OK); + if (step) { + printf("Simple fuzzy test: step %u total_succeeded: %u\n", step, + total_succeeded); + } else { + if (cmp_status != Z_OK) { + printf("uncompress failed!\n"); free(pCmp); free(pUncomp); + return EXIT_FAILURE; + } - step++; + printf("Decompressed from %u to %u bytes\n", (mz_uint32)cmp_len, + (mz_uint32)uncomp_len); - // Keep on fuzzy testing if there's a non-empty command line. - } while (argc >= 2); + // Ensure uncompress() returned the expected data. + if ((uncomp_len != src_len) || + (memcmp(pUncomp, s_pStr, (size_t)src_len))) { + printf("Decompression failed!\n"); + free(pCmp); + free(pUncomp); + return EXIT_FAILURE; + } + } - printf("Success.\n"); - return EXIT_SUCCESS; + free(pCmp); + free(pUncomp); + + step++; + + // Keep on fuzzy testing if there's a non-empty command line. + } while (argc >= 2); + + printf("Success.\n"); + return EXIT_SUCCESS; }