blob: 1c3156c3c426705e1bcd8c8df68f20cdfcdc504d [file] [log] [blame]
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08001/* zip.c -- Zip manipulation
Nathan Moinvaziri64c4c7f2019-05-09 18:46:18 -07002 Version 2.8.7, May 9, 2019
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08003 part of the MiniZip project
4
Nathan Moinvaziri2ca7f392019-01-08 16:07:10 -08005 Copyright (C) 2010-2019 Nathan Moinvaziri
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08006 https://github.com/nmoinvaz/minizip
Nathan Moinvaziri79cfab02018-08-17 12:21:06 -07007 Copyright (C) 2009-2010 Mathias Svensson
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08008 Modifications for Zip64 support
9 http://result42.com
Nathan Moinvaziri79cfab02018-08-17 12:21:06 -070010 Copyright (C) 2007-2008 Even Rouault
11 Modifications of Unzip for Zip64
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080012 Copyright (C) 1998-2010 Gilles Vollant
Viktor Szakats9dea6f22018-10-17 22:39:01 +000013 https://www.winimage.com/zLibDll/minizip.html
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080014
15 This program is distributed under the terms of the same license as zlib.
16 See the accompanying LICENSE file for the full text of the license.
17*/
18
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080019
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -070020#include "mz.h"
Nathan Moinvaziri5f091882018-10-24 18:06:08 -070021#include "mz_crypt.h"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080022#include "mz_strm.h"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080023#ifdef HAVE_BZIP2
24# include "mz_strm_bzip.h"
25#endif
Nathan Moinvaziri76fb8902018-11-21 15:14:09 -080026#ifdef HAVE_LIBCOMP
Nathan Moinvaziri90d31c72018-11-20 03:17:20 -080027# include "mz_strm_libcomp.h"
28#endif
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080029#ifdef HAVE_LZMA
30# include "mz_strm_lzma.h"
31#endif
Nathan Moinvazirib47b41b2018-07-11 09:44:29 -070032#include "mz_strm_mem.h"
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -070033#ifdef HAVE_PKCRYPT
34# include "mz_strm_pkcrypt.h"
35#endif
Nathan Moinvaziri76fb8902018-11-21 15:14:09 -080036#ifdef HAVE_WZAES
Nathan Moinvaziri21a31022018-10-24 09:50:16 -070037# include "mz_strm_wzaes.h"
38#endif
Nathan Moinvaziri4f6a0e32017-11-10 08:45:14 -080039#ifdef HAVE_ZLIB
40# include "mz_strm_zlib.h"
41#endif
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080042
43#include "mz_zip.h"
44
Nathan Moinvazirib16ab562018-11-20 16:56:21 -080045#include <ctype.h> /* tolower */
Nathan Moinvazirie8496272018-11-21 15:05:58 -080046#include <stdio.h> /* snprintf */
Nathan Moinvaziri1ee609b2018-11-19 21:34:35 -080047
zedcf076662019-05-15 21:43:56 +030048#if defined(_MSC_VER) || defined(__MINGW32__)
Nathan Moinvaziri2e0a20a2019-04-28 13:03:11 -070049# define localtime_r(t1,t2) (localtime_s(t2,t1) == 0 ? t1 : NULL)
Nathan Moinvazirib9cb4e62019-04-28 10:05:29 -070050# if (_MSC_VER < 1900)
51# define snprintf _snprintf
Nathan Moinvazirib9cb4e62019-04-28 10:05:29 -070052# endif
Nathan Moinvaziric565fa82018-10-19 08:48:33 -070053#endif
54
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080055/***************************************************************************/
56
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070057#define MZ_ZIP_MAGIC_LOCALHEADER (0x04034b50)
58#define MZ_ZIP_MAGIC_CENTRALHEADER (0x02014b50)
59#define MZ_ZIP_MAGIC_ENDHEADER (0x06054b50)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -080060#define MZ_ZIP_MAGIC_ENDHEADERU8 { 0x50, 0x4b, 0x05, 0x06 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070061#define MZ_ZIP_MAGIC_ENDHEADER64 (0x06064b50)
62#define MZ_ZIP_MAGIC_ENDLOCHEADER64 (0x07064b50)
63#define MZ_ZIP_MAGIC_DATADESCRIPTOR (0x08074b50)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -080064#define MZ_ZIP_MAGIC_DATADESCRIPTORU8 { 0x50, 0x4b, 0x07, 0x08 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070065
Nathan Moinvaziriba1db162018-11-23 10:07:35 -080066#define MZ_ZIP_SIZE_LD_ITEM (30)
Nathan Moinvaziri638f31f2018-08-27 08:17:16 -070067#define MZ_ZIP_SIZE_CD_ITEM (46)
68#define MZ_ZIP_SIZE_CD_LOCATOR64 (20)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080069
Nathan Moinvaziric78c7822018-12-01 20:42:36 -080070#ifndef MZ_ZIP_EOCD_MAX_BACK
71#define MZ_ZIP_EOCD_MAX_BACK (1 << 20)
72#endif
73
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080074/***************************************************************************/
75
76typedef struct mz_zip_s
77{
78 mz_zip_file file_info;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070079 mz_zip_file local_file_info;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080080
Nathan Moinvazirib16ab562018-11-20 16:56:21 -080081 void *stream; /* main stream */
82 void *cd_stream; /* pointer to the stream with the cd */
83 void *cd_mem_stream; /* memory stream for central directory */
84 void *compress_stream; /* compression stream */
85 void *crypt_stream; /* encryption stream */
86 void *file_info_stream; /* memory stream for storing file info */
87 void *local_file_info_stream; /* memory stream for storing local file info */
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080088
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070089 int32_t open_mode;
Nathan Moinvaziridd103f42018-11-13 14:50:32 -080090 uint8_t recover;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070091
Nathan Moinvazirib16ab562018-11-20 16:56:21 -080092 uint32_t disk_number_with_cd; /* number of the disk with the central dir */
93 int64_t disk_offset_shift; /* correction for zips that have wrong offset start of cd */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070094
Nathan Moinvazirib16ab562018-11-20 16:56:21 -080095 int64_t cd_start_pos; /* pos of the first file in the central dir stream */
96 int64_t cd_current_pos; /* pos of the current file in the central dir */
97 int64_t cd_offset; /* offset of start of central directory */
98 int64_t cd_size; /* size of the central directory */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070099
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800100 uint8_t entry_scanned; /* entry header information read ok */
101 uint8_t entry_opened; /* entry is open for read/write */
102 uint8_t entry_raw; /* entry opened with raw mode */
103 uint32_t entry_crc32; /* entry crc32 */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700104
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700105 uint64_t number_entry;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700106
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700107 uint16_t version_madeby;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700108 char *comment;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800109} mz_zip;
110
111/***************************************************************************/
112
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800113#if 0
114# define mz_zip_print printf
115#else
116# define mz_zip_print(fmt,...)
117#endif
118
119/***************************************************************************/
120
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800121/* Locate the end of central directory */
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700122static int32_t mz_zip_search_eocd(void *stream, int64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800123{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700124 int64_t file_size = 0;
Nathan Moinvaziric78c7822018-12-01 20:42:36 -0800125 int64_t max_back = MZ_ZIP_EOCD_MAX_BACK;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800126 uint8_t find[4] = MZ_ZIP_MAGIC_ENDHEADERU8;
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -0700127 int32_t err = MZ_OK;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700128
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -0700129 err = mz_stream_seek(stream, 0, MZ_SEEK_END);
Nathan Moinvaziria93a2ad2018-11-13 17:51:42 -0800130 if (err != MZ_OK)
131 return err;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800132
133 file_size = mz_stream_tell(stream);
Nathan Moinvaziri286a8172018-11-08 14:56:14 -0800134
Nathan Moinvaziric78c7822018-12-01 20:42:36 -0800135 if (max_back <= 0 || max_back > file_size)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800136 max_back = file_size;
137
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800138 return mz_stream_find_reverse(stream, (const void *)find, sizeof(find), max_back, central_pos);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800139}
140
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800141/* Locate the end of central directory 64 of a zip file */
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700142static int32_t mz_zip_search_zip64_eocd(void *stream, const int64_t end_central_offset, int64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800143{
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700144 int64_t offset = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800145 uint32_t value32 = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700146 int32_t err = MZ_OK;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700147
148
149 *central_pos = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800150
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800151 /* Zip64 end of central directory locator */
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700152 err = mz_stream_seek(stream, end_central_offset - MZ_ZIP_SIZE_CD_LOCATOR64, MZ_SEEK_SET);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800153 /* Read locator signature */
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700154 if (err == MZ_OK)
155 {
156 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700157 if (value32 != MZ_ZIP_MAGIC_ENDLOCHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700158 err = MZ_FORMAT_ERROR;
159 }
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800160 /* Number of the disk with the start of the zip64 end of central directory */
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700161 if (err == MZ_OK)
162 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800163 /* Relative offset of the zip64 end of central directory record8 */
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700164 if (err == MZ_OK)
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700165 err = mz_stream_read_uint64(stream, (uint64_t *)&offset);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800166 /* Total number of disks */
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700167 if (err == MZ_OK)
168 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800169 /* Goto end of central directory record */
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700170 if (err == MZ_OK)
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700171 err = mz_stream_seek(stream, (int64_t)offset, MZ_SEEK_SET);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800172 /* The signature */
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700173 if (err == MZ_OK)
174 {
175 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700176 if (value32 != MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700177 err = MZ_FORMAT_ERROR;
178 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800179
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700180 if (err == MZ_OK)
181 *central_pos = offset;
182
183 return err;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800184}
185
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800186/* Get info about the current file in the zip file */
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700187static int32_t mz_zip_entry_read_header(void *stream, uint8_t local, mz_zip_file *file_info, void *file_extra_stream)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700188{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700189 uint64_t ntfs_time = 0;
190 uint32_t reserved = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700191 uint32_t magic = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700192 uint32_t dos_date = 0;
Nathan Moinvaziri69e35492018-11-05 15:48:41 -0800193 uint32_t field_pos = 0;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700194 uint16_t field_type = 0;
195 uint16_t field_length = 0;
196 uint32_t field_length_read = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700197 uint16_t ntfs_attrib_id = 0;
198 uint16_t ntfs_attrib_size = 0;
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700199 uint16_t linkname_size;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700200 uint16_t value16 = 0;
201 uint32_t value32 = 0;
Nathan Moinvazirif9e34482018-11-27 09:45:09 -0800202 int64_t extrafield_pos = 0;
203 int64_t comment_pos = 0;
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700204 int64_t linkname_pos = 0;
205 int64_t saved_pos = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700206 int32_t err = MZ_OK;
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700207 char *linkname = NULL;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700208
209
210 memset(file_info, 0, sizeof(mz_zip_file));
211
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800212 /* Check the magic */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700213 err = mz_stream_read_uint32(stream, &magic);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700214 if (err == MZ_END_OF_STREAM)
215 err = MZ_END_OF_LIST;
216 else if (magic == MZ_ZIP_MAGIC_ENDHEADER || magic == MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700217 err = MZ_END_OF_LIST;
218 else if ((local) && (magic != MZ_ZIP_MAGIC_LOCALHEADER))
219 err = MZ_FORMAT_ERROR;
220 else if ((!local) && (magic != MZ_ZIP_MAGIC_CENTRALHEADER))
221 err = MZ_FORMAT_ERROR;
Viktor Szakats915b82e2018-04-24 10:02:39 +0000222
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800223 /* Read header fields */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700224 if (err == MZ_OK)
225 {
226 if (!local)
227 err = mz_stream_read_uint16(stream, &file_info->version_madeby);
228 if (err == MZ_OK)
229 err = mz_stream_read_uint16(stream, &file_info->version_needed);
230 if (err == MZ_OK)
231 err = mz_stream_read_uint16(stream, &file_info->flag);
232 if (err == MZ_OK)
233 err = mz_stream_read_uint16(stream, &file_info->compression_method);
234 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700235 {
236 err = mz_stream_read_uint32(stream, &dos_date);
237 file_info->modified_date = mz_zip_dosdate_to_time_t(dos_date);
238 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700239 if (err == MZ_OK)
240 err = mz_stream_read_uint32(stream, &file_info->crc);
241 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700242 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700243 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700244 file_info->compressed_size = value32;
245 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700246 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700247 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700248 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700249 file_info->uncompressed_size = value32;
250 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700251 if (err == MZ_OK)
252 err = mz_stream_read_uint16(stream, &file_info->filename_size);
253 if (err == MZ_OK)
254 err = mz_stream_read_uint16(stream, &file_info->extrafield_size);
255 if (!local)
256 {
257 if (err == MZ_OK)
258 err = mz_stream_read_uint16(stream, &file_info->comment_size);
259 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700260 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700261 err = mz_stream_read_uint16(stream, &value16);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700262 file_info->disk_number = value16;
263 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700264 if (err == MZ_OK)
265 err = mz_stream_read_uint16(stream, &file_info->internal_fa);
266 if (err == MZ_OK)
267 err = mz_stream_read_uint32(stream, &file_info->external_fa);
268 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700269 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700270 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700271 file_info->disk_offset = value32;
272 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700273 }
274 }
275
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700276 if (err == MZ_OK)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800277 err = mz_stream_seek(file_extra_stream, 0, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700278
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800279 /* Copy variable length data to memory stream for later retrieval */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700280 if ((err == MZ_OK) && (file_info->filename_size > 0))
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700281 err = mz_stream_copy(file_extra_stream, stream, file_info->filename_size);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800282 mz_stream_write_uint8(file_extra_stream, 0);
Nathan Moinvazirif9e34482018-11-27 09:45:09 -0800283 extrafield_pos = mz_stream_tell(file_extra_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700284
Nathan Moinvaziri69e35492018-11-05 15:48:41 -0800285 if ((err == MZ_OK) && (file_info->extrafield_size > 0))
286 err = mz_stream_copy(file_extra_stream, stream, file_info->extrafield_size);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800287 mz_stream_write_uint8(file_extra_stream, 0);
Nathan Moinvaziri69e35492018-11-05 15:48:41 -0800288
Nathan Moinvazirif9e34482018-11-27 09:45:09 -0800289 comment_pos = mz_stream_tell(file_extra_stream);
Nathan Moinvaziri69e35492018-11-05 15:48:41 -0800290 if ((err == MZ_OK) && (file_info->comment_size > 0))
291 err = mz_stream_copy(file_extra_stream, stream, file_info->comment_size);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800292 mz_stream_write_uint8(file_extra_stream, 0);
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700293
294 linkname_pos = mz_stream_tell(file_extra_stream);
295 /* Overwrite if we encounter UNIX1 extra block */
296 mz_stream_write_uint8(file_extra_stream, 0);
297
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700298 if ((err == MZ_OK) && (file_info->extrafield_size > 0))
299 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800300 /* Seek to and parse the extra field */
Nathan Moinvaziri69e35492018-11-05 15:48:41 -0800301 err = mz_stream_seek(file_extra_stream, extrafield_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700302
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800303 while ((err == MZ_OK) && (field_pos + 4 <= file_info->extrafield_size))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700304 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700305 err = mz_zip_extrafield_read(file_extra_stream, &field_type, &field_length);
Nathan Moinvaziriea2bd792018-11-19 17:44:44 -0800306 if (err != MZ_OK)
307 break;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800308 field_pos += 4;
Nathan Moinvazirifeb86c52018-11-05 11:40:21 -0800309
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800310 /* Don't allow field length to exceed size of remaining extrafield */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800311 if (field_length > (file_info->extrafield_size - field_pos))
Nathan Moinvaziri1d6d1832018-11-10 09:03:55 -0800312 field_length = (uint16_t)(file_info->extrafield_size - field_pos);
Nathan Moinvazirifeb86c52018-11-05 11:40:21 -0800313
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800314 /* Read ZIP64 extra field */
Nathan Moinvaziri761d28e2018-11-07 06:47:15 -0800315 if ((field_type == MZ_ZIP_EXTENSION_ZIP64) && (field_length >= 8))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700316 {
317 if ((err == MZ_OK) && (file_info->uncompressed_size == UINT32_MAX))
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800318 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700319 err = mz_stream_read_int64(file_extra_stream, &file_info->uncompressed_size);
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800320 if (file_info->uncompressed_size < 0)
321 err = MZ_FORMAT_ERROR;
322 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700323 if ((err == MZ_OK) && (file_info->compressed_size == UINT32_MAX))
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800324 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700325 err = mz_stream_read_int64(file_extra_stream, &file_info->compressed_size);
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800326 if (file_info->compressed_size < 0)
327 err = MZ_FORMAT_ERROR;
328 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700329 if ((err == MZ_OK) && (file_info->disk_offset == UINT32_MAX))
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800330 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700331 err = mz_stream_read_int64(file_extra_stream, &file_info->disk_offset);
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800332 if (file_info->disk_offset < 0)
333 err = MZ_FORMAT_ERROR;
334 }
juanii4ae79922018-02-11 14:29:36 -0300335 if ((err == MZ_OK) && (file_info->disk_number == UINT16_MAX))
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700336 err = mz_stream_read_uint32(file_extra_stream, &file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700337 }
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800338 /* Read NTFS extra field */
Nathan Moinvaziri761d28e2018-11-07 06:47:15 -0800339 else if ((field_type == MZ_ZIP_EXTENSION_NTFS) && (field_length > 4))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700340 {
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700341 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700342 err = mz_stream_read_uint32(file_extra_stream, &reserved);
343 field_length_read = 4;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700344
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800345 while ((err == MZ_OK) && (field_length_read + 4 <= field_length))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700346 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700347 err = mz_stream_read_uint16(file_extra_stream, &ntfs_attrib_id);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700348 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700349 err = mz_stream_read_uint16(file_extra_stream, &ntfs_attrib_size);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800350 field_length_read += 4;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700351
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700352 if ((err == MZ_OK) && (ntfs_attrib_id == 0x01) && (ntfs_attrib_size == 24))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700353 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700354 err = mz_stream_read_uint64(file_extra_stream, &ntfs_time);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700355 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->modified_date);
356
juanii7063b0e2018-02-11 13:56:21 -0300357 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700358 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700359 err = mz_stream_read_uint64(file_extra_stream, &ntfs_time);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700360 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->accessed_date);
361 }
juanii7063b0e2018-02-11 13:56:21 -0300362 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700363 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700364 err = mz_stream_read_uint64(file_extra_stream, &ntfs_time);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700365 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->creation_date);
366 }
367 }
Nathan Moinvaziri60008442018-11-07 06:29:01 -0800368 else if ((err == MZ_OK) && (field_length_read + ntfs_attrib_size <= field_length))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700369 {
Nathan Moinvaziri60008442018-11-07 06:29:01 -0800370 err = mz_stream_seek(file_extra_stream, ntfs_attrib_size, MZ_SEEK_CUR);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700371 }
372
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800373 field_length_read += ntfs_attrib_size;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700374 }
375 }
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800376 /* Read UNIX1 extra field */
Nathan Moinvaziri761d28e2018-11-07 06:47:15 -0800377 else if ((field_type == MZ_ZIP_EXTENSION_UNIX1) && (field_length >= 12))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700378 {
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700379 if (err == MZ_OK)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700380 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700381 err = mz_stream_read_uint32(file_extra_stream, &value32);
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700382 if (err == MZ_OK && file_info->accessed_date == 0)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700383 file_info->accessed_date = value32;
384 }
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700385 if (err == MZ_OK)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700386 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700387 err = mz_stream_read_uint32(file_extra_stream, &value32);
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700388 if (err == MZ_OK && file_info->modified_date == 0)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700389 file_info->modified_date = value32;
390 }
391 if (err == MZ_OK)
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800392 err = mz_stream_read_uint16(file_extra_stream, &value16); /* User id */
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700393 if (err == MZ_OK)
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800394 err = mz_stream_read_uint16(file_extra_stream, &value16); /* Group id */
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700395
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700396 /* Copy linkname to end of file extra stream so we can return null
397 terminated string */
398 linkname_size = field_length - 12;
399 if ((err == MZ_OK) && (linkname_size > 0))
400 {
401 linkname = (char *)MZ_ALLOC(linkname_size);
402 if (linkname != NULL)
403 {
404 if (mz_stream_read(file_extra_stream, linkname, linkname_size) != linkname_size)
405 err = MZ_READ_ERROR;
406 if (err == MZ_OK)
407 {
408 saved_pos = mz_stream_tell(file_extra_stream);
409
410 mz_stream_seek(file_extra_stream, linkname_pos, MZ_SEEK_SET);
411 mz_stream_write(file_extra_stream, linkname, linkname_size);
412 mz_stream_write_uint8(file_extra_stream, 0);
413
414 mz_stream_seek(file_extra_stream, saved_pos, MZ_SEEK_SET);
415 }
416 MZ_FREE(linkname);
417 }
418 }
Nathan Moinvaziri413822a2018-10-20 09:45:07 -0700419 }
Nathan Moinvaziri76fb8902018-11-21 15:14:09 -0800420#ifdef HAVE_WZAES
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800421 /* Read AES extra field */
Nathan Moinvaziri761d28e2018-11-07 06:47:15 -0800422 else if ((field_type == MZ_ZIP_EXTENSION_AES) && (field_length == 7))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700423 {
424 uint8_t value8 = 0;
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800425 /* Verify version info */
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700426 err = mz_stream_read_uint16(file_extra_stream, &value16);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800427 /* Support AE-1 and AE-2 */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700428 if (value16 != 1 && value16 != 2)
429 err = MZ_FORMAT_ERROR;
430 file_info->aes_version = value16;
431 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700432 err = mz_stream_read_uint8(file_extra_stream, &value8);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700433 if ((char)value8 != 'A')
434 err = MZ_FORMAT_ERROR;
435 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700436 err = mz_stream_read_uint8(file_extra_stream, &value8);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700437 if ((char)value8 != 'E')
438 err = MZ_FORMAT_ERROR;
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800439 /* Get AES encryption strength and actual compression method */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700440 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700441 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700442 err = mz_stream_read_uint8(file_extra_stream, &value8);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700443 file_info->aes_encryption_mode = value8;
444 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700445 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700446 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700447 err = mz_stream_read_uint16(file_extra_stream, &value16);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700448 file_info->compression_method = value16;
449 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700450 }
451#endif
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800452 else if (field_length > 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700453 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700454 err = mz_stream_seek(file_extra_stream, field_length, MZ_SEEK_CUR);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700455 }
456
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800457 field_pos += field_length;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700458 }
459 }
Nathan Moinvaziri60008442018-11-07 06:29:01 -0800460
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800461 /* Get pointers to variable length data */
Nathan Moinvaziri60008442018-11-07 06:29:01 -0800462 mz_stream_mem_get_buffer(file_extra_stream, (const void **)&file_info->filename);
Nathan Moinvazirif9e34482018-11-27 09:45:09 -0800463 mz_stream_mem_get_buffer_at(file_extra_stream, extrafield_pos, (const void **)&file_info->extrafield);
464 mz_stream_mem_get_buffer_at(file_extra_stream, comment_pos, (const void **)&file_info->comment);
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700465 mz_stream_mem_get_buffer_at(file_extra_stream, linkname_pos, (const void **)&file_info->linkname);
Nathan Moinvaziri60008442018-11-07 06:29:01 -0800466
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800467 /* Set to empty string just in-case */
Nathan Moinvaziri60008442018-11-07 06:29:01 -0800468 if (file_info->filename == NULL)
469 file_info->filename = "";
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800470 if (file_info->extrafield == NULL)
471 file_info->extrafield_size = 0;
Nathan Moinvaziri60008442018-11-07 06:29:01 -0800472 if (file_info->comment == NULL)
473 file_info->comment = "";
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700474 if (file_info->linkname == NULL)
475 file_info->linkname = "";
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700476
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800477 if (err == MZ_OK)
478 {
479 mz_zip_print("Zip - Entry - Read header - %s (local %"PRId8")\n",
480 file_info->filename, local);
Nathan Moinvaziri264dc182018-11-13 17:42:13 -0800481 mz_zip_print("Zip - Entry - Read header compress (ucs %"PRId64" cs %"PRId64" crc 0x%08"PRIx32")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800482 file_info->uncompressed_size, file_info->compressed_size, file_info->crc);
483 if (!local)
Nathan Moinvaziria93a2ad2018-11-13 17:51:42 -0800484 {
485 mz_zip_print("Zip - Entry - Read header disk (disk %"PRIu32" offset %"PRId64")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800486 file_info->disk_number, file_info->disk_offset);
Nathan Moinvaziria93a2ad2018-11-13 17:51:42 -0800487 }
Nathan Moinvaziri264dc182018-11-13 17:42:13 -0800488 mz_zip_print("Zip - Entry - Read header variable (fnl %"PRId32" efs %"PRId32" cms %"PRId32")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800489 file_info->filename_size, file_info->extrafield_size, file_info->comment_size);
490 }
491
492 return err;
493}
494
495static int32_t mz_zip_entry_read_descriptor(void *stream, uint8_t zip64, uint32_t *crc32, int64_t *compressed_size, int64_t *uncompressed_size)
496{
497 uint32_t value32 = 0;
498 int64_t value64 = 0;
499 int32_t err = MZ_OK;
500
501
502 err = mz_stream_read_uint32(stream, &value32);
503 if (value32 != MZ_ZIP_MAGIC_DATADESCRIPTOR)
504 err = MZ_FORMAT_ERROR;
505 if (err == MZ_OK)
506 err = mz_stream_read_uint32(stream, &value32);
507 if ((err == MZ_OK) && (crc32 != NULL))
508 *crc32 = value32;
509 if (err == MZ_OK)
510 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800511 /* If zip 64 extension is enabled then read as 8 byte */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800512 if (!zip64)
513 {
514 err = mz_stream_read_uint32(stream, &value32);
515 value64 = value32;
516 }
517 else
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800518 {
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800519 err = mz_stream_read_int64(stream, &value64);
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800520 if (value64 < 0)
521 err = MZ_FORMAT_ERROR;
522 }
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800523 if ((err == MZ_OK) && (compressed_size != NULL))
524 *compressed_size = value64;
525 }
526 if (err == MZ_OK)
527 {
528 if (!zip64)
529 {
530 err = mz_stream_read_uint32(stream, &value32);
531 value64 = value32;
532 }
533 else
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800534 {
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800535 err = mz_stream_read_int64(stream, &value64);
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800536 if (value64 < 0)
537 err = MZ_FORMAT_ERROR;
538 }
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800539 if ((err == MZ_OK) && (uncompressed_size != NULL))
540 *uncompressed_size = value64;
541 }
542
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700543 return err;
544}
545
Nathan Moinvazirifa620e42018-10-20 10:37:10 -0700546static int32_t mz_zip_entry_write_header(void *stream, uint8_t local, mz_zip_file *file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700547{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700548 uint64_t ntfs_time = 0;
549 uint32_t reserved = 0;
550 uint32_t dos_date = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700551 uint16_t extrafield_size = 0;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700552 uint16_t field_type = 0;
553 uint16_t field_length = 0;
554 uint16_t field_length_zip64 = 0;
555 uint16_t field_length_ntfs = 0;
556 uint16_t field_length_aes = 0;
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700557 uint16_t field_length_unix1 = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700558 uint16_t filename_size = 0;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700559 uint16_t filename_length = 0;
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700560 uint16_t linkname_size = 0;
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700561 uint16_t version_needed = 0;
Nathan Moinvaziridd103f42018-11-13 14:50:32 -0800562 int32_t comment_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700563 int32_t err = MZ_OK;
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700564 int32_t err_mem = MZ_OK;
565 uint8_t zip64 = 0;
566 uint8_t skip_aes = 0;
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700567 uint8_t mask = 0;
568 uint8_t write_end_slash = 0;
569 const char *filename = NULL;
Nathan Moinvazirifa620e42018-10-20 10:37:10 -0700570 char masked_name[64];
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700571 void *file_extra_stream = NULL;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700572
573 if (file_info == NULL)
574 return MZ_PARAM_ERROR;
575
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700576 if ((local) && (file_info->flag & MZ_ZIP_FLAG_MASK_LOCAL_INFO))
577 mask = 1;
578
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800579 /* Calculate extra field sizes */
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700580 if (file_info->uncompressed_size >= UINT32_MAX)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700581 field_length_zip64 += 8;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700582 if (file_info->compressed_size >= UINT32_MAX)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700583 field_length_zip64 += 8;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700584 if (file_info->disk_offset >= UINT32_MAX)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700585 field_length_zip64 += 8;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700586
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700587 if (file_info->zip64 == MZ_ZIP64_AUTO)
Nathan Moinvaziri121e0882018-05-03 17:57:20 -0700588 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800589 /* If uncompressed size is unknown, assume zip64 for 64-bit data descriptors */
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700590 zip64 = (local && file_info->uncompressed_size == 0) || (field_length_zip64 > 0);
Nathan Moinvaziri121e0882018-05-03 17:57:20 -0700591 }
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700592 else if (file_info->zip64 == MZ_ZIP64_FORCE)
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700593 {
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700594 zip64 = 1;
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700595 }
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700596 else if (file_info->zip64 == MZ_ZIP64_DISABLE)
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700597 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800598 /* Zip64 extension is required to zip file */
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700599 if (field_length_zip64 > 0)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700600 return MZ_PARAM_ERROR;
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700601 }
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700602
603 if (zip64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700604 {
605 extrafield_size += 4;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700606 extrafield_size += field_length_zip64;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700607 }
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700608
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800609 /* Calculate extra field size and check for duplicates */
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700610 if (file_info->extrafield_size > 0)
611 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700612 mz_stream_mem_create(&file_extra_stream);
Nathan Moinvaziri915c5132018-10-26 20:00:52 -0700613 mz_stream_mem_set_buffer(file_extra_stream, (void *)file_info->extrafield,
614 file_info->extrafield_size);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700615
616 do
617 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700618 err_mem = mz_stream_read_uint16(file_extra_stream, &field_type);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700619 if (err_mem == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700620 err_mem = mz_stream_read_uint16(file_extra_stream, &field_length);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700621 if (err_mem != MZ_OK)
622 break;
623
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800624 /* Prefer incoming aes extensions over ours */
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700625 if (field_type == MZ_ZIP_EXTENSION_AES)
626 skip_aes = 1;
627
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700628 /* Prefer our zip64, ntfs, unix1 extension over incoming */
629 if (field_type != MZ_ZIP_EXTENSION_ZIP64 && field_type != MZ_ZIP_EXTENSION_NTFS &&
630 field_type != MZ_ZIP_EXTENSION_UNIX1)
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700631 extrafield_size += 4 + field_length;
632
633 if (err_mem == MZ_OK)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800634 err_mem = mz_stream_seek(file_extra_stream, field_length, MZ_SEEK_CUR);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700635 }
636 while (err_mem == MZ_OK);
637 }
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700638
Nathan Moinvaziri76fb8902018-11-21 15:14:09 -0800639#ifdef HAVE_WZAES
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700640 if (!skip_aes)
641 {
642 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
Nathan Moinvaziri413822a2018-10-20 09:45:07 -0700643 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700644 field_length_aes = 1 + 1 + 1 + 2 + 2;
645 extrafield_size += 4 + field_length_aes;
Nathan Moinvaziri413822a2018-10-20 09:45:07 -0700646 }
647 }
Nathan Moinvaziria2bb08c2018-10-28 13:45:30 -0700648#else
Nathan Moinvaziri7de94cf2018-11-19 20:51:06 -0800649 MZ_UNUSED(field_length_aes);
Nathan Moinvaziri4ae469a2018-10-28 15:32:04 -0700650 MZ_UNUSED(skip_aes);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700651#endif
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800652 /* NTFS timestamps */
Nathan Moinvazirid9e159a2018-02-13 15:30:30 -0800653 if ((file_info->modified_date != 0) &&
654 (file_info->accessed_date != 0) &&
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700655 (file_info->creation_date != 0) && (!mask))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700656 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700657 field_length_ntfs = 8 + 8 + 8 + 4 + 2 + 2;
658 extrafield_size += 4 + field_length_ntfs;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700659 }
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700660
661 /* Unix1 symbolic links */
662 if (file_info->linkname != NULL && *file_info->linkname != 0)
663 {
664 linkname_size = (uint16_t)strlen(file_info->linkname);
665 field_length_unix1 = 12 + linkname_size;
666 extrafield_size += 4 + field_length_unix1;
667 }
668
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700669 if (local)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700670 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_LOCALHEADER);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700671 else
672 {
673 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_CENTRALHEADER);
674 if (err == MZ_OK)
675 err = mz_stream_write_uint16(stream, file_info->version_madeby);
676 }
677
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800678 /* Calculate version needed to extract */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700679 if (err == MZ_OK)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700680 {
681 version_needed = file_info->version_needed;
682 if (version_needed == 0)
683 {
684 version_needed = 20;
685 if (zip64)
686 version_needed = 45;
Nathan Moinvaziri76fb8902018-11-21 15:14:09 -0800687#ifdef HAVE_WZAES
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700688 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
689 version_needed = 51;
690#endif
691#ifdef HAVE_LZMA
692 if (file_info->compression_method == MZ_COMPRESS_METHOD_LZMA)
693 version_needed = 63;
694#endif
695 }
696 err = mz_stream_write_uint16(stream, version_needed);
697 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700698 if (err == MZ_OK)
699 err = mz_stream_write_uint16(stream, file_info->flag);
700 if (err == MZ_OK)
701 {
Nathan Moinvaziri76fb8902018-11-21 15:14:09 -0800702#ifdef HAVE_WZAES
Nathan Moinvaziri413822a2018-10-20 09:45:07 -0700703 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700704 err = mz_stream_write_uint16(stream, MZ_COMPRESS_METHOD_AES);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700705 else
706#endif
707 err = mz_stream_write_uint16(stream, file_info->compression_method);
708 }
709 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700710 {
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700711 if (file_info->modified_date != 0 && !mask)
Nathan Moinvaziri17cdaec2017-10-26 10:18:41 -0700712 dos_date = mz_zip_time_t_to_dos_date(file_info->modified_date);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700713 err = mz_stream_write_uint32(stream, dos_date);
714 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700715
716 if (err == MZ_OK)
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700717 {
718 if (mask)
719 err = mz_stream_write_uint32(stream, 0);
720 else
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800721 err = mz_stream_write_uint32(stream, file_info->crc); /* crc */
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700722 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700723 if (err == MZ_OK)
724 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800725 if (file_info->compressed_size >= UINT32_MAX) /* compr size */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700726 err = mz_stream_write_uint32(stream, UINT32_MAX);
727 else
728 err = mz_stream_write_uint32(stream, (uint32_t)file_info->compressed_size);
729 }
730 if (err == MZ_OK)
731 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800732 if (file_info->uncompressed_size >= UINT32_MAX) /* uncompr size */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700733 err = mz_stream_write_uint32(stream, UINT32_MAX);
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700734 else if (mask)
735 err = mz_stream_write_uint32(stream, 0);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700736 else
737 err = mz_stream_write_uint32(stream, (uint32_t)file_info->uncompressed_size);
738 }
739
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700740 if (mask)
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700741 {
Nathan Moinvaziri264dc182018-11-13 17:42:13 -0800742 snprintf(masked_name, sizeof(masked_name), "%"PRIx32"_%"PRIx64,
743 file_info->disk_number, file_info->disk_offset);
Nathan Moinvazirifa620e42018-10-20 10:37:10 -0700744 filename = masked_name;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700745 }
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700746 else
747 {
748 filename = file_info->filename;
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700749 }
750
751 filename_length = (uint16_t)strlen(filename);
752 filename_size += filename_length;
753
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800754 if ((mz_zip_attrib_is_dir(file_info->external_fa, file_info->version_madeby) == MZ_OK) &&
755 ((filename[filename_length - 1] != '/') && (filename[filename_length - 1] != '\\')))
756 {
757 filename_size += 1;
758 write_end_slash = 1;
759 }
760
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700761 if (err == MZ_OK)
762 err = mz_stream_write_uint16(stream, filename_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700763 if (err == MZ_OK)
764 err = mz_stream_write_uint16(stream, extrafield_size);
765
766 if (!local)
767 {
768 if (file_info->comment != NULL)
Nathan Moinvaziridd103f42018-11-13 14:50:32 -0800769 {
770 comment_size = (int32_t)strlen(file_info->comment);
771 if (comment_size > UINT16_MAX)
772 comment_size = UINT16_MAX;
773 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700774 if (err == MZ_OK)
Nathan Moinvaziridd103f42018-11-13 14:50:32 -0800775 err = mz_stream_write_uint16(stream, (uint16_t)comment_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700776 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700777 err = mz_stream_write_uint16(stream, (uint16_t)file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700778 if (err == MZ_OK)
779 err = mz_stream_write_uint16(stream, file_info->internal_fa);
780 if (err == MZ_OK)
781 err = mz_stream_write_uint32(stream, file_info->external_fa);
782 if (err == MZ_OK)
783 {
784 if (file_info->disk_offset >= UINT32_MAX)
785 err = mz_stream_write_uint32(stream, UINT32_MAX);
786 else
787 err = mz_stream_write_uint32(stream, (uint32_t)file_info->disk_offset);
788 }
789 }
790
791 if (err == MZ_OK)
792 {
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700793 if (mz_stream_write(stream, filename, filename_length) != filename_length)
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -0700794 err = MZ_WRITE_ERROR;
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700795
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800796 /* Ensure that directories have a slash appended to them for compatibility */
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700797 if (err == MZ_OK && write_end_slash)
798 err = mz_stream_write_uint8(stream, '/');
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700799 }
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700800
801 if (file_info->extrafield_size > 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700802 {
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800803 err_mem = mz_stream_mem_seek(file_extra_stream, 0, MZ_SEEK_SET);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700804 while (err == MZ_OK && err_mem == MZ_OK)
805 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700806 err_mem = mz_stream_read_uint16(file_extra_stream, &field_type);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700807 if (err_mem == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700808 err_mem = mz_stream_read_uint16(file_extra_stream, &field_length);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700809 if (err_mem != MZ_OK)
810 break;
811
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700812 /* Prefer our zip 64, ntfs, unix1 extensions over incoming */
813 if (field_type == MZ_ZIP_EXTENSION_ZIP64 || field_type == MZ_ZIP_EXTENSION_NTFS ||
814 field_type == MZ_ZIP_EXTENSION_UNIX1)
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700815 {
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800816 err_mem = mz_stream_seek(file_extra_stream, field_length, MZ_SEEK_CUR);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700817 continue;
818 }
819
820 err = mz_stream_write_uint16(stream, field_type);
821 if (err == MZ_OK)
822 err = mz_stream_write_uint16(stream, field_length);
823 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700824 err = mz_stream_copy(stream, file_extra_stream, field_length);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700825 }
826
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700827 mz_stream_mem_delete(&file_extra_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700828 }
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700829
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800830 /* Write ZIP64 extra field */
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700831 if ((err == MZ_OK) && (zip64))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700832 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700833 err = mz_zip_extrafield_write(stream, MZ_ZIP_EXTENSION_ZIP64, field_length_zip64);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700834 if ((err == MZ_OK) && (file_info->uncompressed_size >= UINT32_MAX))
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700835 {
836 if (mask)
837 err = mz_stream_write_int64(stream, 0);
838 else
839 err = mz_stream_write_int64(stream, file_info->uncompressed_size);
840 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700841 if ((err == MZ_OK) && (file_info->compressed_size >= UINT32_MAX))
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700842 err = mz_stream_write_int64(stream, file_info->compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700843 if ((err == MZ_OK) && (file_info->disk_offset >= UINT32_MAX))
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700844 err = mz_stream_write_int64(stream, file_info->disk_offset);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700845 }
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800846 /* Write NTFS extra field */
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700847 if ((err == MZ_OK) && (field_length_ntfs > 0))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700848 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700849 err = mz_zip_extrafield_write(stream, MZ_ZIP_EXTENSION_NTFS, field_length_ntfs);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700850 if (err == MZ_OK)
851 err = mz_stream_write_uint32(stream, reserved);
852 if (err == MZ_OK)
853 err = mz_stream_write_uint16(stream, 0x01);
854 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700855 err = mz_stream_write_uint16(stream, field_length_ntfs - 8);
juanii3679a3d2018-02-11 13:55:38 -0300856 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700857 {
858 mz_zip_unix_to_ntfs_time(file_info->modified_date, &ntfs_time);
859 err = mz_stream_write_uint64(stream, ntfs_time);
860 }
juanii3679a3d2018-02-11 13:55:38 -0300861 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700862 {
863 mz_zip_unix_to_ntfs_time(file_info->accessed_date, &ntfs_time);
864 err = mz_stream_write_uint64(stream, ntfs_time);
865 }
juanii3679a3d2018-02-11 13:55:38 -0300866 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700867 {
868 mz_zip_unix_to_ntfs_time(file_info->creation_date, &ntfs_time);
869 err = mz_stream_write_uint64(stream, ntfs_time);
870 }
871 }
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700872 /* Write UNIX extra block extra field */
873 if ((err == MZ_OK) && (field_length_unix1 > 0))
874 {
875 err = mz_zip_extrafield_write(stream, MZ_ZIP_EXTENSION_UNIX1, field_length_unix1);
876 if (err == MZ_OK)
Nathan Moinvaziriaeb6cdd2019-05-06 13:40:32 -0700877 err = mz_stream_write_uint32(stream, (uint32_t)file_info->accessed_date);
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700878 if (err == MZ_OK)
Nathan Moinvaziriaeb6cdd2019-05-06 13:40:32 -0700879 err = mz_stream_write_uint32(stream, (uint32_t)file_info->modified_date);
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -0700880 if (err == MZ_OK) /* User id */
881 err = mz_stream_write_uint16(stream, 0);
882 if (err == MZ_OK) /* Group id */
883 err = mz_stream_write_uint16(stream, 0);
884 if (err == MZ_OK && linkname_size > 0)
885 {
886 if (mz_stream_write(stream, file_info->linkname, linkname_size) != linkname_size)
887 err = MZ_WRITE_ERROR;
888 }
889 }
Nathan Moinvaziri76fb8902018-11-21 15:14:09 -0800890#ifdef HAVE_WZAES
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800891 /* Write AES extra field */
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700892 if ((err == MZ_OK) && (!skip_aes) && (file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700893 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700894 err = mz_zip_extrafield_write(stream, MZ_ZIP_EXTENSION_AES, field_length_aes);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700895 if (err == MZ_OK)
896 err = mz_stream_write_uint16(stream, file_info->aes_version);
897 if (err == MZ_OK)
898 err = mz_stream_write_uint8(stream, 'A');
899 if (err == MZ_OK)
900 err = mz_stream_write_uint8(stream, 'E');
901 if (err == MZ_OK)
902 err = mz_stream_write_uint8(stream, file_info->aes_encryption_mode);
903 if (err == MZ_OK)
904 err = mz_stream_write_uint16(stream, file_info->compression_method);
905 }
906#endif
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700907 if ((err == MZ_OK) && (!local) && (file_info->comment != NULL))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700908 {
Nathan Moinvazirica014e62018-08-15 07:31:12 -0700909 if (mz_stream_write(stream, file_info->comment, file_info->comment_size) != file_info->comment_size)
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -0700910 err = MZ_WRITE_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700911 }
912
913 return err;
914}
915
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800916static int32_t mz_zip_entry_write_descriptor(void *stream, uint8_t zip64, uint32_t crc32, int64_t compressed_size, int64_t uncompressed_size)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800917{
918 int32_t err = MZ_OK;
919
920 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_DATADESCRIPTOR);
921 if (err == MZ_OK)
922 err = mz_stream_write_uint32(stream, crc32);
923
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800924 /* Store data descriptor as 8 bytes if zip 64 extension enabled */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800925 if (err == MZ_OK)
926 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800927 /* Zip 64 extension is enabled when uncompressed size is > UINT32_MAX */
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800928 if (!zip64)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800929 err = mz_stream_write_uint32(stream, (uint32_t)compressed_size);
930 else
931 err = mz_stream_write_int64(stream, compressed_size);
932 }
933 if (err == MZ_OK)
934 {
Nathan Moinvaziriba1db162018-11-23 10:07:35 -0800935 if (!zip64)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800936 err = mz_stream_write_uint32(stream, (uint32_t)uncompressed_size);
937 else
938 err = mz_stream_write_int64(stream, uncompressed_size);
939 }
940
941 return err;
942}
943
944static int32_t mz_zip_read_cd(void *handle)
945{
946 mz_zip *zip = (mz_zip *)handle;
947 uint64_t number_entry_cd64 = 0;
948 uint64_t number_entry = 0;
949 uint64_t number_entry_cd = 0;
950 int64_t eocd_pos = 0;
951 int64_t eocd_pos64 = 0;
952 int64_t value64i = 0;
953 uint16_t value16 = 0;
954 uint32_t value32 = 0;
955 uint64_t value64 = 0;
956 uint16_t comment_size = 0;
957 int32_t comment_read = 0;
958 int32_t err = MZ_OK;
959
960
961 if (zip == NULL)
962 return MZ_PARAM_ERROR;
963
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800964 /* Read and cache central directory records */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800965 err = mz_zip_search_eocd(zip->stream, &eocd_pos);
966 if (err == MZ_OK)
967 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800968 /* The signature, already checked */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800969 err = mz_stream_read_uint32(zip->stream, &value32);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800970 /* Number of this disk */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800971 if (err == MZ_OK)
972 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800973 /* Number of the disk with the start of the central directory */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800974 if (err == MZ_OK)
975 err = mz_stream_read_uint16(zip->stream, &value16);
976 zip->disk_number_with_cd = value16;
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800977 /* Total number of entries in the central dir on this disk */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800978 if (err == MZ_OK)
979 err = mz_stream_read_uint16(zip->stream, &value16);
980 zip->number_entry = value16;
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800981 /* Total number of entries in the central dir */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800982 if (err == MZ_OK)
983 err = mz_stream_read_uint16(zip->stream, &value16);
984 number_entry_cd = value16;
985 if (number_entry_cd != zip->number_entry)
986 err = MZ_FORMAT_ERROR;
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800987 /* Size of the central directory */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800988 if (err == MZ_OK)
989 err = mz_stream_read_uint32(zip->stream, &value32);
990 if (err == MZ_OK)
991 zip->cd_size = value32;
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800992 /* Offset of start of central directory with respect to the starting disk number */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800993 if (err == MZ_OK)
994 err = mz_stream_read_uint32(zip->stream, &value32);
995 if (err == MZ_OK)
996 zip->cd_offset = value32;
Nathan Moinvazirib16ab562018-11-20 16:56:21 -0800997 /* Zip file global comment length */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -0800998 if (err == MZ_OK)
999 err = mz_stream_read_uint16(zip->stream, &comment_size);
1000 if ((err == MZ_OK) && (comment_size > 0))
1001 {
1002 zip->comment = (char *)MZ_ALLOC(comment_size + 1);
1003 if (zip->comment != NULL)
1004 {
1005 comment_read = mz_stream_read(zip->stream, zip->comment, comment_size);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001006 /* Don't fail if incorrect comment length read, not critical */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001007 if (comment_read < 0)
1008 comment_read = 0;
1009 zip->comment[comment_read] = 0;
1010 }
1011 }
1012
1013 if ((err == MZ_OK) && ((number_entry_cd == UINT16_MAX) || (zip->cd_offset == UINT32_MAX)))
1014 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001015 /* Format should be Zip64, as the central directory or file size is too large */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001016 if (mz_zip_search_zip64_eocd(zip->stream, eocd_pos, &eocd_pos64) == MZ_OK)
1017 {
1018 eocd_pos = eocd_pos64;
1019
1020 err = mz_stream_seek(zip->stream, eocd_pos, MZ_SEEK_SET);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001021 /* The signature, already checked */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001022 if (err == MZ_OK)
1023 err = mz_stream_read_uint32(zip->stream, &value32);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001024 /* Size of zip64 end of central directory record */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001025 if (err == MZ_OK)
1026 err = mz_stream_read_uint64(zip->stream, &value64);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001027 /* Version made by */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001028 if (err == MZ_OK)
1029 err = mz_stream_read_uint16(zip->stream, &zip->version_madeby);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001030 /* Version needed to extract */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001031 if (err == MZ_OK)
1032 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001033 /* Number of this disk */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001034 if (err == MZ_OK)
1035 err = mz_stream_read_uint32(zip->stream, &value32);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001036 /* Number of the disk with the start of the central directory */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001037 if (err == MZ_OK)
1038 err = mz_stream_read_uint32(zip->stream, &zip->disk_number_with_cd);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001039 /* Total number of entries in the central directory on this disk */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001040 if (err == MZ_OK)
1041 err = mz_stream_read_uint64(zip->stream, &number_entry);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001042 /* Total number of entries in the central directory */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001043 if (err == MZ_OK)
1044 err = mz_stream_read_uint64(zip->stream, &number_entry_cd64);
1045 if (number_entry == UINT32_MAX)
1046 zip->number_entry = number_entry_cd64;
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001047 /* Size of the central directory */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001048 if (err == MZ_OK)
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08001049 {
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001050 err = mz_stream_read_int64(zip->stream, &zip->cd_size);
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08001051 if (zip->cd_size < 0)
1052 err = MZ_FORMAT_ERROR;
1053 }
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001054 /* Offset of start of central directory with respect to the starting disk number */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001055 if (err == MZ_OK)
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08001056 {
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001057 err = mz_stream_read_int64(zip->stream, &zip->cd_offset);
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08001058 if (zip->cd_offset < 0)
1059 err = MZ_FORMAT_ERROR;
1060 }
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001061 }
1062 else if ((zip->number_entry == UINT16_MAX) || (number_entry_cd != zip->number_entry) ||
1063 (zip->cd_size == UINT16_MAX) || (zip->cd_offset == UINT32_MAX))
1064 {
1065 err = MZ_FORMAT_ERROR;
1066 }
1067 }
1068 }
1069
1070 if (err == MZ_OK)
1071 {
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08001072 mz_zip_print("Zip - Read cd (disk %"PRId32" entries %"PRId64" offset %"PRId64" size %"PRId64")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001073 zip->disk_number_with_cd, zip->number_entry, zip->cd_offset, zip->cd_size);
1074
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001075 /* Verify central directory signature exists at offset */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001076 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
1077 if (err == MZ_OK)
1078 err = mz_stream_read_uint32(zip->stream, &value32);
1079 if (value32 != MZ_ZIP_MAGIC_CENTRALHEADER)
1080 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001081 /* If not found attempt to seek backward to find it */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001082 err = mz_stream_seek(zip->stream, eocd_pos - zip->cd_size, MZ_SEEK_SET);
1083 if (err == MZ_OK)
1084 err = mz_stream_read_uint32(zip->stream, &value32);
1085 if (value32 == MZ_ZIP_MAGIC_CENTRALHEADER)
1086 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001087 /* If found compensate for incorrect locations */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001088 value64i = zip->cd_offset;
1089 zip->cd_offset = eocd_pos - zip->cd_size;
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001090 /* Assume disk has prepended data */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001091 zip->disk_offset_shift = zip->cd_offset - value64i;
1092 }
1093 }
1094 }
1095
1096 if (err == MZ_OK)
1097 {
1098 if (eocd_pos < zip->cd_offset)
1099 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001100 /* End of central dir should always come after central dir */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001101 err = MZ_FORMAT_ERROR;
1102 }
1103 else if (eocd_pos < zip->cd_offset + zip->cd_size)
1104 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001105 /* Truncate size of cd if incorrect size or offset provided */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001106 zip->cd_size = eocd_pos - zip->cd_offset;
1107 }
1108 }
1109
1110 return err;
1111}
1112
1113static int32_t mz_zip_write_cd(void *handle)
1114{
1115 mz_zip *zip = (mz_zip *)handle;
1116 int64_t zip64_eocd_pos_inzip = 0;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001117 int64_t disk_number = 0;
1118 int64_t disk_size = 0;
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001119 int32_t comment_size = 0;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001120 int32_t err = MZ_OK;
1121
1122
1123 if (zip == NULL)
1124 return MZ_PARAM_ERROR;
1125
1126 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number) == MZ_OK)
1127 zip->disk_number_with_cd = (uint32_t)disk_number;
1128 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_SIZE, &disk_size) == MZ_OK && disk_size > 0)
1129 zip->disk_number_with_cd += 1;
1130 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1131
1132 zip->cd_offset = mz_stream_tell(zip->stream);
1133 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_END);
1134 zip->cd_size = (uint32_t)mz_stream_tell(zip->cd_mem_stream);
1135 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_SET);
1136
1137 err = mz_stream_copy(zip->stream, zip->cd_mem_stream, (int32_t)zip->cd_size);
1138
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08001139 mz_zip_print("Zip - Write cd (disk %"PRId32" entries %"PRId64" offset %"PRId64" size %"PRId64")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001140 zip->disk_number_with_cd, zip->number_entry, zip->cd_offset, zip->cd_size);
1141
Antoine Cœur61f224a2019-05-02 01:42:09 +08001142 if (zip->cd_size == 0 && zip->number_entry > 0)
Nathan Moinvaziri64faa252019-04-21 21:38:48 -07001143 {
1144 // Zip does not contain central directory, open with recovery option
1145 return MZ_FORMAT_ERROR;
1146 }
1147
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001148 /* Write the ZIP64 central directory header */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001149 if (zip->cd_offset >= UINT32_MAX || zip->number_entry > UINT16_MAX)
1150 {
1151 zip64_eocd_pos_inzip = mz_stream_tell(zip->stream);
1152
1153 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER64);
1154
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001155 /* Size of this 'zip64 end of central directory' */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001156 if (err == MZ_OK)
1157 err = mz_stream_write_uint64(zip->stream, (uint64_t)44);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001158 /* Version made by */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001159 if (err == MZ_OK)
1160 err = mz_stream_write_uint16(zip->stream, zip->version_madeby);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001161 /* Version needed */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001162 if (err == MZ_OK)
1163 err = mz_stream_write_uint16(zip->stream, (uint16_t)45);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001164 /* Number of this disk */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001165 if (err == MZ_OK)
1166 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001167 /* Number of the disk with the start of the central directory */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001168 if (err == MZ_OK)
1169 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001170 /* Total number of entries in the central dir on this disk */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001171 if (err == MZ_OK)
1172 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001173 /* Total number of entries in the central dir */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001174 if (err == MZ_OK)
1175 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001176 /* Size of the central directory */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001177 if (err == MZ_OK)
1178 err = mz_stream_write_int64(zip->stream, zip->cd_size);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001179 /* Offset of start of central directory with respect to the starting disk number */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001180 if (err == MZ_OK)
1181 err = mz_stream_write_int64(zip->stream, zip->cd_offset);
1182 if (err == MZ_OK)
1183 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDLOCHEADER64);
1184
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001185 /* Number of the disk with the start of the central directory */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001186 if (err == MZ_OK)
1187 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001188 /* Relative offset to the end of zip64 central directory */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001189 if (err == MZ_OK)
1190 err = mz_stream_write_int64(zip->stream, zip64_eocd_pos_inzip);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001191 /* Number of the disk with the start of the central directory */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001192 if (err == MZ_OK)
1193 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd + 1);
1194 }
1195
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001196 /* Write the central directory header */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001197
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001198 /* Signature */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001199 if (err == MZ_OK)
1200 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001201 /* Number of this disk */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001202 if (err == MZ_OK)
1203 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001204 /* Number of the disk with the start of the central directory */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001205 if (err == MZ_OK)
1206 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001207 /* Total number of entries in the central dir on this disk */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001208 if (err == MZ_OK)
1209 {
1210 if (zip->number_entry >= UINT16_MAX)
1211 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
1212 else
1213 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
1214 }
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001215 /* Total number of entries in the central dir */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001216 if (err == MZ_OK)
1217 {
1218 if (zip->number_entry >= UINT16_MAX)
1219 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
1220 else
1221 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
1222 }
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001223 /* Size of the central directory */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001224 if (err == MZ_OK)
1225 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_size);
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001226 /* Offset of start of central directory with respect to the starting disk number */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001227 if (err == MZ_OK)
1228 {
1229 if (zip->cd_offset >= UINT32_MAX)
1230 err = mz_stream_write_uint32(zip->stream, UINT32_MAX);
1231 else
1232 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_offset);
1233 }
1234
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001235 /* Write global comment */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001236 if (zip->comment != NULL)
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001237 {
1238 comment_size = (int32_t)strlen(zip->comment);
1239 if (comment_size > UINT16_MAX)
1240 comment_size = UINT16_MAX;
1241 }
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001242 if (err == MZ_OK)
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001243 err = mz_stream_write_uint16(zip->stream, (uint16_t)comment_size);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001244 if (err == MZ_OK)
1245 {
1246 if (mz_stream_write(zip->stream, zip->comment, comment_size) != comment_size)
1247 err = MZ_READ_ERROR;
1248 }
1249 return err;
1250}
1251
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001252static int32_t mz_zip_recover_cd(void *handle)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001253{
1254 mz_zip *zip = (mz_zip *)handle;
1255 mz_zip_file local_file_info;
1256 void *local_file_info_stream = NULL;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001257 void *cd_mem_stream = NULL;
1258 uint64_t number_entry = 0;
1259 int64_t descriptor_pos = 0;
1260 int64_t disk_offset = 0;
1261 int64_t disk_number = 0;
1262 int64_t compressed_size = 0;
1263 int64_t uncompressed_size = 0;
1264 uint8_t descriptor_magic[4] = MZ_ZIP_MAGIC_DATADESCRIPTORU8;
1265 uint32_t crc32 = 0;
1266 int32_t disk_number_with_cd = 0;
1267 int32_t err = MZ_OK;
1268 uint8_t zip64 = 0;
1269
1270
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001271 mz_zip_print("Zip - Recover cd\n");
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001272
1273 mz_zip_get_cd_mem_stream(handle, &cd_mem_stream);
1274
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001275 /* Determine if we are on a split disk or not */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001276 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, 0);
1277 if (mz_stream_tell(zip->stream) < 0)
1278 {
1279 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1280 mz_stream_seek(zip->stream, 0, MZ_SEEK_SET);
1281 }
1282 else
1283 disk_number_with_cd = 1;
1284
1285 if (mz_stream_is_open(cd_mem_stream) != MZ_OK)
1286 err = mz_stream_mem_open(cd_mem_stream, NULL, MZ_OPEN_MODE_CREATE);
1287
1288 mz_stream_mem_create(&local_file_info_stream);
1289 mz_stream_mem_open(local_file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
1290
1291 while (err == MZ_OK)
1292 {
1293 memset(&local_file_info, 0, sizeof(local_file_info));
1294
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001295 /* Get current offset and disk number for central dir record */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001296 disk_offset = mz_stream_tell(zip->stream);
1297 mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number);
1298
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001299 /* Read local headers */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001300 err = mz_zip_entry_read_header(zip->stream, 1, &local_file_info, local_file_info_stream);
1301
1302 local_file_info.disk_offset = disk_offset;
1303 if (disk_number < 0)
1304 disk_number = 0;
1305 local_file_info.disk_number = (uint32_t)disk_number;
1306
1307 if (err == MZ_OK)
1308 {
1309 if (local_file_info.compressed_size > 0)
1310 {
1311 err = mz_stream_seek(zip->stream, local_file_info.compressed_size, MZ_SEEK_CUR);
1312 }
1313 else if (local_file_info.uncompressed_size > 0)
1314 {
1315 err = mz_stream_find(zip->stream, (const void *)descriptor_magic, sizeof(descriptor_magic),
1316 INT64_MAX, &descriptor_pos);
1317 }
1318 }
1319
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001320 /* Read descriptor if it exists so we can get to the next local header */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001321 if ((err == MZ_OK) && (local_file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR))
1322 {
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08001323 if (mz_zip_extrafield_contains(local_file_info.extrafield,
1324 local_file_info.extrafield_size, MZ_ZIP_EXTENSION_ZIP64, NULL) == MZ_OK)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001325 zip64 = 1;
1326
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001327 err = mz_zip_entry_read_descriptor(zip->stream, zip64, &crc32,
1328 &compressed_size, &uncompressed_size);
1329
1330 if (local_file_info.crc == 0)
1331 local_file_info.crc = crc32;
1332 if (local_file_info.compressed_size == 0)
1333 local_file_info.compressed_size = compressed_size;
1334 if (local_file_info.uncompressed_size == 0)
1335 local_file_info.uncompressed_size = uncompressed_size;
1336 }
1337
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001338 /* Rewrite central dir with local headers and offsets */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001339 if (err == MZ_OK)
1340 err = mz_zip_entry_write_header(cd_mem_stream, 0, &local_file_info);
1341
1342 if (err == MZ_OK)
1343 number_entry += 1;
1344 }
1345
1346 mz_stream_mem_delete(&local_file_info_stream);
1347
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08001348 mz_zip_print("Zip - Recover cd complete (cddisk %"PRId32" entries %"PRId64")\n",
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001349 disk_number_with_cd, number_entry);
1350
1351 if (number_entry == 0)
1352 return err;
1353
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001354 /* Set new upper seek boundary for central dir mem stream */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001355 disk_offset = mz_stream_tell(cd_mem_stream);
1356 mz_stream_mem_set_buffer_limit(cd_mem_stream, (int32_t)disk_offset);
1357
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001358 /* Set new central directory info */
Nathan Moinvaziri4d089732018-11-19 20:24:26 -08001359 mz_zip_set_cd_stream(handle, 0, cd_mem_stream);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001360 mz_zip_set_number_entry(handle, number_entry);
1361 mz_zip_set_disk_number_with_cd(handle, disk_number_with_cd);
1362
1363 return MZ_OK;
1364}
1365
1366void *mz_zip_create(void **handle)
1367{
1368 mz_zip *zip = NULL;
1369
1370 zip = (mz_zip *)MZ_ALLOC(sizeof(mz_zip));
1371 if (zip != NULL)
1372 memset(zip, 0, sizeof(mz_zip));
1373 if (handle != NULL)
1374 *handle = zip;
1375
1376 return zip;
1377}
1378
1379void mz_zip_delete(void **handle)
1380{
1381 mz_zip *zip = NULL;
1382 if (handle == NULL)
1383 return;
1384 zip = (mz_zip *)*handle;
1385 if (zip != NULL)
1386 {
1387 MZ_FREE(zip);
1388 }
1389 *handle = NULL;
1390}
1391
1392int32_t mz_zip_open(void *handle, void *stream, int32_t mode)
1393{
1394 mz_zip *zip = (mz_zip *)handle;
1395 int32_t err = MZ_OK;
1396
1397
1398 if (zip == NULL)
1399 return MZ_PARAM_ERROR;
1400
1401 mz_zip_print("Zip - Open\n");
1402
1403 zip->stream = stream;
1404
1405 mz_stream_mem_create(&zip->cd_mem_stream);
1406
1407 if (mode & MZ_OPEN_MODE_WRITE)
1408 {
1409 mz_stream_mem_open(zip->cd_mem_stream, NULL, MZ_OPEN_MODE_CREATE);
1410 zip->cd_stream = zip->cd_mem_stream;
1411 }
1412 else
1413 {
1414 zip->cd_stream = stream;
1415 }
1416
1417 if ((mode & MZ_OPEN_MODE_READ) || (mode & MZ_OPEN_MODE_APPEND))
1418 {
1419 if ((mode & MZ_OPEN_MODE_CREATE) == 0)
1420 {
1421 err = mz_zip_read_cd(zip);
1422 if (err != MZ_OK)
1423 {
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08001424 mz_zip_print("Zip - Error detected reading cd (%"PRId32")", err);
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001425 if (zip->recover && mz_zip_recover_cd(zip) == MZ_OK)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001426 err = MZ_OK;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001427 }
1428 }
1429
1430 if ((err == MZ_OK) && (mode & MZ_OPEN_MODE_APPEND))
1431 {
1432 if (zip->cd_size > 0)
1433 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001434 /* Store central directory in memory */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001435 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
1436 if (err == MZ_OK)
1437 err = mz_stream_copy(zip->cd_mem_stream, zip->stream, (int32_t)zip->cd_size);
1438 if (err == MZ_OK)
1439 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
1440 }
1441 else
1442 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001443 /* If no central directory, append new zip to end of file */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001444 err = mz_stream_seek(zip->stream, 0, MZ_SEEK_END);
1445 }
Nathan Moinvaziri2ce1fe22018-11-23 11:53:11 -08001446
1447 if (zip->disk_number_with_cd > 0)
1448 {
1449 /* Move to last disk to begin appending */
1450 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, zip->disk_number_with_cd - 1);
1451 }
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001452 }
1453 else
1454 {
1455 zip->cd_start_pos = zip->cd_offset;
1456 }
1457 }
1458
1459 if (err != MZ_OK)
1460 {
1461 mz_zip_close(zip);
1462 return err;
1463 }
1464
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001465 /* Memory streams used to store variable length file info data */
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001466 mz_stream_mem_create(&zip->file_info_stream);
1467 mz_stream_mem_open(zip->file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
1468
1469 mz_stream_mem_create(&zip->local_file_info_stream);
1470 mz_stream_mem_open(zip->local_file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
1471
1472 zip->open_mode = mode;
1473
1474 return err;
1475}
1476
1477int32_t mz_zip_close(void *handle)
1478{
1479 mz_zip *zip = (mz_zip *)handle;
1480 int32_t err = MZ_OK;
1481
1482 if (zip == NULL)
1483 return MZ_PARAM_ERROR;
1484
1485 mz_zip_print("Zip - Close\n");
1486
1487 if (mz_zip_entry_is_open(handle) == MZ_OK)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001488 err = mz_zip_entry_close(handle);
Nathan Moinvazirieffc1422018-11-08 15:24:17 -08001489
1490 if ((err == MZ_OK) && (zip->open_mode & MZ_OPEN_MODE_WRITE))
1491 err = mz_zip_write_cd(handle);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001492
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001493 if (zip->cd_mem_stream != NULL)
1494 {
1495 mz_stream_close(zip->cd_mem_stream);
1496 mz_stream_delete(&zip->cd_mem_stream);
1497 }
1498
1499 if (zip->file_info_stream != NULL)
1500 {
1501 mz_stream_mem_close(zip->file_info_stream);
1502 mz_stream_mem_delete(&zip->file_info_stream);
1503 }
1504 if (zip->local_file_info_stream != NULL)
1505 {
1506 mz_stream_mem_close(zip->local_file_info_stream);
1507 mz_stream_mem_delete(&zip->local_file_info_stream);
1508 }
1509
1510 if (zip->comment)
1511 {
1512 MZ_FREE(zip->comment);
1513 zip->comment = NULL;
1514 }
1515
1516 zip->stream = NULL;
1517 zip->cd_stream = NULL;
1518
1519 return err;
1520}
1521
1522int32_t mz_zip_get_comment(void *handle, const char **comment)
1523{
1524 mz_zip *zip = (mz_zip *)handle;
1525 if (zip == NULL || comment == NULL)
1526 return MZ_PARAM_ERROR;
1527 if (zip->comment == NULL)
1528 return MZ_EXIST_ERROR;
1529 *comment = zip->comment;
1530 return MZ_OK;
1531}
1532
1533int32_t mz_zip_set_comment(void *handle, const char *comment)
1534{
1535 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001536 int32_t comment_size = 0;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001537 if (zip == NULL || comment == NULL)
1538 return MZ_PARAM_ERROR;
1539 if (zip->comment != NULL)
1540 MZ_FREE(zip->comment);
Nathan Moinvaziri2df1ecb2018-12-01 08:59:21 -08001541 comment_size = (int32_t)strlen(comment);
1542 if (comment_size > UINT16_MAX)
1543 return MZ_PARAM_ERROR;
Nathan Moinvaziria6ecab52019-01-22 13:14:01 -08001544 zip->comment = (char *)MZ_ALLOC(comment_size+1);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001545 if (zip->comment == NULL)
1546 return MZ_MEM_ERROR;
Nathan Moinvaziria6ecab52019-01-22 13:14:01 -08001547 memset(zip->comment, 0, comment_size+1);
Nathan Moinvaziri2df1ecb2018-12-01 08:59:21 -08001548 strncpy(zip->comment, comment, comment_size);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001549 return MZ_OK;
1550}
1551
1552int32_t mz_zip_get_version_madeby(void *handle, uint16_t *version_madeby)
1553{
1554 mz_zip *zip = (mz_zip *)handle;
1555 if (zip == NULL || version_madeby == NULL)
1556 return MZ_PARAM_ERROR;
1557 *version_madeby = zip->version_madeby;
1558 return MZ_OK;
1559}
1560
1561int32_t mz_zip_set_version_madeby(void *handle, uint16_t version_madeby)
1562{
1563 mz_zip *zip = (mz_zip *)handle;
1564 if (zip == NULL)
1565 return MZ_PARAM_ERROR;
1566 zip->version_madeby = version_madeby;
1567 return MZ_OK;
1568}
1569
Nathan Moinvaziridd103f42018-11-13 14:50:32 -08001570int32_t mz_zip_set_recover(void *handle, uint8_t recover)
1571{
1572 mz_zip *zip = (mz_zip *)handle;
1573 if (zip == NULL)
1574 return MZ_PARAM_ERROR;
1575 zip->recover = recover;
1576 return MZ_OK;
1577}
1578
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001579int32_t mz_zip_get_stream(void *handle, void **stream)
1580{
1581 mz_zip *zip = (mz_zip *)handle;
1582 if (zip == NULL || stream == NULL)
1583 return MZ_PARAM_ERROR;
1584 *stream = zip->stream;
1585 if (*stream == NULL)
1586 return MZ_EXIST_ERROR;
1587 return MZ_OK;
1588}
1589
Nathan Moinvaziri4d089732018-11-19 20:24:26 -08001590int32_t mz_zip_set_cd_stream(void *handle, int64_t cd_start_pos, void *cd_stream)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001591{
1592 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri4d089732018-11-19 20:24:26 -08001593 if (zip == NULL || cd_stream == NULL)
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001594 return MZ_PARAM_ERROR;
Nathan Moinvaziri4d089732018-11-19 20:24:26 -08001595 zip->cd_stream = cd_stream;
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001596 zip->cd_start_pos = cd_start_pos;
1597 return MZ_OK;
1598}
1599
1600int32_t mz_zip_get_cd_mem_stream(void *handle, void **cd_mem_stream)
1601{
1602 mz_zip *zip = (mz_zip *)handle;
1603 if (zip == NULL || cd_mem_stream == NULL)
1604 return MZ_PARAM_ERROR;
1605 *cd_mem_stream = zip->cd_mem_stream;
1606 if (*cd_mem_stream == NULL)
1607 return MZ_EXIST_ERROR;
1608 return MZ_OK;
1609}
1610
1611static int32_t mz_zip_entry_close_int(void *handle)
1612{
1613 mz_zip *zip = (mz_zip *)handle;
1614
1615 if (zip->crypt_stream != NULL)
1616 mz_stream_delete(&zip->crypt_stream);
1617 zip->crypt_stream = NULL;
1618 if (zip->compress_stream != NULL)
1619 mz_stream_delete(&zip->compress_stream);
1620 zip->compress_stream = NULL;
1621
1622 zip->entry_opened = 0;
1623
1624 return MZ_OK;
1625}
1626
Nathan Moinvaziri26308b22018-08-08 09:40:15 -07001627static int32_t mz_zip_entry_open_int(void *handle, uint8_t raw, int16_t compress_level, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001628{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001629 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001630 int64_t max_total_in = 0;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001631 int64_t header_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001632 int64_t footer_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001633 int32_t err = MZ_OK;
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001634 uint8_t use_crypt = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001635
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001636 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001637 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001638
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001639 switch (zip->file_info.compression_method)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001640 {
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001641 case MZ_COMPRESS_METHOD_STORE:
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001642 case MZ_COMPRESS_METHOD_DEFLATE:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001643#ifdef HAVE_BZIP2
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001644 case MZ_COMPRESS_METHOD_BZIP2:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001645#endif
Nathan Moinvaziri566dfe02018-10-26 00:36:30 -07001646#ifdef HAVE_LZMA
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001647 case MZ_COMPRESS_METHOD_LZMA:
1648#endif
1649 err = MZ_OK;
1650 break;
1651 default:
Nathan Moinvaziri829ffb52018-08-14 14:00:16 -07001652 return MZ_SUPPORT_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001653 }
1654
Nathan Moinvazirif9e34482018-11-27 09:45:09 -08001655#ifndef HAVE_WZAES
1656 if (zip->file_info.aes_version)
1657 return MZ_SUPPORT_ERROR;
1658#endif
1659
Nathan Moinvaziria64c9192018-08-08 17:47:12 -07001660 zip->entry_raw = raw;
1661
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001662 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password != NULL))
1663 {
1664 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
1665 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001666 /* Encrypt only when we are not trying to write raw and password is supplied. */
Nathan Moinvaziria64c9192018-08-08 17:47:12 -07001667 if (!zip->entry_raw)
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001668 use_crypt = 1;
1669 }
1670 else if (zip->open_mode & MZ_OPEN_MODE_READ)
1671 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001672 /* Decrypt only when password is supplied. Don't error when password */
1673 /* is not supplied as we may want to read the raw encrypted data. */
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001674 use_crypt = 1;
1675 }
1676 }
1677
1678 if ((err == MZ_OK) && (use_crypt))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001679 {
Nathan Moinvaziri76fb8902018-11-21 15:14:09 -08001680#ifdef HAVE_WZAES
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001681 if (zip->file_info.aes_version)
1682 {
Nathan Moinvaziri21a31022018-10-24 09:50:16 -07001683 mz_stream_wzaes_create(&zip->crypt_stream);
1684 mz_stream_wzaes_set_password(zip->crypt_stream, password);
1685 mz_stream_wzaes_set_encryption_mode(zip->crypt_stream, zip->file_info.aes_encryption_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001686 }
1687 else
1688#endif
1689 {
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001690#ifdef HAVE_PKCRYPT
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001691 uint8_t verify1 = 0;
1692 uint8_t verify2 = 0;
1693
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001694 /* Info-ZIP modification to ZipCrypto format: */
1695 /* If bit 3 of the general purpose bit flag is set, it uses high byte of 16-bit File Time. */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001696
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001697 if (zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR)
1698 {
1699 uint32_t dos_date = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001700
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001701 dos_date = mz_zip_time_t_to_dos_date(zip->file_info.modified_date);
1702
1703 verify1 = (uint8_t)((dos_date >> 16) & 0xff);
1704 verify2 = (uint8_t)((dos_date >> 8) & 0xff);
1705 }
1706 else
1707 {
1708 verify1 = (uint8_t)((zip->file_info.crc >> 16) & 0xff);
1709 verify2 = (uint8_t)((zip->file_info.crc >> 24) & 0xff);
1710 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001711
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001712 mz_stream_pkcrypt_create(&zip->crypt_stream);
1713 mz_stream_pkcrypt_set_password(zip->crypt_stream, password);
1714 mz_stream_pkcrypt_set_verify(zip->crypt_stream, verify1, verify2);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001715#endif
1716 }
1717 }
1718
1719 if (err == MZ_OK)
1720 {
1721 if (zip->crypt_stream == NULL)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001722 mz_stream_raw_create(&zip->crypt_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001723
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001724 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001725
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001726 err = mz_stream_open(zip->crypt_stream, NULL, zip->open_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001727 }
1728
1729 if (err == MZ_OK)
1730 {
Nathan Moinvaziria64c9192018-08-08 17:47:12 -07001731 if (zip->entry_raw || zip->file_info.compression_method == MZ_COMPRESS_METHOD_STORE)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001732 mz_stream_raw_create(&zip->compress_stream);
Nathan Moinvaziri76fb8902018-11-21 15:14:09 -08001733#if defined(HAVE_ZLIB) || defined(HAVE_LIBCOMP)
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001734 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_DEFLATE)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001735 mz_stream_zlib_create(&zip->compress_stream);
Andy Maloney3909c232018-05-22 09:02:09 -04001736#endif
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001737#ifdef HAVE_BZIP2
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001738 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_BZIP2)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001739 mz_stream_bzip_create(&zip->compress_stream);
1740#endif
1741#ifdef HAVE_LZMA
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001742 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001743 mz_stream_lzma_create(&zip->compress_stream);
1744#endif
1745 else
1746 err = MZ_PARAM_ERROR;
1747 }
1748
1749 if (err == MZ_OK)
1750 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001751 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001752 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001753 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_LEVEL, compress_level);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001754 }
1755 else
1756 {
Nathan Moinvaziri76fb8902018-11-21 15:14:09 -08001757#ifndef HAVE_LIBCOMP
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001758 if (zip->entry_raw || zip->file_info.compression_method == MZ_COMPRESS_METHOD_STORE || zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
Nathan Moinvaziri90d31c72018-11-20 03:17:20 -08001759#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001760 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001761 max_total_in = zip->file_info.compressed_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001762 mz_stream_set_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
1763
1764 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_HEADER_SIZE, &header_size) == MZ_OK)
1765 max_total_in -= header_size;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001766 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_FOOTER_SIZE, &footer_size) == MZ_OK)
1767 max_total_in -= footer_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001768
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001769 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001770 }
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001771 if ((zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA) && (zip->file_info.flag & MZ_ZIP_FLAG_LZMA_EOS_MARKER) == 0)
juanii55bcdaf2018-02-11 20:55:57 -03001772 {
1773 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, zip->file_info.compressed_size);
1774 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT_MAX, zip->file_info.uncompressed_size);
1775 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001776 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001777
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001778 mz_stream_set_base(zip->compress_stream, zip->crypt_stream);
1779
1780 err = mz_stream_open(zip->compress_stream, NULL, zip->open_mode);
1781 }
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001782
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001783 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001784 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001785 zip->entry_opened = 1;
Nathan Moinvazirie63d2312018-11-03 19:45:41 -07001786 zip->entry_crc32 = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001787 }
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001788 else
1789 {
1790 mz_zip_entry_close_int(handle);
1791 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001792
1793 return err;
1794}
1795
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001796int32_t mz_zip_entry_is_open(void *handle)
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001797{
1798 mz_zip *zip = (mz_zip *)handle;
1799 if (zip == NULL)
1800 return MZ_PARAM_ERROR;
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001801 if (zip->entry_opened == 0)
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001802 return MZ_EXIST_ERROR;
1803 return MZ_OK;
1804}
1805
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08001806static int32_t mz_zip_seek_to_local_header(void *handle)
Nathan Moinvaziri829ffb52018-08-14 14:00:16 -07001807{
1808 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri829ffb52018-08-14 14:00:16 -07001809
Nathan Moinvaziri829ffb52018-08-14 14:00:16 -07001810
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08001811 if (zip->file_info.disk_number == zip->disk_number_with_cd)
1812 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1813 else
1814 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, zip->file_info.disk_number);
1815
1816 mz_zip_print("Zip - Entry - Seek local (disk %"PRId32" offset %"PRId64")\n",
1817 zip->file_info.disk_number, zip->file_info.disk_offset);
Nathan Moinvaziri09725902019-02-20 09:15:48 -08001818
1819 /* Guard against seek overflows */
1820 if ((zip->disk_offset_shift > 0) &&
1821 (zip->file_info.disk_offset > (INT64_MAX - zip->disk_offset_shift)))
1822 return MZ_FORMAT_ERROR;
1823
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08001824 return mz_stream_seek(zip->stream, zip->file_info.disk_offset + zip->disk_offset_shift, MZ_SEEK_SET);
Nathan Moinvaziri829ffb52018-08-14 14:00:16 -07001825}
1826
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001827int32_t mz_zip_entry_read_open(void *handle, uint8_t raw, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001828{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001829 mz_zip *zip = (mz_zip *)handle;
1830 int32_t err = MZ_OK;
Nathan Moinvaziriaacf8732018-11-06 09:38:46 -08001831 int32_t err_shift = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001832
Nathan Moinvaziri40427632018-07-22 11:12:40 -07001833#if defined(MZ_ZIP_NO_ENCRYPTION)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001834 if (password != NULL)
Nathan Moinvazirif9e34482018-11-27 09:45:09 -08001835 return MZ_SUPPORT_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001836#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001837 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001838 return MZ_PARAM_ERROR;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001839 if ((zip->open_mode & MZ_OPEN_MODE_READ) == 0)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001840 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001841 if (zip->entry_scanned == 0)
1842 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001843
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08001844 mz_zip_print("Zip - Entry - Read open (raw %"PRId32")\n", raw);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001845
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08001846 err = mz_zip_seek_to_local_header(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001847 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001848 err = mz_zip_entry_read_header(zip->stream, 1, &zip->local_file_info, zip->local_file_info_stream);
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001849
Nathan Moinvaziriaacf8732018-11-06 09:38:46 -08001850 if (err == MZ_FORMAT_ERROR && zip->disk_offset_shift > 0)
1851 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001852 /* Perhaps we didn't compensated correctly for incorrect cd offset */
Nathan Moinvaziriaacf8732018-11-06 09:38:46 -08001853 err_shift = mz_stream_seek(zip->stream, zip->file_info.disk_offset, MZ_SEEK_SET);
1854 if (err_shift == MZ_OK)
1855 err_shift = mz_zip_entry_read_header(zip->stream, 1, &zip->local_file_info, zip->local_file_info_stream);
1856 if (err_shift == MZ_OK)
1857 {
1858 zip->disk_offset_shift = 0;
1859 err = err_shift;
1860 }
1861 }
1862
Nathan Moinvaziria6d1f662018-07-22 10:35:49 -07001863#ifdef MZ_ZIP_NO_DECOMPRESSION
Nathan Moinvaziri2eb29072018-11-23 10:32:21 -08001864 if (!raw && zip->file_info.compression_method != MZ_COMPRESS_METHOD_STORE)
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001865 err = MZ_SUPPORT_ERROR;
Viktor Szakatse7215072018-09-04 15:06:53 +00001866#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001867 if (err == MZ_OK)
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001868 err = mz_zip_entry_open_int(handle, raw, 0, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001869
1870 return err;
1871}
1872
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001873int32_t mz_zip_entry_write_open(void *handle, const mz_zip_file *file_info, int16_t compress_level, uint8_t raw, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001874{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001875 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri6b64b922018-11-23 11:41:35 -08001876 int64_t filename_pos = -1;
Nathan Moinvazirif9e34482018-11-27 09:45:09 -08001877 int64_t extrafield_pos = 0;
1878 int64_t comment_pos = 0;
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -07001879 int64_t linkname_pos = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001880 int64_t disk_number = 0;
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001881 uint8_t is_dir = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001882 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001883
Nathan Moinvaziri40427632018-07-22 11:12:40 -07001884#if defined(MZ_ZIP_NO_ENCRYPTION)
tz-lomb1b25802017-11-10 15:03:02 +03001885 if (password != NULL)
Nathan Moinvazirif9e34482018-11-27 09:45:09 -08001886 return MZ_SUPPORT_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001887#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001888 if (zip == NULL || file_info == NULL || file_info->filename == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001889 return MZ_PARAM_ERROR;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001890
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001891 if (mz_zip_entry_is_open(handle) == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001892 {
1893 err = mz_zip_entry_close(handle);
1894 if (err != MZ_OK)
1895 return err;
1896 }
1897
1898 memcpy(&zip->file_info, file_info, sizeof(mz_zip_file));
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001899
1900 mz_zip_print("Zip - Entry - Write open - %s (level %"PRId16" raw %"PRId8")\n",
1901 zip->file_info.filename, compress_level, raw);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001902
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08001903 mz_stream_seek(zip->file_info_stream, 0, MZ_SEEK_SET);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001904 mz_stream_write(zip->file_info_stream, file_info, sizeof(mz_zip_file));
1905
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001906 /* Copy filename, extrafield, and comment internally */
Nathan Moinvaziri6b64b922018-11-23 11:41:35 -08001907 filename_pos = mz_stream_tell(zip->file_info_stream);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001908 if (file_info->filename != NULL)
Nathan Moinvaziri6b64b922018-11-23 11:41:35 -08001909 mz_stream_write(zip->file_info_stream, file_info->filename, (int32_t)strlen(file_info->filename));
1910 mz_stream_write_uint8(zip->file_info_stream, 0);
1911
1912 extrafield_pos = mz_stream_tell(zip->file_info_stream);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001913 if (file_info->extrafield != NULL)
Nathan Moinvazirica014e62018-08-15 07:31:12 -07001914 mz_stream_write(zip->file_info_stream, file_info->extrafield, file_info->extrafield_size);
Nathan Moinvaziri6b64b922018-11-23 11:41:35 -08001915 mz_stream_write_uint8(zip->file_info_stream, 0);
1916
1917 comment_pos = mz_stream_tell(zip->file_info_stream);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001918 if (file_info->comment != NULL)
Nathan Moinvaziri6b64b922018-11-23 11:41:35 -08001919 mz_stream_write(zip->file_info_stream, file_info->comment, file_info->comment_size);
1920 mz_stream_write_uint8(zip->file_info_stream, 0);
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -07001921
1922 linkname_pos = mz_stream_tell(zip->file_info_stream);
1923 if (file_info->linkname != NULL)
1924 mz_stream_write(zip->file_info_stream, file_info->linkname, (int32_t)strlen(file_info->linkname));
1925 mz_stream_write_uint8(zip->file_info_stream, 0);
Nathan Moinvaziri6b64b922018-11-23 11:41:35 -08001926
Nathan Moinvazirif9e34482018-11-27 09:45:09 -08001927 mz_stream_mem_get_buffer_at(zip->file_info_stream, filename_pos, (const void **)&zip->file_info.filename);
1928 mz_stream_mem_get_buffer_at(zip->file_info_stream, extrafield_pos, (const void **)&zip->file_info.extrafield);
1929 mz_stream_mem_get_buffer_at(zip->file_info_stream, comment_pos, (const void **)&zip->file_info.comment);
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -07001930 mz_stream_mem_get_buffer_at(zip->file_info_stream, linkname_pos, (const void **)&zip->file_info.linkname);
1931
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001932 if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_DEFLATE)
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001933 {
1934 if ((compress_level == 8) || (compress_level == 9))
1935 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_MAX;
1936 if (compress_level == 2)
1937 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_FAST;
1938 if (compress_level == 1)
1939 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_SUPER_FAST;
1940 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001941#ifdef HAVE_LZMA
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001942 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA)
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001943 zip->file_info.flag |= MZ_ZIP_FLAG_LZMA_EOS_MARKER;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001944#endif
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001945
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001946 if (mz_zip_attrib_is_dir(zip->file_info.external_fa, zip->file_info.version_madeby) == MZ_OK)
1947 is_dir = 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001948
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001949 if (!is_dir)
1950 {
1951 zip->file_info.flag |= MZ_ZIP_FLAG_DATA_DESCRIPTOR;
1952 if (password != NULL)
1953 zip->file_info.flag |= MZ_ZIP_FLAG_ENCRYPTED;
1954 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001955
juaniib8887e92018-02-14 00:51:05 -03001956 mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number);
1957 zip->file_info.disk_number = (uint32_t)disk_number;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001958
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001959 zip->file_info.disk_offset = mz_stream_tell(zip->stream);
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001960 zip->file_info.crc = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001961 zip->file_info.compressed_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001962
Nathan Moinvaziri76fb8902018-11-21 15:14:09 -08001963#ifdef HAVE_WZAES
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001964 if (zip->file_info.aes_version && zip->file_info.aes_encryption_mode == 0)
1965 zip->file_info.aes_encryption_mode = MZ_AES_ENCRYPTION_MODE_256;
1966#endif
1967
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001968 if ((compress_level == 0) || (is_dir))
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001969 zip->file_info.compression_method = MZ_COMPRESS_METHOD_STORE;
Viktor Szakats2884e672018-04-30 08:12:13 +00001970
Nathan Moinvaziria6d1f662018-07-22 10:35:49 -07001971#ifdef MZ_ZIP_NO_COMPRESSION
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001972 if (zip->file_info.compression_method != MZ_COMPRESS_METHOD_STORE)
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001973 err = MZ_SUPPORT_ERROR;
1974#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001975 if (err == MZ_OK)
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001976 err = mz_zip_entry_write_header(zip->stream, 1, &zip->file_info);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001977 if (err == MZ_OK)
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001978 err = mz_zip_entry_open_int(handle, raw, compress_level, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001979
1980 return err;
1981}
1982
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001983int32_t mz_zip_entry_read(void *handle, void *buf, int32_t len)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001984{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001985 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001986 int32_t read = 0;
1987
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001988 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001989 return MZ_PARAM_ERROR;
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001990 if (UINT_MAX == UINT16_MAX && len > UINT16_MAX) /* zlib limitation */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001991 return MZ_PARAM_ERROR;
Nathan Moinvazirid7814e92018-07-11 14:54:14 -07001992 if (len == 0)
1993 return MZ_PARAM_ERROR;
1994
Nathan Moinvazirieb80cd92018-07-11 16:45:09 -07001995 if (zip->file_info.compressed_size == 0)
1996 return 0;
1997
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08001998 /* Read entire entry even if uncompressed_size = 0, otherwise */
1999 /* aes encryption validation will fail if compressed_size > 0 */
Nathan Moinvazirie63d2312018-11-03 19:45:41 -07002000 read = mz_stream_read(zip->compress_stream, buf, len);
2001 if (read > 0)
2002 zip->entry_crc32 = mz_crypt_crc32_update(zip->entry_crc32, buf, read);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08002003
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08002004 mz_zip_print("Zip - Entry - Read - %"PRId32" (max %"PRId32")\n", read, len);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08002005
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002006 return read;
2007}
2008
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002009int32_t mz_zip_entry_write(void *handle, const void *buf, int32_t len)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002010{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002011 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07002012 int32_t written = 0;
2013
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07002014 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002015 return MZ_PARAM_ERROR;
Nathan Moinvazirie63d2312018-11-03 19:45:41 -07002016 written = mz_stream_write(zip->compress_stream, buf, len);
2017 if (written > 0)
2018 zip->entry_crc32 = mz_crypt_crc32_update(zip->entry_crc32, buf, written);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08002019
Nathan Moinvaziri264dc182018-11-13 17:42:13 -08002020 mz_zip_print("Zip - Entry - Write - %"PRId32" (max %"PRId32")\n", written, len);
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08002021
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07002022 return written;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002023}
2024
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08002025int32_t mz_zip_entry_read_close(void *handle, uint32_t *crc32, int64_t *compressed_size,
2026 int64_t *uncompressed_size)
2027{
2028 mz_zip *zip = (mz_zip *)handle;
2029 int64_t total_in = 0;
2030 int32_t err = MZ_OK;
2031 uint8_t zip64 = 0;
2032
2033 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
2034 return MZ_PARAM_ERROR;
2035
2036 mz_stream_close(zip->compress_stream);
2037
2038 mz_zip_print("Zip - Entry - Read Close\n");
2039
2040 if (crc32 != NULL)
2041 *crc32 = zip->file_info.crc;
2042 if (compressed_size != NULL)
2043 *compressed_size = zip->file_info.compressed_size;
2044 if (uncompressed_size != NULL)
2045 *uncompressed_size = zip->file_info.uncompressed_size;
2046
2047 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN, &total_in);
2048
2049 if ((zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR) &&
2050 ((zip->file_info.flag & MZ_ZIP_FLAG_MASK_LOCAL_INFO) == 0) &&
2051 (crc32 != NULL || compressed_size != NULL || uncompressed_size != NULL))
2052 {
2053 /* Check to see if data descriptor is zip64 bit format or not */
2054 if (mz_zip_extrafield_contains(zip->local_file_info.extrafield,
2055 zip->local_file_info.extrafield_size, MZ_ZIP_EXTENSION_ZIP64, NULL) == MZ_OK)
2056 zip64 = 1;
2057
2058 err = mz_zip_seek_to_local_header(handle);
2059
2060 /* Seek to end of compressed stream since we might have over-read during compression */
2061 if (err == MZ_OK)
2062 err = mz_stream_seek(zip->stream, MZ_ZIP_SIZE_LD_ITEM +
Nathan Moinvaziri2df1ecb2018-12-01 08:59:21 -08002063 (int64_t)zip->local_file_info.filename_size +
2064 (int64_t)zip->local_file_info.extrafield_size +
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08002065 total_in, MZ_SEEK_CUR);
2066
2067 /* Read data descriptor */
2068 if (err == MZ_OK)
2069 err = mz_zip_entry_read_descriptor(zip->stream, zip64,
2070 crc32, compressed_size, uncompressed_size);
2071 }
2072
2073 /* If entire entry was not read verification will fail */
2074 if ((err == MZ_OK) && (total_in > 0) && (!zip->entry_raw))
2075 {
2076#ifdef HAVE_WZAES
2077 /* AES zip version AE-1 will expect a valid crc as well */
2078 if (zip->file_info.aes_version <= 0x0001)
2079#endif
2080 {
2081 if (zip->entry_crc32 != zip->file_info.crc)
2082 {
2083 mz_zip_print("Zip - Entry - Crc failed (actual 0x%08"PRIx32" expected 0x%08"PRIx32")\n",
2084 zip->entry_crc32, zip->file_info.crc);
2085
2086 err = MZ_CRC_ERROR;
2087 }
2088 }
2089 }
2090
2091 mz_zip_entry_close_int(handle);
2092
2093 return err;
2094}
2095
2096int32_t mz_zip_entry_write_close(void *handle, uint32_t crc32, int64_t compressed_size,
2097 int64_t uncompressed_size)
2098{
2099 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08002100 int32_t err = MZ_OK;
2101 uint8_t zip64 = 0;
2102
2103 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
2104 return MZ_PARAM_ERROR;
2105
2106 mz_stream_close(zip->compress_stream);
2107
2108 if (!zip->entry_raw)
2109 crc32 = zip->entry_crc32;
2110
2111 mz_zip_print("Zip - Entry - Write Close (crc 0x%08"PRIx32" cs %"PRId64" ucs %"PRId64" )\n",
2112 crc32, compressed_size, uncompressed_size);
2113
2114 /* If sizes are not set, then read them from the compression stream */
2115 if (compressed_size < 0)
2116 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT, &compressed_size);
2117 if (uncompressed_size < 0)
Nathan Moinvazirid9318432018-12-01 07:56:28 -08002118 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN, &uncompressed_size);
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08002119
2120 if (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
2121 {
2122 mz_stream_set_base(zip->crypt_stream, zip->stream);
2123 err = mz_stream_close(zip->crypt_stream);
2124
2125 mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_OUT, &compressed_size);
2126 }
2127
2128 if ((err == MZ_OK) && (zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR))
2129 {
2130 /* Determine if we need to write data descriptor in zip64 format,
2131 if local extrafield was saved with zip64 extrafield */
2132 if (zip->file_info.zip64 == MZ_ZIP64_AUTO)
2133 {
2134 if (zip->file_info.uncompressed_size >= UINT32_MAX)
2135 zip64 = 1;
2136 if (zip->file_info.compressed_size >= UINT32_MAX)
2137 zip64 = 1;
2138 if (zip->file_info.disk_offset >= UINT32_MAX)
2139 zip64 = 1;
2140 else if (zip->file_info.uncompressed_size == 0)
2141 zip64 = 1;
2142 }
2143 else if (zip->file_info.zip64 == MZ_ZIP64_FORCE)
2144 {
2145 zip64 = 1;
2146 }
2147
2148 if (zip->file_info.flag & MZ_ZIP_FLAG_MASK_LOCAL_INFO)
2149 err = mz_zip_entry_write_descriptor(zip->stream,
2150 zip64, 0, compressed_size, 0);
2151 else
2152 err = mz_zip_entry_write_descriptor(zip->stream,
2153 zip64, crc32, compressed_size, uncompressed_size);
2154 }
2155
2156 /* Write file info to central directory */
2157
2158 mz_zip_print("Zip - Entry - Write cd (ucs %"PRId64" cs %"PRId64" crc 0x%08"PRIx32")\n",
2159 uncompressed_size, compressed_size, crc32);
2160
2161 zip->file_info.crc = crc32;
2162 zip->file_info.compressed_size = compressed_size;
2163 zip->file_info.uncompressed_size = uncompressed_size;
2164
2165 if (err == MZ_OK)
2166 err = mz_zip_entry_write_header(zip->cd_mem_stream, 0, &zip->file_info);
2167
2168 zip->number_entry += 1;
2169
2170 mz_zip_entry_close_int(handle);
2171
2172 return err;
2173}
2174
2175int32_t mz_zip_entry_is_dir(void *handle)
2176{
2177 mz_zip *zip = (mz_zip *)handle;
2178 int32_t filename_length = 0;
2179
2180 if (zip == NULL)
2181 return MZ_PARAM_ERROR;
2182 if (zip->entry_scanned == 0)
2183 return MZ_PARAM_ERROR;
2184 if (mz_zip_attrib_is_dir(zip->file_info.external_fa, zip->file_info.version_madeby) == MZ_OK)
2185 return MZ_OK;
2186
2187 filename_length = (int32_t)strlen(zip->file_info.filename);
2188 if (filename_length > 0)
2189 {
2190 if ((zip->file_info.filename[filename_length - 1] == '/') ||
2191 (zip->file_info.filename[filename_length - 1] == '\\'))
2192 return MZ_OK;
2193 }
2194 return MZ_EXIST_ERROR;
2195}
2196
Nathan Moinvaziri3249eac2019-05-02 21:07:39 -07002197int32_t mz_zip_entry_is_symlink(void *handle)
2198{
2199 mz_zip *zip = (mz_zip *)handle;
2200
2201 if (zip == NULL)
2202 return MZ_PARAM_ERROR;
2203 if (zip->entry_scanned == 0)
2204 return MZ_PARAM_ERROR;
Nathan Moinvaziri426543d2019-05-07 09:46:25 -07002205 if (mz_zip_attrib_is_symlink(zip->file_info.external_fa, zip->file_info.version_madeby) != MZ_OK)
2206 return MZ_EXIST_ERROR;
Nathan Moinvaziria5a1d5d2019-05-05 20:13:58 -07002207 if (zip->file_info.linkname == NULL || *zip->file_info.linkname == 0)
2208 return MZ_EXIST_ERROR;
2209
2210 return MZ_OK;
Nathan Moinvaziri3249eac2019-05-02 21:07:39 -07002211}
2212
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002213int32_t mz_zip_entry_get_info(void *handle, mz_zip_file **file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002214{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002215 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07002216
2217 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002218 return MZ_PARAM_ERROR;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07002219
2220 if ((zip->open_mode & MZ_OPEN_MODE_WRITE) == 0)
2221 {
2222 if (!zip->entry_scanned)
2223 return MZ_PARAM_ERROR;
2224 }
2225
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002226 *file_info = &zip->file_info;
2227 return MZ_OK;
2228}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002229
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002230int32_t mz_zip_entry_get_local_info(void *handle, mz_zip_file **local_file_info)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002231{
2232 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07002233 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002234 return MZ_PARAM_ERROR;
2235 *local_file_info = &zip->local_file_info;
2236 return MZ_OK;
2237}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002238
Nathan Moinvaziri915c5132018-10-26 20:00:52 -07002239int32_t mz_zip_entry_set_extrafield(void *handle, const uint8_t *extrafield, uint16_t extrafield_size)
2240{
2241 mz_zip *zip = (mz_zip *)handle;
2242
2243 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
2244 return MZ_PARAM_ERROR;
2245
2246 zip->file_info.extrafield = extrafield;
2247 zip->file_info.extrafield_size = extrafield_size;
2248 return MZ_OK;
2249}
2250
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002251int32_t mz_zip_entry_close(void *handle)
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -07002252{
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08002253 return mz_zip_entry_close_raw(handle, UINT64_MAX, 0);
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -07002254}
2255
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07002256int32_t mz_zip_entry_close_raw(void *handle, int64_t uncompressed_size, uint32_t crc32)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002257{
2258 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002259 int32_t err = MZ_OK;
2260
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07002261 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002262 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002263
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07002264 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08002265 err = mz_zip_entry_write_close(handle, crc32, UINT64_MAX, uncompressed_size);
2266 else
2267 err = mz_zip_entry_read_close(handle, NULL, NULL, NULL);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002268
2269 return err;
2270}
2271
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002272static int32_t mz_zip_goto_next_entry_int(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002273{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002274 mz_zip *zip = (mz_zip *)handle;
2275 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002276
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002277 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002278 return MZ_PARAM_ERROR;
2279
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002280 zip->entry_scanned = 0;
2281
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002282 mz_stream_set_prop_int64(zip->cd_stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
Viktor Szakats915b82e2018-04-24 10:02:39 +00002283
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07002284 err = mz_stream_seek(zip->cd_stream, zip->cd_current_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002285 if (err == MZ_OK)
Nathan Moinvaziricda36002017-10-21 09:37:18 -07002286 err = mz_zip_entry_read_header(zip->cd_stream, 0, &zip->file_info, zip->file_info_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002287 if (err == MZ_OK)
2288 zip->entry_scanned = 1;
2289 return err;
2290}
2291
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07002292int32_t mz_zip_set_number_entry(void *handle, uint64_t number_entry)
2293{
2294 mz_zip *zip = (mz_zip *)handle;
2295 if (zip == NULL)
2296 return MZ_PARAM_ERROR;
2297 zip->number_entry = number_entry;
2298 return MZ_OK;
2299}
2300
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07002301int32_t mz_zip_get_number_entry(void *handle, uint64_t *number_entry)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07002302{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002303 mz_zip *zip = (mz_zip *)handle;
2304 if (zip == NULL || number_entry == NULL)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07002305 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07002306 *number_entry = zip->number_entry;
2307 return MZ_OK;
2308}
2309
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08002310int32_t mz_zip_set_disk_number_with_cd(void *handle, uint32_t disk_number_with_cd)
2311{
2312 mz_zip *zip = (mz_zip *)handle;
2313 if (zip == NULL)
2314 return MZ_PARAM_ERROR;
2315 zip->disk_number_with_cd = disk_number_with_cd;
2316 return MZ_OK;
2317}
2318
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002319int32_t mz_zip_get_disk_number_with_cd(void *handle, uint32_t *disk_number_with_cd)
juanii566b9782018-02-10 15:07:54 -03002320{
2321 mz_zip *zip = (mz_zip *)handle;
2322 if (zip == NULL || disk_number_with_cd == NULL)
2323 return MZ_PARAM_ERROR;
2324 *disk_number_with_cd = zip->disk_number_with_cd;
2325 return MZ_OK;
2326}
2327
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002328int64_t mz_zip_get_entry(void *handle)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002329{
2330 mz_zip *zip = (mz_zip *)handle;
2331
2332 if (zip == NULL)
2333 return MZ_PARAM_ERROR;
2334
2335 return zip->cd_current_pos;
2336}
2337
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07002338int32_t mz_zip_goto_entry(void *handle, int64_t cd_pos)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002339{
2340 mz_zip *zip = (mz_zip *)handle;
2341
2342 if (zip == NULL)
2343 return MZ_PARAM_ERROR;
2344
2345 if (cd_pos < zip->cd_start_pos || cd_pos > zip->cd_start_pos + zip->cd_size)
2346 return MZ_PARAM_ERROR;
2347
2348 zip->cd_current_pos = cd_pos;
2349
2350 return mz_zip_goto_next_entry_int(handle);
2351}
2352
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002353int32_t mz_zip_goto_first_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002354{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002355 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002356
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002357 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002358 return MZ_PARAM_ERROR;
2359
Nathan Moinvaziricda36002017-10-21 09:37:18 -07002360 zip->cd_current_pos = zip->cd_start_pos;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002361
2362 return mz_zip_goto_next_entry_int(handle);
2363}
2364
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002365int32_t mz_zip_goto_next_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002366{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002367 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002368
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002369 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002370 return MZ_PARAM_ERROR;
2371
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07002372 zip->cd_current_pos += (int64_t)MZ_ZIP_SIZE_CD_ITEM + zip->file_info.filename_size +
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002373 zip->file_info.extrafield_size + zip->file_info.comment_size;
2374
2375 return mz_zip_goto_next_entry_int(handle);
2376}
2377
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002378int32_t mz_zip_locate_entry(void *handle, const char *filename, uint8_t ignore_case)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002379{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07002380 mz_zip *zip = (mz_zip *)handle;
2381 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002382 int32_t result = 0;
2383
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07002384 if (zip == NULL || filename == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002385 return MZ_PARAM_ERROR;
2386
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002387 /* If we are already on the current entry, no need to search */
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07002388 if ((zip->entry_scanned) && (zip->file_info.filename != NULL))
2389 {
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002390 result = mz_zip_path_compare(zip->file_info.filename, filename, ignore_case);
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07002391 if (result == 0)
2392 return MZ_OK;
2393 }
2394
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002395 /* Search all entries starting at the first */
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002396 err = mz_zip_goto_first_entry(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002397 while (err == MZ_OK)
2398 {
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002399 result = mz_zip_path_compare(zip->file_info.filename, filename, ignore_case);
2400 if (result == 0)
2401 return MZ_OK;
2402
2403 err = mz_zip_goto_next_entry(handle);
2404 }
2405
2406 return err;
2407}
2408
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002409int32_t mz_zip_locate_first_entry(void *handle, void *userdata, mz_zip_locate_entry_cb cb)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002410{
2411 mz_zip *zip = (mz_zip *)handle;
2412 int32_t err = MZ_OK;
2413 int32_t result = 0;
2414
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002415 /* Search first entry looking for match */
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002416 err = mz_zip_goto_first_entry(handle);
2417 if (err != MZ_OK)
2418 return err;
2419
2420 result = cb(handle, userdata, &zip->file_info);
2421 if (result == 0)
2422 return MZ_OK;
2423
2424 return mz_zip_locate_next_entry(handle, userdata, cb);
2425}
2426
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07002427int32_t mz_zip_locate_next_entry(void *handle, void *userdata, mz_zip_locate_entry_cb cb)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002428{
2429 mz_zip *zip = (mz_zip *)handle;
2430 int32_t err = MZ_OK;
2431 int32_t result = 0;
2432
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002433 /* Search next entries looking for match */
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002434 err = mz_zip_goto_next_entry(handle);
2435 while (err == MZ_OK)
2436 {
2437 result = cb(handle, userdata, &zip->file_info);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07002438 if (result == 0)
2439 return MZ_OK;
2440
2441 err = mz_zip_goto_next_entry(handle);
2442 }
2443
2444 return err;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002445}
2446
2447/***************************************************************************/
2448
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002449int32_t mz_zip_attrib_is_dir(uint32_t attrib, int32_t version_madeby)
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07002450{
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002451 uint32_t posix_attrib = 0;
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07002452 uint8_t system = MZ_HOST_SYSTEM(version_madeby);
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002453 int32_t err = MZ_OK;
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07002454
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002455 err = mz_zip_attrib_convert(system, attrib, MZ_HOST_SYSTEM_UNIX, &posix_attrib);
2456 if (err == MZ_OK)
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07002457 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002458 if ((posix_attrib & 0170000) == 0040000) /* S_ISDIR */
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07002459 return MZ_OK;
2460 }
2461
2462 return MZ_EXIST_ERROR;
2463}
2464
Nathan Moinvaziri426543d2019-05-07 09:46:25 -07002465int32_t mz_zip_attrib_is_symlink(uint32_t attrib, int32_t version_madeby)
2466{
2467 uint32_t posix_attrib = 0;
2468 uint8_t system = MZ_HOST_SYSTEM(version_madeby);
2469 int32_t err = MZ_OK;
2470
2471 err = mz_zip_attrib_convert(system, attrib, MZ_HOST_SYSTEM_UNIX, &posix_attrib);
2472 if (err == MZ_OK)
2473 {
2474 if ((posix_attrib & 0170000) == 0120000) /* S_ISLNK */
2475 return MZ_OK;
2476 }
2477
2478 return MZ_EXIST_ERROR;
2479}
2480
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002481int32_t mz_zip_attrib_convert(uint8_t src_sys, uint32_t src_attrib, uint8_t target_sys, uint32_t *target_attrib)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002482{
2483 if (target_attrib == NULL)
2484 return MZ_PARAM_ERROR;
2485
2486 *target_attrib = 0;
2487
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002488 if ((src_sys == MZ_HOST_SYSTEM_MSDOS) || (src_sys == MZ_HOST_SYSTEM_WINDOWS_NTFS))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002489 {
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002490 if ((target_sys == MZ_HOST_SYSTEM_MSDOS) || (target_sys == MZ_HOST_SYSTEM_WINDOWS_NTFS))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002491 {
2492 *target_attrib = src_attrib;
2493 return MZ_OK;
2494 }
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002495 if ((target_sys == MZ_HOST_SYSTEM_UNIX) || (target_sys == MZ_HOST_SYSTEM_OSX_DARWIN))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002496 return mz_zip_attrib_win32_to_posix(src_attrib, target_attrib);
2497 }
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002498 else if ((src_sys == MZ_HOST_SYSTEM_UNIX) || (src_sys == MZ_HOST_SYSTEM_OSX_DARWIN))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002499 {
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002500 if ((target_sys == MZ_HOST_SYSTEM_UNIX) || (target_sys == MZ_HOST_SYSTEM_OSX_DARWIN))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002501 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002502 /* If high bytes are set, it contains unix specific attributes */
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002503 if ((src_attrib >> 16) != 0)
2504 src_attrib >>= 16;
2505
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002506 *target_attrib = src_attrib;
2507 return MZ_OK;
2508 }
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002509 if ((target_sys == MZ_HOST_SYSTEM_MSDOS) || (target_sys == MZ_HOST_SYSTEM_WINDOWS_NTFS))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002510 return mz_zip_attrib_posix_to_win32(src_attrib, target_attrib);
2511 }
2512
2513 return MZ_SUPPORT_ERROR;
2514}
2515
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002516int32_t mz_zip_attrib_posix_to_win32(uint32_t posix_attrib, uint32_t *win32_attrib)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002517{
2518 if (win32_attrib == NULL)
2519 return MZ_PARAM_ERROR;
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002520
2521 *win32_attrib = 0;
2522
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002523 /* S_IWUSR | S_IWGRP | S_IWOTH | S_IXUSR | S_IXGRP | S_IXOTH */
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002524 if ((posix_attrib & 0000333) == 0 && (posix_attrib & 0000444) != 0)
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002525 *win32_attrib |= 0x01; /* FILE_ATTRIBUTE_READONLY */
Nathan Moinvaziri426543d2019-05-07 09:46:25 -07002526 /* S_IFLNK */
2527 if ((posix_attrib & 0170000) == 0120000)
2528 *win32_attrib |= 0x400; /* FILE_ATTRIBUTE_REPARSE_POINT */
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002529 /* S_IFDIR */
Nathan Moinvaziri7d94b1f2019-05-08 23:41:47 -07002530 else if ((posix_attrib & 0170000) == 0040000)
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002531 *win32_attrib |= 0x10; /* FILE_ATTRIBUTE_DIRECTORY */
2532 /* S_IFREG */
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002533 else
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002534 *win32_attrib |= 0x80; /* FILE_ATTRIBUTE_NORMAL */
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002535
2536 return MZ_OK;
2537}
2538
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002539int32_t mz_zip_attrib_win32_to_posix(uint32_t win32_attrib, uint32_t *posix_attrib)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002540{
2541 if (posix_attrib == NULL)
2542 return MZ_PARAM_ERROR;
2543
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002544 *posix_attrib = 0000444; /* S_IRUSR | S_IRGRP | S_IROTH */
2545 /* FILE_ATTRIBUTE_READONLY */
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002546 if ((win32_attrib & 0x01) == 0)
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002547 *posix_attrib |= 0000222; /* S_IWUSR | S_IWGRP | S_IWOTH */
Nathan Moinvaziri426543d2019-05-07 09:46:25 -07002548 /* FILE_ATTRIBUTE_REPARSE_POINT */
2549 if ((win32_attrib & 0x400) == 0x400)
2550 *posix_attrib |= 0120000; /* S_IFLNK */
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002551 /* FILE_ATTRIBUTE_DIRECTORY */
Nathan Moinvaziri7d94b1f2019-05-08 23:41:47 -07002552 else if ((win32_attrib & 0x10) == 0x10)
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002553 *posix_attrib |= 0040111; /* S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH */
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002554 else
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002555 *posix_attrib |= 0100000; /* S_IFREG */
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002556
2557 return MZ_OK;
2558}
2559
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07002560/***************************************************************************/
2561
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07002562int32_t mz_zip_extrafield_find(void *stream, uint16_t type, uint16_t *length)
2563{
2564 int32_t err = MZ_OK;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07002565 uint16_t field_type = 0;
2566 uint16_t field_length = 0;
2567
2568 do
2569 {
2570 err = mz_stream_read_uint16(stream, &field_type);
2571 if (err == MZ_OK)
2572 err = mz_stream_read_uint16(stream, &field_length);
2573 if (err != MZ_OK)
2574 break;
2575
2576 if (type == field_type)
2577 {
2578 if (length != NULL)
2579 *length = field_length;
2580 return MZ_OK;
2581 }
2582
Nathan Moinvaziri0dfcdf92018-11-07 20:33:47 -08002583 err = mz_stream_seek(stream, field_length, MZ_SEEK_CUR);
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07002584 }
2585 while (err == MZ_OK);
2586
2587 return MZ_EXIST_ERROR;
2588}
2589
Nathan Moinvaziriba1db162018-11-23 10:07:35 -08002590int32_t mz_zip_extrafield_contains(const uint8_t *extrafield, int32_t extrafield_size,
2591 uint16_t type, uint16_t *length)
2592{
2593 void *file_extra_stream = NULL;
2594 int32_t err = MZ_OK;
2595
2596 if (extrafield == NULL || extrafield_size == 0)
2597 return MZ_PARAM_ERROR;
2598
2599 mz_stream_mem_create(&file_extra_stream);
2600 mz_stream_mem_set_buffer(file_extra_stream, (void *)extrafield, extrafield_size);
2601
2602 err = mz_zip_extrafield_find(file_extra_stream, type, length);
2603
2604 mz_stream_mem_delete(&file_extra_stream);
2605
2606 return err;
2607}
2608
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07002609int32_t mz_zip_extrafield_read(void *stream, uint16_t *type, uint16_t *length)
2610{
2611 int32_t err = MZ_OK;
2612 if (type == NULL || length == NULL)
2613 return MZ_PARAM_ERROR;
2614 err = mz_stream_read_uint16(stream, type);
2615 if (err == MZ_OK)
2616 err = mz_stream_read_uint16(stream, length);
2617 return err;
2618}
2619
Nathan Moinvaziri1d6d1832018-11-10 09:03:55 -08002620int32_t mz_zip_extrafield_write(void *stream, uint16_t type, uint16_t length)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07002621{
2622 int32_t err = MZ_OK;
2623 err = mz_stream_write_uint16(stream, type);
2624 if (err == MZ_OK)
2625 err = mz_stream_write_uint16(stream, length);
2626 return err;
2627}
2628
2629/***************************************************************************/
2630
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002631static int32_t mz_zip_invalid_date(const struct tm *ptm)
2632{
2633#define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max))
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002634 return (!datevalue_in_range(0, 127 + 80, ptm->tm_year) || /* 1980-based year, allow 80 extra */
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002635 !datevalue_in_range(0, 11, ptm->tm_mon) ||
2636 !datevalue_in_range(1, 31, ptm->tm_mday) ||
2637 !datevalue_in_range(0, 23, ptm->tm_hour) ||
2638 !datevalue_in_range(0, 59, ptm->tm_min) ||
2639 !datevalue_in_range(0, 59, ptm->tm_sec));
2640#undef datevalue_in_range
2641}
2642
2643static void mz_zip_dosdate_to_raw_tm(uint64_t dos_date, struct tm *ptm)
2644{
2645 uint64_t date = (uint64_t)(dos_date >> 16);
2646
Nathan Moinvaziri1d6d1832018-11-10 09:03:55 -08002647 ptm->tm_mday = (uint16_t)(date & 0x1f);
2648 ptm->tm_mon = (uint16_t)(((date & 0x1E0) / 0x20) - 1);
2649 ptm->tm_year = (uint16_t)(((date & 0x0FE00) / 0x0200) + 80);
2650 ptm->tm_hour = (uint16_t)((dos_date & 0xF800) / 0x800);
2651 ptm->tm_min = (uint16_t)((dos_date & 0x7E0) / 0x20);
2652 ptm->tm_sec = (uint16_t)(2 * (dos_date & 0x1f));
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002653 ptm->tm_isdst = -1;
2654}
2655
2656int32_t mz_zip_dosdate_to_tm(uint64_t dos_date, struct tm *ptm)
2657{
2658 if (ptm == NULL)
2659 return MZ_PARAM_ERROR;
2660
2661 mz_zip_dosdate_to_raw_tm(dos_date, ptm);
2662
2663 if (mz_zip_invalid_date(ptm))
2664 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002665 /* Invalid date stored, so don't return it */
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002666 memset(ptm, 0, sizeof(struct tm));
2667 return MZ_FORMAT_ERROR;
2668 }
2669 return MZ_OK;
2670}
2671
2672time_t mz_zip_dosdate_to_time_t(uint64_t dos_date)
2673{
2674 struct tm ptm;
2675 mz_zip_dosdate_to_raw_tm(dos_date, &ptm);
2676 return mktime(&ptm);
2677}
2678
2679int32_t mz_zip_time_t_to_tm(time_t unix_time, struct tm *ptm)
2680{
Nathan Moinvaziricd3e9e12019-04-28 10:22:49 -07002681 struct tm ltm;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002682 if (ptm == NULL)
2683 return MZ_PARAM_ERROR;
Nathan Moinvaziri2e0a20a2019-04-28 13:03:11 -07002684 if (localtime_r(&unix_time, &ltm) == NULL) /* Returns a 1900-based year */
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002685 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002686 /* Invalid date stored, so don't return it */
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002687 memset(ptm, 0, sizeof(struct tm));
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07002688 return MZ_INTERNAL_ERROR;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002689 }
Nathan Moinvaziricd3e9e12019-04-28 10:22:49 -07002690 memcpy(ptm, &ltm, sizeof(struct tm));
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002691 return MZ_OK;
2692}
2693
2694uint32_t mz_zip_time_t_to_dos_date(time_t unix_time)
2695{
2696 struct tm ptm;
2697 mz_zip_time_t_to_tm(unix_time, &ptm);
2698 return mz_zip_tm_to_dosdate((const struct tm *)&ptm);
2699}
2700
2701uint32_t mz_zip_tm_to_dosdate(const struct tm *ptm)
2702{
Nathan Moinvazirie33916b2018-05-01 13:45:08 -07002703 struct tm fixed_tm;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002704
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002705 /* Years supported: */
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002706
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002707 /* [00, 79] (assumed to be between 2000 and 2079) */
2708 /* [80, 207] (assumed to be between 1980 and 2107, typical output of old */
2709 /* software that does 'year-1900' to get a double digit year) */
2710 /* [1980, 2107] (due to format limitations, only years 1980-2107 can be stored.) */
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002711
2712 memcpy(&fixed_tm, ptm, sizeof(struct tm));
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002713 if (fixed_tm.tm_year >= 1980) /* range [1980, 2107] */
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002714 fixed_tm.tm_year -= 1980;
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002715 else if (fixed_tm.tm_year >= 80) /* range [80, 207] */
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002716 fixed_tm.tm_year -= 80;
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002717 else /* range [00, 79] */
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002718 fixed_tm.tm_year += 20;
2719
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002720 if (mz_zip_invalid_date(&fixed_tm))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002721 return 0;
2722
Anand K Mistry57b65f82018-11-09 10:52:19 +11002723 return (((uint32_t)fixed_tm.tm_mday + (32 * ((uint32_t)fixed_tm.tm_mon + 1)) + (512 * (uint32_t)fixed_tm.tm_year)) << 16) |
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07002724 (((uint32_t)fixed_tm.tm_sec / 2) + (32 * (uint32_t)fixed_tm.tm_min) + (2048 * (uint32_t)fixed_tm.tm_hour));
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002725}
2726
2727int32_t mz_zip_ntfs_to_unix_time(uint64_t ntfs_time, time_t *unix_time)
2728{
2729 *unix_time = (time_t)((ntfs_time - 116444736000000000LL) / 10000000);
2730 return MZ_OK;
2731}
2732
2733int32_t mz_zip_unix_to_ntfs_time(time_t unix_time, uint64_t *ntfs_time)
2734{
2735 *ntfs_time = ((uint64_t)unix_time * 10000000) + 116444736000000000LL;
2736 return MZ_OK;
2737}
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002738
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002739/***************************************************************************/
2740
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002741int32_t mz_zip_path_compare(const char *path1, const char *path2, uint8_t ignore_case)
2742{
2743 do
2744 {
2745 if ((*path1 == '\\' && *path2 == '/') ||
2746 (*path2 == '\\' && *path1 == '/'))
2747 {
Nathan Moinvazirib16ab562018-11-20 16:56:21 -08002748 /* Ignore comparison of path slashes */
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002749 }
2750 else if (ignore_case)
2751 {
2752 if (tolower(*path1) != tolower(*path2))
2753 break;
2754 }
2755 else if (*path1 != *path2)
2756 {
2757 break;
2758 }
2759
2760 path1 += 1;
2761 path2 += 1;
2762 }
2763 while (*path1 != 0 && *path2 != 0);
2764
2765 if (ignore_case)
2766 return (int32_t)(tolower(*path1) - tolower(*path2));
2767
2768 return (int32_t)(*path1 - *path2);
2769}
2770
2771/***************************************************************************/