Coverage Report

Created: 2026-04-07 06:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/zip/zip.c
Line
Count
Source
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 HAS_DEVICE(P)                                                          \
22
  ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) &&   \
23
   (P)[1] == ':')
24
#define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0)
25
26
#else
27
28
#include <unistd.h> // needed for symlink()
29
30
#endif
31
32
#ifdef __MINGW32__
33
#include <sys/types.h>
34
#include <unistd.h>
35
#endif
36
37
#include "miniz.h"
38
#include "zip.h"
39
40
#ifdef _MSC_VER
41
#include <io.h>
42
43
#define ftruncate(fd, sz) (-(_chsize_s((fd), (sz)) != 0))
44
#define fileno _fileno
45
#endif
46
47
#if defined(__TINYC__) && (defined(_WIN32) || defined(_WIN64))
48
#include <io.h>
49
50
#define ftruncate(fd, sz) (-(_chsize_s((fd), (sz)) != 0))
51
#define fileno _fileno
52
#endif
53
54
#ifndef HAS_DEVICE
55
#define HAS_DEVICE(P) 0
56
#endif
57
58
#ifndef FILESYSTEM_PREFIX_LEN
59
0
#define FILESYSTEM_PREFIX_LEN(P) 0
60
#endif
61
62
#ifndef ISSLASH
63
0
#define ISSLASH(C) ((C) == '/' || (C) == '\\')
64
#endif
65
66
#define CLEANUP(ptr)                                                           \
67
0
  do {                                                                         \
68
0
    if (ptr) {                                                                 \
69
0
      free((void *)ptr);                                                       \
70
0
      ptr = NULL;                                                              \
71
0
    }                                                                          \
72
0
  } while (0)
73
74
0
#define UNX_IFDIR 0040000  /* Unix directory */
75
0
#define UNX_IFREG 0100000  /* Unix regular file */
76
0
#define UNX_IFSOCK 0140000 /* Unix socket (BSD, not SysV or Amiga) */
77
0
#define UNX_IFLNK 0120000  /* Unix symbolic link (not SysV, Amiga) */
78
0
#define UNX_IFBLK 0060000  /* Unix block special       (not Amiga) */
79
0
#define UNX_IFCHR 0020000  /* Unix character special   (not Amiga) */
80
0
#define UNX_IFIFO 0010000  /* Unix fifo    (BCC, not MSC or Amiga) */
81
82
/*
83
 * Write function for in-memory delete mode. Behaves identically to
84
 * mz_zip_heap_write_func but has a distinct address so
85
 * mz_zip_writer_end_internal won't free m_pMem (it only frees when
86
 * m_pWrite == mz_zip_heap_write_func). This lets the caller retain
87
 * ownership of the buffer passed to zip_stream_open.
88
 */
89
static size_t zip_stream_delete_write_func(void *pOpaque, mz_uint64 file_ofs,
90
0
                                           const void *pBuf, size_t n) {
91
0
  mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
92
0
  mz_zip_internal_state *pState = pZip->m_pState;
93
0
  mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size);
94
95
0
  if (!n)
96
0
    return 0;
97
98
0
  if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF)) {
99
0
    mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE);
100
0
    return 0;
101
0
  }
102
103
0
  if (new_size > pState->m_mem_capacity) {
104
0
    void *pNew_block;
105
0
    size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity);
106
0
    while (new_capacity < new_size)
107
0
      new_capacity *= 2;
108
0
    if (NULL == (pNew_block = pZip->m_pRealloc(
109
0
                     pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) {
110
0
      mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
111
0
      return 0;
112
0
    }
113
0
    pState->m_pMem = pNew_block;
114
0
    pState->m_mem_capacity = new_capacity;
115
0
  }
116
0
  memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n);
117
0
  pState->m_mem_size = (size_t)new_size;
118
0
  return n;
119
0
}
120
121
struct zip_entry_t {
122
  ssize_t index;
123
  char *name;
124
  mz_uint64 uncomp_size;
125
  mz_uint64 comp_size;
126
  mz_uint32 uncomp_crc32;
127
  mz_uint64 dir_offset;
128
  mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
129
  mz_uint64 header_offset;
130
  mz_uint16 method;
131
  mz_zip_writer_add_state state;
132
  tdefl_compressor comp;
133
  mz_uint32 external_attr;
134
  time_t m_time;
135
};
136
137
struct zip_t {
138
  mz_zip_archive archive;
139
  mz_uint level;
140
  struct zip_entry_t entry;
141
};
142
143
enum zip_modify_t {
144
  MZ_KEEP = 0,
145
  MZ_DELETE = 1,
146
  MZ_MOVE = 2,
147
};
148
149
struct zip_entry_mark_t {
150
  ssize_t file_index;
151
  enum zip_modify_t type;
152
  mz_uint64 m_local_header_ofs;
153
  size_t lf_length;
154
};
155
156
static const char *const zip_errlist[ZIP_NERRORS] = {
157
    NULL,
158
    "not initialized",
159
    "invalid entry name",
160
    "entry not found",
161
    "invalid zip mode",
162
    "invalid compression level",
163
    "no zip 64 support",
164
    "memset error",
165
    "cannot write data to entry",
166
    "cannot initialize tdefl compressor",
167
    "invalid index",
168
    "header not found",
169
    "cannot flush tdefl buffer",
170
    "cannot create entry header",
171
    "cannot write entry header",
172
    "cannot write to central dir",
173
    "cannot open file",
174
    "invalid entry type",
175
    "extracting data using no memory allocation",
176
    "file not found",
177
    "no permission",
178
    "out of memory",
179
    "invalid zip archive name",
180
    "make dir error",
181
    "symlink error",
182
    "close archive error",
183
    "capacity size too small",
184
    "fseek error",
185
    "fread error",
186
    "fwrite error",
187
    "cannot initialize reader",
188
    "cannot initialize writer",
189
    "cannot initialize writer from reader",
190
    "invalid argument",
191
    "cannot initialize reader iterator",
192
    "check dir error: path exists but is not directory",
193
};
194
195
0
const char *zip_strerror(int errnum) {
196
0
  errnum = -errnum;
197
0
  if (errnum <= 0 || errnum >= ZIP_NERRORS) {
198
0
    return NULL;
199
0
  }
200
201
0
  return zip_errlist[errnum];
202
0
}
203
204
0
static const char *zip_basename(const char *name) {
205
0
  char const *p;
206
0
  char const *base = name += FILESYSTEM_PREFIX_LEN(name);
207
0
  int all_slashes = 1;
208
209
0
  for (p = name; *p; p++) {
210
0
    if (ISSLASH(*p))
211
0
      base = p + 1;
212
0
    else
213
0
      all_slashes = 0;
214
0
  }
215
216
  /* If NAME is all slashes, arrange to return `/'. */
217
0
  if (*base == '\0' && ISSLASH(*name) && all_slashes)
218
0
    --base;
219
220
0
  return base;
221
0
}
222
223
0
static int zip_mkpath(char *path, size_t pos) {
224
0
  char *p;
225
0
  char npath[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE + 1];
226
0
  size_t len = pos;
227
0
  struct MZ_FILE_STAT_STRUCT st;
228
229
0
  memset(npath, 0, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE + 1);
230
0
  strncpy(npath, path, len);
231
232
0
  if (MZ_FILE_STAT(npath, &st) < 0) {
233
0
    return ZIP_ENOFILE;
234
0
  }
235
236
0
  for (p = path + len; *p && len < MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE; p++) {
237
0
    if (ISSLASH(*p)) {
238
#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) ||              \
239
    defined(__MINGW32__)
240
      if (MZ_MKDIR(npath) == -1) {
241
        if (errno != EEXIST) {
242
          return ZIP_EMKDIR;
243
        }
244
      }
245
#else
246
0
      if ('\\' == *p) {
247
0
        *p = '/';
248
0
      }
249
250
0
      if (lstat(npath, &st) < 0) {
251
0
        if (MZ_MKDIR(npath) == -1) {
252
0
          if (errno != EEXIST) {
253
0
            return ZIP_EMKDIR;
254
0
          }
255
0
        }
256
0
      } else {
257
0
        if (!S_ISDIR(st.st_mode)) {
258
0
          return ZIP_ECHKDIR;
259
0
        } else {
260
          // OK - DIR EXISTS
261
0
        }
262
0
      }
263
0
#endif
264
0
    }
265
0
    npath[len++] = *p;
266
0
  }
267
0
  return 0;
268
0
}
269
270
0
static char *zip_strclone(const char *str, size_t n) {
271
0
  char c;
272
0
  size_t i;
273
0
  char *rpl = (char *)calloc((1 + n), sizeof(char));
274
0
  char *begin = rpl;
275
0
  if (!rpl) {
276
0
    return NULL;
277
0
  }
278
279
0
  for (i = 0; (i < n) && (c = *str++); ++i) {
280
0
    *rpl++ = c;
281
0
  }
282
283
0
  return begin;
284
0
}
285
286
0
static char *zip_strrpl(const char *str, size_t n, char oldchar, char newchar) {
287
0
  char c;
288
0
  size_t i;
289
0
  char *rpl = (char *)calloc((1 + n), sizeof(char));
290
0
  char *begin = rpl;
291
0
  if (!rpl) {
292
0
    return NULL;
293
0
  }
294
295
0
  for (i = 0; (i < n) && (c = *str++); ++i) {
296
0
    if (c == oldchar) {
297
0
      c = newchar;
298
0
    }
299
0
    *rpl++ = c;
300
0
  }
301
302
0
  return begin;
303
0
}
304
305
0
static inline int zip_strchr_match(const char *const str, size_t len, char c) {
306
0
  size_t i;
307
0
  for (i = 0; i < len; ++i) {
308
0
    if (str[i] != c) {
309
0
      return 0;
310
0
    }
311
0
  }
312
313
0
  return 1;
314
0
}
315
316
0
static char *zip_name_normalize(char *name, char *const nname, size_t len) {
317
0
  size_t offn = 0, ncpy = 0;
318
0
  char c;
319
320
0
  if (name == NULL || nname == NULL || len == 0) {
321
0
    return NULL;
322
0
  }
323
  // skip trailing '/'
324
0
  while (ISSLASH(*name)) {
325
0
    name++;
326
0
  }
327
328
0
  while ((c = *name++)) {
329
0
    if (ISSLASH(c)) {
330
0
      if (ncpy > 0 && !zip_strchr_match(&nname[offn], ncpy, '.')) {
331
0
        offn += ncpy;
332
0
        nname[offn++] = c; // append '/'
333
0
      }
334
0
      ncpy = 0;
335
0
    } else {
336
0
      nname[offn + ncpy] = c;
337
0
      if (c) {
338
0
        ncpy++;
339
0
      }
340
0
    }
341
0
  }
342
343
0
  if (!zip_strchr_match(&nname[offn], ncpy, '.')) {
344
0
    nname[offn + ncpy] = '\0';
345
0
  } else {
346
0
    nname[offn] = '\0';
347
0
  }
348
349
0
  return nname;
350
0
}
351
352
0
static int zip_archive_truncate(mz_zip_archive *pzip) {
353
0
  mz_zip_internal_state *pState = pzip->m_pState;
354
0
  mz_uint64 file_size = pzip->m_archive_size;
355
0
  if ((pzip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) {
356
0
    return 0;
357
0
  }
358
0
  if (pzip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) {
359
0
    if (pState->m_pFile) {
360
0
      int fd = fileno(pState->m_pFile);
361
0
      return ftruncate(fd, pState->m_file_archive_start_ofs + file_size);
362
0
    }
363
0
  }
364
0
  return 0;
365
0
}
366
367
static int zip_archive_extract(mz_zip_archive *zip_archive, const char *dir,
368
                               int (*on_extract)(const char *filename,
369
                                                 void *arg),
370
0
                               void *arg) {
371
0
  int err = 0;
372
0
  mz_uint i, n;
373
0
  char path[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE + 1];
374
0
  char symlink_to[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE + 1];
375
0
  mz_zip_archive_file_stat info;
376
0
  size_t dirlen = 0, filename_size = MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE;
377
0
  mz_uint32 xattr = 0;
378
379
0
  memset(path, 0, sizeof(path));
380
0
  memset(symlink_to, 0, sizeof(symlink_to));
381
382
0
  dirlen = strlen(dir);
383
0
  if (dirlen == 0 || dirlen + 1 > MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE) {
384
0
    return ZIP_EINVENTNAME;
385
0
  }
386
387
0
  memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat));
