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
5.26k
zip_open(const char *fn, int _flags, int *zep) {
69
5.26k
    zip_t *za;
70
5.26k
    zip_source_t *src;
71
5.26k
    struct zip_error error;
72
73
5.26k
    zip_error_init(&error);
74
5.26k
    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
5.26k
    if ((za = zip_open_from_source(src, _flags, &error)) == NULL) {
81
2.58k
        zip_source_free(src);
82
2.58k
        _zip_set_open_error(zep, &error, 0);
83
2.58k
        zip_error_fini(&error);
84
2.58k
        return NULL;
85
2.58k
    }
86
87
2.68k
    zip_error_fini(&error);
88
2.68k
    return za;
89
5.26k
}
90
91
92
ZIP_EXTERN zip_t *
93
10.0k
zip_open_from_source(zip_source_t *src, int _flags, zip_error_t *error) {
94
10.0k
    unsigned int flags;
95
10.0k
    zip_int64_t supported;
96
10.0k
    exists_t exists;
97
98
10.0k
    if (_flags < 0 || src == NULL) {
99
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
100
0
        return NULL;
101
0
    }
102
10.0k
    flags = (unsigned int)_flags;
103
104
10.0k
    supported = zip_source_supports(src);
105
10.0k
    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
10.0k
    if ((supported & ZIP_SOURCE_SUPPORTS_WRITABLE) != ZIP_SOURCE_SUPPORTS_WRITABLE) {
110
0
        flags |= ZIP_RDONLY;
111
0
    }
112
113
10.0k
    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
10.0k
    exists = _zip_file_exists(src, error);
119
10.0k
    switch (exists) {
120
0
    case EXISTS_ERROR:
121
0
        return NULL;
122
123
940
    case EXISTS_NOT:
124
940
        if ((flags & ZIP_CREATE) == 0) {
125
0
            zip_error_set(error, ZIP_ER_NOENT, 0);
126
0
            return NULL;
127
0
        }
128
940
        return _zip_allocate_new(src, flags, error);
129
130
9.10k
    default: {
131
9.10k
        zip_t *za;
132
9.10k
        if (flags & ZIP_EXCL) {
133
0
            zip_error_set(error, ZIP_ER_EXISTS, 0);
134
0
            return NULL;
135
0
        }
136
9.10k
        if (zip_source_open(src) < 0) {
137
0
            zip_error_set_from_source(error, src);
138
0
            return NULL;
139
0
        }
140
141
9.10k
        if (flags & ZIP_TRUNCATE) {
142
0
            za = _zip_allocate_new(src, flags, error);
143
0
        }
144
9.10k
        else {
145
            /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, just like open() */
146
9.10k
            za = _zip_open(src, flags, error);
147
9.10k
        }
148
149
9.10k
        if (za == NULL) {
150
5.22k
            zip_source_close(src);
151
5.22k
            return NULL;
152
5.22k
        }
153
3.87k
        return za;
154
9.10k
    }
155
10.0k
    }
156
10.0k
}
157
158
159
static bool
160
1.15k
_is_truncated_zip(zip_source_t *src) {
161
1.15k
    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
1.15k
    if (zip_source_seek(src, 0, SEEK_SET) < 0) {
165
0
        return false;
166
0
    }
167
168
1.15k
    if (zip_source_read(src, data, 4) != 4) {
169
6
        return false;
170
6
    }
171
172
1.15k
    if (memcmp(data, LOCAL_MAGIC, 4) == 0) {
173
        /* file starts with a ZIP local header signature */
174
24
        return true;
175
24
    }
176
1.12k
    return false;
177
1.15k
}
178
179
180
zip_t *
181
9.10k
_zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) {
182
9.10k
    zip_t *za;
183
9.10k
    zip_cdir_t *cdir;
184
9.10k
    struct zip_stat st;
185
9.10k
    zip_uint64_t len, idx;
186
187
9.10k
    zip_stat_init(&st);
188
9.10k
    if (zip_source_stat(src, &st) < 0) {
189
0
        zip_error_set_from_source(error, src);
190
0
        return NULL;
191
0
    }
192
9.10k
    if ((st.valid & ZIP_STAT_SIZE) == 0) {
193
0
        zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP);
194
0
        return NULL;
195
0
    }
196
9.10k
    len = st.size;
197
198
199
9.10k
    if ((za = _zip_allocate_new(src, flags, error)) == NULL) {
200
0
        return NULL;
201
0
    }
