ZIP_EXTERN int zip_close(zip_t *za) { zip_uint64_t i, j, survivors; zip_int64_t off; int error; zip_filelist_t *filelist; int changed; if (za == NULL) return -1; changed = _zip_changed(za, &survivors); /* don't create zip files with no entries */ if (survivors == 0) { if ((za->open_flags & ZIP_TRUNCATE) || changed) { if (zip_source_remove(za->src) < 0) { _zip_error_set_from_source(&za->error, za->src); return -1; } } zip_discard(za); return 0; } if (!changed) { zip_discard(za); return 0; } if (survivors > za->nentry) { zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } if ((filelist=(zip_filelist_t *)malloc(sizeof(filelist[0])*(size_t)survivors)) == NULL) return -1; /* create list of files with index into original archive */ for (i=j=0; i<za->nentry; i++) { if (za->entry[i].deleted) continue; if (j >= survivors) { free(filelist); zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } filelist[j].idx = i; j++; } if (j < survivors) { free(filelist); zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } if (zip_source_begin_write(za->src) < 0) { _zip_error_set_from_source(&za->error, za->src); free(filelist); return -1; } error = 0; for (j=0; j<survivors; j++) { int new_data; zip_entry_t *entry; zip_dirent_t *de; i = filelist[j].idx; entry = za->entry+i; new_data = (ZIP_ENTRY_DATA_CHANGED(entry) || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_COMP_METHOD)); /* create new local directory entry */ if (entry->changes == NULL) { if ((entry->changes=_zip_dirent_clone(entry->orig)) == NULL) { zip_error_set(&za->error, ZIP_ER_MEMORY, 0); error = 1; break; } } de = entry->changes; if (_zip_read_local_ef(za, i) < 0) { error = 1; break; } if ((off = zip_source_tell_write(za->src)) < 0) { error = 1; break; } de->offset = (zip_uint64_t)off; if (new_data) { zip_source_t *zs; zs = NULL; if (!ZIP_ENTRY_DATA_CHANGED(entry)) { if ((zs=_zip_source_zip_new(za, za, i, ZIP_FL_UNCHANGED, 0, 0, NULL)) == NULL) { error = 1; break; } } /* add_data writes dirent */ if (add_data(za, zs ? zs : entry->source, de) < 0) { error = 1; if (zs) zip_source_free(zs); break; } if (zs) zip_source_free(zs); } else { zip_uint64_t offset; /* when copying data, all sizes are known -> no data descriptor needed */ de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR; if (_zip_dirent_write(za, de, ZIP_FL_LOCAL) < 0) { error = 1; break; } if ((offset=_zip_file_get_offset(za, i, &za->error)) == 0) { error = 1; break; } if (zip_source_seek(za->src, (zip_int64_t)offset, SEEK_SET) < 0) { _zip_error_set_from_source(&za->error, za->src); error = 1; break; } if (copy_data(za, de->comp_size) < 0) { error = 1; break; } } } if (!error) { if (write_cdir(za, filelist, survivors) < 0) error = 1; } free(filelist); if (!error) { if (zip_source_commit_write(za->src) != 0) { _zip_error_set_from_source(&za->error, za->src); error = 1; } } if (error) { zip_source_rollback_write(za->src); return -1; } zip_discard(za); return 0; }
ZIP_EXTERN int zip_close(struct zip *za) { zip_uint64_t i, j, survivors; int error; char *temp; FILE *out; #ifndef _WIN32 mode_t mask; #endif struct zip_filelist *filelist; int reopen_on_error; int new_torrentzip; int changed; reopen_on_error = 0; if (za == NULL) return -1; changed = _zip_changed(za, &survivors); /* don't create zip files with no entries */ if (survivors == 0) { if (za->zn && ((za->open_flags & ZIP_TRUNCATE) || (changed && za->zp))) { if (remove(za->zn) != 0) { _zip_error_set(&za->error, ZIP_ER_REMOVE, errno); return -1; } } zip_discard(za); return 0; } if (!changed) { zip_discard(za); return 0; } if (survivors > za->nentry) { _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } if ((filelist=(struct zip_filelist *)malloc(sizeof(filelist[0])*(size_t)survivors)) == NULL) return -1; /* archive comment is special for torrentzip */ if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) { /* TODO: use internal function when zip_set_archive_comment clears TORRENT flag */ if (zip_set_archive_comment(za, TORRENT_SIG "XXXXXXXX", TORRENT_SIG_LEN + TORRENT_CRC_LEN) < 0) { free(filelist); return -1; } } /* TODO: if no longer torrentzip and archive comment not changed by user, delete it */ /* create list of files with index into original archive */ for (i=j=0; i<za->nentry; i++) { if (za->entry[i].deleted) continue; if (j >= survivors) { free(filelist); _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } filelist[j].idx = i; filelist[j].name = zip_get_name(za, i, 0); j++; } if (j < survivors) { free(filelist); _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } if ((temp=_zip_create_temp_output(za, &out)) == NULL) { free(filelist); return -1; } if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) qsort(filelist, (size_t)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++) { int new_data; struct zip_entry *entry; struct zip_dirent *de; i = filelist[j].idx; entry = za->entry+i; new_data = (ZIP_ENTRY_DATA_CHANGED(entry) || new_torrentzip || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_COMP_METHOD)); /* create new local directory entry */ if (entry->changes == NULL) { if ((entry->changes=_zip_dirent_clone(entry->orig)) == NULL) { _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); error = 1; break; } } de = entry->changes; if (_zip_read_local_ef(za, i) < 0) { error = 1; break; } if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) _zip_dirent_torrent_normalize(entry->changes); de->offset = (zip_uint64_t)ftello(out); /* TODO: check for errors */ if (new_data) { struct zip_source *zs; zs = NULL; if (!ZIP_ENTRY_DATA_CHANGED(entry)) { if ((zs=_zip_source_zip_new(za, za, i, ZIP_FL_UNCHANGED, 0, 0, NULL)) == NULL) { error = 1; break; } } /* add_data writes dirent */ if (add_data(za, zs ? zs : entry->source, de, out) < 0) { error = 1; if (zs) zip_source_free(zs); break; } if (zs) zip_source_free(zs); } else { zip_uint64_t offset; /* when copying data, all sizes are known -> no data descriptor needed */ de->bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR; if (_zip_dirent_write(de, out, ZIP_FL_LOCAL, &za->error) < 0) { error = 1; break; } if ((offset=_zip_file_get_offset(za, i, &za->error)) == 0) { error = 1; break; } if ((fseeko(za->zp, (off_t)offset, SEEK_SET) < 0)) { _zip_error_set(&za->error, ZIP_ER_SEEK, errno); error = 1; break; } if (copy_data(za->zp, de->comp_size, out, &za->error) < 0) { error = 1; break; } } } if (!error) { if (write_cdir(za, filelist, survivors, out) < 0) error = 1; } free(filelist); if (error) { fclose(out); (void)remove(temp); free(temp); return -1; } if (fclose(out) != 0) { _zip_error_set(&za->error, ZIP_ER_CLOSE, errno); (void)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); (void)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 _WIN32 mask = umask(0); umask(mask); chmod(za->zn, 0666&~mask); #endif zip_discard(za); free(temp); return 0; }
ZIP_EXTERN struct zip_file * zip_fopen_index(struct zip *za, int fileno, int flags) { int len, ret; int zfflags; struct zip_file *zf; if ((fileno < 0) || (fileno >= za->nentry)) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return NULL; } if ((flags & ZIP_FL_UNCHANGED) == 0 && ZIP_ENTRY_DATA_CHANGED(za->entry+fileno)) { _zip_error_set(&za->error, ZIP_ER_CHANGED, 0); return NULL; } if (fileno >= za->cdir->nentry) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return NULL; } zfflags = 0; switch (za->cdir->entry[fileno].comp_method) { case ZIP_CM_STORE: zfflags |= ZIP_ZF_CRC; break; case ZIP_CM_DEFLATE: if ((flags & ZIP_FL_COMPRESSED) == 0) zfflags |= ZIP_ZF_CRC | ZIP_ZF_DECOMP; break; default: if ((flags & ZIP_FL_COMPRESSED) == 0) { _zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); return NULL; } break; } zf = _zip_file_new(za); zf->flags = zfflags; /* zf->name = za->cdir->entry[fileno].filename; */ zf->method = za->cdir->entry[fileno].comp_method; zf->bytes_left = za->cdir->entry[fileno].uncomp_size; zf->cbytes_left = za->cdir->entry[fileno].comp_size; zf->crc_orig = za->cdir->entry[fileno].crc; if ((zf->fpos=_zip_file_get_offset(za, fileno)) == 0) { zip_fclose(zf); return NULL; } if ((zf->flags & ZIP_ZF_DECOMP) == 0) zf->bytes_left = zf->cbytes_left; else { if ((zf->buffer=(char *)malloc(BUFSIZE)) == NULL) { _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); zip_fclose(zf); return NULL; } len = _zip_file_fillbuf(zf->buffer, BUFSIZE, zf); if (len <= 0) { _zip_error_copy(&za->error, &zf->error); zip_fclose(zf); return NULL; } if ((zf->zstr = (z_stream *)malloc(sizeof(z_stream))) == NULL) { _zip_error_set(&za->error, ZIP_ER_MEMORY, 0); zip_fclose(zf); return NULL; } zf->zstr->zalloc = Z_NULL; zf->zstr->zfree = Z_NULL; zf->zstr->opaque = NULL; zf->zstr->next_in = (Bytef *)zf->buffer; zf->zstr->avail_in = len; /* negative value to tell zlib that there is no header */ if ((ret=inflateInit2(zf->zstr, -MAX_WBITS)) != Z_OK) { _zip_error_set(&za->error, ZIP_ER_ZLIB, ret); zip_fclose(zf); return NULL; } } return zf; }
zip_fopen_index_encrypted(struct zip *za, zip_uint64_t fileno, int flags, const char *password) { struct zip_file *zf; zip_compression_implementation comp_impl; zip_encryption_implementation enc_impl; struct zip_source *src, *s2; zip_uint64_t start; struct zip_stat st; if (fileno >= za->nentry) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return NULL; } if ((flags & ZIP_FL_UNCHANGED) == 0 && ZIP_ENTRY_DATA_CHANGED(za->entry+fileno)) { _zip_error_set(&za->error, ZIP_ER_CHANGED, 0); return NULL; } if (fileno >= za->cdir->nentry) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return NULL; } if (flags & ZIP_FL_ENCRYPTED) flags |= ZIP_FL_COMPRESSED; zip_stat_index(za, fileno, flags, &st); enc_impl = NULL; if ((flags & ZIP_FL_ENCRYPTED) == 0) { if (st.encryption_method != ZIP_EM_NONE) { if (password == NULL) { _zip_error_set(&za->error, ZIP_ER_NOPASSWD, 0); return NULL; } if ((enc_impl=zip_get_encryption_implementation( st.encryption_method)) == NULL) { _zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); return NULL; } } } comp_impl = NULL; if ((flags & ZIP_FL_COMPRESSED) == 0) { if (st.comp_method != ZIP_CM_STORE) { if ((comp_impl=zip_get_compression_implementation( st.comp_method)) == NULL) { _zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); return NULL; } } } if ((start=_zip_file_get_offset(za, fileno)) == 0) return NULL; if (st.comp_size == 0) { if ((src=zip_source_buffer(za, NULL, 0, 0)) == NULL) return NULL; } else { if ((src=_zip_source_file_or_p(za, NULL, za->zp, start, st.comp_size, 0, &st)) == NULL) return NULL; if (enc_impl) { if ((s2=enc_impl(za, src, ZIP_EM_TRAD_PKWARE, 0, password)) == NULL) { zip_source_free(src); /* XXX: set error (how?) */ return NULL; } src = s2; } if (comp_impl) { if ((s2=comp_impl(za, src, za->cdir->entry[fileno].comp_method, 0)) == NULL) { zip_source_free(src); /* XXX: set error (how?) */ return NULL; } src = s2; } if ((flags & ZIP_FL_COMPRESSED) == 0 || st.comp_method == ZIP_CM_STORE ) { if ((s2=zip_source_crc(za, src, 1)) == NULL) { zip_source_free(src); /* XXX: set error (how?) */ return NULL; } src = s2; } } if (zip_source_open(src) < 0) { _zip_error_set_from_source(&za->error, src); zip_source_free(src); return NULL; } zf = _zip_file_new(za); zf->src = src; return zf; }
struct zip_source * _zip_source_zip_new(struct zip *za, struct zip *srcza, zip_uint64_t srcidx, zip_flags_t flags, zip_uint64_t start, zip_uint64_t len, const char *password) { zip_compression_implementation comp_impl; zip_encryption_implementation enc_impl; struct zip_source *src, *s2; zip_uint64_t offset; struct zip_stat st; if (za == NULL) return NULL; if (srcza == NULL || srcidx >= srcza->nentry) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return NULL; } if ((flags & ZIP_FL_UNCHANGED) == 0 && (ZIP_ENTRY_DATA_CHANGED(srcza->entry+srcidx) || srcza->entry[srcidx].deleted)) { _zip_error_set(&za->error, ZIP_ER_CHANGED, 0); return NULL; } if (zip_stat_index(srcza, srcidx, flags|ZIP_FL_UNCHANGED, &st) < 0) { _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return NULL; } if (flags & ZIP_FL_ENCRYPTED) flags |= ZIP_FL_COMPRESSED; if ((start > 0 || len > 0) && (flags & ZIP_FL_COMPRESSED)) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return NULL; } /* overflow or past end of file */ if ((start > 0 || len > 0) && (start+len < start || start+len > st.size)) { _zip_error_set(&za->error, ZIP_ER_INVAL, 0); return NULL; } enc_impl = NULL; if (((flags & ZIP_FL_ENCRYPTED) == 0) && (st.encryption_method != ZIP_EM_NONE)) { if (password == NULL) { _zip_error_set(&za->error, ZIP_ER_NOPASSWD, 0); return NULL; } if ((enc_impl=_zip_get_encryption_implementation(st.encryption_method)) == NULL) { _zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); return NULL; } } comp_impl = NULL; if ((flags & ZIP_FL_COMPRESSED) == 0) { if (st.comp_method != ZIP_CM_STORE) { if ((comp_impl=_zip_get_compression_implementation(st.comp_method)) == NULL) { _zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); return NULL; } } } if ((offset=_zip_file_get_offset(srcza, srcidx, &za->error)) == 0) return NULL; if (st.comp_size == 0) { if ((src=zip_source_buffer(za, NULL, 0, 0)) == NULL) return NULL; } else { if (start+len > 0 && enc_impl == NULL && comp_impl == NULL) { struct zip_stat st2; st2.size = len ? len : st.size-start; st2.comp_size = st2.size; st2.comp_method = ZIP_CM_STORE; st2.mtime = st.mtime; st2.valid = ZIP_STAT_SIZE|ZIP_STAT_COMP_SIZE|ZIP_STAT_COMP_METHOD|ZIP_STAT_MTIME; /* XXX: check for overflow of st2.size */ if ((src=_zip_source_file_or_p(za, NULL, srcza->zp, offset+start, (zip_int64_t)st2.size, 0, &st2)) == NULL) return NULL; } else { /* XXX: check for overflow of st.comp_size */ if ((src=_zip_source_file_or_p(za, NULL, srcza->zp, offset, (zip_int64_t)st.comp_size, 0, &st)) == NULL) return NULL; } if (enc_impl) { if ((s2=enc_impl(za, src, st.encryption_method, 0, password)) == NULL) { zip_source_free(src); /* XXX: set error (how?) */ return NULL; } src = s2; } if (comp_impl) { if ((s2=comp_impl(za, src, st.comp_method, 0)) == NULL) { zip_source_free(src); /* XXX: set error (how?) */ return NULL; } src = s2; } if (((flags & ZIP_FL_COMPRESSED) == 0 || st.comp_method == ZIP_CM_STORE) && (len == 0 || len == st.comp_size)) { /* when reading the whole file, check for crc errors */ if ((s2=zip_source_crc(za, src, 1)) == NULL) { zip_source_free(src); /* XXX: set error (how?) */ return NULL; } src = s2; } if (start+len > 0 && (comp_impl || enc_impl)) { if ((s2=zip_source_window(za, src, start, len ? len : st.size-start)) == NULL) { zip_source_free(src); /* XXX: set error (how?) (why?) */ return NULL; } src = s2; } } return src; }