diff options
Diffstat (limited to 'src/Common/libzip/zip_close.c')
-rw-r--r-- | src/Common/libzip/zip_close.c | 313 |
1 files changed, 189 insertions, 124 deletions
diff --git a/src/Common/libzip/zip_close.c b/src/Common/libzip/zip_close.c index 88fa4449..c46e1b34 100644 --- a/src/Common/libzip/zip_close.c +++ b/src/Common/libzip/zip_close.c @@ -1,6 +1,6 @@ /* zip_close.c -- close zip archive and update changes - Copyright (C) 1999-2016 Dieter Baron and Thomas Klausner + Copyright (C) 1999-2017 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> @@ -17,7 +17,7 @@ 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 @@ -43,27 +43,22 @@ #ifdef HAVE_UNISTD_H #include <unistd.h> #endif -#include <sys/types.h> #include <sys/stat.h> +#include <sys/types.h> #ifdef _WIN32 -#include <io.h> #include <fcntl.h> +#include <io.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 copy_source(zip_t *, zip_source_t *, zip_int64_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_close(zip_t *za) { + zip_uint64_t i, j, survivors, unchanged_offset; zip_int64_t off; int error; zip_filelist_t *filelist; @@ -84,7 +79,7 @@ zip_close(zip_t *za) } zip_discard(za); return 0; - } + } if (!changed) { zip_discard(za); @@ -92,60 +87,102 @@ zip_close(zip_t *za) } if (survivors > za->nentry) { - zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); - return -1; + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + return -1; } - - if ((filelist=(zip_filelist_t *)malloc(sizeof(filelist[0])*(size_t)survivors)) == NULL) + + if ((filelist = (zip_filelist_t *)malloc(sizeof(filelist[0]) * (size_t)survivors)) == NULL) return -1; + unchanged_offset = ZIP_UINT64_MAX; /* create list of files with index into original archive */ - for (i=j=0; i<za->nentry; i++) { - if (za->entry[i].deleted) + for (i = j = 0; i < za->nentry; i++) { + if (za->entry[i].orig != NULL && ZIP_ENTRY_HAS_CHANGES(&za->entry[i])) { + unchanged_offset = ZIP_MIN(unchanged_offset, za->entry[i].orig->offset); + } + if (za->entry[i].deleted) { continue; + } + + if (j >= survivors) { + free(filelist); + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); + return -1; + } - 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); + zip_error_set(&za->error, ZIP_ER_INTERNAL, 0); return -1; } - + + if ((zip_source_supports(za->src) & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE_CLONING)) == 0) { + unchanged_offset = 0; + } + else { + if (unchanged_offset == ZIP_UINT64_MAX) { + /* we're keeping all file data, find the end of the last one */ + zip_uint64_t last_index = ZIP_UINT64_MAX; + unchanged_offset = 0; + + for (i = 0; i < za->nentry; i++) { + if (za->entry[i].orig != NULL) { + if (za->entry[i].orig->offset >= unchanged_offset) { + unchanged_offset = za->entry[i].orig->offset; + last_index = i; + } + } + } + if (last_index != ZIP_UINT64_MAX) { + if ((unchanged_offset = _zip_file_get_end(za, last_index, &za->error)) == 0) { + free(filelist); + return -1; + } + } + } + if (unchanged_offset > 0) { + if (zip_source_begin_write_cloning(za->src, unchanged_offset) < 0) { + /* cloning not supported, need to copy everything */ + unchanged_offset = 0; + } + } + } + if (unchanged_offset == 0) { + if (zip_source_begin_write(za->src) < 0) { + _zip_error_set_from_source(&za->error, za->src); + free(filelist); + return -1; + } + } + + _zip_progress_start(za->progress); error = 0; - for (j=0; j<survivors; j++) { + for (j = 0; j < survivors; j++) { int new_data; zip_entry_t *entry; zip_dirent_t *de; - if (za->progress_callback) { - za->progress_callback((double)j/survivors); - } + _zip_progress_subrange(za->progress, (double)j / (double)survivors, (double)(j + 1) / (double)survivors); i = filelist[j].idx; - entry = za->entry+i; + entry = za->entry + i; + + if (entry->orig != NULL && entry->orig->offset < unchanged_offset) { + /* already implicitly copied by cloning */ + continue; + } new_data = (ZIP_ENTRY_DATA_CHANGED(entry) || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_COMP_METHOD) || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_ENCRYPTION_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; + if ((entry->changes = _zip_dirent_clone(entry->orig)) == NULL) { + zip_error_set(&za->error, ZIP_ER_MEMORY, 0); + error = 1; + break; } } de = entry->changes; @@ -155,18 +192,18 @@ zip_close(zip_t *za) break; } - if ((off = zip_source_tell_write(za->src)) < 0) { - error = 1; - break; - } - de->offset = (zip_uint64_t)off; + 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) { + if ((zs = _zip_source_zip_new(za, za, i, ZIP_FL_UNCHANGED, 0, 0, NULL)) == NULL) { error = 1; break; } @@ -191,7 +228,7 @@ zip_close(zip_t *za) error = 1; break; } - if ((offset=_zip_file_get_offset(za, i, &za->error)) == 0) { + if ((offset = _zip_file_get_offset(za, i, &za->error)) == 0) { error = 1; break; } @@ -221,32 +258,30 @@ zip_close(zip_t *za) } } + _zip_progress_end(za->progress); + if (error) { zip_source_rollback_write(za->src); return -1; } - if (za->progress_callback) { - za->progress_callback(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; +add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) { + zip_int64_t offstart, offdata, offend, data_length; struct zip_stat st; zip_source_t *src_final, *src_tmp; int ret; int is_zip64; zip_flags_t flags; + zip_int8_t compression_flags; bool needs_recompress, needs_decompress, needs_crc, needs_compress, needs_reencrypt, needs_decrypt, needs_encrypt; - + if (zip_source_stat(src, &st) < 0) { _zip_error_set_from_source(&za->error, src); return -1; @@ -275,30 +310,58 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) flags = ZIP_EF_LOCAL; - if ((st.valid & ZIP_STAT_SIZE) == 0) + if ((st.valid & ZIP_STAT_SIZE) == 0) { flags |= ZIP_FL_FORCE_ZIP64; + data_length = -1; + } else { de->uncomp_size = st.size; - + /* this is technically incorrect (copy_source counts compressed data), but it's the best we have */ + data_length = (zip_int64_t)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)))) + zip_uint64_t max_size; + + switch (ZIP_CM_ACTUAL(de->comp_method)) { + case ZIP_CM_BZIP2: + /* computed by looking at increase of 10 random files of size 1MB when + * compressed with bzip2, rounded up: 1.006 */ + max_size = 4269351188u; + break; + + case ZIP_CM_DEFLATE: + /* max deflate size increase: size + ceil(size/16k)*5+6 */ + max_size = 4293656963u; + break; + + case ZIP_CM_STORE: + max_size = 0xffffffffu; + break; + + default: + max_size = 0; + } + + if (st.size > max_size) { flags |= ZIP_FL_FORCE_ZIP64; + } } else de->comp_size = st.comp_size; } if ((offstart = zip_source_tell_write(za->src)) < 0) { - return -1; + _zip_error_set_from_source(&za->error, za->src); + 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) + if ((is_zip64 = _zip_dirent_write(za, de, flags)) < 0) { return -1; + } - needs_recompress = !((st.comp_method == de->comp_method) || (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method == ZIP_CM_DEFLATE)); + needs_recompress = st.comp_method != ZIP_CM_ACTUAL(de->comp_method); needs_decompress = needs_recompress && (st.comp_method != ZIP_CM_STORE); needs_crc = (st.comp_method == ZIP_CM_STORE) || needs_decompress; needs_compress = needs_recompress && (de->comp_method != ZIP_CM_STORE); @@ -312,7 +375,7 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) if (needs_decrypt) { zip_encryption_implementation impl; - + if ((impl = _zip_get_encryption_implementation(st.encryption_method, ZIP_CODEC_DECODE)) == NULL) { zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); zip_source_free(src_final); @@ -327,17 +390,9 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) zip_source_free(src_final); src_final = src_tmp; } - + if (needs_decompress) { - zip_compression_implementation comp_impl; - - if ((comp_impl = _zip_get_compression_implementation(st.comp_method, ZIP_CODEC_DECODE)) == NULL) { - zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); - zip_source_free(src_final); - return -1; - } - if ((src_tmp = comp_impl(za, src_final, st.comp_method, ZIP_CODEC_DECODE)) == NULL) { - /* error set by comp_impl */ + if ((src_tmp = zip_source_decompress(za, src_final, st.comp_method)) == NULL) { zip_source_free(src_final); return -1; } @@ -357,33 +412,27 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) } if (needs_compress) { - zip_compression_implementation comp_impl; - - if ((comp_impl = _zip_get_compression_implementation(de->comp_method, ZIP_CODEC_ENCODE)) == NULL) { - zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0); + if ((src_tmp = zip_source_compress(za, src_final, de->comp_method, de->compression_level)) == NULL) { zip_source_free(src_final); return -1; } - if ((src_tmp = comp_impl(za, src_final, de->comp_method, ZIP_CODEC_ENCODE)) == NULL) { - zip_source_free(src_final); - return -1; - } - + zip_source_free(src_final); src_final = src_tmp; } - + if (needs_encrypt) { zip_encryption_implementation impl; const char *password = NULL; if (de->password) { password = de->password; - } else if (za->default_password) { + } + else if (za->default_password) { password = za->default_password; } - + if ((impl = _zip_get_encryption_implementation(de->encryption_method, ZIP_CODEC_ENCODE)) == NULL) { zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0); zip_source_free(src_final); @@ -401,12 +450,19 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) if ((offdata = zip_source_tell_write(za->src)) < 0) { - return -1; + _zip_error_set_from_source(&za->error, za->src); + return -1; } - ret = copy_source(za, src_final); - + ret = copy_source(za, src_final, data_length); + if (zip_source_stat(src_final, &st) < 0) { + _zip_error_set_from_source(&za->error, src_final); + ret = -1; + } + + if ((compression_flags = zip_source_get_compression_flags(src_final)) < 0) { + _zip_error_set_from_source(&za->error, src_final); ret = -1; } @@ -417,7 +473,8 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) } if ((offend = zip_source_tell_write(za->src)) < 0) { - return -1; + _zip_error_set_from_source(&za->error, za->src); + return -1; } if (zip_source_seek_write(za->src, offstart, SEEK_SET) < 0) { @@ -425,32 +482,33 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) 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)) { + 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); + 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); + de->bitflags = (zip_uint16_t)((de->bitflags & (zip_uint16_t)~6) | ((zip_uint8_t)compression_flags << 1)); + _zip_dirent_set_version_needed(de, (flags & ZIP_FL_FORCE_ZIP64) != 0); - if ((ret=_zip_dirent_write(za, de, flags)) < 0) + 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; @@ -461,10 +519,10 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de) static int -copy_data(zip_t *za, zip_uint64_t len) -{ +copy_data(zip_t *za, zip_uint64_t len) { zip_uint8_t buf[BUFSIZE]; size_t n; + double total = (double)len; while (len > 0) { n = len > sizeof(buf) ? sizeof(buf) : len; @@ -475,8 +533,10 @@ copy_data(zip_t *za, zip_uint64_t len) if (_zip_write(za, buf, n) < 0) { return -1; } - + len -= n; + + _zip_progress_update(za->progress, (total - (double)len) / total); } return 0; @@ -484,10 +544,9 @@ copy_data(zip_t *za, zip_uint64_t len) static int -copy_source(zip_t *za, zip_source_t *src) -{ +copy_source(zip_t *za, zip_source_t *src, zip_int64_t data_length) { zip_uint8_t buf[BUFSIZE]; - zip_int64_t n; + zip_int64_t n, current; int ret; if (zip_source_open(src) < 0) { @@ -496,39 +555,42 @@ copy_source(zip_t *za, zip_source_t *src) } ret = 0; - while ((n=zip_source_read(src, buf, sizeof(buf))) > 0) { + current = 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 == sizeof(buf) && za->progress && data_length > 0) { + current += n; + _zip_progress_update(za->progress, (double)current / (double)data_length); + } } - + 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) -{ +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; + return -1; } - if ((size=_zip_cdir_write(za, filelist, survivors)) < 0) { + if ((size = _zip_cdir_write(za, filelist, survivors)) < 0) { return -1; } - + if ((end = zip_source_tell_write(za->src)) < 0) { - return -1; + return -1; } return 0; @@ -536,26 +598,29 @@ write_cdir(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivors) int -_zip_changed(const zip_t *za, zip_uint64_t *survivorsp) -{ +_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) + 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)) + for (i = 0; i < za->nentry; i++) { + if (ZIP_ENTRY_HAS_CHANGES(&za->entry[i])) { changed = 1; - if (!za->entry[i].deleted) + } + if (!za->entry[i].deleted) { survivors++; + } } - if (survivorsp) + if (survivorsp) { *survivorsp = survivors; + } return changed; } |