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; }
struct zip_extra_field * _zip_ef_parse(const zip_uint8_t *data, zip_uint16_t len, zip_flags_t flags, struct zip_error *error) { struct zip_extra_field *ef, *ef2, *ef_head; const zip_uint8_t *p; zip_uint16_t fid, flen; ef_head = NULL; for (p=data; p<data+len; p+=flen) { if (p+4 > data+len) { _zip_error_set(error, ZIP_ER_INCONS, 0); _zip_ef_free(ef_head); return NULL; } fid = _zip_read2(&p); flen = _zip_read2(&p); if (p+flen > data+len) { _zip_error_set(error, ZIP_ER_INCONS, 0); _zip_ef_free(ef_head); return NULL; } if ((ef2=_zip_ef_new(fid, flen, p, flags)) == NULL) { _zip_error_set(error, ZIP_ER_MEMORY, 0); _zip_ef_free(ef_head); return NULL; } if (ef_head) { ef->next = ef2; ef = ef2; } else ef_head = ef = ef2; } return ef_head; }
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) { struct zip_cdir *cd; const unsigned char *cdp; const unsigned char **bufp; zip_int64_t tail_len, comment_len; zip_uint64_t i, left; 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; } /* 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; } 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 == NULL) return NULL; cdp = eocd + 20; comment_len = _zip_read2(&cdp); 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); _zip_cdir_free(cd); return NULL; } if (tail_len < comment_len || ((flags & ZIP_CHECKCONS) && tail_len != comment_len)) { _zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } 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; } } 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) || (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); _zip_cdir_free(cd); return NULL; } } left = (zip_uint64_t)cd->size; i=0; while (i<cd->nentry && left > 0) { 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 || ((flags & ZIP_CHECKCONS) && left != 0)) { _zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } return cd; }
int _zip_read_local_ef(struct zip *za, zip_uint64_t idx) { struct zip_entry *e; unsigned char b[4]; const unsigned char *p; zip_uint16_t fname_len, ef_len; if (idx >= za->nentry) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return -1; } e = za->entry+idx; if (e->orig == NULL || e->orig->local_extra_fields_read) return 0; if (fseeko(za->zp, (off_t)(e->orig->offset + 26), SEEK_SET) < 0) { _zip_error_set(&za->error, ZIP_ER_SEEK, errno); return -1; } if (fread(b, sizeof(b), 1, za->zp) != 1) { _zip_error_set(&za->error, ZIP_ER_READ, errno); return -1; } p = b; fname_len = _zip_read2(&p); ef_len = _zip_read2(&p); if (ef_len > 0) { struct zip_extra_field *ef; zip_uint8_t *ef_raw; if (fseek(za->zp, fname_len, SEEK_CUR) < 0) { _zip_error_set(&za->error, ZIP_ER_SEEK, errno); return -1; } ef_raw = _zip_read_data(NULL, za->zp, ef_len, 0, &za->error); if (ef_raw == NULL) return -1; if ((ef=_zip_ef_parse(ef_raw, ef_len, ZIP_EF_LOCAL, &za->error)) == NULL) { free(ef_raw); return -1; } free(ef_raw); ef = _zip_ef_remove_internal(ef); e->orig->extra_fields = _zip_ef_merge(e->orig->extra_fields, ef); } e->orig->local_extra_fields_read = 1; if (e->changes && e->changes->local_extra_fields_read == 0) { e->changes->extra_fields = e->orig->extra_fields; e->changes->local_extra_fields_read = 1; } return 0; }
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; }