You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by bo...@apache.org on 2020/01/21 17:30:09 UTC

[commons-compress] 01/04: COMPRESS-477 : add Zip64 support for split zip

This is an automated email from the ASF dual-hosted git repository.

bodewig pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-compress.git

commit b4fd49a959cc58db476e1c79f25df1728c7f22b8
Author: Lee <pe...@gmail.com>
AuthorDate: Fri Dec 27 20:07:47 2019 +0800

    COMPRESS-477 : add Zip64 support for split zip
---
 .../archivers/zip/Zip64RequiredException.java      | 12 +++
 .../archivers/zip/ZipArchiveOutputStream.java      | 95 +++++++++++++++++++---
 2 files changed, 94 insertions(+), 13 deletions(-)

diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/Zip64RequiredException.java b/src/main/java/org/apache/commons/compress/archivers/zip/Zip64RequiredException.java
index 625ea64..eb11d06 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/Zip64RequiredException.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/Zip64RequiredException.java
@@ -37,6 +37,18 @@ public class Zip64RequiredException extends ZipException {
         return ze.getName() + "'s size exceeds the limit of 4GByte.";
     }
 
+    static final String NUMBER_OF_THIS_DISK_TOO_BIG_MESSAGE =
+        "Number of the disk of End Of Central Directory exceeds the limmit of 65535.";
+
+    static final String NUMBER_OF_THE_DISK_OF_CENTRAL_DIRECTORY_TOO_BIG_MESSAGE =
+        "Number of the disk with the start of Central Directory exceeds the limmit of 65535.";
+
+    static final String TOO_MANY_ENTRIES_ON_THIS_DISK_MESSAGE =
+        "Number of entries on this disk exceeds the limmit of 65535.";
+
+    static final String SIZE_OF_CENTRAL_DIRECTORY_TOO_BIG_MESSAGE =
+        "The size of the entire central directory exceeds the limit of 4GByte.";
+
     static final String ARCHIVE_TOO_BIG_MESSAGE =
         "Archive's size exceeds the limit of 4GByte.";
 
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 345a5ad..759aa75 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
@@ -1297,6 +1297,7 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
                 || ze.getCompressedSize() >= ZIP64_MAGIC
                 || ze.getSize() >= ZIP64_MAGIC
                 || entryMetaData.offset >= ZIP64_MAGIC
+                || ze.getDiskNumberStart() >= ZIP64_MAGIC_SHORT
                 || zip64Mode == Zip64Mode.Always;
 
         if (needsZip64Extra && zip64Mode == Zip64Mode.Never) {
@@ -1392,7 +1393,11 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
 
         // disk number start
         if(isSplitZip) {
-            putShort((int) ze.getDiskNumberStart(), buf, CFH_DISK_NUMBER_OFFSET);
+            if (ze.getDiskNumberStart() >= ZIP64_MAGIC_SHORT || zip64Mode == Zip64Mode.Always) {
+                putShort(ZIP64_MAGIC_SHORT, buf, CFH_DISK_NUMBER_OFFSET);
+            } else {
+                putShort((int) ze.getDiskNumberStart(), buf, CFH_DISK_NUMBER_OFFSET);
+            }
         } else {
             System.arraycopy(ZERO, 0, buf, CFH_DISK_NUMBER_OFFSET, SHORT);
         }
@@ -1444,6 +1449,9 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
             if (lfhOffset >= ZIP64_MAGIC || zip64Mode == Zip64Mode.Always) {
                 z64.setRelativeHeaderOffset(new ZipEightByteInteger(lfhOffset));
             }
+            if (ze.getDiskNumberStart() >= ZIP64_MAGIC_SHORT || zip64Mode == Zip64Mode.Always) {
+                z64.setDiskStartNumber(new ZipLong(ze.getDiskNumberStart()));
+            }
             ze.setExtra();
         }
     }
@@ -1460,6 +1468,8 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
             ((ZipSplitOutputStream)this.out).prepareToWriteUnsplittableContent(eocdLength);
         }
 
+        validateIfZip64IsNeededInEOCD();
+
         writeCounted(EOCD_SIG);
 
         // number of this disk
@@ -1474,15 +1484,6 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
 
         // number of entries
         final int numberOfEntries = entries.size();
-        if (numberOfEntries > ZIP64_MAGIC_SHORT
-            && zip64Mode == Zip64Mode.Never) {
-            throw new Zip64RequiredException(Zip64RequiredException
-                                             .TOO_MANY_ENTRIES_MESSAGE);
-        }
-        if (cdOffset > ZIP64_MAGIC && zip64Mode == Zip64Mode.Never) {
-            throw new Zip64RequiredException(Zip64RequiredException
-                                             .ARCHIVE_TOO_BIG_MESSAGE);
-        }
 
         // total number of entries in the central directory on this disk
         int numOfEntriesOnThisDisk = numberOfCDInDiskData.get(numberOfThisDisk) == null ? 0 : numberOfCDInDiskData.get(numberOfThisDisk);