388
389
#if defined(_MSC_VER)
390
  strcpy_s(path, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE, dir);
391
#else
392
0
  strncpy(path, dir, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE);
393
0
#endif
394
395
0
  if (!ISSLASH(path[dirlen - 1])) {
396
#if defined(_WIN32) || defined(__WIN32__)
397
    path[dirlen] = '\\';
398
#else
399
0
    path[dirlen] = '/';
400
0
#endif
401
0
    ++dirlen;
402
0
  }
403
404
0
  if (filename_size > MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - dirlen) {
405
0
    filename_size = MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - dirlen;
406
0
  }
407
  // Get and print information about each file in the archive.
408
0
  n = mz_zip_reader_get_num_files(zip_archive);
409
0
  for (i = 0; i < n; ++i) {
410
0
    if (!mz_zip_reader_file_stat(zip_archive, i, &info)) {
411
      // Cannot get information about zip archive;
412
0
      err = ZIP_ENOENT;
413
0
      goto out;
414
0
    }
415
416
0
    if (!zip_name_normalize(info.m_filename, info.m_filename,
417
0
                            strlen(info.m_filename))) {
418
      // Cannot normalize file name;
419
0
      err = ZIP_EINVENTNAME;
420
0
      goto out;
421
0
    }
422
423
#if defined(_MSC_VER)
424
    strncpy_s(&path[dirlen], filename_size, info.m_filename, filename_size);
425
#else
426
0
    strncpy(&path[dirlen], info.m_filename, filename_size);
427
0
#endif
428
429
0
    err = zip_mkpath(path, dirlen);
430
0
    if (err < 0) {
431
      // Cannot make a path
432
0
      goto out;
433
0
    }
434
435
0
    if ((((info.m_version_made_by >> 8) == 3) ||
436
0
         ((info.m_version_made_by >> 8) ==
437
0
          19)) // if zip is produced on Unix or macOS (3 and 19 from
438
               // section 4.4.2.2 of zip standard)
439
0
        && info.m_external_attr &
440
0
               (0x20 << 24)) { // and has sym link attribute (0x80 is file,
441
                               // 0x40 is directory)
442
#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) ||              \
443
    defined(__MINGW32__)
444
#else
445
0
      if (info.m_uncomp_size > MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE ||
446
0
          !mz_zip_reader_extract_to_mem_no_alloc(
447
0
              zip_archive, i, symlink_to, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE, 0,
448
0
              NULL, 0)) {
449
0
        err = ZIP_EMEMNOALLOC;
450
0
        goto out;
451
0
      }
452
0
      symlink_to[info.m_uncomp_size] = '\0';
453
0
      if (symlink(symlink_to, path) != 0) {
454
0
        err = ZIP_ESYMLINK;
455
0
        goto out;
456
0
      }
457
0
#endif
458
0
    } else {
459
0
      if (!mz_zip_reader_is_file_a_directory(zip_archive, i)) {
460
0
        if (!mz_zip_reader_extract_to_file(zip_archive, i, path, 0)) {
461
          // Cannot extract zip archive to file
462
0
          err = ZIP_ENOFILE;
463
0
          goto out;
464
0
        }
465
0
      }
466
467
#if defined(_MSC_VER) || defined(PS4)
468
      (void)xattr; // unused
469
#else
470
0
      xattr = (info.m_external_attr >> 16) & 0xFFFF;
471
0
      if (xattr > 0 && xattr <= MZ_UINT16_MAX) {
472
0
        if (CHMOD(path, (mode_t)xattr) < 0) {
473
0
          err = ZIP_ENOPERM;
474
0
          goto out;
475
0
        }
476
0
      }
477
0
#endif
478
0
    }
479
480
0
    if (on_extract) {
481
0
      if (on_extract(path, arg) < 0) {
482
0
        goto out;
483
0
      }
484
0
    }
485
0
  }
486
487
0
out:
488
  // Close the archive, freeing any resources it was using
489
0
  if (!mz_zip_reader_end(zip_archive)) {
490
    // Cannot end zip reader
491
0
    err = ZIP_ECLSZIP;
492
0
  }
493
0
  return err;
