Coverage Report

Created: 2026-07-30 07:17

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
1.16k
ZIP_EXTERN zip_t *zip_open(const char *fn, int _flags, int *zep) {
68
1.16k
    zip_t *za;
69
1.16k
    zip_source_t *src;
70
1.16k
    struct zip_error error;
71
72
1.16k
    zip_error_init(&error);
73
1.16k
    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
1.16k
    if ((za = zip_open_from_source(src, _flags, &error)) == NULL) {
80
335
        zip_source_free(src);
81
335
        _zip_set_open_error(zep, &error, 0);
82
335
        zip_error_fini(&error);
83
335
        return NULL;
84
335
    }
85
86
832
    zip_error_fini(&error);
87
832
    return za;
88
1.16k
}
89
90
91
1.16k
ZIP_EXTERN zip_t *zip_open_from_source(zip_source_t *src, int _flags, zip_error_t *error) {
92
1.16k
    unsigned int flags;
93
1.16k
    zip_int64_t supported;
94
1.16k
    exists_t exists;
95
96
1.16k
    if (_flags < 0 || src == NULL) {
97
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
98
0
        return NULL;
99
0
    }
100
1.16k
    flags = (unsigned int)_flags;
101
102
1.16k
    supported = zip_source_supports(src);
103
1.16k
    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
1.16k
    if ((supported & ZIP_SOURCE_SUPPORTS_WRITABLE) != ZIP_SOURCE_SUPPORTS_WRITABLE) {
108
0
        flags |= ZIP_RDONLY;
109
0
    }
110
111
1.16k
    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
1.16k
    exists = _zip_file_exists(src, error);
117
1.16k
    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
1.16k
    default: {
129
1.16k
        zip_t *za;
130
1.16k
        if (flags & ZIP_EXCL) {
131
0
            zip_error_set(error, ZIP_ER_EXISTS, 0);
132
0
            return NULL;
133
0
        }
134
1.16k
        if (zip_source_open(src) < 0) {
135
0
            zip_error_set_from_source(error, src);
136
0
            return NULL;
137
0
        }
138
139
1.16k
        if (flags & ZIP_TRUNCATE) {
140
0
            za = _zip_allocate_new(src, flags, error);
141
0
        }
142
1.16k
        else {
143
            /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, just like open() */
144
1.16k
            za = _zip_open(src, flags, error);
145
1.16k
        }
146
147
1.16k
        if (za == NULL) {
148
335
            zip_source_close(src);
149
335
            return NULL;
150
335
        }
151
832
        return za;
152
1.16k
    }
153
1.16k
    }
154
1.16k
}
155
156
157
198
static bool _is_truncated_zip(zip_source_t *src) {
158
198
    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
198
    if (zip_source_seek(src, 0, SEEK_SET) < 0) {
162
0
        return false;
163
0
    }
164
165
198
    if (zip_source_read(src, data, 4) != 4) {
166
0
        return false;
167
0
    }
168
169
198
    if (memcmp(data, LOCAL_MAGIC, 4) == 0) {
170
        /* file starts with a ZIP local header signature */
171
170
        return true;
172
170
    }
173
28
    return false;
174
198
}
175
176
177
1.16k
zip_t *_zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) {
178
1.16k
    zip_t *za;
179
1.16k
    zip_cdir_t *cdir;
180
1.16k
    struct zip_stat st;
181
1.16k
    zip_uint64_t len, idx;
182
183
1.16k
    zip_stat_init(&st);
184
1.16k
    if (zip_source_stat(src, &st) < 0) {
185
0
        zip_error_set_from_source(error, src);
186
0
        return NULL;
187
0
    }
188
1.16k
    if ((st.valid & ZIP_STAT_SIZE) == 0) {
189
0
        zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP);
190
0
        return NULL;
191
0
    }
192
1.16k
    len = st.size;
193
194
195
1.16k
    if ((za = _zip_allocate_new(src, flags, error)) == NULL) {
196
0
        return NULL;
197
0
    }
198
199
    /* treat empty files as empty archives */
200
1.16k
    if (len == 0 && zip_source_accept_empty(src)) {
201
0
        return za;
202
0
    }
203
204
1.16k
    if ((cdir = _zip_find_central_dir(za, len)) == NULL) {
205
335
        _zip_error_copy(error, &za->error);
206
335
        if (zip_error_code_zip(&za->error) == ZIP_ER_NOZIP) {
207
            /* not a zip - find out if it's truncated */
208
198
            if (_is_truncated_zip(src)) {
209
170
                zip_error_set(error, ZIP_ER_TRUNCATED_ZIP, 0);
210
170
            }
211
198
        }
212
        /* keep src so discard does not get rid of it */
213
335
        zip_source_keep(src);
214
335
        zip_discard(za);
215
335
        return NULL;
216
335
    }
