Coverage Report

Created: 2026-09-28 06:46

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
0
ZIP_EXTERN zip_t *zip_open(const char *fn, int _flags, int *zep) {
68
0
    zip_t *za;
69
0
    zip_source_t *src;
70
0
    struct zip_error error;
71
72
0
    zip_error_init(&error);
73
0
    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
0
    if ((za = zip_open_from_source(src, _flags, &error)) == NULL) {
80
0
        zip_source_free(src);
81
0
        _zip_set_open_error(zep, &error, 0);
82
0
        zip_error_fini(&error);
83
0
        return NULL;
84
0
    }
85
86
0
    zip_error_fini(&error);
87
0
    return za;
88
0
}
89
90
91
5.19k
ZIP_EXTERN zip_t *zip_open_from_source(zip_source_t *src, int _flags, zip_error_t *error) {
92
5.19k
    unsigned int flags;
93
5.19k
    zip_int64_t supported;
94
5.19k
    exists_t exists;
95
96
5.19k
    if (_flags < 0 || src == NULL) {
97
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
98
0
        return NULL;
99
0
    }
100
5.19k
    flags = (unsigned int)_flags;
101
102
5.19k
    supported = zip_source_supports(src);
103
5.19k
    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
5.19k
    if ((supported & ZIP_SOURCE_SUPPORTS_WRITABLE) != ZIP_SOURCE_SUPPORTS_WRITABLE) {
108
0
        flags |= ZIP_RDONLY;
109
0
    }
110
111
5.19k
    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
5.19k
    exists = _zip_file_exists(src, error);
117
5.19k
    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
5.19k
    default: {
129
5.19k
        zip_t *za;
130
5.19k
        if (flags & ZIP_EXCL) {
131
0
            zip_error_set(error, ZIP_ER_EXISTS, 0);
132
0
            return NULL;
133
0
        }
134
5.19k
        if (zip_source_open(src) < 0) {
135
0
            zip_error_set_from_source(error, src);
136
0
            return NULL;
137
0
        }
138
139
5.19k
        if (flags & ZIP_TRUNCATE) {
140
0
            za = _zip_allocate_new(src, flags, error);
141
0
        }
142
5.19k
        else {
143
            /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, just like open() */
144
5.19k
            za = _zip_open(src, flags, error);
145
5.19k
        }
146
147
5.19k
        if (za == NULL) {
148
2.72k
            zip_source_close(src);
149
2.72k
            return NULL;
150
2.72k
        }
151
2.46k
        return za;
152
5.19k
    }
153
5.19k
    }
154
5.19k
}
155
156
157
551
static bool _is_truncated_zip(zip_source_t *src) {
158
551
    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
551
    if (zip_source_seek(src, 0, SEEK_SET) < 0) {
162
0
        return false;
163
0
    }
164
165
551
    if (zip_source_read(src, data, 4) != 4) {
166
3
        return false;
167
3
    }
168
169
548
    if (memcmp(data, LOCAL_MAGIC, 4) == 0) {
170
        /* file starts with a ZIP local header signature */
171
21
        return true;
172
21
    }
173
527
    return false;
174
548
}
175
176
177
5.19k
zip_t *_zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) {
178
5.19k
    zip_t *za;
179
5.19k
    zip_cdir_t *cdir;
180
5.19k
    struct zip_stat st;
181
5.19k
    zip_uint64_t len, idx;
182
183
5.19k
    zip_stat_init(&st);
184
5.19k
    if (zip_source_stat(src, &st) < 0) {
185
0
        zip_error_set_from_source(error, src);
186
0
        return NULL;
187
0
    }
188
5.19k
    if ((st.valid & ZIP_STAT_SIZE) == 0) {
189
0
        zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP);
190
0
        return NULL;
191
0
    }
192
5.19k
    len = st.size;
193
194
195
5.19k
    if ((za = _zip_allocate_new(src, flags, error)) == NULL) {
196
0
        return NULL;
197
0
    }
198
199
    /* treat empty files as empty archives */
200
5.19k
    if (len == 0 && zip_source_accept_empty(src)) {
201
0
        return za;
202
0
    }