494
0
}
495
496
0
static inline void zip_archive_finalize(mz_zip_archive *pzip) {
497
0
  mz_zip_writer_finalize_archive(pzip);
498
0
  zip_archive_truncate(pzip);
499
0
}
500
501
static ssize_t zip_entry_mark(struct zip_t *zip,
502
                              struct zip_entry_mark_t *entry_mark,
503
                              const size_t n, char *const entries[],
504
0
                              const size_t len) {
505
0
  size_t i = 0;
506
0
  ssize_t err = 0;
507
0
  if (!zip || !entry_mark || !entries) {
508
0
    return ZIP_ENOINIT;
509
0
  }
510
511
0
  mz_zip_archive_file_stat file_stat;
512
0
  mz_uint64 d_pos = UINT64_MAX;
513
0
  for (i = 0; i < n; ++i) {
514
0
    if ((err = zip_entry_openbyindex(zip, i))) {
515
0
      return (ssize_t)err;
516
0
    }
517
518
0
    mz_bool name_matches = MZ_FALSE;
519
0
    {
520
0
      size_t j;
521
0
      for (j = 0; j < len; ++j) {
522
0
        if (strcmp(zip->entry.name, entries[j]) == 0) {
523
0
          name_matches = MZ_TRUE;
524
0
          break;
525
0
        }
526
0
      }
527
0
    }
528
0
    if (name_matches) {
529
0
      entry_mark[i].type = MZ_DELETE;
530
0
    } else {
531
0
      entry_mark[i].type = MZ_KEEP;
532
0
    }
533
534
0
    if (!mz_zip_reader_file_stat(&zip->archive, (mz_uint)i, &file_stat)) {
535
0
      return ZIP_ENOENT;
536
0
    }
537
538
0
    zip_entry_close(zip);
539
540
0
    entry_mark[i].m_local_header_ofs = file_stat.m_local_header_ofs;
541
0
    entry_mark[i].file_index = (ssize_t)-1;
542
0
    entry_mark[i].lf_length = 0;
543
0
    if ((entry_mark[i].type) == MZ_DELETE &&
544
0
        (d_pos > entry_mark[i].m_local_header_ofs)) {
545
0
      d_pos = entry_mark[i].m_local_header_ofs;
546
0
    }
547
0
  }
548
549
0
  for (i = 0; i < n; ++i) {
550
0
    if ((entry_mark[i].m_local_header_ofs > d_pos) &&
551
0
        (entry_mark[i].type != MZ_DELETE)) {
552
0
      entry_mark[i].type = MZ_MOVE;
553
0
    }
554
0
  }
555
0
  return err;
556
0
}
557
558
static ssize_t zip_entry_markbyindex(struct zip_t *zip,
559
                                     struct zip_entry_mark_t *entry_mark,
560
                                     const size_t n, size_t entries[],
561
0
                                     const size_t len) {
562
0
  size_t i = 0;
563
0
  ssize_t err = 0;
564
0
  if (!zip || !entry_mark || !entries) {
565
0
    return ZIP_ENOINIT;
566
0
  }
567
568
0
  mz_zip_archive_file_stat file_stat;
569
0
  mz_uint64 d_pos = UINT64_MAX;
570
0
  for (i = 0; i < n; ++i) {
571
0
    if ((err = zip_entry_openbyindex(zip, i))) {
572
0
      return (ssize_t)err;
573
0
    }
574
575
0
    mz_bool matches = MZ_FALSE;
576
0
    {
577
0
      size_t j;
578
0
      for (j = 0; j < len; ++j) {
579
0
        if (i == entries[j]) {
580
0
          matches = MZ_TRUE;
581
0
          break;
582
0
        }
583
0
      }
584
0
    }
585
0
    if (matches) {
586
0
      entry_mark[i].type = MZ_DELETE;
587
0
    } else {
588
0
      entry_mark[i].type = MZ_KEEP;
589
0
    }
590
591
0
    if (!mz_zip_reader_file_stat(&zip->archive, (mz_uint)i, &file_stat)) {
592
0
      return ZIP_ENOENT;
593
0
    }
594
595
0
    zip_entry_close(zip);
596
597
0
    entry_mark[i].m_local_header_ofs = file_stat.m_local_header_ofs;
598
0
    entry_mark[i].file_index = (ssize_t)-1;
599
0
    entry_mark[i].lf_length = 0;
600
0
    if ((entry_mark[i].type) == MZ_DELETE &&
601
0
        (d_pos > entry_mark[i].m_local_header_ofs)) {
602
0
      d_pos = entry_mark[i].m_local_header_ofs;
603
0
    }
604
0
  }
605
606
0
  for (i = 0; i < n; ++i) {
607
0
    if ((entry_mark[i].m_local_header_ofs > d_pos) &&
608
0
        (entry_mark[i].type != MZ_DELETE)) {
609
0
      entry_mark[i].type = MZ_MOVE;
610
0
    }
611
0
  }
612
0
  return err;
613
0
}
614
static ssize_t zip_index_next(mz_uint64 *local_header_ofs_array,
615
0
                              ssize_t cur_index) {
616
0
  ssize_t new_index = 0, i;
617
0
  for (i = cur_index - 1; i >= 0; --i) {
618
0
    if (local_header_ofs_array[cur_index] > local_header_ofs_array[i]) {
619
0
      new_index = i + 1;
620
0
      return new_index;
621
0
    }
622
0
  }
623
0
  return new_index;
624
0
}
625
626
0
static ssize_t zip_sort(mz_uint64 *local_header_ofs_array, ssize_t cur_index) {
627
0
  ssize_t nxt_index = zip_index_next(local_header_ofs_array, cur_index);
628
629
0
  if (nxt_index != cur_index) {
630
0
    mz_uint64 temp = local_header_ofs_array[cur_index];
631
0
    ssize_t i;
632
0
    for (i = cur_index; i > nxt_index; i--) {
633
0
      local_header_ofs_array[i] = local_header_ofs_array[i - 1];
634
0
    }
635
0
    local_header_ofs_array[nxt_index] = temp;
636
0
  }
637
0
  return nxt_index;
638
0
}
639
640
static int zip_index_update(struct zip_entry_mark_t *entry_mark,
641
0
                            ssize_t last_index, ssize_t nxt_index) {
642
0
  ssize_t j;
643
0
  for (j = 0; j < last_index; j++) {
644
0
    if (entry_mark[j].file_index >= nxt_index) {
645
0
      entry_mark[j].file_index += 1;
646
0
    }
647
0
  }
648
0
  entry_mark[nxt_index].file_index = last_index;
649
0
  return 0;
650
0
}
651
652
static int zip_entry_finalize(struct zip_t *zip,
653
                              struct zip_entry_mark_t *entry_mark,
654
0
                              const size_t n) {
655
0
  size_t i = 0;
656
0
  mz_uint64 *local_header_ofs_array = (mz_uint64 *)calloc(n, sizeof(mz_uint64));
657
0
  if (!local_header_ofs_array) {
658
0
    return ZIP_EOOMEM;
659
0
  }
660
661
0
  for (i = 0; i < n; ++i) {
662
0
    local_header_ofs_array[i] = entry_mark[i].m_local_header_ofs;
663
0
    ssize_t index = zip_sort(local_header_ofs_array, i);
664
665
0
    if ((size_t)index != i) {
666
0
      zip_index_update(entry_mark, i, index);
667
0
    }
668
0
    entry_mark[i].file_index = index;
669
0
  }
670
671
0
  size_t *length = (size_t *)calloc(n, sizeof(size_t));
672
0
  if (!length) {
673
0
    CLEANUP(local_header_ofs_array);
674
0
    return ZIP_EOOMEM;
675
0
  }
676
0
  for (i = 0; i < n - 1; i++) {
677
0
    length[i] =
678
0
        (size_t)(local_header_ofs_array[i + 1] - local_header_ofs_array[i]);
679
0
  }
680
0
  length[n - 1] =
681
0
      (size_t)(zip->archive.m_archive_size - local_header_ofs_array[n - 1]);
682
683
0
  for (i = 0; i < n; i++) {
684
0
    entry_mark[i].lf_length = length[entry_mark[i].file_index];
685
0
  }
686
687
0
  CLEANUP(length);
688
0
  CLEANUP(local_header_ofs_array);
689
0
  return 0;
690
0
}
691
692
static ssize_t zip_entry_set(struct zip_t *zip,
693
                             struct zip_entry_mark_t *entry_mark, size_t n,
694
0
                             char *const entries[], const size_t len) {
695
0
  ssize_t err = 0;
696
697
0
  if ((err = zip_entry_mark(zip, entry_mark, n, entries, len)) < 0) {
698
0
    return err;
699
0
  }
700
0
  if ((err = zip_entry_finalize(zip, entry_mark, n)) < 0) {
701
0
    return err;
702
0
  }
703
0
  return 0;
704
0
}
705
706
static ssize_t zip_entry_setbyindex(struct zip_t *zip,
707
                                    struct zip_entry_mark_t *entry_mark,
708
                                    size_t n, size_t entries[],
709
0
                                    const size_t len) {
710
0
  ssize_t err = 0;
711
712
0
  if ((err = zip_entry_markbyindex(zip, entry_mark, n, entries, len)) < 0) {
713
0
    return err;
714
0
  }
715
0
  if ((err = zip_entry_finalize(zip, entry_mark, n)) < 0) {
716
0
    return err;
717
0
  }
718
0
  return 0;
719
0
}
720
721
static ssize_t zip_mem_move(void *pBuf, size_t bufSize, const mz_uint64 to,
722
0
                            const mz_uint64 from, const size_t length) {
723
0
  uint8_t *dst = NULL, *src = NULL, *end = NULL;
724
725
0
  if (!pBuf) {
726
0
    return ZIP_EINVIDX;
727
0
  }
728
729
0
  end = (uint8_t *)pBuf + bufSize;
730
731
0
  if (to > bufSize) {
732
0
    return ZIP_EINVIDX;
733
0
  }
734
735
0
  if (from > bufSize) {
736
0
    return ZIP_EINVIDX;
737
0
  }
738
739
0
  dst = (uint8_t *)pBuf + to;
740
0
  src = (uint8_t *)pBuf + from;
741
742
0
  if (((dst + length) > end) || ((src + length) > end)) {
743
0
    return ZIP_EINVIDX;
744
0
  }
745
746
0
  memmove(dst, src, length);
747
0
  return length;
748
0
}
749
750
static ssize_t zip_file_move(MZ_FILE *m_pFile, const mz_uint64 to,
751
                             const mz_uint64 from, const size_t length,
752
0
                             mz_uint8 *move_buf, const size_t capacity_size) {
753
0
  if (length > capacity_size) {
754
0
    return ZIP_ECAPSIZE;
755
0
  }
756
0
  if (MZ_FSEEK64(m_pFile, from, SEEK_SET)) {
757
0
    return ZIP_EFSEEK;
758
0
  }
759
0
  if (fread(move_buf, 1, length, m_pFile) != length) {
760
0
    return ZIP_EFREAD;
761
0
  }
762
0
  if (MZ_FSEEK64(m_pFile, to, SEEK_SET)) {
763
0
    return ZIP_EFSEEK;
764
0
  }
765
0
  if (fwrite(move_buf, 1, length, m_pFile) != length) {
766
0
    return ZIP_EFWRITE;
767
0
  }
768
0
  return (ssize_t)length;
769
0
}
770
771
static ssize_t zip_files_move(struct zip_t *zip, mz_uint64 writen_num,
772
0
                              mz_uint64 read_num, size_t length) {
773
0
  ssize_t n = 0;
774
0
  const size_t page_size = 1 << 12; // 4K
775
0
  mz_zip_internal_state *pState = zip->archive.m_pState;
776
777
0
  mz_uint8 *move_buf = (mz_uint8 *)calloc(1, page_size);
778
0
  if (!move_buf) {
779
0
    return ZIP_EOOMEM;
780
0
  }
781
782
0
  ssize_t moved_length = 0;
783
0
  ssize_t move_count = 0;
784
0
  while ((mz_int64)length > 0) {
785
0
    move_count = (length >= page_size) ? page_size : length;
786
787
0
    if (pState->m_pFile) {
788
0
      n = zip_file_move(pState->m_pFile, writen_num, read_num, move_count,
789
0
                        move_buf, page_size);
790
0
    } else if (pState->m_pMem) {
791
0
      n = zip_mem_move(pState->m_pMem, pState->m_mem_size, writen_num, read_num,
792
0
                       move_count);
793
0
    } else {
794
0
      CLEANUP(move_buf);
795
0
      return ZIP_ENOFILE;
796
0
    }
797
798
0
    if (n < 0) {
799
0
      moved_length = n;
800
0
      goto cleanup;
801
0
    }
802
803
0
    if (n != move_count) {
804
0
      goto cleanup;
805
0
    }
806
807
0
    writen_num += move_count;
808
0
    read_num += move_count;
809
0
    length -= move_count;
810
0
    moved_length += move_count;
811
0
  }
812
813
0
cleanup:
814
0
  CLEANUP(move_buf);
815
0
  return moved_length;
816
0
}
817
818
static int zip_central_dir_move(mz_zip_internal_state *pState, int begin,
819
0
                                int end, int entry_num) {
820
0
  if (begin == entry_num) {
821
0
    return 0;
822
0
  }
823
824
0
  size_t l_size = 0;
825
0
  size_t r_size = 0;
826
0
  mz_uint32 d_size = 0;
827
0
  mz_uint8 *next = NULL;
828
0
  mz_uint8 *deleted = &MZ_ZIP_ARRAY_ELEMENT(
829
0
      &pState->m_central_dir, mz_uint8,
830
0
      MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, begin));
831
0
  l_size = (size_t)(deleted - (mz_uint8 *)(pState->m_central_dir.m_p));
832
0
  if (end == entry_num) {
833
0
    r_size = 0;
834
0
  } else {
835
0
    next = &MZ_ZIP_ARRAY_ELEMENT(
836
0
        &pState->m_central_dir, mz_uint8,
837
0
        MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, end));
838
0
    r_size = pState->m_central_dir.m_size -
839
0
             (mz_uint32)(next - (mz_uint8 *)(pState->m_central_dir.m_p));
840
0
    d_size = (mz_uint32)(next - deleted);
841
0
  }
842
843
0
  if (next && l_size == 0) {
844
0
    memmove(pState->m_central_dir.m_p, next, r_size);
845
0
    pState->m_central_dir.m_p = MZ_REALLOC(pState->m_central_dir.m_p, r_size);
846
0
    {
847
0
      int i;
848
0
      for (i = end; i < entry_num; i++) {
849
0
        MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -=
850
0
            d_size;
851
0
      }
852
0
    }
853
0
  }
854
855
0
  if (next && l_size * r_size != 0) {
856
0
    memmove(deleted, next, r_size);
857
0
    {
858
0
      int i;
859
0
      for (i = end; i < entry_num; i++) {
860
0
        MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i) -=
861
0
            d_size;
862
0
      }
863
0
    }
864
0
  }
865
866
0
  pState->m_central_dir.m_size = l_size + r_size;
867
0
  return 0;
868
0
}
869
870
static int zip_central_dir_delete(mz_zip_internal_state *pState,
871
                                  int *deleted_entry_index_array,
872
0
                                  int entry_num) {
873
0
  int i = 0;
874
0
  int begin = 0;
875
0
  int end = 0;
876
0
  int d_num = 0;
877
0
  while (i < entry_num) {
878
0
    while ((i < entry_num) && (!deleted_entry_index_array[i])) {
879
0
      i++;
880
0
    }
881
0
    begin = i;
882
883
0
    while ((i < entry_num) && (deleted_entry_index_array[i])) {
884
0
      i++;
885
0
    }
886
0
    end = i;
887
0
    zip_central_dir_move(pState, begin, end, entry_num);
888
0
  }
889
890
0
  i = 0;
891
0
  while (i < entry_num) {
892
0
    while ((i < entry_num) && (!deleted_entry_index_array[i])) {
893
0
      i++;
894
0
    }
895
0
    begin = i;
896
0
    if (begin == entry_num) {
897
0
      break;
898
0
    }
899
0
    while ((i < entry_num) && (deleted_entry_index_array[i])) {
900
0
      i++;
901
0
    }
902
0
    end = i;
903
0
    int k = 0, j;
904
0
    for (j = end; j < entry_num; j++) {
905
0
      MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32,
906
0
                           begin + k) =
907
0
          (mz_uint32)MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets,
908
0
                                          mz_uint32, j);
909
0
      k++;
910
0
    }
