Coverage Report

Created: 2026-08-11 07:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libzip/lib/zip_open.c
Line
Count
Source
1
/*
2
  zip_open.c -- open zip archive by name
3
  Copyright (C) 1999-2024 Dieter Baron and Thomas Klausner
4
5
  This file is part of libzip, a library to manipulate ZIP archives.
6
  The authors can be contacted at <info@libzip.org>
7
8
  Redistribution and use in source and binary forms, with or without
9
  modification, are permitted provided that the following conditions
10
  are met:
11
  1. Redistributions of source code must retain the above copyright
12
     notice, this list of conditions and the following disclaimer.
13
  2. Redistributions in binary form must reproduce the above copyright
14
     notice, this list of conditions and the following disclaimer in
15
     the documentation and/or other materials provided with the
16
     distribution.
17
  3. The names of the authors may not be used to endorse or promote
18
     products derived from this software without specific prior
19
     written permission.
20
21
  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
22
  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24
  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26
  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27
  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29
  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30
  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
31
  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
*/
33
34
#include <limits.h>
35
#include <stdio.h>
36
#include <stdlib.h>
37
#include <string.h>
38
39
#include "zipint.h"
40
41
typedef enum {
42
    EXISTS_ERROR = -1,
43
    EXISTS_NOT = 0,
44
    EXISTS_OK
45
} exists_t;
46
47
typedef enum {
48
    CDIR_OK,
49
    CDIR_INVALID,
50
    CDIR_NOT_FOUND
51
} cdir_status_t;
52
53
static bool check_eocd(zip_cdir_t *cd, unsigned int flags, zip_error_t *error);
54
static bool check_magic(zip_uint64_t offset, zip_buffer_t *buffer, zip_uint64_t buffer_offset, zip_source_t *src, const char *magic);
55
static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error);
56
static zip_int64_t _zip_checkcons(zip_t *za, zip_cdir_t *cdir, zip_error_t *error);
57
static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir);
58
static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len);
59
static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error);
60
static int _zip_headercomp(const zip_dirent_t *, const zip_dirent_t *);
61
static bool _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_cdir_t **cdirp, zip_error_t *error);
62
static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error);
63
static cdir_status_t _zip_read_eocd64(zip_cdir_t *cdir, zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error);
64
static const unsigned char *find_eocd(zip_buffer_t *buffer, const unsigned char *last);
65
66
67
6.61k
ZIP_EXTERN zip_t *zip_open(const char *fn, int _flags, int *zep) {
68
6.61k
    zip_t *za;
69
6.61k
    zip_source_t *src;
70
6.61k
    struct zip_error error;
71
72
6.61k
    zip_error_init(&error);
73
6.61k
    if ((src = zip_source_file_create(fn, 0, -1, &error)) == NULL) {
74
0
        _zip_set_open_error(zep, &error, 0);
75
0
        zip_error_fini(&error);
76
0
        return NULL;
77
0
    }
78
79
6.61k
    if ((za = zip_open_from_source(src, _flags, &error)) == NULL) {
80
2.45k
        zip_source_free(src);
81
2.45k
        _zip_set_open_error(zep, &error, 0);
82
2.45k
        zip_error_fini(&error);
83
2.45k
        return NULL;
84
2.45k
    }
85
86
4.16k
    zip_error_fini(&error);
87
4.16k
    return za;
88
6.61k
}
89
90
91
6.61k
ZIP_EXTERN zip_t *zip_open_from_source(zip_source_t *src, int _flags, zip_error_t *error) {
92
6.61k
    unsigned int flags;
93
6.61k
    zip_int64_t supported;
94
6.61k
    exists_t exists;
95
96
6.61k
    if (_flags < 0 || src == NULL) {
97
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
98
0
        return NULL;
99
0
    }
100
6.61k
    flags = (unsigned int)_flags;
101
102
6.61k
    supported = zip_source_supports(src);
103
6.61k
    if ((supported & ZIP_SOURCE_SUPPORTS_SEEKABLE) != ZIP_SOURCE_SUPPORTS_SEEKABLE) {
104
0
        zip_error_set(error, ZIP_ER_OPNOTSUPP, 0);
105
0
        return NULL;
106
0
    }
107
6.61k
    if ((supported & ZIP_SOURCE_SUPPORTS_WRITABLE) != ZIP_SOURCE_SUPPORTS_WRITABLE) {
108
0
        flags |= ZIP_RDONLY;
109
0
    }
110
111
6.61k
    if ((flags & (ZIP_RDONLY | ZIP_TRUNCATE)) == (ZIP_RDONLY | ZIP_TRUNCATE)) {
112
0
        zip_error_set(error, ZIP_ER_RDONLY, 0);
113
0
        return NULL;
114
0
    }
115
116
6.61k
    exists = _zip_file_exists(src, error);
117
6.61k
    switch (exists) {
118
0
    case EXISTS_ERROR:
119
0
        return NULL;
120
121
0
    case EXISTS_NOT:
122
0
        if ((flags & ZIP_CREATE) == 0) {
123
0
            zip_error_set(error, ZIP_ER_NOENT, 0);
124
0
            return NULL;
125
0
        }
126
0
        return _zip_allocate_new(src, flags, error);
127
128
6.61k
    default: {
129
6.61k
        zip_t *za;
130
6.61k
        if (flags & ZIP_EXCL) {
131
0
            zip_error_set(error, ZIP_ER_EXISTS, 0);
132
0
            return NULL;
133
0
        }
134
6.61k
        if (zip_source_open(src) < 0) {
135
0
            zip_error_set_from_source(error, src);
136
0
            return NULL;
137
0
        }
138
139
6.61k
        if (flags & ZIP_TRUNCATE) {
140
0
            za = _zip_allocate_new(src, flags, error);
141
0
        }
142
6.61k
        else {
143
            /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, just like open() */
144
6.61k
            za = _zip_open(src, flags, error);
145
6.61k
        }
146
147
6.61k
        if (za == NULL) {
148
2.45k
            zip_source_close(src);
149
2.45k
            return NULL;
150
2.45k
        }
151
4.16k
        return za;
152
6.61k
    }
153
6.61k
    }
154
6.61k
}
155
156
157
1.41k
static bool _is_truncated_zip(zip_source_t *src) {
158
1.41k
    unsigned char data[4];
159
    /* check if the source is a truncated zip archive: true if yes, no
160
       if not or can't be determined */
161
1.41k
    if (zip_source_seek(src, 0, SEEK_SET) < 0) {
162
0
        return false;
163
0
    }
164
165
1.41k
    if (zip_source_read(src, data, 4) != 4) {
166
96
        return false;
167
96
    }
168
169
1.32k
    if (memcmp(data, LOCAL_MAGIC, 4) == 0) {
170
        /* file starts with a ZIP local header signature */
171
194
        return true;
172
194
    }
173
1.12k
    return false;
174
1.32k
}
175
176
177
6.61k
zip_t *_zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) {
178
6.61k
    zip_t *za;
179
6.61k
    zip_cdir_t *cdir;
180
6.61k
    struct zip_stat st;
181
6.61k
    zip_uint64_t len, idx;
182
183
6.61k
    zip_stat_init(&st);
184
6.61k
    if (zip_source_stat(src, &st) < 0) {
185
0
        zip_error_set_from_source(error, src);
186
0
        return NULL;
187
0
    }
188
6.61k
    if ((st.valid & ZIP_STAT_SIZE) == 0) {
189
0
        zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP);
190
0
        return NULL;
191
0
    }