217
218
832
    za->entry = cdir->entry;
219
832
    za->nentry = cdir->nentry;
220
832
    za->nentry_alloc = cdir->nentry_alloc;
221
222
832
    zip_check_torrentzip(za, cdir);
223
224
832
    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
832
    else {
229
832
        za->comment_orig = cdir->comment;
230
832
    }
231
232
832
    free(cdir);
233
234
832
    _zip_hash_reserve_capacity(za->names, za->nentry, &za->error);
235
236
8.32k
    for (idx = 0; idx < za->nentry; idx++) {
237
7.48k
        const zip_uint8_t *name = _zip_string_get(za->entry[idx].orig->filename, NULL, 0, error);
238
7.48k
        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
7.48k
        if (_zip_hash_add(za->names, name, idx, ZIP_FL_UNCHANGED, &za->error) == false) {
246
2
            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
2
        }
254
7.48k
    }
255
256
832
    za->ch_flags = za->flags;
257
258
832
    return za;
259
832
}
260
261
262
335
void _zip_set_open_error(int *zep, const zip_error_t *err, int ze) {
263
335
    if (err) {
264
335
        ze = zip_error_code_zip(err);
265
335
        switch (zip_error_system_type(err)) {
266
0
        case ZIP_ET_SYS:
267
130
        case ZIP_ET_LIBZIP:
268
130
            errno = zip_error_code_system(err);
269
130
            break;
270
271
205
        default:
272
205
            break;
273
335
        }
274
335
    }
275
276
335
    if (zep) {
277
335
        *zep = ze;
278
335
    }
279
335
}
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
1.11k
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
1.11k
    zip_cdir_t *cd;
290
1.11k
    zip_uint16_t comment_len;
291
1.11k
    zip_uint64_t i, left;
292
1.11k
    zip_uint64_t eocd_offset = _zip_buffer_offset(buffer);
293
1.11k
    zip_buffer_t *cd_buffer;
294
1.11k
    bool eocd64_found = false;
295
296
1.11k
    *cdirp = NULL;
297
298
1.11k
    if ((cd = _zip_read_eocd(buffer, buf_offset, error)) == NULL) {
299
7
        return false;
300
7
    }
301
302
1.10k
    if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) {
303
0
        eocd64_found = true;
304
0
        _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN);
305
0
        switch (_zip_read_eocd64(cd, za->src, buffer, buf_offset, za->open_flags, error)) {
306
0
        case CDIR_OK:
307
0
            break;
308
309
0
        case CDIR_INVALID:
310
0
            _zip_cdir_free(cd);
311
0
            return true;
312
313
0
        case CDIR_NOT_FOUND:
314
0
            _zip_cdir_free(cd);
315
0
            return false;
316
0
        }
317
0
    }
318
319
1.10k
    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
42
        if (cd->this_disk < cd->eocd_disk) {
322
            /* Disks before the start of the central directory don't contain an EOCD. */
323
15
            _zip_cdir_free(cd);
324
15
            return false;
325
15
        }
326
27
        if (cd->size <= cd->eocd_offset) {
327
            /* The complete central directory would fit on this disk. */
328
7
            _zip_cdir_free(cd);
329
7
            return false;
330
7
        }
331
27
    }
332
333
1.08k
    if (!eocd64_found) {
334
1.08k
        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
0
        }
337
1.08k
        else if (!check_magic(cd->offset, buffer, buf_offset, za->src, CENTRAL_MAGIC)) {
338
91
            _zip_cdir_free(cd);
339
91
            return false;
340
91
        }
341
1.08k
    }
342
343
    /* We accept this EOCD as valid and won't search for an earlier one if it is unusable. */
344
345
992
    if (!check_eocd(cd, za->flags, error)) {
346
7
        _zip_cdir_free(cd);
347
7
        return true;
348
7
    }
349
350
985
    _zip_buffer_set_offset(buffer, eocd_offset + 20);
351
985
    comment_len = _zip_buffer_get_16(buffer);
352
353
985
    if (cd->offset + cd->size > buf_offset + eocd_offset) {
354
        /* cdir spans past EOCD record */
355
7
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
356
7
        _zip_cdir_free(cd);
357
7
        return true;
358
7
    }