911
0
    d_num += end - begin;
912
0
  }
913
914
0
  pState->m_central_dir_offsets.m_size =
915
0
      sizeof(mz_uint32) * (entry_num - d_num);
916
0
  return 0;
917
0
}
918
919
static ssize_t zip_entries_delete_mark(struct zip_t *zip,
920
                                       struct zip_entry_mark_t *entry_mark,
921
0
                                       int entry_num) {
922
0
  mz_uint64 writen_num = 0;
923
0
  mz_uint64 read_num = 0;
924
0
  size_t deleted_length = 0;
925
0
  size_t move_length = 0;
926
0
  int i = 0;
927
0
  size_t deleted_entry_num = 0;
928
0
  ssize_t n = 0;
929
930
0
  mz_bool *deleted_entry_flag_array =
931
0
      (mz_bool *)calloc(entry_num, sizeof(mz_bool));
932
0
  if (deleted_entry_flag_array == NULL) {
933
0
    return ZIP_EOOMEM;
934
0
  }
935
936
0
  mz_zip_internal_state *pState = zip->archive.m_pState;
937
0
  zip->archive.m_zip_mode = MZ_ZIP_MODE_WRITING;
938
939
0
  if (pState->m_pFile) {
940
0
    if (MZ_FSEEK64(pState->m_pFile, 0, SEEK_SET)) {
941
0
      CLEANUP(deleted_entry_flag_array);
942
0
      return ZIP_ENOENT;
943
0
    }
944
0
  }
945
946
0
  while (i < entry_num) {
947
0
    while ((i < entry_num) && (entry_mark[i].type == MZ_KEEP)) {
948
0
      writen_num += entry_mark[i].lf_length;
949
0
      read_num = writen_num;
950
0
      i++;
951
0
    }
952
953
0
    while ((i < entry_num) && (entry_mark[i].type == MZ_DELETE)) {
954
0
      deleted_entry_flag_array[i] = MZ_TRUE;
955
0
      read_num += entry_mark[i].lf_length;
956
0
      deleted_length += entry_mark[i].lf_length;
957
0
      i++;
958
0
      deleted_entry_num++;
959
0
    }
960
961
0
    while ((i < entry_num) && (entry_mark[i].type == MZ_MOVE)) {
962
0
      move_length += entry_mark[i].lf_length;
963
0
      mz_uint8 *p = &MZ_ZIP_ARRAY_ELEMENT(
964
0
          &pState->m_central_dir, mz_uint8,
965
0
          MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i));
966
0
      if (!p) {
967
0
        CLEANUP(deleted_entry_flag_array);
968
0
        return ZIP_ENOENT;
969
0
      }
970
0
      mz_uint32 offset = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
971
0
      offset -= (mz_uint32)deleted_length;
972
0
      MZ_WRITE_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS, offset);
973
0
      i++;
974
0
    }
975
976
0
    n = zip_files_move(zip, writen_num, read_num, move_length);
977
0
    if (n != (ssize_t)move_length) {
978
0
      CLEANUP(deleted_entry_flag_array);
979
0
      return n;
980
0
    }
981
0
    writen_num += move_length;
982
0
    read_num += move_length;
983
0
  }
984
985
0
  zip->archive.m_archive_size -= (mz_uint64)deleted_length;
986
0
  zip->archive.m_total_files =
987
0
      (mz_uint32)entry_num - (mz_uint32)deleted_entry_num;
988
989
0
  zip_central_dir_delete(pState, deleted_entry_flag_array, entry_num);
990
0
  CLEANUP(deleted_entry_flag_array);
991
992
0
  return (ssize_t)deleted_entry_num;
993
0
}
994
995
0
struct zip_t *zip_open(const char *zipname, int level, char mode) {
996
0
  int errnum = 0;
997
0
  return zip_openwitherror(zipname, level, mode, &errnum);
998
0
}
999
1000
struct zip_t *zip_openwitherror(const char *zipname, int level, char mode,
1001
0
                                int *errnum) {
1002
0
  struct zip_t *zip = NULL;
1003
0
  mz_uint wflags = (mode == 'w') ? MZ_ZIP_FLAG_WRITE_ZIP64 : 0;
1004
0
  *errnum = 0;
1005
1006
0
  if (!zipname || strlen(zipname) < 1) {
1007
    // zip_t archive name is empty or NULL
1008
0
    *errnum = ZIP_EINVZIPNAME;
1009
0
    goto cleanup;
1010
0
  }
1011
1012
0
  if (level < 0)
1013
0
    level = MZ_DEFAULT_LEVEL;
1014
0
  if ((level & 0xF) > MZ_UBER_COMPRESSION) {
1015
    // Wrong compression level
1016
0
    *errnum = ZIP_EINVLVL;
1017
0
    goto cleanup;
1018
0
  }
1019
1020
0
  zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
1021
0
  if (!zip) {
1022
    // out of memory
1023
0
    *errnum = ZIP_EOOMEM;
1024
0
    goto cleanup;
1025
0
  }
1026
1027
0
  zip->level = (mz_uint)level;
1028
0
  zip->entry.index = -1;
1029
0
  switch (mode) {
1030
0
  case 'w':
1031
0
  case ('w' - 64): {
1032
    // Create a new archive.
1033
0
    if (!mz_zip_writer_init_file_v2(&(zip->archive), zipname, 0, wflags)) {
1034
      // Cannot initialize zip_archive writer
1035
0
      *errnum = ZIP_EWINIT;
1036
0
      goto cleanup;
1037
0
    }
1038
0
  } break;
1039
1040
0
  case 'r':
1041
0
  case ('r' - 64): {
1042
0
    if (!mz_zip_reader_init_file_v2(
1043
0
            &(zip->archive), zipname,
1044
0
            zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0)) {
1045
      // An archive file does not exist or cannot initialize
1046
      // zip_archive reader
1047
0
      *errnum = ZIP_ERINIT;
1048
0
      goto cleanup;
1049
0
    }
1050
0
  } break;
1051
1052
0
  case 'a':
1053
0
  case 'd':
1054
0
  case ('a' - 64):
1055
0
  case ('d' - 64): {
1056
0
    MZ_FILE *fp = MZ_FOPEN(zipname, "r+b");
1057
0
    if (!fp) {
1058
0
      *errnum = ZIP_EOPNFILE;
1059
0
      goto cleanup;
1060
0
    }
1061
0
    if (!mz_zip_reader_init_cfile(
1062
0
            &(zip->archive), fp, 0,
1063
0
            zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) {
1064
      // An archive file does not exist or cannot initialize zip_archive
1065
      // reader
1066
0
      *errnum = ZIP_ERINIT;
1067
0
      fclose(fp);
1068
0
      goto cleanup;
1069
0
    }
1070
0
    if (!mz_zip_writer_init_from_reader_v2(&(zip->archive), zipname, 0)) {
1071
0
      *errnum = ZIP_EWRINIT;
1072
0
      fclose(fp);
1073
0
      mz_zip_reader_end(&(zip->archive));
1074
0
      goto cleanup;
1075
0
    }
1076
    // The file pointer is now owned by the archive object.
1077
0
    zip->archive.m_zip_type = MZ_ZIP_TYPE_FILE;
1078
0
  } break;
1079
1080
0
  default:
1081
0
    *errnum = ZIP_EINVMODE;
1082
0
    goto cleanup;
1083
0
  }
1084
1085
0
  return zip;
1086
1087
0
cleanup:
1088
0
  CLEANUP(zip);
1089
0
  return NULL;
1090
0
}
1091
1092
0
void zip_close(struct zip_t *zip) {
1093
0
  if (zip) {
1094
0
    mz_zip_archive *pZip = &(zip->archive);
1095
    // Always finalize, even if adding failed for some reason, so we have a
1096
    // valid central directory.
1097
0
    if (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) {
1098
0
      mz_zip_writer_finalize_archive(pZip);
1099
0
    }
1100
1101
0
    if (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING ||
1102
0
        pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) {
1103
0
      zip_archive_truncate(pZip);
1104
0
      mz_zip_writer_end(pZip);
1105
0
    } else if (pZip->m_zip_mode == MZ_ZIP_MODE_READING) {
1106
0
      mz_zip_reader_end(pZip);
1107
0
    }
1108
1109
0
    CLEANUP(zip);
1110
0
  }
1111
0
}
1112
1113
0
int zip_is64(struct zip_t *zip) {
1114
0
  if (!zip || !zip->archive.m_pState) {
1115
    // zip_t handler or zip state is not initialized
1116
0
    return ZIP_ENOINIT;
1117
0
  }
1118
1119
0
  return (int)zip->archive.m_pState->m_zip64;
1120
0
}
1121
1122
0
int zip_offset(struct zip_t *zip, uint64_t *offset) {
1123
0
  if (!zip || !zip->archive.m_pState) {
1124
    // zip_t handler or zip state is not initialized
1125
0
    return ZIP_ENOINIT;
1126
0
  }
1127
1128
0
  *offset = mz_zip_get_archive_file_start_offset(&zip->archive);
1129
0
  return 0;
1130
0
}
1131
1132
static int _zip_entry_open(struct zip_t *zip, const char *entryname,
1133
0
                           int case_sensitive) {
1134
0
  size_t entrylen = 0;
1135
0
  mz_zip_archive *pzip = NULL;
1136
0
  mz_uint num_alignment_padding_bytes, level;
1137
0
  mz_zip_archive_file_stat stats;
1138
0
  int err = 0;
1139
0
  mz_uint16 dos_time = 0, dos_date = 0;
1140
0
  mz_uint32 extra_size = 0;
1141
0
  mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE];
1142
0
  mz_uint64 local_dir_header_ofs = 0;
1143
1144
0
  if (!zip) {
1145
0
    return ZIP_ENOINIT;
1146
0
  }
1147
1148
0
  local_dir_header_ofs = zip->archive.m_archive_size;
1149
1150
0
  if (!entryname) {
1151
0
    return ZIP_EINVENTNAME;
1152
0
  }
1153
1154
0
  entrylen = strlen(entryname);
1155
0
  if (entrylen == 0) {
1156
0
    return ZIP_EINVENTNAME;
1157
0
  }
1158
1159
0
  if (zip->entry.name) {
1160
0
    CLEANUP(zip->entry.name);
1161
0
  }
1162
1163
0
  pzip = &(zip->archive);
1164
0
  if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) {
1165
0
    zip->entry.name = zip_strclone(entryname, entrylen);
1166
0
    if (!zip->entry.name) {
1167
      // Cannot parse zip entry name
1168
0
      return ZIP_EINVENTNAME;
1169
0
    }
1170
1171
0
    zip->entry.index = (ssize_t)mz_zip_reader_locate_file(
1172
0
        pzip, zip->entry.name, NULL,
1173
0
        case_sensitive ? MZ_ZIP_FLAG_CASE_SENSITIVE : 0);
1174
0
    if (zip->entry.index < (ssize_t)0) {
1175
0
      err = ZIP_ENOENT;
1176
0
      goto cleanup;
1177
0
    }
1178
1179
0
    if (!mz_zip_reader_file_stat(pzip, (mz_uint)zip->entry.index, &stats)) {
1180
0
      err = ZIP_ENOENT;
1181
0
      goto cleanup;
1182
0
    }
1183
1184
0
    zip->entry.comp_size = stats.m_comp_size;
1185
0
    zip->entry.uncomp_size = stats.m_uncomp_size;
1186
0
    zip->entry.uncomp_crc32 = stats.m_crc32;
1187
0
    zip->entry.dir_offset = stats.m_central_dir_ofs;
1188
0
    zip->entry.header_offset = stats.m_local_header_ofs;
1189
0
    zip->entry.method = stats.m_method;
1190
0
    zip->entry.external_attr = stats.m_external_attr;
1191
0
#ifndef MINIZ_NO_TIME
1192
0
    zip->entry.m_time = stats.m_time;
1193
0
#endif
1194
1195
0
    return 0;
1196
0
  }