202
203
    /* treat empty files as empty archives */
204
9.10k
    if (len == 0 && zip_source_accept_empty(src)) {
205
0
        return za;
206
0
    }
207
208
9.10k
    if ((cdir = _zip_find_central_dir(za, len)) == NULL) {
209
5.22k
        _zip_error_copy(error, &za->error);
210
5.22k
        if (zip_error_code_zip(&za->error) == ZIP_ER_NOZIP) {
211
            /* not a zip - find out if it's truncated */
212
1.15k
            if (_is_truncated_zip(src)) {
213
24
                zip_error_set(error, ZIP_ER_TRUNCATED_ZIP, 0);
214
24
            }
215
1.15k
        }
216
        /* keep src so discard does not get rid of it */
217
5.22k
        zip_source_keep(src);
218
5.22k
        zip_discard(za);
219
5.22k
        return NULL;
220
5.22k
    }
221
222
3.87k
    za->entry = cdir->entry;
223
3.87k
    za->nentry = cdir->nentry;
224
3.87k
    za->nentry_alloc = cdir->nentry_alloc;
225
226
3.87k
    zip_check_torrentzip(za, cdir);
227
228
3.87k
    if (ZIP_IS_TORRENTZIP(za)) {
229
        /* Torrentzip uses the archive comment to detect changes by tools that are not torrentzip aware. */
230
2
        _zip_string_free(cdir->comment);
231
2
    }
232
3.87k
    else {
233
3.87k
        za->comment_orig = cdir->comment;
234
3.87k
    }
235
236
3.87k
    free(cdir);
237
238
3.87k
    _zip_hash_reserve_capacity(za->names, za->nentry, &za->error);
239
240
52.8k
    for (idx = 0; idx < za->nentry; idx++) {
241
48.9k
        const zip_uint8_t *name = _zip_string_get(za->entry[idx].orig->filename, NULL, 0, error);
242
48.9k
        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
48.9k
        if (_zip_hash_add(za->names, name, idx, ZIP_FL_UNCHANGED, &za->error) == false) {
250
35.5k
            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
35.5k
        }
258
48.9k
    }
259
260
3.87k
    za->ch_flags = za->flags;
261
262
3.87k
    return za;
263
3.87k
}
264
265
266
void
267
2.58k
_zip_set_open_error(int *zep, const zip_error_t *err, int ze) {
268
2.58k
    if (err) {
269
2.58k
        ze = zip_error_code_zip(err);
270
2.58k
        switch (zip_error_system_type(err)) {
271
126
        case ZIP_ET_SYS:
272
1.69k
        case ZIP_ET_LIBZIP:
273
1.69k
            errno = zip_error_code_system(err);
274
1.69k
            break;
275
276
891
        default:
277
891
            break;
278
2.58k
        }
279
2.58k
    }
280
281
2.58k
    if (zep)
282
2.58k
        *zep = ze;
283
2.58k
}
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
82.0k
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
82.0k
    zip_cdir_t *cd;
294
82.0k
    zip_uint16_t comment_len;
295
82.0k
    zip_uint64_t i, left;
296
82.0k
    zip_uint64_t eocd_offset = _zip_buffer_offset(buffer);
297
82.0k
    zip_buffer_t *cd_buffer;
298
82.0k
    bool eocd64_found = false;
299
300
82.0k
    *cdirp = NULL;
301
302
82.0k
    if ((cd = _zip_read_eocd(buffer, buf_offset, error)) == NULL) {
303
457
        return false;
304
457
    }
305
306
81.6k
    if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) {
307
10.8k
        eocd64_found = true;
308
10.8k
        _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN);
309
10.8k
        switch (_zip_read_eocd64(cd, za->src, buffer, buf_offset, za->flags, error)) {
310
377
        case CDIR_OK:
311
377
            break;
312
313
1.62k
        case CDIR_INVALID:
314
1.62k
            _zip_cdir_free(cd);
315
1.62k
            return true;
316
317
8.86k
        case CDIR_NOT_FOUND:
318
8.86k
            _zip_cdir_free(cd);
319
8.86k
            return false;
320
10.8k
        }
321
10.8k
    }
322
323
71.1k
    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
28.6k
        if (cd->this_disk < cd->eocd_disk) {
326
            /* Disks before the start of the central directory don't contain an EOCD. */
327
10.0k
            _zip_cdir_free(cd);
328
10.0k
            return false;
329
10.0k
        }