359
360
978
    if (comment_len || (za->open_flags & ZIP_CHECKCONS)) {
361
12
        zip_uint64_t tail_len;
362
363
12
        _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN);
364
12
        tail_len = _zip_buffer_left(buffer);
365
366
12
        if (tail_len != comment_len) {
367
12
            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
12
            if (tail_len < comment_len) {
373
12
                comment_len = tail_len;
374
12
            }
375
12
        }
376
377
12
        if (comment_len) {
378
9
            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
9
        }
383
12
    }
384
385
978
    if (cd->offset >= buf_offset) {
386
978
        zip_uint8_t *data;
387
        /* if buffer already read in, use it */
388
978
        _zip_buffer_set_offset(buffer, cd->offset - buf_offset);
389
390
978
        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
978
        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
978
    }
401
0
    else {
402
0
        cd_buffer = NULL;
403
404
0
        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
0
        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
0
    }
417
418
978
    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
978
    left = (zip_uint64_t)cd->size;
424
978
    i = 0;
425
8.98k
    while (left > 0) {
426
8.15k
        bool grown = false;
427
8.15k
        zip_int64_t entry_size;
428
429
8.15k
        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
5
            if (cd->is_zip64 || left < CDENTRYSIZE) {
434
3
                break;
435
3
            }
436
437
2
            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
2
            grown = true;
443
2
        }
444
445
8.14k
        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
140
            if (zip_error_code_zip(error) == ZIP_ER_INCONS) {
447
110
                zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i));
448
110
            }
449
30
            else if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) {
450
0
                zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_CDIR_ENTRY_INVALID, i));
451
0
            }
452
140
            _zip_cdir_free(cd);
453
140
            _zip_buffer_free(cd_buffer);
454
140
            return true;
455
140
        }
456
8.00k
        i++;
457
8.00k
        left -= (zip_uint64_t)entry_size;
458
8.00k
    }
459
460
    /* If we didn't fill all we grew, cd->num_entries was wrong. */
461
838
    if (i != cd->nentry || left > 0) {
462
6
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_WRONG_ENTRIES_COUNT);
463
6
        _zip_buffer_free(cd_buffer);
464
6
        _zip_cdir_free(cd);
465
6
        return true;
466
6
    }
467
468
832
    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
832
    _zip_buffer_free(cd_buffer);
494
832
    *cdirp = cd;
495
832
    return true;
496
832
}
497
498
499
1.08k
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
1.08k
    if (buffer_offset <= offset) {
501
1.08k
        zip_uint8_t *data;
502
1.08k
        if (_zip_buffer_set_offset(buffer, offset - buffer_offset) < 0 || (data = _zip_buffer_get(buffer, MAGIC_LEN)) == NULL) {
503
48
            return false;
504
48
        }
505
1.03k
        return memcmp(data, magic, MAGIC_LEN) == 0;
506
1.08k
    }
507
0
    else {
508
0
        zip_uint8_t data[MAGIC_LEN];
509
510
0
        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
0
        return memcmp(data, magic, MAGIC_LEN) == 0;
514
0
    }
515
1.08k
}
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
1.16k
static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error) {
633
1.16k
    zip_t *za;
634
635
1.16k
    if ((za = _zip_new(error)) == NULL) {
636
0
        return NULL;
637
0
    }
638
639
1.16k
    za->src = src;
640
1.16k
    za->open_flags = flags;
641
1.16k
    za->flags = 0;
642
1.16k
    za->ch_flags = 0;
643
1.16k
    za->write_crc = NULL;
644
645
1.16k
    if (flags & ZIP_RDONLY) {
646
0
        za->flags |= ZIP_AFL_RDONLY;
647
0
        za->ch_flags |= ZIP_AFL_RDONLY;
648
0
    }
649
650
1.16k
    return za;
651
1.16k
}
652
653
654
/*
655
 * tests for file existence
656
 */
