struct zip * _zip_open(const char *fn, FILE *fp, int flags, int aflags, int *zep) { struct zip *za; struct zip_cdir *cdir; int i; off_t len; if (fseeko(fp, 0, SEEK_END) < 0) { *zep = ZIP_ER_SEEK; return NULL; } len = ftello(fp); /* treat empty files as empty archives */ if (len == 0) { if ((za=_zip_allocate_new(fn, zep)) == NULL) fclose(fp); else za->zp = fp; return za; } cdir = _zip_find_central_dir(fp, flags, zep, len); if (cdir == NULL) { fclose(fp); return NULL; } if ((za=_zip_allocate_new(fn, zep)) == NULL) { _zip_cdir_free(cdir); fclose(fp); return NULL; } za->cdir = cdir; 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); za->ch_flags = za->flags; return za; }
struct zip * _zip_open(const char *fn, FILE *fp, unsigned int flags, int *zep) { struct zip *za; struct zip_cdir *cdir; off_t len; if (fseeko(fp, 0, SEEK_END) < 0) { *zep = ZIP_ER_SEEK; return NULL; } len = ftello(fp); /* treat empty files as empty archives */ if (len == 0) { if ((za=_zip_allocate_new(fn, flags, zep)) == NULL) fclose(fp); else za->zp = fp; return za; } cdir = _zip_find_central_dir(fp, flags, zep, len); if (cdir == NULL) { fclose(fp); return NULL; } if ((za=_zip_allocate_new(fn, flags, zep)) == NULL) { _zip_cdir_free(cdir); fclose(fp); return NULL; } za->entry = cdir->entry; za->nentry = cdir->nentry; za->nentry_alloc = cdir->nentry_alloc; za->comment_orig = cdir->comment; za->zp = fp; _zip_check_torrentzip(za, cdir); za->ch_flags = za->flags; free(cdir); return za; }
void _zip_free(struct zip *za) { int i; if (za == NULL) return; if (za->zn) free(za->zn); if (za->zp) fclose(za->zp); _zip_cdir_free(za->cdir); if (za->entry) { for (i=0; i<za->nentry; i++) { _zip_entry_free(za->entry+i); } free(za->entry); } for (i=0; i<za->nfile; i++) { if (za->file[i]->error.zip_err == ZIP_ER_OK) { _zip_error_set(&za->file[i]->error, ZIP_ER_ZIPCLOSED, 0); za->file[i]->za = NULL; } } free(za->file); free(za); return; }
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 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; }
ZIP_EXTERN struct zip * zip_open(const char *fn, int flags, int *zep) { FILE *fp; struct zip *za; struct zip_cdir *cdir; int i; off_t len; switch (_zip_file_exists(fn, flags, zep)) { case -1: return NULL; case 0: return _zip_allocate_new(fn, zep); default: break; } if ((fp=fopen(fn, "rb")) == NULL) { set_error(zep, NULL, ZIP_ER_OPEN); return NULL; } fseeko(fp, 0, SEEK_END); len = ftello(fp); /* treat empty files as empty archives */ if (len == 0) { if ((za=_zip_allocate_new(fn, zep)) == NULL) fclose(fp); else za->zp = fp; return za; } cdir = _zip_find_central_dir(fp, flags, zep, len); if (cdir == NULL) { fclose(fp); return NULL; } if ((za=_zip_allocate_new(fn, zep)) == NULL) { _zip_cdir_free(cdir); fclose(fp); return NULL; } za->cdir = cdir; 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); za->ch_flags = za->flags; return za; }
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 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; }
static zip_cdir_t * _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) { zip_cdir_t *cd; zip_uint16_t comment_len; zip_uint64_t i, left; zip_uint64_t eocd_offset = _zip_buffer_offset(buffer); zip_buffer_t *cd_buffer; if (_zip_buffer_left(buffer) < EOCDLEN) { /* 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(_zip_buffer_get(buffer, 4), EOCD_MAGIC, 4) != 0) { zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) { _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN); cd = _zip_read_eocd64(za->src, buffer, buf_offset, za->flags, error); } else { _zip_buffer_set_offset(buffer, eocd_offset); cd = _zip_read_eocd(buffer, buf_offset, za->flags, error); } if (cd == NULL) return NULL; _zip_buffer_set_offset(buffer, eocd_offset + 20); comment_len = _zip_buffer_get_16(buffer); if (cd->offset + cd->size > buf_offset + eocd_offset) { /* cdir spans past EOCD record */ zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if (comment_len || (za->open_flags & ZIP_CHECKCONS)) { zip_uint64_t tail_len; _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN); tail_len = _zip_buffer_left(buffer); if (tail_len < comment_len || ((za->open_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(_zip_buffer_get(buffer, comment_len), comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) { _zip_cdir_free(cd); return NULL; } } } if (cd->offset >= buf_offset) { zip_uint8_t *data; /* if buffer already read in, use it */ _zip_buffer_set_offset(buffer, cd->offset - buf_offset); if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if ((cd_buffer = _zip_buffer_new(data, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); _zip_cdir_free(cd); return NULL; } } else { cd_buffer = NULL; if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) { _zip_error_set_from_source(error, za->src); _zip_cdir_free(cd); return NULL; } /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */ if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) { zip_error_set(error, ZIP_ER_NOZIP, 0); _zip_cdir_free(cd); return NULL; } } left = (zip_uint64_t)cd->size; i=0; while (left > 0) { bool grown = false; zip_int64_t entry_size; if (i == cd->nentry) { /* InfoZIP has a hack to avoid using Zip64: it stores nentries % 0x10000 */ /* This hack isn't applicable if we're using Zip64, or if there is no central directory entry following. */ if (cd->is_zip64 || left < CDENTRYSIZE) { break; } if (!_zip_cdir_grow(cd, 0x10000, error)) { _zip_cdir_free(cd); _zip_buffer_free(cd_buffer); return NULL; } grown = true; } if ((cd->entry[i].orig=_zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, error)) < 0) { if (grown && zip_error_code_zip(error) == ZIP_ER_NOZIP) { zip_error_set(error, ZIP_ER_INCONS, 0); } _zip_cdir_free(cd); _zip_buffer_free(cd_buffer); return NULL; } i++; left -= (zip_uint64_t)entry_size; } if (i != cd->nentry || left > 0) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } if (za->open_flags & ZIP_CHECKCONS) { bool ok; if (cd_buffer) { ok = _zip_buffer_eof(cd_buffer); } else { zip_int64_t offset = zip_source_tell(za->src); if (offset < 0) { _zip_error_set_from_source(error, za->src); _zip_cdir_free(cd); return NULL; } ok = ((zip_uint64_t)offset == cd->offset + cd->size); } if (!ok) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } } _zip_buffer_free(cd_buffer); return cd; }
ZIP_EXTERN int zip_close(struct zip *za) { int survivors; int i, j, error; char *temp; FILE *out; mode_t mask; struct zip_cdir *cd; struct zip_dirent de; struct filelist *filelist; int reopen_on_error; int new_torrentzip; reopen_on_error = 0; if (za == NULL) return -1; if (!_zip_changed(za, &survivors)) { _zip_free(za); return 0; } /* don't create zip files with no entries */ if (survivors == 0) { if (za->zn && za->zp) { if (remove(za->zn) != 0) { _zip_error_set(&za->error, ZIP_ER_REMOVE, errno); return -1; } } _zip_free(za); return 0; } if ((filelist=(struct filelist *)malloc(sizeof(filelist[0])*survivors)) == NULL) return -1; if ((cd=_zip_cdir_new(survivors, &za->error)) == NULL) { free(filelist); return -1; } for (i=0; i<survivors; i++) _zip_dirent_init(&cd->entry[i]); /* archive comment is special for torrentzip */ if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) { cd->comment = (char*)_zip_memdup((void*)(TORRENT_SIG "XXXXXXXX"), TORRENT_SIG_LEN + TORRENT_CRC_LEN, &za->error); if (cd->comment == NULL) { _zip_cdir_free(cd); free(filelist); return -1; } cd->comment_len = TORRENT_SIG_LEN + TORRENT_CRC_LEN; } else if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, ZIP_FL_UNCHANGED) == 0) { if (_zip_cdir_set_comment(cd, za) == -1) { _zip_cdir_free(cd); free(filelist); return -1; } } if ((temp=_zip_create_temp_output(za, &out)) == NULL) { _zip_cdir_free(cd); free(filelist); return -1; } /* create list of files with index into original archive */ for (i=j=0; i<za->nentry; i++) { if (za->entry[i].state == ZIP_ST_DELETED) continue; filelist[j].idx = i; filelist[j].name = zip_get_name(za, i, 0); j++; } survivors = j; if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) qsort(filelist, survivors, sizeof(filelist[0]), _zip_torrentzip_cmp); new_torrentzip = (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 1 && zip_get_archive_flag(za, ZIP_AFL_TORRENT, ZIP_FL_UNCHANGED) == 0); error = 0; for (j=0; j<survivors; j++) { i = filelist[j].idx; /* create new local directory entry */ if (ZIP_ENTRY_DATA_CHANGED(za->entry+i) || new_torrentzip) { _zip_dirent_init(&de); if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) _zip_dirent_torrent_normalize(&de); /* use it as central directory entry */ memcpy(cd->entry+j, &de, sizeof(cd->entry[j])); /* set/update file name */ if (za->entry[i].ch_filename == NULL) { if (za->entry[i].state == ZIP_ST_ADDED) { de.filename = strdup("-"); de.filename_len = 1; cd->entry[j].filename = "-"; cd->entry[j].filename_len = 1; } else { de.filename = strdup(za->cdir->entry[i].filename); de.filename_len = strlen(de.filename); cd->entry[j].filename = za->cdir->entry[i].filename; cd->entry[j].filename_len = de.filename_len; } } } else { /* copy existing directory entries */ if (fseeko(za->zp, za->cdir->entry[i].offset, SEEK_SET) != 0) { _zip_error_set(&za->error, ZIP_ER_SEEK, errno); error = 1; break; } if (_zip_dirent_read(&de, za->zp, NULL, NULL, 1, &za->error) != 0) { error = 1; break; } memcpy(cd->entry+j, za->cdir->entry+i, sizeof(cd->entry[j])); if (de.bitflags & ZIP_GPBF_DATA_DESCRIPTOR) { de.crc = za->cdir->entry[i].crc; de.comp_size = za->cdir->entry[i].comp_size; de.uncomp_size = za->cdir->entry[i].uncomp_size; de.bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR; cd->entry[j].bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR; } } if (za->entry[i].ch_filename) { free(de.filename); if ((de.filename=strdup(za->entry[i].ch_filename)) == NULL) { error = 1; break; } de.filename_len = strlen(de.filename); cd->entry[j].filename = za->entry[i].ch_filename; cd->entry[j].filename_len = de.filename_len; } if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0 && za->entry[i].ch_comment_len != -1) { /* as the rest of cd entries, its malloc/free is done by za */ cd->entry[j].comment = za->entry[i].ch_comment; cd->entry[j].comment_len = za->entry[i].ch_comment_len; } cd->entry[j].offset = (unsigned int)ftello(out); if (ZIP_ENTRY_DATA_CHANGED(za->entry+i) || new_torrentzip) { struct zip_source *zs; zs = NULL; if (!ZIP_ENTRY_DATA_CHANGED(za->entry+i)) { if ((zs=zip_source_zip(za, za, i, ZIP_FL_RECOMPRESS, 0, -1)) == NULL) { error = 1; break; } } if (add_data(za, zs ? zs : za->entry[i].source, &de, out) < 0) { error = 1; break; } cd->entry[j].last_mod = de.last_mod; cd->entry[j].comp_method = de.comp_method; cd->entry[j].comp_size = de.comp_size; cd->entry[j].uncomp_size = de.uncomp_size; cd->entry[j].crc = de.crc; } else { if (_zip_dirent_write(&de, out, 1, &za->error) < 0) { error = 1; break; } /* we just read the local dirent, file is at correct position */ if (copy_data(za->zp, cd->entry[j].comp_size, out, &za->error) < 0) { error = 1; break; } } _zip_dirent_finalize(&de); } free(filelist); if (!error) { if (write_cdir(za, cd, out) < 0) error = 1; } /* pointers in cd entries are owned by za */ cd->nentry = 0; _zip_cdir_free(cd); if (error) { _zip_dirent_finalize(&de); fclose(out); remove(temp); free(temp); return -1; } if (fclose(out) != 0) { _zip_error_set(&za->error, ZIP_ER_CLOSE, errno); remove(temp); free(temp); return -1; } if (za->zp) { fclose(za->zp); za->zp = NULL; reopen_on_error = 1; } if (_zip_rename(temp, za->zn) != 0) { _zip_error_set(&za->error, ZIP_ER_RENAME, errno); remove(temp); free(temp); if (reopen_on_error) { /* ignore errors, since we're already in an error case */ za->zp = fopen(za->zn, "rb"); } return -1; } #ifndef _MSC_VER mask = umask(0); umask(mask); chmod(za->zn, 0666&~mask); #endif _zip_free(za); free(temp); return 0; }
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; }
static zip_cdir_t * _zip_read_cdir(zip_t *za, zip_buffer_t *buffer, zip_uint64_t buf_offset, zip_error_t *error) { zip_cdir_t *cd; zip_uint16_t comment_len; zip_uint64_t i, left; zip_uint64_t eocd_offset = _zip_buffer_offset(buffer); zip_buffer_t *cd_buffer; if (_zip_buffer_left(buffer) < EOCDLEN) { /* 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(_zip_buffer_get(buffer, 4), EOCD_MAGIC, 4) != 0) { zip_error_set(error, ZIP_ER_NOZIP, 0); return NULL; } if (_zip_buffer_get_32(buffer) != 0) { zip_error_set(error, ZIP_ER_MULTIDISK, 0); return NULL; } if (eocd_offset >= EOCD64LOCLEN && memcmp(_zip_buffer_data(buffer) + eocd_offset - EOCD64LOCLEN, EOCD64LOC_MAGIC, 4) == 0) { _zip_buffer_set_offset(buffer, eocd_offset - EOCD64LOCLEN); cd = _zip_read_eocd64(za->src, buffer, buf_offset, za->flags, error); } else { _zip_buffer_set_offset(buffer, eocd_offset); cd = _zip_read_eocd(buffer, buf_offset, za->flags, error); } if (cd == NULL) return NULL; _zip_buffer_set_offset(buffer, eocd_offset + 20); comment_len = _zip_buffer_get_16(buffer); if (cd->offset + cd->size > buf_offset + eocd_offset) { /* cdir spans past EOCD record */ zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if (comment_len || (za->open_flags & ZIP_CHECKCONS)) { zip_uint64_t tail_len; _zip_buffer_set_offset(buffer, eocd_offset + EOCDLEN); tail_len = _zip_buffer_left(buffer); if (tail_len < comment_len || ((za->open_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(_zip_buffer_get(buffer, comment_len), comment_len, ZIP_FL_ENC_GUESS, error)) == NULL) { _zip_cdir_free(cd); return NULL; } } } if (cd->offset >= buf_offset) { zip_uint8_t *data; /* if buffer already read in, use it */ _zip_buffer_set_offset(buffer, cd->offset - buf_offset); if ((data = _zip_buffer_get(buffer, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_cdir_free(cd); return NULL; } if ((cd_buffer = _zip_buffer_new(data, cd->size)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); _zip_cdir_free(cd); return NULL; } } else { cd_buffer = NULL; if (zip_source_seek(za->src, (zip_int64_t)cd->offset, SEEK_SET) < 0) { _zip_error_set_from_source(error, za->src); _zip_cdir_free(cd); return NULL; } /* possible consistency check: cd->offset = len-(cd->size+cd->comment_len+EOCDLEN) ? */ if (zip_source_tell(za->src) != (zip_int64_t)cd->offset) { 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) { zip_int64_t entry_size; if ((cd->entry[i].orig=_zip_dirent_new()) == NULL || (entry_size = _zip_dirent_read(cd->entry[i].orig, za->src, cd_buffer, false, error)) < 0) { _zip_cdir_free(cd); _zip_buffer_free(cd_buffer); return NULL; } i++; left -= (zip_uint64_t)entry_size; } if (i != cd->nentry) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } if (za->open_flags & ZIP_CHECKCONS) { bool ok; if (cd_buffer) { ok = _zip_buffer_eof(cd_buffer); } else { zip_int64_t offset = zip_source_tell(za->src); if (offset < 0) { _zip_error_set_from_source(error, za->src); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } ok = ((zip_uint64_t)offset == cd->offset + cd->size); } if (!ok) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_buffer_free(cd_buffer); _zip_cdir_free(cd); return NULL; } } _zip_buffer_free(cd_buffer); return cd; }
int zip_close(struct zip *za) { int changed, survivors; int i, j, tfd, error; char *temp; FILE *tfp; mode_t mask; struct zip_cdir *cd; struct zip_dirent de; changed = survivors = 0; for (i=0; i<za->nentry; i++) { if (za->entry[i].state != ZIP_ST_UNCHANGED) changed = 1; if (za->entry[i].state != ZIP_ST_DELETED) survivors++; } if (!changed) { _zip_free(za); return 0; } /* don't create zip files with no entries */ if (survivors == 0) { if (za->zn) { if (remove(za->zn) != 0) { _zip_error_set(&za->error, ZIP_ER_REMOVE, errno); return -1; } } _zip_free(za); return 0; } if ((cd=_zip_cdir_new(survivors, &za->error)) == NULL) return -1; if ((temp=malloc(strlen(za->zn)+8)) == NULL) { _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); _zip_cdir_free(cd); return -1; } sprintf(temp, "%s.XXXXXX", za->zn); if ((tfd=mkstemp(temp)) == -1) { _zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno); _zip_cdir_free(cd); free(temp); return -1; } if ((tfp=fdopen(tfd, "r+b")) == NULL) { _zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno); _zip_cdir_free(cd); close(tfd); remove(temp); free(temp); return -1; } error = 0; for (i=j=0; i<za->nentry; i++) { if (za->entry[i].state == ZIP_ST_DELETED) continue; if (ZIP_ENTRY_DATA_CHANGED(za->entry+i)) { _zip_dirent_init(&de); memcpy(cd->entry+j, &de, sizeof(cd->entry[i])); if (za->entry[i].ch_filename == NULL) { if (za->entry[i].state == ZIP_ST_REPLACED) { de.filename = strdup(za->cdir->entry[i].filename); de.filename_len = strlen(de.filename); cd->entry[j].filename = za->cdir->entry[i].filename; cd->entry[j].filename_len = de.filename_len; } else { de.filename = strdup("-"); de.filename_len = 1; cd->entry[j].filename = "-"; cd->entry[j].filename_len = de.filename_len; } } } else { if (fseek(za->zp, za->cdir->entry[i].offset, SEEK_SET) != 0) { _zip_error_set(&za->error, ZIP_ER_SEEK, errno); error = 1; break; } if (_zip_dirent_read(&de, za->zp, NULL, 0, 1, &za->error) != 0) { error = 1; break; } memcpy(cd->entry+j, za->cdir->entry+i, sizeof(cd->entry[i])); } if (za->entry[i].ch_filename) { free(de.filename); de.filename = strdup(za->entry[i].ch_filename); de.filename_len = strlen(de.filename); cd->entry[j].filename = za->entry[i].ch_filename; cd->entry[j].filename_len = de.filename_len; } cd->entry[j].offset = ftell(tfp); if (ZIP_ENTRY_DATA_CHANGED(za->entry+i)) { if (add_data(za, i, &de, tfp) < 0) { error = -1; break; } cd->entry[j].last_mod = de.last_mod; cd->entry[j].comp_method = de.comp_method; cd->entry[j].comp_size = de.comp_size; cd->entry[j].uncomp_size = de.uncomp_size; cd->entry[j].crc = de.crc; } else { if (_zip_dirent_write(&de, tfp, 1, &za->error) < 0) { error = 1; break; } /* we just read the local dirent, file is at correct position */ if (copy_data(za->zp, de.comp_size, tfp, &za->error) < 0) { error = 1; break; } } j++; _zip_dirent_finalize(&de); } if (!error) { if (_zip_cdir_write(cd, tfp, &za->error) < 0) error = 1; } /* pointers in cd are owned by za */ cd->nentry = 0; _zip_cdir_free(cd); if (error) { _zip_dirent_finalize(&de); fclose(tfp); remove(temp); free(temp); return -1; } if (fclose(tfp) != 0) { _zip_error_set(&za->error, ZIP_ER_CLOSE, errno); remove(temp); free(temp); return -1; } if (rename(temp, za->zn) != 0) { _zip_error_set(&za->error, ZIP_ER_RENAME, errno); remove(temp); free(temp); return -1; } if (za->zp) { fclose(za->zp); za->zp = NULL; } mask = umask(0); umask(mask); chmod(za->zn, 0666&~mask); _zip_free(za); return 0; }
ZIP_EXTERN struct zip * zip_open(const char *fn, int flags, int *zep) { FILE *fp; struct zip *za; struct zip_cdir *cdir; int i; off_t len; switch (_zip_file_exists(fn, flags, zep)) { case -1: return NULL; case 0: return _zip_allocate_new(fn, zep); default: break; } if ((fp=fopen(fn, "rb")) == NULL) { set_error(zep, NULL, ZIP_ER_OPEN); return NULL; } __android_log_print(ANDROID_LOG_VERBOSE, "ZIP", "FILE OPENED"); fseeko(fp, 0, SEEK_END); len = ftello(fp); /* treat empty files as empty archives */ if (len == 0) { if ((za=_zip_allocate_new(fn, zep)) == NULL) fclose(fp); else za->zp = fp; return za; } __android_log_print(ANDROID_LOG_VERBOSE, "ZIP", "SCANNING"); cdir = _zip_find_central_dir(fp, flags, zep, len); if (cdir == NULL) { fclose(fp); return NULL; } __android_log_print(ANDROID_LOG_VERBOSE, "ZIP", "DIR FOUND"); if ((za=_zip_allocate_new(fn, zep)) == NULL) { _zip_cdir_free(cdir); fclose(fp); return NULL; } za->cdir = cdir; za->zp = fp; __android_log_print(ANDROID_LOG_VERBOSE, "ZIP", "ALLOCING ENTRIES"); 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; } za->nentry_alloc = cdir->nentry; __android_log_print(ANDROID_LOG_VERBOSE, "ZIP", "FILLING ENTRIES"); for (i=0; i<cdir->nentry; i++) _zip_entry_new2(za); __android_log_print(ANDROID_LOG_VERBOSE, "ZIP", "CHECK TORRENTZIP"); _zip_check_torrentzip(za); za->ch_flags = za->flags; __android_log_print(ANDROID_LOG_VERBOSE, "ZIP", "DONE"); return za; }