Coverage Report

Created: 2025-11-16 06:38

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