Coverage Report

Created: 2026-09-04 07:03

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
4.58k
ZIP_EXTERN zip_t *zip_open(const char *fn, int _flags, int *zep) {
68
4.58k
    zip_t *za;
69
4.58k
    zip_source_t *src;
70
4.58k
    struct zip_error error;
71
72
4.58k
    zip_error_init(&error);
73
4.58k
    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
4.58k
    if ((za = zip_open_from_source(src, _flags, &error)) == NULL) {
80
2.73k
        zip_source_free(src);
81
2.73k
        _zip_set_open_error(zep, &error, 0);
82
2.73k
        zip_error_fini(&error);
83
2.73k
        return NULL;
84
2.73k
    }
85
86
1.84k
    zip_error_fini(&error);
87
1.84k
    return za;
88
4.58k
}
89
90
91
24.4k
ZIP_EXTERN zip_t *zip_open_from_source(zip_source_t *src, int _flags, zip_error_t *error) {
92
24.4k
    unsigned int flags;
93
24.4k
    zip_int64_t supported;
94
24.4k
    exists_t exists;
95
96
24.4k
    if (_flags < 0 || src == NULL) {
97
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
98
0
        return NULL;
99
0
    }
100
24.4k
    flags = (unsigned int)_flags;
101
102
24.4k
    supported = zip_source_supports(src);
103
24.4k
    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
24.4k
    if ((supported & ZIP_SOURCE_SUPPORTS_WRITABLE) != ZIP_SOURCE_SUPPORTS_WRITABLE) {
108
0
        flags |= ZIP_RDONLY;
109
0
    }
110
111
24.4k
    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
24.4k
    exists = _zip_file_exists(src, error);
117
24.4k
    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
24.4k
    default: {
129
24.4k
        zip_t *za;
130
24.4k
        if (flags & ZIP_EXCL) {
131
0
            zip_error_set(error, ZIP_ER_EXISTS, 0);
132
0
            return NULL;
133
0
        }
134
24.4k
        if (zip_source_open(src) < 0) {
135
197
            zip_error_set_from_source(error, src);
136
197
            return NULL;
137
197
        }
138
139
24.2k
        if (flags & ZIP_TRUNCATE) {
140
5.45k
            za = _zip_allocate_new(src, flags, error);
141
5.45k
        }
142
18.8k
        else {
143
            /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, just like open() */
144
18.8k
            za = _zip_open(src, flags, error);
145
18.8k
        }
146
147
24.2k
        if (za == NULL) {
148
10.0k
            zip_source_close(src);
149
10.0k
            return NULL;
150
10.0k
        }
151
14.2k
        return za;
152
24.2k
    }
153
24.4k
    }
154
24.4k
}
155
156
157
2.10k
static bool _is_truncated_zip(zip_source_t *src) {
158
2.10k
    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
2.10k
    if (zip_source_seek(src, 0, SEEK_SET) < 0) {
162
0
        return false;
163
0
    }
164
165
2.10k
    if (zip_source_read(src, data, 4) != 4) {
166
9
        return false;
167
9
    }
168
169
2.09k
    if (memcmp(data, LOCAL_MAGIC, 4) == 0) {
170
        /* file starts with a ZIP local header signature */
171
364
        return true;
172
364
    }
173
1.72k
    return false;
174
2.09k
}
175
176
177
18.8k
zip_t *_zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) {
178
18.8k
    zip_t *za;
179
18.8k
    zip_cdir_t *cdir;
180
18.8k
    struct zip_stat st;
181
18.8k
    zip_uint64_t len, idx;
182
183
18.8k
    zip_stat_init(&st);
184
18.8k
    if (zip_source_stat(src, &st) < 0) {
185
0
        zip_error_set_from_source(error, src);
186
0
        return NULL;
187
0
    }
188
18.8k
    if ((st.valid & ZIP_STAT_SIZE) == 0) {
189
0
        zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP);
190
0
        return NULL;
191
0
    }
192
18.8k
    len = st.size;
193
194
195
18.8k
    if ((za = _zip_allocate_new(src, flags, error)) == NULL) {
196
0
        return NULL;
197
0
    }
198
199
    /* treat empty files as empty archives */
200
18.8k
    if (len == 0 && zip_source_accept_empty(src)) {
201
0
        return za;
202
0
    }