330
18.6k
        if (cd->size <= cd->eocd_offset) {
331
            /* The complete central directory would fit on this disk. */
332
5.38k
            _zip_cdir_free(cd);
333
5.38k
            return false;
334
5.38k
        }
335
18.6k
    }
336
337
55.6k
    if (!eocd64_found) {
338
55.2k
        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
651
        }
341
54.6k
        else if (!check_magic(cd->offset, buffer, buf_offset, za->src, CENTRAL_MAGIC)) {
342
49.1k
            _zip_cdir_free(cd);
343
49.1k
            return false;
344
49.1k
        }
345
55.2k
    }
346
347
    /* We accept this EOCD as valid and won't search for an earlier one if it is unusable. */
348
349
6.51k
    if (!check_eocd(cd, za->flags, error)) {
350
214
        _zip_cdir_free(cd);
351
214
        return true;
352
214
    }
353
354
6.29k
    _zip_buffer_set_offset(buffer, eocd_offset + 20);
355
6.29k
    comment_len = _zip_buffer_get_16(buffer);
356
357
6.29k
    if (cd->offset + cd->size > buf_offset + eocd_offset) {
358
        /* cdir spans past EOCD record */
359
293
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
360
293
        _zip_cdir_free(cd);
361
293
        return true;
362
293
    }
363
364
6.00k
    if (comment_len || (za->open_flags & ZIP_CHECKCONS)) {
365
4.61k
        zip_uint64_t tail_len;
366
367
4.61k
        _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN);
368
4.61k
        tail_len = _zip_buffer_left(buffer);
369
370
4.61k
        if (tail_len != comment_len) {
371
4.34k
            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
4.34k
            if (tail_len < comment_len) {
377
4.18k
                comment_len = tail_len;
378
4.18k
            }
379
4.34k
        }
380
381
4.61k
        if (comment_len) {
382
1.25k
            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
1.25k
        }
387
4.61k
    }
388
389
6.00k
    if (cd->offset >= buf_offset) {
390
5.73k
        zip_uint8_t *data;
391
        /* if buffer already read in, use it */
392
5.73k
        _zip_buffer_set_offset(buffer, cd->offset - buf_offset);
393
394
5.73k
        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
5.73k
        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
5.73k
    }
405
268
    else {
406
268
        cd_buffer = NULL;
407
408
268
        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
268
        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
268
    }
421
422
6.00k
    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
6.00k
    left = (zip_uint64_t)cd->size;
428
6.00k
    i = 0;
429
105k
    while (left > 0) {
430
101k
        bool grown = false;
431
101k
        zip_int64_t entry_size;
432
433
101k
        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
493
            if (cd->is_zip64 || left < CDENTRYSIZE) {
438
117
                break;
439
117
            }
440
441
376
            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
376
            grown = true;
447
376
        }
448
449
101k
        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
1.83k
            if (zip_error_code_zip(error) == ZIP_ER_INCONS) {
451
1.17k
                zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i));
452
1.17k
            }
453
664
            else if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) {
454
61
                zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_CDIR_ENTRY_INVALID, i));
455
61
            }
456
1.83k
            _zip_cdir_free(cd);
457
1.83k
            _zip_buffer_free(cd_buffer);
458
1.83k
            return true;
459
1.83k
        }
460
99.7k
        i++;
461
99.7k
        left -= (zip_uint64_t)entry_size;
462
99.7k
    }
463
464
    /* If we didn't fill all we grew, cd->num_entries was wrong. */
465
4.16k
    if (i != cd->nentry || left > 0) {
466
293
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_WRONG_ENTRIES_COUNT);
467
293
        _zip_buffer_free(cd_buffer);
468
293
        _zip_cdir_free(cd);
469
293
        return true;
470
293
    }
471
472
3.87k
    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
3.87k
    _zip_buffer_free(cd_buffer);
498
3.87k
    *cdirp = cd;
499
3.87k
    return true;
500
3.87k
}
501
502
503
65.5k
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
65.5k
    if (buffer_offset <= offset) {
505
56.6k
        zip_uint8_t* data;
506
56.6k
        if (_zip_buffer_set_offset(buffer, offset - buffer_offset) < 0 || (data = _zip_buffer_get(buffer, MAGIC_LEN)) == NULL) {
507
24.3k
            return false;
508
24.3k
        }
509
32.2k
        return memcmp(data, magic, MAGIC_LEN) == 0;
510
56.6k
    }
