xref: /PHP-5.6/ext/zip/lib/zip_open.c (revision f6e8ce81)
1 /*
2   zip_open.c -- open zip archive by name
3   Copyright (C) 1999-2012 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 
36 #include <sys/stat.h>
37 #include <errno.h>
38 #include <limits.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 
43 #include "zipint.h"
44 
45 static void set_error(int *, const struct zip_error *, int);
46 static struct zip *_zip_allocate_new(const char *, unsigned int, int *);
47 static zip_int64_t _zip_checkcons(FILE *, struct zip_cdir *, struct zip_error *);
48 static void _zip_check_torrentzip(struct zip *, const struct zip_cdir *);
49 static struct zip_cdir *_zip_find_central_dir(FILE *, unsigned int, int *, off_t);
50 static int _zip_file_exists(const char *, unsigned int, int *);
51 static int _zip_headercomp(const struct zip_dirent *, const struct zip_dirent *);
52 static unsigned char *_zip_memmem(const unsigned char *, size_t,
53 				  const unsigned char *, size_t);
54 static struct zip_cdir *_zip_readcdir(FILE *, off_t, unsigned char *, const unsigned char *,
55 				 size_t, unsigned int, struct zip_error *);
56 static struct zip_cdir *_zip_read_eocd(const unsigned char *, const unsigned char *, off_t,
57 				       size_t, unsigned int, struct zip_error *);
58 static struct zip_cdir *_zip_read_eocd64(FILE *, const unsigned char *, const unsigned char *,
59 					 off_t, size_t, unsigned int, struct zip_error *);
60 
61 
62 
63 ZIP_EXTERN struct zip *
zip_open(const char * fn,int _flags,int * zep)64 zip_open(const char *fn, int _flags, int *zep)
65 {
66     FILE *fp;
67     unsigned int flags;
68 
69     if (_flags < 0) {
70         if (zep)
71             *zep = ZIP_ER_INVAL;
72         return NULL;
73     }
74     flags = (unsigned int)_flags;
75 
76     switch (_zip_file_exists(fn, flags, zep)) {
77     case -1:
78 	return NULL;
79     case 0:
80 	return _zip_allocate_new(fn, flags, zep);
81     default:
82 	if (flags & ZIP_TRUNCATE) {
83 	    FILE *f;
84 
85 	    if ((f = fopen(fn, "rb")) == NULL) {
86 		set_error(zep, NULL, ZIP_ER_OPEN);
87 		return NULL;
88 	    }
89 	    fclose(f);
90 	    return _zip_allocate_new(fn, flags, zep);
91 	}
92 	break;
93     }
94 
95     if ((fp=fopen(fn, "rb")) == NULL) {
96 	set_error(zep, NULL, ZIP_ER_OPEN);
97 	return NULL;
98     }
99 
100     return _zip_open(fn, fp, flags, zep);
101 }
102 
103 
104 ZIP_EXTERN int
zip_archive_set_tempdir(struct zip * za,const char * tempdir)105 zip_archive_set_tempdir(struct zip *za, const char *tempdir)
106 {
107     char *new_tempdir;
108 
109     if (tempdir) {
110         if ((new_tempdir = strdup(tempdir)) == NULL) {
111             _zip_error_set(&za->error, ZIP_ER_MEMORY, errno);
112             return -1;
113         }
114     }
115     else
116         new_tempdir = NULL;
117 
118     free(za->tempdir);
119     za->tempdir = new_tempdir;
120 
121     return 0;
122 }
123 
124 
125 struct zip *
_zip_open(const char * fn,FILE * fp,unsigned int flags,int * zep)126 _zip_open(const char *fn, FILE *fp, unsigned int flags, int *zep)
127 {
128     struct zip *za;
129     struct zip_cdir *cdir;
130     off_t len;
131 
132     if (fseeko(fp, 0, SEEK_END) < 0) {
133 	*zep = ZIP_ER_SEEK;
134 	return NULL;
135     }
136     len = ftello(fp);
137 
138     /* treat empty files as empty archives */
139     if (len == 0) {
140 	if ((za=_zip_allocate_new(fn, flags, zep)) == NULL)
141 	    fclose(fp);
142 	else
143 	    za->zp = fp;
144 	return za;
145     }
146 
147     cdir = _zip_find_central_dir(fp, flags, zep, len);
148     if (cdir == NULL) {
149 	fclose(fp);
150 	return NULL;
151     }
152 
153     if ((za=_zip_allocate_new(fn, flags, zep)) == NULL) {
154 	_zip_cdir_free(cdir);
155 	fclose(fp);
156 	return NULL;
157     }
158 
159     za->entry = cdir->entry;
160     za->nentry = cdir->nentry;
161     za->nentry_alloc = cdir->nentry_alloc;
162     za->comment_orig = cdir->comment;
163 
164     za->zp = fp;
165 
166     _zip_check_torrentzip(za, cdir);
167 
168     za->ch_flags = za->flags;
169 
170     free(cdir);
171 
172     return za;
173 }
174 
175 
176 
177 static void
set_error(int * zep,const struct zip_error * err,int ze)178 set_error(int *zep, const struct zip_error *err, int ze)
179 {
180     int se;
181 
182     if (err) {
183 	_zip_error_get(err, &ze, &se);
184 	if (zip_error_get_sys_type(ze) == ZIP_ET_SYS)
185 	    errno = se;
186     }
187 
188     if (zep)
189 	*zep = ze;
190 }
191 
192 
193 
194 /* _zip_readcdir:
195    tries to find a valid end-of-central-directory at the beginning of
196    buf, and then the corresponding central directory entries.
197    Returns a struct zip_cdir which contains the central directory
198    entries, or NULL if unsuccessful. */
199 
200 static struct zip_cdir *
_zip_readcdir(FILE * fp,off_t buf_offset,unsigned char * buf,const unsigned char * eocd,size_t buflen,unsigned int flags,struct zip_error * error)201 _zip_readcdir(FILE *fp, off_t buf_offset, unsigned char *buf, const unsigned char *eocd, size_t buflen,
202 	      unsigned int flags, struct zip_error *error)
203 {
204     struct zip_cdir *cd;
205     const unsigned char *cdp;
206     const unsigned char **bufp;
207     zip_int64_t tail_len, comment_len;
208     zip_uint64_t i, left;
209 
210     tail_len = buf + buflen - eocd - EOCDLEN;
211     if (tail_len < 0) {
212 	/* not enough bytes left for comment */
213 	_zip_error_set(error, ZIP_ER_NOZIP, 0);
214 	return NULL;
215     }
216 
217     /* check for end-of-central-dir magic */
218     if (memcmp(eocd, EOCD_MAGIC, 4) != 0) {
219 	_zip_error_set(error, ZIP_ER_NOZIP, 0);
220 	return NULL;
221     }
222 
223     if (memcmp(eocd+4, "\0\0\0\0", 4) != 0) {
224 	_zip_error_set(error, ZIP_ER_MULTIDISK, 0);
225 	return NULL;
226     }
227 
228     if (eocd-EOCD64LOCLEN >= buf && memcmp(eocd-EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0)
229 	cd = _zip_read_eocd64(fp, eocd-EOCD64LOCLEN, buf, buf_offset, buflen, flags, error);
230     else
231 	cd = _zip_read_eocd(eocd, buf, buf_offset, buflen, flags, error);
232 
233     if (cd == NULL)
234 	return NULL;
235 
236     cdp = eocd + 20;
237     comment_len = _zip_read2(&cdp);
238 
239     if ((zip_uint64_t)cd->offset+(zip_uint64_t)cd->size > (zip_uint64_t)buf_offset + (zip_uint64_t)(eocd-buf)) {
240 	/* cdir spans past EOCD record */
241 	_zip_error_set(error, ZIP_ER_INCONS, 0);
242 	_zip_cdir_free(cd);
243 	return NULL;
244     }
245 
246     if (tail_len < comment_len || ((flags & ZIP_CHECKCONS) && tail_len != comment_len)) {
247 	_zip_error_set(error, ZIP_ER_INCONS, 0);
248 	_zip_cdir_free(cd);
249 	return NULL;
250     }
251 
252     if (comment_len) {
253 	if ((cd->comment=_zip_string_new(eocd+EOCDLEN, (zip_uint16_t)comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) {
254 	    _zip_cdir_free(cd);
255 	    return NULL;
256 	}
257     }
258 
259     if (cd->offset >= buf_offset) {
260 	/* if buffer already read in, use it */
261 	cdp = buf + (cd->offset - buf_offset);
262 	bufp = &cdp;
263     }
264     else {
265 	/* go to start of cdir and read it entry by entry */
266 	bufp = NULL;
267 	clearerr(fp);
268 	fseeko(fp, cd->offset, SEEK_SET);
269 	/* possible consistency check: cd->offset =
270 	   len-(cd->size+cd->comment_len+EOCDLEN) ? */
271 	if (ferror(fp) || (ftello(fp) != cd->offset)) {
272 	    /* seek error or offset of cdir wrong */
273 	    if (ferror(fp))
274 		_zip_error_set(error, ZIP_ER_SEEK, errno);
275 	    else
276 		_zip_error_set(error, ZIP_ER_NOZIP, 0);
277 	    _zip_cdir_free(cd);
278 	    return NULL;
279 	}
280     }
281 
282     left = (zip_uint64_t)cd->size;
283     i=0;
284     while (i<cd->nentry && left > 0) {
285 	if ((cd->entry[i].orig=_zip_dirent_new()) == NULL
286 	    || (_zip_dirent_read(cd->entry[i].orig, fp, bufp, &left, 0, error)) < 0) {
287 	    _zip_cdir_free(cd);
288 	    return NULL;
289 	}
290 	i++;
291     }
292     if (i != cd->nentry || ((flags & ZIP_CHECKCONS) && left != 0)) {
293         _zip_error_set(error, ZIP_ER_INCONS, 0);
294         _zip_cdir_free(cd);
295         return NULL;
296     }
297 
298     return cd;
299 }
300 
301 
302 
303 /* _zip_checkcons:
304    Checks the consistency of the central directory by comparing central
305    directory entries with local headers and checking for plausible
306    file and header offsets. Returns -1 if not plausible, else the
307    difference between the lowest and the highest fileposition reached */
308 
309 static zip_int64_t
_zip_checkcons(FILE * fp,struct zip_cdir * cd,struct zip_error * error)310 _zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error)
311 {
312     zip_uint64_t i;
313     zip_uint64_t min, max, j;
314     struct zip_dirent temp;
315 
316     if (cd->nentry) {
317 	max = cd->entry[0].orig->offset;
318 	min = cd->entry[0].orig->offset;
319     }
320     else
321 	min = max = 0;
322 
323     for (i=0; i<cd->nentry; i++) {
324 	if (cd->entry[i].orig->offset < min)
325 	    min = cd->entry[i].orig->offset;
326 	if (min > (zip_uint64_t)cd->offset) {
327 	    _zip_error_set(error, ZIP_ER_NOZIP, 0);
328 	    return -1;
329 	}
330 
331 	j = cd->entry[i].orig->offset + cd->entry[i].orig->comp_size
332 	    + _zip_string_length(cd->entry[i].orig->filename) + LENTRYSIZE;
333 	if (j > max)
334 	    max = j;
335 	if (max > (zip_uint64_t)cd->offset) {
336 	    _zip_error_set(error, ZIP_ER_NOZIP, 0);
337 	    return -1;
338 	}
339 
340 	if (fseeko(fp, (off_t)cd->entry[i].orig->offset, SEEK_SET) != 0) {
341 	    _zip_error_set(error, ZIP_ER_SEEK, errno);
342 	    return -1;
343 	}
344 
345 	if (_zip_dirent_read(&temp, fp, NULL, NULL, 1, error) == -1)
346 	    return -1;
347 
348 	if (_zip_headercomp(cd->entry[i].orig, &temp) != 0) {
349 	    _zip_error_set(error, ZIP_ER_INCONS, 0);
350 	    _zip_dirent_finalize(&temp);
351 	    return -1;
352 	}
353 
354 	cd->entry[i].orig->extra_fields = _zip_ef_merge(cd->entry[i].orig->extra_fields, temp.extra_fields);
355 	cd->entry[i].orig->local_extra_fields_read = 1;
356 	temp.extra_fields = NULL;
357 
358 	_zip_dirent_finalize(&temp);
359     }
360 
361     return (max-min) < ZIP_INT64_MAX ? (zip_int64_t)(max-min) : ZIP_INT64_MAX;
362 }
363 
364 
365 
366 /* _zip_check_torrentzip:
367    check whether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */
368 
369 static void
_zip_check_torrentzip(struct zip * za,const struct zip_cdir * cdir)370 _zip_check_torrentzip(struct zip *za, const struct zip_cdir *cdir)
371 {
372     uLong crc_got, crc_should;
373     char buf[8+1];
374     char *end;
375 
376     if (za->zp == NULL || cdir == NULL)
377 	return;
378 
379     if (_zip_string_length(cdir->comment) != TORRENT_SIG_LEN+8
380 	|| strncmp((const char *)cdir->comment->raw, TORRENT_SIG, TORRENT_SIG_LEN) != 0)
381 	return;
382 
383     memcpy(buf, cdir->comment->raw+TORRENT_SIG_LEN, 8);
384     buf[8] = '\0';
385     errno = 0;
386     crc_should = strtoul(buf, &end, 16);
387     if ((crc_should == UINT_MAX && errno != 0) || (end && *end))
388 	return;
389 
390     if (_zip_filerange_crc(za->zp, cdir->offset, cdir->size, &crc_got, NULL) < 0)
391 	return;
392 
393     if (crc_got == crc_should)
394 	za->flags |= ZIP_AFL_TORRENT;
395 }
396 
397 
398 
399 
400 /* _zip_headercomp:
401    compares a central directory entry and a local file header
402    Return 0 if they are consistent, -1 if not. */
403 
404 static int
_zip_headercomp(const struct zip_dirent * central,const struct zip_dirent * local)405 _zip_headercomp(const struct zip_dirent *central, const struct zip_dirent *local)
406 {
407     if ((central->version_needed != local->version_needed)
408 #if 0
409 	/* some zip-files have different values in local
410 	   and global headers for the bitflags */
411 	|| (central->bitflags != local->bitflags)
412 #endif
413 	|| (central->comp_method != local->comp_method)
414 	|| (central->last_mod != local->last_mod)
415 	|| !_zip_string_equal(central->filename, local->filename))
416 	return -1;
417 
418 
419     if ((central->crc != local->crc) || (central->comp_size != local->comp_size)
420 	|| (central->uncomp_size != local->uncomp_size)) {
421 	/* InfoZip stores valid values in local header even when data descriptor is used.
422 	   This is in violation of the appnote. */
423 	if (((local->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0
424 	     || local->crc != 0 || local->comp_size != 0 || local->uncomp_size != 0))
425 	    return -1;
426     }
427 
428     return 0;
429 }
430 
431 
432 
433 static struct zip *
_zip_allocate_new(const char * fn,unsigned int flags,int * zep)434 _zip_allocate_new(const char *fn, unsigned int flags, int *zep)
435 {
436     struct zip *za;
437     struct zip_error error;
438 
439     if ((za=_zip_new(&error)) == NULL) {
440 	set_error(zep, &error, 0);
441 	return NULL;
442     }
443 
444     if (fn == NULL)
445 	za->zn = NULL;
446     else {
447 	za->zn = strdup(fn);
448 	if (!za->zn) {
449 	    zip_discard(za);
450 	    set_error(zep, NULL, ZIP_ER_MEMORY);
451 	    return NULL;
452 	}
453     }
454     za->open_flags = flags;
455     return za;
456 }
457 
458 
459 
460 static int
_zip_file_exists(const char * fn,unsigned int flags,int * zep)461 _zip_file_exists(const char *fn, unsigned int flags, int *zep)
462 {
463     struct stat st;
464 
465     if (fn == NULL) {
466 	set_error(zep, NULL, ZIP_ER_INVAL);
467 	return -1;
468     }
469 
470     if (stat(fn, &st) != 0) {
471 	if (flags & ZIP_CREATE)
472 	    return 0;
473 	else {
474 	    set_error(zep, NULL, ZIP_ER_OPEN);
475 	    return -1;
476 	}
477     }
478     else if ((flags & ZIP_EXCL)) {
479 	set_error(zep, NULL, ZIP_ER_EXISTS);
480 	return -1;
481     }
482     /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL,
483        just like open() */
484 
485     return 1;
486 }
487 
488 
489 
490 static struct zip_cdir *
_zip_find_central_dir(FILE * fp,unsigned int flags,int * zep,off_t len)491 _zip_find_central_dir(FILE *fp, unsigned int flags, int *zep, off_t len)
492 {
493     struct zip_cdir *cdir, *cdirnew;
494     unsigned char *buf, *match;
495     off_t buf_offset;
496     size_t buflen;
497     zip_int64_t a, i;
498     zip_int64_t best;
499     struct zip_error zerr;
500 
501     if (len < (off_t)EOCDLEN) {
502         set_error(zep, NULL, ZIP_ER_NOZIP);
503         return NULL;
504     }
505 
506     i = fseeko(fp, -(len < CDBUFSIZE ? len : CDBUFSIZE), SEEK_END);
507     if (i == -1 && errno != EFBIG) {
508 	/* seek before start of file on my machine */
509 	set_error(zep, NULL, ZIP_ER_SEEK);
510 	return NULL;
511     }
512     buf_offset = ftello(fp);
513 
514     /* 64k is too much for stack */
515     if ((buf=(unsigned char *)malloc(CDBUFSIZE)) == NULL) {
516 	set_error(zep, NULL, ZIP_ER_MEMORY);
517 	return NULL;
518     }
519 
520     clearerr(fp);
521     buflen = fread(buf, 1, CDBUFSIZE, fp);
522 
523     if (ferror(fp)) {
524 	set_error(zep, NULL, ZIP_ER_READ);
525 	free(buf);
526 	return NULL;
527     }
528 
529     best = -1;
530     cdir = NULL;
531     match = buf+ (buflen < CDBUFSIZE ? 0 : EOCD64LOCLEN);
532     _zip_error_set(&zerr, ZIP_ER_NOZIP, 0);
533 
534     while ((match=_zip_memmem(match, buflen-(size_t)(match-buf)-(EOCDLEN-4),
535 			      (const unsigned char *)EOCD_MAGIC, 4))!=NULL) {
536 	/* found match -- check, if good */
537 	/* to avoid finding the same match all over again */
538 	match++;
539 	if ((cdirnew=_zip_readcdir(fp, buf_offset, buf, match-1, buflen, flags,
540 				   &zerr)) == NULL)
541 	    continue;
542 
543 	if (cdir) {
544 	    if (best <= 0)
545 		best = _zip_checkcons(fp, cdir, &zerr);
546 	    a = _zip_checkcons(fp, cdirnew, &zerr);
547 	    if (best < a) {
548 		_zip_cdir_free(cdir);
549 		cdir = cdirnew;
550 		best = a;
551 	    }
552 	    else
553 		_zip_cdir_free(cdirnew);
554 	}
555 	else {
556 	    cdir = cdirnew;
557 	    if (flags & ZIP_CHECKCONS)
558 		best = _zip_checkcons(fp, cdir, &zerr);
559 	    else
560 		best = 0;
561 	}
562 	cdirnew = NULL;
563     }
564 
565     free(buf);
566 
567     if (best < 0) {
568 	set_error(zep, &zerr, 0);
569 	_zip_cdir_free(cdir);
570 	return NULL;
571     }
572 
573     return cdir;
574 }
575 
576 
577 
578 static unsigned char *
_zip_memmem(const unsigned char * big,size_t biglen,const unsigned char * little,size_t littlelen)579 _zip_memmem(const unsigned char *big, size_t biglen, const unsigned char *little, size_t littlelen)
580 {
581     const unsigned char *p;
582 
583     if ((biglen < littlelen) || (littlelen == 0))
584 	return NULL;
585     p = big-1;
586     while ((p=(const unsigned char *)
587 	        memchr(p+1, little[0], (size_t)(big-(p+1))+(size_t)(biglen-littlelen)+1)) != NULL) {
588 	if (memcmp(p+1, little+1, littlelen-1)==0)
589 	    return (unsigned char *)p;
590     }
591 
592     return NULL;
593 }
594 
595 
596 
597 static struct zip_cdir *
_zip_read_eocd(const unsigned char * eocd,const unsigned char * buf,off_t buf_offset,size_t buflen,unsigned int flags,struct zip_error * error)598 _zip_read_eocd(const unsigned char *eocd, const unsigned char *buf, off_t buf_offset, size_t buflen,
599 	       unsigned int flags, struct zip_error *error)
600 {
601     struct zip_cdir *cd;
602     const unsigned char *cdp;
603     zip_uint64_t i, nentry, size, offset;
604 
605     if (eocd+EOCDLEN > buf+buflen) {
606 	_zip_error_set(error, ZIP_ER_INCONS, 0);
607 	return NULL;
608     }
609 
610     cdp = eocd + 8;
611 
612     /* number of cdir-entries on this disk */
613     i = _zip_read2(&cdp);
614     /* number of cdir-entries */
615     nentry = _zip_read2(&cdp);
616 
617     if (nentry != i) {
618 	_zip_error_set(error, ZIP_ER_NOZIP, 0);
619 	return NULL;
620     }
621 
622     size = _zip_read4(&cdp);
623     offset = _zip_read4(&cdp);
624 
625     if (size > ZIP_OFF_MAX || offset > ZIP_OFF_MAX || offset+size > ZIP_OFF_MAX) {
626         _zip_error_set(error, ZIP_ER_SEEK, EFBIG);
627         return NULL;
628     }
629 
630     if (offset+size > (zip_uint64_t)(buf_offset + (eocd-buf))) {
631 	/* cdir spans past EOCD record */
632 	_zip_error_set(error, ZIP_ER_INCONS, 0);
633 	return NULL;
634     }
635 
636     if ((flags & ZIP_CHECKCONS) && offset+size != (zip_uint64_t)(buf_offset + (eocd-buf))) {
637 	_zip_error_set(error, ZIP_ER_INCONS, 0);
638 	return NULL;
639     }
640 
641     if ((cd=_zip_cdir_new(nentry, error)) == NULL)
642 	return NULL;
643 
644     cd->size = (off_t)size;
645     cd->offset = (off_t)offset;
646 
647     return cd;
648 }
649 
650 
651 
652 static struct zip_cdir *
_zip_read_eocd64(FILE * f,const zip_uint8_t * eocd64loc,const zip_uint8_t * buf,off_t buf_offset,size_t buflen,unsigned int flags,struct zip_error * error)653 _zip_read_eocd64(FILE *f, const zip_uint8_t *eocd64loc, const zip_uint8_t *buf,
654 		 off_t buf_offset, size_t buflen, unsigned int flags, struct zip_error *error)
655 {
656     struct zip_cdir *cd;
657     zip_uint64_t offset;
658     const zip_uint8_t *cdp;
659     zip_uint8_t eocd[EOCD64LEN];
660     zip_uint64_t eocd_offset;
661     zip_uint64_t size, nentry, i;
662 
663     cdp = eocd64loc+8;
664     eocd_offset = _zip_read8(&cdp);
665 
666     if (eocd_offset > ZIP_OFF_MAX || eocd_offset + EOCD64LEN > ZIP_OFF_MAX) {
667         _zip_error_set(error, ZIP_ER_SEEK, EFBIG);
668         return NULL;
669     }
670 
671     if (eocd64loc < buf || (off_t)eocd_offset+EOCD64LEN > (buf_offset+(eocd64loc-buf))) {
672 	_zip_error_set(error, ZIP_ER_INCONS, 0);
673 	return NULL;
674     }
675 
676     if ((off_t)eocd_offset >= buf_offset && (off_t)eocd_offset+EOCD64LEN <= buf_offset+(ssize_t)buflen)
677 	cdp = buf+((off_t)eocd_offset-buf_offset);
678     else {
679 	if (fseeko(f, (off_t)eocd_offset, SEEK_SET) != 0) {
680 	    _zip_error_set(error, ZIP_ER_SEEK, errno);
681 	    return NULL;
682 	}
683 
684 	clearerr(f);
685 	if (fread(eocd, 1, EOCD64LEN, f) < EOCD64LEN) {
686             _zip_error_set(error, ZIP_ER_READ, errno);
687             return NULL;
688         }
689 
690 	if (ferror(f)) {
691 	    _zip_error_set(error, ZIP_ER_READ, errno);
692 	    return NULL;
693 	}
694 
695 	cdp = eocd;
696     }
697 
698     if (memcmp(cdp, EOCD64_MAGIC, 4) != 0) {
699 	_zip_error_set(error, ZIP_ER_INCONS, 0);
700 	return NULL;
701     }
702     cdp += 4;
703 
704     size = _zip_read8(&cdp);
705 
706     if ((flags & ZIP_CHECKCONS) && size+eocd_offset+12 != (zip_uint64_t)(buf_offset+(eocd64loc-buf))) {
707 	_zip_error_set(error, ZIP_ER_INCONS, 0);
708 	return NULL;
709     }
710 
711     cdp += 4; /* skip version made by/needed */
712     cdp += 8; /* skip num disks */
713 
714     nentry = _zip_read8(&cdp);
715     i = _zip_read8(&cdp);
716 
717     if (nentry != i) {
718 	_zip_error_set(error, ZIP_ER_MULTIDISK, 0);
719 	return NULL;
720     }
721 
722     size = _zip_read8(&cdp);
723     offset = _zip_read8(&cdp);
724 
725     if (size > ZIP_OFF_MAX || offset > ZIP_OFF_MAX || offset+size > ZIP_OFF_MAX) {
726         _zip_error_set(error, ZIP_ER_SEEK, EFBIG);
727         return NULL;
728     }
729     if (offset+size > buf_offset + eocd_offset) {
730 	/* cdir spans past EOCD record */
731 	_zip_error_set(error, ZIP_ER_INCONS, 0);
732 	return NULL;
733     }
734     if ((flags & ZIP_CHECKCONS) && offset+size != buf_offset + eocd_offset) {
735 	_zip_error_set(error, ZIP_ER_INCONS, 0);
736 	return NULL;
737     }
738 
739     if ((cd=_zip_cdir_new(nentry, error)) == NULL)
740 	return NULL;
741 
742 
743     cd->size = (off_t)size;
744     cd->offset = (off_t)offset;
745 
746     return cd;
747 }
748