203
204
18.8k
    if ((cdir = _zip_find_central_dir(za, len)) == NULL) {
205
10.0k
        _zip_error_copy(error, &za->error);
206
10.0k
        if (zip_error_code_zip(&za->error) == ZIP_ER_NOZIP) {
207
            /* not a zip - find out if it's truncated */
208
2.10k
            if (_is_truncated_zip(src)) {
209
364
                zip_error_set(error, ZIP_ER_TRUNCATED_ZIP, 0);
210
364
            }
211
2.10k
        }
212
        /* keep src so discard does not get rid of it */
213
10.0k
        zip_source_keep(src);
214
10.0k
        zip_discard(za);
215
10.0k
        return NULL;
216
10.0k
    }
217
218
8.80k
    za->entry = cdir->entry;
219
8.80k
    za->nentry = cdir->nentry;
220
8.80k
    za->nentry_alloc = cdir->nentry_alloc;
221
222
8.80k
    zip_check_torrentzip(za, cdir);
223
224
8.80k
    if (ZIP_IS_TORRENTZIP(za)) {
225
        /* Torrentzip uses the archive comment to detect changes by tools that are not torrentzip aware. */
226
3
        _zip_string_free(cdir->comment);
227
3
    }
228
8.80k
    else {
229
8.80k
        za->comment_orig = cdir->comment;
230
8.80k
    }
231
232
8.80k
    free(cdir);
233
234
8.80k
    _zip_hash_reserve_capacity(za->names, za->nentry, &za->error);
235
236
115k
    for (idx = 0; idx < za->nentry; idx++) {
237
106k
        const zip_uint8_t *name = _zip_string_get(za->entry[idx].orig->filename, NULL, 0, error);
238
106k
        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
106k
        if (_zip_hash_add(za->names, name, idx, ZIP_FL_UNCHANGED, &za->error) == false) {
246
48.0k
            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
48.0k
        }
254
106k
    }
255
256
8.80k
    za->ch_flags = za->flags;
257
258
8.80k
    return za;
259
8.80k
}
260
261
262
2.73k
void _zip_set_open_error(int *zep, const zip_error_t *err, int ze) {
263
2.73k
    if (err) {
264
2.73k
        ze = zip_error_code_zip(err);
265
2.73k
        switch (zip_error_system_type(err)) {
266
135
        case ZIP_ET_SYS:
267
1.79k
        case ZIP_ET_LIBZIP:
268
1.79k
            errno = zip_error_code_system(err);
269
1.79k
            break;
270
271
939
        default:
272
939
            break;
273
2.73k
        }
274
2.73k
    }
275
276
2.73k
    if (zep) {
277
2.73k
        *zep = ze;
278
2.73k
    }
279
2.73k
}
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
127k
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
127k
    zip_cdir_t *cd;
290
127k
    zip_uint16_t comment_len;
291
127k
    zip_uint64_t i, left;
292
127k
    zip_uint64_t eocd_offset = _zip_buffer_offset(buffer);
293
127k
    zip_buffer_t *cd_buffer;
294
127k
    bool eocd64_found = false;
295
296
127k
    *cdirp = NULL;
297
298
127k
    if ((cd = _zip_read_eocd(buffer, buf_offset, error)) == NULL) {
299
739
        return false;
300
739
    }
301
302
126k
    if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) {
303
31.3k
        eocd64_found = true;
304
31.3k
        _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN);
305
31.3k
        switch (_zip_read_eocd64(cd, za->src, buffer, buf_offset, za->open_flags, error)) {
306
721
        case CDIR_OK:
307
721
            break;
308
309
3.38k
        case CDIR_INVALID:
310
3.38k
            _zip_cdir_free(cd);
311
3.38k
            return true;
312
313
27.2k
        case CDIR_NOT_FOUND:
314
27.2k
            _zip_cdir_free(cd);
315
27.2k
            return false;
316
31.3k
        }
317
31.3k
    }
318
319
95.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
53.6k
        if (cd->this_disk < cd->eocd_disk) {
322
            /* Disks before the start of the central directory don't contain an EOCD. */
323
11.7k
            _zip_cdir_free(cd);
324
11.7k
            return false;
325
11.7k
        }
326
41.9k
        if (cd->size <= cd->eocd_offset) {
327
            /* The complete central directory would fit on this disk. */
328
7.60k
            _zip_cdir_free(cd);
329
7.60k
            return false;
330
7.60k
        }
