diff options
Diffstat (limited to 'src/Common/libzip/zip_close.c')
-rw-r--r-- | src/Common/libzip/zip_close.c | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/src/Common/libzip/zip_close.c b/src/Common/libzip/zip_close.c new file mode 100644 index 00000000..b5eca67a --- /dev/null +++ b/src/Common/libzip/zip_close.c @@ -0,0 +1,492 @@ +/* + zip_close.c -- close zip archive and update changes + Copyright (C) 1999-2015 Dieter Baron and Thomas Klausner + + This file is part of libzip, a library to manipulate ZIP archives. + The authors can be contacted at <libzip@nih.at> + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + 3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#include "zipint.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <sys/types.h> +#include <sys/stat.h> +#ifdef _WIN32 +#include <io.h> +#include <fcntl.h> +#endif + + +/* max deflate size increase: size + ceil(size/16k)*5+6 */ +#define MAX_DEFLATE_SIZE_32 4293656963u + +static int add_data(zip_t *, zip_source_t *, zip_dirent_t *); +static int copy_data(zip_t *, zip_uint64_t); +static int copy_source(zip_t *, zip_source_t *); +static int write_cdir(zip_t *, const zip_filelist_t *, zip_uint64_t); + + +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 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 +copy_data(zip_t *za, zip_uint64_t len) +{ + zip_uint8_t buf[BUFSIZE]; + size_t n; + + while (len > 0) { + n = len > sizeof(buf) ? sizeof(buf) : len; + if (_zip_read(za->src, buf, n, &za->error) < 0) { + return -1; + } + + if (_zip_write(za, buf, n) < 0) { + return -1; + } + + len -= n; + } + + return 0; +} + + +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 +write_cdir(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivors) +{ + zip_int64_t cd_start, end, size; + + if ((cd_start = zip_source_tell_write(za->src)) < 0) { + return -1; + } + + if ((size=_zip_cdir_write(za, filelist, survivors)) < 0) { + return -1; + } + + if ((end = zip_source_tell_write(za->src)) < 0) { + return -1; + } + + return 0; +} + + +int +_zip_changed(const zip_t *za, zip_uint64_t *survivorsp) +{ + int changed; + zip_uint64_t i, survivors; + + changed = 0; + survivors = 0; + + if (za->comment_changed || za->ch_flags != za->flags) + changed = 1; + + for (i=0; i<za->nentry; i++) { + if (za->entry[i].deleted || za->entry[i].source || (za->entry[i].changes && za->entry[i].changes->changed != 0)) + changed = 1; + if (!za->entry[i].deleted) + survivors++; + } + + if (survivorsp) + *survivorsp = survivors; + + return changed; +} |