static int copy_source(struct zip *za, struct zip_source *src, FILE *ft) { char buf[BUFSIZE]; zip_int64_t n; int ret; if (zip_source_open(src) < 0) { _zip_error_set_from_source(&za->error, src); return -1; } ret = 0; while ((n=zip_source_read(src, buf, sizeof(buf))) > 0) { if (fwrite(buf, 1, (size_t)n, ft) != (size_t)n) { _zip_error_set(&za->error, ZIP_ER_WRITE, errno); ret = -1; break; } } if (n < 0) { if (ret == 0) _zip_error_set_from_source(&za->error, src); ret = -1; } zip_source_close(src); return ret; }
static int copy_source(zip_t *za, zip_source_t *src) { zip_uint8_t buf[BUFSIZE]; zip_int64_t n; int ret; if (zip_source_open(src) < 0) { _zip_error_set_from_source(&za->error, src); return -1; } ret = 0; while ((n=zip_source_read(src, buf, sizeof(buf))) > 0) { if (_zip_write(za, buf, (zip_uint64_t)n) < 0) { ret = -1; break; } } if (n < 0) { _zip_error_set_from_source(&za->error, src); ret = -1; } zip_source_close(src); return ret; }
static int decrypt_header(zip_source_t *src, struct winzip_aes *ctx) { zip_uint8_t header[MAX_HEADER_LENGTH]; zip_uint8_t password_verification[PWD_VER_LENGTH]; zip_uint8_t headerlen; zip_int64_t n; headerlen = PWD_VER_LENGTH + salt_length[ctx->mode]; if ((n=zip_source_read(src, header, headerlen)) < 0) { _zip_error_set_from_source(&ctx->error, src); return -1; } if (n != headerlen) { zip_error_set(&ctx->error, ZIP_ER_EOF, 0); return -1; } if (_zip_fcrypt_init(ctx->mode, ctx->password, strlen(ctx->password), header, password_verification, &ctx->fcrypt_ctx) != 0) { zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0); return -1; } if (memcmp(password_verification, header + salt_length[ctx->mode], PWD_VER_LENGTH) != 0) { zip_error_set(&ctx->error, ZIP_ER_WRONGPASSWD, 0); return -1; } return 0; }
zip_fread(struct zip_file *zf, void *outbuf, zip_uint64_t toread) { zip_int64_t n; if (!zf) return -1; if (zf->error.zip_err != 0) return -1; if (toread > ZIP_INT64_MAX) { _zip_error_set(&zf->error, ZIP_ER_INVAL, 0); return -1; } if ((zf->eof) || (toread == 0)) return 0; if ((n=zip_source_read(zf->src, outbuf, toread)) < 0) { _zip_error_set_from_source(&zf->error, zf->src); return -1; } return n; }
ZIP_EXTERN struct zip_file * zip_fopen_index_encrypted(struct zip *za, zip_uint64_t index, zip_flags_t flags, const char *password) { struct zip_file *zf; struct zip_source *src; if ((src=_zip_source_zip_new(za, za, index, flags, 0, 0, password)) == NULL) return NULL; if (zip_source_open(src) < 0) { _zip_error_set_from_source(&za->error, src); zip_source_free(src); return NULL; } if ((zf=_zip_file_new(za)) == NULL) { zip_source_free(src); return NULL; } zf->src = src; return zf; }
ZIP_EXTERN int zip_source_stat(zip_source_t *src, zip_stat_t *st) { if (src->source_closed) { return -1; } if (st == NULL) { zip_error_set(&src->error, ZIP_ER_INVAL, 0); return -1; } zip_stat_init(st); if (ZIP_SOURCE_IS_LAYERED(src)) { if (zip_source_stat(src->src, st) < 0) { _zip_error_set_from_source(&src->error, src->src); return -1; } } if (_zip_source_call(src, st, sizeof(*st), ZIP_SOURCE_STAT) < 0) { return -1; } return 0; }
static zip_int64_t _zip_checkcons(zip_t *za, zip_cdir_t *cd, zip_error_t *error) { zip_uint64_t i; zip_uint64_t min, max, j; struct zip_dirent temp; _zip_dirent_init(&temp); if (cd->nentry) { max = cd->entry[0].orig->offset; min = cd->entry[0].orig->offset; } else min = max = 0; for (i=0; i<cd->nentry; i++) { if (cd->entry[i].orig->offset < min) min = cd->entry[i].orig->offset; if (min > (zip_uint64_t)cd->offset) { zip_error_set(error, ZIP_ER_NOZIP, 0); return -1; } j = cd->entry[i].orig->offset + cd->entry[i].orig->comp_size + _zip_string_length(cd->entry[i].orig->filename) + LENTRYSIZE; if (j > max) max = j; if (max > (zip_uint64_t)cd->offset) { zip_error_set(error, ZIP_ER_NOZIP, 0); return -1; } if (zip_source_seek(za->src, (zip_int64_t)cd->entry[i].orig->offset, SEEK_SET) < 0) { _zip_error_set_from_source(error, za->src); return -1; } if (_zip_dirent_read(&temp, za->src, NULL, true, error) == -1) { _zip_dirent_finalize(&temp); return -1; } if (_zip_headercomp(cd->entry[i].orig, &temp) != 0) { zip_error_set(error, ZIP_ER_INCONS, 0); _zip_dirent_finalize(&temp); return -1; } cd->entry[i].orig->extra_fields = _zip_ef_merge(cd->entry[i].orig->extra_fields, temp.extra_fields); cd->entry[i].orig->local_extra_fields_read = 1; temp.extra_fields = NULL; _zip_dirent_finalize(&temp); } return (max-min) < ZIP_INT64_MAX ? (zip_int64_t)(max-min) : ZIP_INT64_MAX; }
zip_source_t * zip_source_winzip_aes_decode(zip_t *za, zip_source_t *src, zip_uint16_t em, int flags, const char *password) { zip_source_t *s2; int mode = 0; zip_stat_t st; zip_uint64_t aux_length; struct winzip_aes *ctx; switch (em) { case ZIP_EM_AES_128: mode = 1; break; case ZIP_EM_AES_192: mode = 2; break; case ZIP_EM_AES_256: mode = 3; break; } if (password == NULL || src == NULL || mode == 0) { zip_error_set(&za->error, ZIP_ER_INVAL, 0); return NULL; } if (flags & ZIP_CODEC_ENCODE) { zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); return NULL; } if (zip_source_stat(src, &st) != 0) { _zip_error_set_from_source(&za->error, src); return NULL; } aux_length = PWD_VER_LENGTH + salt_length[mode] + HMAC_LENGTH; if ((st.valid & ZIP_STAT_COMP_SIZE) == 0 || st.comp_size < aux_length) { zip_error_set(&za->error, ZIP_ER_OPNOTSUPP, 0); return NULL; } if ((ctx = winzip_aes_new(mode, password)) == NULL) { zip_error_set(&za->error, ZIP_ER_MEMORY, 0); return NULL; } ctx->data_length = st.comp_size - aux_length; if ((s2 = zip_source_layered(za, src, winzip_aes_decrypt, ctx)) == NULL) { winzip_aes_free(ctx); return NULL; } return s2; }
zip_t * _zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) { zip_t *za; zip_cdir_t *cdir; struct zip_stat st; zip_uint64_t len; zip_stat_init(&st); if (zip_source_stat(src, &st) < 0) { _zip_error_set_from_source(error, src); return NULL; } if ((st.valid & ZIP_STAT_SIZE) == 0) { zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP); return NULL; } len = st.size; /* treat empty files as empty archives */ if (len == 0) { if ((za=_zip_allocate_new(src, flags, error)) == NULL) { zip_source_free(src); return NULL; } return za; } if ((za=_zip_allocate_new(src, flags, error)) == NULL) { return NULL; } if ((cdir = _zip_find_central_dir(za, len)) == NULL) { _zip_error_copy(error, &za->error); /* keep src so discard does not get rid of it */ zip_source_keep(src); zip_discard(za); return NULL; } za->entry = cdir->entry; za->nentry = cdir->nentry; za->nentry_alloc = cdir->nentry_alloc; za->comment_orig = cdir->comment; za->ch_flags = za->flags; free(cdir); return za; }
int _zip_filerange_crc(zip_source_t *src, zip_uint64_t start, zip_uint64_t len, uLong *crcp, zip_error_t *error) { Bytef buf[BUFSIZE]; zip_int64_t n; *crcp = crc32(0L, Z_NULL, 0); if (start > ZIP_INT64_MAX) { zip_error_set(error, ZIP_ER_SEEK, EFBIG); return -1; } if (zip_source_seek(src, (zip_int64_t)start, SEEK_SET) != 0) { _zip_error_set_from_source(error, src); return -1; } while (len > 0) { n = (zip_int64_t)(len > BUFSIZE ? BUFSIZE : len); if ((n = zip_source_read(src, buf, (zip_uint64_t)n)) < 0) { _zip_error_set_from_source(error, src); return -1; } if (n == 0) { zip_error_set(error, ZIP_ER_EOF, 0); return -1; } *crcp = crc32(*crcp, buf, (uInt)n); len -= (zip_uint64_t)n; } return 0; }
int _zip_write(zip_t *za, const void *data, zip_uint64_t length) { zip_int64_t n; if ((n = zip_source_write(za->src, data, length)) < 0) { _zip_error_set_from_source(&za->error, za->src); return -1; } if ((zip_uint64_t)n != length) { zip_error_set(&za->error, ZIP_ER_WRITE, EINTR); return -1; } return 0; }
static bool verify_hmac(zip_source_t *src, struct winzip_aes *ctx) { unsigned char computed[HMAC_LENGTH], from_file[HMAC_LENGTH]; if (zip_source_read(src, from_file, HMAC_LENGTH) < HMAC_LENGTH) { _zip_error_set_from_source(&ctx->error, src); return false; } _zip_fcrypt_end(computed, &ctx->fcrypt_ctx); if (memcmp(from_file, computed, HMAC_LENGTH) != 0) { zip_error_set(&ctx->error, ZIP_ER_CRC, 0); return false; } return true; }
ZIP_EXTERN int zip_source_open(zip_source_t *src) { if (src->source_closed) { return -1; } if (src->write_state == ZIP_SOURCE_WRITE_REMOVED) { zip_error_set(&src->error, ZIP_ER_DELETED, 0); return -1; } if (ZIP_SOURCE_IS_OPEN_READING(src)) { if ((zip_source_supports(src) & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK)) == 0) { zip_error_set(&src->error, ZIP_ER_INUSE, 0); return -1; } } else { if (ZIP_SOURCE_IS_LAYERED(src)) { if (zip_source_open(src->src) < 0) { _zip_error_set_from_source(&src->error, src->src); return -1; } } if (_zip_source_call(src, NULL, 0, ZIP_SOURCE_OPEN) < 0) { if (ZIP_SOURCE_IS_LAYERED(src)) { zip_source_close(src->src); } return -1; } } src->eof = false; src->had_read_error = false; _zip_error_clear(&src->error); src->open_count++; return 0; }
int _zip_read(zip_source_t *src, zip_uint8_t *b, zip_uint64_t length, zip_error_t *error) { zip_int64_t n; if (length > ZIP_INT64_MAX) { zip_error_set(error, ZIP_ER_INTERNAL, 0); return -1; } if ((n = zip_source_read(src, b, length)) < 0) { _zip_error_set_from_source(error, src); return -1; } if (n < (zip_int64_t)length) { zip_error_set(error, ZIP_ER_EOF, 0); return -1; } return 0; }
zip_uint64_t _zip_file_get_offset(const zip_t *za, zip_uint64_t idx, zip_error_t *error) { zip_uint64_t offset; zip_int32_t size; offset = za->entry[idx].orig->offset; if (zip_source_seek(za->src, (zip_int64_t)offset, SEEK_SET) < 0) { _zip_error_set_from_source(error, za->src); return 0; } /* TODO: cache? */ if ((size=_zip_dirent_size(za->src, ZIP_EF_LOCAL, error)) < 0) return 0; if (offset+(zip_uint32_t)size > ZIP_INT64_MAX) { zip_error_set(error, ZIP_ER_SEEK, EFBIG); return 0; } return offset + (zip_uint32_t)size; }
zip_t * _zip_open(zip_source_t *src, unsigned int flags, zip_error_t *error) { zip_t *za; zip_cdir_t *cdir; struct zip_stat st; zip_uint64_t len, idx; zip_stat_init(&st); if (zip_source_stat(src, &st) < 0) { _zip_error_set_from_source(error, src); return NULL; } if ((st.valid & ZIP_STAT_SIZE) == 0) { zip_error_set(error, ZIP_ER_SEEK, EOPNOTSUPP); return NULL; } len = st.size; /* treat empty files as empty archives */ if (len == 0) { if ((za=_zip_allocate_new(src, flags, error)) == NULL) { zip_source_free(src); return NULL; } return za; } if ((za=_zip_allocate_new(src, flags, error)) == NULL) { return NULL; } if ((cdir = _zip_find_central_dir(za, len)) == NULL) { _zip_error_copy(error, &za->error); /* keep src so discard does not get rid of it */ zip_source_keep(src); zip_discard(za); return NULL; } za->entry = cdir->entry; za->nentry = cdir->nentry; za->nentry_alloc = cdir->nentry_alloc; za->comment_orig = cdir->comment; free(cdir); _zip_hash_reserve_capacity(za->names, za->nentry, &za->error); for (idx = 0; idx < za->nentry; idx++) { const zip_uint8_t *name = _zip_string_get(za->entry[idx].orig->filename, NULL, 0, error); if (name == NULL) { /* keep src so discard does not get rid of it */ zip_source_keep(src); zip_discard(za); return NULL; } if (_zip_hash_add(za->names, name, idx, ZIP_FL_UNCHANGED, &za->error) == false) { if (za->error.zip_err != ZIP_ER_EXISTS || (flags & ZIP_CHECKCONS)) { _zip_error_copy(error, &za->error); /* keep src so discard does not get rid of it */ zip_source_keep(src); zip_discard(za); return NULL; } } } za->ch_flags = za->flags; return za; }
static int add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) { zip_int64_t offstart, offdata, offend; struct zip_stat st; zip_source_t *s2; int ret; int is_zip64; zip_flags_t flags; if (zip_source_stat(src, &st) < 0) { _zip_error_set_from_source(&za->error, src); return -1; } if ((st.valid & ZIP_STAT_COMP_METHOD) == 0) { st.valid |= ZIP_STAT_COMP_METHOD; st.comp_method = ZIP_CM_STORE; } if (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != ZIP_CM_STORE) de->comp_method = st.comp_method; else if (de->comp_method == ZIP_CM_STORE && (st.valid & ZIP_STAT_SIZE)) { st.valid |= ZIP_STAT_COMP_SIZE; st.comp_size = st.size; } else { /* we'll recompress */ st.valid &= ~ZIP_STAT_COMP_SIZE; } flags = ZIP_EF_LOCAL; if ((st.valid & ZIP_STAT_SIZE) == 0) flags |= ZIP_FL_FORCE_ZIP64; else { de->uncomp_size = st.size; if ((st.valid & ZIP_STAT_COMP_SIZE) == 0) { if (( ((de->comp_method == ZIP_CM_DEFLATE || ZIP_CM_IS_DEFAULT(de->comp_method)) && st.size > MAX_DEFLATE_SIZE_32) || (de->comp_method != ZIP_CM_STORE && de->comp_method != ZIP_CM_DEFLATE && !ZIP_CM_IS_DEFAULT(de->comp_method)))) flags |= ZIP_FL_FORCE_ZIP64; } else de->comp_size = st.comp_size; } if ((offstart = zip_source_tell_write(za->src)) < 0) { return -1; } /* as long as we don't support non-seekable output, clear data descriptor bit */ de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR; if ((is_zip64=_zip_dirent_write(za, de, flags)) < 0) return -1; if (st.comp_method == ZIP_CM_STORE || (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != de->comp_method)) { zip_source_t *s_store, *s_crc; zip_compression_implementation comp_impl; 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 -1; } if ((s_store=comp_impl(za, src, st.comp_method, ZIP_CODEC_DECODE)) == NULL) { /* error set by comp_impl */ return -1; } } else { /* to have the same reference count to src as in the case where it's not stored */ zip_source_keep(src); s_store = src; } s_crc = zip_source_crc(za, s_store, 0); zip_source_free(s_store); if (s_crc == NULL) { return -1; } if (de->comp_method != ZIP_CM_STORE && ((st.valid & ZIP_STAT_SIZE) == 0 || st.size != 0)) { if ((comp_impl=_zip_get_compression_implementation(de->comp_method)) == NULL) { zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); zip_source_free(s_crc); return -1; } s2 = comp_impl(za, s_crc, de->comp_method, ZIP_CODEC_ENCODE); zip_source_free(s_crc); if (s2 == NULL) { return -1; } } else { s2 = s_crc; } } else { zip_source_keep(src); s2 = src; } if ((offdata = zip_source_tell_write(za->src)) < 0) { return -1; } ret = copy_source(za, s2); if (zip_source_stat(s2, &st) < 0) ret = -1; zip_source_free(s2); if (ret < 0) return -1; if ((offend = zip_source_tell_write(za->src)) < 0) { return -1; } if (zip_source_seek_write(za->src, offstart, SEEK_SET) < 0) { _zip_error_set_from_source(&za->error, za->src); return -1; } if ((st.valid & (ZIP_STAT_COMP_METHOD|ZIP_STAT_CRC|ZIP_STAT_SIZE)) != (ZIP_STAT_COMP_METHOD|ZIP_STAT_CRC|ZIP_STAT_SIZE)) { zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } if ((de->changed & ZIP_DIRENT_LAST_MOD) == 0) { if (st.valid & ZIP_STAT_MTIME) de->last_mod = st.mtime; else time(&de->last_mod); } de->comp_method = st.comp_method; de->crc = st.crc; de->uncomp_size = st.size; de->comp_size = (zip_uint64_t)(offend - offdata); if ((ret=_zip_dirent_write(za, de, flags)) < 0) return -1; if (is_zip64 != ret) { /* Zip64 mismatch between preliminary file header written before data and final file header written afterwards */ zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } if (zip_source_seek_write(za->src, offend, SEEK_SET) < 0) { _zip_error_set_from_source(&za->error, za->src); return -1; } return 0; }
static int add_data(struct zip *za, struct zip_source *src, struct zip_dirent *de, FILE *ft) { off_t offstart, offdata, offend; struct zip_stat st; struct zip_source *s2; int ret; int is_zip64; zip_flags_t flags; if (zip_source_stat(src, &st) < 0) { _zip_error_set_from_source(&za->error, src); return -1; } if ((st.valid & ZIP_STAT_COMP_METHOD) == 0) { st.valid |= ZIP_STAT_COMP_METHOD; st.comp_method = ZIP_CM_STORE; } if (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != ZIP_CM_STORE) de->comp_method = st.comp_method; else if (de->comp_method == ZIP_CM_STORE && (st.valid & ZIP_STAT_SIZE)) { st.valid |= ZIP_STAT_COMP_SIZE; st.comp_size = st.size; } else { /* we'll recompress */ st.valid &= ~ZIP_STAT_COMP_SIZE; } flags = ZIP_EF_LOCAL; if ((st.valid & ZIP_STAT_SIZE) == 0) flags |= ZIP_FL_FORCE_ZIP64; else { de->uncomp_size = st.size; if ((st.valid & ZIP_STAT_COMP_SIZE) == 0) { if (( ((de->comp_method == ZIP_CM_DEFLATE || ZIP_CM_IS_DEFAULT(de->comp_method)) && st.size > MAX_DEFLATE_SIZE_32) || (de->comp_method != ZIP_CM_STORE && de->comp_method != ZIP_CM_DEFLATE && !ZIP_CM_IS_DEFAULT(de->comp_method)))) flags |= ZIP_FL_FORCE_ZIP64; } else de->comp_size = st.comp_size; } offstart = ftello(ft); /* as long as we don't support non-seekable output, clear data descriptor bit */ de->bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR; if ((is_zip64=_zip_dirent_write(de, ft, flags, &za->error)) < 0) return -1; if (st.comp_method == ZIP_CM_STORE || (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != de->comp_method)) { struct zip_source *s_store, *s_crc; zip_compression_implementation comp_impl; 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 -1; } if ((s_store=comp_impl(za, src, st.comp_method, ZIP_CODEC_DECODE)) == NULL) { /* error set by comp_impl */ return -1; } } else s_store = src; if ((s_crc=zip_source_crc(za, s_store, 0)) == NULL) { if (s_store != src) zip_source_pop(s_store); return -1; } /* TODO: deflate 0-byte files for torrentzip? */ if (de->comp_method != ZIP_CM_STORE && ((st.valid & ZIP_STAT_SIZE) == 0 || st.size != 0)) { if ((comp_impl=_zip_get_compression_implementation(de->comp_method)) == NULL) { _zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); zip_source_pop(s_crc); if (s_store != src) zip_source_pop(s_store); return -1; } if ((s2=comp_impl(za, s_crc, de->comp_method, ZIP_CODEC_ENCODE)) == NULL) { zip_source_pop(s_crc); if (s_store != src) zip_source_pop(s_store); return -1; } } else s2 = s_crc; } else s2 = src; offdata = ftello(ft); ret = copy_source(za, s2, ft); if (zip_source_stat(s2, &st) < 0) ret = -1; while (s2 != src) { if ((s2=zip_source_pop(s2)) == NULL) { /* TODO: set erorr */ ret = -1; break; } } if (ret < 0) return -1; offend = ftello(ft); if (fseeko(ft, offstart, SEEK_SET) < 0) { _zip_error_set(&za->error, ZIP_ER_SEEK, errno); return -1; } if ((st.valid & (ZIP_STAT_COMP_METHOD|ZIP_STAT_CRC|ZIP_STAT_SIZE)) != (ZIP_STAT_COMP_METHOD|ZIP_STAT_CRC|ZIP_STAT_SIZE)) { _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } if (st.valid & ZIP_STAT_MTIME) de->last_mod = st.mtime; else time(&de->last_mod); de->comp_method = st.comp_method; de->crc = st.crc; de->uncomp_size = st.size; de->comp_size = (zip_uint64_t)(offend - offdata); if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) _zip_dirent_torrent_normalize(de); if ((ret=_zip_dirent_write(de, ft, flags, &za->error)) < 0) return -1; if (is_zip64 != ret) { /* Zip64 mismatch between preliminary file header written before data and final file header written afterwards */ _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } if (fseeko(ft, offend, SEEK_SET) < 0) { _zip_error_set(&za->error, ZIP_ER_SEEK, errno); return -1; } return 0; }
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; }
static zip_int64_t compress_read(zip_source_t *src, struct deflate *ctx, void *data, zip_uint64_t len) { int end, ret; zip_int64_t n; zip_uint64_t out_offset; uInt out_len; if (zip_error_code_zip(&ctx->error) != ZIP_ER_OK) return -1; if (len == 0 || ctx->is_stored) { return 0; } out_offset = 0; out_len = (uInt)ZIP_MIN(UINT_MAX, len); ctx->zstr.next_out = (Bytef *)data; ctx->zstr.avail_out = out_len; end = 0; while (!end) { ret = deflate(&ctx->zstr, ctx->eof ? Z_FINISH : 0); switch (ret) { case Z_STREAM_END: if (ctx->can_store && ctx->zstr.total_in <= ctx->zstr.total_out) { ctx->is_stored = true; ctx->size = ctx->zstr.total_in; memcpy(data, ctx->buffer, ctx->size); return (zip_int64_t)ctx->size; } /* fallthrough */ case Z_OK: /* all ok */ if (ctx->zstr.avail_out == 0) { out_offset += out_len; if (out_offset < len) { out_len = (uInt)ZIP_MIN(UINT_MAX, len-out_offset); ctx->zstr.next_out = (Bytef *)data+out_offset; ctx->zstr.avail_out = out_len; } else { ctx->can_store = false; end = 1; } } else if (ctx->eof && ctx->zstr.avail_in == 0) end = 1; break; case Z_BUF_ERROR: if (ctx->zstr.avail_in == 0) { if (ctx->eof) { end = 1; break; } if ((n=zip_source_read(src, ctx->buffer, sizeof(ctx->buffer))) < 0) { _zip_error_set_from_source(&ctx->error, src); end = 1; break; } else if (n == 0) { ctx->eof = true; /* TODO: check against stat of src? */ ctx->size = ctx->zstr.total_in; } else { if (ctx->zstr.total_in > 0) { /* we overwrote a previously filled ctx->buffer */ ctx->can_store = false; } ctx->zstr.next_in = (Bytef *)ctx->buffer; ctx->zstr.avail_in = (uInt)n; } continue; } /* fallthrough */ case Z_NEED_DICT: case Z_DATA_ERROR: case Z_STREAM_ERROR: case Z_MEM_ERROR: zip_error_set(&ctx->error, ZIP_ER_ZLIB, ret); end = 1; break; } } if (ctx->zstr.avail_out < len) { ctx->can_store = false; return (zip_int64_t)(len - ctx->zstr.avail_out); } return (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) ? 0 : -1; }
ZIP_EXTERN zip_t * zip_open_from_source(zip_source_t *src, int _flags, zip_error_t *error) { static zip_int64_t needed_support_read = -1; static zip_int64_t needed_support_write = -1; unsigned int flags; zip_int64_t supported; exists_t exists; if (_flags < 0 || src == NULL) { zip_error_set(error, ZIP_ER_INVAL, 0); return NULL; } flags = (unsigned int)_flags; supported = zip_source_supports(src); if (needed_support_read == -1) { needed_support_read = zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_STAT, -1); needed_support_write = zip_source_make_command_bitmap(ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_REMOVE, -1); } if ((supported & needed_support_read) != needed_support_read) { zip_error_set(error, ZIP_ER_OPNOTSUPP, 0); return NULL; } if ((supported & needed_support_write) != needed_support_write) { flags |= ZIP_RDONLY; } if ((flags & (ZIP_RDONLY|ZIP_TRUNCATE)) == (ZIP_RDONLY|ZIP_TRUNCATE)) { zip_error_set(error, ZIP_ER_RDONLY, 0); return NULL; } exists = _zip_file_exists(src, error); switch (exists) { case EXISTS_ERROR: return NULL; case EXISTS_NOT: if ((flags & ZIP_CREATE) == 0) { zip_error_set(error, ZIP_ER_NOENT, 0); return NULL; } return _zip_allocate_new(src, flags, error); default: { zip_t *za; if (flags & ZIP_EXCL) { zip_error_set(error, ZIP_ER_EXISTS, 0); return NULL; } if (zip_source_open(src) < 0) { _zip_error_set_from_source(error, src); return NULL; } if (flags & ZIP_TRUNCATE) { za = _zip_allocate_new(src, flags, error); } else { /* ZIP_CREATE gets ignored if file exists and not ZIP_EXCL, just like open() */ za = _zip_open(src, flags, error); } if (za == NULL) { zip_source_close(src); return NULL; } return za; } } }
static zip_int64_t decompress_read(zip_source_t *src, struct deflate *ctx, void *data, zip_uint64_t len) { int end, ret; zip_int64_t n; zip_uint64_t out_offset; uInt out_len; if (zip_error_code_zip(&ctx->error) != ZIP_ER_OK) return -1; if (len == 0) return 0; out_offset = 0; out_len = (uInt)ZIP_MIN(UINT_MAX, len); ctx->zstr.next_out = (Bytef *)data; ctx->zstr.avail_out = out_len; end = 0; while (!end) { ret = inflate(&ctx->zstr, Z_SYNC_FLUSH); switch (ret) { case Z_OK: if (ctx->zstr.avail_out == 0) { out_offset += out_len; if (out_offset < len) { out_len = (uInt)ZIP_MIN(UINT_MAX, len-out_offset); ctx->zstr.next_out = (Bytef *)data+out_offset; ctx->zstr.avail_out = out_len; } else { end = 1; } } break; case Z_STREAM_END: ctx->eof = 1; end = 1; break; case Z_BUF_ERROR: if (ctx->zstr.avail_in == 0) { if (ctx->eof) { end = 1; break; } if ((n=zip_source_read(src, ctx->buffer, sizeof(ctx->buffer))) < 0) { _zip_error_set_from_source(&ctx->error, src); end = 1; break; } else if (n == 0) { ctx->eof = 1; } else { ctx->zstr.next_in = (Bytef *)ctx->buffer; ctx->zstr.avail_in = (uInt)n; } continue; } /* fallthrough */ case Z_NEED_DICT: case Z_DATA_ERROR: case Z_STREAM_ERROR: case Z_MEM_ERROR: zip_error_set(&ctx->error, ZIP_ER_ZLIB, ret); end = 1; break; } } if (ctx->zstr.avail_out < len) return (zip_int64_t)(len - ctx->zstr.avail_out); return (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) ? 0 : -1; }
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; }
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 int add_data(struct zip *za, struct zip_source *src, struct zip_dirent *de, FILE *ft) { off_t offstart, offdata, offend; struct zip_stat st; struct zip_source *s2; zip_compression_implementation comp_impl; int ret; if (zip_source_stat(src, &st) < 0) { _zip_error_set_from_source(&za->error, src); return -1; } offstart = ftello(ft); if (_zip_dirent_write(de, ft, 1, &za->error) < 0) return -1; if ((s2=zip_source_crc(za, src, 0)) == NULL) { zip_source_pop(s2); return -1; } /* XXX: deflate 0-byte files for torrentzip? */ if (((st.valid & ZIP_STAT_COMP_METHOD) == 0 || st.comp_method == ZIP_CM_STORE) && ((st.valid & ZIP_STAT_SIZE) == 0 || st.size != 0)) { comp_impl = NULL; if ((comp_impl=zip_get_compression_implementation(ZIP_CM_DEFLATE)) == NULL) { _zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); zip_source_pop(s2); return -1; } if ((s2=comp_impl(za, s2, ZIP_CM_DEFLATE, ZIP_CODEC_ENCODE)) == NULL) { /* XXX: set error? */ zip_source_pop(s2); return -1; } } else s2 = src; offdata = ftello(ft); ret = copy_source(za, s2, ft); if (zip_source_stat(s2, &st) < 0) ret = -1; while (s2 != src) { if ((s2=zip_source_pop(s2)) == NULL) { /* XXX: set erorr */ ret = -1; break; } } if (ret < 0) return -1; offend = ftello(ft); if (fseeko(ft, offstart, SEEK_SET) < 0) { _zip_error_set(&za->error, ZIP_ER_SEEK, errno); return -1; } de->last_mod = st.mtime; de->comp_method = st.comp_method; de->crc = st.crc; de->uncomp_size = st.size; de->comp_size = offend - offdata; if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) _zip_dirent_torrent_normalize(de); if (_zip_dirent_write(de, ft, 1, &za->error) < 0) return -1; if (fseeko(ft, offend, SEEK_SET) < 0) { _zip_error_set(&za->error, ZIP_ER_SEEK, errno); return -1; } return 0; }
static zip_cdir_t * _zip_read_eocd64(zip_source_t *src, zip_buffer_t *buffer, zip_uint64_t buf_offset, unsigned int flags, zip_error_t *error) { zip_cdir_t *cd; zip_uint64_t offset; zip_uint8_t eocd[EOCD64LEN]; zip_uint64_t eocd_offset; zip_uint64_t size, nentry, i, eocdloc_offset; bool free_buffer; zip_uint32_t num_disks, num_disks64, eocd_disk, eocd_disk64; eocdloc_offset = _zip_buffer_offset(buffer); _zip_buffer_get(buffer, 4); /* magic already verified */ num_disks = _zip_buffer_get_16(buffer); eocd_disk = _zip_buffer_get_16(buffer); eocd_offset = _zip_buffer_get_64(buffer); if (eocd_offset > ZIP_INT64_MAX || eocd_offset + EOCD64LEN < eocd_offset) { zip_error_set(error, ZIP_ER_SEEK, EFBIG); return NULL; } if (eocd_offset + EOCD64LEN > eocdloc_offset + buf_offset) { zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) { _zip_buffer_set_offset(buffer, eocd_offset - buf_offset); free_buffer = false; } else { if (zip_source_seek(src, (zip_int64_t)eocd_offset, SEEK_SET) < 0) { _zip_error_set_from_source(error, src); return NULL; } if ((buffer = _zip_buffer_new_from_source(src, EOCD64LEN, eocd, error)) == NULL) { return NULL; } free_buffer = true; } if (memcmp(_zip_buffer_get(buffer, 4), EOCD64_MAGIC, 4) != 0) { zip_error_set(error, ZIP_ER_INCONS, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } size = _zip_buffer_get_64(buffer); if ((flags & ZIP_CHECKCONS) && size + eocd_offset + 12 != buf_offset + eocdloc_offset) { zip_error_set(error, ZIP_ER_INCONS, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } _zip_buffer_get(buffer, 4); /* skip version made by/needed */ num_disks64 = _zip_buffer_get_32(buffer); eocd_disk64 = _zip_buffer_get_32(buffer); /* if eocd values are 0xffff, we have to use eocd64 values. otherwise, if the values are not the same, it's inconsistent; in any case, if the value is not 0, we don't support it */ if (num_disks == 0xffff) { num_disks = num_disks64; } if (eocd_disk == 0xffff) { eocd_disk = eocd_disk64; } if ((flags & ZIP_CHECKCONS) && (eocd_disk != eocd_disk64 || num_disks != num_disks64)) { zip_error_set(error, ZIP_ER_INCONS, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } if (num_disks != 0 || eocd_disk != 0) { zip_error_set(error, ZIP_ER_MULTIDISK, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } nentry = _zip_buffer_get_64(buffer); i = _zip_buffer_get_64(buffer); if (nentry != i) { zip_error_set(error, ZIP_ER_MULTIDISK, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } size = _zip_buffer_get_64(buffer); offset = _zip_buffer_get_64(buffer); if (!_zip_buffer_ok(buffer)) { zip_error_set(error, ZIP_ER_INTERNAL, 0); if (free_buffer) { _zip_buffer_free(buffer); } return NULL; } if (free_buffer) { _zip_buffer_free(buffer); } if (offset > ZIP_INT64_MAX || offset+size < offset) { zip_error_set(error, ZIP_ER_SEEK, EFBIG); return NULL; } if (offset+size > buf_offset + eocd_offset) { /* cdir spans past EOCD record */ zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } if ((flags & ZIP_CHECKCONS) && offset+size != buf_offset + eocd_offset) { zip_error_set(error, ZIP_ER_INCONS, 0); return NULL; } if ((cd=_zip_cdir_new(nentry, error)) == NULL) return NULL; cd->is_zip64 = true; cd->size = size; cd->offset = offset; return cd; }
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; }
static zip_int64_t deflate_decompress(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { struct deflate *ctx; zip_int64_t n; int ret; ctx = (struct deflate *)ud; switch (cmd) { case ZIP_SOURCE_OPEN: if ((n=zip_source_read(src, ctx->buffer, sizeof(ctx->buffer))) < 0) { _zip_error_set_from_source(&ctx->error, src); return -1; } ctx->zstr.zalloc = Z_NULL; ctx->zstr.zfree = Z_NULL; ctx->zstr.opaque = NULL; ctx->zstr.next_in = (Bytef *)ctx->buffer; ctx->zstr.avail_in = (uInt)n; /* negative value to tell zlib that there is no header */ if ((ret=inflateInit2(&ctx->zstr, -MAX_WBITS)) != Z_OK) { zip_error_set(&ctx->error, ZIP_ER_ZLIB, ret); return -1; } return 0; case ZIP_SOURCE_READ: return decompress_read(src, ctx, data, len); case ZIP_SOURCE_CLOSE: inflateEnd(&ctx->zstr); return 0; case ZIP_SOURCE_STAT: { zip_stat_t *st; st = (zip_stat_t *)data; st->comp_method = ZIP_CM_STORE; if (st->comp_size > 0 && st->size > 0) st->comp_size = st->size; return 0; } case ZIP_SOURCE_ERROR: return zip_error_to_data(&ctx->error, data, len); case ZIP_SOURCE_FREE: free(ctx); return 0; case ZIP_SOURCE_SUPPORTS: return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, -1); default: zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0); return -1; } }
static zip_int64_t winzip_aes_decrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd) { struct winzip_aes *ctx; zip_int64_t n; zip_uint64_t total, offset; ctx = (struct winzip_aes *)ud; switch (cmd) { case ZIP_SOURCE_OPEN: if (decrypt_header(src, ctx) < 0) { return -1; } ctx->current_position = 0; return 0; case ZIP_SOURCE_READ: if (len > ctx->data_length - ctx->current_position) { len = ctx->data_length - ctx->current_position; } if (len == 0) { if (!verify_hmac(src, ctx)) { return -1; } return 0; } if ((n=zip_source_read(src, data, len)) < 0) { _zip_error_set_from_source(&ctx->error, src); return -1; } ctx->current_position += n; total = (zip_uint64_t)n; for (offset = 0; offset < total; offset += ZIP_MIN(total - offset, UINT_MAX)) { _zip_fcrypt_decrypt(data + offset, ZIP_MIN(total - offset, UINT_MAX), &ctx->fcrypt_ctx); } return n; case ZIP_SOURCE_CLOSE: return 0; case ZIP_SOURCE_STAT: { zip_stat_t *st; st = (zip_stat_t *)data; st->encryption_method = ZIP_EM_NONE; st->valid |= ZIP_STAT_ENCRYPTION_METHOD; if (st->valid & ZIP_STAT_COMP_SIZE) { st->comp_size -= 12 + salt_length[ctx->mode]; } return 0; } case ZIP_SOURCE_SUPPORTS: return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, -1); case ZIP_SOURCE_ERROR: return zip_error_to_data(&ctx->error, data, len); case ZIP_SOURCE_FREE: winzip_aes_free(ctx); return 0; default: zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); return -1; } }
int _zip_read_local_ef(zip_t *za, zip_uint64_t idx) { zip_entry_t *e; unsigned char b[4]; zip_buffer_t *buffer; 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 (e->orig->offset + 26 > ZIP_INT64_MAX) { zip_error_set(&za->error, ZIP_ER_SEEK, EFBIG); return -1; } if (zip_source_seek(za->src, (zip_int64_t)(e->orig->offset + 26), SEEK_SET) < 0) { _zip_error_set_from_source(&za->error, za->src); return -1; } if ((buffer = _zip_buffer_new_from_source(za->src, sizeof(b), b, &za->error)) == NULL) { return -1; } fname_len = _zip_buffer_get_16(buffer); ef_len = _zip_buffer_get_16(buffer); if (!_zip_buffer_eof(buffer)) { _zip_buffer_free(buffer); zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } _zip_buffer_free(buffer); if (ef_len > 0) { zip_extra_field_t *ef; zip_uint8_t *ef_raw; if (zip_source_seek(za->src, fname_len, SEEK_CUR) < 0) { zip_error_set(&za->error, ZIP_ER_SEEK, errno); return -1; } ef_raw = _zip_read_data(NULL, za->src, ef_len, 0, &za->error); if (ef_raw == NULL) return -1; if (!_zip_ef_parse(ef_raw, ef_len, ZIP_EF_LOCAL, &ef, &za->error)) { free(ef_raw); return -1; } free(ef_raw); if (ef) { 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; }