331
41.9k
    }
332
333
76.2k
    if (!eocd64_found) {
334
75.5k
        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
1.22k
        }
337
74.3k
        else if (!check_magic(cd->offset, buffer, buf_offset, za->src, CENTRAL_MAGIC)) {
338
62.5k
            _zip_cdir_free(cd);
339
62.5k
            return false;
340
62.5k
        }
341
75.5k
    }
342
343
    /* We accept this EOCD as valid and won't search for an earlier one if it is unusable. */
344
345
13.7k
    if (!check_eocd(cd, za->open_flags, error)) {
346
423
        _zip_cdir_free(cd);
347
423
        return true;
348
423
    }
349
350
13.3k
    _zip_buffer_set_offset(buffer, eocd_offset + 20);
351
13.3k
    comment_len = _zip_buffer_get_16(buffer);
352
353
13.3k
    if (cd->offset + cd->size > buf_offset + eocd_offset) {
354
        /* cdir spans past EOCD record */
355
551
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_OVERLAPS_EOCD);
356
551
        _zip_cdir_free(cd);
357
551
        return true;
358
551
    }
359
360
12.7k
    if (comment_len || (za->open_flags & ZIP_CHECKCONS)) {
361
9.12k
        zip_uint64_t tail_len;
362
363
9.12k
        _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN);
364
9.12k
        tail_len = _zip_buffer_left(buffer);
365
366
9.12k
        if (tail_len != comment_len) {
367
8.38k
            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
8.38k
            if (tail_len < comment_len) {
373
8.04k
                comment_len = tail_len;
374
8.04k
            }
375
8.38k
        }
376
377
9.12k
        if (comment_len) {
378
4.21k
            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
4.21k
        }
383
9.12k
    }
384
385
12.7k
    if (cd->offset >= buf_offset) {
386
12.2k
        zip_uint8_t *data;
387
        /* if buffer already read in, use it */
388
12.2k
        _zip_buffer_set_offset(buffer, cd->offset - buf_offset);
389
390
12.2k
        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
12.2k
        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
12.2k
    }
401
499
    else {
402
499
        cd_buffer = NULL;
403
404
499
        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
499
        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
499
    }
417
418
12.7k
    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
12.7k
    left = (zip_uint64_t)cd->size;
424
12.7k
    i = 0;
425
281k
    while (left > 0) {
426
272k
        bool grown = false;
427
272k
        zip_int64_t entry_size;
428
429
272k
        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
1.56k
            if (cd->is_zip64 || left < CDENTRYSIZE) {
434
184
                break;
435
184
            }
436
437
1.38k
            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
1.38k
            grown = true;
443
1.38k
        }
444
445
272k
        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
3.45k
            if (zip_error_code_zip(error) == ZIP_ER_INCONS) {
447
2.19k
                zip_error_set(error, ZIP_ER_INCONS, ADD_INDEX_TO_DETAIL(zip_error_code_system(error), i));
448
2.19k
            }
449
1.25k
            else if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) {
450
105
                zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_CDIR_ENTRY_INVALID, i));
451
105
            }
452
3.45k
            _zip_cdir_free(cd);
453
3.45k
            _zip_buffer_free(cd_buffer);
454
3.45k
            return true;
455
3.45k
        }
456
268k
        i++;
457
268k
        left -= (zip_uint64_t)entry_size;
458
268k
    }
459
460
    /* If we didn't fill all we grew, cd->num_entries was wrong. */
461
9.31k
    if (i != cd->nentry || left > 0) {
462
512
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_WRONG_ENTRIES_COUNT);
463
512
        _zip_buffer_free(cd_buffer);
464
512
        _zip_cdir_free(cd);
465
512
        return true;
466
512
    }
467
468
8.80k
    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
8.80k
    _zip_buffer_free(cd_buffer);
494
8.80k
    *cdirp = cd;
495
8.80k
    return true;
496
8.80k
}
497
498
499
105k
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
105k
    if (buffer_offset <= offset) {
501
97.9k
        zip_uint8_t *data;
502
97.9k
        if (_zip_buffer_set_offset(buffer, offset - buffer_offset) < 0 || (data = _zip_buffer_get(buffer, MAGIC_LEN)) == NULL) {
503
65.5k
            return false;
504
65.5k
        }
505
32.4k
        return memcmp(data, magic, MAGIC_LEN) == 0;
506
97.9k
    }