192
6.61k
    len = st.size;
193
194
195
6.61k
    if ((za = _zip_allocate_new(src, flags, error)) == NULL) {
196
0
        return NULL;
197
0
    }
198
199
    /* treat empty files as empty archives */
200
6.61k
    if (len == 0 && zip_source_accept_empty(src)) {
201
0
        return za;
202
0
    }
203
204
6.61k
    if ((cdir = _zip_find_central_dir(za, len)) == NULL) {
205
2.45k
        _zip_error_copy(error, &za->error);
206
2.45k
        if (zip_error_code_zip(&za->error) == ZIP_ER_NOZIP) {
207
            /* not a zip - find out if it's truncated */
208
1.41k
            if (_is_truncated_zip(src)) {
209
194
                zip_error_set(error, ZIP_ER_TRUNCATED_ZIP, 0);
210
194
            }
211
1.41k
        }
212
        /* keep src so discard does not get rid of it */
213
2.45k
        zip_source_keep(src);
214
2.45k
        zip_discard(za);
215
2.45k
        return NULL;
216
2.45k
    }
217
218
4.16k
    za->entry = cdir->entry;
219
4.16k
    za->nentry = cdir->nentry;
220
4.16k
    za->nentry_alloc = cdir->nentry_alloc;
221
222
4.16k
    zip_check_torrentzip(za, cdir);
223
224
4.16k
    if (ZIP_IS_TORRENTZIP(za)) {
225
        /* Torrentzip uses the archive comment to detect changes by tools that are not torrentzip aware. */
226
0
        _zip_string_free(cdir->comment);
227
0
    }
228
4.16k
    else {
229
4.16k
        za->comment_orig = cdir->comment;
230
4.16k
    }
231
232
4.16k
    free(cdir);
233
234
4.16k
    _zip_hash_reserve_capacity(za->names, za->nentry, &za->error);
235
236
29.4k
    for (idx = 0; idx < za->nentry; idx++) {
237
25.3k
        const zip_uint8_t *name = _zip_string_get(za->entry[idx].orig->filename, NULL, 0, error);
238
25.3k
        if (name == NULL) {
239
            /* keep src so discard does not get rid of it */
240
0
            zip_source_keep(src);
241
0
            zip_discard(za);
242
0
            return NULL;
243
0
        }
244
245
25.3k
        if (_zip_hash_add(za->names, name, idx, ZIP_FL_UNCHANGED, &za->error) == false) {
246
14
            if (za->error.zip_err != ZIP_ER_EXISTS || (flags & ZIP_CHECKCONS)) {
247
0
                _zip_error_copy(error, &za->error);
248
                /* keep src so discard does not get rid of it */
249
0
                zip_source_keep(src);
250
0
                zip_discard(za);
251
0
                return NULL;
252
0
            }
253
14
        }
254
25.3k
    }
255
256
4.16k
    za->ch_flags = za->flags;
257
258
4.16k
    return za;
