You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Impossible to create .zip files with version_needed < 45 #258
It seems to be impossible to create a .zip file that contains a version_needed field smaller than 45 (meaning version 4.5). This in turn means that minizip 2.x cannot be used to create valid Office Open XML (e.g. .xlsx) or OpenDocument (e.g. .ods) files, because — due to the minimum required unzip version of 4.5 indicated by the headers —, MS Office and LibreOffice refuse to open such files and/or need to "fix" them before opening. These apps require version_needed to be 30 or lower (at the same time they don't mind the ZIP64 extensions, if the sizes/offsets stay below 32-bit.)
The cause of the issue is that the version_needed value calculated for the central entry is forced to be no less than the pre-calculated value (which is always 45 or higher). There are cases when a .zip file created with version 4.5 features (= ZIP64 extensions) can be safely unzipped using a 2.0 compliant unzip, e.g. when the actual file sizes/offsets didn't break the 32-bit limits.
This patch fixes it:
diff --git a/mz_zip.c b/mz_zip.c
index 7c19400..5cd9f06 100644
--- a/mz_zip.c+++ b/mz_zip.c@@ -614,8 +614,6 @@ static int16_t mz_zip_entry_get_version_needed(int16_t zip64, mz_zip_file *file_
if (file_info == NULL)
return MZ_PARAM_ERROR;
- if (file_info->version_needed > 0)- version_needed = file_info->version_needed;
if (zip64)
version_needed = 45;
#ifdef HAVE_AES
Thanks! I'm afraid the patch makes it fall into a different problem by omitting the version_needed detection on file close, making it retain its initial (and incorrect) value (45) which got determined on file add without knowing all the details (such as uncompressed file size.)
[ The interesting thing with all this is that the correct/expected behaviour will result in a different version_needed value in the local entry header vs. the central entry one (the latter being the accurate one and the one actually read by unzip tools.) This is how the traditional minizip library works. ]
The traditional minizip library does a seek backwards into the local header and fills in the correct values. I don't want to do a seek backwards. So if the file_info->uncompressed_size is 0 and it is the local header, then assumes zip64 (45). You are right in that it could result in different values for the version_needed and I am okay with that if there is no better solution. It is possible to set the file_info->uncompressed_size prior to calling mz_zip_entry_open and it should fill it in in both places properly right?
I'm not setting file_info->version_needed (couldn't find a way to do this with the traditional API.) [ I'm setting version_madeby (to various test values ranging from 20 to 63) when calling zipOpenNewFileInZip4_64() in my tests, OP and minizip1 works fine with those values. ]
As for setting the uncompressed_size in file open: That could work, but would break with input streams and it'd mean an incompatibility with the original minizip1 API, which didn't require it.
minizip1 indeed rewrites the local file entry but as per the source code and local tests, but it doesn't do so for the version_needed value (only for CRC32 and size fields.)
(As per new tests, the "adventure" of making .zips compatible with Excel (2016 even) isn't over with the version_needed fix. I'm still investigating.)
This extended patch avoids the other pitfall with Excel (2016), by omitting the 4 extrafield ZIP64 bytes in the headers. It does so by honoring the zip64 parameter of the zipOpenNewFileInZip*() compatibility functions (in current code this flag is ignored and ZIP64 extrafield header gets unconditionally enabled.)
Patch retains binary compatibility of mz_zip*() API.
I'm finished (for the context of this patch at least). Above two fixes cover the issues I/we experienced with Office 2016, 2007 (probably) and LibreOffice.
Thank you, I've seen a56a08c. I haven't tested it yet, but it LGTM.
The initial issue remains however, where it's still not possible to create .zip with version_needed < 45. This causes corruption errors/warnings with both LibreOffice and MS Offices (or any other app that's not prepared to handle .zip versions above 2.0.) For that to be fixed, this hunk of the above patch would need to be used:
UPDATE: I managed to test and it turns out, that the zip64 fix resolved the second issue with the above extra patch. Great stuff! I'm going to integrate everything and retest once more.
I don't understand how that is preventing it from being created with version_needed < 45. If I use MZ_ZIP64_DISABLE then it works. I do the if statement to allow for a custom value to be used.
You're right, I haven't considered the effect of the second (zip64) fix to the initial issue. After fixing the zip64 one, the version_needed value initially calculated will be 20, so there is not need to revise that when writing the central entry. So it solves the non-zip64 case perfectly.
[ For the rest of similar cases (should they exist), one should zero the field to have it recalculated again, which is fine when using the native mz_*() API. ]
All in all, I'm perfectly happy with this. Thank you very much for following through this and giving these patches their proper final form!
Activity
Allow version_needed to be less than customized. #258
nmoinvaz commentedon May 2, 2018
Take a look at the commit I made. It should fix the issue.
vszakats commentedon May 2, 2018
Thanks! I'm afraid the patch makes it fall into a different problem by omitting the
version_neededdetection on file close, making it retain its initial (and incorrect) value (45) which got determined on file add without knowing all the details (such as uncompressed file size.)[ The interesting thing with all this is that the correct/expected behaviour will result in a different
version_neededvalue in the local entry header vs. the central entry one (the latter being the accurate one and the one actually read by unzip tools.) This is how the traditional minizip library works. ]nmoinvaz commentedon May 2, 2018
Are you manually setting
file_info->versIon_neededto 20 when opening the zip entry for writing?nmoinvaz commentedon May 2, 2018
The traditional minizip library does a seek backwards into the local header and fills in the correct values. I don't want to do a seek backwards. So if the
file_info->uncompressed_sizeis 0 and it is the local header, then assumes zip64 (45). You are right in that it could result in different values for theversion_neededand I am okay with that if there is no better solution. It is possible to set thefile_info->uncompressed_sizeprior to callingmz_zip_entry_openand it should fill it in in both places properly right?vszakats commentedon May 2, 2018
I'm not setting
file_info->version_needed(couldn't find a way to do this with the traditional API.) [ I'm settingversion_madeby(to various test values ranging from 20 to 63) when callingzipOpenNewFileInZip4_64()in my tests, OP and minizip1 works fine with those values. ]As for setting the
uncompressed_sizein file open: That could work, but would break with input streams and it'd mean an incompatibility with the original minizip1 API, which didn't require it.minizip1 indeed rewrites the local file entry but as per the source code and local tests, but it doesn't do so for the
version_neededvalue (only for CRC32 and size fields.)(As per new tests, the "adventure" of making
.zips compatible with Excel (2016 even) isn't over with theversion_neededfix. I'm still investigating.)vszakats commentedon May 2, 2018
This extended patch avoids the other pitfall with Excel (2016), by omitting the 4 extrafield ZIP64 bytes in the headers. It does so by honoring the
zip64parameter of thezipOpenNewFileInZip*()compatibility functions (in current code this flag is ignored and ZIP64 extrafield header gets unconditionally enabled.)Patch retains binary compatibility of
mz_zip*()API.nmoinvaz commentedon May 3, 2018
Are you still investigating or does this fix all the compatibility issues with Excel?
vszakats commentedon May 3, 2018
I'm finished (for the context of this patch at least). Above two fixes cover the issues I/we experienced with Office 2016, 2007 (probably) and LibreOffice.
Allow configuration of zip64 support. #258
vszakats commentedon May 3, 2018
Thank you, I've seen a56a08c. I haven't tested it yet, but it LGTM.
The initial issue remains however, where it's still not possible to create .zip with:version_needed< 45. This causes corruption errors/warnings with both LibreOffice and MS Offices (or any other app that's not prepared to handle .zip versions above 2.0.) For that to be fixed, this hunk of the above patch would need to be usedUPDATE: I managed to test and it turns out, that the zip64 fix resolved the second issue with the above extra patch. Great stuff! I'm going to integrate everything and retest once more.
nmoinvaz commentedon May 3, 2018
I don't understand how that is preventing it from being created with
version_needed< 45. If I use MZ_ZIP64_DISABLE then it works. I do the if statement to allow for a custom value to be used.vszakats commentedon May 3, 2018
You're right, I haven't considered the effect of the second (zip64) fix to the initial issue. After fixing the zip64 one, the
version_neededvalue initially calculated will be 20, so there is not need to revise that when writing the central entry. So it solves the non-zip64 case perfectly.[ For the rest of similar cases (should they exist), one should zero the field to have it recalculated again, which is fine when using the native
mz_*()API. ]All in all, I'm perfectly happy with this. Thank you very much for following through this and giving these patches their proper final form!
Simplification & fixed formatting. #258
Add back auto detection of zip64. #258