diff --git a/src/miniz.h b/src/miniz.h index 1d678ed2..3f011c59 100644 --- a/src/miniz.h +++ b/src/miniz.h @@ -301,6 +301,18 @@ #define fseeko64 fseeko #define fopen64 fopen #define freopen64 freopen + +// Darwin OSX +#define MZ_PLATFORM 19 +#endif + +#ifndef MZ_PLATFORM +#if defined(_WIN64) || defined(_WIN32) || defined(__WIN32__) +#define MZ_PLATFORM 0 +#else +// UNIX +#define MZ_PLATFORM 3 +#endif #endif #ifdef __cplusplus @@ -862,7 +874,8 @@ mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, // with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, - mz_uint16 comment_size, mz_uint level_and_flags); + mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint32 ext_attributes); #endif // Adds a file to an archive by fully cloning the data from another archive. @@ -5333,6 +5346,7 @@ mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile; if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) return MZ_FALSE; + pFile = MZ_FOPEN(pDst_filename, "wb"); if (!pFile) return MZ_FALSE; @@ -5344,6 +5358,7 @@ mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, if (status) mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); #endif + return status; } #endif // #ifndef MINIZ_NO_STDIO @@ -5634,8 +5649,12 @@ static mz_bool mz_zip_writer_create_central_dir_header( mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { (void)pZip; + mz_uint16 version_made_by = 10 * MZ_VER_MAJOR + MZ_VER_MINOR; + version_made_by |= (MZ_PLATFORM << 8); + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_MADE_BY_OFS, version_made_by); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); @@ -5740,8 +5759,9 @@ mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) { + mz_uint32 ext_attributes = 0; mz_uint16 method = 0, dos_time = 0, dos_date = 0; - mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; + mz_uint level, num_alignment_padding_bytes; mz_uint64 local_dir_header_ofs, cur_archive_file_ofs, comp_size = 0; size_t archive_name_size; mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; @@ -5915,10 +5935,10 @@ mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, #ifndef MINIZ_NO_STDIO mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, - mz_uint16 comment_size, - mz_uint level_and_flags) { + mz_uint16 comment_size, mz_uint level_and_flags, + 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, ext_attributes = 0; + mz_uint16 method = 0, dos_time = 0, dos_date = 0; mz_uint64 local_dir_header_ofs, cur_archive_file_ofs, uncomp_size = 0, comp_size = 0; size_t archive_name_size; diff --git a/src/zip.c b/src/zip.c index db26d15e..48aff6a1 100644 --- a/src/zip.c +++ b/src/zip.c @@ -12,7 +12,8 @@ #include #include -#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || defined(__MINGW64__) +#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ + defined(__MINGW64__) /* Win32, DOS, MSVC, MSVS */ #include @@ -117,6 +118,7 @@ struct zip_entry_t { mz_uint16 method; mz_zip_writer_add_state state; tdefl_compressor comp; + mz_uint32 external_attr; }; struct zip_t { @@ -244,6 +246,7 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { zip->entry.offset = stats.m_central_dir_ofs; zip->entry.header_offset = stats.m_local_header_ofs; zip->entry.method = stats.m_method; + zip->entry.external_attr = stats.m_external_attr; return 0; } @@ -256,6 +259,7 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { zip->entry.header_offset = zip->archive.m_archive_size; memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8)); zip->entry.method = 0; + zip->entry.external_attr = 0; num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pzip); @@ -381,6 +385,7 @@ int zip_entry_openbyindex(struct zip_t *zip, int index) { zip->entry.offset = stats.m_central_dir_ofs; zip->entry.header_offset = stats.m_local_header_ofs; zip->entry.method = stats.m_method; + zip->entry.external_attr = stats.m_external_attr; return 0; } @@ -394,7 +399,8 @@ int zip_entry_close(struct zip_t *zip) { mz_uint16 dos_time, dos_date; int status = -1; -#if defined(_MSC_VER) || defined(_WIN32) || defined(__WIN32__) || defined(__MINGW64__) +#if defined(_MSC_VER) || defined(_WIN32) || defined(__WIN32__) || \ + defined(__MINGW64__) struct tm tmb, *tm = (struct tm *)&tmb; #else struct tm *tm; @@ -468,7 +474,7 @@ int zip_entry_close(struct zip_t *zip) { pzip, zip->entry.name, entrylen, NULL, 0, "", 0, zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, dos_time, dos_date, zip->entry.header_offset, - 0)) { + zip->entry.external_attr)) { // Cannot write to zip central dir goto cleanup; } @@ -566,12 +572,24 @@ int zip_entry_fwrite(struct zip_t *zip, const char *filename) { size_t n = 0; FILE *stream = NULL; mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE] = {0}; + struct MZ_FILE_STAT_STRUCT file_stat = {0}; if (!zip) { // zip_t handler is not initialized return -1; } + if (MZ_FILE_STAT(filename, &file_stat) != 0) { + // problem getting information - check errno + return -1; + } + + if ((file_stat.st_mode & 0200) == 0) { + // MS-DOS read-only attribute + zip->entry.external_attr |= 0x01; + } + zip->entry.external_attr |= ((file_stat.st_mode & 0xFFFF) << 16); + #if defined(_MSC_VER) || defined(__MINGW64__) if (!fopen_s(&stream, filename, "rb")) #else @@ -646,6 +664,8 @@ int zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) { int zip_entry_fread(struct zip_t *zip, const char *filename) { mz_zip_archive *pzip = NULL; mz_uint idx; + mz_uint32 xattr = 0; + mz_zip_archive_file_stat info = {0}; if (!zip) { // zip_t handler is not initialized @@ -664,7 +684,26 @@ int zip_entry_fread(struct zip_t *zip, const char *filename) { return -1; } - return (mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) ? 0 : -1; + if (!mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) { + return -1; + } + +#if defined(_MSC_VER) +#else + if (!mz_zip_reader_file_stat(pzip, idx, &info)) { + // Cannot get information about zip archive; + return -1; + } + + xattr = (info.m_external_attr >> 16) & 0xFFFF; + if (xattr > 0) { + if (chmod(filename, xattr) < 0) { + return -1; + } + } +#endif + + return 0; } int zip_entry_extract(struct zip_t *zip, @@ -704,6 +743,8 @@ int zip_create(const char *zipname, const char *filenames[], size_t len) { int status = 0; size_t i; mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat = {0}; + mz_uint32 ext_attributes = 0; if (!zipname || strlen(zipname) < 1) { // zip_t archive name is empty or NULL @@ -728,8 +769,20 @@ int zip_create(const char *zipname, const char *filenames[], size_t len) { break; } + if (MZ_FILE_STAT(name, &file_stat) != 0) { + // problem getting information - check errno + return -1; + } + + if ((file_stat.st_mode & 0200) == 0) { + // MS-DOS read-only attribute + ext_attributes |= 0x01; + } + ext_attributes |= ((file_stat.st_mode & 0xFFFF) << 16); + if (!mz_zip_writer_add_file(&zip_archive, basename(name), name, "", 0, - ZIP_DEFAULT_COMPRESSION_LEVEL)) { + ZIP_DEFAULT_COMPRESSION_LEVEL, + ext_attributes)) { // Cannot add file to zip_archive status = -1; break; @@ -747,8 +800,9 @@ int zip_extract(const char *zipname, const char *dir, mz_uint i, n; char path[MAX_PATH + 1] = {0}; mz_zip_archive zip_archive; - mz_zip_archive_file_stat info; + mz_zip_archive_file_stat info = {0}; size_t dirlen = 0; + mz_uint32 xattr = 0; if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { // Cannot memset zip archive @@ -811,6 +865,16 @@ int zip_extract(const char *zipname, const char *dir, } } +#if defined(_MSC_VER) +#else + xattr = (info.m_external_attr >> 16) & 0xFFFF; + if (xattr > 0) { + if (chmod(path, xattr) < 0) { + goto out; + } + } +#endif + if (on_extract) { if (on_extract(path, arg) < 0) { goto out; diff --git a/test/test.c b/test/test.c index 578aae5a..2676f4f3 100644 --- a/test/test.c +++ b/test/test.c @@ -4,6 +4,7 @@ #include #include #include +#include #define ZIPNAME "test.zip" #define TESTDATA1 "Some test data 1...\0" @@ -11,6 +12,15 @@ #define TESTDATA2 "Some test data 2...\0" #define CRC32DATA2 2532008468 +#define RFILE "4.txt" +#define RMODE 0100444 + +#define WFILE "6.txt" +#define WMODE 0100666 + +#define XFILE "7.txt" +#define XMODE 0100777 + static int total_entries = 0; static void test_write(void) { @@ -255,7 +265,50 @@ static void test_list_entries(void) { zip_close(zip); } +static void test_file_permissions(void) { +#if defined(_MSC_VER) +#else + + struct stat file_stats; + const char *filenames[] = {RFILE, WFILE, XFILE}; + FILE *f4 = fopen(RFILE, "w"), *f6 = fopen(WFILE, "w"), + *f7 = fopen(XFILE, "w"); + fclose(f4); + fclose(f6); + fclose(f7); + chmod(RFILE, RMODE); + chmod(WFILE, WMODE); + chmod(XFILE, XMODE); + + remove(ZIPNAME); + + assert(0 == zip_create(ZIPNAME, filenames, 3)); + + remove(RFILE); + remove(WFILE); + remove(XFILE); + + assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL)); + + assert(0 == stat(RFILE, &file_stats)); + assert(RMODE == file_stats.st_mode); + + assert(0 == stat(WFILE, &file_stats)); + assert(WMODE == file_stats.st_mode); + + assert(0 == stat(XFILE, &file_stats)); + assert(XMODE == file_stats.st_mode); + + remove(RFILE); + remove(WFILE); + remove(XFILE); + remove(ZIPNAME); +#endif +} + int main(int argc, char *argv[]) { + remove(ZIPNAME); + test_write(); test_append(); test_read(); @@ -265,6 +318,8 @@ int main(int argc, char *argv[]) { test_entry_index(); test_entry_openbyindex(); test_list_entries(); + test_file_permissions(); - return remove(ZIPNAME); + remove(ZIPNAME); + return 0; }