259
4.16k
}
260
261
262
2.45k
void _zip_set_open_error(int *zep, const zip_error_t *err, int ze) {
263
2.45k
    if (err) {
264
2.45k
        ze = zip_error_code_zip(err);
265
2.45k
        switch (zip_error_system_type(err)) {
266
0
        case ZIP_ET_SYS:
267
927
        case ZIP_ET_LIBZIP:
268
927
            errno = zip_error_code_system(err);
269
927
            break;
270
271
1.52k
        default:
272
1.52k
            break;
273
2.45k
        }
274
2.45k
    }
275
276
2.45k
    if (zep) {
277
2.45k
        *zep = ze;
278
2.45k
    }
279
2.45k
}
280
281
282
/* _zip_readcdir:
283
   tries to find a valid end-of-central-directory at the beginning of
284
   buf, and then the corresponding central directory entries.
285
   Returns a struct zip_cdir which contains the central directory
286
   entries, or NULL if unsuccessful. */
287
288
45.7k
static bool _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_cdir_t **cdirp, zip_error_t *error) {
289
45.7k
    zip_cdir_t *cd;
290
45.7k
    zip_uint16_t comment_len;
291
45.7k
    zip_uint64_t i, left;
292
45.7k
    zip_uint64_t eocd_offset = _zip_buffer_offset(buffer);
293
45.7k
    zip_buffer_t *cd_buffer;
294
45.7k
    bool eocd64_found = false;
295
296
45.7k
    *cdirp = NULL;
297
298
45.7k
    if ((cd = _zip_read_eocd(buffer, buf_offset, error)) == NULL) {
299
162
        return false;
300
162
    }
301
302
45.5k
    if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) {
303
1.25k
        eocd64_found = true;
304
1.25k
        _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN);
305
1.25k
        switch (_zip_read_eocd64(cd, za->src, buffer, buf_offset, za->open_flags, error)) {
306
0
        case CDIR_OK:
307
0
            break;
308
309
62
        case CDIR_INVALID:
310
62
            _zip_cdir_free(cd);
311
62
            return true;
312
313
1.19k
        case CDIR_NOT_FOUND:
314
1.19k
            _zip_cdir_free(cd);
315
1.19k
            return false;
316
1.25k
        }
317
1.25k
    }
318
319
44.3k
    if ((cd->eocd_disk != 0 || cd->this_disk != 0) && !eocd64_found && cd->eocd_disk != cd->this_disk) {
320
        /* If the central directory doesn't start on this disk, we can't check that offset is valid. Check as much as we can instead. */
321
10.4k
        if (cd->this_disk < cd->eocd_disk) {
322
            /* Disks before the start of the central directory don't contain an EOCD. */
323
4.27k
            _zip_cdir_free(cd);
324
4.27k
            return false;
325
4.27k
        }
326
6.20k
        if (cd->size <= cd->eocd_offset) {
327
            /* The complete central directory would fit on this disk. */
328
770
            _zip_cdir_free(cd);
329
770
            return false;
330
770
        }
331
6.20k
    }
332
333
39.2k
    if (!eocd64_found) {
334
39.2k
        if (cd->this_disk == 0 && cd->eocd_disk == 0 && cd->eocd_offset == 0 && cd->offset == 0 && cd->num_entries == 0) {
335
            /* An empty archive doesn't contain central directory entries. */
336
162
        }
337
39.1k
        else if (!check_magic(cd->offset, buffer, buf_offset, za->src, CENTRAL_MAGIC)) {
338
34.1k
            _zip_cdir_free(cd);
339
34.1k
            return false;
340
34.1k
        }
341
39.2k
    }
342
343
    /* We accept this EOCD as valid and won't search for an earlier one if it is unusable. */
344
345
5.16k
    if (!check_eocd(cd, za->flags, error)) {
346
67
        _zip_cdir_free(cd);
347
67
        return true;
348
67
    }
349
350
5.09k
    _zip_buffer_set_offset(buffer, eocd_offset + 20);
351
5.09k
    comment_len = _zip_buffer_get_16(buffer);
352
353
5.09k
    if (cd->offset + cd->size > buf_offset + eocd_offset) {
354
        /* cdir spans past EOCD record */
355
80
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
356
80
        _zip_cdir_free(cd);
357
80
        return true;
358
80
    }
359
360
5.01k
    if (comment_len || (za->open_flags & ZIP_CHECKCONS)) {
361
1.31k
        zip_uint64_t tail_len;
362
363
1.31k
        _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN);
364
1.31k
        tail_len = _zip_buffer_left(buffer);
365
366
1.31k
        if (tail_len != comment_len) {
367
1.29k
            if (za->open_flags & ZIP_CHECKCONS) {
368
0
                zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_COMMENT_LENGTH_INVALID);
369
0
                _zip_cdir_free(cd);
370
0
                return true;
371
0
            }
372
1.29k
            if (tail_len < comment_len) {
373
1.16k
                comment_len = tail_len;
374
1.16k
            }
375
1.29k
        }
376
377
1.31k
        if (comment_len) {
378
1.07k
            if ((cd->comment = _zip_string_new(_zip_buffer_get(buffer, comment_len), comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) {
379
0
                _zip_cdir_free(cd);
380
0
                return true;
381
0
            }
382
1.07k
        }
383
1.31k
    }
384
385
5.01k
    if (cd->offset >= buf_offset) {
386
4.78k
        zip_uint8_t *data;
387
        /* if buffer already read in, use it */
388
4.78k
        _zip_buffer_set_offset(buffer, cd->offset - buf_offset);
389
390
4.78k
        if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) {
391
0
            zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID);
392
0
            _zip_cdir_free(cd);
393
0
            return true;
394
0
        }
