2 zip_close.c -- close zip archive and update changes
3 Copyright (C) 1999-2022 Dieter Baron and Thomas Klausner
5 This file is part of libzip, a library to manipulate ZIP archives.
6 The authors can be contacted at <info@libzip.org>
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
11 1. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in
15 the documentation and/or other materials provided with the
17 3. The names of the authors may not be used to endorse or promote
18 products derived from this software without specific prior
21 THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
22 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29 IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
31 IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 static int add_data(zip_t *, zip_source_t *, zip_dirent_t *, zip_uint32_t);
46 static int copy_data(zip_t *, zip_uint64_t);
47 static int copy_source(zip_t *, zip_source_t *, zip_int64_t);
48 static int torrentzip_compare_names(const void *a, const void *b);
49 static int write_cdir(zip_t *, const zip_filelist_t *, zip_uint64_t);
50 static int write_data_descriptor(zip_t *za, const zip_dirent_t *dirent, int is_zip64);
53 zip_close(zip_t *za) {
54 zip_uint64_t i, j, survivors, unchanged_offset;
57 zip_filelist_t *filelist;
63 changed = _zip_changed(za, &survivors);
65 if (survivors == 0 && !(za->ch_flags & ZIP_AFL_CREATE_OR_KEEP_FILE_FOR_EMPTY_ARCHIVE)) {
66 /* don't create zip files with no entries */
67 if ((za->open_flags & ZIP_TRUNCATE) || changed) {
68 if (zip_source_remove(za->src) < 0) {
69 if (!((zip_error_code_zip(zip_source_error(za->src)) == ZIP_ER_REMOVE) && (zip_error_code_system(zip_source_error(za->src)) == ENOENT))) {
70 zip_error_set_from_source(&za->error, za->src);
79 /* Always write empty archive if we are told to keep it, otherwise it wouldn't be created if the file doesn't already exist. */
80 if (!changed && survivors > 0) {
85 if (survivors > za->nentry) {
86 zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
90 if ((filelist = (zip_filelist_t *)malloc(sizeof(filelist[0]) * (size_t)survivors)) == NULL)
93 unchanged_offset = ZIP_UINT64_MAX;
94 /* create list of files with index into original archive */
95 for (i = j = 0; i < za->nentry; i++) {
96 if (za->entry[i].orig != NULL && ZIP_ENTRY_HAS_CHANGES(&za->entry[i])) {
97 unchanged_offset = ZIP_MIN(unchanged_offset, za->entry[i].orig->offset);
99 if (za->entry[i].deleted) {
103 if (j >= survivors) {
105 zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
110 filelist[j].name = zip_get_name(za, i, 0);
115 zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
119 if (ZIP_WANT_TORRENTZIP(za)) {
120 qsort(filelist, (size_t)survivors, sizeof(filelist[0]), torrentzip_compare_names);
123 if (ZIP_WANT_TORRENTZIP(za) || (zip_source_supports(za->src) & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE_CLONING)) == 0) {
124 unchanged_offset = 0;
127 if (unchanged_offset == ZIP_UINT64_MAX) {
128 /* we're keeping all file data, find the end of the last one */
129 zip_uint64_t last_index = ZIP_UINT64_MAX;
130 unchanged_offset = 0;
132 for (i = 0; i < za->nentry; i++) {
133 if (za->entry[i].orig != NULL) {
134 if (za->entry[i].orig->offset >= unchanged_offset) {
135 unchanged_offset = za->entry[i].orig->offset;
140 if (last_index != ZIP_UINT64_MAX) {
141 if ((unchanged_offset = _zip_file_get_end(za, last_index, &za->error)) == 0) {
147 if (unchanged_offset > 0) {
148 if (zip_source_begin_write_cloning(za->src, unchanged_offset) < 0) {
149 /* cloning not supported, need to copy everything */
150 unchanged_offset = 0;
154 if (unchanged_offset == 0) {
155 if (zip_source_begin_write(za->src) < 0) {
156 zip_error_set_from_source(&za->error, za->src);
162 if (_zip_progress_start(za->progress) != 0) {
163 zip_error_set(&za->error, ZIP_ER_CANCELLED, 0);
164 zip_source_rollback_write(za->src);
169 for (j = 0; j < survivors; j++) {
174 if (_zip_progress_subrange(za->progress, (double)j / (double)survivors, (double)(j + 1) / (double)survivors) != 0) {
175 zip_error_set(&za->error, ZIP_ER_CANCELLED, 0);
181 entry = za->entry + i;
183 if (entry->orig != NULL && entry->orig->offset < unchanged_offset) {
184 /* already implicitly copied by cloning */
188 new_data = (ZIP_ENTRY_DATA_CHANGED(entry) || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_COMP_METHOD) || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_ENCRYPTION_METHOD)) || (ZIP_WANT_TORRENTZIP(za) && !ZIP_IS_TORRENTZIP(za));
190 /* create new local directory entry */
191 if (entry->changes == NULL) {
192 if ((entry->changes = _zip_dirent_clone(entry->orig)) == NULL) {
193 zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
200 if (_zip_read_local_ef(za, i) < 0) {
205 if (ZIP_WANT_TORRENTZIP(za)) {
206 zip_dirent_torrentzip_normalize(entry->changes);
209 if ((off = zip_source_tell_write(za->src)) < 0) {
210 zip_error_set_from_source(&za->error, za->src);
214 de->offset = (zip_uint64_t)off;
220 if (!ZIP_ENTRY_DATA_CHANGED(entry)) {
221 if ((zs = zip_source_zip_file_create(za, i, ZIP_FL_UNCHANGED, 0, -1, NULL, &za->error)) == NULL) {
227 /* add_data writes dirent */
228 if (add_data(za, zs ? zs : entry->source, de, entry->changes ? entry->changes->changed : 0) < 0) {
240 if (de->encryption_method != ZIP_EM_TRAD_PKWARE) {
241 /* when copying data, all sizes are known -> no data descriptor needed */
242 /* except for PKWare encryption, where removing the data descriptor breaks password validation */
243 de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR;
245 if (_zip_dirent_write(za, de, ZIP_FL_LOCAL) < 0) {
249 if ((offset = _zip_file_get_offset(za, i, &za->error)) == 0) {
253 if (zip_source_seek(za->src, (zip_int64_t)offset, SEEK_SET) < 0) {
254 zip_error_set_from_source(&za->error, za->src);
258 if (copy_data(za, de->comp_size) < 0) {
263 if (de->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) {
264 if (write_data_descriptor(za, de, _zip_dirent_needs_zip64(de, 0)) < 0) {
273 if (write_cdir(za, filelist, survivors) < 0)
280 if (zip_source_commit_write(za->src) != 0) {
281 zip_error_set_from_source(&za->error, za->src);
284 _zip_progress_end(za->progress);
288 zip_source_rollback_write(za->src);
299 add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) {
300 zip_int64_t offstart, offdata, offend, data_length;
302 zip_file_attributes_t attributes;
303 zip_source_t *src_final, *src_tmp;
307 bool needs_recompress, needs_decompress, needs_crc, needs_compress, needs_reencrypt, needs_decrypt, needs_encrypt;
309 if (zip_source_stat(src, &st) < 0) {
310 zip_error_set_from_source(&za->error, src);
314 if ((st.valid & ZIP_STAT_COMP_METHOD) == 0) {
315 st.valid |= ZIP_STAT_COMP_METHOD;
316 st.comp_method = ZIP_CM_STORE;
319 if (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != ZIP_CM_STORE)
320 de->comp_method = st.comp_method;
321 else if (de->comp_method == ZIP_CM_STORE && (st.valid & ZIP_STAT_SIZE)) {
322 st.valid |= ZIP_STAT_COMP_SIZE;
323 st.comp_size = st.size;
326 /* we'll recompress */
327 st.valid &= ~ZIP_STAT_COMP_SIZE;
330 if ((st.valid & ZIP_STAT_ENCRYPTION_METHOD) == 0) {
331 st.valid |= ZIP_STAT_ENCRYPTION_METHOD;
332 st.encryption_method = ZIP_EM_NONE;
335 flags = ZIP_EF_LOCAL;
337 if ((st.valid & ZIP_STAT_SIZE) == 0) {
338 /* TODO: not valid for torrentzip */
339 flags |= ZIP_FL_FORCE_ZIP64;
343 de->uncomp_size = st.size;
344 /* this is technically incorrect (copy_source counts compressed data), but it's the best we have */
345 data_length = (zip_int64_t)st.size;
347 if ((st.valid & ZIP_STAT_COMP_SIZE) == 0) {
348 zip_uint64_t max_compressed_size;
349 zip_uint16_t compression_method = ZIP_CM_ACTUAL(de->comp_method);
351 if (compression_method == ZIP_CM_STORE) {
352 max_compressed_size = st.size;
355 zip_compression_algorithm_t *algorithm = _zip_get_compression_algorithm(compression_method, true);
356 if (algorithm == NULL) {
357 max_compressed_size = ZIP_UINT64_MAX;
360 max_compressed_size = algorithm->maximum_compressed_size(st.size);
364 if (max_compressed_size > 0xffffffffu) {
365 /* TODO: not valid for torrentzip */
366 flags |= ZIP_FL_FORCE_ZIP64;
370 de->comp_size = st.comp_size;
371 data_length = (zip_int64_t)st.comp_size;
375 if ((offstart = zip_source_tell_write(za->src)) < 0) {
376 zip_error_set_from_source(&za->error, za->src);
380 /* as long as we don't support non-seekable output, clear data descriptor bit */
381 de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR;
382 if ((is_zip64 = _zip_dirent_write(za, de, flags)) < 0) {
386 needs_recompress = ZIP_WANT_TORRENTZIP(za) || st.comp_method != ZIP_CM_ACTUAL(de->comp_method);
387 needs_decompress = needs_recompress && (st.comp_method != ZIP_CM_STORE);
388 /* in these cases we can compute the CRC ourselves, so we do */
389 needs_crc = (st.comp_method == ZIP_CM_STORE) || needs_decompress;
390 needs_compress = needs_recompress && (de->comp_method != ZIP_CM_STORE);
392 needs_reencrypt = needs_recompress || (de->changed & ZIP_DIRENT_PASSWORD) || (de->encryption_method != st.encryption_method);
393 needs_decrypt = needs_reencrypt && (st.encryption_method != ZIP_EM_NONE);
394 needs_encrypt = needs_reencrypt && (de->encryption_method != ZIP_EM_NONE);
397 zip_source_keep(src_final);
399 if (!needs_decrypt && st.encryption_method == ZIP_EM_TRAD_PKWARE && (de->changed & ZIP_DIRENT_LAST_MOD)) {
400 /* PKWare encryption uses the last modification time for password verification, therefore we can't change it without re-encrypting. Ignoring the requested modification time change seems more sensible than failing to close the archive. */
401 de->changed &= ~ZIP_DIRENT_LAST_MOD;
405 zip_encryption_implementation impl;
407 if ((impl = _zip_get_encryption_implementation(st.encryption_method, ZIP_CODEC_DECODE)) == NULL) {
408 zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
409 zip_source_free(src_final);
412 if ((src_tmp = impl(za, src_final, st.encryption_method, ZIP_CODEC_DECODE, za->default_password)) == NULL) {
413 /* error set by impl */
414 zip_source_free(src_final);
421 if (needs_decompress) {
422 if ((src_tmp = zip_source_decompress(za, src_final, st.comp_method)) == NULL) {
423 zip_source_free(src_final);
431 if ((src_tmp = zip_source_crc_create(src_final, 0, &za->error)) == NULL) {
432 zip_source_free(src_final);
439 if (needs_compress) {
440 if ((src_tmp = zip_source_compress(za, src_final, de->comp_method, de->compression_level)) == NULL) {
441 zip_source_free(src_final);
450 zip_encryption_implementation impl;
451 const char *password = NULL;
454 password = de->password;
456 else if (za->default_password) {
457 password = za->default_password;
460 if ((impl = _zip_get_encryption_implementation(de->encryption_method, ZIP_CODEC_ENCODE)) == NULL) {
461 zip_error_set(&za->error, ZIP_ER_ENCRNOTSUPP, 0);
462 zip_source_free(src_final);
466 if (de->encryption_method == ZIP_EM_TRAD_PKWARE) {
467 de->bitflags |= ZIP_GPBF_DATA_DESCRIPTOR;
469 /* PKWare encryption uses last_mod, make sure it gets the right value. */
470 if (de->changed & ZIP_DIRENT_LAST_MOD) {
472 zip_stat_init(&st_mtime);
473 st_mtime.valid = ZIP_STAT_MTIME;
474 st_mtime.mtime = de->last_mod;
475 if ((src_tmp = _zip_source_window_new(src_final, 0, -1, &st_mtime, 0, NULL, NULL, 0, true, &za->error)) == NULL) {
476 zip_source_free(src_final);
483 if ((src_tmp = impl(za, src_final, de->encryption_method, ZIP_CODEC_ENCODE, password)) == NULL) {
484 /* error set by impl */
485 zip_source_free(src_final);
493 if ((offdata = zip_source_tell_write(za->src)) < 0) {
494 zip_error_set_from_source(&za->error, za->src);
498 ret = copy_source(za, src_final, data_length);
500 if (zip_source_stat(src_final, &st) < 0) {
501 zip_error_set_from_source(&za->error, src_final);
505 if (zip_source_get_file_attributes(src_final, &attributes) != 0) {
506 zip_error_set_from_source(&za->error, src_final);
510 zip_source_free(src_final);
516 if ((offend = zip_source_tell_write(za->src)) < 0) {
517 zip_error_set_from_source(&za->error, za->src);
521 if (zip_source_seek_write(za->src, offstart, SEEK_SET) < 0) {
522 zip_error_set_from_source(&za->error, za->src);
526 if ((st.valid & (ZIP_STAT_COMP_METHOD | ZIP_STAT_CRC | ZIP_STAT_SIZE)) != (ZIP_STAT_COMP_METHOD | ZIP_STAT_CRC | ZIP_STAT_SIZE)) {
527 zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
531 if ((de->changed & ZIP_DIRENT_LAST_MOD) == 0) {
532 if (st.valid & ZIP_STAT_MTIME)
533 de->last_mod = st.mtime;
537 de->comp_method = st.comp_method;
539 de->uncomp_size = st.size;
540 de->comp_size = (zip_uint64_t)(offend - offdata);
541 _zip_dirent_apply_attributes(de, &attributes, (flags & ZIP_FL_FORCE_ZIP64) != 0, changed);
543 if (ZIP_WANT_TORRENTZIP(za)) {
544 zip_dirent_torrentzip_normalize(de);
547 if ((ret = _zip_dirent_write(za, de, flags)) < 0)
550 if (is_zip64 != ret) {
551 /* Zip64 mismatch between preliminary file header written before data and final file header written afterwards */
552 zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
556 if (zip_source_seek_write(za->src, offend, SEEK_SET) < 0) {
557 zip_error_set_from_source(&za->error, za->src);
561 if (de->bitflags & ZIP_GPBF_DATA_DESCRIPTOR) {
562 if (write_data_descriptor(za, de, is_zip64) < 0) {
572 copy_data(zip_t *za, zip_uint64_t len) {
573 DEFINE_BYTE_ARRAY(buf, BUFSIZE);
574 double total = (double)len;
576 if (!byte_array_init(buf, BUFSIZE)) {
577 zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
582 zip_uint64_t n = ZIP_MIN(len, BUFSIZE);
584 if (_zip_read(za->src, buf, n, &za->error) < 0) {
585 byte_array_fini(buf);
589 if (_zip_write(za, buf, n) < 0) {
590 byte_array_fini(buf);
596 if (_zip_progress_update(za->progress, (total - (double)len) / total) != 0) {
597 zip_error_set(&za->error, ZIP_ER_CANCELLED, 0);
602 byte_array_fini(buf);
608 copy_source(zip_t *za, zip_source_t *src, zip_int64_t data_length) {
609 DEFINE_BYTE_ARRAY(buf, BUFSIZE);
610 zip_int64_t n, current;
613 if (zip_source_open(src) < 0) {
614 zip_error_set_from_source(&za->error, src);
618 if (!byte_array_init(buf, BUFSIZE)) {
619 zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
625 while ((n = zip_source_read(src, buf, BUFSIZE)) > 0) {
626 if (_zip_write(za, buf, (zip_uint64_t)n) < 0) {
630 if (n == BUFSIZE && za->progress && data_length > 0) {
632 if (_zip_progress_update(za->progress, (double)current / (double)data_length) != 0) {
633 zip_error_set(&za->error, ZIP_ER_CANCELLED, 0);
641 zip_error_set_from_source(&za->error, src);
645 byte_array_fini(buf);
647 zip_source_close(src);
653 write_cdir(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivors) {
654 if (zip_source_tell_write(za->src) < 0) {
658 if (_zip_cdir_write(za, filelist, survivors) < 0) {
662 if (zip_source_tell_write(za->src) < 0) {
671 _zip_changed(const zip_t *za, zip_uint64_t *survivorsp) {
673 zip_uint64_t i, survivors;
678 if (za->comment_changed || (ZIP_WANT_TORRENTZIP(za) && !ZIP_IS_TORRENTZIP(za))) {
682 for (i = 0; i < za->nentry; i++) {
683 if (ZIP_ENTRY_HAS_CHANGES(&za->entry[i])) {
686 if (!za->entry[i].deleted) {
692 *survivorsp = survivors;
699 write_data_descriptor(zip_t *za, const zip_dirent_t *de, int is_zip64) {
700 zip_buffer_t *buffer = _zip_buffer_new(NULL, MAX_DATA_DESCRIPTOR_LENGTH);
703 if (buffer == NULL) {
704 zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
708 _zip_buffer_put(buffer, DATADES_MAGIC, 4);
709 _zip_buffer_put_32(buffer, de->crc);
711 _zip_buffer_put_64(buffer, de->comp_size);
712 _zip_buffer_put_64(buffer, de->uncomp_size);
715 _zip_buffer_put_32(buffer, (zip_uint32_t)de->comp_size);
716 _zip_buffer_put_32(buffer, (zip_uint32_t)de->uncomp_size);
719 if (!_zip_buffer_ok(buffer)) {
720 zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
724 ret = _zip_write(za, _zip_buffer_data(buffer), _zip_buffer_offset(buffer));
727 _zip_buffer_free(buffer);
733 static int torrentzip_compare_names(const void *a, const void *b) {
734 const char *aname = ((const zip_filelist_t *)a)->name;
735 const char *bname = ((const zip_filelist_t *)b)->name;
738 return (bname != NULL) * -1;
740 else if (bname == NULL) {
744 return strcasecmp(aname, bname);