203
204
5.19k
    if ((cdir = _zip_find_central_dir(za, len)) == NULL) {
205
2.72k
        _zip_error_copy(error, &za->error);
206
2.72k
        if (zip_error_code_zip(&za->error) == ZIP_ER_NOZIP) {
207
            /* not a zip - find out if it's truncated */
208
551
            if (_is_truncated_zip(src)) {
209
21
                zip_error_set(error, ZIP_ER_TRUNCATED_ZIP, 0);
210
21
            }
211
551
        }
212
        /* keep src so discard does not get rid of it */
213
2.72k
        zip_source_keep(src);
214
2.72k
        zip_discard(za);
215
2.72k
        return NULL;
216
2.72k
    }
217
218
2.46k
    za->entry = cdir->entry;
219
2.46k
    za->nentry = cdir->nentry;
220
2.46k
    za->nentry_alloc = cdir->nentry_alloc;
221
222
2.46k
    zip_check_torrentzip(za, cdir);
223
224
2.46k
    if (ZIP_IS_TORRENTZIP(za)) {
225
        /* Torrentzip uses the archive comment to detect changes by tools that are not torrentzip aware. */
226
1
        _zip_string_free(cdir->comment);
227
1
    }
228
2.46k
    else {
229
2.46k
        za->comment_orig = cdir->comment;
230
2.46k
    }
231
232
2.46k
    free(cdir);
233
234
2.46k
    _zip_hash_reserve_capacity(za->names, za->nentry, &za->error);
235
236
38.1k
    for (idx = 0; idx < za->nentry; idx++) {
237
35.7k
        const zip_uint8_t *name = _zip_string_get(za->entry[idx].orig->filename, NULL, 0, error);
238
35.7k
        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
35.7k
        if (_zip_hash_add(za->names, name, idx, ZIP_FL_UNCHANGED, &za->error) == false) {
246
22.9k
            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
22.9k
        }
254
35.7k
    }
255
256
2.46k
    za->ch_flags = za->flags;
257
258
2.46k
    return za;
259
2.46k
}
260
261
262
0
void _zip_set_open_error(int *zep, const zip_error_t *err, int ze) {
263
0
    if (err) {
264
0
        ze = zip_error_code_zip(err);
265
0
        switch (zip_error_system_type(err)) {
266
0
        case ZIP_ET_SYS:
267
0
        case ZIP_ET_LIBZIP:
268
0
            errno = zip_error_code_system(err);
269
0
            break;
270
271
0
        default:
272
0
            break;
273
0
        }
274
0
    }
275
276
0
    if (zep) {
277
0
        *zep = ze;
278
0
    }
279
0
}
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
38.1k
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
38.1k
    zip_cdir_t *cd;
290
38.1k
    zip_uint16_t comment_len;
291
38.1k
    zip_uint64_t i, left;
292
38.1k
    zip_uint64_t eocd_offset = _zip_buffer_offset(buffer);
293
38.1k
    zip_buffer_t *cd_buffer;
294
38.1k
    bool eocd64_found = false;
295
296
38.1k
    *cdirp = NULL;
297
298
38.1k
    if ((cd = _zip_read_eocd(buffer, buf_offset, error)) == NULL) {
299
303
        return false;
300
303
    }
301
302
37.8k
    if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) {
303
11.3k
        eocd64_found = true;
304
11.3k
        _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN);
305
11.3k
        switch (_zip_read_eocd64(cd, za->src, buffer, buf_offset, za->open_flags, error)) {
306
211
        case CDIR_OK:
307
211
            break;
308
309
856
        case CDIR_INVALID:
310
856
            _zip_cdir_free(cd);
311
856
            return true;
312
313
10.2k
        case CDIR_NOT_FOUND:
314
10.2k
            _zip_cdir_free(cd);
315
10.2k
            return false;
316
11.3k
        }
317
11.3k
    }
318
319
26.6k
    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
14.6k
        if (cd->this_disk < cd->eocd_disk) {
322
            /* Disks before the start of the central directory don't contain an EOCD. */
323
3.21k
            _zip_cdir_free(cd);
324
3.21k
            return false;
325
3.21k
        }
326
11.3k
        if (cd->size <= cd->eocd_offset) {
327
            /* The complete central directory would fit on this disk. */
328
2.87k
            _zip_cdir_free(cd);
329
2.87k
            return false;
330
2.87k
        }