395
4.78k
        if ((cd_buffer = _zip_buffer_new(data, cd->size)) == NULL) {
396
0
            zip_error_set(error, ZIP_ER_MEMORY, 0);
397
0
            _zip_cdir_free(cd);
398
0
            return true;
399
0
        }
400
4.78k
    }
401
230
    else {
402
230
        cd_buffer = NULL;
403
404
230
        if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) {
405
0
            zip_error_set_from_source(error, za->src);
406
0
            _zip_cdir_free(cd);
407
0
            return true;
408
0
        }
409
410
        /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */
411
230
        if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) {
412
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
413
0
            _zip_cdir_free(cd);
414
0
            return true;
415
0
        }
416
230
    }
417
418
5.01k
    if (!_zip_cdir_grow(cd, cd->num_entries, error)) {
419
0
        _zip_cdir_free(cd);
420
0
        _zip_buffer_free(cd_buffer);
421
0
        return true;
422
0
    }
423
5.01k
    left = (zip_uint64_t)cd->size;
424
5.01k
    i = 0;
425
32.1k
    while (left > 0) {
426
27.9k
        bool grown = false;
427
27.9k
        zip_int64_t entry_size;
428
429
27.9k
        if (i == cd->nentry) {
430
            /* InfoZIP has a hack to avoid using Zip64: it stores nentries % 0x10000 */
431
            /* This hack isn't applicable if we're using Zip64, or if there is no central directory entry following. */
432
433
279
            if (cd->is_zip64 || left < CDENTRYSIZE) {
434
146
                break;
435
146
            }
436
437
133
            if (!_zip_cdir_grow(cd, 0x10000, error)) {
438
0
                _zip_cdir_free(cd);
439
0
                _zip_buffer_free(cd_buffer);
440
0
                return true;
441
0
            }
442
133
            grown = true;
443
133
        }
444
445
27.8k
        if ((cd->entry[i].orig = _zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, 0, za->open_flags & ZIP_CHECKCONS, error)) < 0) {
446
684
            if (zip_error_code_zip(error) == ZIP_ER_INCONS) {
447
524
                zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i));
448
524
            }
449
160
            else if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) {
450
36
                zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_CDIR_ENTRY_INVALID, i));
451
36
            }
452
684
            _zip_cdir_free(cd);
453
684
            _zip_buffer_free(cd_buffer);
454
684
            return true;
455
684
        }
456
27.1k
        i++;
457
27.1k
        left -= (zip_uint64_t)entry_size;
458
27.1k
    }
459
460
    /* If we didn't fill all we grew, cd->num_entries was wrong. */
461
4.33k
    if (i != cd->nentry || left > 0) {
462
165
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_WRONG_ENTRIES_COUNT);
463
165
        _zip_buffer_free(cd_buffer);
464
165
        _zip_cdir_free(cd);
465
165
        return true;
466
165
    }
467
468
4.16k
    if (za->open_flags & ZIP_CHECKCONS) {
469
0
        bool ok;
470
471
0
        if (cd_buffer) {
472
0
            ok = _zip_buffer_eof(cd_buffer);
473
0
        }
474
0
        else {
475
0
            zip_int64_t offset = zip_source_tell(za->src);
476
477
0
            if (offset < 0) {
478
0
                zip_error_set_from_source(error, za->src);
479
0
                _zip_cdir_free(cd);
480
0
                return true;
481
0
            }
482
0
            ok = ((zip_uint64_t)offset == cd->offset + cd->size);
483
0
        }
484
485
0
        if (!ok) {
486
0
            zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID);
487
0
            _zip_buffer_free(cd_buffer);
488
0
            _zip_cdir_free(cd);
489
0
            return true;
490
0
        }
491
0
    }
492
493
4.16k
    _zip_buffer_free(cd_buffer);
494
4.16k
    *cdirp = cd;
495
4.16k
    return true;
496
4.16k
}
497
498
499
40.3k
static bool check_magic(zip_uint64_t offset, zip_buffer_t *buffer, zip_uint64_t buffer_offset, zip_source_t *src, const char *magic) {
500
40.3k
    if (buffer_offset <= offset) {
501
38.8k
        zip_uint8_t *data;
502
38.8k
        if (_zip_buffer_set_offset(buffer, offset - buffer_offset) < 0 || (data = _zip_buffer_get(buffer, MAGIC_LEN)) == NULL) {
503
31.7k
            return false;
504
31.7k
        }
505
7.11k
        return memcmp(data, magic, MAGIC_LEN) == 0;
506
38.8k
    }
507
1.47k
    else {
508
1.47k
        zip_uint8_t data[MAGIC_LEN];
509
510
1.47k
        if (zip_source_seek(src, offset, SEEK_SET) < 0 || zip_source_read(src, data, MAGIC_LEN) != MAGIC_LEN) {
511
0
            return false;
512
0
        }
513
1.47k
        return memcmp(data, magic, MAGIC_LEN) == 0;
514
1.47k
    }
515
40.3k
}
516
517
518
/* _zip_checkcons:
519
   Checks the consistency of the central directory by comparing central
520
   directory entries with local headers and checking for plausible
521
   file and header offsets. Returns -1 if not plausible, else the
522
   difference between the lowest and the highest fileposition reached */
