| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698 |
- /*
- zip_close.c -- close zip archive and update changes
- Copyright (C) 1999-2021 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>
- #ifdef _WIN32
- #include <fcntl.h>
- #include <io.h>
- #endif
- static int add_data(zip_t *, zip_source_t *, zip_dirent_t *, zip_uint32_t);
- static int copy_data(zip_t *, zip_uint64_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);
- static int write_data_descriptor(zip_t *za, const zip_dirent_t *dirent, int is_zip64);
- ZIP_EXTERN int
- zip_close(zip_t *za) {
- zip_uint64_t i, j, survivors, unchanged_offset;
- 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) {
- if (!((zip_error_code_zip(zip_source_error(za->src)) == ZIP_ER_REMOVE) && (zip_error_code_system(zip_source_error(za->src)) == ENOENT))) {
- _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;
- 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].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;
- }
- filelist[j].idx = i;
- j++;
- }
- if (j < survivors) {
- 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;
- }
- }
- if (_zip_progress_start(za->progress) != 0) {
- zip_error_set(&za->error, ZIP_ER_CANCELLED, 0);
- zip_source_rollback_write(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;
- if (_zip_progress_subrange(za->progress, (double)j / (double)survivors, (double)(j + 1) / (double)survivors) != 0) {
- zip_error_set(&za->error, ZIP_ER_CANCELLED, 0);
- error = 1;
- break;
- }
- i = filelist[j].idx;
- 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;
- }
- }
- de = entry->changes;
- if (_zip_read_local_ef(za, i) < 0) {
- error = 1;
- break;
- }
- if ((off = zip_source_tell_write(za->src)) < 0) {
- _zip_error_set_from_source(&za->error, za->src);
- 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, i, ZIP_FL_UNCHANGED, 0, 0, NULL, &za->error)) == NULL) {
- error = 1;
- break;
- }
- }
- /* add_data writes dirent */
- if (add_data(za, zs ? zs : entry->source, de, entry->changes ? entry->changes->changed : 0) < 0) {
- error = 1;
- if (zs)
- zip_source_free(zs);
- break;
- }
- if (zs)
- zip_source_free(zs);
- }
- else {
- zip_uint64_t offset;
- if (de->encryption_method != ZIP_EM_TRAD_PKWARE) {
- /* when copying data, all sizes are known -> no data descriptor needed */
- /* except for PKWare encryption, where removing the data descriptor breaks password validation */
- 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 (de->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) {
- if (write_data_descriptor(za, de, _zip_dirent_needs_zip64(de, 0)) < 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;
- }
- _zip_progress_end(za->progress);
- }
- 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_uint32_t changed) {
- zip_int64_t offstart, offdata, offend, data_length;
- zip_stat_t st;
- zip_file_attributes_t attributes;
- zip_source_t *src_final, *src_tmp;
- int ret;
- int is_zip64;
- zip_flags_t 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;
- }
- 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;
- }
- if ((st.valid & ZIP_STAT_ENCRYPTION_METHOD) == 0) {
- st.valid |= ZIP_STAT_ENCRYPTION_METHOD;
- st.encryption_method = ZIP_EM_NONE;
- }
- flags = ZIP_EF_LOCAL;
- 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) {
- zip_uint64_t max_compressed_size;
- zip_uint16_t compression_method = ZIP_CM_ACTUAL(de->comp_method);
- if (compression_method == ZIP_CM_STORE) {
- max_compressed_size = st.size;
- }
- else {
- zip_compression_algorithm_t *algorithm = _zip_get_compression_algorithm(compression_method, true);
- if (algorithm == NULL) {
- max_compressed_size = ZIP_UINT64_MAX;
- }
- else {
- max_compressed_size = algorithm->maximum_compressed_size(st.size);
- }
- }
- if (max_compressed_size > 0xffffffffu) {
- flags |= ZIP_FL_FORCE_ZIP64;
- }
- }
- else {
- de->comp_size = st.comp_size;
- data_length = (zip_int64_t)st.comp_size;
- }
- }
- if ((offstart = zip_source_tell_write(za->src)) < 0) {
- _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) {
- return -1;
- }
- needs_recompress = st.comp_method != ZIP_CM_ACTUAL(de->comp_method);
- needs_decompress = needs_recompress && (st.comp_method != ZIP_CM_STORE);
- /* in these cases we can compute the CRC ourselves, so we do */
- needs_crc = (st.comp_method == ZIP_CM_STORE) || needs_decompress;
- needs_compress = needs_recompress && (de->comp_method != ZIP_CM_STORE);
- needs_reencrypt = needs_recompress || (de->changed & ZIP_DIRENT_PASSWORD) || (de->encryption_method != st.encryption_method);
- needs_decrypt = needs_reencrypt && (st.encryption_method != ZIP_EM_NONE);
- needs_encrypt = needs_reencrypt && (de->encryption_method != ZIP_EM_NONE);
- src_final = src;
- zip_source_keep(src_final);
- 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);
- return -1;
- }
- if ((src_tmp = impl(za, src_final, st.encryption_method, ZIP_CODEC_DECODE, za->default_password)) == NULL) {
- /* error set by impl */
- zip_source_free(src_final);
- return -1;
- }
- zip_source_free(src_final);
- src_final = src_tmp;
- }
- if (needs_decompress) {
- if ((src_tmp = zip_source_decompress(za, src_final, st.comp_method)) == NULL) {
- zip_source_free(src_final);
- return -1;
- }
- zip_source_free(src_final);
- src_final = src_tmp;
- }
- if (needs_crc) {
- if ((src_tmp = zip_source_crc_create(src_final, 0, &za->error)) == NULL) {
- zip_source_free(src_final);
- return -1;
- }
- zip_source_free(src_final);
- src_final = src_tmp;
- }
- if (needs_compress) {
- if ((src_tmp = zip_source_compress(za, src_final, de->comp_method, de->compression_level)) == 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) {
- 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);
- return -1;
- }
- if ((src_tmp = impl(za, src_final, de->encryption_method, ZIP_CODEC_ENCODE, password)) == NULL) {
- /* error set by impl */
- zip_source_free(src_final);
- return -1;
- }
- if (de->encryption_method == ZIP_EM_TRAD_PKWARE) {
- de->bitflags |= ZIP_GPBF_DATA_DESCRIPTOR;
- }
- zip_source_free(src_final);
- src_final = src_tmp;
- }
- if ((offdata = zip_source_tell_write(za->src)) < 0) {
- _zip_error_set_from_source(&za->error, za->src);
- return -1;
- }
- 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 (zip_source_get_file_attributes(src_final, &attributes) != 0) {
- _zip_error_set_from_source(&za->error, src_final);
- ret = -1;
- }
- zip_source_free(src_final);
- if (ret < 0) {
- return -1;
- }
- if ((offend = zip_source_tell_write(za->src)) < 0) {
- _zip_error_set_from_source(&za->error, za->src);
- 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);
- _zip_dirent_apply_attributes(de, &attributes, (flags & ZIP_FL_FORCE_ZIP64) != 0, changed);
- 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;
- }
- if (de->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) {
- if (write_data_descriptor(za, de, is_zip64) < 0) {
- return -1;
- }
- }
- return 0;
- }
- static int
- copy_data(zip_t *za, zip_uint64_t len) {
- DEFINE_BYTE_ARRAY(buf, BUFSIZE);
- size_t n;
- double total = (double)len;
- if (!byte_array_init(buf, BUFSIZE)) {
- zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
- return -1;
- }
- while (len > 0) {
- n = len > BUFSIZE ? BUFSIZE : len;
- if (_zip_read(za->src, buf, n, &za->error) < 0) {
- byte_array_fini(buf);
- return -1;
- }
- if (_zip_write(za, buf, n) < 0) {
- byte_array_fini(buf);
- return -1;
- }
- len -= n;
- if (_zip_progress_update(za->progress, (total - (double)len) / total) != 0) {
- zip_error_set(&za->error, ZIP_ER_CANCELLED, 0);
- return -1;
- }
- }
- byte_array_fini(buf);
- return 0;
- }
- static int
- copy_source(zip_t *za, zip_source_t *src, zip_int64_t data_length) {
- DEFINE_BYTE_ARRAY(buf, BUFSIZE);
- zip_int64_t n, current;
- int ret;
- if (zip_source_open(src) < 0) {
- _zip_error_set_from_source(&za->error, src);
- return -1;
- }
- if (!byte_array_init(buf, BUFSIZE)) {
- zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
- return -1;
- }
- ret = 0;
- current = 0;
- while ((n = zip_source_read(src, buf, BUFSIZE)) > 0) {
- if (_zip_write(za, buf, (zip_uint64_t)n) < 0) {
- ret = -1;
- break;
- }
- if (n == BUFSIZE && za->progress && data_length > 0) {
- current += n;
- if (_zip_progress_update(za->progress, (double)current / (double)data_length) != 0) {
- zip_error_set(&za->error, ZIP_ER_CANCELLED, 0);
- ret = -1;
- break;
- }
- }
- }
- if (n < 0) {
- _zip_error_set_from_source(&za->error, src);
- ret = -1;
- }
- byte_array_fini(buf);
- zip_source_close(src);
- return ret;
- }
- static int
- write_cdir(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivors) {
- if (zip_source_tell_write(za->src) < 0) {
- return -1;
- }
- if (_zip_cdir_write(za, filelist, survivors) < 0) {
- return -1;
- }
- if (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 (ZIP_ENTRY_HAS_CHANGES(&za->entry[i])) {
- changed = 1;
- }
- if (!za->entry[i].deleted) {
- survivors++;
- }
- }
- if (survivorsp) {
- *survivorsp = survivors;
- }
- return changed;
- }
- static int
- write_data_descriptor(zip_t *za, const zip_dirent_t *de, int is_zip64) {
- zip_buffer_t *buffer = _zip_buffer_new(NULL, MAX_DATA_DESCRIPTOR_LENGTH);
- int ret = 0;
- if (buffer == NULL) {
- zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
- return -1;
- }
- _zip_buffer_put(buffer, DATADES_MAGIC, 4);
- _zip_buffer_put_32(buffer, de->crc);
- if (is_zip64) {
- _zip_buffer_put_64(buffer, de->comp_size);
- _zip_buffer_put_64(buffer, de->uncomp_size);
- }
- else {
- _zip_buffer_put_32(buffer, (zip_uint32_t)de->comp_size);
- _zip_buffer_put_32(buffer, (zip_uint32_t)de->uncomp_size);
- }
- if (!_zip_buffer_ok(buffer)) {
- zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
- ret = -1;
- }
- else {
- ret = _zip_write(za, _zip_buffer_data(buffer), _zip_buffer_offset(buffer));
- }
- _zip_buffer_free(buffer);
- return ret;
- }
|