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