523
524
0
static zip_int64_t _zip_checkcons(zip_t *za, zip_cdir_t *cd, zip_error_t *error) {
525
0
    zip_uint64_t i;
526
0
    zip_uint64_t min, max, j, tail_length;
527
0
    struct zip_dirent temp;
528
0
    int detail;
529
530
0
    _zip_dirent_init(&temp);
531
0
    if (cd->nentry) {
532
0
        max = cd->entry[0].orig->offset;
533
0
        min = cd->entry[0].orig->offset;
534
0
    }
535
0
    else {
536
0
        min = max = 0;
537
0
    }
538
539
0
    for (i = 0; i < cd->nentry; i++) {
540
0
        if (cd->entry[i].orig->offset < min) {
541
0
            min = cd->entry[i].orig->offset;
542
0
        }
543
0
        if (min > (zip_uint64_t)cd->offset) {
544
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
545
0
            return -1;
546
0
        }
547
548
0
        tail_length = _zip_string_length(cd->entry[i].orig->filename) + LENTRYSIZE;
549
0
        if (ZIP_CHECK_ADD_OVERFLOW(cd->entry[i].orig->comp_size, tail_length) || ZIP_CHECK_ADD_OVERFLOW(cd->entry[i].orig->offset + tail_length, cd->entry[i].orig->comp_size)) {
550
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
551
0
            return -1;
552
0
        }
553
0
        j = cd->entry[i].orig->offset + cd->entry[i].orig->comp_size + tail_length;
554
555
0
        if (j > max) {
556
0
            max = j;
557
0
        }
558
0
        if (max > (zip_uint64_t)cd->offset) {
559
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
560
0
            return -1;
561
0
        }
562
563
0
        if (zip_source_seek(za->src, (zip_int64_t)cd->entry[i].orig->offset, SEEK_SET) < 0) {
564
0
            zip_error_set_from_source(error, za->src);
565
0
            return -1;
566
0
        }
567
568
0
        if (_zip_dirent_read(&temp, za->src, NULL, true, cd->entry[i].orig->comp_size, true, error) == -1) {
569
0
            if (zip_error_code_zip(error) == ZIP_ER_INCONS) {
570
0
                zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i));
571
0
            }
572
0
            _zip_dirent_finalize(&temp);
573
0
            return -1;
574
0
        }
575
576
0
        if (_zip_headercomp(cd->entry[i].orig, &temp) != 0) {
577
0
            zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_ENTRY_HEADER_MISMATCH, i));
578
0
            _zip_dirent_finalize(&temp);
579
0
            return -1;
580
0
        }
581
582
0
        cd->entry[i].orig->extra_fields = _zip_ef_merge(cd->entry[i].orig->extra_fields, temp.extra_fields);
583
0
        cd->entry[i].orig->local_extra_fields_read = 1;
584
0
        temp.extra_fields = NULL;
585
586
0
        _zip_dirent_finalize(&temp);
587
588
0
        if ((detail = zip_dirent_check_consistency(cd->entry[i].orig)) != 0) {
589
0
            zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(detail, i));
590
0
            return -1;
591
0
        }
592
0
    }
593
594
0
    return (max - min) < ZIP_INT64_MAX ? (zip_int64_t)(max - min) : ZIP_INT64_MAX;
595
0
}
596
597
598
/* _zip_headercomp:
599
   compares a central directory entry and a local file header
600
   Return 0 if they are consistent, -1 if not. */
601
602
0
static int _zip_headercomp(const zip_dirent_t *central, const zip_dirent_t *local) {
603
0
    if ((central->version_needed < local->version_needed)
604
#if 0
605
  /* some zip-files have different values in local
606
     and global headers for the bitflags */
607
  || (central->bitflags != local->bitflags)
608
#endif
609
0
        || (central->comp_method != local->comp_method) || (central->last_mod.time != local->last_mod.time) || (central->last_mod.date != local->last_mod.date) || !_zip_string_equal(central->filename, local->filename))
610
0
        return -1;
611
612
0
    if ((central->crc != local->crc) || (central->comp_size != local->comp_size) || (central->uncomp_size != local->uncomp_size)) {
613
        /* InfoZip stores valid values in local header even when data descriptor is used.
614
           This is in violation of the appnote.
615
           macOS Archive sets the compressed size even when data descriptor is used ( but not the others),
616
           also in violation of the appnote.
617
        */
618
        /* if data descriptor is not used, the values must match */
619
0
        if ((local->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0) {
620
0
            return -1;
621
0
        }
622
        /* when using a data descriptor, the local header value must be zero or match */
623
0
        if ((local->crc != 0 && central->crc != local->crc) || (local->comp_size != 0 && central->comp_size != local->comp_size) || (local->uncomp_size != 0 && central->uncomp_size != local->uncomp_size)) {
624
0
            return -1;
625
0
        }
626
0
    }
627
628
0
    return 0;
629
0
}
630
631
632
6.61k
static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error) {
633
6.61k
    zip_t *za;
634
635
6.61k
    if ((za = _zip_new(error)) == NULL) {
636
0
        return NULL;
637
0
    }
638
639
6.61k
    za->src = src;
640
6.61k
    za->open_flags = flags;
641
6.61k
    za->flags = 0;
642
6.61k
    za->ch_flags = 0;
643
6.61k
    za->write_crc = NULL;
644
645
6.61k
    if (flags & ZIP_RDONLY) {
646
0
        za->flags |= ZIP_AFL_RDONLY;
647
0
        za->ch_flags |= ZIP_AFL_RDONLY;
648
0
    }
649
650
6.61k
    return za;
651
6.61k
}
652
653
654
/*
655
 * tests for file existence
656
 */