331
11.3k
    }
332
333
20.5k
    if (!eocd64_found) {
334
20.3k
        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
311
        }
337
20.0k
        else if (!check_magic(cd->offset, buffer, buf_offset, za->src, CENTRAL_MAGIC)) {
338
16.7k
            _zip_cdir_free(cd);
339
16.7k
            return false;
340
16.7k
        }
341
20.3k
    }
342
343
    /* We accept this EOCD as valid and won't search for an earlier one if it is unusable. */
344
345
3.84k
    if (!check_eocd(cd, za->open_flags, error)) {
346
120
        _zip_cdir_free(cd);
347
120
        return true;
348
120
    }
349
350
3.72k
    _zip_buffer_set_offset(buffer, eocd_offset + 20);
351
3.72k
    comment_len = _zip_buffer_get_16(buffer);
352
353
3.72k
    if (cd->offset + cd->size > buf_offset + eocd_offset) {
354
        /* cdir spans past EOCD record */
355
144
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
356
144
        _zip_cdir_free(cd);
357
144
        return true;
358
144
    }
359
360
3.58k
    if (comment_len || (za->open_flags & ZIP_CHECKCONS)) {
361
2.97k
        zip_uint64_t tail_len;
362
363
2.97k
        _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN);
364
2.97k
        tail_len = _zip_buffer_left(buffer);
365
366
2.97k
        if (tail_len != comment_len) {
367
2.80k
            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
2.80k
            if (tail_len < comment_len) {
373
2.71k
                comment_len = (zip_uint16_t)tail_len;
374
2.71k
            }
375
2.80k
        }
376
377
2.97k
        if (comment_len) {
378
751
            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
751
        }
383
2.97k
    }
384
385
3.58k
    if (cd->offset >= buf_offset) {
386
3.40k
        zip_uint8_t *data;
387
        /* if buffer already read in, use it */
388
3.40k
        _zip_buffer_set_offset(buffer, cd->offset - buf_offset);
389
390
3.40k
        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
3.40k
        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
3.40k
    }
401
180
    else {
402
180
        cd_buffer = NULL;
403
404
180
        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
180
        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
180
    }
417
418
3.58k
    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
3.58k
    left = (zip_uint64_t)cd->size;
424
3.58k
    i = 0;
425
84.4k
    while (left > 0) {
426
81.8k
        bool grown = false;
427
81.8k
        zip_int64_t entry_size;
428
429
81.8k
        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
247
            if (cd->is_zip64 || left < CDENTRYSIZE) {
434
68
                break;
435
68
            }
436
437
179
            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
179
            grown = true;
443
179
        }
444
445
81.8k
        if ((cd->entry[i].orig = _zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, cd->is_zip64, 0, za->open_flags & ZIP_CHECKCONS, error)) < 0) {
446
968
            if (zip_error_code_zip(error) == ZIP_ER_INCONS) {
447
633
                zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i));
448
633
            }
449
335
            else if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) {
450
39
                zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_CDIR_ENTRY_INVALID, i));
451
39
            }
452
968
            _zip_cdir_free(cd);
453
968
            _zip_buffer_free(cd_buffer);
454
968
            return true;
455
968
        }
456
80.8k
        i++;
457
80.8k
        left -= (zip_uint64_t)entry_size;
458
80.8k
    }
459
460
    /* If we didn't fill all we grew, cd->num_entries was wrong. */
461
2.61k
    if (i != cd->nentry || left > 0) {
462
147
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_WRONG_ENTRIES_COUNT);
463
147
        _zip_buffer_free(cd_buffer);
464
147
        _zip_cdir_free(cd);
465
147
        return true;
466
147
    }
467
468
2.46k
    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
2.46k
    _zip_buffer_free(cd_buffer);
494
2.46k
    *cdirp = cd;
495
2.46k
    return true;
496
2.46k
}
497
498
499
31.4k
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
31.4k
    if (buffer_offset <= offset) {
501
29.0k
        zip_uint8_t *data;
502
29.0k
        if (_zip_buffer_set_offset(buffer, offset - buffer_offset) < 0 || (data = _zip_buffer_get(buffer, MAGIC_LEN)) == NULL) {
503
18.5k
            return false;
504
18.5k
        }
505
10.5k
        return memcmp(data, magic, MAGIC_LEN) == 0;
506
29.0k
    }