507
7.70k
    else {
508
7.70k
        zip_uint8_t data[MAGIC_LEN];
509
510
7.70k
        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
7.70k
        return memcmp(data, magic, MAGIC_LEN) == 0;
514
7.70k
    }
515
105k
}
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
24.2k
static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error) {
644
24.2k
    zip_t *za;
645
646
24.2k
    if ((za = _zip_new(error)) == NULL) {
647
0
        return NULL;
648
0
    }
649
650
24.2k
    za->src = src;
651
24.2k
    za->open_flags = flags;
652
24.2k
    za->flags = 0;
653
24.2k
    za->ch_flags = 0;
654
24.2k
    za->write_crc = NULL;
655
656
24.2k
    if (flags & ZIP_RDONLY) {
657
3.81k
        za->flags |= ZIP_AFL_RDONLY;
658
3.81k
        za->ch_flags |= ZIP_AFL_RDONLY;
659
3.81k
    }
660
661
24.2k
    return za;
662
24.2k
}
663
664
665
/*
666
 * tests for file existence
667
 */
668
24.4k
static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error) {
669
24.4k
    struct zip_stat st;
670
671
24.4k
    zip_stat_init(&st);
672
24.4k
    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
24.4k
    return EXISTS_OK;
682
24.4k
}
683
684
685
18.8k
static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len) {
686
18.8k
    zip_cdir_t *cdir;
687
18.8k
    const zip_uint8_t *match;
688
18.8k
    zip_int64_t buf_offset;
689
18.8k
    zip_uint64_t buflen;
690
18.8k
    zip_error_t error;
691
18.8k
    zip_buffer_t *buffer;
692
693
18.8k
    if (len < EOCDLEN) {
694
139
        zip_error_set(&za->error, ZIP_ER_NOZIP, 0);
695
139
        return NULL;
696
139
    }
697
698
18.6k
    buflen = (len < CDBUFSIZE ? len : CDBUFSIZE);
699
18.6k
    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
18.6k
    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
18.6k
    if ((buffer = _zip_buffer_new_from_source(za->src, buflen, NULL, &za->error)) == NULL) {
713
0
        return NULL;
714
0
    }
715
716
18.6k
    cdir = NULL;
717
18.6k
    if (buflen >= CDBUFSIZE) {
718
        /* EOCD64 locator is before EOCD, so leave place for it */
719
801
        _zip_buffer_set_offset(buffer, EOCD64LOCLEN);
720
801
    }
721
18.6k
    zip_error_set(&error, ZIP_ER_NOZIP, 0);
722
723
18.6k
    match = NULL;
724
128k
    while ((match = find_eocd(buffer, match)) != NULL) {
725
127k
        _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer)));
726
127k
        if (_zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &cdir, &error)) {
727
17.1k
            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
17.1k
            break;
732
17.1k
        }
733
127k
    }
734
735
18.6k
    _zip_buffer_free(buffer);
736
737
18.6k
    if (cdir == NULL) {
738
9.86k
        _zip_error_copy(&za->error, &error);
739
9.86k
    }
740
18.6k
    return cdir;
741
18.6k
}
742
743
744
128k
static const unsigned char *find_eocd(zip_buffer_t *buffer, const unsigned char *last) {
745
128k
    const unsigned char *data = _zip_buffer_data(buffer);
746
128k
    const unsigned char *p;
747
748
128k
    if (last == NULL) {
749
18.6k
        if (_zip_buffer_size(buffer) < MAGIC_LEN) {
750
0
            return NULL;
751
0
        }
752
18.6k
        last = data + _zip_buffer_size(buffer) - MAGIC_LEN;
753
18.6k
    }
754
109k
    else {
755
109k
        if (last == _zip_buffer_data(buffer)) {
756
646
            return NULL;
757
646
        }
758
109k
        last -= 1;
759
109k
    }
760
761
17.0M
    for (p = last; p >= data; p -= 1) {
762
17.0M
        if (*p == EOCD_MAGIC[0]) {
763
486k
            if (memcmp(p, EOCD_MAGIC, MAGIC_LEN) == 0) {
764
127k
                return p;
765
127k
            }
766
486k
        }
767
16.8M
        if (p == data) {
768
            /* Avoid undefined behavior by creating pointer outside buffer */
769
903
            break;
770
903
        }
771
16.8M
    }
772
773
903
    return NULL;
774
127k
}
775
776
777
127k
static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) {
778
127k
    zip_cdir_t *cd;
779
780
127k
    if (_zip_buffer_left(buffer) < EOCDLEN) {
781
739
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD_LENGTH_INVALID);
782
739
        return NULL;
783
739
    }