657
6.61k
static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error) {
658
6.61k
    struct zip_stat st;
659
660
6.61k
    zip_stat_init(&st);
661
6.61k
    if (zip_source_stat(src, &st) != 0) {
662
0
        zip_error_t *src_error = zip_source_error(src);
663
0
        if (zip_error_code_zip(src_error) == ZIP_ER_READ && zip_error_code_system(src_error) == ENOENT) {
664
0
            return EXISTS_NOT;
665
0
        }
666
0
        _zip_error_copy(error, src_error);
667
0
        return EXISTS_ERROR;
668
0
    }
669
670
6.61k
    return EXISTS_OK;
671
6.61k
}
672
673
674
6.61k
static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len) {
675
6.61k
    zip_cdir_t *cdir;
676
6.61k
    const zip_uint8_t *match;
677
6.61k
    zip_int64_t buf_offset;
678
6.61k
    zip_uint64_t buflen;
679
6.61k
    zip_error_t error;
680
6.61k
    zip_buffer_t *buffer;
681
682
6.61k
    if (len < EOCDLEN) {
683
278
        zip_error_set(&za->error, ZIP_ER_NOZIP, 0);
684
278
        return NULL;
685
278
    }
686
687
6.33k
    buflen = (len < CDBUFSIZE ? len : CDBUFSIZE);
688
6.33k
    if (zip_source_seek(za->src, -(zip_int64_t)buflen, SEEK_END) < 0) {
689
0
        zip_error_t *src_error = zip_source_error(za->src);
690
0
        if (zip_error_code_zip(src_error) != ZIP_ER_SEEK || zip_error_code_system(src_error) != EFBIG) {
691
            /* seek before start of file on my machine */
692
0
            _zip_error_copy(&za->error, src_error);
693
0
            return NULL;
694
0
        }
695
0
    }
696
6.33k
    if ((buf_offset = zip_source_tell(za->src)) < 0) {
697
0
        zip_error_set_from_source(&za->error, za->src);
698
0
        return NULL;
699
0
    }
700
701
6.33k
    if ((buffer = _zip_buffer_new_from_source(za->src, buflen, NULL, &za->error)) == NULL) {
702
0
        return NULL;
703
0
    }
704
705
6.33k
    cdir = NULL;
706
6.33k
    if (buflen >= CDBUFSIZE) {
707
        /* EOCD64 locator is before EOCD, so leave place for it */
708
440
        _zip_buffer_set_offset(buffer, EOCD64LOCLEN);
709
440
    }
710
6.33k
    zip_error_set(&error, ZIP_ER_NOZIP, 0);
711
712
6.33k
    match = NULL;
713
46.8k
    while ((match = find_eocd(buffer, match)) != NULL) {
714
45.7k
        _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer)));
715
45.7k
        if (_zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &cdir, &error)) {
716
5.22k
            if (cdir != NULL && (za->open_flags & ZIP_CHECKCONS) && _zip_checkcons(za, cdir, &error) < 0) {
717
0
                _zip_cdir_free(cdir);
718
0
                cdir = NULL;
719
0
            }
720
5.22k
            break;
721
5.22k
        }
722
45.7k
    }
723
724
6.33k
    _zip_buffer_free(buffer);
725
726
6.33k
    if (cdir == NULL) {
727
2.17k
        _zip_error_copy(&za->error, &error);
728
2.17k
    }
729
6.33k
    return cdir;
730
6.33k
}
731
732
733
46.8k
static const unsigned char *find_eocd(zip_buffer_t *buffer, const unsigned char *last) {
734
46.8k
    const unsigned char *data = _zip_buffer_data(buffer);
735
46.8k
    const unsigned char *p;
736
737
46.8k
    if (last == NULL) {
738
6.33k
        if (_zip_buffer_size(buffer) < MAGIC_LEN) {
739
0
            return NULL;
740
0
        }
741
6.33k
        last = data + _zip_buffer_size(buffer) - MAGIC_LEN;
742
6.33k
    }
743
40.4k
    else {
744
40.4k
        if (last == _zip_buffer_data(buffer)) {
745
188
            return NULL;
746
188
        }
747
40.3k
        last -= 1;
748
40.3k
    }
749
750
15.0M
    for (p = last; p >= data; p -= 1) {
751
15.0M
        if (*p == EOCD_MAGIC[0]) {
752
167k
            if (memcmp(p, EOCD_MAGIC, MAGIC_LEN) == 0) {
753
45.7k
                return p;
754
45.7k
            }
755
167k
        }
756
14.9M
        if (p == data) {
757
            /* Avoid undefined behavior by creating pointer outside buffer */
758
927
            break;
759
927
        }
760
14.9M
    }
761
762
927
    return NULL;
763
46.6k
}
764
765
766
45.7k
static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) {
767
45.7k
    zip_cdir_t *cd;
768
769
45.7k
    if (_zip_buffer_left(buffer) < EOCDLEN) {
770
162
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD_LENGTH_INVALID);
771
162
        return NULL;
772
162
    }
773
774
45.5k
    if ((cd = _zip_cdir_new(error)) == NULL) {
775
0
        return NULL;
776
0
    }
777
778
45.5k
    cd->eocd_offset = buf_offset + _zip_buffer_offset(buffer);
779
    /* This function is only called where EOCD magic was found, so no need to check that here. */
780
45.5k
    _zip_buffer_skip(buffer, MAGIC_LEN);