507
2.34k
    else {
508
2.34k
        zip_uint8_t data[MAGIC_LEN];
509
510
2.34k
        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
2.34k
        return memcmp(data, magic, MAGIC_LEN) == 0;
514
2.34k
    }
515
31.4k
}
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
    /*
540
        Currently we use the presence of a Zip64 End of Central Directory do decide
541
        if the archive is Zip64 and use that to decide the size of data descriptors
542
        in local headers. This is not guaranteed to be correct. We could also assume
543
        Zip64 format if any of the central directory entries has a Zip64 extra field.
544
545
        Since we only read local headers when checking consistency, this is not a
546
        problem for normal use, but only when checking consistency for archives using
547
        data descriptors.
548
    */
549
550
0
    for (i = 0; i < cd->nentry; i++) {
551
0
        if (cd->entry[i].orig->offset < min) {
552
0
            min = cd->entry[i].orig->offset;
553
0
        }
554
0
        if (min > (zip_uint64_t)cd->offset) {
555
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
556
0
            return -1;
557
0
        }
558
559
0
        tail_length = _zip_string_length(cd->entry[i].orig->filename) + LENTRYSIZE;
560
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)) {
561
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
562
0
            return -1;
563
0
        }
564
0
        j = cd->entry[i].orig->offset + cd->entry[i].orig->comp_size + tail_length;
565
566
0
        if (j > max) {
567
0
            max = j;
568
0
        }
569
0
        if (max > (zip_uint64_t)cd->offset) {
570
0
            zip_error_set(error, ZIP_ER_NOZIP, 0);
571
0
            return -1;
572
0
        }
573
574
0
        if (zip_source_seek(za->src, (zip_int64_t)cd->entry[i].orig->offset, SEEK_SET) < 0) {
575
0
            zip_error_set_from_source(error, za->src);
576
0
            return -1;
577
0
        }
578
579
0
        if (_zip_dirent_read(&temp, za->src, NULL, true, cd->is_zip64, cd->entry[i].orig->comp_size, true, error) == -1) {
580
0
            if (zip_error_code_zip(error) == ZIP_ER_INCONS) {
581
0
                zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i));
582
0
            }
583
0
            _zip_dirent_finalize(&temp);
584
0
            return -1;
585
0
        }
586
587
0
        if (_zip_headercomp(cd->entry[i].orig, &temp) != 0) {
588
0
            zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_ENTRY_HEADER_MISMATCH, i));
589
0
            _zip_dirent_finalize(&temp);
590
0
            return -1;
591
0
        }
592
593
0
        cd->entry[i].orig->extra_fields.local = temp.extra_fields.local;
594
0
        cd->entry[i].orig->local_extra_fields_read = 1;
595
0
        temp.extra_fields.local = NULL;
596
597
0
        _zip_dirent_finalize(&temp);
598
599
0
        if ((detail = zip_dirent_check_consistency(cd->entry[i].orig)) != 0) {
600
0
            zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(detail, i));
601
0
            return -1;
602
0
        }
603
0
    }
604
605
0
    return (max - min) < ZIP_INT64_MAX ? (zip_int64_t)(max - min) : ZIP_INT64_MAX;
606
0
}
607
608
609
/* _zip_headercomp:
610
   compares a central directory entry and a local file header
611
   Return 0 if they are consistent, -1 if not. */
612
613
0
static int _zip_headercomp(const zip_dirent_t *central, const zip_dirent_t *local) {
614
0
    if ((central->version_needed < local->version_needed)
615
#if 0
616
  /* some zip-files have different values in local
617
     and global headers for the bitflags */
618
  || (central->bitflags != local->bitflags)
619
#endif
620
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))
621
0
        return -1;
622
623
0
    if ((central->crc != local->crc) || (central->comp_size != local->comp_size) || (central->uncomp_size != local->uncomp_size)) {
624
        /* InfoZip stores valid values in local header even when data descriptor is used.
625
           This is in violation of the appnote.
626
           macOS Archive sets the compressed size even when data descriptor is used ( but not the others),
627
           also in violation of the appnote.
628
        */
629
        /* if data descriptor is not used, the values must match */
630
0
        if ((local->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0) {
631
0
            return -1;
632
0
        }
633
        /* when using a data descriptor, the local header value must be zero or match */
634
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)) {
635
0
            return -1;
636
0
        }