1197
1198
  /*
1199
    .ZIP File Format Specification Version: 6.3.3
1200
1201
    4.4.17.1 The name of the file, with optional relative path.
1202
    The path stored MUST not contain a drive or
1203
    device letter, or a leading slash.  All slashes
1204
    MUST be forward slashes '/' as opposed to
1205
    backwards slashes '\' for compatibility with Amiga
1206
    and UNIX file systems etc.  If input came from standard
1207
    input, there is no file name field.
1208
  */
1209
0
  zip->entry.name = zip_strrpl(entryname, entrylen, '\\', '/');
1210
0
  if (!zip->entry.name) {
1211
    // Cannot parse zip entry name
1212
0
    return ZIP_EINVENTNAME;
1213
0
  }
1214
1215
0
  level = zip->level & 0xF;
1216
1217
0
  zip->entry.index = (ssize_t)zip->archive.m_total_files;
1218
0
  zip->entry.comp_size = 0;
1219
0
  zip->entry.uncomp_size = 0;
1220
0
  zip->entry.uncomp_crc32 = MZ_CRC32_INIT;
1221
0
  zip->entry.dir_offset = zip->archive.m_archive_size;
1222
0
  zip->entry.header_offset = zip->archive.m_archive_size;
1223
0
  memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8));
1224
0
  zip->entry.method = level ? MZ_DEFLATED : 0;
1225
1226
  // UNIX or APPLE
1227
#if MZ_PLATFORM == 3 || MZ_PLATFORM == 19
1228
  // regular file with rw-r--r-- permissions
1229
  zip->entry.external_attr = (mz_uint32)(0100644) << 16;
1230
#else
1231
0
  zip->entry.external_attr = 0;
1232
0
#endif
1233
1234
0
  num_alignment_padding_bytes =
1235
0
      mz_zip_writer_compute_padding_needed_for_file_alignment(pzip);
1236
1237
0
  if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) {
1238
    // Invalid zip mode
1239
0
    err = ZIP_EINVMODE;
1240
0
    goto cleanup;
1241
0
  }
1242
0
  if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) {
1243
    // Invalid zip compression level
1244
0
    err = ZIP_EINVLVL;
1245
0
    goto cleanup;
1246
0
  }
1247
1248
0
  if (!mz_zip_writer_write_zeros(pzip, zip->entry.dir_offset,
1249
0
                                 num_alignment_padding_bytes)) {
1250
    // Cannot memset zip entry header
1251
0
    err = ZIP_EMEMSET;
1252
0
    goto cleanup;
1253
0
  }
1254
0
  local_dir_header_ofs += num_alignment_padding_bytes;
1255
1256
0
  zip->entry.m_time = time(NULL);
1257
0
#ifndef MINIZ_NO_TIME
1258
0
  mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date);
1259
0
#endif
1260
1261
  // ZIP64 header with NULL sizes (sizes will be in the data descriptor, just
1262
  // after file data)
1263
0
  extra_size = mz_zip_writer_create_zip64_extra_data(
1264
0
      extra_data, NULL, NULL,
1265
0
      (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);
1266
1267
0
  if (!mz_zip_writer_create_local_dir_header(
1268
0
          pzip, zip->entry.header, (mz_uint16)entrylen, (mz_uint16)extra_size,
1269
0
          0, 0, 0, zip->entry.method,
1270
0
          MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 |
1271
0
              MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR,
1272
0
          dos_time, dos_date)) {
1273
    // Cannot create zip entry header
1274
0
    err = ZIP_EMEMSET;
1275
0
    goto cleanup;
1276
0
  }
1277
1278
0
  zip->entry.header_offset =
1279
0
      zip->entry.dir_offset + num_alignment_padding_bytes;
1280
1281
0
  if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset,
1282
0
                     zip->entry.header,
1283
0
                     sizeof(zip->entry.header)) != sizeof(zip->entry.header)) {
1284
    // Cannot write zip entry header
1285
0
    err = ZIP_EMEMSET;
1286
0
    goto cleanup;
1287
0
  }
1288
1289
0
  if (pzip->m_file_offset_alignment) {
1290
0
    MZ_ASSERT(
1291
0
        (zip->entry.header_offset & (pzip->m_file_offset_alignment - 1)) == 0);
1292
0
  }
1293
0
  zip->entry.dir_offset +=
1294
0
      num_alignment_padding_bytes + sizeof(zip->entry.header);
1295
1296
0
  if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, zip->entry.name,
1297
0
                     entrylen) != entrylen) {
1298
    // Cannot write data to zip entry
1299
0
    err = ZIP_EWRTENT;
1300
0
    goto cleanup;
1301
0
  }
1302
1303
0
  zip->entry.dir_offset += entrylen;
1304
1305
0
  if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, extra_data,
1306
0
                     extra_size) != extra_size) {
1307
    // Cannot write ZIP64 data to zip entry
1308
0
    err = ZIP_EWRTENT;
1309
0
    goto cleanup;
1310
0
  }
1311
0
  zip->entry.dir_offset += extra_size;
1312
1313
0
  if (level) {
1314
0
    zip->entry.state.m_pZip = pzip;
1315
0
    zip->entry.state.m_cur_archive_file_ofs = zip->entry.dir_offset;
1316
0
    zip->entry.state.m_comp_size = 0;
1317
1318
0
    if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback,
1319
0
                   &(zip->entry.state),
1320
0
                   (int)tdefl_create_comp_flags_from_zip_params(
1321
0
                       (int)level, -15, MZ_DEFAULT_STRATEGY)) !=
1322
0
        TDEFL_STATUS_OKAY) {
1323
      // Cannot initialize the zip compressor
1324
0
      err = ZIP_ETDEFLINIT;
1325
0
      goto cleanup;
1326
0
    }
1327
0
  }
1328
1329
0
  return 0;
1330
1331
0
cleanup:
1332
0
  CLEANUP(zip->entry.name);
1333
0
  return err;
1334
0
}
1335
1336
0
int zip_entry_open(struct zip_t *zip, const char *entryname) {
1337
0
  return _zip_entry_open(zip, entryname, 0);
1338
0
}
1339
1340
0
int zip_entry_opencasesensitive(struct zip_t *zip, const char *entryname) {
1341
0
  return _zip_entry_open(zip, entryname, 1);
1342
0
}
1343
1344
0
int zip_entry_openbyindex(struct zip_t *zip, size_t index) {
1345
0
  mz_zip_archive *pZip = NULL;
1346
0
  mz_zip_archive_file_stat stats;
1347
0
  mz_uint namelen;
1348
0
  const mz_uint8 *pHeader;
1349
0
  const char *pFilename;
1350
1351
0
  if (!zip) {
1352
    // zip_t handler is not initialized
1353
0
    return ZIP_ENOINIT;
1354
0
  }
1355
1356
0
  pZip = &(zip->archive);
1357
0
  if (pZip->m_zip_mode != MZ_ZIP_MODE_READING) {
1358
    // open by index requires readonly mode
1359
0
    return ZIP_EINVMODE;
1360
0
  }
1361
1362
0
  if (index >= (size_t)pZip->m_total_files) {
1363
    // index out of range
1364
0
    return ZIP_EINVIDX;
1365
0
  }
1366
1367
0
  if (!(pHeader = &MZ_ZIP_ARRAY_ELEMENT(
1368
0
            &pZip->m_pState->m_central_dir, mz_uint8,
1369
0
            MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets,
1370
0
                                 mz_uint32, (mz_uint)index)))) {
1371
    // cannot find header in central directory
1372
0
    return ZIP_ENOHDR;
1373
0
  }
1374
1375
0
  namelen = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
1376
0
  pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
1377
1378
0
  if (zip->entry.name) {
1379
0
    CLEANUP(zip->entry.name);
1380
0
  }
1381
1382
0
  zip->entry.name = zip_strclone(pFilename, namelen);
1383
0
  if (!zip->entry.name) {
1384
    // local entry name is NULL
1385
0
    return ZIP_EINVENTNAME;
1386
0
  }
1387
1388
0
  if (!mz_zip_reader_file_stat(pZip, (mz_uint)index, &stats)) {
1389
0
    return ZIP_ENOENT;
1390
0
  }
1391
1392
0
  zip->entry.index = (ssize_t)index;
1393
0
  zip->entry.comp_size = stats.m_comp_size;
1394
0
  zip->entry.uncomp_size = stats.m_uncomp_size;
1395
0
  zip->entry.uncomp_crc32 = stats.m_crc32;
1396
0
  zip->entry.dir_offset = stats.m_central_dir_ofs;
1397
0
  zip->entry.header_offset = stats.m_local_header_ofs;
1398
0
  zip->entry.method = stats.m_method;
1399
0
  zip->entry.external_attr = stats.m_external_attr;
1400
0
#ifndef MINIZ_NO_TIME
1401
0
  zip->entry.m_time = stats.m_time;
1402
0
#endif
1403
1404
0
  return 0;
1405
0
}
1406
1407
0
int zip_entry_close(struct zip_t *zip) {
1408
0
  mz_zip_archive *pzip = NULL;
1409
0
  mz_uint level;
1410
0
  tdefl_status done;
1411
0
  mz_uint16 entrylen;
1412
0
  mz_uint16 dos_time = 0, dos_date = 0;
1413
0
  int err = 0;
1414
0
  mz_uint8 *pExtra_data = NULL;
1415
0
  mz_uint32 extra_size = 0;
1416
0
  mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE];
1417
0
  mz_uint8 local_dir_footer[MZ_ZIP_DATA_DESCRIPTER_SIZE64];
1418
0
  mz_uint32 local_dir_footer_size = MZ_ZIP_DATA_DESCRIPTER_SIZE64;
1419
1420
0
  if (!zip) {
1421
    // zip_t handler is not initialized
1422
0
    err = ZIP_ENOINIT;
1423
0
    goto cleanup;
1424
0
  }
1425
1426
0
  pzip = &(zip->archive);