784
785
126k
    if ((cd = _zip_cdir_new(error)) == NULL) {
786
0
        return NULL;
787
0
    }
788
789
126k
    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
126k
    _zip_buffer_skip(buffer, MAGIC_LEN);
792
126k
    cd->is_zip64 = false;
793
126k
    cd->this_disk = _zip_buffer_get_16(buffer);
794
126k
    cd->eocd_disk = _zip_buffer_get_16(buffer);
795
796
    /* number of cdir-entries on this disk */
797
126k
    cd->disk_entries = _zip_buffer_get_16(buffer);
798
    /* number of cdir-entries */
799
126k
    cd->num_entries = _zip_buffer_get_16(buffer);
800
126k
    cd->size = _zip_buffer_get_32(buffer);
801
126k
    cd->offset = _zip_buffer_get_32(buffer);
802
803
126k
    return cd;
804
126k
}
805
806
13.7k
static bool check_eocd(zip_cdir_t *cd, unsigned int flags, zip_error_t *error) {
807
13.7k
    if (cd->disk_entries != cd->num_entries || cd->this_disk != 0 || cd->eocd_disk != 0) {
808
423
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
809
423
        return false;
810
423
    }
811
812
13.3k
    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
13.3k
    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
13.3k
    return true;
823
13.3k
}
824
825
826
31.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
31.3k
    zip_uint64_t offset;
828
31.3k
    zip_uint8_t eocd[EOCD64LEN];
829
31.3k
    zip_uint64_t eocd_offset;
830
31.3k
    zip_uint64_t size, nentry, i, eocdloc_offset;
831
31.3k
    bool free_buffer;
832
31.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
31.3k
    eocdloc_offset = buf_offset + _zip_buffer_offset(buffer);
836
837
31.3k
    _zip_buffer_get(buffer, 4); /* magic already verified */
838
839
31.3k
    eocd_disk = _zip_buffer_get_32(buffer);
840
31.3k
    eocd_offset = _zip_buffer_get_64(buffer);
841
31.3k
    num_disks = _zip_buffer_get_32(buffer);
842
843
31.3k
    if (!check_magic(eocd_offset, buffer, buf_offset, src, EOCD64_MAGIC)) {
844
27.2k
        return CDIR_NOT_FOUND;
845
27.2k
    }
846
847
4.10k
    if (num_disks != 1) {
848
190
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
849
190
        return CDIR_INVALID;
850
190
    }
851
852
    /* valid seek value for start of EOCD */
853
3.91k
    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
3.91k
    if (eocd_offset + EOCD64LEN > eocdloc_offset) {
860
104
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_OVERLAPS_EOCD);
861
104
        return CDIR_INVALID;
862
104
    }
863
864
    /* make sure current position of buffer is beginning of EOCD */
865
3.80k
    if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) {
866
3.73k
        _zip_buffer_set_offset(buffer, eocd_offset - buf_offset);
867
3.73k
        free_buffer = false;
868
3.73k
    }
869
72
    else {
870
72
        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
72
        if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) {
875
0
            return CDIR_INVALID;
876
0
        }
877
72
        free_buffer = true;
878
72
    }
879
880
3.80k
    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
3.80k
    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
3.80k
    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
3.80k
    _zip_buffer_get(buffer, 4); /* skip version made by/needed */
901
902
3.80k
    this_disk = _zip_buffer_get_32(buffer);
903
3.80k
    if (_zip_buffer_get_32(buffer) != eocd_disk) {
904
299
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_LOCATOR_MISMATCH);
905
299
        if (free_buffer) {
906
24
            _zip_buffer_free(buffer);
907
24
        }
908
299
        return CDIR_INVALID;
909
299
    }
910
911
3.50k
    i = _zip_buffer_get_64(buffer);
912
3.50k
    nentry = _zip_buffer_get_64(buffer);
913
914
3.50k
    if (nentry != i) {
915
410
        zip_error_set(error, ZIP_ER_MULTIDISK, 0);
916
410
        if (free_buffer) {
917
18
            _zip_buffer_free(buffer);
918
18
        }
919
410
        return CDIR_INVALID;
920
410
    }