637
0
    }
638
639
0
    return 0;
640
0
}
641
642
643
5.19k
static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error) {
644
5.19k
    zip_t *za;
645
646
5.19k
    if ((za = _zip_new(error)) == NULL) {
647
0
        return NULL;
648
0
    }
649
650
5.19k
    za->src = src;
651
5.19k
    za->open_flags = flags;
652
5.19k
    za->flags = 0;
653
5.19k
    za->ch_flags = 0;
654
5.19k
    za->write_crc = NULL;
655
656
5.19k
    if (flags & ZIP_RDONLY) {
657
0
        za->flags |= ZIP_AFL_RDONLY;
658
0
        za->ch_flags |= ZIP_AFL_RDONLY;
659
0
    }
660
661
5.19k
    return za;
662
5.19k
}
663
664
665
/*
666
 * tests for file existence
667
 */
668
5.19k
static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error) {
669
5.19k
    struct zip_stat st;
670
671
5.19k
    zip_stat_init(&st);
672
5.19k
    if (zip_source_stat(src, &st) != 0) {
673
0
        zip_error_t *src_error = zip_source_error(src);
674
0
        if (zip_error_code_zip(src_error) == ZIP_ER_READ && zip_error_code_system(src_error) == ENOENT) {
675
0
            return EXISTS_NOT;
676
0
        }
677
0
        _zip_error_copy(error, src_error);
678
0
        return EXISTS_ERROR;
679
0
    }
680
681
5.19k
    return EXISTS_OK;
682
5.19k
}
683
684
685
5.19k
static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len) {
686
5.19k
    zip_cdir_t *cdir;
687
5.19k
    const zip_uint8_t *match;
688
5.19k
    zip_int64_t buf_offset;
689
5.19k
    zip_uint64_t buflen;
690
5.19k
    zip_error_t error;
691
5.19k
    zip_buffer_t *buffer;
692
693
5.19k
    if (len < EOCDLEN) {
694
45
        zip_error_set(&za->error, ZIP_ER_NOZIP, 0);
695
45
        return NULL;
696
45
    }
697
698
5.14k
    buflen = (len < CDBUFSIZE ? len : CDBUFSIZE);
699
5.14k
    if (zip_source_seek(za->src, -(zip_int64_t)buflen, SEEK_END) < 0) {
700
0
        zip_error_t *src_error = zip_source_error(za->src);
701
0
        if (zip_error_code_zip(src_error) != ZIP_ER_SEEK || zip_error_code_system(src_error) != EFBIG) {
702
            /* seek before start of file on my machine */
703
0
            _zip_error_copy(&za->error, src_error);
704
0
            return NULL;
705
0
        }
706
0
    }
707
5.14k
    if ((buf_offset = zip_source_tell(za->src)) < 0) {
708
0
        zip_error_set_from_source(&za->error, za->src);
709
0
        return NULL;
710
0
    }
711
712
5.14k
    if ((buffer = _zip_buffer_new_from_source(za->src, buflen, NULL, &za->error)) == NULL) {
713
0
        return NULL;
714
0
    }
715
716
5.14k
    cdir = NULL;
717
5.14k
    if (buflen >= CDBUFSIZE) {
718
        /* EOCD64 locator is before EOCD, so leave place for it */
719
251
        _zip_buffer_set_offset(buffer, EOCD64LOCLEN);
720
251
    }
721
5.14k
    zip_error_set(&error, ZIP_ER_NOZIP, 0);
722
723
5.14k
    match = NULL;
724
38.5k
    while ((match = find_eocd(buffer, match)) != NULL) {
725
38.1k
        _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer)));
726
38.1k
        if (_zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &cdir, &error)) {
727
4.70k
            if (cdir != NULL && (za->open_flags & ZIP_CHECKCONS) && _zip_checkcons(za, cdir, &error) < 0) {
728
0
                _zip_cdir_free(cdir);
729
0
                cdir = NULL;
730
0
            }
731
4.70k
            break;
732
4.70k
        }