1427
0
  if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) {
1428
0
    goto cleanup;
1429
0
  }
1430
1431
0
  level = zip->level & 0xF;
1432
0
  if (level) {
1433
0
    done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH);
1434
0
    if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) {
1435
      // Cannot flush compressed buffer
1436
0
      err = ZIP_ETDEFLBUF;
1437
0
      goto cleanup;
1438
0
    }
1439
0
    zip->entry.comp_size = zip->entry.state.m_comp_size;
1440
0
    zip->entry.dir_offset = zip->entry.state.m_cur_archive_file_ofs;
1441
0
    zip->entry.method = MZ_DEFLATED;
1442
0
  }
1443
1444
0
  entrylen = (mz_uint16)strlen(zip->entry.name);
1445
0
#ifndef MINIZ_NO_TIME
1446
0
  mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date);
1447
0
#endif
1448
1449
0
  MZ_WRITE_LE32(local_dir_footer + 0, MZ_ZIP_DATA_DESCRIPTOR_ID);
1450
0
  MZ_WRITE_LE32(local_dir_footer + 4, zip->entry.uncomp_crc32);
1451
0
  MZ_WRITE_LE64(local_dir_footer + 8, zip->entry.comp_size);
1452
0
  MZ_WRITE_LE64(local_dir_footer + 16, zip->entry.uncomp_size);
1453
1454
0
  if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset,
1455
0
                     local_dir_footer,
1456
0
                     local_dir_footer_size) != local_dir_footer_size) {
1457
    // Cannot write zip entry header
1458
0
    err = ZIP_EWRTHDR;
1459
0
    goto cleanup;
1460
0
  }
1461
0
  zip->entry.dir_offset += local_dir_footer_size;
1462
1463
0
  pExtra_data = extra_data;
1464
0
  extra_size = mz_zip_writer_create_zip64_extra_data(
1465
0
      extra_data,
1466
0
      (zip->entry.uncomp_size >= MZ_UINT32_MAX) ? &zip->entry.uncomp_size
1467
0
                                                : NULL,
1468
0
      (zip->entry.comp_size >= MZ_UINT32_MAX) ? &zip->entry.comp_size : NULL,
1469
0
      (zip->entry.header_offset >= MZ_UINT32_MAX) ? &zip->entry.header_offset
1470
0
                                                  : NULL);
1471
1472
0
  if ((entrylen) && ISSLASH(zip->entry.name[entrylen - 1]) &&
1473
0
      !zip->entry.uncomp_size) {
1474
    /* Set DOS Subdirectory attribute bit. */
1475
0
    zip->entry.external_attr |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG;
1476
0
  }
1477
1478
0
  if (!mz_zip_writer_add_to_central_dir(
1479
0
          pzip, zip->entry.name, entrylen, pExtra_data, (mz_uint16)extra_size,
1480
0
          "", 0, zip->entry.uncomp_size, zip->entry.comp_size,
1481
0
          zip->entry.uncomp_crc32, zip->entry.method,
1482
0
          MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 |
1483
0
              MZ_ZIP_LDH_BIT_FLAG_HAS_LOCATOR,
1484
0
          dos_time, dos_date, zip->entry.header_offset,
1485
0
          zip->entry.external_attr, NULL, 0)) {
1486
    // Cannot write to zip central dir
1487
0
    err = ZIP_EWRTDIR;
1488
0
    goto cleanup;
1489
0
  }
1490
1491
0
  pzip->m_total_files++;
1492
0
  pzip->m_archive_size = zip->entry.dir_offset;
1493
1494
0
cleanup:
1495
0
  if (zip) {
1496
0
    zip->entry.m_time = 0;
1497
0
    zip->entry.index = -1;
1498
0
    CLEANUP(zip->entry.name);
1499
0
  }
1500
0
  return err;
1501
0
}
1502
1503
0
const char *zip_entry_name(struct zip_t *zip) {
1504
0
  if (!zip) {
1505
    // zip_t handler is not initialized
1506
0
    return NULL;
1507
0
  }
1508
0
  return zip->entry.name;
1509
0
}
1510
1511
0
ssize_t zip_entry_index(struct zip_t *zip) {
1512
0
  if (!zip) {
1513
    // zip_t handler is not initialized
1514
0
    return (ssize_t)ZIP_ENOINIT;
1515
0
  }
1516
1517
0
  return zip->entry.index;
1518
0
}
1519
1520
0
int zip_entry_isdir(struct zip_t *zip) {
1521
0
  mz_uint16 entrylen;
1522
0
  if (!zip) {
1523
    // zip_t handler is not initialized
1524
0
    return ZIP_ENOINIT;
1525
0
  }
1526
1527
0
  if (zip->entry.index < (ssize_t)0) {
1528
    // zip entry is not opened
1529
0
    return ZIP_EINVIDX;
1530
0
  }
1531
1532
0
  if (!zip->entry.name) {
1533
0
    return ZIP_EINVENTNAME;
1534
0
  }
1535
1536
0
  entrylen = (mz_uint16)strlen(zip->entry.name);
1537
0
  if (entrylen == 0) {
1538
0
    return 0;
1539
0
  }
1540
0
  return ISSLASH(zip->entry.name[entrylen - 1]);
1541
0
}
1542
1543
0
unsigned long long zip_entry_size(struct zip_t *zip) {
1544
0
  return zip_entry_uncomp_size(zip);
1545
0
}
1546
1547
0
unsigned long long zip_entry_uncomp_size(struct zip_t *zip) {
1548
0
  return zip ? zip->entry.uncomp_size : 0;
1549
0
}
1550
1551
0
unsigned long long zip_entry_comp_size(struct zip_t *zip) {
1552
0
  return zip ? zip->entry.comp_size : 0;
1553
0
}
1554
1555
0
unsigned int zip_entry_crc32(struct zip_t *zip) {
1556
0
  return zip ? zip->entry.uncomp_crc32 : 0;
1557
0
}
1558
1559
0
unsigned long long zip_entry_dir_offset(struct zip_t *zip) {
1560
0
  return zip ? zip->entry.dir_offset : 0;
1561
0
}
1562
1563
0
unsigned long long zip_entry_header_offset(struct zip_t *zip) {
1564
0
  return zip ? zip->entry.header_offset : 0;
1565
0
}
1566
1567
0
int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) {
1568
0
  mz_uint level;
1569
0
  mz_zip_archive *pzip = NULL;
1570
0
  tdefl_status status;
1571
1572
0
  if (!zip) {
1573
    // zip_t handler is not initialized
1574
0
    return ZIP_ENOINIT;
1575
0
  }
1576
1577
0
  pzip = &(zip->archive);
1578
0
  if (buf && bufsize > 0) {
1579
0
    zip->entry.uncomp_size += bufsize;
1580
0
    zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32(
1581
0
        zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize);
1582
1583
0
    level = zip->level & 0xF;
1584
0
    if (!level) {
1585
0
      if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.dir_offset, buf,
1586
0
                          bufsize) != bufsize)) {
1587
        // Cannot write buffer
1588
0
        return ZIP_EWRTENT;
1589
0
      }
1590
0
      zip->entry.dir_offset += bufsize;
1591
0
      zip->entry.comp_size += bufsize;
1592
0
    } else {
1593
0
      status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize,
1594
0
                                     TDEFL_NO_FLUSH);
1595
0
      if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) {
1596
        // Cannot compress buffer
1597
0
        return ZIP_ETDEFLBUF;
1598
0
      }
1599
0
    }
1600
0
  }
1601
1602
0
  return 0;
1603
0
}
1604
1605
0
int zip_entry_fwrite(struct zip_t *zip, const char *filename) {
1606
0
  int err = 0;
1607
0
  size_t n = 0;
1608
0
  MZ_FILE *stream = NULL;
1609
0
  mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE];
1610
0
  struct MZ_FILE_STAT_STRUCT file_stat;
1611
0
  mz_uint16 modes;
1612
1613
0
  if (!zip) {
1614
    // zip_t handler is not initialized
1615
0
    return ZIP_ENOINIT;
1616
0
  }
1617
1618
0
  memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT));
1619
0
  if (MZ_FILE_STAT(filename, &file_stat) != 0) {
1620
    // problem getting information - check errno
1621
0
    return ZIP_ENOENT;
1622
0
  }
1623
1624
#if defined(_WIN32) || defined(__WIN32__) || defined(DJGPP)
1625
  (void)modes; // unused
1626
#else
1627
  /* Initialize with permission bits--which are not implementation-optional */
1628
0
  modes = file_stat.st_mode &
1629
0
          (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX);
1630
0
  if (S_ISDIR(file_stat.st_mode))
1631
0
    modes |= UNX_IFDIR;
1632
0
  if (S_ISREG(file_stat.st_mode))
1633
0
    modes |= UNX_IFREG;
1634
0
  if (S_ISLNK(file_stat.st_mode))
1635
0
    modes |= UNX_IFLNK;
1636
0
  if (S_ISBLK(file_stat.st_mode))
1637
0
    modes |= UNX_IFBLK;
1638
0
  if (S_ISCHR(file_stat.st_mode))
1639
0
    modes |= UNX_IFCHR;
1640
0
  if (S_ISFIFO(file_stat.st_mode))
1641
0
    modes |= UNX_IFIFO;
1642
0
  if (S_ISSOCK(file_stat.st_mode))
1643
0
    modes |= UNX_IFSOCK;
1644
0
  zip->entry.external_attr = (modes << 16) | !(file_stat.st_mode & S_IWUSR);
1645
0
  if ((file_stat.st_mode & S_IFMT) == S_IFDIR) {
1646
0
    zip->entry.external_attr |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG;
1647
0
  }
1648
0
#endif
1649
1650
0
  zip->entry.m_time = file_stat.st_mtime;
1651
1652
0
  if (!(stream = MZ_FOPEN(filename, "rb"))) {
1653
    // Cannot open filename
1654
0
    return ZIP_EOPNFILE;
1655
0
  }
1656
1657
0
  while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) >
1658
0
         0) {
1659
0
    if (zip_entry_write(zip, buf, n) < 0) {
1660
0
      err = ZIP_EWRTENT;
1661
0
      break;
1662
0
    }
1663
0
  }
1664
0
  fclose(stream);
1665
1666
0
  return err;
1667
0
}
1668
1669
0
ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) {
1670
0
  mz_zip_archive *pzip = NULL;
1671
0
  mz_uint idx;
1672
0
  size_t size = 0;
1673
1674
0
  if (!zip) {
1675
    // zip_t handler is not initialized
1676
0
    return (ssize_t)ZIP_ENOINIT;
1677
0
  }
1678
1679
0
  pzip = &(zip->archive);
1680
0
  if (pzip->m_zip_mode != MZ_ZIP_MODE_READING ||
1681
0
      zip->entry.index < (ssize_t)0) {
1682
    // the entry is not found or we do not have read access
1683
0
    return (ssize_t)ZIP_ENOENT;
1684
0
  }
1685
1686
0
  idx = (mz_uint)zip->entry.index;
1687
0
  if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
1688
    // the entry is a directory
1689
0
    return (ssize_t)ZIP_EINVENTTYPE;
1690
0
  }