511
8.89k
    else {
512
8.89k
        zip_uint8_t data[MAGIC_LEN];
513
514
8.89k
        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
8.89k
        return memcmp(data, magic, MAGIC_LEN) == 0;
518
8.89k
    }
519
65.5k
}
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
10.0k
_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error) {
631
10.0k
    zip_t *za;
632
633
10.0k
    if ((za = _zip_new(error)) == NULL) {
634
0
        return NULL;
635
0
    }
636
637
10.0k
    za->src = src;
638
10.0k
    za->open_flags = flags;
639
10.0k
    za->flags = 0;
640
10.0k
    za->ch_flags = 0;
641
10.0k
    za->write_crc = NULL;
642
643
10.0k
    if (flags & ZIP_RDONLY) {
644
0
        za->flags |= ZIP_AFL_RDONLY;
645
0
        za->ch_flags |= ZIP_AFL_RDONLY;
646
0
    }
647
648
10.0k
    return za;
649
10.0k
}
650
651
652
/*
653
 * tests for file existence
654
 */
655
static exists_t
656
10.0k
_zip_file_exists(zip_source_t *src, zip_error_t *error) {
657
10.0k
    struct zip_stat st;
658
659
10.0k
    zip_stat_init(&st);
660
10.0k
    if (zip_source_stat(src, &st) != 0) {
661
940
        zip_error_t *src_error = zip_source_error(src);
662
940
        if (zip_error_code_zip(src_error) == ZIP_ER_READ && zip_error_code_system(src_error) == ENOENT) {
663
940
            return EXISTS_NOT;
664
940
        }
665
0
        _zip_error_copy(error, src_error);
666
0
        return EXISTS_ERROR;
667
940
    }
668
669
9.10k
    return EXISTS_OK;
670
10.0k
}
671
672
673
static zip_cdir_t *
674
9.10k
_zip_find_central_dir(zip_t *za, zip_uint64_t len) {
675
9.10k
    zip_cdir_t *cdir;
676
9.10k
    const zip_uint8_t *match;
677
9.10k
    zip_int64_t buf_offset;
678
9.10k
    zip_uint64_t buflen;
679
9.10k
    zip_error_t error;
680
9.10k
    zip_buffer_t *buffer;
681
682
9.10k
    if (len < EOCDLEN) {
683
90
        zip_error_set(&za->error, ZIP_ER_NOZIP, 0);
684
90
        return NULL;
685
90
    }
686
687
9.01k
    buflen = (len < CDBUFSIZE ? len : CDBUFSIZE);
688
9.01k
    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
9.01k
    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
9.01k
    if ((buffer = _zip_buffer_new_from_source(za->src, buflen, NULL, &za->error)) == NULL) {
702
0
        return NULL;
703
0
    }
704
705
9.01k
    cdir = NULL;
706
9.01k
    if (buflen >= CDBUFSIZE) {
707
        /* EOCD64 locator is before EOCD, so leave place for it */
708
350
        _zip_buffer_set_offset(buffer, EOCD64LOCLEN);
709
350
    }
710
9.01k
    zip_error_set(&error, ZIP_ER_NOZIP, 0);
711
712
9.01k
    match = NULL;
713
82.9k
    while ((match = find_eocd(buffer, match)) != NULL) {
714
82.0k
        _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer)));
715
82.0k
        if (_zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &cdir, &error)) {
716
8.13k
            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
8.13k
            break;
721
8.13k
        }
722
82.0k
    }
723
724
9.01k
    _zip_buffer_free(buffer);
725
726
9.01k
    if (cdir == NULL) {
727
5.13k
        _zip_error_copy(&za->error, &error);
728
5.13k
    }
729
9.01k
    return cdir;
730
9.01k
}
731
732
733
static const unsigned char *
734
82.9k
find_eocd(zip_buffer_t *buffer, const unsigned char *last) {
735
82.9k
    const unsigned char *data = _zip_buffer_data(buffer);
736
82.9k
    const unsigned char *p;
737
738
82.9k
    if (last == NULL) {
739
9.01k
        last = data + _zip_buffer_size(buffer) - MAGIC_LEN;
740
9.01k
    }
741
73.9k
    else if (last == _zip_buffer_data(buffer)) {
742
426
        return NULL;
743
426
    }
744
73.5k
    else {
745
73.5k
        last -= 1;
746
73.5k
    }
747
748
8.90M
    for (p = last; p >= data; p -= 1) {
749
8.90M
        if (*p == EOCD_MAGIC[0]) {
750
321k
            if (memcmp(p, EOCD_MAGIC, MAGIC_LEN) == 0) {
751
82.0k
                return p;
752
82.0k
            }
753
321k
        }
754
8.90M
    }
755
756
453
    return NULL;
757
82.5k
}
758
759
760
static zip_cdir_t *
761
82.0k
_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) {
762
82.0k
    zip_cdir_t *cd;
763
764
82.0k
    if (_zip_buffer_left(buffer) < EOCDLEN) {
765
457
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD_LENGTH_INVALID);
766
457
        return NULL;
767
457
    }