733
38.1k
    }
734
735
5.14k
    _zip_buffer_free(buffer);
736
737
5.14k
    if (cdir == NULL) {
738
2.68k
        _zip_error_copy(&za->error, &error);
739
2.68k
    }
740
5.14k
    return cdir;
741
5.14k
}
742
743
744
38.5k
static const unsigned char *find_eocd(zip_buffer_t *buffer, const unsigned char *last) {
745
38.5k
    const unsigned char *data = _zip_buffer_data(buffer);
746
38.5k
    const unsigned char *p;
747
748
38.5k
    if (last == NULL) {
749
5.14k
        if (_zip_buffer_size(buffer) < MAGIC_LEN) {
750
0
            return NULL;
751
0
        }
752
5.14k
        last = data + _zip_buffer_size(buffer) - MAGIC_LEN;
753
5.14k
    }
754
33.4k
    else {
755
33.4k
        if (last == _zip_buffer_data(buffer)) {
756
213
            return NULL;
757
213
        }
758
33.2k
        last -= 1;
759
33.2k
    }
760
761
5.12M
    for (p = last; p >= data; p -= 1) {
762
5.12M
        if (*p == EOCD_MAGIC[0]) {
763
172k
            if (memcmp(p, EOCD_MAGIC, MAGIC_LEN) == 0) {
764
38.1k
                return p;
765
38.1k
            }
766
172k
        }
767
5.08M
        if (p == data) {
768
            /* Avoid undefined behavior by creating pointer outside buffer */
769
232
            break;
770
232
        }
771
5.08M
    }
772
773
232
    return NULL;
774
38.3k
}
775
776
777
38.1k
static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) {
778
38.1k
    zip_cdir_t *cd;
779
780
38.1k
    if (_zip_buffer_left(buffer) < EOCDLEN) {
781
303
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD_LENGTH_INVALID);
782
303
        return NULL;
783
303
    }
784
785
37.8k
    if ((cd = _zip_cdir_new(error)) == NULL) {
786
0
        return NULL;
787
0
    }
788
789
37.8k
    cd->eocd_offset = buf_offset + _zip_buffer_offset(buffer);
790
    /* This function is only called where EOCD magic was found, so no need to check that here. */
791
37.8k
    _zip_buffer_skip(buffer, MAGIC_LEN);
792
37.8k
    cd->is_zip64 = false;
793
37.8k
    cd->this_disk = _zip_buffer_get_16(buffer);
794
37.8k
    cd->eocd_disk = _zip_buffer_get_16(buffer);
795
796
    /* number of cdir-entries on this disk */
797
37.8k
    cd->disk_entries = _zip_buffer_get_16(buffer);
798
    /* number of cdir-entries */
799
37.8k
    cd->num_entries = _zip_buffer_get_16(buffer);
800
37.8k
    cd->size = _zip_buffer_get_32(buffer);
801
37.8k
    cd->offset = _zip_buffer_get_32(buffer);
802
803
37.8k
    return cd;
