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_readcdir(FILE *fp, unsigned char *buf, unsigned char *eocd, int buflen, int flags, struct zip_error *error) { struct zip_cdir *cd; unsigned char *cdp, **bufp; int i, comlen, nentry; unsigned int left; comlen = buf + buflen - eocd - EOCDLEN; if (comlen < 0) { /* not enough bytes left for comment */ _zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } /* check for end-of-central-dir magic */ if (memcmp(eocd, EOCD_MAGIC, 4) != 0) { _zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } if (memcmp(eocd+4, "\0\0\0\0", 4) != 0) { _zip_error_set(error, ZIP_ER_MULTIDISK, 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 ((cd=_zip_cdir_new(nentry, error)) == NULL) return NULL; cd->size = _zip_read4(&cdp); cd->offset = _zip_read4(&cdp); cd->comment = NULL; cd->comment_len = _zip_read2(&cdp); if ((comlen < cd->comment_len) || (cd->nentry != i)) { _zip_error_set(error, ZIP_ER_NOZIP, 0); free(cd); return NULL; } if ((flags & ZIP_CHECKCONS) && comlen != cd->comment_len) { _zip_error_set(error, ZIP_ER_INCONS, 0); free(cd); return NULL; } if (cd->comment_len) { if ((cd->comment=(char *)_zip_memdup(eocd+EOCDLEN, cd->comment_len, error)) == NULL) { free(cd); return NULL; } } if (cd->size < (unsigned int)(eocd-buf)) { /* if buffer already read in, use it */ cdp = eocd - cd->size; bufp = &cdp; } else { /* go to start of cdir and read it entry by entry */ bufp = NULL; clearerr(fp); 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)) { /* 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); free(cd); return NULL; } } left = cd->size; i=0; do { if (i == cd->nentry && left > 0) { /* Infozip extension for more than 64k entries: nentries wraps around, size indicates correct EOCD */ _zip_cdir_grow(cd, cd->nentry+0x10000, error); } if ((_zip_dirent_read(cd->entry+i, fp, bufp, &left, 0, error)) < 0) { cd->nentry = i; _zip_cdir_free(cd); return NULL; } i++; } while (i<cd->nentry); return cd; }
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) { struct zip_cdir *cd; unsigned char *cdp, **bufp; int i, comlen, nentry; zip_uint32_t left; comlen = buf + buflen - eocd - EOCDLEN; if (comlen < 0) { /* not enough bytes left for comment */ _zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } /* check for end-of-central-dir magic */ if (memcmp(eocd, EOCD_MAGIC, 4) != 0) { _zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } if (memcmp(eocd+4, "\0\0\0\0", 4) != 0) { _zip_error_set(error, ZIP_ER_MULTIDISK, 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 ((cd=_zip_cdir_new(nentry, error)) == NULL) return NULL; cd->size = _zip_read4(&cdp); cd->offset = _zip_read4(&cdp); cd->comment = NULL; cd->comment_len = _zip_read2(&cdp); /* without checking the ZIP_CHECKCONS flag we'll not able even to open inconsistent archives at this place, which would break bc in PHP */ if ((ZIP_CHECKCONS == (flags & ZIP_CHECKCONS)) && ((zip_uint64_t)cd->offset)+cd->size > buf_offset + (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) { _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; _zip_cdir_free(cd); return NULL; } } /* the first if branch goes the old way of libzip 0.9 so we don't loose the bc for reading inconsistent files */ if ((ZIP_CHECKCONS != (flags & ZIP_CHECKCONS)) && cd->size < (unsigned int)(eocd-buf)) { cdp = eocd - cd->size; bufp = &cdp; } else if (cd->offset >= buf_offset) { /* if buffer already read in, use it */ cdp = buf + (cd->offset - buf_offset); bufp = &cdp; } else { /* go to start of cdir and read it entry by entry */ bufp = NULL; clearerr(fp); 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)) { /* 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; i=0; while (i<cd->nentry && left > 0) { if ((_zip_dirent_read(cd->entry+i, fp, bufp, &left, 0, error)) < 0) { cd->nentry = i; _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; return cd; }