768
769
81.6k
    if ((cd = _zip_cdir_new(error)) == NULL) {
770
0
        return NULL;
771
0
    }
772
773
81.6k
    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
81.6k
    _zip_buffer_skip(buffer, MAGIC_LEN);
776
81.6k
    cd->is_zip64 = false;
777
81.6k
    cd->this_disk = _zip_buffer_get_16(buffer);
778
81.6k
    cd->eocd_disk = _zip_buffer_get_16(buffer);
779
780
    /* number of cdir-entries on this disk */
781
81.6k
    cd->disk_entries = _zip_buffer_get_16(buffer);
782
    /* number of cdir-entries */
783
81.6k
    cd->num_entries = _zip_buffer_get_16(buffer);
784
81.6k
    cd->size = _zip_buffer_get_32(buffer);
785
81.6k
    cd->offset = _zip_buffer_get_32(buffer);
786
787
81.6k
    return cd;
788
81.6k
}
789
790
static bool
791
6.51k
check_eocd(zip_cdir_t *cd, unsigned int flags, zip_error_t *error) {
792
6.51k
    if (cd->disk_entries != cd->num_entries || cd->this_disk != 0 || cd->eocd_disk != 0) {
793
214
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
794
214
        return false;
795
214
    }
796
797
6.29k
    if (cd->offset + cd->size < cd->offset) {
798
0
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
799
0
        return false;
800
0
    }
801
6.29k
    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
6.29k
    return true;
807
6.29k
}
808
809
810
10.8k
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
10.8k
    zip_uint64_t offset;
812
10.8k
    zip_uint8_t eocd[EOCD64LEN];
813
10.8k
    zip_uint64_t eocd_offset;
814
10.8k
    zip_uint64_t size, nentry, i, eocdloc_offset;
815
10.8k
    bool free_buffer;
816
10.8k
    zip_uint32_t num_disks, eocd_disk, this_disk;
817
818
10.8k
    eocdloc_offset = _zip_buffer_offset(buffer);
819
820
10.8k
    _zip_buffer_get(buffer, 4); /* magic already verified */
821
822
10.8k
    eocd_disk = _zip_buffer_get_32(buffer);
823
10.8k
    eocd_offset = _zip_buffer_get_64(buffer);
824
10.8k
    num_disks = _zip_buffer_get_32(buffer);
825
826
10.8k
    if (!check_magic(eocd_offset, buffer, buf_offset, src, EOCD64_MAGIC)) {
827
8.86k
        return CDIR_NOT_FOUND;
828
8.86k
    }
829
830
1.99k
    if (num_disks != 1) {
831
103
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
832
103
        return CDIR_INVALID;
833
103
    }
834
835
    /* valid seek value for start of EOCD */
836
1.89k
    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
1.89k
    if (eocd_offset + EOCD64LEN > eocdloc_offset + buf_offset) {
843
21
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
844
21
        return CDIR_INVALID;
845
21
    }
846
847
    /* make sure current position of buffer is beginning of EOCD */
848
1.87k
    if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) {
849
1.82k
        _zip_buffer_set_offset(buffer, eocd_offset - buf_offset);
850
1.82k
        free_buffer = false;
851
1.82k
    }
852
45
    else {
853
45
        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
45
        if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) {
858
0
            return CDIR_INVALID;
859
0
        }
860
45
        free_buffer = true;
861
45
    }
862
863
1.87k
    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
1.87k
    size = _zip_buffer_get_64(buffer);
873
874
    /* is there a hole between EOCD and EOCD locator, or do they overlap? */
875
1.87k
    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
1.87k
    _zip_buffer_get(buffer, 4); /* skip version made by/needed */
884
885
1.87k
    this_disk = _zip_buffer_get_32(buffer);
