527
|
1 /* |
|
2 zip_open.c -- open zip archive by name |
|
3 Copyright (C) 1999-2015 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 <libzip@nih.at> |
|
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 |
|
35 #include <sys/stat.h> |
|
36 #include <limits.h> |
|
37 #include <stdio.h> |
|
38 #include <stdlib.h> |
|
39 #include <string.h> |
|
40 |
|
41 #include "zipint.h" |
|
42 |
|
43 typedef enum { |
|
44 EXISTS_ERROR = -1, |
|
45 EXISTS_NOT = 0, |
|
46 EXISTS_EMPTY, |
|
47 EXISTS_NONEMPTY, |
|
48 } exists_t; |
|
49 static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error); |
|
50 static zip_int64_t _zip_checkcons(zip_t *za, zip_cdir_t *cdir, zip_error_t *error); |
|
51 static zip_cdir_t *_zip_find_central_dir(zip_t *za, zip_uint64_t len); |
|
52 static exists_t _zip_file_exists(zip_source_t *src, zip_error_t *error); |
|
53 static int _zip_headercomp(const zip_dirent_t *, const zip_dirent_t *); |
|
54 static unsigned char *_zip_memmem(const unsigned char *, size_t, const unsigned char *, size_t); |
|
55 static zip_cdir_t *_zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error); |
|
56 static zip_cdir_t *_zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error); |
|
57 static zip_cdir_t *_zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error); |
|
58 |
|
59 |
|
60 ZIP_EXTERN zip_t * |
|
61 zip_open(const char *fn, int _flags, int *zep) |
|
62 { |
|
63 zip_t *za; |
|
64 zip_source_t *src; |
|
65 struct zip_error error; |
|
66 |
|
67 zip_error_init(&error); |
|
68 if ((src = zip_source_file_create(fn, 0, -1, &error)) == NULL) { |
|
69 _zip_set_open_error(zep, &error, 0); |
|
70 zip_error_fini(&error); |
|
71 return NULL; |
|
72 } |
|
73 |
|
74 if ((za = zip_open_from_source(src, _flags, &error)) == NULL) { |
|
75 zip_source_free(src); |
|
76 _zip_set_open_error(zep, &error, 0); |
|
77 zip_error_fini(&error); |
|
78 return NULL; |
|
79 } |
|
80 |
|
81 zip_error_fini(&error); |
|
82 return za; |
|
83 } |
|
84 |
|
85 |
|
86 ZIP_EXTERN zip_t * |
|
87 zip_open_from_source(zip_source_t *src, int _flags, zip_error_t *error) |
|
88 { |
|
89 static zip_int64_t needed_support_read = -1; |
|
90 static zip_int64_t needed_support_write = -1; |
|
91 |
|
92 unsigned int flags; |
|
93 zip_int64_t supported; |
|
94 exists_t exists; |
|
95 |
|
96 if (_flags < 0 || src == NULL) { |
|
97 zip_error_set(error, ZIP_ER_INVAL, 0); |
|
98 return NULL; |
|
99 } |
|
100 flags = (unsigned int)_flags; |
|
101 |
|
102 supported = zip_source_supports(src); |
|
103 if (needed_support_read == -1) { |
|
104 needed_support_read = zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_STAT, -1); |
|
105 needed_support_write = zip_source_make_command_bitmap(ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_REMOVE, -1); |
|
106 } |
|
107 if ((supported & needed_support_read) != needed_support_read) { |
|
108 zip_error_set(error, ZIP_ER_OPNOTSUPP, 0); |
|
109 return NULL; |
|
110 } |
|
111 if ((supported & needed_support_write) != needed_support_write) { |
|
112 flags |= ZIP_RDONLY; |
|
113 } |
|
114 |
|
115 if ((flags & (ZIP_RDONLY|ZIP_TRUNCATE)) == (ZIP_RDONLY|ZIP_TRUNCATE)) { |
|
116 zip_error_set(error, ZIP_ER_RDONLY, 0); |
|
117 return NULL; |
|
118 } |
|
119 |
|
120 exists = _zip_file_exists(src, error); |
|
121 switch (exists) { |
|
122 case EXISTS_ERROR: |
|
123 return NULL; |
|
124 |
|
125 case EXISTS_NOT: |
|
126 if ((flags & ZIP_CREATE) == 0) { |
|
127 zip_error_set(error, ZIP_ER_NOENT, 0); |
|
128 return NULL; |
|
129 } |
|
130 return _zip_allocate_new(src, flags, error); |
|
131 |
|
132 default: { |
|
133 zip_t *za; |
|
134 if (flags & ZIP_EXCL) { |
|
135 zip_error_set(error, ZIP_ER_EXISTS, 0); |
|
136 return NULL; |
|
137 } |
|
138 if (zip_source_open(src) < 0) { |
|
139 _zip_error_set_from_source(error, src); |
|
140 return NULL; |
|
141 } |
|
142 |
|
143 if (flags & ZIP_TRUNCATE) { |
|
144 za = _zip_allocate_new(src, flags, error); |
|
145 } |
|
146 else { |
|
147 /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, just like open() */ |
|
148 za = _zip_open(src, flags, error); |
|
149 } |
|
150 |
|
151 if (za == NULL) { |
|
152 zip_source_close(src); |
|
153 return NULL; |
|
154 } |
|
155 return za; |
|
156 } |
|
157 } |
|
158 } |
|
159 |
|
160 ZIP_EXTERN int |
|
161 zip_archive_set_tempdir(zip_t *za, const char *tempdir) |
|
162 { |
|
163 char *new_tempdir; |
|
164 |
|
165 if (tempdir) { |
|
166 if ((new_tempdir = strdup(tempdir)) == NULL) { |
|
167 zip_error_set(&za->error, ZIP_ER_MEMORY, errno); |
|
168 return -1; |
|
169 } |
|
170 } |
|
171 else |
|
172 new_tempdir = NULL; |
|
173 |
|
174 free(za->tempdir); |
|
175 za->tempdir = new_tempdir; |
|
176 |
|
177 return 0; |
|
178 } |
|
179 |
|
180 zip_t * |
|
181 _zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) |
|
182 { |
|
183 zip_t *za; |
|
184 zip_cdir_t *cdir; |
|
185 struct zip_stat st; |
|
186 zip_uint64_t len, idx; |
|
187 |
|
188 zip_stat_init(&st); |
|
189 if (zip_source_stat(src, &st) < 0) { |
|
190 _zip_error_set_from_source(error, src); |
|
191 return NULL; |
|
192 } |
|
193 if ((st.valid & ZIP_STAT_SIZE) == 0) { |
|
194 zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP); |
|
195 return NULL; |
|
196 } |
|
197 len = st.size; |
|
198 |
|
199 /* treat empty files as empty archives */ |
|
200 if (len == 0) { |
|
201 if ((za=_zip_allocate_new(src, flags, error)) == NULL) { |
|
202 zip_source_free(src); |
|
203 return NULL; |
|
204 } |
|
205 |
|
206 return za; |
|
207 } |
|
208 |
|
209 if ((za=_zip_allocate_new(src, flags, error)) == NULL) { |
|
210 return NULL; |
|
211 } |
|
212 |
|
213 if ((cdir = _zip_find_central_dir(za, len)) == NULL) { |
|
214 _zip_error_copy(error, &za->error); |
|
215 /* keep src so discard does not get rid of it */ |
|
216 zip_source_keep(src); |
|
217 zip_discard(za); |
|
218 return NULL; |
|
219 } |
|
220 |
|
221 za->entry = cdir->entry; |
|
222 za->nentry = cdir->nentry; |
|
223 za->nentry_alloc = cdir->nentry_alloc; |
|
224 za->comment_orig = cdir->comment; |
|
225 |
|
226 free(cdir); |
|
227 |
|
228 for (idx = 0; idx < za->nentry; idx++) { |
|
229 const zip_uint8_t *name = _zip_string_get(za->entry[idx].orig->filename, NULL, 0, error); |
|
230 if (name == NULL) { |
|
231 /* keep src so discard does not get rid of it */ |
|
232 zip_source_keep(src); |
|
233 zip_discard(za); |
|
234 return NULL; |
|
235 } |
|
236 |
|
237 if (_zip_hash_add(za->names, name, idx, ZIP_FL_UNCHANGED, &za->error) == false) { |
|
238 if (za->error.zip_err != ZIP_ER_EXISTS || (flags & ZIP_CHECKCONS)) { |
|
239 _zip_error_copy(error, &za->error); |
|
240 /* keep src so discard does not get rid of it */ |
|
241 zip_source_keep(src); |
|
242 zip_discard(za); |
|
243 return NULL; |
|
244 } |
|
245 } |
|
246 } |
|
247 |
|
248 za->ch_flags = za->flags; |
|
249 |
|
250 return za; |
|
251 } |
|
252 |
|
253 |
|
254 void |
|
255 _zip_set_open_error(int *zep, const zip_error_t *err, int ze) |
|
256 { |
|
257 if (err) { |
|
258 ze = zip_error_code_zip(err); |
|
259 if (zip_error_system_type(err) == ZIP_ET_SYS) { |
|
260 errno = zip_error_code_system(err); |
|
261 } |
|
262 } |
|
263 |
|
264 if (zep) |
|
265 *zep = ze; |
|
266 } |
|
267 |
|
268 |
|
269 /* _zip_readcdir: |
|
270 tries to find a valid end-of-central-directory at the beginning of |
|
271 buf, and then the corresponding central directory entries. |
|
272 Returns a struct zip_cdir which contains the central directory |
|
273 entries, or NULL if unsuccessful. */ |
|
274 |
|
275 static zip_cdir_t * |
|
276 _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) |
|
277 { |
|
278 zip_cdir_t *cd; |
|
279 zip_uint16_t comment_len; |
|
280 zip_uint64_t i, left; |
|
281 zip_uint64_t eocd_offset = _zip_buffer_offset(buffer); |
|
282 zip_buffer_t *cd_buffer; |
|
283 |
|
284 if (_zip_buffer_left(buffer) < EOCDLEN) { |
|
285 /* not enough bytes left for comment */ |
|
286 zip_error_set(error, ZIP_ER_NOZIP, 0); |
|
287 return NULL; |
|
288 } |
|
289 |
|
290 /* check for end-of-central-dir magic */ |
|
291 if (memcmp(_zip_buffer_get(buffer, 4), EOCD_MAGIC, 4) != 0) { |
|
292 zip_error_set(error, ZIP_ER_NOZIP, 0); |
|
293 return NULL; |
|
294 } |
|
295 |
|
296 if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) { |
|
297 _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN); |
|
298 cd = _zip_read_eocd64(za->src, buffer, buf_offset, za->flags, error); |
|
299 } |
|
300 else { |
|
301 _zip_buffer_set_offset(buffer, eocd_offset); |
|
302 cd = _zip_read_eocd(buffer, buf_offset, za->flags, error); |
|
303 } |
|
304 |
|
305 if (cd == NULL) |
|
306 return NULL; |
|
307 |
|
308 _zip_buffer_set_offset(buffer, eocd_offset + 20); |
|
309 comment_len = _zip_buffer_get_16(buffer); |
|
310 |
|
311 if (cd->offset + cd->size > buf_offset + eocd_offset) { |
|
312 /* cdir spans past EOCD record */ |
|
313 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
314 _zip_cdir_free(cd); |
|
315 return NULL; |
|
316 } |
|
317 |
|
318 if (comment_len || (za->open_flags & ZIP_CHECKCONS)) { |
|
319 zip_uint64_t tail_len; |
|
320 |
|
321 _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN); |
|
322 tail_len = _zip_buffer_left(buffer); |
|
323 |
|
324 if (tail_len < comment_len || ((za->open_flags & ZIP_CHECKCONS) && tail_len != comment_len)) { |
|
325 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
326 _zip_cdir_free(cd); |
|
327 return NULL; |
|
328 } |
|
329 |
|
330 if (comment_len) { |
|
331 if ((cd->comment=_zip_string_new(_zip_buffer_get(buffer, comment_len), comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) { |
|
332 _zip_cdir_free(cd); |
|
333 return NULL; |
|
334 } |
|
335 } |
|
336 } |
|
337 |
|
338 if (cd->offset >= buf_offset) { |
|
339 zip_uint8_t *data; |
|
340 /* if buffer already read in, use it */ |
|
341 _zip_buffer_set_offset(buffer, cd->offset - buf_offset); |
|
342 |
|
343 if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) { |
|
344 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
345 _zip_cdir_free(cd); |
|
346 return NULL; |
|
347 } |
|
348 if ((cd_buffer = _zip_buffer_new(data, cd->size)) == NULL) { |
|
349 zip_error_set(error, ZIP_ER_MEMORY, 0); |
|
350 _zip_cdir_free(cd); |
|
351 return NULL; |
|
352 } |
|
353 } |
|
354 else { |
|
355 cd_buffer = NULL; |
|
356 |
|
357 if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) { |
|
358 _zip_error_set_from_source(error, za->src); |
|
359 _zip_cdir_free(cd); |
|
360 return NULL; |
|
361 } |
|
362 |
|
363 /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */ |
|
364 if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) { |
|
365 zip_error_set(error, ZIP_ER_NOZIP, 0); |
|
366 _zip_cdir_free(cd); |
|
367 return NULL; |
|
368 } |
|
369 } |
|
370 |
|
371 left = (zip_uint64_t)cd->size; |
|
372 i=0; |
|
373 while (i<cd->nentry && left > 0) { |
|
374 zip_int64_t entry_size; |
|
375 if ((cd->entry[i].orig=_zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, error)) < 0) { |
|
376 _zip_cdir_free(cd); |
|
377 _zip_buffer_free(cd_buffer); |
|
378 return NULL; |
|
379 } |
|
380 i++; |
|
381 left -= (zip_uint64_t)entry_size; |
|
382 } |
|
383 |
|
384 if (i != cd->nentry) { |
|
385 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
386 _zip_buffer_free(cd_buffer); |
|
387 _zip_cdir_free(cd); |
|
388 return NULL; |
|
389 } |
|
390 |
|
391 if (za->open_flags & ZIP_CHECKCONS) { |
|
392 bool ok; |
|
393 |
|
394 if (cd_buffer) { |
|
395 ok = _zip_buffer_eof(cd_buffer); |
|
396 } |
|
397 else { |
|
398 zip_int64_t offset = zip_source_tell(za->src); |
|
399 |
|
400 if (offset < 0) { |
|
401 _zip_error_set_from_source(error, za->src); |
|
402 _zip_buffer_free(cd_buffer); |
|
403 _zip_cdir_free(cd); |
|
404 return NULL; |
|
405 } |
|
406 ok = ((zip_uint64_t)offset == cd->offset + cd->size); |
|
407 } |
|
408 |
|
409 if (!ok) { |
|
410 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
411 _zip_buffer_free(cd_buffer); |
|
412 _zip_cdir_free(cd); |
|
413 return NULL; |
|
414 } |
|
415 } |
|
416 |
|
417 _zip_buffer_free(cd_buffer); |
|
418 return cd; |
|
419 } |
|
420 |
|
421 |
|
422 /* _zip_checkcons: |
|
423 Checks the consistency of the central directory by comparing central |
|
424 directory entries with local headers and checking for plausible |
|
425 file and header offsets. Returns -1 if not plausible, else the |
|
426 difference between the lowest and the highest fileposition reached */ |
|
427 |
|
428 static zip_int64_t |
|
429 _zip_checkcons(zip_t *za, zip_cdir_t *cd, zip_error_t *error) |
|
430 { |
|
431 zip_uint64_t i; |
|
432 zip_uint64_t min, max, j; |
|
433 struct zip_dirent temp; |
|
434 |
|
435 _zip_dirent_init(&temp); |
|
436 if (cd->nentry) { |
|
437 max = cd->entry[0].orig->offset; |
|
438 min = cd->entry[0].orig->offset; |
|
439 } |
|
440 else |
|
441 min = max = 0; |
|
442 |
|
443 for (i=0; i<cd->nentry; i++) { |
|
444 if (cd->entry[i].orig->offset < min) |
|
445 min = cd->entry[i].orig->offset; |
|
446 if (min > (zip_uint64_t)cd->offset) { |
|
447 zip_error_set(error, ZIP_ER_NOZIP, 0); |
|
448 return -1; |
|
449 } |
|
450 |
|
451 j = cd->entry[i].orig->offset + cd->entry[i].orig->comp_size |
|
452 + _zip_string_length(cd->entry[i].orig->filename) + LENTRYSIZE; |
|
453 if (j > max) |
|
454 max = j; |
|
455 if (max > (zip_uint64_t)cd->offset) { |
|
456 zip_error_set(error, ZIP_ER_NOZIP, 0); |
|
457 return -1; |
|
458 } |
|
459 |
|
460 if (zip_source_seek(za->src, (zip_int64_t)cd->entry[i].orig->offset, SEEK_SET) < 0) { |
|
461 _zip_error_set_from_source(error, za->src); |
|
462 return -1; |
|
463 } |
|
464 |
|
465 if (_zip_dirent_read(&temp, za->src, NULL, true, error) == -1) { |
|
466 _zip_dirent_finalize(&temp); |
|
467 return -1; |
|
468 } |
|
469 |
|
470 if (_zip_headercomp(cd->entry[i].orig, &temp) != 0) { |
|
471 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
472 _zip_dirent_finalize(&temp); |
|
473 return -1; |
|
474 } |
|
475 |
|
476 cd->entry[i].orig->extra_fields = _zip_ef_merge(cd->entry[i].orig->extra_fields, temp.extra_fields); |
|
477 cd->entry[i].orig->local_extra_fields_read = 1; |
|
478 temp.extra_fields = NULL; |
|
479 |
|
480 _zip_dirent_finalize(&temp); |
|
481 } |
|
482 |
|
483 return (max-min) < ZIP_INT64_MAX ? (zip_int64_t)(max-min) : ZIP_INT64_MAX; |
|
484 } |
|
485 |
|
486 |
|
487 /* _zip_headercomp: |
|
488 compares a central directory entry and a local file header |
|
489 Return 0 if they are consistent, -1 if not. */ |
|
490 |
|
491 static int |
|
492 _zip_headercomp(const zip_dirent_t *central, const zip_dirent_t *local) |
|
493 { |
|
494 if ((central->version_needed != local->version_needed) |
|
495 #if 0 |
|
496 /* some zip-files have different values in local |
|
497 and global headers for the bitflags */ |
|
498 || (central->bitflags != local->bitflags) |
|
499 #endif |
|
500 || (central->comp_method != local->comp_method) |
|
501 || (central->last_mod != local->last_mod) |
|
502 || !_zip_string_equal(central->filename, local->filename)) |
|
503 return -1; |
|
504 |
|
505 if ((central->crc != local->crc) || (central->comp_size != local->comp_size) |
|
506 || (central->uncomp_size != local->uncomp_size)) { |
|
507 /* InfoZip stores valid values in local header even when data descriptor is used. |
|
508 This is in violation of the appnote. */ |
|
509 if (((local->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 |
|
510 || local->crc != 0 || local->comp_size != 0 || local->uncomp_size != 0)) |
|
511 return -1; |
|
512 } |
|
513 |
|
514 return 0; |
|
515 } |
|
516 |
|
517 |
|
518 static zip_t * |
|
519 _zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error) |
|
520 { |
|
521 zip_t *za; |
|
522 |
|
523 if ((za = _zip_new(error)) == NULL) { |
|
524 return NULL; |
|
525 } |
|
526 |
|
527 za->src = src; |
|
528 za->open_flags = flags; |
|
529 if (flags & ZIP_RDONLY) { |
|
530 za->flags |= ZIP_AFL_RDONLY; |
|
531 za->ch_flags |= ZIP_AFL_RDONLY; |
|
532 } |
|
533 return za; |
|
534 } |
|
535 |
|
536 |
|
537 /* |
|
538 * tests for file existence |
|
539 */ |
|
540 static exists_t |
|
541 _zip_file_exists(zip_source_t *src, zip_error_t *error) |
|
542 { |
|
543 struct zip_stat st; |
|
544 |
|
545 zip_stat_init(&st); |
|
546 if (zip_source_stat(src, &st) != 0) { |
|
547 zip_error_t *src_error = zip_source_error(src); |
|
548 if (zip_error_code_zip(src_error) == ZIP_ER_READ && zip_error_code_system(src_error) == ENOENT) { |
|
549 return EXISTS_NOT; |
|
550 } |
|
551 _zip_error_copy(error, src_error); |
|
552 return EXISTS_ERROR; |
|
553 } |
|
554 |
|
555 return (st.valid & ZIP_STAT_SIZE) && st.size == 0 ? EXISTS_EMPTY : EXISTS_NONEMPTY; |
|
556 } |
|
557 |
|
558 |
|
559 static zip_cdir_t * |
|
560 _zip_find_central_dir(zip_t *za, zip_uint64_t len) |
|
561 { |
|
562 zip_cdir_t *cdir, *cdirnew; |
|
563 zip_uint8_t *match; |
|
564 zip_int64_t buf_offset; |
|
565 zip_uint64_t buflen; |
|
566 zip_int64_t a; |
|
567 zip_int64_t best; |
|
568 zip_error_t error; |
|
569 zip_buffer_t *buffer; |
|
570 |
|
571 if (len < EOCDLEN) { |
|
572 zip_error_set(&za->error, ZIP_ER_NOZIP, 0); |
|
573 return NULL; |
|
574 } |
|
575 |
|
576 buflen = (len < CDBUFSIZE ? len : CDBUFSIZE); |
|
577 if (zip_source_seek(za->src, -(zip_int64_t)buflen, SEEK_END) < 0) { |
|
578 zip_error_t *src_error = zip_source_error(za->src); |
|
579 if (zip_error_code_zip(src_error) != ZIP_ER_SEEK || zip_error_code_system(src_error) != EFBIG) { |
|
580 /* seek before start of file on my machine */ |
|
581 _zip_error_copy(&za->error, src_error); |
|
582 return NULL; |
|
583 } |
|
584 } |
|
585 if ((buf_offset = zip_source_tell(za->src)) < 0) { |
|
586 _zip_error_set_from_source(&za->error, za->src); |
|
587 return NULL; |
|
588 } |
|
589 |
|
590 if ((buffer = _zip_buffer_new_from_source(za->src, buflen, NULL, &za->error)) == NULL) { |
|
591 return NULL; |
|
592 } |
|
593 |
|
594 best = -1; |
|
595 cdir = NULL; |
|
596 if (buflen >= CDBUFSIZE) { |
|
597 /* EOCD64 locator is before EOCD, so leave place for it */ |
|
598 _zip_buffer_set_offset(buffer, EOCD64LOCLEN); |
|
599 } |
|
600 zip_error_set(&error, ZIP_ER_NOZIP, 0); |
|
601 |
|
602 match = _zip_buffer_get(buffer, 0); |
|
603 while ((match=_zip_memmem(match, _zip_buffer_left(buffer)-(EOCDLEN-4), (const unsigned char *)EOCD_MAGIC, 4)) != NULL) { |
|
604 _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer))); |
|
605 if ((cdirnew = _zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &error)) != NULL) { |
|
606 if (cdir) { |
|
607 if (best <= 0) { |
|
608 best = _zip_checkcons(za, cdir, &error); |
|
609 } |
|
610 |
|
611 a = _zip_checkcons(za, cdirnew, &error); |
|
612 if (best < a) { |
|
613 _zip_cdir_free(cdir); |
|
614 cdir = cdirnew; |
|
615 best = a; |
|
616 } |
|
617 else { |
|
618 _zip_cdir_free(cdirnew); |
|
619 } |
|
620 } |
|
621 else { |
|
622 cdir = cdirnew; |
|
623 if (za->open_flags & ZIP_CHECKCONS) |
|
624 best = _zip_checkcons(za, cdir, &error); |
|
625 else { |
|
626 best = 0; |
|
627 } |
|
628 } |
|
629 cdirnew = NULL; |
|
630 } |
|
631 |
|
632 match++; |
|
633 _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer))); |
|
634 } |
|
635 |
|
636 _zip_buffer_free(buffer); |
|
637 |
|
638 if (best < 0) { |
|
639 _zip_error_copy(&za->error, &error); |
|
640 _zip_cdir_free(cdir); |
|
641 return NULL; |
|
642 } |
|
643 |
|
644 return cdir; |
|
645 } |
|
646 |
|
647 |
|
648 static unsigned char * |
|
649 _zip_memmem(const unsigned char *big, size_t biglen, const unsigned char *little, size_t littlelen) |
|
650 { |
|
651 const unsigned char *p; |
|
652 |
|
653 if ((biglen < littlelen) || (littlelen == 0)) |
|
654 return NULL; |
|
655 p = big-1; |
|
656 while ((p=(const unsigned char *) |
|
657 memchr(p+1, little[0], (size_t)(big-(p+1))+(size_t)(biglen-littlelen)+1)) != NULL) { |
|
658 if (memcmp(p+1, little+1, littlelen-1)==0) |
|
659 return (unsigned char *)p; |
|
660 } |
|
661 |
|
662 return NULL; |
|
663 } |
|
664 |
|
665 |
|
666 static zip_cdir_t * |
|
667 _zip_read_eocd(zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) |
|
668 { |
|
669 zip_cdir_t *cd; |
|
670 zip_uint64_t i, nentry, size, offset, eocd_offset; |
|
671 |
|
672 if (_zip_buffer_left(buffer) < EOCDLEN) { |
|
673 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
674 return NULL; |
|
675 } |
|
676 |
|
677 eocd_offset = _zip_buffer_offset(buffer); |
|
678 |
|
679 _zip_buffer_get(buffer, 4); /* magic already verified */ |
|
680 |
|
681 if (_zip_buffer_get_32(buffer) != 0) { |
|
682 zip_error_set(error, ZIP_ER_MULTIDISK, 0); |
|
683 return NULL; |
|
684 } |
|
685 |
|
686 /* number of cdir-entries on this disk */ |
|
687 i = _zip_buffer_get_16(buffer); |
|
688 /* number of cdir-entries */ |
|
689 nentry = _zip_buffer_get_16(buffer); |
|
690 |
|
691 if (nentry != i) { |
|
692 zip_error_set(error, ZIP_ER_NOZIP, 0); |
|
693 return NULL; |
|
694 } |
|
695 |
|
696 size = _zip_buffer_get_32(buffer); |
|
697 offset = _zip_buffer_get_32(buffer); |
|
698 |
|
699 if (offset+size < offset) { |
|
700 zip_error_set(error, ZIP_ER_SEEK, EFBIG); |
|
701 return NULL; |
|
702 } |
|
703 |
|
704 if (offset+size > buf_offset + eocd_offset) { |
|
705 /* cdir spans past EOCD record */ |
|
706 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
707 return NULL; |
|
708 } |
|
709 |
|
710 if ((flags & ZIP_CHECKCONS) && offset+size != buf_offset + eocd_offset) { |
|
711 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
712 return NULL; |
|
713 } |
|
714 |
|
715 if ((cd=_zip_cdir_new(nentry, error)) == NULL) |
|
716 return NULL; |
|
717 |
|
718 cd->size = size; |
|
719 cd->offset = offset; |
|
720 |
|
721 return cd; |
|
722 } |
|
723 |
|
724 |
|
725 static zip_cdir_t * |
|
726 _zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) |
|
727 { |
|
728 zip_cdir_t *cd; |
|
729 zip_uint64_t offset; |
|
730 zip_uint8_t eocd[EOCD64LEN]; |
|
731 zip_uint64_t eocd_offset; |
|
732 zip_uint64_t size, nentry, i, eocdloc_offset; |
|
733 bool free_buffer; |
|
734 zip_uint32_t num_disks, num_disks64, eocd_disk, eocd_disk64; |
|
735 |
|
736 eocdloc_offset = _zip_buffer_offset(buffer); |
|
737 |
|
738 _zip_buffer_get(buffer, 4); /* magic already verified */ |
|
739 |
|
740 num_disks = _zip_buffer_get_16(buffer); |
|
741 eocd_disk = _zip_buffer_get_16(buffer); |
|
742 eocd_offset = _zip_buffer_get_64(buffer); |
|
743 |
|
744 if (eocd_offset > ZIP_INT64_MAX || eocd_offset + EOCD64LEN < eocd_offset) { |
|
745 zip_error_set(error, ZIP_ER_SEEK, EFBIG); |
|
746 return NULL; |
|
747 } |
|
748 |
|
749 if (eocd_offset + EOCD64LEN > eocdloc_offset + buf_offset) { |
|
750 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
751 return NULL; |
|
752 } |
|
753 |
|
754 if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) { |
|
755 _zip_buffer_set_offset(buffer, eocd_offset - buf_offset); |
|
756 free_buffer = false; |
|
757 } |
|
758 else { |
|
759 if (zip_source_seek(src, (zip_int64_t)eocd_offset, SEEK_SET) < 0) { |
|
760 _zip_error_set_from_source(error, src); |
|
761 return NULL; |
|
762 } |
|
763 if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) { |
|
764 return NULL; |
|
765 } |
|
766 free_buffer = true; |
|
767 } |
|
768 |
|
769 if (memcmp(_zip_buffer_get(buffer, 4), EOCD64_MAGIC, 4) != 0) { |
|
770 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
771 if (free_buffer) { |
|
772 _zip_buffer_free(buffer); |
|
773 } |
|
774 return NULL; |
|
775 } |
|
776 |
|
777 size = _zip_buffer_get_64(buffer); |
|
778 |
|
779 if ((flags & ZIP_CHECKCONS) && size + eocd_offset + 12 != buf_offset + eocdloc_offset) { |
|
780 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
781 if (free_buffer) { |
|
782 _zip_buffer_free(buffer); |
|
783 } |
|
784 return NULL; |
|
785 } |
|
786 |
|
787 _zip_buffer_get(buffer, 4); /* skip version made by/needed */ |
|
788 |
|
789 num_disks64 = _zip_buffer_get_32(buffer); |
|
790 eocd_disk64 = _zip_buffer_get_32(buffer); |
|
791 |
|
792 /* if eocd values are 0xffff, we have to use eocd64 values. |
|
793 otherwise, if the values are not the same, it's inconsistent; |
|
794 in any case, if the value is not 0, we don't support it */ |
|
795 if (num_disks == 0xffff) { |
|
796 num_disks = num_disks64; |
|
797 } |
|
798 if (eocd_disk == 0xffff) { |
|
799 eocd_disk = eocd_disk64; |
|
800 } |
|
801 if ((flags & ZIP_CHECKCONS) && (eocd_disk != eocd_disk64 || num_disks != num_disks64)) { |
|
802 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
803 return NULL; |
|
804 } |
|
805 if (num_disks != 0 || eocd_disk != 0) { |
|
806 zip_error_set(error, ZIP_ER_MULTIDISK, 0); |
|
807 return NULL; |
|
808 } |
|
809 |
|
810 nentry = _zip_buffer_get_64(buffer); |
|
811 i = _zip_buffer_get_64(buffer); |
|
812 |
|
813 if (nentry != i) { |
|
814 zip_error_set(error, ZIP_ER_MULTIDISK, 0); |
|
815 if (free_buffer) { |
|
816 _zip_buffer_free(buffer); |
|
817 } |
|
818 return NULL; |
|
819 } |
|
820 |
|
821 size = _zip_buffer_get_64(buffer); |
|
822 offset = _zip_buffer_get_64(buffer); |
|
823 |
|
824 if (!_zip_buffer_ok(buffer)) { |
|
825 zip_error_set(error, ZIP_ER_INTERNAL, 0); |
|
826 if (free_buffer) { |
|
827 _zip_buffer_free(buffer); |
|
828 } |
|
829 return NULL; |
|
830 } |
|
831 |
|
832 if (free_buffer) { |
|
833 _zip_buffer_free(buffer); |
|
834 } |
|
835 |
|
836 if (offset > ZIP_INT64_MAX || offset+size < offset) { |
|
837 zip_error_set(error, ZIP_ER_SEEK, EFBIG); |
|
838 return NULL; |
|
839 } |
|
840 if ((flags & ZIP_CHECKCONS) && offset+size != eocd_offset) { |
|
841 zip_error_set(error, ZIP_ER_INCONS, 0); |
|
842 return NULL; |
|
843 } |
|
844 |
|
845 if ((cd=_zip_cdir_new(nentry, error)) == NULL) |
|
846 return NULL; |
|
847 |
|
848 |
|
849 cd->size = size; |
|
850 cd->offset = offset; |
|
851 |
|
852 return cd; |
|
853 } |