static struct zip_cdir * _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; 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 */ set_error(zep, NULL, ZIP_ER_SEEK); return NULL; } buf_offset = ftello(fp); /* 64k is too much for stack */ if ((buf=(unsigned char *)malloc(CDBUFSIZE)) == NULL) { set_error(zep, NULL, ZIP_ER_MEMORY); return NULL; } clearerr(fp); buflen = fread(buf, 1, CDBUFSIZE, fp); if (ferror(fp)) { set_error(zep, NULL, ZIP_ER_READ); free(buf); return NULL; } best = -1; cdir = NULL; match = buf+ (buflen < CDBUFSIZE ? 0 : EOCD64LOCLEN); _zip_error_set(&zerr, ZIP_ER_NOZIP, 0); 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 */ match++; if ((cdirnew=_zip_readcdir(fp, buf_offset, buf, match-1, buflen, flags, &zerr)) == NULL) continue; if (cdir) { if (best <= 0) best = _zip_checkcons(fp, cdir, &zerr); a = _zip_checkcons(fp, cdirnew, &zerr); if (best < a) { _zip_cdir_free(cdir); cdir = cdirnew; best = a; } else _zip_cdir_free(cdirnew); } else { cdir = cdirnew; if (flags & ZIP_CHECKCONS) best = _zip_checkcons(fp, cdir, &zerr); else best = 0; } cdirnew = NULL; } free(buf); if (best < 0) { set_error(zep, &zerr, 0); _zip_cdir_free(cdir); return NULL; } return cdir; }
static zip_cdir_t * _zip_find_central_dir(zip_t *za, zip_uint64_t len) { zip_cdir_t *cdir, *cdirnew; zip_uint8_t *match; zip_int64_t buf_offset; zip_uint64_t buflen; zip_int64_t a; zip_int64_t best; zip_error_t error; zip_buffer_t *buffer; if (len < EOCDLEN) { zip_error_set(&za->error, ZIP_ER_NOZIP, 0); return NULL; } buflen = (len < CDBUFSIZE ? len : CDBUFSIZE); if (zip_source_seek(za->src, -(zip_int64_t)buflen, SEEK_END) < 0) { zip_error_t *src_error = zip_source_error(za->src); if (zip_error_code_zip(src_error) != ZIP_ER_SEEK || zip_error_code_system(src_error) != EFBIG) { /* seek before start of file on my machine */ _zip_error_copy(&za->error, src_error); return NULL; } } if ((buf_offset = zip_source_tell(za->src)) < 0) { _zip_error_set_from_source(&za->error, za->src); return NULL; } if ((buffer = _zip_buffer_new_from_source(za->src, buflen, NULL, &za->error)) == NULL) { return NULL; } best = -1; cdir = NULL; if (buflen >= CDBUFSIZE) { /* EOCD64 locator is before EOCD, so leave place for it */ _zip_buffer_set_offset(buffer, EOCD64LOCLEN); } zip_error_set(&error, ZIP_ER_NOZIP, 0); match = _zip_buffer_get(buffer, 0); while ((match=_zip_memmem(match, _zip_buffer_left(buffer)-(EOCDLEN-4), (const unsigned char *)EOCD_MAGIC, 4)) != NULL) { _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer))); if ((cdirnew = _zip_read_cdir(za, buffer, (zip_uint64_t)buf_offset, &error)) != NULL) { if (cdir) { if (best <= 0) { best = _zip_checkcons(za, cdir, &error); } a = _zip_checkcons(za, cdirnew, &error); if (best < a) { _zip_cdir_free(cdir); cdir = cdirnew; best = a; } else { _zip_cdir_free(cdirnew); } } else { cdir = cdirnew; if (za->open_flags & ZIP_CHECKCONS) best = _zip_checkcons(za, cdir, &error); else { best = 0; } } cdirnew = NULL; } match++; _zip_buffer_set_offset(buffer, (zip_uint64_t)(match - _zip_buffer_data(buffer))); } _zip_buffer_free(buffer); if (best < 0) { _zip_error_copy(&za->error, &error); _zip_cdir_free(cdir); return NULL; } return cdir; }