657
1.16k
static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error) {
658
1.16k
    struct zip_stat st;
659
660
1.16k
    zip_stat_init(&st);
661
1.16k
    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
1.16k
    return EXISTS_OK;
671
1.16k
}
672
673
674
1.16k
static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len) {
675
1.16k
    zip_cdir_t *cdir;
676
1.16k
    const zip_uint8_t *match;
677
1.16k
    zip_int64_t buf_offset;
678
1.16k
    zip_uint64_t buflen;
679
1.16k
    zip_error_t error;
680
1.16k
    zip_buffer_t *buffer;
681
682
1.16k
    if (len < EOCDLEN) {
683
12
        zip_error_set(&za->error, ZIP_ER_NOZIP, 0);
684
12
        return NULL;
685
12
    }
686
687
1.15k
    buflen = (len < CDBUFSIZE ? len : CDBUFSIZE);
688
1.15k
    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
1.15k
    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
1.15k
    if ((buffer = _zip_buffer_new_from_source(za->src, buflen, NULL, &za->error)) == NULL) {
702
0
        return NULL;
703
0
    }
704
705
1.15k
    cdir = NULL;
706
1.15k
    if (buflen >= CDBUFSIZE) {
707
        /* EOCD64 locator is before EOCD, so leave place for it */
708
0
        _zip_buffer_set_offset(buffer, EOCD64LOCLEN);
709
0
    }
710
1.15k
    zip_error_set(&error, ZIP_ER_NOZIP, 0);
711
712
1.15k
    match = NULL;
713
1.27k
    while ((match = find_eocd(buffer, match)) != NULL) {
714
1.11k
        _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer)));
715
1.11k
        if (_zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &cdir, &error)) {
716
992
            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
992
            break;
721
992
        }
722
1.11k
    }
723
724
1.15k
    _zip_buffer_free(buffer);
725
726
1.15k
    if (cdir == NULL) {
727
323
        _zip_error_copy(&za->error, &error);
728
323
    }
729
1.15k
    return cdir;
730
1.15k
}
731
732
733
1.27k
static const unsigned char *find_eocd(zip_buffer_t *buffer, const unsigned char *last) {
734
1.27k
    const unsigned char *data = _zip_buffer_data(buffer);
735
1.27k
    const unsigned char *p;
736
737
1.27k
    if (last == NULL) {
738
1.15k
        if (_zip_buffer_size(buffer) < MAGIC_LEN) {
739
0
            return NULL;
740
0
        }
741
1.15k
        last = data + _zip_buffer_size(buffer) - MAGIC_LEN;
742
1.15k
    }
743
120
    else {
744
120
        if (last == _zip_buffer_data(buffer)) {
745
0
            return NULL;
746
0
        }
747
120
        last -= 1;
748
120
    }
749
750
527k
    for (p = last; p >= data; p -= 1) {
751
527k
        if (*p == EOCD_MAGIC[0]) {
752
5.71k
            if (memcmp(p, EOCD_MAGIC, MAGIC_LEN) == 0) {
753
1.11k
                return p;
754
1.11k
            }
755
5.71k
        }
756
526k
        if (p == data) {
757
            /* Avoid undefined behavior by creating pointer outside buffer */
758
163
            break;
759
163
        }
760
526k
    }
761
762
163
    return NULL;
763
1.27k
}
764
765
766
1.11k
static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) {
767
1.11k
    zip_cdir_t *cd;
768
769
1.11k
    if (_zip_buffer_left(buffer) < EOCDLEN) {
770
7
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD_LENGTH_INVALID);
771
7
        return NULL;
772
7
    }
773
774
1.10k
    if ((cd = _zip_cdir_new(error)) == NULL) {
775
0
        return NULL;
776
0
    }
777
778
1.10k
    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
1.10k
    _zip_buffer_skip(buffer, MAGIC_LEN);
781
1.10k
    cd->is_zip64 = false;
782
1.10k
    cd->this_disk = _zip_buffer_get_16(buffer);
783
1.10k
    cd->eocd_disk = _zip_buffer_get_16(buffer);
784
785
    /* number of cdir-entries on this disk */
786
1.10k
    cd->disk_entries = _zip_buffer_get_16(buffer);
787
    /* number of cdir-entries */
788
1.10k
    cd->num_entries = _zip_buffer_get_16(buffer);
789
1.10k
    cd->size = _zip_buffer_get_32(buffer);
790
1.10k
    cd->offset = _zip_buffer_get_32(buffer);
791
792
1.10k
    return cd;
793
1.10k
}
794
795
992
static bool check_eocd(zip_cdir_t *cd, unsigned int flags, zip_error_t *error) {
796
992
    if (cd->disk_entries != cd->num_entries || cd->this_disk != 0 || cd->eocd_disk != 0) {
797
7
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
798
7
        return false;
799
7
    }
800
801
985
    if (cd->offset + cd->size < cd->offset) {
802
0
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
803
0
        return false;
804
0
    }
805
985
    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
985
    return true;
811
985
}
812
813
814
0
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
0
    zip_uint64_t offset;
816
0
    zip_uint8_t eocd[EOCD64LEN];
817
0
    zip_uint64_t eocd_offset;
