This is an automated email from the ASF dual-hosted git repository. peterlee pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-compress.git
The following commit(s) were added to refs/heads/master by this push: new f4b5586 COMPRESS-565 : add a new option in Zip64Mode f4b5586 is described below commit f4b5586835e56373d6099c44a8be58a77efe5db4 Author: PeterAlfredLee <peteralfred...@gmail.com> AuthorDate: Mon Feb 22 15:17:49 2021 +0800 COMPRESS-565 : add a new option in Zip64Mode Add a new AlwaysWithCompatibility in Zip64Mode, this is a compromise for some libraries including 7z and Expand-Archive Powershell utility(and likely Excel) And we will encode LFH Offset in the Zip64 Extended Information Extra Field if the Disk Number Start needs to be encoded, even through the LFH Offset itself doesn't need to be encoded. --- .../commons/compress/archivers/zip/Zip64Mode.java | 12 ++++- .../archivers/zip/ZipArchiveOutputStream.java | 23 ++++++--- .../compress/archivers/zip/Zip64SupportIT.java | 56 ++++++++++++++++++++++ 3 files changed, 84 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/Zip64Mode.java b/src/main/java/org/apache/commons/compress/archivers/zip/Zip64Mode.java index d051e89..428b970 100644 --- a/src/main/java/org/apache/commons/compress/archivers/zip/Zip64Mode.java +++ b/src/main/java/org/apache/commons/compress/archivers/zip/Zip64Mode.java @@ -43,5 +43,15 @@ public enum Zip64Mode { * Use Zip64 extensions for all entries where they are required, * don't use them for entries that clearly don't require them. */ - AsNeeded + AsNeeded, + /** + * Always use Zip64 extensions for LFH and central directory as + * {@link Zip64Mode#Always} did, and at the meantime encode + * the relative offset of LFH and disk number start as needed in + * CFH as {@link Zip64Mode#AsNeeded} did. + * <p> + * This is a compromise for some libraries including 7z and + * Expand-Archive Powershell utility(and likely Excel). + */ + AlwaysWithCompatibility } diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java index 5808bf5..f73af51 100644 --- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java +++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java @@ -749,7 +749,8 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream { } private boolean isZip64Required(final ZipArchiveEntry entry1, final Zip64Mode requestedMode) { - return requestedMode == Zip64Mode.Always || isTooLargeForZip32(entry1); + return requestedMode == Zip64Mode.Always || requestedMode == Zip64Mode.AlwaysWithCompatibility + || isTooLargeForZip32(entry1); } private boolean isTooLargeForZip32(final ZipArchiveEntry zipArchiveEntry){ @@ -940,6 +941,7 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream { */ private boolean shouldAddZip64Extra(final ZipArchiveEntry entry, final Zip64Mode mode) { return mode == Zip64Mode.Always + || mode == Zip64Mode.AlwaysWithCompatibility || entry.getSize() >= ZIP64_MAGIC || entry.getCompressedSize() >= ZIP64_MAGIC || (entry.getSize() == ArchiveEntry.SIZE_UNKNOWN @@ -1339,7 +1341,8 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream { || ze.getSize() >= ZIP64_MAGIC || entryMetaData.offset >= ZIP64_MAGIC || ze.getDiskNumberStart() >= ZIP64_MAGIC_SHORT - || zip64Mode == Zip64Mode.Always; + || zip64Mode == Zip64Mode.Always + || zip64Mode == Zip64Mode.AlwaysWithCompatibility; if (needsZip64Extra && zip64Mode == Zip64Mode.Never) { // must be the offset that is too big, otherwise an @@ -1418,7 +1421,8 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream { putLong(ze.getCrc(), buf, CFH_CRC_OFFSET); if (ze.getCompressedSize() >= ZIP64_MAGIC || ze.getSize() >= ZIP64_MAGIC - || zip64Mode == Zip64Mode.Always) { + || zip64Mode == Zip64Mode.Always + || zip64Mode == Zip64Mode.AlwaysWithCompatibility) { ZipLong.ZIP64_MAGIC.putLong(buf, CFH_COMPRESSED_SIZE_OFFSET); ZipLong.ZIP64_MAGIC.putLong(buf, CFH_ORIGINAL_SIZE_OFFSET); } else { @@ -1480,7 +1484,8 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream { final Zip64ExtendedInformationExtraField z64 = getZip64Extra(ze); if (ze.getCompressedSize() >= ZIP64_MAGIC || ze.getSize() >= ZIP64_MAGIC - || zip64Mode == Zip64Mode.Always) { + || zip64Mode == Zip64Mode.Always + || zip64Mode == Zip64Mode.AlwaysWithCompatibility) { z64.setCompressedSize(new ZipEightByteInteger(ze.getCompressedSize())); z64.setSize(new ZipEightByteInteger(ze.getSize())); } else { @@ -1488,10 +1493,16 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream { z64.setCompressedSize(null); z64.setSize(null); } - if (lfhOffset >= ZIP64_MAGIC || zip64Mode == Zip64Mode.Always) { + + final boolean needsToEncodeLfhOffset = + lfhOffset >= ZIP64_MAGIC || zip64Mode == Zip64Mode.Always; + final boolean needsToEncodeDiskNumberStart = + ze.getDiskNumberStart() >= ZIP64_MAGIC_SHORT || zip64Mode == Zip64Mode.Always; + + if (needsToEncodeLfhOffset || needsToEncodeDiskNumberStart) { z64.setRelativeHeaderOffset(new ZipEightByteInteger(lfhOffset)); } - if (ze.getDiskNumberStart() >= ZIP64_MAGIC_SHORT || zip64Mode == Zip64Mode.Always) { + if (needsToEncodeDiskNumberStart) { z64.setDiskStartNumber(new ZipLong(ze.getDiskNumberStart())); } ze.setExtra(); diff --git a/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportIT.java b/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportIT.java index 9e8cb74..07c85c7 100644 --- a/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportIT.java +++ b/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportIT.java @@ -39,6 +39,7 @@ import java.util.Random; import java.util.zip.ZipEntry; import org.apache.commons.compress.AbstractTestCase; +import org.apache.commons.compress.utils.IOUtils; import org.junit.Test; public class Zip64SupportIT { @@ -2338,6 +2339,43 @@ public class Zip64SupportIT { true, 65536L); } + @Test + public void testZip64ModeAlwaysWithCompatibility() throws Throwable { + final File inputFile = getFile("test3.xml"); + + // with Zip64Mode.AlwaysWithCompatibility, the relative header offset and disk number + // start will not be set in extra fields + final File zipUsingModeAlwaysWithCompatibility = buildZipWithZip64Mode( + "testZip64ModeAlwaysWithCompatibility-output-1", + Zip64Mode.AlwaysWithCompatibility, inputFile); + final ZipFile zipFileWithAlwaysWithCompatibility = new ZipFile(zipUsingModeAlwaysWithCompatibility); + ZipArchiveEntry entry = zipFileWithAlwaysWithCompatibility.getEntries().nextElement(); + for (final ZipExtraField extraField : entry.getExtraFields()) { + if (!(extraField instanceof Zip64ExtendedInformationExtraField)) { + continue; + } + + assertNull(((Zip64ExtendedInformationExtraField) extraField).getRelativeHeaderOffset()); + assertNull(((Zip64ExtendedInformationExtraField) extraField).getDiskStartNumber()); + } + + // with Zip64Mode.Always, the relative header offset and disk number start will be + // set in extra fields + final File zipUsingModeAlways = buildZipWithZip64Mode( + "testZip64ModeAlwaysWithCompatibility-output-2", + Zip64Mode.Always, inputFile); + final ZipFile zipFileWithAlways = new ZipFile(zipUsingModeAlways); + entry = zipFileWithAlways.getEntries().nextElement(); + for (final ZipExtraField extraField : entry.getExtraFields()) { + if (!(extraField instanceof Zip64ExtendedInformationExtraField)) { + continue; + } + + assertNotNull(((Zip64ExtendedInformationExtraField) extraField).getRelativeHeaderOffset()); + assertNotNull(((Zip64ExtendedInformationExtraField) extraField).getDiskStartNumber()); + } + } + interface ZipOutputTest { void test(File f, ZipArchiveOutputStream zos) throws IOException; } @@ -2609,4 +2647,22 @@ public class Zip64SupportIT { zos.closeArchiveEntry(); zos.close(); } + + private File buildZipWithZip64Mode(final String fileName, final Zip64Mode zip64Mode, final File inputFile) throws Throwable { + final File outputFile = getTempFile(fileName); + outputFile.createNewFile(); + try(ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(new BufferedOutputStream(new FileOutputStream(outputFile)))) { + zipArchiveOutputStream.setUseZip64(zip64Mode); + zipArchiveOutputStream.setCreateUnicodeExtraFields(ZipArchiveOutputStream.UnicodeExtraFieldPolicy.ALWAYS); + + zipArchiveOutputStream.putArchiveEntry(new ZipArchiveEntry("input.bin")); + + final InputStream inputStream = new FileInputStream(inputFile); + IOUtils.copy(inputStream, zipArchiveOutputStream); + + zipArchiveOutputStream.closeArchiveEntry(); + } + + return outputFile; + } }