1691
1692
0
  *buf = mz_zip_reader_extract_to_heap(pzip, idx, &size, 0);
1693
0
  if (!*buf) {
1694
0
    return (ssize_t)ZIP_EOOMEM;
1695
0
  }
1696
0
  if (bufsize) {
1697
0
    *bufsize = size;
1698
0
  }
1699
0
  return (ssize_t)size;
1700
0
}
1701
1702
0
ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) {
1703
0
  mz_zip_archive *pzip = NULL;
1704
1705
0
  if (!zip) {
1706
    // zip_t handler is not initialized
1707
0
    return (ssize_t)ZIP_ENOINIT;
1708
0
  }
1709
1710
0
  pzip = &(zip->archive);
1711
0
  if (pzip->m_zip_mode != MZ_ZIP_MODE_READING ||
1712
0
      zip->entry.index < (ssize_t)0) {
1713
    // the entry is not found or we do not have read access
1714
0
    return (ssize_t)ZIP_ENOENT;
1715
0
  }
1716
1717
0
  if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index,
1718
0
                                             buf, bufsize, 0, NULL, 0)) {
1719
0
    return (ssize_t)ZIP_EMEMNOALLOC;
1720
0
  }
1721
1722
0
  return (ssize_t)zip->entry.uncomp_size;
1723
0
}
1724
1725
ssize_t zip_entry_noallocreadwithoffset(struct zip_t *zip, size_t offset,
1726
0
                                        size_t size, void *buf) {
1727
0
  mz_zip_archive *pzip = NULL;
1728
1729
0
  if (!zip) {
1730
    // zip_t handler is not initialized
1731
0
    return (ssize_t)ZIP_ENOINIT;
1732
0
  }
1733
1734
0
  if (offset >= (size_t)zip->entry.uncomp_size) {
1735
0
    return (ssize_t)ZIP_EINVAL;
1736
0
  }
1737
1738
0
  if ((offset + size) > (size_t)zip->entry.uncomp_size) {
1739
0
    size = (size_t)(zip->entry.uncomp_size - (mz_uint64)offset);
1740
0
  }
1741
1742
0
  pzip = &(zip->archive);
1743
0
  if (pzip->m_zip_mode != MZ_ZIP_MODE_READING ||
1744
0
      zip->entry.index < (ssize_t)0) {
1745
    // the entry is not found or we do not have read access
1746
0
    return (ssize_t)ZIP_ENOENT;
1747
0
  }
1748
1749
0
  mz_zip_reader_extract_iter_state *iter =
1750
0
      mz_zip_reader_extract_iter_new(pzip, (mz_uint)zip->entry.index, 0);
1751
0
  if (!iter) {
1752
0
    return (ssize_t)ZIP_ENORITER;
1753
0
  }
1754
1755
0
  mz_uint8 *writebuf = (mz_uint8 *)buf;
1756
0
  size_t file_offset = 0;
1757
0
  size_t write_cursor = 0;
1758
0
  size_t to_read = size;
1759
1760
  // iterate until the requested offset is in range
1761
0
  while (file_offset < zip->entry.uncomp_size && to_read > 0) {
1762
0
    size_t nread = mz_zip_reader_extract_iter_read(
1763
0
        iter, (void *)&writebuf[write_cursor], to_read);
1764
1765
0
    if (nread == 0)
1766
0
      break;
1767
1768
0
    if (offset < (file_offset + nread)) {
1769
0
      size_t read_cursor = offset - file_offset;
1770
0
      MZ_ASSERT(read_cursor < size);
1771
0
      size_t read_size = nread - read_cursor;
1772
1773
0
      if (to_read < read_size)
1774
0
        read_size = to_read;
1775
0
      MZ_ASSERT(read_size <= size);
1776
1777
      // If it's an unaligned read (i.e. the first one)
1778
0
      if (read_cursor != 0) {
1779
0
        memmove(&writebuf[write_cursor], &writebuf[read_cursor], read_size);
1780
0
      }
1781
1782
0
      write_cursor += read_size;
1783
0
      offset += read_size;
1784
0
      to_read -= read_size;
1785
0
    }
1786
1787
0
    file_offset += nread;
1788
0
  }
1789
1790
0
  mz_zip_reader_extract_iter_free(iter);
1791
0
  return (ssize_t)write_cursor;
1792
0
}
1793
1794
0
int zip_entry_fread(struct zip_t *zip, const char *filename) {
1795
0
  mz_zip_archive *pzip = NULL;
1796
0
  mz_uint idx;
1797
0
  mz_uint32 xattr = 0;
1798
0
  mz_zip_archive_file_stat info;
1799
1800
0
  if (!zip) {
1801
    // zip_t handler is not initialized
1802
0
    return ZIP_ENOINIT;
1803
0
  }
1804
1805
0
  memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat));
1806
0
  pzip = &(zip->archive);
1807
0
  if (pzip->m_zip_mode != MZ_ZIP_MODE_READING ||
1808
0
      zip->entry.index < (ssize_t)0) {
1809
    // the entry is not found or we do not have read access
1810
0
    return ZIP_ENOENT;
1811
0
  }
1812
1813
0
  idx = (mz_uint)zip->entry.index;
1814
0
  if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
1815
    // the entry is a directory
1816
0
    return ZIP_EINVENTTYPE;
1817
0
  }
1818
1819
0
  if (!mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) {
1820
0
    return ZIP_ENOFILE;
1821
0
  }
1822
1823
#if defined(_MSC_VER) || defined(PS4)
1824
  (void)xattr; // unused
1825
#else
1826
0
  if (!mz_zip_reader_file_stat(pzip, idx, &info)) {
1827
    // Cannot get information about zip archive;
1828
0
    return ZIP_ENOFILE;
1829
0
  }
1830
1831
0
  xattr = (info.m_external_attr >> 16) & 0xFFFF;
1832
0
  if (xattr > 0 && xattr <= MZ_UINT16_MAX) {
1833
0
    if (CHMOD(filename, (mode_t)xattr) < 0) {
1834
0
      return ZIP_ENOPERM;
1835
0
    }
1836
0
  }
1837
0
#endif
1838
1839
0
  return 0;
1840
0
}
1841
1842
int zip_entry_extract(struct zip_t *zip,
1843
                      size_t (*on_extract)(void *arg, uint64_t offset,
1844
                                           const void *buf, size_t bufsize),
1845
0
                      void *arg) {
1846
0
  mz_zip_archive *pzip = NULL;
1847
0
  mz_uint idx;
1848
1849
0
  if (!zip) {
1850
    // zip_t handler is not initialized
1851
0
    return ZIP_ENOINIT;
1852
0
  }
1853
1854
0
  pzip = &(zip->archive);
1855
0
  if (pzip->m_zip_mode != MZ_ZIP_MODE_READING ||
1856
0
      zip->entry.index < (ssize_t)0) {
1857
    // the entry is not found or we do not have read access
1858
0
    return ZIP_ENOENT;
1859
0
  }
1860
1861
0
  idx = (mz_uint)zip->entry.index;
1862
0
  return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0))
1863
0
             ? 0
1864
0
             : ZIP_EINVIDX;