886
1.87k
    if (_zip_buffer_get_32(buffer) != eocd_disk) {
887
56
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_LOCATOR_MISMATCH);
888
56
        if (free_buffer) {
889
10
            _zip_buffer_free(buffer);
890
10
        }
891
56
        return CDIR_INVALID;
892
56
    }
893
894
1.81k
    i = _zip_buffer_get_64(buffer);
895
1.81k
    nentry = _zip_buffer_get_64(buffer);
896
897
1.81k
    if (nentry != i) {
898
209
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
899
209
        if (free_buffer) {
900
6
            _zip_buffer_free(buffer);
901
6
        }
902
209
        return CDIR_INVALID;
903
209
    }
904
905
1.60k
    size = _zip_buffer_get_64(buffer);
906
1.60k
    offset = _zip_buffer_get_64(buffer);
907
908
    /* did we read past the end of the buffer? */
909
1.60k
    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
1.60k
    if (free_buffer) {
918
29
        _zip_buffer_free(buffer);
919
29
    }
920
921
1.60k
    if (offset > ZIP_INT64_MAX || offset + size < offset) {
922
107
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
923
107
        return CDIR_INVALID;
924
107
    }
925
926
1.50k
    if (nentry > size / CDENTRYSIZE) {
927
155
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_INVALID);
928
155
        return CDIR_INVALID;
929
155
    }
930
931
1.34k
    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
970
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_MISMATCH);
933
970
        return CDIR_INVALID;
934
970
    }
935
936
377
    cdir->is_zip64 = true;
937
377
    cdir->size = size;
938
377
    cdir->offset = offset;
939
377
    cdir->disk_entries = i;
940
377
    cdir->num_entries = nentry;
941
377
    cdir->this_disk = this_disk;
942
377
    cdir->eocd_disk = eocd_disk;
943
944
377
    return CDIR_OK;
945
1.34k
}
946
947
948
static int
949
1.05k
decode_hex(char c) {
950
1.05k
    if (c >= '0' && c <= '9') {
951
561
        return c - '0';
952
561
    }
953
497
    else if (c >= 'A' && c <= 'F') {
954
382
        return c - 'A' + 10;
955
382
    }
956
115
    else {
957
115
        return -1;
958
115
    }
959
1.05k
}
960
961
/* _zip_check_torrentzip:
962
   check whether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */
963
964
static void
965
3.87k
zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir) {
966
3.87k
    zip_uint32_t crc_should;
967
3.87k
    char buf[8 + 1];
968
3.87k
    size_t i;
969
970
3.87k
    if (cdir == NULL) {
971
0
        return;
972
0
    }
973
974
3.87k
    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
3.70k
        return;
976
977
166
    memcpy(buf, cdir->comment->raw + TORRENTZIP_SIGNATURE_LENGTH, TORRENTZIP_CRC_LENGTH);
978
166
    buf[TORRENTZIP_CRC_LENGTH] = '\0';
979
166
    crc_should = 0;
980
620
    for (i = 0; i < TORRENTZIP_CRC_LENGTH; i += 2) {
981
529
        int low, high;
982
529
        high = decode_hex((buf[i]));
983
529
        low = decode_hex(buf[i + 1]);
984
529
        if (high < 0 || low < 0) {
985
75
            return;
986
75
        }
987
454
        crc_should = (crc_should << 8) + (high << 4) + low;
988
454
    }
989
990
91
    {
991
91
        zip_stat_t st;
992
91
        zip_source_t *src_window;
993
91
        zip_source_t *src_crc;
994
91
        zip_uint8_t buffer[512];
995
91
        zip_int64_t ret;
996
997
91
        zip_stat_init(&st);
998
91
        st.valid |= ZIP_STAT_SIZE | ZIP_STAT_CRC;
999
91
        st.size = cdir->size;
1000
91
        st.crc = crc_should;
1001
91
        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
91
        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
91
        if (zip_source_open(src_crc) != 0) {
1009
0
            zip_source_free(src_crc);
1010
0
            return;
1011
0
        }
1012
149
        while ((ret = zip_source_read(src_crc, buffer, sizeof(buffer))) > 0) {
1013
58
        }
1014
91
        zip_source_free(src_crc);
1015
91
        if (ret < 0) {
1016
89
            return;
1017
89
        }
1018
91
    }
1019
1020
    /* TODO: if check consistency, check cdir entries for valid values */
1021
2
    za->flags |= ZIP_AFL_IS_TORRENTZIP;
1022
2
}