781
45.5k
    cd->is_zip64 = false;
782
45.5k
    cd->this_disk = _zip_buffer_get_16(buffer);
783
45.5k
    cd->eocd_disk = _zip_buffer_get_16(buffer);
784
785
    /* number of cdir-entries on this disk */
786
45.5k
    cd->disk_entries = _zip_buffer_get_16(buffer);
787
    /* number of cdir-entries */
788
45.5k
    cd->num_entries = _zip_buffer_get_16(buffer);
789
45.5k
    cd->size = _zip_buffer_get_32(buffer);
790
45.5k
    cd->offset = _zip_buffer_get_32(buffer);
791
792
45.5k
    return cd;
793
45.5k
}
794
795
5.16k
static bool check_eocd(zip_cdir_t *cd, unsigned int flags, zip_error_t *error) {
796
5.16k
    if (cd->disk_entries != cd->num_entries || cd->this_disk != 0 || cd->eocd_disk != 0) {
797
67
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
798
67
        return false;
799
67
    }
800
801
5.09k
    if (cd->offset + cd->size < cd->offset) {
802
0
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
803
0
        return false;
804
0
    }
805
5.09k
    if ((flags & ZIP_CHECKCONS) && cd->offset + cd->size != cd->eocd_offset) {
806
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_LENGTH_INVALID);
807
0
        return false;
808
0
    }
809
810
5.09k
    return true;
811
5.09k
}
812
813
814
1.25k
cdir_status_t _zip_read_eocd64(zip_cdir_t *cdir, zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) {
815
1.25k
    zip_uint64_t offset;
816
1.25k
    zip_uint8_t eocd[EOCD64LEN];
817
1.25k
    zip_uint64_t eocd_offset;
818
1.25k
    zip_uint64_t size, nentry, i, eocdloc_offset;
819
1.25k
    bool free_buffer;
820
1.25k
    zip_uint32_t num_disks, eocd_disk, this_disk;
821
822
    /* The offset of the end of the buffer is less than ZIP_UINT64_MAX, so this can't overflow. */
823
1.25k
    eocdloc_offset = buf_offset + _zip_buffer_offset(buffer);
824
825
1.25k
    _zip_buffer_get(buffer, 4); /* magic already verified */
826
827
1.25k
    eocd_disk = _zip_buffer_get_32(buffer);
828
1.25k
    eocd_offset = _zip_buffer_get_64(buffer);
829
1.25k
    num_disks = _zip_buffer_get_32(buffer);
830
831
1.25k
    if (!check_magic(eocd_offset, buffer, buf_offset, src, EOCD64_MAGIC)) {
832
1.19k
        return CDIR_NOT_FOUND;
833
1.19k
    }
834
835
62
    if (num_disks != 1) {
836
17
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
837
17
        return CDIR_INVALID;
838
17
    }
839
840
    /* valid seek value for start of EOCD */
841
45
    if (eocd_offset > ZIP_INT64_MAX) {
842
0
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
843
0
        return CDIR_INVALID;
844
0
    }
845
846
    /* does EOCD fit before EOCD locator? */
847
45
    if (eocd_offset + EOCD64LEN > eocdloc_offset) {
848
14
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
849
14
        return CDIR_INVALID;
850
14
    }
851
852
    /* make sure current position of buffer is beginning of EOCD */
853
31
    if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) {
854
21
        _zip_buffer_set_offset(buffer, eocd_offset - buf_offset);
855
21
        free_buffer = false;
856
21
    }
857
10
    else {
858
10
        if (zip_source_seek(src, (zip_int64_t)eocd_offset, SEEK_SET) < 0) {
859
0
            zip_error_set_from_source(error, src);
860
0
            return CDIR_INVALID;
861
0
        }
862
10
        if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) {
863
0
            return CDIR_INVALID;
864
0
        }
865
10
        free_buffer = true;
866
10
    }
867
868
31
    if (memcmp(_zip_buffer_get(buffer, 4), EOCD64_MAGIC, 4) != 0) {
869
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_WRONG_MAGIC);
870
0
        if (free_buffer) {
871
0
            _zip_buffer_free(buffer);
872
0
        }
873
0
        return CDIR_INVALID;
874
0
    }
875
876
    /* size of EOCD */
877
31
    size = _zip_buffer_get_64(buffer);
878
879
    /* Is there a hole between EOCD and EOCD locator, or do they overlap? Also check for overflow. */
880
31
    if ((flags & ZIP_CHECKCONS) && (ZIP_CHECK_ADD_OVERFLOW(size, eocd_offset + 12) || size + eocd_offset + 12 != eocdloc_offset)) {
881
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
882
0
        if (free_buffer) {
883
0
            _zip_buffer_free(buffer);
884
0
        }
885
0
        return CDIR_INVALID;
886
0
    }
887
888
31
    _zip_buffer_get(buffer, 4); /* skip version made by/needed */
889
890
31
    this_disk = _zip_buffer_get_32(buffer);
891
31
    if (_zip_buffer_get_32(buffer) != eocd_disk) {
892
31
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_LOCATOR_MISMATCH);
893
31
        if (free_buffer) {
894
10
            _zip_buffer_free(buffer);
895
10
        }
896
31
        return CDIR_INVALID;
897
31
    }
898
899
0
    i = _zip_buffer_get_64(buffer);
900
0
    nentry = _zip_buffer_get_64(buffer);
