1 /*
2  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
3  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
4  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
5  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
6  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
7  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
8  * OTHER DEALINGS IN THE SOFTWARE.
9  */
10 #define __STDC_WANT_LIB_EXT1__ 1
11 
12 #include <errno.h>
13 #include <sys/stat.h>
14 #include <time.h>
15 
16 #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) ||              \
17     defined(__MINGW32__)
18 /* Win32, DOS, MSVC, MSVS */
19 #include <direct.h>
20 
21 #define MKDIR(DIRNAME) _mkdir(DIRNAME)
22 #define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL)
23 #define HAS_DEVICE(P)                                                          \
24   ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) &&   \
25    (P)[1] == ':')
26 #define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0)
27 
28 #else
29 
30 #include <unistd.h> // needed for symlink()
31 
32 #define MKDIR(DIRNAME) mkdir(DIRNAME, 0755)
33 #define STRCLONE(STR) ((STR) ? strdup(STR) : NULL)
34 
35 #endif
36 
37 #ifdef __MINGW32__
38 #include <sys/types.h>
39 #include <unistd.h>
40 #endif
41 
42 #include "miniz.h"
43 #include "zip.h"
44 
45 #ifdef _MSC_VER
46 #include <io.h>
47 
48 #define ftruncate(fd, sz) (-(_chsize_s((fd), (sz)) != 0))
49 #define fileno _fileno
50 #endif
51 
52 #ifndef HAS_DEVICE
53 #define HAS_DEVICE(P) 0
54 #endif
55 
56 #ifndef FILESYSTEM_PREFIX_LEN
57 #define FILESYSTEM_PREFIX_LEN(P) 0
58 #endif
59 
60 #ifndef ISSLASH
61 #define ISSLASH(C) ((C) == '/' || (C) == '\\')
62 #endif
63 
64 #define CLEANUP(ptr)                                                           \
65   do {                                                                         \
66     if (ptr) {                                                                 \
67       free((void *)ptr);                                                       \
68       ptr = NULL;                                                              \
69     }                                                                          \
70   } while (0)
71 
72 struct zip_entry_t {
73   int index;
74   char *name;
75   mz_uint64 uncomp_size;
76   mz_uint64 comp_size;
77   mz_uint32 uncomp_crc32;
78   mz_uint64 offset;
79   mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
80   mz_uint64 header_offset;
81   mz_uint16 method;
82   mz_zip_writer_add_state state;
83   tdefl_compressor comp;
84   mz_uint32 external_attr;
85   time_t m_time;
86 };
87 
88 struct zip_t {
89   mz_zip_archive archive;
90   mz_uint level;
91   struct zip_entry_t entry;
92 };
93 
94 enum zip_modify_t {
95   MZ_KEEP = 0,
96   MZ_DELETE = 1,
97   MZ_MOVE = 2,
98 };
99 
100 struct zip_entry_mark_t {
101   int file_index;
102   enum zip_modify_t type;
103   mz_uint64 m_local_header_ofs;
104   mz_uint64 lf_length;
105 };
106 
107 static const char *const zip_errlist[30] = {
108     NULL,
109     "not initialized\0",
110     "invalid entry name\0",
111     "entry not found\0",
112     "invalid zip mode\0",
113     "invalid compression level\0",
114     "no zip 64 support\0",
115     "memset error\0",
116     "cannot write data to entry\0",
117     "cannot initialize tdefl compressor\0",
118     "invalid index\0",
119     "header not found\0",
120     "cannot flush tdefl buffer\0",
121     "cannot write entry header\0",
122     "cannot create entry header\0",
123     "cannot write to central dir\0",
124     "cannot open file\0",
125     "invalid entry type\0",
126     "extracting data using no memory allocation\0",
127     "file not found\0",
128     "no permission\0",
129     "out of memory\0",
130     "invalid zip archive name\0",
131     "make dir error\0",
132     "symlink error\0",
133     "close archive error\0",
134     "capacity size too small\0",
135     "fseek error\0",
136     "fread error\0",
137     "fwrite error\0",
138 };
139 
zip_strerror(int errnum)140 const char *zip_strerror(int errnum) {
141   errnum = -errnum;
142   if (errnum <= 0 || errnum >= 30) {
143     return NULL;
144   }
145 
146   return zip_errlist[errnum];
147 }
148 
zip_basename(const char * name)149 static const char *zip_basename(const char *name) {
150   char const *p;
151   char const *base = name += FILESYSTEM_PREFIX_LEN(name);
152   int all_slashes = 1;
153 
154   for (p = name; *p; p++) {
155     if (ISSLASH(*p))
156       base = p + 1;
157     else
158       all_slashes = 0;
159   }
160 
161   /* If NAME is all slashes, arrange to return `/'. */
162   if (*base == '\0' && ISSLASH(*name) && all_slashes)
163     --base;
164 
165   return base;
166 }
167 
zip_mkpath(char * path)168 static int zip_mkpath(char *path) {
169   char *p;
170   char npath[MAX_PATH + 1];
171   int len = 0;
172   int has_device = HAS_DEVICE(path);
173 
174   memset(npath, 0, MAX_PATH + 1);
175   if (has_device) {
176     // only on windows
177     npath[0] = path[0];
178     npath[1] = path[1];
179     len = 2;
180   }
181   for (p = path + len; *p && len < MAX_PATH; p++) {
182     if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) {
183 #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) ||              \
184     defined(__MINGW32__)
185 #else
186       if ('\\' == *p) {
187         *p = '/';
188       }
189 #endif
190 
191       if (MKDIR(npath) == -1) {
192         if (errno != EEXIST) {
193           return ZIP_EMKDIR;
194         }
195       }
196     }
197     npath[len++] = *p;
198   }
199 
200   return 0;
201 }
202 
zip_strrpl(const char * str,size_t n,char oldchar,char newchar)203 static char *zip_strrpl(const char *str, size_t n, char oldchar, char newchar) {
204   char c;
205   size_t i;
206   char *rpl = (char *)calloc((1 + n), sizeof(char));
207   char *begin = rpl;
208   if (!rpl) {
209     return NULL;
210   }
211 
212   for (i = 0; (i < n) && (c = *str++); ++i) {
213     if (c == oldchar) {
214       c = newchar;
215     }
216     *rpl++ = c;
217   }
218 
219   return begin;
220 }
221 
zip_name_normalize(char * name,char * const nname,size_t len)222 static char *zip_name_normalize(char *name, char *const nname, size_t len) {
223   size_t offn = 0;
224   size_t offnn = 0, ncpy = 0;
225 
226   if (name == NULL || nname == NULL || len <= 0) {
227     return NULL;
228   }
229   // skip trailing '/'
230   while (ISSLASH(*name))
231     name++;
232 
233   for (; offn < len; offn++) {
234     if (ISSLASH(name[offn])) {
235       if (ncpy > 0 && strcmp(&nname[offnn], ".\0") &&
236           strcmp(&nname[offnn], "..\0")) {
237         offnn += ncpy;
238         nname[offnn++] = name[offn]; // append '/'
239       }
240       ncpy = 0;
241     } else {
242       nname[offnn + ncpy] = name[offn];
243       ncpy++;
244     }
245   }
246 
247   // at the end, extra check what we've already copied
248   if (ncpy == 0 || !strcmp(&nname[offnn], ".\0") ||
249       !strcmp(&nname[offnn], "..\0")) {
250     nname[offnn] = 0;
251   }
252   return nname;
253 }
254 
zip_name_match(const char * name1,const char * name2)255 static mz_bool zip_name_match(const char *name1, const char *name2) {
256   int len2 = strlen(name2);
257   char *nname2 = zip_strrpl(name2, len2, '\\', '/');
258   if (!nname2) {
259     return MZ_FALSE;
260   }
261 
262   mz_bool res = (strcmp(name1, nname2) == 0) ? MZ_TRUE : MZ_FALSE;
263   CLEANUP(nname2);
264   return res;
265 }
266 
zip_archive_truncate(mz_zip_archive * pzip)267 static int zip_archive_truncate(mz_zip_archive *pzip) {
268   mz_zip_internal_state *pState = pzip->m_pState;
269   mz_uint64 file_size = pzip->m_archive_size;
270   if ((pzip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) {
271     return 0;
272   }
273   if (pzip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) {
274     if (pState->m_pFile) {
275       int fd = fileno(pState->m_pFile);
276       return ftruncate(fd, file_size);
277     }
278   }
279   return 0;
280 }
281 
zip_archive_extract(mz_zip_archive * zip_archive,const char * dir,int (* on_extract)(const char * filename,void * arg),void * arg)282 static int zip_archive_extract(mz_zip_archive *zip_archive, const char *dir,
283                                int (*on_extract)(const char *filename,
284                                                  void *arg),
285                                void *arg) {
286   int err = 0;
287   mz_uint i, n;
288   char path[MAX_PATH + 1];
289   char symlink_to[MAX_PATH + 1];
290   mz_zip_archive_file_stat info;
291   size_t dirlen = 0;
292   mz_uint32 xattr = 0;
293 
294   memset(path, 0, sizeof(path));
295   memset(symlink_to, 0, sizeof(symlink_to));
296 
297   dirlen = strlen(dir);
298   if (dirlen + 1 > MAX_PATH) {
299     return ZIP_EINVENTNAME;
300   }
301 
302   memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat));
303 
304 #if defined(_MSC_VER)
305   strcpy_s(path, MAX_PATH, dir);
306 #else
307   strcpy(path, dir);
308 #endif
309 
310   if (!ISSLASH(path[dirlen - 1])) {
311 #if defined(_WIN32) || defined(__WIN32__)
312     path[dirlen] = '\\';
313 #else
314     path[dirlen] = '/';
315 #endif
316     ++dirlen;
317   }
318 
319   // Get and print information about each file in the archive.
320   n = mz_zip_reader_get_num_files(zip_archive);
321   for (i = 0; i < n; ++i) {
322     if (!mz_zip_reader_file_stat(zip_archive, i, &info)) {
323       // Cannot get information about zip archive;
324       err = ZIP_ENOENT;
325       goto out;
326     }
327 
328     if (!zip_name_normalize(info.m_filename, info.m_filename,
329                             strlen(info.m_filename))) {
330       // Cannot normalize file name;
331       err = ZIP_EINVENTNAME;
332       goto out;
333     }
334 #if defined(_MSC_VER)
335     strncpy_s(&path[dirlen], MAX_PATH - dirlen, info.m_filename,
336               MAX_PATH - dirlen);
337 #else
338     strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen);
339 #endif
340     err = zip_mkpath(path);
341     if (err < 0) {
342       // Cannot make a path
343       goto out;
344     }
345 
346     if ((((info.m_version_made_by >> 8) == 3) ||
347          ((info.m_version_made_by >> 8) ==
348           19)) // if zip is produced on Unix or macOS (3 and 19 from
349                // section 4.4.2.2 of zip standard)
350         && info.m_external_attr &
351                (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40
352                                // is directory)
353 #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) ||              \
354     defined(__MINGW32__)
355 #else
356       if (info.m_uncomp_size > MAX_PATH ||
357           !mz_zip_reader_extract_to_mem_no_alloc(zip_archive, i, symlink_to,
358                                                  MAX_PATH, 0, NULL, 0)) {
359         err = ZIP_EMEMNOALLOC;
360         goto out;
361       }
362       symlink_to[info.m_uncomp_size] = '\0';
363       if (symlink(symlink_to, path) != 0) {
364         err = ZIP_ESYMLINK;
365         goto out;
366       }
367 #endif
368     } else {
369       if (!mz_zip_reader_is_file_a_directory(zip_archive, i)) {
370         if (!mz_zip_reader_extract_to_file(zip_archive, i, path, 0)) {
371           // Cannot extract zip archive to file
372           err = ZIP_ENOFILE;
373           goto out;
374         }
375       }
376 
377 #if defined(_MSC_VER)
378       (void)xattr; // unused
379 #else
380       xattr = (info.m_external_attr >> 16) & 0xFFFF;
381       if (xattr > 0) {
382         if (chmod(path, (mode_t)xattr) < 0) {
383           err = ZIP_ENOPERM;
384           goto out;
385         }
386       }
387 #endif
388     }
389 
390     if (on_extract) {
391       if (on_extract(path, arg) < 0) {
392         goto out;
393       }
394     }
395   }
396 
397 out:
398   // Close the archive, freeing any resources it was using
399   if (!mz_zip_reader_end(zip_archive)) {
400     // Cannot end zip reader
401     err = ZIP_ECLSZIP;
402   }
403   return err;
404 }
405 
zip_archive_finalize(mz_zip_archive * pzip)406 static inline void zip_archive_finalize(mz_zip_archive *pzip) {
407   mz_zip_writer_finalize_archive(pzip);
408   zip_archive_truncate(pzip);
409 }
410 
zip_entry_mark(struct zip_t * zip,struct zip_entry_mark_t * entry_mark,int n,char * const entries[],const size_t len)411 static int zip_entry_mark(struct zip_t *zip,
412                           struct zip_entry_mark_t *entry_mark, int n,
413                           char *const entries[], const size_t len) {
414   int err = 0;
415   if (!zip || !entry_mark || !entries) {
416     return ZIP_ENOINIT;
417   }
418 
419   mz_zip_archive_file_stat file_stat;
420   mz_uint64 d_pos = ~0;
421   for (int i = 0; i < n; ++i) {
422 
423     if ((err = zip_entry_openbyindex(zip, i))) {
424       return err;
425     }
426 
427     mz_bool name_matches = MZ_FALSE;
428     for (int j = 0; j < (const int)len; ++j) {
429       if (zip_name_match(zip->entry.name, entries[j])) {
430         name_matches = MZ_TRUE;
431         break;
432       }
433     }
434     if (name_matches) {
435       entry_mark[i].type = MZ_DELETE;
436     } else {
437       entry_mark[i].type = MZ_KEEP;
438     }
439 
440     if (!mz_zip_reader_file_stat(&zip->archive, i, &file_stat)) {
441       return ZIP_ENOENT;
442     }
443 
444     zip_entry_close(zip);
445 
446     entry_mark[i].m_local_header_ofs = file_stat.m_local_header_ofs;
447     entry_mark[i].file_index = -1;
448     entry_mark[i].lf_length = 0;
449     if ((entry_mark[i].type) == MZ_DELETE &&
450         (d_pos > entry_mark[i].m_local_header_ofs)) {
451       d_pos = entry_mark[i].m_local_header_ofs;
452     }
453   }
454   for (int i = 0; i < n; ++i) {
455     if ((entry_mark[i].m_local_header_ofs > d_pos) &&
456         (entry_mark[i].type != MZ_DELETE)) {
457       entry_mark[i].type = MZ_MOVE;
458     }
459   }
460   return err;
461 }
462 
zip_index_next(mz_uint64 * local_header_ofs_array,int cur_index)463 static int zip_index_next(mz_uint64 *local_header_ofs_array, int cur_index) {
464   int new_index = 0;
465   for (int i = cur_index - 1; i >= 0; --i) {
466     if (local_header_ofs_array[cur_index] > local_header_ofs_array[i]) {
467       new_index = i + 1;
468       return new_index;
469     }
470   }
471   return new_index;
472 }
473 
zip_sort(mz_uint64 * local_header_ofs_array,int cur_index)474 static int zip_sort(mz_uint64 *local_header_ofs_array, int cur_index) {
475   int nxt_index = zip_index_next(local_header_ofs_array, cur_index);
476 
477   if (nxt_index != cur_index) {
478     mz_uint64 temp = local_header_ofs_array[cur_index];
479     for (int i = cur_index; i > nxt_index; i--) {
480       local_header_ofs_array[i] = local_header_ofs_array[i - 1];
481     }
482     local_header_ofs_array[nxt_index] = temp;
483   }
484   return nxt_index;
485 }
486 
zip_index_update(struct zip_entry_mark_t * entry_mark,int last_index,int nxt_index)487 static int zip_index_update(struct zip_entry_mark_t *entry_mark, int last_index,
488                             int nxt_index) {
489   for (int j = 0; j < last_index; j++) {
490     if (entry_mark[j].file_index >= nxt_index) {
491       entry_mark[j].file_index += 1;
492     }
493   }
494   entry_mark[nxt_index].file_index = last_index;
495   return 0;
496 }
497 
zip_entry_finalize(struct zip_t * zip,struct zip_entry_mark_t * entry_mark,const int n)498 static int zip_entry_finalize(struct zip_t *zip,
499                               struct zip_entry_mark_t *entry_mark,
500                               const int n) {
501 
502   mz_uint64 *local_header_ofs_array = (mz_uint64 *)calloc(n, sizeof(mz_uint64));
503   if (!local_header_ofs_array) {
504     return ZIP_EOOMEM;
505   }
506 
507   for (int i = 0; i < n; ++i) {
508     local_header_ofs_array[i] = entry_mark[i].m_local_header_ofs;
509     int index = zip_sort(local_header_ofs_array, i);
510 
511     if (index != i) {
512       zip_index_update(entry_mark, i, index);
513     }
514     entry_mark[i].file_index = index;
515   }
516 
517   mz_uint64 *length = (mz_uint64 *)calloc(n, sizeof(mz_uint64));
518   if (!length) {
519     CLEANUP(local_header_ofs_array);
520     return ZIP_EOOMEM;
521   }
522   for (int i = 0; i < n - 1; i++) {
523     length[i] = local_header_ofs_array[i + 1] - local_header_ofs_array[i];
524   }
525   length[n - 1] = zip->archive.m_archive_size - local_header_ofs_array[n - 1];
526 
527   for (int i = 0; i < n; i++) {
528     entry_mark[i].lf_length = length[entry_mark[i].file_index];
529   }
530 
531   CLEANUP(length);
532   CLEANUP(local_header_ofs_array);
533   return 0;
534 }
535 
zip_entry_set(struct zip_t * zip,struct zip_entry_mark_t * entry_mark,int n,char * const entries[],const size_t len)536 static int zip_entry_set(struct zip_t *zip, struct zip_entry_mark_t *entry_mark,
537                          int n, char *const entries[], const size_t len) {
538   int err = 0;
539 
540   if ((err = zip_entry_mark(zip, entry_mark, n, entries, len)) < 0) {
541     return err;
542   }
543   if ((err = zip_entry_finalize(zip, entry_mark, n)) < 0) {
544     return err;
545   }
546   return 0;
547 }
548 
zip_file_move(MZ_FILE * m_pFile,const mz_uint64 to,const mz_uint64 from,const mz_uint64 length,mz_uint8 * move_buf,const mz_int64 capacity_size)549 static mz_int64 zip_file_move(MZ_FILE *m_pFile, const mz_uint64 to,
550                               const mz_uint64 from, const mz_uint64 length,
551                               mz_uint8 *move_buf,
552                               const mz_int64 capacity_size) {
553   if ((mz_int64)length > capacity_size) {
554     return ZIP_ECAPSIZE;
555   }
556   if (MZ_FSEEK64(m_pFile, from, SEEK_SET)) {
557     MZ_FCLOSE(m_pFile);
558     return ZIP_EFSEEK;
559   }
560 
561   if (fread(move_buf, 1, length, m_pFile) != length) {
562     MZ_FCLOSE(m_pFile);
563     return ZIP_EFREAD;
564   }
565   if (MZ_FSEEK64(m_pFile, to, SEEK_SET)) {
566     MZ_FCLOSE(m_pFile);
567     return ZIP_EFSEEK;
568   }
569   if (fwrite(move_buf, 1, length, m_pFile) != length) {
570     MZ_FCLOSE(m_pFile);
571     return ZIP_EFWRITE;
572   }
573   return (mz_int64)length;
574 }
575 
zip_files_move(MZ_FILE * m_pFile,mz_uint64 writen_num,mz_uint64 read_num,mz_uint64 length)576 static mz_int64 zip_files_move(MZ_FILE *m_pFile, mz_uint64 writen_num,
577                                mz_uint64 read_num, mz_uint64 length) {
578   int n = 0;
579   const mz_int64 page_size = 1 << 12; // 4K
580   mz_uint8 *move_buf = (mz_uint8 *)calloc(1, page_size);
581   if (move_buf == NULL) {
582     return ZIP_EOOMEM;
583   }
584 
585   mz_int64 moved_length = 0;
586   mz_int64 move_count = 0;
587   while ((mz_int64)length > 0) {
588     move_count = ((mz_int64)length >= page_size) ? page_size : (mz_int64)length;
589     n = zip_file_move(m_pFile, writen_num, read_num, move_count, move_buf,
590                       page_size);
591     if (n < 0) {
592       moved_length = n;
593       goto cleanup;
594     }
595 
596     if (n != move_count) {
597       goto cleanup;
598     }
599 
600     writen_num += move_count;
601     read_num += move_count;
602     length -= move_count;
603     moved_length += move_count;
604   }
605 
606 cleanup:
607   CLEANUP(move_buf);
608   return moved_length;
609 }
610 
zip_central_dir_move(mz_zip_internal_state * pState,int begin,int end,int entry_num)611 static int zip_central_dir_move(mz_zip_internal_state *pState, int begin,
612                                 int end, int entry_num) {
613   if (begin == entry_num) {
614     return 0;
615   }
616 
617   mz_uint64 l_size = 0;
618   mz_uint64 r_size = 0;
619   mz_uint64 d_size = 0;
620   mz_uint8 *next = NULL;
621   mz_uint8 *deleted = &MZ_ZIP_ARRAY_ELEMENT(
622       &pState->m_central_dir, mz_uint8,
623       MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, begin));
624   l_size = (mz_uint32)(deleted - (mz_uint8 *)(pState->m_central_dir.m_p));
625   if (end == entry_num) {
626     r_size = 0;
627   } else {
628     next = &MZ_ZIP_ARRAY_ELEMENT(
629         &pState->m_central_dir, mz_uint8,
630         MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, end));
631     r_size = pState->m_central_dir.m_size -
632              (mz_uint32)(next - (mz_uint8 *)(pState->m_central_dir.m_p));
633     d_size = next - deleted;
634   }
635 
636   if (l_size == 0) {
637     memmove(pState->m_central_dir.m_p, next, r_size);
638     pState->m_central_dir.m_p = MZ_REALLOC(pState->m_central_dir.m_p, r_size);
639     for (int i = end; i < entry_num; i++) {
640       MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -=
641           d_size;
642     }
643   }
644 
645   if (l_size * r_size != 0) {
646     memmove(deleted, next, r_size);
647     for (int i = end; i < entry_num; i++) {
648       MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -=
649           d_size;
650     }
651   }
652 
653   pState->m_central_dir.m_size = l_size + r_size;
654   return 0;
655 }
656 
zip_central_dir_delete(mz_zip_internal_state * pState,int * deleted_entry_index_array,int entry_num)657 static int zip_central_dir_delete(mz_zip_internal_state *pState,
658                                   int *deleted_entry_index_array,
659                                   int entry_num) {
660   int i = 0;
661   int begin = 0;
662   int end = 0;
663   int d_num = 0;
664   while (i < entry_num) {
665     while ((!deleted_entry_index_array[i]) && (i < entry_num)) {
666       i++;
667     }
668     begin = i;
669 
670     while ((deleted_entry_index_array[i]) && (i < entry_num)) {
671       i++;
672     }
673     end = i;
674     zip_central_dir_move(pState, begin, end, entry_num);
675   }
676 
677   i = 0;
678   while (i < entry_num) {
679     while ((!deleted_entry_index_array[i]) && (i < entry_num)) {
680       i++;
681     }
682     begin = i;
683     if (begin == entry_num) {
684       break;
685     }
686     while ((deleted_entry_index_array[i]) && (i < entry_num)) {
687       i++;
688     }
689     end = i;
690     int k = 0;
691     for (int j = end; j < entry_num; j++) {
692       MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32,
693                            begin + k) =
694           (mz_uint32)MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets,
695                                           mz_uint32, j);
696       k++;
697     }
698     d_num += end - begin;
699   }
700 
701   pState->m_central_dir_offsets.m_size =
702       sizeof(mz_uint32) * (entry_num - d_num);
703   return 0;
704 }
705 
zip_entries_delete_mark(struct zip_t * zip,struct zip_entry_mark_t * entry_mark,int entry_num)706 static int zip_entries_delete_mark(struct zip_t *zip,
707                                    struct zip_entry_mark_t *entry_mark,
708                                    int entry_num) {
709   mz_uint64 writen_num = 0;
710   mz_uint64 read_num = 0;
711   mz_uint64 deleted_length = 0;
712   mz_uint64 move_length = 0;
713   int i = 0;
714   int deleted_entry_num = 0;
715   int n = 0;
716 
717   mz_bool *deleted_entry_flag_array =
718       (mz_bool *)calloc(entry_num, sizeof(mz_bool));
719   if (deleted_entry_flag_array == NULL) {
720     return ZIP_EOOMEM;
721   }
722 
723   mz_zip_internal_state *pState = zip->archive.m_pState;
724   zip->archive.m_zip_mode = MZ_ZIP_MODE_WRITING;
725 
726   if (MZ_FSEEK64(pState->m_pFile, 0, SEEK_SET)) {
727     CLEANUP(deleted_entry_flag_array);
728     return ZIP_ENOENT;
729   }
730 
731   while (i < entry_num) {
732     while ((entry_mark[i].type == MZ_KEEP) && (i < entry_num)) {
733       writen_num += entry_mark[i].lf_length;
734       read_num = writen_num;
735       i++;
736     }
737 
738     while ((entry_mark[i].type == MZ_DELETE) && (i < entry_num)) {
739       deleted_entry_flag_array[i] = MZ_TRUE;
740       read_num += entry_mark[i].lf_length;
741       deleted_length += entry_mark[i].lf_length;
742       i++;
743       deleted_entry_num++;
744     }
745 
746     while ((entry_mark[i].type == MZ_MOVE) && (i < entry_num)) {
747       move_length += entry_mark[i].lf_length;
748       mz_uint8 *p = &MZ_ZIP_ARRAY_ELEMENT(
749           &pState->m_central_dir, mz_uint8,
750           MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i));
751       if (!p) {
752         CLEANUP(deleted_entry_flag_array);
753         return ZIP_ENOENT;
754       }
755       mz_uint32 offset = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
756       offset -= (mz_uint32)deleted_length;
757       MZ_WRITE_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS, offset);
758       i++;
759     }
760 
761     n = zip_files_move(pState->m_pFile, writen_num, read_num, move_length);
762     if (n != (mz_int64)move_length) {
763       CLEANUP(deleted_entry_flag_array);
764       return n;
765     }
766     writen_num += move_length;
767     read_num += move_length;
768   }
769 
770   zip->archive.m_archive_size -= deleted_length;
771   zip->archive.m_total_files = entry_num - deleted_entry_num;
772 
773   zip_central_dir_delete(pState, deleted_entry_flag_array, entry_num);
774   CLEANUP(deleted_entry_flag_array);
775 
776   return deleted_entry_num;
777 }
778 
zip_open(const char * zipname,int level,char mode)779 struct zip_t *zip_open(const char *zipname, int level, char mode) {
780   struct zip_t *zip = NULL;
781 
782   if (!zipname || strlen(zipname) < 1) {
783     // zip_t archive name is empty or NULL
784     goto cleanup;
785   }
786 
787   if (level < 0)
788     level = MZ_DEFAULT_LEVEL;
789   if ((level & 0xF) > MZ_UBER_COMPRESSION) {
790     // Wrong compression level
791     goto cleanup;
792   }
793 
794   zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
795   if (!zip)
796     goto cleanup;
797 
798   zip->level = (mz_uint)level;
799   switch (mode) {
800   case 'w':
801     // Create a new archive.
802     if (!mz_zip_writer_init_file(&(zip->archive), zipname, 0)) {
803       // Cannot initialize zip_archive writer
804       goto cleanup;
805     }
806     break;
807 
808   case 'r':
809   case 'a':
810   case 'd':
811     if (!mz_zip_reader_init_file(
812             &(zip->archive), zipname,
813             zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) {
814       // An archive file does not exist or cannot initialize
815       // zip_archive reader
816       goto cleanup;
817     }
818     if ((mode == 'a' || mode == 'd') &&
819         !mz_zip_writer_init_from_reader(&(zip->archive), zipname)) {
820       mz_zip_reader_end(&(zip->archive));
821       goto cleanup;
822     }
823     break;
824 
825   default:
826     goto cleanup;
827   }
828 
829   return zip;
830 
831 cleanup:
832   CLEANUP(zip);
833   return NULL;
834 }
835 
zip_close(struct zip_t * zip)836 void zip_close(struct zip_t *zip) {
837   if (zip) {
838     // Always finalize, even if adding failed for some reason, so we have a
839     // valid central directory.
840     mz_zip_writer_finalize_archive(&(zip->archive));
841     zip_archive_truncate(&(zip->archive));
842     mz_zip_writer_end(&(zip->archive));
843     mz_zip_reader_end(&(zip->archive));
844 
845     CLEANUP(zip);
846   }
847 }
848 
zip_is64(struct zip_t * zip)849 int zip_is64(struct zip_t *zip) {
850   if (!zip || !zip->archive.m_pState) {
851     // zip_t handler or zip state is not initialized
852     return ZIP_ENOINIT;
853   }
854 
855   return (int)zip->archive.m_pState->m_zip64;
856 }
857 
zip_entry_open(struct zip_t * zip,const char * entryname)858 int zip_entry_open(struct zip_t *zip, const char *entryname) {
859   size_t entrylen = 0;
860   mz_zip_archive *pzip = NULL;
861   mz_uint num_alignment_padding_bytes, level;
862   mz_zip_archive_file_stat stats;
863   int err = 0;
864 
865   if (!zip) {
866     return ZIP_ENOINIT;
867   }
868 
869   if (!entryname) {
870     return ZIP_EINVENTNAME;
871   }
872 
873   entrylen = strlen(entryname);
874   if (entrylen == 0) {
875     return ZIP_EINVENTNAME;
876   }
877 
878   /*
879     .ZIP File Format Specification Version: 6.3.3
880 
881     4.4.17.1 The name of the file, with optional relative path.
882     The path stored MUST not contain a drive or
883     device letter, or a leading slash.  All slashes
884     MUST be forward slashes '/' as opposed to
885     backwards slashes '\' for compatibility with Amiga
886     and UNIX file systems etc.  If input came from standard
887     input, there is no file name field.
888   */
889   if (zip->entry.name) {
890     CLEANUP(zip->entry.name);
891   }
892   zip->entry.name = zip_strrpl(entryname, entrylen, '\\', '/');
893   if (!zip->entry.name) {
894     // Cannot parse zip entry name
895     return ZIP_EINVENTNAME;
896   }
897 
898   pzip = &(zip->archive);
899   if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) {
900     zip->entry.index =
901         mz_zip_reader_locate_file(pzip, zip->entry.name, NULL, 0);
902     if (zip->entry.index < 0) {
903       err = ZIP_ENOENT;
904       goto cleanup;
905     }
906 
907     if (!mz_zip_reader_file_stat(pzip, (mz_uint)zip->entry.index, &stats)) {
908       err = ZIP_ENOENT;
909       goto cleanup;
910     }
911 
912     zip->entry.comp_size = stats.m_comp_size;
913     zip->entry.uncomp_size = stats.m_uncomp_size;
914     zip->entry.uncomp_crc32 = stats.m_crc32;
915     zip->entry.offset = stats.m_central_dir_ofs;
916     zip->entry.header_offset = stats.m_local_header_ofs;
917     zip->entry.method = stats.m_method;
918     zip->entry.external_attr = stats.m_external_attr;
919 #ifndef MINIZ_NO_TIME
920     zip->entry.m_time = stats.m_time;
921 #endif
922 
923     return 0;
924   }
925 
926   zip->entry.index = (int)zip->archive.m_total_files;
927   zip->entry.comp_size = 0;
928   zip->entry.uncomp_size = 0;
929   zip->entry.uncomp_crc32 = MZ_CRC32_INIT;
930   zip->entry.offset = zip->archive.m_archive_size;
931   zip->entry.header_offset = zip->archive.m_archive_size;
932   memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8));
933   zip->entry.method = 0;
934 
935   // UNIX or APPLE
936 #if MZ_PLATFORM == 3 || MZ_PLATFORM == 19
937   // regular file with rw-r--r-- persmissions
938   zip->entry.external_attr = (mz_uint32)(0100644) << 16;
939 #else
940   zip->entry.external_attr = 0;
941 #endif
942 
943   num_alignment_padding_bytes =
944       mz_zip_writer_compute_padding_needed_for_file_alignment(pzip);
945 
946   if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) {
947     // Invalid zip mode
948     err = ZIP_EINVMODE;
949     goto cleanup;
950   }
951   if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) {
952     // Invalid zip compression level
953     err = ZIP_EINVLVL;
954     goto cleanup;
955   }
956   // no zip64 support yet
957   if ((pzip->m_total_files == 0xFFFF) ||
958       ((pzip->m_archive_size + num_alignment_padding_bytes +
959         MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
960         entrylen) > 0xFFFFFFFF)) {
961     // No zip64 support yet
962     err = ZIP_ENOSUP64;
963     goto cleanup;
964   }
965   if (!mz_zip_writer_write_zeros(pzip, zip->entry.offset,
966                                  num_alignment_padding_bytes +
967                                      sizeof(zip->entry.header))) {
968     // Cannot memset zip entry header
969     err = ZIP_EMEMSET;
970     goto cleanup;
971   }
972 
973   zip->entry.header_offset += num_alignment_padding_bytes;
974   if (pzip->m_file_offset_alignment) {
975     MZ_ASSERT(
976         (zip->entry.header_offset & (pzip->m_file_offset_alignment - 1)) == 0);
977   }
978   zip->entry.offset += num_alignment_padding_bytes + sizeof(zip->entry.header);
979 
980   if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name,
981                      entrylen) != entrylen) {
982     // Cannot write data to zip entry
983     err = ZIP_EWRTENT;
984     goto cleanup;
985   }
986 
987   zip->entry.offset += entrylen;
988   level = zip->level & 0xF;
989   if (level) {
990     zip->entry.state.m_pZip = pzip;
991     zip->entry.state.m_cur_archive_file_ofs = zip->entry.offset;
992     zip->entry.state.m_comp_size = 0;
993 
994     if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback,
995                    &(zip->entry.state),
996                    (int)tdefl_create_comp_flags_from_zip_params(
997                        (int)level, -15, MZ_DEFAULT_STRATEGY)) !=
998         TDEFL_STATUS_OKAY) {
999       // Cannot initialize the zip compressor
1000       err = ZIP_ETDEFLINIT;
1001       goto cleanup;
1002     }
1003   }
1004 
1005   zip->entry.m_time = time(NULL);
1006 
1007   return 0;
1008 
1009 cleanup:
1010   CLEANUP(zip->entry.name);
1011   return err;
1012 }
1013 
zip_entry_openbyindex(struct zip_t * zip,int index)1014 int zip_entry_openbyindex(struct zip_t *zip, int index) {
1015   mz_zip_archive *pZip = NULL;
1016   mz_zip_archive_file_stat stats;
1017   mz_uint namelen;
1018   const mz_uint8 *pHeader;
1019   const char *pFilename;
1020 
1021   if (!zip) {
1022     // zip_t handler is not initialized
1023     return ZIP_ENOINIT;
1024   }
1025 
1026   pZip = &(zip->archive);
1027   if (pZip->m_zip_mode != MZ_ZIP_MODE_READING) {
1028     // open by index requires readonly mode
1029     return ZIP_EINVMODE;
1030   }
1031 
1032   if (index < 0 || (mz_uint)index >= pZip->m_total_files) {
1033     // index out of range
1034     return ZIP_EINVIDX;
1035   }
1036 
1037   if (!(pHeader = &MZ_ZIP_ARRAY_ELEMENT(
1038             &pZip->m_pState->m_central_dir, mz_uint8,
1039             MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets,
1040                                  mz_uint32, index)))) {
1041     // cannot find header in central directory
1042     return ZIP_ENOHDR;
1043   }
1044 
1045   namelen = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
1046   pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
1047 
1048   /*
1049     .ZIP File Format Specification Version: 6.3.3
1050 
1051     4.4.17.1 The name of the file, with optional relative path.
1052     The path stored MUST not contain a drive or
1053     device letter, or a leading slash.  All slashes
1054     MUST be forward slashes '/' as opposed to
1055     backwards slashes '\' for compatibility with Amiga
1056     and UNIX file systems etc.  If input came from standard
1057     input, there is no file name field.
1058   */
1059   if (zip->entry.name) {
1060     CLEANUP(zip->entry.name);
1061   }
1062   zip->entry.name = zip_strrpl(pFilename, namelen, '\\', '/');
1063   if (!zip->entry.name) {
1064     // local entry name is NULL
1065     return ZIP_EINVENTNAME;
1066   }
1067 
1068   if (!mz_zip_reader_file_stat(pZip, (mz_uint)index, &stats)) {
1069     return ZIP_ENOENT;
1070   }
1071 
1072   zip->entry.index = index;
1073   zip->entry.comp_size = stats.m_comp_size;
1074   zip->entry.uncomp_size = stats.m_uncomp_size;
1075   zip->entry.uncomp_crc32 = stats.m_crc32;
1076   zip->entry.offset = stats.m_central_dir_ofs;
1077   zip->entry.header_offset = stats.m_local_header_ofs;
1078   zip->entry.method = stats.m_method;
1079   zip->entry.external_attr = stats.m_external_attr;
1080 #ifndef MINIZ_NO_TIME
1081   zip->entry.m_time = stats.m_time;
1082 #endif
1083 
1084   return 0;
1085 }
1086 
zip_entry_close(struct zip_t * zip)1087 int zip_entry_close(struct zip_t *zip) {
1088   mz_zip_archive *pzip = NULL;
1089   mz_uint level;
1090   tdefl_status done;
1091   mz_uint16 entrylen;
1092   mz_uint16 dos_time = 0, dos_date = 0;
1093   int err = 0;
1094 
1095   if (!zip) {
1096     // zip_t handler is not initialized
1097     err = ZIP_ENOINIT;
1098     goto cleanup;
1099   }
1100 
1101   pzip = &(zip->archive);
1102   if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) {
1103     goto cleanup;
1104   }
1105 
1106   level = zip->level & 0xF;
1107   if (level) {
1108     done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH);
1109     if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) {
1110       // Cannot flush compressed buffer
1111       err = ZIP_ETDEFLBUF;
1112       goto cleanup;
1113     }
1114     zip->entry.comp_size = zip->entry.state.m_comp_size;
1115     zip->entry.offset = zip->entry.state.m_cur_archive_file_ofs;
1116     zip->entry.method = MZ_DEFLATED;
1117   }
1118 
1119   entrylen = (mz_uint16)strlen(zip->entry.name);
1120   if ((zip->entry.comp_size > 0xFFFFFFFF) || (zip->entry.offset > 0xFFFFFFFF)) {
1121     // No zip64 support, yet
1122     err = ZIP_ENOSUP64;
1123     goto cleanup;
1124   }
1125 
1126 #ifndef MINIZ_NO_TIME
1127   mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date);
1128 #endif
1129 
1130   if (!mz_zip_writer_create_local_dir_header(
1131           pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size,
1132           zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0,
1133           dos_time, dos_date)) {
1134     // Cannot create zip entry header
1135     err = ZIP_ECRTHDR;
1136     goto cleanup;
1137   }
1138 
1139   if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset,
1140                      zip->entry.header,
1141                      sizeof(zip->entry.header)) != sizeof(zip->entry.header)) {
1142     // Cannot write zip entry header
1143     err = ZIP_EWRTHDR;
1144     goto cleanup;
1145   }
1146 
1147   if (!mz_zip_writer_add_to_central_dir(
1148           pzip, zip->entry.name, entrylen, NULL, 0, "", 0,
1149           zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32,
1150           zip->entry.method, 0, dos_time, dos_date, zip->entry.header_offset,
1151           zip->entry.external_attr)) {
1152     // Cannot write to zip central dir
1153     err = ZIP_EWRTDIR;
1154     goto cleanup;
1155   }
1156 
1157   pzip->m_total_files++;
1158   pzip->m_archive_size = zip->entry.offset;
1159 
1160 cleanup:
1161   if (zip) {
1162     zip->entry.m_time = 0;
1163     CLEANUP(zip->entry.name);
1164   }
1165   return err;
1166 }
1167 
zip_entry_name(struct zip_t * zip)1168 const char *zip_entry_name(struct zip_t *zip) {
1169   if (!zip) {
1170     // zip_t handler is not initialized
1171     return NULL;
1172   }
1173 
1174   return zip->entry.name;
1175 }
1176 
zip_entry_index(struct zip_t * zip)1177 int zip_entry_index(struct zip_t *zip) {
1178   if (!zip) {
1179     // zip_t handler is not initialized
1180     return ZIP_ENOINIT;
1181   }
1182 
1183   return zip->entry.index;
1184 }
1185 
zip_entry_isdir(struct zip_t * zip)1186 int zip_entry_isdir(struct zip_t *zip) {
1187   if (!zip) {
1188     // zip_t handler is not initialized
1189     return ZIP_ENOINIT;
1190   }
1191 
1192   if (zip->entry.index < 0) {
1193     // zip entry is not opened
1194     return ZIP_EINVIDX;
1195   }
1196 
1197   return (int)mz_zip_reader_is_file_a_directory(&zip->archive,
1198                                                 (mz_uint)zip->entry.index);
1199 }
1200 
zip_entry_size(struct zip_t * zip)1201 unsigned long long zip_entry_size(struct zip_t *zip) {
1202   return zip ? zip->entry.uncomp_size : 0;
1203 }
1204 
zip_entry_crc32(struct zip_t * zip)1205 unsigned int zip_entry_crc32(struct zip_t *zip) {
1206   return zip ? zip->entry.uncomp_crc32 : 0;
1207 }
1208 
zip_entry_write(struct zip_t * zip,const void * buf,size_t bufsize)1209 int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) {
1210   mz_uint level;
1211   mz_zip_archive *pzip = NULL;
1212   tdefl_status status;
1213 
1214   if (!zip) {
1215     // zip_t handler is not initialized
1216     return ZIP_ENOINIT;
1217   }
1218 
1219   pzip = &(zip->archive);
1220   if (buf && bufsize > 0) {
1221     zip->entry.uncomp_size += bufsize;
1222     zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32(
1223         zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize);
1224 
1225     level = zip->level & 0xF;
1226     if (!level) {
1227       if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, buf,
1228                           bufsize) != bufsize)) {
1229         // Cannot write buffer
1230         return ZIP_EWRTENT;
1231       }
1232       zip->entry.offset += bufsize;
1233       zip->entry.comp_size += bufsize;
1234     } else {
1235       status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize,
1236                                      TDEFL_NO_FLUSH);
1237       if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) {
1238         // Cannot compress buffer
1239         return ZIP_ETDEFLBUF;
1240       }
1241     }
1242   }
1243 
1244   return 0;
1245 }
1246 
zip_entry_fwrite(struct zip_t * zip,const char * filename)1247 int zip_entry_fwrite(struct zip_t *zip, const char *filename) {
1248   int err = 0;
1249   size_t n = 0;
1250   FILE *stream = NULL;
1251   mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE];
1252   struct MZ_FILE_STAT_STRUCT file_stat;
1253 
1254   if (!zip) {
1255     // zip_t handler is not initialized
1256     return ZIP_ENOINIT;
1257   }
1258 
1259   memset(buf, 0, MZ_ZIP_MAX_IO_BUF_SIZE);
1260   memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT));
1261   if (MZ_FILE_STAT(filename, &file_stat) != 0) {
1262     // problem getting information - check errno
1263     return ZIP_ENOENT;
1264   }
1265 
1266   if ((file_stat.st_mode & 0200) == 0) {
1267     // MS-DOS read-only attribute
1268     zip->entry.external_attr |= 0x01;
1269   }
1270   zip->entry.external_attr |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16);
1271   zip->entry.m_time = file_stat.st_mtime;
1272 
1273 #if defined(_MSC_VER)
1274   if (fopen_s(&stream, filename, "rb"))
1275 #else
1276   if (!(stream = fopen(filename, "rb")))
1277 #endif
1278   {
1279     // Cannot open filename
1280     return ZIP_EOPNFILE;
1281   }
1282 
1283   while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) >
1284          0) {
1285     if (zip_entry_write(zip, buf, n) < 0) {
1286       err = ZIP_EWRTENT;
1287       break;
1288     }
1289   }
1290   fclose(stream);
1291 
1292   return err;
1293 }
1294 
zip_entry_read(struct zip_t * zip,void ** buf,size_t * bufsize)1295 ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) {
1296   mz_zip_archive *pzip = NULL;
1297   mz_uint idx;
1298   size_t size = 0;
1299 
1300   if (!zip) {
1301     // zip_t handler is not initialized
1302     return (ssize_t)ZIP_ENOINIT;
1303   }
1304 
1305   pzip = &(zip->archive);
1306   if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) {
1307     // the entry is not found or we do not have read access
1308     return (ssize_t)ZIP_ENOENT;
1309   }
1310 
1311   idx = (mz_uint)zip->entry.index;
1312   if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
1313     // the entry is a directory
1314     return (ssize_t)ZIP_EINVENTTYPE;
1315   }
1316 
1317   *buf = mz_zip_reader_extract_to_heap(pzip, idx, &size, 0);
1318   if (*buf && bufsize) {
1319     *bufsize = size;
1320   }
1321   return (ssize_t)size;
1322 }
1323 
zip_entry_noallocread(struct zip_t * zip,void * buf,size_t bufsize)1324 ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) {
1325   mz_zip_archive *pzip = NULL;
1326 
1327   if (!zip) {
1328     // zip_t handler is not initialized
1329     return (ssize_t)ZIP_ENOINIT;
1330   }
1331 
1332   pzip = &(zip->archive);
1333   if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) {
1334     // the entry is not found or we do not have read access
1335     return (ssize_t)ZIP_ENOENT;
1336   }
1337 
1338   if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index,
1339                                              buf, bufsize, 0, NULL, 0)) {
1340     return (ssize_t)ZIP_EMEMNOALLOC;
1341   }
1342 
1343   return (ssize_t)zip->entry.uncomp_size;
1344 }
1345 
zip_entry_fread(struct zip_t * zip,const char * filename)1346 int zip_entry_fread(struct zip_t *zip, const char *filename) {
1347   mz_zip_archive *pzip = NULL;
1348   mz_uint idx;
1349   mz_uint32 xattr = 0;
1350   mz_zip_archive_file_stat info;
1351 
1352   if (!zip) {
1353     // zip_t handler is not initialized
1354     return ZIP_ENOINIT;
1355   }
1356 
1357   memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat));
1358   pzip = &(zip->archive);
1359   if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) {
1360     // the entry is not found or we do not have read access
1361     return ZIP_ENOENT;
1362   }
1363 
1364   idx = (mz_uint)zip->entry.index;
1365   if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
1366     // the entry is a directory
1367     return ZIP_EINVENTTYPE;
1368   }
1369 
1370   if (!mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) {
1371     return ZIP_ENOFILE;
1372   }
1373 
1374 #if defined(_MSC_VER)
1375   (void)xattr; // unused
1376 #else
1377   if (!mz_zip_reader_file_stat(pzip, idx, &info)) {
1378     // Cannot get information about zip archive;
1379     return ZIP_ENOFILE;
1380   }
1381 
1382   xattr = (info.m_external_attr >> 16) & 0xFFFF;
1383   if (xattr > 0) {
1384     if (chmod(filename, (mode_t)xattr) < 0) {
1385       return ZIP_ENOPERM;
1386     }
1387   }
1388 #endif
1389 
1390   return 0;
1391 }
1392 
zip_entry_extract(struct zip_t * zip,size_t (* on_extract)(void * arg,unsigned long long offset,const void * buf,size_t bufsize),void * arg)1393 int zip_entry_extract(struct zip_t *zip,
1394                       size_t (*on_extract)(void *arg, unsigned long long offset,
1395                                            const void *buf, size_t bufsize),
1396                       void *arg) {
1397   mz_zip_archive *pzip = NULL;
1398   mz_uint idx;
1399 
1400   if (!zip) {
1401     // zip_t handler is not initialized
1402     return ZIP_ENOINIT;
1403   }
1404 
1405   pzip = &(zip->archive);
1406   if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) {
1407     // the entry is not found or we do not have read access
1408     return ZIP_ENOENT;
1409   }
1410 
1411   idx = (mz_uint)zip->entry.index;
1412   return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0))
1413              ? 0
1414              : ZIP_EINVIDX;
1415 }
1416 
zip_entries_total(struct zip_t * zip)1417 int zip_entries_total(struct zip_t *zip) {
1418   if (!zip) {
1419     // zip_t handler is not initialized
1420     return ZIP_ENOINIT;
1421   }
1422 
1423   return (int)zip->archive.m_total_files;
1424 }
1425 
zip_entries_delete(struct zip_t * zip,char * const entries[],const size_t len)1426 int zip_entries_delete(struct zip_t *zip, char *const entries[],
1427                        const size_t len) {
1428   int n = 0;
1429   int err = 0;
1430   struct zip_entry_mark_t *entry_mark = NULL;
1431 
1432   if (zip == NULL || (entries == NULL && len != 0)) {
1433     return ZIP_ENOINIT;
1434   }
1435 
1436   if (entries == NULL && len == 0) {
1437     return 0;
1438   }
1439 
1440   n = zip_entries_total(zip);
1441 
1442   entry_mark =
1443       (struct zip_entry_mark_t *)calloc(n, sizeof(struct zip_entry_mark_t));
1444   if (!entry_mark) {
1445     return ZIP_EOOMEM;
1446   }
1447 
1448   zip->archive.m_zip_mode = MZ_ZIP_MODE_READING;
1449 
1450   err = zip_entry_set(zip, entry_mark, n, entries, len);
1451   if (err < 0) {
1452     CLEANUP(entry_mark);
1453     return err;
1454   }
1455 
1456   err = zip_entries_delete_mark(zip, entry_mark, n);
1457   CLEANUP(entry_mark);
1458   return err;
1459 }
1460 
zip_stream_extract(const char * stream,size_t size,const char * dir,int (* on_extract)(const char * filename,void * arg),void * arg)1461 int zip_stream_extract(const char *stream, size_t size, const char *dir,
1462                        int (*on_extract)(const char *filename, void *arg),
1463                        void *arg) {
1464   mz_zip_archive zip_archive;
1465   if (!stream || !dir) {
1466     // Cannot parse zip archive stream
1467     return ZIP_ENOINIT;
1468   }
1469   if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) {
1470     // Cannot memset zip archive
1471     return ZIP_EMEMSET;
1472   }
1473   if (!mz_zip_reader_init_mem(&zip_archive, stream, size, 0)) {
1474     // Cannot initialize zip_archive reader
1475     return ZIP_ENOINIT;
1476   }
1477 
1478   return zip_archive_extract(&zip_archive, dir, on_extract, arg);
1479 }
1480 
zip_stream_open(const char * stream,size_t size,int level,char mode)1481 struct zip_t *zip_stream_open(const char *stream, size_t size, int level,
1482                               char mode) {
1483   struct zip_t *zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
1484   if (!zip) {
1485     return NULL;
1486   }
1487 
1488   if (level < 0) {
1489     level = MZ_DEFAULT_LEVEL;
1490   }
1491   if ((level & 0xF) > MZ_UBER_COMPRESSION) {
1492     // Wrong compression level
1493     goto cleanup;
1494   }
1495   zip->level = (mz_uint)level;
1496 
1497   if ((stream != NULL) && (size > 0) && (mode == 'r')) {
1498     if (!mz_zip_reader_init_mem(&(zip->archive), stream, size, 0)) {
1499       goto cleanup;
1500     }
1501   } else if ((stream == NULL) && (size == 0) && (mode == 'w')) {
1502     // Create a new archive.
1503     if (!mz_zip_writer_init_heap(&(zip->archive), 0, 1024)) {
1504       // Cannot initialize zip_archive writer
1505       goto cleanup;
1506     }
1507   } else {
1508     goto cleanup;
1509   }
1510   return zip;
1511 
1512 cleanup:
1513   CLEANUP(zip);
1514   return NULL;
1515 }
1516 
zip_stream_copy(struct zip_t * zip,void ** buf,size_t * bufsize)1517 ssize_t zip_stream_copy(struct zip_t *zip, void **buf, size_t *bufsize) {
1518   if (!zip) {
1519     return (ssize_t)ZIP_ENOINIT;
1520   }
1521 
1522   zip_archive_finalize(&(zip->archive));
1523 
1524   if (bufsize != NULL) {
1525     *bufsize = (size_t)zip->archive.m_archive_size;
1526   }
1527   *buf = calloc(sizeof(unsigned char), zip->archive.m_archive_size);
1528   memcpy(*buf, zip->archive.m_pState->m_pMem, zip->archive.m_archive_size);
1529 
1530   return (ssize_t)zip->archive.m_archive_size;
1531 }
1532 
zip_stream_close(struct zip_t * zip)1533 void zip_stream_close(struct zip_t *zip) {
1534   if (zip) {
1535     mz_zip_writer_end(&(zip->archive));
1536     mz_zip_reader_end(&(zip->archive));
1537     CLEANUP(zip);
1538   }
1539 }
1540 
zip_create(const char * zipname,const char * filenames[],size_t len)1541 int zip_create(const char *zipname, const char *filenames[], size_t len) {
1542   int err = 0;
1543   size_t i;
1544   mz_zip_archive zip_archive;
1545   struct MZ_FILE_STAT_STRUCT file_stat;
1546   mz_uint32 ext_attributes = 0;
1547 
1548   if (!zipname || strlen(zipname) < 1) {
1549     // zip_t archive name is empty or NULL
1550     return ZIP_EINVZIPNAME;
1551   }
1552 
1553   // Create a new archive.
1554   if (!memset(&(zip_archive), 0, sizeof(zip_archive))) {
1555     // Cannot memset zip archive
1556     return ZIP_EMEMSET;
1557   }
1558 
1559   if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) {
1560     // Cannot initialize zip_archive writer
1561     return ZIP_ENOINIT;
1562   }
1563 
1564   if (!memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT))) {
1565     return ZIP_EMEMSET;
1566   }
1567 
1568   for (i = 0; i < len; ++i) {
1569     const char *name = filenames[i];
1570     if (!name) {
1571       err = ZIP_EINVENTNAME;
1572       break;
1573     }
1574 
1575     if (MZ_FILE_STAT(name, &file_stat) != 0) {
1576       // problem getting information - check errno
1577       err = ZIP_ENOFILE;
1578       break;
1579     }
1580 
1581     if ((file_stat.st_mode & 0200) == 0) {
1582       // MS-DOS read-only attribute
1583       ext_attributes |= 0x01;
1584     }
1585     ext_attributes |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16);
1586 
1587     if (!mz_zip_writer_add_file(&zip_archive, zip_basename(name), name, "", 0,
1588                                 ZIP_DEFAULT_COMPRESSION_LEVEL,
1589                                 ext_attributes)) {
1590       // Cannot add file to zip_archive
1591       err = ZIP_ENOFILE;
1592       break;
1593     }
1594   }
1595 
1596   mz_zip_writer_finalize_archive(&zip_archive);
1597   mz_zip_writer_end(&zip_archive);
1598   return err;
1599 }
1600 
zip_extract(const char * zipname,const char * dir,int (* on_extract)(const char * filename,void * arg),void * arg)1601 int zip_extract(const char *zipname, const char *dir,
1602                 int (*on_extract)(const char *filename, void *arg), void *arg) {
1603   mz_zip_archive zip_archive;
1604 
1605   if (!zipname || !dir) {
1606     // Cannot parse zip archive name
1607     return ZIP_EINVZIPNAME;
1608   }
1609 
1610   if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) {
1611     // Cannot memset zip archive
1612     return ZIP_EMEMSET;
1613   }
1614 
1615   // Now try to open the archive.
1616   if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) {
1617     // Cannot initialize zip_archive reader
1618     return ZIP_ENOINIT;
1619   }
1620 
1621   return zip_archive_extract(&zip_archive, dir, on_extract, arg);
1622 }
1623