@@ -1507,6 +1508,54 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
     }
 
     /**
+     * If the Zip64 mode is set to never, then all the data in End Of Central Directory
+     * should not exceed their limits.
+     * @throws Zip64RequiredException if Zip64 is actually needed
+     */
+    private void validateIfZip64IsNeededInEOCD() throws Zip64RequiredException {
+        // exception will only be thrown if the Zip64 mode is never while Zip64 is actually needed
+        if (zip64Mode != Zip64Mode.Never) {
+            return;
+        }
+
+        long numberOfThisDisk = 0;
+        if (isSplitZip) {
+            numberOfThisDisk = ((ZipSplitOutputStream)this.out).getCurrentSplitSegmentIndex();
+        }
+        if (numberOfThisDisk >= ZIP64_MAGIC_SHORT) {
+            throw new Zip64RequiredException(Zip64RequiredException
+                    .NUMBER_OF_THIS_DISK_TOO_BIG_MESSAGE);
+        }
+
+        if (cdDiskNumberStart >= ZIP64_MAGIC_SHORT) {
+            throw new Zip64RequiredException(Zip64RequiredException
+                    .NUMBER_OF_THE_DISK_OF_CENTRAL_DIRECTORY_TOO_BIG_MESSAGE);
+        }
+
+        final int numOfEntriesOnThisDisk = numberOfCDInDiskData.get(numberOfThisDisk) == null ? 0 : numberOfCDInDiskData.get(numberOfThisDisk);
+        if (numOfEntriesOnThisDisk >= ZIP64_MAGIC_SHORT) {
+            throw new Zip64RequiredException(Zip64RequiredException
+                    .TOO_MANY_ENTRIES_ON_THIS_DISK_MESSAGE);
+        }
+
+        // number of entries
+        if (entries.size() >= ZIP64_MAGIC_SHORT) {
+            throw new Zip64RequiredException(Zip64RequiredException
+                    .TOO_MANY_ENTRIES_MESSAGE);
+        }
+
+        if (cdLength >= ZIP64_MAGIC) {
+            throw new Zip64RequiredException(Zip64RequiredException
+                    .SIZE_OF_CENTRAL_DIRECTORY_TOO_BIG_MESSAGE);
+        }
+
+        if (cdOffset >= ZIP64_MAGIC) {
+            throw new Zip64RequiredException(Zip64RequiredException
+                    .ARCHIVE_TOO_BIG_MESSAGE);
+        }
+    }
+
+    /**
      * Writes the &quot;ZIP64 End of central dir record&quot; and
      * &quot;ZIP64 End of central dir locator&quot;.
      * @throws IOException on error
@@ -1517,9 +1566,7 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
             return;
         }
 
-        if (!hasUsedZip64
-            && (cdOffset >= ZIP64_MAGIC || cdLength >= ZIP64_MAGIC
-                || entries.size() >= ZIP64_MAGIC_SHORT)) {
+        if (!hasUsedZip64 && shouldUseZip64EOCD()) {
             // actually "will use"
             hasUsedZip64 = true;
         }
@@ -1614,6 +1661,28 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
     }
 
     /**
+     * 4.4.1.4  If one of the fields in the end of central directory
+     * record is too small to hold required data, the field SHOULD be
+     * set to -1 (0xFFFF or 0xFFFFFFFF) and the ZIP64 format record
+     * SHOULD be created.
+     * @return true if zip64 End Of Central Directory is needed
+     */
+    private boolean shouldUseZip64EOCD() {
+        int numberOfThisDisk = 0;
+        if(isSplitZip) {
+            numberOfThisDisk = ((ZipSplitOutputStream)this.out).getCurrentSplitSegmentIndex();
+        }
+        int numOfEntriesOnThisDisk = numberOfCDInDiskData.get(numberOfThisDisk) == null ? 0 : numberOfCDInDiskData.get(numberOfThisDisk);
+        return numberOfThisDisk >= ZIP64_MAGIC_SHORT            /* number of this disk */
+                || cdDiskNumberStart >= ZIP64_MAGIC_SHORT       /* number of the disk with the start of the central directory */
+                || numOfEntriesOnThisDisk >= ZIP64_MAGIC_SHORT  /* total number of entries in the central directory on this disk */
+                || entries.size() >= ZIP64_MAGIC_SHORT          /* total number of entries in the central directory */
+                || cdLength >= ZIP64_MAGIC                      /* size of the central directory */
+                || cdOffset >= ZIP64_MAGIC;                     /* offset of start of central directory with respect to
+                                                                the starting disk number */
+    }
+
+    /**
      * Write bytes to output or random access file.
      * @param data the byte array to write
      * @throws IOException on error