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