diff options
Diffstat (limited to 'ext/zip/lib/zip_open.c')
-rw-r--r-- | ext/zip/lib/zip_open.c | 480 |
1 files changed, 307 insertions, 173 deletions
diff --git a/ext/zip/lib/zip_open.c b/ext/zip/lib/zip_open.c index 5ce1c1070a..d91fe469db 100644 --- a/ext/zip/lib/zip_open.c +++ b/ext/zip/lib/zip_open.c @@ -1,6 +1,6 @@ /* zip_open.c -- open zip archive by name - Copyright (C) 1999-2011 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2012 Dieter Baron and Thomas Klausner This file is part of libzip, a library to manipulate ZIP archives. The authors can be contacted at <libzip@nih.at> @@ -42,38 +42,53 @@ #include "zipint.h" -static void set_error(int *, struct zip_error *, int); -static struct zip *_zip_allocate_new(const char *, int *); -static int _zip_checkcons(FILE *, struct zip_cdir *, struct zip_error *); -static void _zip_check_torrentzip(struct zip *); -static struct zip_cdir *_zip_find_central_dir(FILE *, int, int *, off_t); -static int _zip_file_exists(const char *, int, int *); -static int _zip_headercomp(struct zip_dirent *, int, - struct zip_dirent *, int); -static unsigned char *_zip_memmem(const unsigned char *, int, - const unsigned char *, int); -static struct zip_cdir *_zip_readcdir(FILE *, off_t, unsigned char *, unsigned char *, - int, int, struct zip_error *); +static void set_error(int *, const struct zip_error *, int); +static struct zip *_zip_allocate_new(const char *, unsigned int, int *); +static zip_int64_t _zip_checkcons(FILE *, struct zip_cdir *, struct zip_error *); +static void _zip_check_torrentzip(struct zip *, const struct zip_cdir *); +static struct zip_cdir *_zip_find_central_dir(FILE *, unsigned int, int *, off_t); +static int _zip_file_exists(const char *, unsigned int, int *); +static int _zip_headercomp(const struct zip_dirent *, const struct zip_dirent *); +static unsigned char *_zip_memmem(const unsigned char *, size_t, + const unsigned char *, size_t); +static struct zip_cdir *_zip_readcdir(FILE *, off_t, unsigned char *, const unsigned char *, + size_t, unsigned int, struct zip_error *); +static struct zip_cdir *_zip_read_eocd(const unsigned char *, const unsigned char *, off_t, + size_t, unsigned int, struct zip_error *); +static struct zip_cdir *_zip_read_eocd64(FILE *, const unsigned char *, const unsigned char *, + off_t, size_t, unsigned int, struct zip_error *); -ZIP_EXTERN(struct zip *) -zip_open(const char *fn, int flags, int *zep) +ZIP_EXTERN struct zip * +zip_open(const char *fn, int _flags, int *zep) { FILE *fp; - - if (flags & ZIP_OVERWRITE) { - return _zip_allocate_new(fn, zep); + unsigned int flags; + + if (_flags < 0) { + if (zep) + *zep = ZIP_ER_INVAL; + return NULL; } - + flags = (unsigned int)_flags; + switch (_zip_file_exists(fn, flags, zep)) { case -1: - if (!(flags & ZIP_OVERWRITE)) { - return NULL; - } + return NULL; case 0: - return _zip_allocate_new(fn, zep); + return _zip_allocate_new(fn, flags, zep); default: + if (flags & ZIP_TRUNCATE) { + FILE *f; + + if ((f = fopen(fn, "rb")) == NULL) { + set_error(zep, NULL, ZIP_ER_OPEN); + return NULL; + } + fclose(f); + return _zip_allocate_new(fn, flags, zep); + } break; } @@ -82,17 +97,36 @@ zip_open(const char *fn, int flags, int *zep) return NULL; } - return _zip_open(fn, fp, flags, 0, zep); + return _zip_open(fn, fp, flags, zep); } + +ZIP_EXTERN int +zip_archive_set_tempdir(struct zip *za, const char *tempdir) +{ + char *new_tempdir; + + if (tempdir) { + if ((new_tempdir = strdup(tempdir)) == NULL) { + _zip_error_set(&za->error, ZIP_ER_MEMORY, errno); + return -1; + } + } + else + new_tempdir = NULL; + + free(za->tempdir); + za->tempdir = new_tempdir; + + return 0; +} struct zip * -_zip_open(const char *fn, FILE *fp, int flags, int aflags, int *zep) +_zip_open(const char *fn, FILE *fp, unsigned int flags, int *zep) { struct zip *za; struct zip_cdir *cdir; - int i; off_t len; if (fseeko(fp, 0, SEEK_END) < 0) { @@ -103,7 +137,7 @@ _zip_open(const char *fn, FILE *fp, int flags, int aflags, int *zep) /* treat empty files as empty archives */ if (len == 0) { - if ((za=_zip_allocate_new(fn, zep)) == NULL) + if ((za=_zip_allocate_new(fn, flags, zep)) == NULL) fclose(fp); else za->zp = fp; @@ -116,34 +150,32 @@ _zip_open(const char *fn, FILE *fp, int flags, int aflags, int *zep) return NULL; } - if ((za=_zip_allocate_new(fn, zep)) == NULL) { + if ((za=_zip_allocate_new(fn, flags, zep)) == NULL) { _zip_cdir_free(cdir); fclose(fp); return NULL; } - za->cdir = cdir; + za->entry = cdir->entry; + za->nentry = cdir->nentry; + za->nentry_alloc = cdir->nentry_alloc; + za->comment_orig = cdir->comment; + za->zp = fp; - if ((za->entry=(struct zip_entry *)malloc(sizeof(*(za->entry)) - * cdir->nentry)) == NULL) { - set_error(zep, NULL, ZIP_ER_MEMORY); - _zip_free(za); - return NULL; - } - for (i=0; i<cdir->nentry; i++) - _zip_entry_new(za); + _zip_check_torrentzip(za, cdir); - _zip_check_torrentzip(za); za->ch_flags = za->flags; + free(cdir); + return za; } static void -set_error(int *zep, struct zip_error *err, int ze) +set_error(int *zep, const struct zip_error *err, int ze) { int se; @@ -166,16 +198,17 @@ set_error(int *zep, struct zip_error *err, int ze) entries, or NULL if unsuccessful. */ static struct zip_cdir * -_zip_readcdir(FILE *fp, off_t buf_offset, unsigned char *buf, unsigned char *eocd, int buflen, - int flags, struct zip_error *error) +_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) { struct zip_cdir *cd; - unsigned char *cdp, **bufp; - int i, comlen, nentry; - zip_uint32_t left; + const unsigned char *cdp; + const unsigned char **bufp; + zip_int64_t tail_len, comment_len; + zip_uint64_t i, left; - comlen = buf + buflen - eocd - EOCDLEN; - if (comlen < 0) { + tail_len = buf + buflen - eocd - EOCDLEN; + if (tail_len < 0) { /* not enough bytes left for comment */ _zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; @@ -192,46 +225,32 @@ _zip_readcdir(FILE *fp, off_t buf_offset, unsigned char *buf, unsigned char *eoc return NULL; } - cdp = eocd + 8; - /* number of cdir-entries on this disk */ - i = _zip_read2(&cdp); - /* number of cdir-entries */ - nentry = _zip_read2(&cdp); + if (eocd-EOCD64LOCLEN >= buf && memcmp(eocd-EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) + cd = _zip_read_eocd64(fp, eocd-EOCD64LOCLEN, buf, buf_offset, buflen, flags, error); + else + cd = _zip_read_eocd(eocd, buf, buf_offset, buflen, flags, error); - if ((cd=_zip_cdir_new(nentry, error)) == NULL) + if (cd == NULL) return NULL; - cd->size = _zip_read4(&cdp); - cd->offset = _zip_read4(&cdp); - cd->comment = NULL; - cd->comment_len = _zip_read2(&cdp); + cdp = eocd + 20; + comment_len = _zip_read2(&cdp); - if (((zip_uint64_t)cd->offset)+cd->size > buf_offset + (eocd-buf)) { + if ((zip_uint64_t)cd->offset+(zip_uint64_t)cd->size > (zip_uint64_t)buf_offset + (zip_uint64_t)(eocd-buf)) { /* cdir spans past EOCD record */ _zip_error_set(error, ZIP_ER_INCONS, 0); - cd->nentry = 0; _zip_cdir_free(cd); return NULL; } - if ((comlen < cd->comment_len) || (cd->nentry != i)) { - _zip_error_set(error, ZIP_ER_NOZIP, 0); - cd->nentry = 0; - _zip_cdir_free(cd); - return NULL; - } - if ((flags & ZIP_CHECKCONS) && comlen != cd->comment_len) { + if (tail_len < comment_len || ((flags & ZIP_CHECKCONS) && tail_len != comment_len)) { _zip_error_set(error, ZIP_ER_INCONS, 0); - cd->nentry = 0; _zip_cdir_free(cd); return NULL; } - if (cd->comment_len) { - if ((cd->comment=(char *)_zip_memdup(eocd+EOCDLEN, - cd->comment_len, error)) - == NULL) { - cd->nentry = 0; + if (comment_len) { + if ((cd->comment=_zip_string_new(eocd+EOCDLEN, (zip_uint16_t)comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) { _zip_cdir_free(cd); return NULL; } @@ -249,40 +268,32 @@ _zip_readcdir(FILE *fp, off_t buf_offset, unsigned char *buf, unsigned char *eoc fseeko(fp, cd->offset, SEEK_SET); /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */ - if (ferror(fp) || ((unsigned long)ftello(fp) != cd->offset)) { + if (ferror(fp) || (ftello(fp) != cd->offset)) { /* seek error or offset of cdir wrong */ if (ferror(fp)) _zip_error_set(error, ZIP_ER_SEEK, errno); else _zip_error_set(error, ZIP_ER_NOZIP, 0); - cd->nentry = 0; _zip_cdir_free(cd); return NULL; } } - left = cd->size; + left = (zip_uint64_t)cd->size; i=0; while (i<cd->nentry && left > 0) { - if ((_zip_dirent_read(cd->entry+i, fp, bufp, &left, 0, error)) < 0) { - cd->nentry = i; + if ((cd->entry[i].orig=_zip_dirent_new()) == NULL + || (_zip_dirent_read(cd->entry[i].orig, fp, bufp, &left, 0, error)) < 0) { _zip_cdir_free(cd); return NULL; } i++; - - if (i == cd->nentry && left > 0) { - /* Infozip extension for more than 64k entries: - nentries wraps around, size indicates correct EOCD */ - if (_zip_cdir_grow(cd, cd->nentry+ZIP_UINT16_MAX, error) < 0) { - cd->nentry = i; - _zip_cdir_free(cd); - return NULL; - } - } } - - cd->nentry = i; + if (i != cd->nentry || ((flags & ZIP_CHECKCONS) && left != 0)) { + _zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_cdir_free(cd); + return NULL; + } return cd; } @@ -295,54 +306,59 @@ _zip_readcdir(FILE *fp, off_t buf_offset, unsigned char *buf, unsigned char *eoc file and header offsets. Returns -1 if not plausible, else the difference between the lowest and the highest fileposition reached */ -static int +static zip_int64_t _zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error) { - int i; - unsigned int min, max, j; + zip_uint64_t i; + zip_uint64_t min, max, j; struct zip_dirent temp; if (cd->nentry) { - max = cd->entry[0].offset; - min = cd->entry[0].offset; + max = cd->entry[0].orig->offset; + min = cd->entry[0].orig->offset; } else min = max = 0; for (i=0; i<cd->nentry; i++) { - if (cd->entry[i].offset < min) - min = cd->entry[i].offset; - if (min > cd->offset) { + if (cd->entry[i].orig->offset < min) + min = cd->entry[i].orig->offset; + if (min > (zip_uint64_t)cd->offset) { _zip_error_set(error, ZIP_ER_NOZIP, 0); return -1; } - j = cd->entry[i].offset + cd->entry[i].comp_size - + cd->entry[i].filename_len + LENTRYSIZE; + j = cd->entry[i].orig->offset + cd->entry[i].orig->comp_size + + _zip_string_length(cd->entry[i].orig->filename) + LENTRYSIZE; if (j > max) max = j; - if (max > cd->offset) { + if (max > (zip_uint64_t)cd->offset) { _zip_error_set(error, ZIP_ER_NOZIP, 0); return -1; } - if (fseeko(fp, cd->entry[i].offset, SEEK_SET) != 0) { - _zip_error_set(error, ZIP_ER_SEEK, 0); + if (fseeko(fp, (off_t)cd->entry[i].orig->offset, SEEK_SET) != 0) { + _zip_error_set(error, ZIP_ER_SEEK, errno); return -1; } if (_zip_dirent_read(&temp, fp, NULL, NULL, 1, error) == -1) return -1; - if (_zip_headercomp(cd->entry+i, 0, &temp, 1) != 0) { + if (_zip_headercomp(cd->entry[i].orig, &temp) != 0) { _zip_error_set(error, ZIP_ER_INCONS, 0); _zip_dirent_finalize(&temp); return -1; } + + cd->entry[i].orig->extra_fields = _zip_ef_merge(cd->entry[i].orig->extra_fields, temp.extra_fields); + cd->entry[i].orig->local_extra_fields_read = 1; + temp.extra_fields = NULL; + _zip_dirent_finalize(&temp); } - return max - min; + return (max-min) < ZIP_INT64_MAX ? (zip_int64_t)(max-min) : ZIP_INT64_MAX; } @@ -351,28 +367,27 @@ _zip_checkcons(FILE *fp, struct zip_cdir *cd, struct zip_error *error) check whether ZA has a valid TORRENTZIP comment, i.e. is torrentzipped */ static void -_zip_check_torrentzip(struct zip *za) +_zip_check_torrentzip(struct zip *za, const struct zip_cdir *cdir) { uLong crc_got, crc_should; char buf[8+1]; char *end; - if (za->zp == NULL || za->cdir == NULL) + if (za->zp == NULL || cdir == NULL) return; - if (za->cdir->comment_len != TORRENT_SIG_LEN+8 - || strncmp(za->cdir->comment, TORRENT_SIG, TORRENT_SIG_LEN) != 0) + if (_zip_string_length(cdir->comment) != TORRENT_SIG_LEN+8 + || strncmp((const char *)cdir->comment->raw, TORRENT_SIG, TORRENT_SIG_LEN) != 0) return; - memcpy(buf, za->cdir->comment+TORRENT_SIG_LEN, 8); + memcpy(buf, cdir->comment->raw+TORRENT_SIG_LEN, 8); buf[8] = '\0'; errno = 0; crc_should = strtoul(buf, &end, 16); if ((crc_should == UINT_MAX && errno != 0) || (end && *end)) return; - if (_zip_filerange_crc(za->zp, za->cdir->offset, za->cdir->size, - &crc_got, NULL) < 0) + if (_zip_filerange_crc(za->zp, cdir->offset, cdir->size, &crc_got, NULL) < 0) return; if (crc_got == crc_should) @@ -383,68 +398,32 @@ _zip_check_torrentzip(struct zip *za) /* _zip_headercomp: - compares two headers h1 and h2; if they are local headers, set - local1p or local2p respectively to 1, else 0. Return 0 if they - are identical, -1 if not. */ + compares a central directory entry and a local file header + Return 0 if they are consistent, -1 if not. */ static int -_zip_headercomp(struct zip_dirent *h1, int local1p, struct zip_dirent *h2, - int local2p) +_zip_headercomp(const struct zip_dirent *central, const struct zip_dirent *local) { - if ((h1->version_needed != h2->version_needed) + if ((central->version_needed != local->version_needed) #if 0 /* some zip-files have different values in local and global headers for the bitflags */ - || (h1->bitflags != h2->bitflags) + || (central->bitflags != local->bitflags) #endif - || (h1->comp_method != h2->comp_method) - || (h1->last_mod != h2->last_mod) - || (h1->filename_len != h2->filename_len) - || !h1->filename || !h2->filename - || strcmp(h1->filename, h2->filename)) + || (central->comp_method != local->comp_method) + || (central->last_mod != local->last_mod) + || !_zip_string_equal(central->filename, local->filename)) return -1; - /* check that CRC and sizes are zero if data descriptor is used */ - if ((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local1p - && (h1->crc != 0 - || h1->comp_size != 0 - || h1->uncomp_size != 0)) - return -1; - if ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) && local2p - && (h2->crc != 0 - || h2->comp_size != 0 - || h2->uncomp_size != 0)) - return -1; - - /* check that CRC and sizes are equal if no data descriptor is used */ - if (((h1->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local1p == 0) - && ((h2->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 || local2p == 0)) { - if ((h1->crc != h2->crc) - || (h1->comp_size != h2->comp_size) - || (h1->uncomp_size != h2->uncomp_size)) + + if ((central->crc != local->crc) || (central->comp_size != local->comp_size) + || (central->uncomp_size != local->uncomp_size)) { + /* InfoZip stores valid values in local header even when data descriptor is used. + This is in violation of the appnote. */ + if (((local->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) == 0 + || local->crc != 0 || local->comp_size != 0 || local->uncomp_size != 0)) return -1; } - - if ((local1p == local2p) - && ((h1->extrafield_len != h2->extrafield_len) - || (h1->extrafield_len && h2->extrafield - && memcmp(h1->extrafield, h2->extrafield, - h1->extrafield_len)))) - return -1; - - /* if either is local, nothing more to check */ - if (local1p || local2p) - return 0; - - if ((h1->version_madeby != h2->version_madeby) - || (h1->disk_number != h2->disk_number) - || (h1->int_attrib != h2->int_attrib) - || (h1->ext_attrib != h2->ext_attrib) - || (h1->offset != h2->offset) - || (h1->comment_len != h2->comment_len) - || (h1->comment_len && h2->comment - && memcmp(h1->comment, h2->comment, h1->comment_len))) - return -1; return 0; } @@ -452,7 +431,7 @@ _zip_headercomp(struct zip_dirent *h1, int local1p, struct zip_dirent *h2, static struct zip * -_zip_allocate_new(const char *fn, int *zep) +_zip_allocate_new(const char *fn, unsigned int flags, int *zep) { struct zip *za; struct zip_error error; @@ -467,18 +446,19 @@ _zip_allocate_new(const char *fn, int *zep) else { za->zn = strdup(fn); if (!za->zn) { - _zip_free(za); + zip_discard(za); set_error(zep, NULL, ZIP_ER_MEMORY); return NULL; } } + za->open_flags = flags; return za; } static int -_zip_file_exists(const char *fn, int flags, int *zep) +_zip_file_exists(const char *fn, unsigned int flags, int *zep) { struct stat st; @@ -488,7 +468,7 @@ _zip_file_exists(const char *fn, int flags, int *zep) } if (stat(fn, &st) != 0) { - if (flags & ZIP_CREATE || flags & ZIP_OVERWRITE) + if (flags & ZIP_CREATE) return 0; else { set_error(zep, NULL, ZIP_ER_OPEN); @@ -508,14 +488,21 @@ _zip_file_exists(const char *fn, int flags, int *zep) static struct zip_cdir * -_zip_find_central_dir(FILE *fp, int flags, int *zep, off_t len) +_zip_find_central_dir(FILE *fp, unsigned int flags, int *zep, off_t len) { struct zip_cdir *cdir, *cdirnew; unsigned char *buf, *match; off_t buf_offset; - int a, best, buflen, i; + size_t buflen; + zip_int64_t a, i; + zip_int64_t best; struct zip_error zerr; + if (len < (off_t)EOCDLEN) { + set_error(zep, NULL, ZIP_ER_NOZIP); + return NULL; + } + i = fseeko(fp, -(len < CDBUFSIZE ? len : CDBUFSIZE), SEEK_END); if (i == -1 && errno != EFBIG) { /* seek before start of file on my machine */ @@ -541,10 +528,10 @@ _zip_find_central_dir(FILE *fp, int flags, int *zep, off_t len) best = -1; cdir = NULL; - match = buf; + match = buf+ (buflen < CDBUFSIZE ? 0 : EOCD64LOCLEN); _zip_error_set(&zerr, ZIP_ER_NOZIP, 0); - while ((match=_zip_memmem(match, buflen-(match-buf)-18, + while ((match=_zip_memmem(match, buflen-(size_t)(match-buf)-(EOCDLEN-4), (const unsigned char *)EOCD_MAGIC, 4))!=NULL) { /* found match -- check, if good */ /* to avoid finding the same match all over again */ @@ -589,8 +576,7 @@ _zip_find_central_dir(FILE *fp, int flags, int *zep, off_t len) static unsigned char * -_zip_memmem(const unsigned char *big, int biglen, const unsigned char *little, - int littlelen) +_zip_memmem(const unsigned char *big, size_t biglen, const unsigned char *little, size_t littlelen) { const unsigned char *p; @@ -598,11 +584,159 @@ _zip_memmem(const unsigned char *big, int biglen, const unsigned char *little, return NULL; p = big-1; while ((p=(const unsigned char *) - memchr(p+1, little[0], (size_t)(big-(p+1)+biglen-littlelen+1))) - != NULL) { + memchr(p+1, little[0], (size_t)(big-(p+1))+(size_t)(biglen-littlelen)+1)) != NULL) { if (memcmp(p+1, little+1, littlelen-1)==0) return (unsigned char *)p; } return NULL; } + + + +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) +{ + struct zip_cdir *cd; + const unsigned char *cdp; + zip_uint64_t i, nentry, size, offset; + + if (eocd+EOCDLEN > buf+buflen) { + _zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + + cdp = eocd + 8; + + /* number of cdir-entries on this disk */ + i = _zip_read2(&cdp); + /* number of cdir-entries */ + nentry = _zip_read2(&cdp); + + if (nentry != i) { + _zip_error_set(error, ZIP_ER_NOZIP, 0); + return NULL; + } + + size = _zip_read4(&cdp); + offset = _zip_read4(&cdp); + + if (size > ZIP_OFF_MAX || offset > ZIP_OFF_MAX || offset+size > ZIP_OFF_MAX) { + _zip_error_set(error, ZIP_ER_SEEK, EFBIG); + return NULL; + } + + if (offset+size > (zip_uint64_t)(buf_offset + (eocd-buf))) { + /* cdir spans past EOCD record */ + _zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + + if ((flags & ZIP_CHECKCONS) && offset+size != (zip_uint64_t)(buf_offset + (eocd-buf))) { + _zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + + if ((cd=_zip_cdir_new(nentry, error)) == NULL) + return NULL; + + cd->size = (off_t)size; + cd->offset = (off_t)offset; + + return cd; +} + + + +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) +{ + struct zip_cdir *cd; + zip_uint64_t offset; + const zip_uint8_t *cdp; + zip_uint8_t eocd[EOCD64LEN]; + zip_uint64_t eocd_offset; + zip_uint64_t size, nentry, i; + + cdp = eocd64loc+8; + eocd_offset = _zip_read8(&cdp); + + if (eocd_offset > ZIP_OFF_MAX || eocd_offset + EOCD64LEN > ZIP_OFF_MAX) { + _zip_error_set(error, ZIP_ER_SEEK, EFBIG); + return NULL; + } + + if (eocd64loc < buf || (off_t)eocd_offset+EOCD64LEN > (buf_offset+(eocd64loc-buf))) { + _zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + + if ((off_t)eocd_offset >= buf_offset && (off_t)eocd_offset+EOCD64LEN <= buf_offset+(ssize_t)buflen) + cdp = buf+((off_t)eocd_offset-buf_offset); + else { + if (fseeko(f, (off_t)eocd_offset, SEEK_SET) != 0) { + _zip_error_set(error, ZIP_ER_SEEK, errno); + return NULL; + } + + clearerr(f); + if (fread(eocd, 1, EOCD64LEN, f) < EOCD64LEN) { + _zip_error_set(error, ZIP_ER_READ, errno); + return NULL; + } + + if (ferror(f)) { + _zip_error_set(error, ZIP_ER_READ, errno); + return NULL; + } + + cdp = eocd; + } + + if (memcmp(cdp, EOCD64_MAGIC, 4) != 0) { + _zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + cdp += 4; + + size = _zip_read8(&cdp); + + if ((flags & ZIP_CHECKCONS) && size+eocd_offset+12 != (zip_uint64_t)(buf_offset+(eocd64loc-buf))) { + _zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + + cdp += 4; /* skip version made by/needed */ + cdp += 8; /* skip num disks */ + + nentry = _zip_read8(&cdp); + i = _zip_read8(&cdp); + + if (nentry != i) { + _zip_error_set(error, ZIP_ER_MULTIDISK, 0); + return NULL; + } + + size = _zip_read8(&cdp); + offset = _zip_read8(&cdp); + + if (size > ZIP_OFF_MAX || offset > ZIP_OFF_MAX || offset+size > ZIP_OFF_MAX) { + _zip_error_set(error, ZIP_ER_SEEK, EFBIG); + return NULL; + } + if ((flags & ZIP_CHECKCONS) && offset+size != eocd_offset) { + _zip_error_set(error, ZIP_ER_INCONS, 0); + return NULL; + } + + if ((cd=_zip_cdir_new(nentry, error)) == NULL) + return NULL; + + + cd->size = (off_t)size; + cd->offset = (off_t)offset; + + return cd; +} |