804
37.8k
}
805
806
3.84k
static bool check_eocd(zip_cdir_t *cd, unsigned int flags, zip_error_t *error) {
807
3.84k
    if (cd->disk_entries != cd->num_entries || cd->this_disk != 0 || cd->eocd_disk != 0) {
808
120
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
809
120
        return false;
810
120
    }
811
812
3.72k
    if (cd->offset + cd->size < cd->offset) {
813
0
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
814
0
        return false;
815
0
    }
816
817
3.72k
    if ((flags & ZIP_CHECKCONS) && cd->offset + cd->size != cd->eocd_offset) {
818
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
819
0
        return false;
820
0
    }
821
822
3.72k
    return true;
823
3.72k
}
824
825
826
11.3k
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) {
827
11.3k
    zip_uint64_t offset;
828
11.3k
    zip_uint8_t eocd[EOCD64LEN];
829
11.3k
    zip_uint64_t eocd_offset;
830
11.3k
    zip_uint64_t size, nentry, i, eocdloc_offset;
831
11.3k
    bool free_buffer;
832
11.3k
    zip_uint32_t num_disks, eocd_disk, this_disk;
833
834
    /* The offset of the end of the buffer is less than ZIP_UINT64_MAX, so this can't overflow. */
835
11.3k
    eocdloc_offset = buf_offset + _zip_buffer_offset(buffer);
836
837
11.3k
    _zip_buffer_get(buffer, 4); /* magic already verified */
838
839
11.3k
    eocd_disk = _zip_buffer_get_32(buffer);
840
11.3k
    eocd_offset = _zip_buffer_get_64(buffer);
841
11.3k
    num_disks = _zip_buffer_get_32(buffer);
842
843
11.3k
    if (!check_magic(eocd_offset, buffer, buf_offset, src, EOCD64_MAGIC)) {
844
10.2k
        return CDIR_NOT_FOUND;
845
10.2k
    }
846
847
1.06k
    if (num_disks != 1) {
848
56
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
849
56
        return CDIR_INVALID;
850
56
    }
851
852
    /* valid seek value for start of EOCD */
853
1.01k
    if (eocd_offset > ZIP_INT64_MAX) {
854
0
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
855
0
        return CDIR_INVALID;
856
0
    }
857
858
    /* does EOCD fit before EOCD locator? */
859
1.01k
    if (eocd_offset + EOCD64LEN > eocdloc_offset) {
860
28
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
861
28
        return CDIR_INVALID;
862
28
    }
863
864
    /* make sure current position of buffer is beginning of EOCD */
865
983
    if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) {
866
952
        _zip_buffer_set_offset(buffer, eocd_offset - buf_offset);
867
952
        free_buffer = false;
868
952
    }
869
31
    else {
870
31
        if (zip_source_seek(src, (zip_int64_t)eocd_offset, SEEK_SET) < 0) {
871
0
            zip_error_set_from_source(error, src);
872
0
            return CDIR_INVALID;
873
0
        }
874
31
        if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) {
875
0
            return CDIR_INVALID;
876
0
        }
877
31
        free_buffer = true;
878
31
    }
879
880
983
    if (memcmp(_zip_buffer_get(buffer, 4), EOCD64_MAGIC, 4) != 0) {
881
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_WRONG_MAGIC);
882
0
        if (free_buffer) {
883
0
            _zip_buffer_free(buffer);
884
0
        }
885
0
        return CDIR_INVALID;
886
0
    }
887
888
    /* size of EOCD */
889
983
    size = _zip_buffer_get_64(buffer);
890
891
    /* Is there a hole between EOCD and EOCD locator, or do they overlap? Also check for overflow. */
892
983
    if ((flags & ZIP_CHECKCONS) && (ZIP_CHECK_ADD_OVERFLOW(size, eocd_offset + 12) || size + eocd_offset + 12 != eocdloc_offset)) {
893
0
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
894
0
        if (free_buffer) {
895
0
            _zip_buffer_free(buffer);
896
0
        }
897
0
        return CDIR_INVALID;
898
0
    }
899
900
983
    _zip_buffer_get(buffer, 4); /* skip version made by/needed */
901
902
983
    this_disk = _zip_buffer_get_32(buffer);
903
983
    if (_zip_buffer_get_32(buffer) != eocd_disk) {
904
78
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_LOCATOR_MISMATCH);
905
78
        if (free_buffer) {
906
11
            _zip_buffer_free(buffer);
907
11
        }
908
78
        return CDIR_INVALID;
909
78
    }
910
911
905
    i = _zip_buffer_get_64(buffer);
912
905
    nentry = _zip_buffer_get_64(buffer);
913
914
905
    if (nentry != i) {
915
100
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
916
100
        if (free_buffer) {
917
5
            _zip_buffer_free(buffer);
918
5
        }
919
100
        return CDIR_INVALID;
920
100
    }
921
922
805
    size = _zip_buffer_get_64(buffer);
923
805
    offset = _zip_buffer_get_64(buffer);
924
925
    /* did we read past the end of the buffer? */
926
805
    if (!_zip_buffer_ok(buffer)) {
927
0
        zip_error_set(error, ZIP_ER_INTERNAL, 0);
928
0
        if (free_buffer) {
929
0
            _zip_buffer_free(buffer);
930
0
        }
931
0
        return CDIR_INVALID;
932
0
    }
933
934
805
    if (free_buffer) {
935
15
        _zip_buffer_free(buffer);
936
15
    }
