Skip to content

Commit

Permalink
Reapply dirent writing optimization with PKWare encryption fixed.
Browse files Browse the repository at this point in the history
  • Loading branch information
dillof committed Nov 26, 2024
1 parent 25731d1 commit c4ec8eb
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 71 deletions.
126 changes: 77 additions & 49 deletions lib/zip_close.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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) {
Expand Down
58 changes: 41 additions & 17 deletions lib/zip_dirent.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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;
}


Expand Down Expand Up @@ -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;
Expand Down
3 changes: 1 addition & 2 deletions lib/zip_source_get_file_attributes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 3 additions & 1 deletion lib/zip_source_pkware_encode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
5 changes: 3 additions & 2 deletions lib/zipint.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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 *);
Expand Down

0 comments on commit c4ec8eb

Please sign in to comment.