818
0
    zip_uint64_t size, nentry, i, eocdloc_offset;
819
0
    bool free_buffer;
820
0
    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
0
    eocdloc_offset = buf_offset + _zip_buffer_offset(buffer);
824
825
0
    _zip_buffer_get(buffer, 4); /* magic already verified */
826
827
0
    eocd_disk = _zip_buffer_get_32(buffer);
828
0
    eocd_offset = _zip_buffer_get_64(buffer);
829
0
    num_disks = _zip_buffer_get_32(buffer);
830
831
0
    if (!check_magic(eocd_offset, buffer, buf_offset, src, EOCD64_MAGIC)) {
832
0
        return CDIR_NOT_FOUND;
833
0
    }
834
835
0
    if (num_disks != 1) {
836
0
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
837
0
        return CDIR_INVALID;
838
0
    }
839
840
    /* valid seek value for start of EOCD */
841
0
    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
0
    if (eocd_offset + EOCD64LEN > eocdloc_offset) {
848
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
849
0
        return CDIR_INVALID;
850
0
    }
851
852
    /* make sure current position of buffer is beginning of EOCD */
853
0
    if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) {
854
0
        _zip_buffer_set_offset(buffer, eocd_offset - buf_offset);
855
0
        free_buffer = false;
856
0
    }
857
0
    else {
858
0
        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
0
        if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) {
863
0
            return CDIR_INVALID;
864
0
        }
865
0
        free_buffer = true;
866
0
    }
867
868
0
    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
0
    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
0
    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
0
    _zip_buffer_get(buffer, 4); /* skip version made by/needed */
889
890
0
    this_disk = _zip_buffer_get_32(buffer);
891
0
    if (_zip_buffer_get_32(buffer) != eocd_disk) {
892
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_LOCATOR_MISMATCH);
893
0
        if (free_buffer) {
894
0
            _zip_buffer_free(buffer);
895
0
        }
896
0
        return CDIR_INVALID;
897
0
    }
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
0
static int decode_hex(char c) {
954
0
    if (c >= '0' && c <= '9') {
955
0
        return c - '0';
956
0
    }
957
0
    else if (c >= 'A' && c <= 'F') {
958
0
        return c - 'A' + 10;
959
0
    }
960
0
    else {
961
0
        return -1;
962
0
    }
963
0
}
964
965
/* _zip_check_torrentzip:
966
   check whether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */
967
968
832
static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir) {
969
832
    zip_uint32_t crc_should;
970
832
    char buf[8 + 1];
971
832
    size_t i;
972
973
832
    if (cdir == NULL) {
974
0
        return;
975
0
    }
976
977
832
    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
832
        return;
979
832
    }
980
981
0
    memcpy(buf, cdir->comment->raw + TORRENTZIP_SIGNATURE_LENGTH, TORRENTZIP_CRC_LENGTH);
982
0
    buf[TORRENTZIP_CRC_LENGTH] = '\0';
983
0
    crc_should = 0;
984
0
    for (i = 0; i < TORRENTZIP_CRC_LENGTH; i += 2) {
985
0
        int low, high;
986
0
        high = decode_hex((buf[i]));
987
0
        low = decode_hex(buf[i + 1]);
988
0
        if (high < 0 || low < 0) {
989
0
            return;
990
0
        }
991
0
        crc_should = (crc_should << 8) + (high << 4) + low;
992
0
    }
993
994
0
    {
995
0
        zip_stat_t st;
996
0
        zip_source_t *src_window;
997
0
        zip_source_t *src_crc;
998
0
        zip_uint8_t buffer[512];
999
0
        zip_int64_t ret;
1000
1001
0
        zip_stat_init(&st);
1002
0
        st.valid |= ZIP_STAT_SIZE | ZIP_STAT_CRC;
1003
0
        st.size = cdir->size;
1004
0
        st.crc = crc_should;
1005
0
        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
0
        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
0
        if (zip_source_open(src_crc) != 0) {
1013
0
            zip_source_free(src_crc);
1014
0
            return;
1015
0
        }
1016
0
        while ((ret = zip_source_read(src_crc, buffer, sizeof(buffer))) > 0) {
1017
0
        }
1018
0
        zip_source_free(src_crc);
1019
0
        if (ret < 0) {
1020
0
            return;
1021
0
        }
1022
0
    }
1023
1024
    /* TODO: if check consistency, check cdir entries for valid values */
1025
0
    za->flags |= ZIP_AFL_IS_TORRENTZIP;
1026
0
}