937
938
805
    if (offset > ZIP_INT64_MAX || offset + size < offset) {
939
69
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
940
69
        return CDIR_INVALID;
941
69
    }
942
943
736
    if (nentry > size / CDENTRYSIZE) {
944
82
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_INVALID);
945
82
        return CDIR_INVALID;
946
82
    }
947
948
654
    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)) {
949
443
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_MISMATCH);
950
443
        return CDIR_INVALID;
951
443
    }
952
953
211
    cdir->is_zip64 = true;
954
211
    cdir->size = size;
955
211
    cdir->offset = offset;
956
211
    cdir->disk_entries = i;
957
211
    cdir->num_entries = nentry;
958
211
    cdir->this_disk = this_disk;
959
211
    cdir->eocd_disk = eocd_disk;
960
211
    cdir->eocd_offset = eocd_offset;
961
962
211
    return CDIR_OK;
963
654
}
964
965
966
654
static int decode_hex(char c) {
967
654
    if (c >= '0' && c <= '9') {
968
370
        return c - '0';
969
370
    }
970
284
    else if (c >= 'A' && c <= 'F') {
971
225
        return c - 'A' + 10;
972
225
    }
973
59
    else {
974
59
        return -1;
975
59
    }
976
654
}
977
978
/* _zip_check_torrentzip:
979
   check whether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */
980
981
2.46k
static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir) {
982
2.46k
    zip_uint32_t crc_should;
983
2.46k
    char buf[8 + 1];
984
2.46k
    size_t i;
985
986
2.46k
    if (cdir == NULL) {
987
0
        return;
988
0
    }
989
990
2.46k
    if (_zip_string_length(cdir->comment) != TORRENTZIP_SIGNATURE_LENGTH + TORRENTZIP_CRC_LENGTH || strncmp((const char *)cdir->comment->raw, TORRENTZIP_SIGNATURE, TORRENTZIP_SIGNATURE_LENGTH) != 0) {
991
2.36k
        return;
992
2.36k
    }
993
994
98
    memcpy(buf, cdir->comment->raw + TORRENTZIP_SIGNATURE_LENGTH, TORRENTZIP_CRC_LENGTH);
995
98
    buf[TORRENTZIP_CRC_LENGTH] = '\0';
996
98
    crc_should = 0;
997
388
    for (i = 0; i < TORRENTZIP_CRC_LENGTH; i += 2) {
998
327
        int low, high;
999
327
        high = decode_hex((buf[i]));
1000
327
        low = decode_hex(buf[i + 1]);
1001
327
        if (high < 0 || low < 0) {
1002
37
            return;
1003
37
        }
1004
290
        crc_should = (crc_should << 8) + (high << 4) + low;
1005
290
    }
1006
1007
61
    {
1008
61
        zip_stat_t st;
1009
61
        zip_source_t *src_window;
1010
61
        zip_source_t *src_crc;
1011
61
        zip_uint8_t buffer[512];
1012
61
        zip_int64_t ret;
1013
1014
61
        zip_stat_init(&st);
1015
61
        st.valid |= ZIP_STAT_SIZE | ZIP_STAT_CRC;
1016
61
        st.size = cdir->size;
1017
61
        st.crc = crc_should;
1018
61
        if ((src_window = _zip_source_window_new(za->src, cdir->offset, cdir->size, &st, 0, NULL, NULL, NULL, 0, false, NULL)) == NULL) {
1019
0
            return;
1020
0
        }
1021
61
        if ((src_crc = zip_source_crc_create(src_window, 1, NULL)) == NULL) {
1022
0
            zip_source_free(src_window);
1023
0
            return;
1024
0
        }
1025
61
        if (zip_source_open(src_crc) != 0) {
1026
0
            zip_source_free(src_crc);
1027
0
            return;
1028
0
        }
1029
237
        while ((ret = zip_source_read(src_crc, buffer, sizeof(buffer))) > 0) {
1030
176
        }
1031
61
        zip_source_free(src_crc);
1032
61
        if (ret < 0) {
1033
60
            return;
1034
60
        }
1035
61
    }
1036
1037
    /* TODO: if check consistency, check cdir entries for valid values */
1038
1
    za->flags |= ZIP_AFL_IS_TORRENTZIP;
1039
1
}