You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/12/08 01:53:54 UTC

[commons-compress] branch master updated: COMPRESS-621: Fix calculation the offset of the first zip central directory entry (#334)

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

ggregory 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 0bacf83e COMPRESS-621: Fix calculation the offset of the first zip central directory entry (#334)
0bacf83e is described below

commit 0bacf83eb0b3367cd5572ab27e42a444efbcd5f8
Author: Glavo <zj...@gmail.com>
AuthorDate: Thu Dec 8 09:53:48 2022 +0800

    COMPRESS-621: Fix calculation the offset of the first zip central directory entry (#334)
---
 .../commons/compress/archivers/zip/ZipFile.java    |  49 +++++++++++++++++----
 .../compress/archivers/zip/ZipFileTest.java        |  24 ++++++++++
 src/test/resources/COMPRESS-621.zip                | Bin 0 -> 217 bytes
 3 files changed, 64 insertions(+), 9 deletions(-)

diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
index df18dd41..3caf12f3 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
@@ -163,6 +163,7 @@ public class ZipFile implements Closeable {
 
     private long centralDirectoryStartDiskNumber, centralDirectoryStartRelativeOffset;
     private long centralDirectoryStartOffset;
+    private long firstLocalFileHeaderOffset = 0L;
 
     /**
      * Opens the given file for reading, assuming "UTF8" for file names.
@@ -720,6 +721,28 @@ public class ZipFile implements Closeable {
         return null;
     }
 
+    /**
+     * Offset of the first local file header in the file.
+     *
+     * @return the length of the content before the first local file header
+     * @since 1.23
+     */
+    public long getFirstLocalFileHeaderOffset() {
+        return firstLocalFileHeaderOffset;
+    }
+
+    /**
+     * Returns an InputStream for reading the content before the first local file header.
+     *
+     * @return null if there is no content before the first local file header.
+     * Otherwise returns a stream to read the content before the first local file header.
+     * @since 1.23
+     */
+    public InputStream getContentBeforeFirstLocalFileHeader() {
+        return firstLocalFileHeaderOffset == 0
+                ? null : createBoundedInputStream(0, firstLocalFileHeaderOffset);
+    }
+
     /**
      * Ensures that the close method of this zipfile is called when
      * there are no more references to it.
@@ -896,7 +919,7 @@ public class ZipFile implements Closeable {
         ze.setName(entryEncoding.decode(fileName), fileName);
 
         // LFH offset,
-        ze.setLocalHeaderOffset(ZipLong.getValue(cfhBuf, off));
+        ze.setLocalHeaderOffset(ZipLong.getValue(cfhBuf, off) + firstLocalFileHeaderOffset);
         // data offset will be filled later
         entries.add(ze);
 
@@ -1038,12 +1061,12 @@ public class ZipFile implements Closeable {
         /* maximum length of zipfile comment */ + ZIP64_MAGIC_SHORT;
 
     /**
-     * Offset of the field that holds the location of the first
-     * central directory entry inside the "End of central directory
+     * Offset of the field that holds the location of the length of
+     * the central directory inside the "End of central directory
      * record" relative to the start of the "End of central directory
      * record".
      */
-    private static final int CFD_LOCATOR_OFFSET =
+    private static final int CFD_LENGTH_OFFSET =
         /* end of central dir signature    */ WORD
         /* number of this disk             */ + SHORT
         /* number of the disk with the     */
@@ -1051,8 +1074,7 @@ public class ZipFile implements Closeable {
         /* total number of entries in      */
         /* the central dir on this disk    */ + SHORT
         /* total number of entries in      */
-        /* the central dir                 */ + SHORT
-        /* size of the central directory   */ + WORD;
+        /* the central dir                 */ + SHORT;
 
     /**
      * Offset of the field that holds the disk number of the first
@@ -1253,6 +1275,7 @@ public class ZipFile implements Closeable {
      */
     private void positionAtCentralDirectory32()
         throws IOException {
+        long endOfCentralDirectoryRecordOffset = archive.position();
         if (isSplitZipArchive) {
             skipBytes(CFD_DISK_OFFSET);
             shortBbuf.rewind();
@@ -1267,12 +1290,20 @@ public class ZipFile implements Closeable {
             ((ZipSplitReadOnlySeekableByteChannel) archive)
                 .position(centralDirectoryStartDiskNumber, centralDirectoryStartRelativeOffset);
         } else {
-            skipBytes(CFD_LOCATOR_OFFSET);
+            skipBytes(CFD_LENGTH_OFFSET);
+            wordBbuf.rewind();
+            IOUtils.readFully(archive, wordBbuf);
+            long centralDirectoryLength = ZipLong.getValue(wordBuf);
+
             wordBbuf.rewind();
             IOUtils.readFully(archive, wordBbuf);
             centralDirectoryStartDiskNumber = 0;
             centralDirectoryStartRelativeOffset = ZipLong.getValue(wordBuf);
-            archive.position(centralDirectoryStartRelativeOffset);
+
+            firstLocalFileHeaderOffset = Long.max(
+                    endOfCentralDirectoryRecordOffset - centralDirectoryLength - centralDirectoryStartRelativeOffset,
+                    0L);
+            archive.position(centralDirectoryStartRelativeOffset + firstLocalFileHeaderOffset);
         }
     }
 
@@ -1448,7 +1479,7 @@ public class ZipFile implements Closeable {
      * it may be an empty archive.
      */
     private boolean startsWithLocalFileHeader() throws IOException {
-        archive.position(0);
+        archive.position(firstLocalFileHeaderOffset);
         wordBbuf.rewind();
         IOUtils.readFully(archive, wordBbuf);
         return Arrays.equals(wordBuf, ZipArchiveOutputStream.LFH_SIG);
diff --git a/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java b/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
index 9f1056ba..eed9e9eb 100644
--- a/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/zip/ZipFileTest.java
@@ -827,6 +827,30 @@ public class ZipFileTest {
         }
     }
 
+    /**
+     * Test case for
+     * <a href="https://issues.apache.org/jira/browse/COMPRESS-621"
+     * >COMPRESS-621</a>.
+     */
+    @Test
+    public void testReadingOfExtraDataBeforeZip() throws IOException {
+        final byte[] fileHeader = "Before Zip file".getBytes(UTF_8);
+        final String entryName = "COMPRESS-621.txt";
+        final byte[] entryContent = "https://issues.apache.org/jira/browse/COMPRESS-621".getBytes(UTF_8);
+        try (ZipFile archive = new ZipFile(getFile("COMPRESS-621.zip"))) {
+            assertEquals(fileHeader.length, archive.getFirstLocalFileHeaderOffset());
+            try (InputStream input = archive.getContentBeforeFirstLocalFileHeader()) {
+                assertArrayEquals(fileHeader, IOUtils.toByteArray(input));
+            }
+
+            ZipArchiveEntry e = archive.getEntry(entryName);
+            assertEquals(entryContent.length, e.getSize());
+            try (InputStream input = archive.getInputStream(e)) {
+                assertArrayEquals(entryContent, IOUtils.toByteArray(input));
+            }
+        }
+    }
+
     private void multiByteReadConsistentlyReturnsMinusOneAtEof(final File file) throws Exception {
         final byte[] buf = new byte[2];
         try (ZipFile archive = new ZipFile(file)) {
diff --git a/src/test/resources/COMPRESS-621.zip b/src/test/resources/COMPRESS-621.zip
new file mode 100644
index 00000000..e4ce3e38
Binary files /dev/null and b/src/test/resources/COMPRESS-621.zip differ