901
902
0
    if (nentry != i) {
903
0
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
904
0
        if (free_buffer) {
905
0
            _zip_buffer_free(buffer);
906
0
        }
907
0
        return CDIR_INVALID;
908
0
    }
909
910
0
    size = _zip_buffer_get_64(buffer);
911
0
    offset = _zip_buffer_get_64(buffer);
912
913
    /* did we read past the end of the buffer? */
914
0
    if (!_zip_buffer_ok(buffer)) {
915
0
        zip_error_set(error, ZIP_ER_INTERNAL, 0);
916
0
        if (free_buffer) {
917
0
            _zip_buffer_free(buffer);
918
0
        }
919
0
        return CDIR_INVALID;
920
0
    }
921
922
0
    if (free_buffer) {
923
0
        _zip_buffer_free(buffer);
924
0
    }
925
926
0
    if (offset > ZIP_INT64_MAX || offset + size < offset) {
927
0
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
928
0
        return CDIR_INVALID;
929
0
    }
930
931
0
    if (nentry > size / CDENTRYSIZE) {
932
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_INVALID);
933
0
        return CDIR_INVALID;
934
0
    }
935
936
0
    if ((cdir->size != 0xffffffff && cdir->size != size) || (cdir->offset != 0xffffffff && cdir->offset != offset) || (cdir->num_entries != 0xffff && cdir->num_entries != nentry) || (cdir->disk_entries != 0xffff && cdir->disk_entries != i) || (cdir->this_disk != 0xffff && cdir->this_disk != this_disk) || (cdir->eocd_disk != 0xffff && cdir->eocd_disk != eocd_disk)) {
937
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_MISMATCH);
938
0
        return CDIR_INVALID;
939
0
    }
940
941
0
    cdir->is_zip64 = true;
942
0
    cdir->size = size;
943
0
    cdir->offset = offset;
944
0
    cdir->disk_entries = i;
945
0
    cdir->num_entries = nentry;
946
0
    cdir->this_disk = this_disk;
947
0
    cdir->eocd_disk = eocd_disk;
948
949
0
    return CDIR_OK;
950
0
}
951
952
953
230
static int decode_hex(char c) {
954
230
    if (c >= '0' && c <= '9') {
955
146
        return c - '0';
956
146
    }
957
84
    else if (c >= 'A' && c <= 'F') {
958
15
        return c - 'A' + 10;
959
15
    }
960
69
    else {
961
69
        return -1;
962
69
    }
963
230
}
964
965
/* _zip_check_torrentzip:
966
   check whether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */
967
968
4.16k
static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir) {
969
4.16k
    zip_uint32_t crc_should;
970
4.16k
    char buf[8 + 1];
971
4.16k
    size_t i;
972
973
4.16k
    if (cdir == NULL) {
974
0
        return;
975
0
    }
976
977
4.16k
    if (_zip_string_length(cdir->comment) != TORRENTZIP_SIGNATURE_LENGTH + TORRENTZIP_CRC_LENGTH || strncmp((const char *)cdir->comment->raw, TORRENTZIP_SIGNATURE, TORRENTZIP_SIGNATURE_LENGTH) != 0) {
978
4.11k
        return;
979
4.11k
    }
980
981
52
    memcpy(buf, cdir->comment->raw + TORRENTZIP_SIGNATURE_LENGTH, TORRENTZIP_CRC_LENGTH);
982
52
    buf[TORRENTZIP_CRC_LENGTH] = '\0';
983
52
    crc_should = 0;
984
123
    for (i = 0; i < TORRENTZIP_CRC_LENGTH; i += 2) {
985
115
        int low, high;
986
115
        high = decode_hex((buf[i]));
987
115
        low = decode_hex(buf[i + 1]);
988
115
        if (high < 0 || low < 0) {
989
44
            return;
990
44
        }
991
71
        crc_should = (crc_should << 8) + (high << 4) + low;
992
71
    }
993
994
8
    {
995
8
        zip_stat_t st;
996
8
        zip_source_t *src_window;
997
8
        zip_source_t *src_crc;
998
8
        zip_uint8_t buffer[512];
999
8
        zip_int64_t ret;
1000
1001
8
        zip_stat_init(&st);
1002
8
        st.valid |= ZIP_STAT_SIZE | ZIP_STAT_CRC;
1003
8
        st.size = cdir->size;
1004
8
        st.crc = crc_should;
1005
8
        if ((src_window = _zip_source_window_new(za->src, cdir->offset, cdir->size, &st, 0, NULL, NULL, NULL, 0, false, NULL)) == NULL) {
1006
0
            return;
1007
0
        }
1008
8
        if ((src_crc = zip_source_crc_create(src_window, 1, NULL)) == NULL) {
1009
0
            zip_source_free(src_window);
1010
0
            return;
1011
0
        }
1012
8
        if (zip_source_open(src_crc) != 0) {
1013
0
            zip_source_free(src_crc);
1014
0
            return;
1015
0
        }
1016
16
        while ((ret = zip_source_read(src_crc, buffer, sizeof(buffer))) > 0) {
1017
8
        }
1018
8
        zip_source_free(src_crc);
1019
8
        if (ret < 0) {
1020
8
            return;
1021
8
        }
1022
8
    }
1023
1024
    /* TODO: if check consistency, check cdir entries for valid values */
1025
0
    za->flags |= ZIP_AFL_IS_TORRENTZIP;
1026
0
}