921
922
3.09k
    size = _zip_buffer_get_64(buffer);
923
3.09k
    offset = _zip_buffer_get_64(buffer);
924
925
    /* did we read past the end of the buffer? */
926
3.09k
    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
3.09k
    if (free_buffer) {
935
30
        _zip_buffer_free(buffer);
936
30
    }
937
938
3.09k
    if (offset > ZIP_INT64_MAX || offset + size < offset) {
939
305
        zip_error_set(error, ZIP_ER_SEEK, EFBIG);
940
305
        return CDIR_INVALID;
941
305
    }
942
943
2.79k
    if (nentry > size / CDENTRYSIZE) {
944
296
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_CDIR_INVALID);
945
296
        return CDIR_INVALID;
946
296
    }
947
948
2.49k
    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
1.77k
        zip_error_set(error, ZIP_ER_INCONS, ZIP_ER_DETAIL_EOCD64_MISMATCH);
950
1.77k
        return CDIR_INVALID;
951
1.77k
    }
952
953
721
    cdir->is_zip64 = true;
954
721
    cdir->size = size;
955
721
    cdir->offset = offset;
956
721
    cdir->disk_entries = i;
957
721
    cdir->num_entries = nentry;
958
721
    cdir->this_disk = this_disk;
959
721
    cdir->eocd_disk = eocd_disk;
960
721
    cdir->eocd_offset = eocd_offset;
961
962
721
    return CDIR_OK;
963
2.49k
}
964
965
966
2.72k
static int decode_hex(char c) {
967
2.72k
    if (c >= '0' && c <= '9') {
968
1.62k
        return c - '0';
969
1.62k
    }
970
1.09k
    else if (c >= 'A' && c <= 'F') {
971
891
        return c - 'A' + 10;
972
891
    }
973
207
    else {
974
207
        return -1;
975
207
    }
976
2.72k
}
977
978
/* _zip_check_torrentzip:
979
   check whether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */
980
981
8.80k
static void zip_check_torrentzip(zip_t *za, const zip_cdir_t *cdir) {
982
8.80k
    zip_uint32_t crc_should;
983
8.80k
    char buf[8 + 1];
984
8.80k
    size_t i;
985
986
8.80k
    if (cdir == NULL) {
987
0
        return;
988
0
    }
989
990
8.80k
    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
8.39k
        return;
992
8.39k
    }
993
994
407
    memcpy(buf, cdir->comment->raw + TORRENTZIP_SIGNATURE_LENGTH, TORRENTZIP_CRC_LENGTH);
995
407
    buf[TORRENTZIP_CRC_LENGTH] = '\0';
996
407
    crc_should = 0;
997
1.63k
    for (i = 0; i < TORRENTZIP_CRC_LENGTH; i += 2) {
998
1.36k
        int low, high;
999
1.36k
        high = decode_hex((buf[i]));
1000
1.36k
        low = decode_hex(buf[i + 1]);
1001
1.36k
        if (high < 0 || low < 0) {
1002
138
            return;
1003
138
        }
1004
1.22k
        crc_should = (crc_should << 8) + (high << 4) + low;
1005
1.22k
    }
1006
1007
269
    {
1008
269
        zip_stat_t st;
1009
269
        zip_source_t *src_window;
1010
269
        zip_source_t *src_crc;
1011
269
        zip_uint8_t buffer[512];
1012
269
        zip_int64_t ret;
1013
1014
269
        zip_stat_init(&st);
1015
269
        st.valid |= ZIP_STAT_SIZE | ZIP_STAT_CRC;
1016
269
        st.size = cdir->size;
1017
269
        st.crc = crc_should;
1018
269
        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
269
        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
269
        if (zip_source_open(src_crc) != 0) {
1026
0
            zip_source_free(src_crc);
1027
0
            return;
1028
0
        }
1029
786
        while ((ret = zip_source_read(src_crc, buffer, sizeof(buffer))) > 0) {
1030
517
        }
1031
269
        zip_source_free(src_crc);
1032
269
        if (ret < 0) {
1033
266
            return;
1034
266
        }
1035
269
    }
1036
1037
    /* TODO: if check consistency, check cdir entries for valid values */
1038
3
    za->flags |= ZIP_AFL_IS_TORRENTZIP;
1039
3
}