diff --git a/lib/zip_close.c b/lib/zip_close.c index 4313592cd..decc5d65a 100644 --- a/lib/zip_close.c +++ b/lib/zip_close.c @@ -295,8 +295,7 @@ zip_close(zip_t *za) { } -static int -add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { +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; @@ -305,19 +304,24 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { int is_zip64; zip_flags_t flags; bool needs_recompress, needs_decompress, needs_crc, needs_compress, needs_reencrypt, needs_decrypt, needs_encrypt; + bool have_dos_time, dirent_changed; + time_t mtime_before_copy; if (zip_source_stat(src, &st) < 0) { zip_error_set_from_source(&za->error, src); return -1; } + de->bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR; + 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) + 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; @@ -372,14 +376,30 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { } } - if ((offstart = zip_source_tell_write(za->src)) < 0) { - zip_error_set_from_source(&za->error, za->src); - return -1; + if ((de->changed & ZIP_DIRENT_LAST_MOD) == 0) { + int ret2 = zip_source_get_dos_time(src, &de->last_mod); + if (ret2 < 0) { + zip_error_set_from_source(&za->error, src); + return -1; + } + if (ret2 == 1) { + have_dos_time = true; + } + else { + if (st.valid & ZIP_STAT_MTIME) { + mtime_before_copy = st.mtime; + } + else { + time(&mtime_before_copy); + } + if (_zip_u2d_time(mtime_before_copy, &de->last_mod, &za->error) < 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) { + if ((offstart = zip_source_tell_write(za->src)) < 0) { + zip_error_set_from_source(&za->error, za->src); return -1; } @@ -485,6 +505,18 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { src_final = src_tmp; } + if (!ZIP_WANT_TORRENTZIP(za)) { + if (zip_source_get_file_attributes(src_final, &attributes) != 0) { + zip_error_set_from_source(&za->error, src_final); + ret = -1; + } + _zip_dirent_apply_attributes(de, &attributes, (flags & ZIP_FL_FORCE_ZIP64) != 0, changed); + } + + /* as long as we don't support non-seekable output, clear data descriptor bit */ + if ((is_zip64 = _zip_dirent_write(za, de, flags)) < 0) { + return -1; + } if ((offdata = zip_source_tell_write(za->src)) < 0) { zip_error_set_from_source(&za->error, za->src); @@ -498,9 +530,11 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { ret = -1; } - if (zip_source_get_file_attributes(src_final, &attributes) != 0) { - zip_error_set_from_source(&za->error, src_final); - ret = -1; + if (!ZIP_WANT_TORRENTZIP(za)) { + 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); @@ -514,57 +548,51 @@ add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de, zip_uint32_t changed) { 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) { - int ret2 = zip_source_get_dos_time(src, &de->last_mod); - if (ret2 < 0) { - zip_error_set_from_source(&za->error, src); - return -1; - } - if (ret2 == 0) { - time_t mtime; - if (st.valid & ZIP_STAT_MTIME) { - mtime = st.mtime; - } - else { - time(&mtime); - } - if (_zip_u2d_time(mtime, &de->last_mod, &za->error) < 0) { - return -1; - } - } - } + dirent_changed = ZIP_CM_ACTUAL(de->comp_method) != st.comp_method || de->crc != st.crc || de->uncomp_size != st.size || de->comp_size != (zip_uint64_t)(offend - offdata); 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 (ZIP_WANT_TORRENTZIP(za)) { - zip_dirent_torrentzip_normalize(de); + if (!ZIP_WANT_TORRENTZIP(za)) { + dirent_changed |= _zip_dirent_apply_attributes(de, &attributes, (flags & ZIP_FL_FORCE_ZIP64) != 0, changed); + + if ((de->changed & ZIP_DIRENT_LAST_MOD) == 0 && !have_dos_time) { + if (st.valid & ZIP_STAT_MTIME) { + if (st.mtime != mtime_before_copy) { + if (_zip_u2d_time(st.mtime, &de->last_mod, &za->error) < 0) { + return -1; + } + dirent_changed = true; + } + } + } } - if ((ret = _zip_dirent_write(za, de, flags)) < 0) - return -1; + if (dirent_changed) { + if (zip_source_seek_write(za->src, offstart, SEEK_SET) < 0) { + zip_error_set_from_source(&za->error, za->src); + 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 ((ret = _zip_dirent_write(za, de, flags)) < 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 (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) { diff --git a/lib/zip_dirent.c b/lib/zip_dirent.c index 24bc6abf9..418546acc 100644 --- a/lib/zip_dirent.c +++ b/lib/zip_dirent.c @@ -968,7 +968,7 @@ _zip_dirent_write(zip_t *za, zip_dirent_t *de, zip_flags_t flags) { _zip_buffer_put_16(buffer, ZIP_CM_WINZIP_AES); } else { - _zip_buffer_put_16(buffer, (zip_uint16_t)de->comp_method); + _zip_buffer_put_16(buffer, (zip_uint16_t)ZIP_CM_ACTUAL(de->comp_method)); } if (ZIP_WANT_TORRENTZIP(za)) { @@ -1190,52 +1190,75 @@ _zip_u2d_time(time_t intime, zip_dostime_t *dtime, zip_error_t *ze) { } -void -_zip_dirent_apply_attributes(zip_dirent_t *de, zip_file_attributes_t *attributes, bool force_zip64, zip_uint32_t changed) { +bool _zip_dirent_apply_attributes(zip_dirent_t *de, zip_file_attributes_t *attributes, bool force_zip64, zip_uint32_t changed) { zip_uint16_t length; + bool has_changed = false; if (attributes->valid & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS) { zip_uint16_t mask = attributes->general_purpose_bit_mask & ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK; - de->bitflags = (de->bitflags & ~mask) | (attributes->general_purpose_bit_flags & mask); + zip_uint16_t bitflags = (de->bitflags & ~mask) | (attributes->general_purpose_bit_flags & mask); + if (de->bitflags != bitflags) { + de->bitflags = bitflags; + has_changed = true; + } } if (attributes->valid & ZIP_FILE_ATTRIBUTES_ASCII) { - de->int_attrib = (de->int_attrib & ~0x1) | (attributes->ascii ? 1 : 0); + zip_uint16_t int_attrib = (de->int_attrib & ~0x1) | (attributes->ascii ? 1 : 0); + if (de->int_attrib != int_attrib) { + de->int_attrib = int_attrib; + has_changed = true; + } } /* manually set attributes are preferred over attributes provided by source */ if ((changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES)) { - de->ext_attrib = attributes->external_file_attributes; + if (de->ext_attrib != attributes->external_file_attributes) { + de->ext_attrib = attributes->external_file_attributes; + has_changed = true; + } } + zip_uint16_t version_needed; if (de->comp_method == ZIP_CM_LZMA) { - de->version_needed = 63; + version_needed = 63; } else if (de->encryption_method == ZIP_EM_AES_128 || de->encryption_method == ZIP_EM_AES_192 || de->encryption_method == ZIP_EM_AES_256) { - de->version_needed = 51; + version_needed = 51; } else if (de->comp_method == ZIP_CM_BZIP2) { - de->version_needed = 46; + version_needed = 46; } else if (force_zip64 || _zip_dirent_needs_zip64(de, 0)) { - de->version_needed = 45; + version_needed = 45; } else if (de->comp_method == ZIP_CM_DEFLATE || de->encryption_method == ZIP_EM_TRAD_PKWARE) { - de->version_needed = 20; + version_needed = 20; } else if ((length = _zip_string_length(de->filename)) > 0 && de->filename->raw[length - 1] == '/') { - de->version_needed = 20; + version_needed = 20; } else { - de->version_needed = 10; + version_needed = 10; } if (attributes->valid & ZIP_FILE_ATTRIBUTES_VERSION_NEEDED) { - de->version_needed = ZIP_MAX(de->version_needed, attributes->version_needed); + version_needed = ZIP_MAX(version_needed, attributes->version_needed); } - de->version_madeby = 63 | (de->version_madeby & 0xff00); + if (de->version_needed != version_needed) { + de->version_needed = version_needed; + has_changed = true; + } + + zip_int16_t version_madeby = 63 | (de->version_madeby & 0xff00); if ((changed & ZIP_DIRENT_ATTRIBUTES) == 0 && (attributes->valid & ZIP_FILE_ATTRIBUTES_HOST_SYSTEM)) { - de->version_madeby = (de->version_madeby & 0xff) | (zip_uint16_t)(attributes->host_system << 8); + version_madeby = (version_madeby & 0xff) | (zip_uint16_t)(attributes->host_system << 8); + } + if (de->version_madeby != version_madeby) { + de->version_madeby = version_madeby; + has_changed = true; } + + return has_changed; } @@ -1265,7 +1288,8 @@ zip_dirent_check_consistency(zip_dirent_t *dirent) { return 0; } -time_t zip_dirent_get_last_mod_mtime(zip_dirent_t *de) { +time_t +zip_dirent_get_last_mod_mtime(zip_dirent_t *de) { if (!de->last_mod_mtime_valid) { de->last_mod_mtime = _zip_d2u_time(&de->last_mod); de->last_mod_mtime_valid = true; diff --git a/lib/zip_source_get_file_attributes.c b/lib/zip_source_get_file_attributes.c index 209e39f50..59c064fe0 100644 --- a/lib/zip_source_get_file_attributes.c +++ b/lib/zip_source_get_file_attributes.c @@ -39,8 +39,7 @@ zip_file_attributes_init(zip_file_attributes_t *attributes) { attributes->version = 1; } -int -zip_source_get_file_attributes(zip_source_t *src, zip_file_attributes_t *attributes) { +int zip_source_get_file_attributes(zip_source_t *src, zip_file_attributes_t *attributes) { if (src->source_closed) { return -1; } diff --git a/lib/zip_source_pkware_encode.c b/lib/zip_source_pkware_encode.c index fed76115b..26e1ce794 100644 --- a/lib/zip_source_pkware_encode.c +++ b/lib/zip_source_pkware_encode.c @@ -198,8 +198,10 @@ pkware_encrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t length, zip zip_error_set(&ctx->error, ZIP_ER_INVAL, 0); return -1; } - attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED; + attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED | ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS; attributes->version_needed = 20; + attributes->general_purpose_bit_flags = ZIP_GPBF_DATA_DESCRIPTOR; + attributes->general_purpose_bit_mask = ZIP_GPBF_DATA_DESCRIPTOR; return 0; } diff --git a/lib/zipint.h b/lib/zipint.h index e22d74c21..701f4e1de 100644 --- a/lib/zipint.h +++ b/lib/zipint.h @@ -102,7 +102,8 @@ /* according to unzip-6.0's zipinfo.c, this corresponds to a directory with rwx permissions for everyone */ #define ZIP_EXT_ATTRIB_DEFAULT_DIR (0040777u << 16) -#define ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK 0x0836 +/* Allowed: Encryption specific bits, data descriptor, compression specific, UTF-8 filename */ +#define ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK 0x083e #define ZIP_MAX(a, b) ((a) > (b) ? (a) : (b)) #define ZIP_MIN(a, b) ((a) < (b) ? (a) : (b)) @@ -552,7 +553,7 @@ zip_int64_t _zip_cdir_write(zip_t *za, const zip_filelist_t *filelist, zip_uint6 time_t _zip_d2u_time(const zip_dostime_t*); void _zip_deregister_source(zip_t *za, zip_source_t *src); -void _zip_dirent_apply_attributes(zip_dirent_t *, zip_file_attributes_t *, bool, zip_uint32_t); +bool _zip_dirent_apply_attributes(zip_dirent_t *, zip_file_attributes_t *, bool, zip_uint32_t); int zip_dirent_check_consistency(zip_dirent_t *dirent); zip_dirent_t *_zip_dirent_clone(const zip_dirent_t *); void _zip_dirent_free(zip_dirent_t *);