1865
0
}
1866
1867
0
ssize_t zip_entries_total(struct zip_t *zip) {
1868
0
  if (!zip) {
1869
    // zip_t handler is not initialized
1870
0
    return ZIP_ENOINIT;
1871
0
  }
1872
1873
0
  return (ssize_t)zip->archive.m_total_files;
1874
0
}
1875
1876
ssize_t zip_entries_delete(struct zip_t *zip, char *const entries[],
1877
0
                           size_t len) {
1878
0
  ssize_t n = 0;
1879
0
  ssize_t err = 0;
1880
0
  struct zip_entry_mark_t *entry_mark = NULL;
1881
1882
0
  if (zip == NULL || (entries == NULL && len != 0)) {
1883
0
    return ZIP_ENOINIT;
1884
0
  }
1885
1886
0
  if (entries == NULL && len == 0) {
1887
0
    return 0;
1888
0
  }
1889
1890
0
  n = zip_entries_total(zip);
1891
0
  if (n < 0) {
1892
0
    return n;
1893
0
  }
1894
1895
0
  entry_mark = (struct zip_entry_mark_t *)calloc(
1896
0
      (size_t)n, sizeof(struct zip_entry_mark_t));
1897
0
  if (!entry_mark) {
1898
0
    return ZIP_EOOMEM;
1899
0
  }
1900
1901
0
  zip->archive.m_zip_mode = MZ_ZIP_MODE_READING;
1902
1903
0
  err = zip_entry_set(zip, entry_mark, (size_t)n, entries, len);
1904
0
  if (err < 0) {
1905
0
    CLEANUP(entry_mark);
1906
0
    return err;
1907
0
  }
1908
1909
0
  err = zip_entries_delete_mark(zip, entry_mark, (int)n);
1910
0
  CLEANUP(entry_mark);
1911
0
  return err;
1912
0
}
1913
1914
ssize_t zip_entries_deletebyindex(struct zip_t *zip, size_t entries[],
1915
0
                                  size_t len) {
1916
0
  ssize_t n = 0;
1917
0
  ssize_t err = 0;
1918
0
  struct zip_entry_mark_t *entry_mark = NULL;
1919
1920
0
  if (zip == NULL || (entries == NULL && len != 0)) {
1921
0
    return ZIP_ENOINIT;
1922
0
  }
1923
1924
0
  if (entries == NULL && len == 0) {
1925
0
    return 0;
1926
0
  }
1927
1928
0
  n = zip_entries_total(zip);
1929
0
  if (n < 0) {
1930
0
    return n;
1931
0
  }
1932
1933
0
  entry_mark = (struct zip_entry_mark_t *)calloc(
1934
0
      (size_t)n, sizeof(struct zip_entry_mark_t));
1935
0
  if (!entry_mark) {
1936
0
    return ZIP_EOOMEM;
1937
0
  }
1938
1939
0
  zip->archive.m_zip_mode = MZ_ZIP_MODE_READING;
1940
1941
0
  err = zip_entry_setbyindex(zip, entry_mark, (size_t)n, entries, len);
1942
0
  if (err < 0) {
1943
0
    CLEANUP(entry_mark);
1944
0
    return err;
1945
0
  }
1946
1947
0
  err = zip_entries_delete_mark(zip, entry_mark, (int)n);
1948
0
  CLEANUP(entry_mark);
1949
0
  return err;
1950
0
}
1951
1952
int zip_stream_extract(const char *stream, size_t size, const char *dir,
1953
                       int (*on_extract)(const char *filename, void *arg),
1954
0
                       void *arg) {
1955
0
  mz_zip_archive zip_archive;
1956
0
  if (!stream || !dir) {
1957
    // Cannot parse zip archive stream
1958
0
    return ZIP_ENOINIT;
1959
0
  }
1960
0
  memset(&zip_archive, 0, sizeof(mz_zip_archive));
1961
0
  if (!mz_zip_reader_init_mem(&zip_archive, stream, size, 0)) {
1962
    // Cannot initialize zip_archive reader
1963
0
    return ZIP_ENOINIT;
1964
0
  }
1965
1966
0
  return zip_archive_extract(&zip_archive, dir, on_extract, arg);
1967
0
}
1968
1969
struct zip_t *zip_stream_open(const char *stream, size_t size, int level,
1970
0
                              char mode) {
1971
0
  int errnum = 0;
1972
0
  return zip_stream_openwitherror(stream, size, level, mode, &errnum);
1973
0
}
1974
1975
struct zip_t *zip_stream_openwitherror(const char *stream, size_t size,
1976
0
                                       int level, char mode, int *errnum) {
1977
0
  mz_uint wflags = (mode == 'w') ? MZ_ZIP_FLAG_WRITE_ZIP64 : 0;
1978
0
  struct zip_t *zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
1979
0
  if (!zip) {
1980
    // out of memory
1981
0
    *errnum = ZIP_EOOMEM;
1982
0
    return NULL;
1983
0
  }
1984
1985
0
  zip->entry.index = -1;
1986
1987
0
  if (level < 0) {
1988
0
    level = MZ_DEFAULT_LEVEL;
1989
0
  }
1990
0
  if ((level & 0xF) > MZ_UBER_COMPRESSION) {
1991
    // Wrong compression level
1992
0
    *errnum = ZIP_EINVLVL;
1993
0
    goto cleanup;
1994
0
  }
1995
0
  zip->level = (mz_uint)level;
1996
1997
0
  switch (mode) {
1998
0
  case 'r':
1999
0
  case ('r' - 64): {
2000
0
    if (stream && size > 0) {
2001
0
      if (!mz_zip_reader_init_mem(&(zip->archive), stream, size, 0)) {
2002
0
        *errnum = ZIP_ERINIT;
2003
0
        goto cleanup;
2004
0
      }
2005
0
    } else {
2006
0
      *errnum = ZIP_EINVMODE;
2007
0
      goto cleanup;
2008
0
    }
2009
0
  } break;
2010
2011
0
  case 'd':
2012
0
  case ('d' - 64): {
2013
0
    if (stream && size > 0) {
2014
0
      if (!mz_zip_reader_init_mem(
2015
0
              &(zip->archive), stream, size,
2016
0
              zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) {
2017
0
        *errnum = ZIP_ERINIT;
2018
0
        goto cleanup;
2019
0
      }
2020
0
      if (!mz_zip_writer_init_from_reader_v2(&(zip->archive), NULL, 0)) {
2021
0
        *errnum = ZIP_EWRINIT;
2022
0
        mz_zip_reader_end(&(zip->archive));
2023
0
        goto cleanup;
2024
0
      }
2025
0
      zip->archive.m_pWrite = zip_stream_delete_write_func;
2026
0
    } else {
2027
0
      *errnum = ZIP_EINVMODE;
2028
0
      goto cleanup;
2029
0
    }
2030
0
  } break;
2031
2032
0
  case 'w':
2033
0
  case ('w' - 64): {
2034
0
    if (stream == NULL && size == 0) {
2035
0
      if (!mz_zip_writer_init_heap_v2(&(zip->archive), 0, 1024, wflags)) {
2036
0
        *errnum = ZIP_EWINIT;
2037
0
        goto cleanup;
2038
0
      }
2039
0
    } else {
2040
0
      *errnum = ZIP_EINVMODE;
2041
0
      goto cleanup;
2042
0
    }
2043
0
  } break;
2044
2045
0
  default:
2046
0
    *errnum = ZIP_EINVMODE;
2047
0
    goto cleanup;
2048
0
  }
2049
2050
0
  *errnum = 0;
2051
0
  return zip;
2052
2053
0
cleanup:
2054
0
  CLEANUP(zip);
2055
0
  return NULL;
2056
0
}
2057
2058
0
ssize_t zip_stream_copy(struct zip_t *zip, void **buf, size_t *bufsize) {
2059
0
  size_t n;
2060
2061
0
  if (!zip || !buf) {
2062
0
    return (ssize_t)ZIP_ENOINIT;
2063
0
  }
2064
2065
0
  if (zip->archive.m_zip_mode == MZ_ZIP_MODE_WRITING) {
2066
0
    zip_archive_finalize(&(zip->archive));
2067
0
  }
2068
2069
0
  if (!zip->archive.m_pState || !zip->archive.m_pState->m_pMem) {
2070
0
    return (ssize_t)ZIP_ENOINIT;
2071
0
  }
2072
2073
0
  n = (size_t)zip->archive.m_archive_size;
2074
0
  if (bufsize != NULL) {
2075
0
    *bufsize = n;
2076
0
  }
2077
2078
0
  *buf = calloc(n, sizeof(unsigned char));
2079
0
  if (!*buf) {
2080
0
    return (ssize_t)ZIP_EOOMEM;
2081
0
  }
2082
0
  memcpy(*buf, zip->archive.m_pState->m_pMem, n);
2083
2084
0
  return (ssize_t)n;
2085
0
}
2086
2087
0
void zip_stream_close(struct zip_t *zip) {
2088
0
  if (zip) {
2089
0
    mz_zip_writer_end(&(zip->archive));
2090
0
    mz_zip_reader_end(&(zip->archive));
2091
0
    CLEANUP(zip);
2092
0
  }
2093
0
}
2094
2095
0
struct zip_t *zip_cstream_open(FILE *stream, int level, char mode) {
2096
0
  int errnum = 0;
2097
0
  return zip_cstream_openwitherror(stream, level, mode, &errnum);
2098
0
}
2099
2100
struct zip_t *zip_cstream_openwitherror(FILE *stream, int level, char mode,
2101
0
                                        int *errnum) {
2102
0
  mz_uint wflags = (mode == 'w') ? MZ_ZIP_FLAG_WRITE_ZIP64 : 0;
2103
0
  struct zip_t *zip = NULL;
2104
0
  *errnum = 0;
2105
0
  if (!stream) {
2106
    // zip archive stream is NULL
2107
0
    *errnum = ZIP_ENOFILE;
2108
0
    goto cleanup;
2109
0
  }
2110
2111
0
  if (level < 0)
2112
0
    level = MZ_DEFAULT_LEVEL;
2113
0
  if ((level & 0xF) > MZ_UBER_COMPRESSION) {
2114
    // Wrong compression level
2115
0
    *errnum = ZIP_EINVLVL;
2116
0
    goto cleanup;
2117
0
  }
2118
2119
0
  zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
2120
0
  if (!zip) {
2121
    // out of memory
2122
0
    *errnum = ZIP_EOOMEM;
2123
0
    goto cleanup;
2124
0
  }
2125
2126
0
  zip->level = (mz_uint)level;
2127
0
  zip->entry.index = -1;
2128
0
  switch (mode) {
2129
0
  case 'w':
2130
0
  case ('w' - 64): {
2131
    // Create a new archive.
2132
0
    if (!mz_zip_writer_init_cfile(&(zip->archive), stream, wflags)) {
2133
      // Cannot initialize zip_archive writer
2134
0
      *errnum = ZIP_EWINIT;
2135
0
      goto cleanup;
2136
0
    }
2137
0
  } break;
2138
2139
0
  case ('r' - 64):
2140
0
  case 'r':
2141
0
    if (!mz_zip_reader_init_cfile(
2142
0
            &(zip->archive), stream, 0,
2143
0
            zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) {
2144
      // An archive file does not exist or cannot initialize
2145
      // zip_archive reader
2146
0
      *errnum = ZIP_ERINIT;
2147
0
      goto cleanup;
2148
0
    }
2149
0
    break;
2150
2151
0
  case ('a' - 64):
2152
0
  case ('d' - 64):
2153
0
  case 'a':
2154
0
  case 'd': {
2155
0
    if (!mz_zip_reader_init_cfile(
2156
0
            &(zip->archive), stream, 0,
2157
0
            zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) {
2158
      // An archive file does not exist or cannot initialize
2159
      // zip_archive reader
2160
0
      *errnum = ZIP_ERINIT;
2161
0
      goto cleanup;
2162
0
    }
2163
0
    if (!mz_zip_writer_init_from_reader_v2(&(zip->archive), NULL, 0)) {
2164
0
      *errnum = ZIP_EWRINIT;
2165
0
      mz_zip_reader_end(&(zip->archive));
2166
0
      goto cleanup;
2167
0
    }
2168
0
  } break;
2169
2170
0
  default:
2171
0
    *errnum = ZIP_EINVMODE;
2172
0
    goto cleanup;
2173
0
  }
2174
2175
0
  return zip;
2176
2177
0
cleanup:
2178
0
  CLEANUP(zip);
2179
0
  return NULL;
2180
0
}
2181
2182
0
void zip_cstream_close(struct zip_t *zip) { zip_close(zip); }
2183
2184
0
int zip_create(const char *zipname, const char *filenames[], size_t len) {
2185
0
  int err = 0;
2186
0
  size_t i;
2187
0
  int open_err = 0;
2188
0
  struct zip_t *zip;
2189
2190
0
  if (!zipname || !*zipname) {
2191
0
    return ZIP_EINVZIPNAME;
2192
0
  }
2193
2194
0
  zip =
2195
0
      zip_openwitherror(zipname, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w', &open_err);
2196
0
  if (!zip) {
2197
0
    return open_err;
2198
0
  }
2199
0
  for (i = 0; i < len; ++i) {
2200
0
    const char *name = filenames[i];
2201
0
    if (!name) {
2202
0
      err = ZIP_EINVENTNAME;
2203
0
      break;
2204
0
    }
2205
2206
0
    err = zip_entry_open(zip, zip_basename(name));
2207
0
    if (err) {
2208
0
      break;
2209
0
    }
2210
2211
0
    err = zip_entry_fwrite(zip, name);
2212
0
    if (err) {
2213
0
      zip_entry_close(zip);
2214
0
      break;
2215
0
    }
2216
2217
0
    err = zip_entry_close(zip);
2218
0
    if (err) {
2219
0
      break;
2220
0
    }
2221
0
  }
2222
2223
0
  zip_close(zip);
2224
0
  return err;
2225
0
}
2226
2227
int zip_extract(const char *zipname, const char *dir,
2228
0
                int (*on_extract)(const char *filename, void *arg), void *arg) {
2229
0
  mz_zip_archive zip_archive;
2230
2231
0
  if (!zipname || !dir) {
2232
    // Cannot parse zip archive name
2233
0
    return ZIP_EINVZIPNAME;
2234
0
  }
2235
2236
0
  memset(&zip_archive, 0, sizeof(mz_zip_archive));
2237
2238
  // Now try to open the archive.
2239
0
  if (!mz_zip_reader_init_file_v2(&zip_archive, zipname, 0, 0, 0)) {
2240
    // Cannot initialize zip_archive reader
2241
0
    return ZIP_ENOINIT;
2242
0
  }
2243
2244
0
  return zip_archive_extract(&zip_archive, dir, on_extract, arg);
2245
0
}