VeraCrypt
aboutsummaryrefslogtreecommitdiff
path: root/src/Common/libzip/zip_close.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/Common/libzip/zip_close.c')
-rw-r--r--src/Common/libzip/zip_close.c492
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;
+}