commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From bode...@apache.org
Subject svn commit: r1155274 - in /commons/proper/compress/trunk/src: main/java/org/apache/commons/compress/archivers/zip/ test/java/org/apache/commons/compress/archivers/zip/
Date Tue, 09 Aug 2011 09:08:00 GMT
Author: bodewig
Date: Tue Aug  9 09:08:00 2011
New Revision: 1155274

URL: http://svn.apache.org/viewvc?rev=1155274&view=rev
Log:
Implement Zip64Mode.Never.  COMPRESS-36 and COMPRESS-150

Modified:
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/Zip64RequiredException.java
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
    commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportTest.java

Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/Zip64RequiredException.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/Zip64RequiredException.java?rev=1155274&r1=1155273&r2=1155274&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/Zip64RequiredException.java
(original)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/Zip64RequiredException.java
Tue Aug  9 09:08:00 2011
@@ -30,6 +30,19 @@ public class Zip64RequiredException exte
 
     private static final long serialVersionUID = 20110809L;
 
+    /**
+     * Helper to format "entry too big" messages.
+     */
+    static String getEntryTooBigMessage(ZipArchiveEntry ze) {
+        return ze.getName() + "'s size exceeds the limit of 4GByte.";
+    }
+
+    static final String ARCHIVE_TOO_BIG_MESSAGE =
+        "archive's size exceeds the limit of 4GByte.";
+
+    static final String TOO_MANY_ENTRIES_MESSAGE =
+        "archive contains more than 65535 entries.";
+
     public Zip64RequiredException(String reason) {
         super(reason);
     }

Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java?rev=1155274&r1=1155273&r2=1155274&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
(original)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
Tue Aug  9 09:08:00 2011
@@ -390,7 +390,11 @@ public class ZipArchiveOutputStream exte
         zip64Mode = mode;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     * @throws Zip64RequiredException if the archive's size exceeds 4
+     * GByte or there are more than 65535 entries inside the archive
+     */
     @Override
     public void finish() throws IOException {
         if (finished) {
@@ -417,6 +421,8 @@ public class ZipArchiveOutputStream exte
     /**
      * Writes all necessary data for this entry.
      * @throws IOException on error
+     * @throws Zip64RequiredException if the entry's uncompressed or
+     * compressed size exceeds 4 GByte
      */
     @Override
     public void closeArchiveEntry() throws IOException {
@@ -475,12 +481,17 @@ public class ZipArchiveOutputStream exte
             entry.entry.setCrc(realCrc);
         }
 
+        boolean actuallyNeedsZip64 = entry.entry.getSize() >= ZIP64_MAGIC
+            || entry.entry.getCompressedSize() >= ZIP64_MAGIC;
+        if (actuallyNeedsZip64 && zip64Mode == Zip64Mode.Never) {
+            throw new Zip64RequiredException(Zip64RequiredException
+                                             .getEntryTooBigMessage(entry.entry));
+        }
+
         // If random access output, write the local file header containing
         // the correct CRC and compressed/uncompressed sizes
         if (raf != null) {
             long save = raf.getFilePointer();
-            boolean actuallyNeedsZip64 = entry.entry.getSize() >= ZIP64_MAGIC
-                || entry.entry.getCompressedSize() >= ZIP64_MAGIC;
 
             raf.seek(entry.localDataStart);
             writeOut(ZipLong.getBytes(entry.entry.getCrc()));
@@ -530,6 +541,8 @@ public class ZipArchiveOutputStream exte
     /**
      * {@inheritDoc} 
      * @throws ClassCastException if entry is not an instance of ZipArchiveEntry
+     * @throws Zip64RequiredException if the entry's uncompressed or
+     * compressed size is known to exceed 4 GByte
      */
     @Override
     public void putArchiveEntry(ArchiveEntry archiveEntry) throws IOException {
@@ -566,6 +579,13 @@ public class ZipArchiveOutputStream exte
             entry.entry.setCompressedSize(entry.entry.getSize());
         }
 
+        if ((entry.entry.getSize() >= ZIP64_MAGIC
+             || entry.entry.getCompressedSize() >= ZIP64_MAGIC)
+            && zip64Mode == Zip64Mode.Never) {
+            throw new Zip64RequiredException(Zip64RequiredException
+                                             .getEntryTooBigMessage(entry.entry));
+        }
+
         // add a ZIP64 extended information extra field if we already
         // know it is going to be needed or the size is unknown and we
         // can ensure it won't hurt other implementations if we add it
@@ -693,6 +713,8 @@ public class ZipArchiveOutputStream exte
      * associated with the stream.
      *
      * @exception  IOException  if an I/O error occurs.
+     * @throws Zip64RequiredException if the archive's size exceeds 4
+     * GByte or there are more than 65535 entries inside the archive
      */
     @Override
     public void close() throws IOException {
@@ -893,6 +915,8 @@ public class ZipArchiveOutputStream exte
      * Writes the central file header entry.
      * @param ze the entry to write
      * @throws IOException on error
+     * @throws Zip64RequiredException if the archive's size exceeds 4
+     * GByte
      */
     protected void writeCentralFileHeader(ZipArchiveEntry ze) throws IOException {
         writeOut(CFH_SIG);
@@ -903,6 +927,14 @@ public class ZipArchiveOutputStream exte
             || ze.getSize() >= ZIP64_MAGIC
             || lfhOffset >= ZIP64_MAGIC;
 
+        if (needsZip64Extra && zip64Mode == Zip64Mode.Never) {
+            // must be the offset that is too big, otherwise an
+            // exception would have been throw in putArchiveEntry or
+            // closeArchiveEntry
+            throw new Zip64RequiredException(Zip64RequiredException
+                                             .ARCHIVE_TOO_BIG_MESSAGE);
+        }
+
         if (needsZip64Extra) {
             Zip64ExtendedInformationExtraField z64 = getZip64Extra(ze);
             if (ze.getCompressedSize() >= ZIP64_MAGIC
@@ -1012,6 +1044,8 @@ public class ZipArchiveOutputStream exte
     /**
      * Writes the "End of central dir record".
      * @throws IOException on error
+     * @throws Zip64RequiredException if the archive's size exceeds 4
+     * GByte or there are more than 65535 entries inside the archive
      */
     protected void writeCentralDirectoryEnd() throws IOException {
         writeOut(EOCD_SIG);
@@ -1021,7 +1055,18 @@ public class ZipArchiveOutputStream exte
         writeOut(ZERO);
 
         // number of entries
-        byte[] num = ZipShort.getBytes(Math.min(entries.size(),
+        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);
+        }
+
+        byte[] num = ZipShort.getBytes(Math.min(numberOfEntries,
                                                 ZIP64_MAGIC_SHORT));
         writeOut(num);
         writeOut(num);
@@ -1045,6 +1090,10 @@ public class ZipArchiveOutputStream exte
      * @since Apache Commons Compress 1.3
      */
     protected void writeZip64CentralDirectory() throws IOException {
+        if (zip64Mode == Zip64Mode.Never) {
+            return;
+        }
+
         if (!hasUsedZip64) {
             if (cdOffset >= ZIP64_MAGIC || cdLength >= ZIP64_MAGIC
                 || entries.size() >= ZIP64_MAGIC_SHORT) {
@@ -1238,6 +1287,22 @@ public class ZipArchiveOutputStream exte
     }
 
     /**
+     * Closes the underlying stream/file without finishing the
+     * archive, the result will likely be a corrupt archive.
+     *
+     * <p>This method only exists to support tests that generate
+     * corrupt archives so they can clean up any temporary files.</p>
+     */
+    void destroy() throws IOException {
+        if (raf != null) {
+            raf.close();
+        }
+        if (out != null) {
+            out.close();
+        }
+    }
+
+    /**
      * enum that represents the possible policies for creating Unicode
      * extra fields.
      */

Modified: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportTest.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportTest.java?rev=1155274&r1=1155273&r2=1155274&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportTest.java
(original)
+++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/zip/Zip64SupportTest.java
Tue Aug  9 09:08:00 2011
@@ -38,6 +38,7 @@ import static org.junit.Assert.assertFal
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeNotNull;
 import static org.junit.Assume.assumeTrue;
 
@@ -218,16 +219,39 @@ public class Zip64SupportTest {
             }
         };
 
-    @Ignore
     @Test public void write100KFilesFile() throws Throwable {
         withTemporaryArchive("write100KFilesFile", write100KFiles, true);
     }
 
-    @Ignore
     @Test public void write100KFilesStream() throws Throwable {
         withTemporaryArchive("write100KFilesStream", write100KFiles, false);
     }
 
+    private static final ZipOutputTest write100KFilesModeNever =
+        new ZipOutputTest() {
+            public void test(File f, ZipArchiveOutputStream zos)
+                throws IOException {
+                zos.setUseZip64(Zip64Mode.Never);
+                try {
+                    write100KFilesToStream(zos);
+                    fail("expected a Zip64RequiredException");
+                } catch (Zip64RequiredException ex) {
+                    assertEquals(Zip64RequiredException.TOO_MANY_ENTRIES_MESSAGE,
+                                 ex.getMessage());
+                }
+            }
+        };
+
+    @Test public void write100KFilesFileModeNever() throws Throwable {
+        withTemporaryArchive("write100KFilesFileModeNever",
+                             write100KFilesModeNever, true);
+    }
+
+    @Test public void write100KFilesStreamModeNever() throws Throwable {
+        withTemporaryArchive("write100KFilesStreamModeNever",
+                             write100KFilesModeNever, false);
+    }
+
     @Ignore
     @Test public void readSelfGenerated100KFilesUsingZipFile()
         throws Throwable {
@@ -330,20 +354,47 @@ public class Zip64SupportTest {
             }
         };
 
-    @Ignore
     @Test public void write3EntriesCreatingBigArchiveFile() throws Throwable {
         withTemporaryArchive("write3EntriesCreatingBigArchiveFile",
                              write3EntriesCreatingBigArchive,
                              true);
     }
 
-    @Ignore
     @Test public void write3EntriesCreatingBigArchiveStream() throws Throwable {
         withTemporaryArchive("write3EntriesCreatingBigArchiveStream",
                              write3EntriesCreatingBigArchive,
                              false);
     }
 
+    private static final ZipOutputTest write3EntriesCreatingBigArchiveModeNever =
+        new ZipOutputTest() {
+            public void test(File f, ZipArchiveOutputStream zos)
+                throws IOException {
+                zos.setUseZip64(Zip64Mode.Never);
+                try {
+                    write3EntriesCreatingBigArchiveToStream(zos);
+                    fail("expected a Zip64RequiredException");
+                } catch (Zip64RequiredException ex) {
+                    assertEquals(Zip64RequiredException.ARCHIVE_TOO_BIG_MESSAGE,
+                                 ex.getMessage());
+                }
+            }
+        };
+
+    @Test public void write3EntriesCreatingBigArchiveFileModeNever()
+        throws Throwable {
+        withTemporaryArchive("write3EntriesCreatingBigArchiveFileModeNever",
+                             write3EntriesCreatingBigArchiveModeNever,
+                             true);
+    }
+
+    @Test public void write3EntriesCreatingBigArchiveStreamModeNever()
+        throws Throwable {
+        withTemporaryArchive("write3EntriesCreatingBigArchiveStreamModeNever",
+                             write3EntriesCreatingBigArchiveModeNever,
+                             false);
+    }
+
     @Ignore
     @Test public void read3EntriesCreatingBigArchiveFileUsingZipFile()
         throws Throwable {
@@ -537,27 +588,70 @@ public class Zip64SupportTest {
      * No Compression + Stream => sizes must be known before data is
      * written.
      */
-    @Ignore
     @Test public void writeBigStoredEntryToStream() throws Throwable {
         withTemporaryArchive("writeBigStoredEntryToStream",
                              writeBigStoredEntry(true),
                              false);
     }
 
-    @Ignore
     @Test public void writeBigStoredEntryKnownSizeToFile() throws Throwable {
         withTemporaryArchive("writeBigStoredEntryKnownSizeToFile",
                              writeBigStoredEntry(true),
                              true);
     }
 
-    @Ignore
     @Test public void writeBigStoredEntryUnnownSizeToFile() throws Throwable {
         withTemporaryArchive("writeBigStoredEntryUnknownSizeToFile",
                              writeBigStoredEntry(false),
                              true);
     }
 
+    private static ZipOutputTest writeBigStoredEntryModeNever(final boolean knownSize) {
+        return new ZipOutputTest() {
+            public void test(File f, ZipArchiveOutputStream zos)
+                throws IOException {
+                zos.setUseZip64(Zip64Mode.Never);
+                try {
+                    byte[] buf = new byte[ONE_MILLION];
+                    ZipArchiveEntry zae = new ZipArchiveEntry("0");
+                    if (knownSize) {
+                        zae.setSize(FIVE_BILLION);
+                        zae.setCrc(0x5c316f50L);
+                    }
+                    zae.setMethod(ZipArchiveEntry.STORED);
+                    zos.putArchiveEntry(zae);
+                    for (int j = 0; j < FIVE_BILLION / 1000 / 1000; j++) {
+                        zos.write(buf);
+                    }
+                    zos.closeArchiveEntry();
+                    fail("expected a Zip64RequiredException");
+                } catch (Zip64RequiredException ex) {
+                    assertTrue(ex.getMessage().startsWith("0's size"));
+                }
+            }
+        };
+    }
+
+    @Test public void writeBigStoredEntryToStreamModeNever() throws Throwable {
+        withTemporaryArchive("writeBigStoredEntryToStreamModeNever",
+                             writeBigStoredEntryModeNever(true),
+                             false);
+    }
+
+    @Test public void writeBigStoredEntryKnownSizeToFileModeNever()
+        throws Throwable {
+        withTemporaryArchive("writeBigStoredEntryKnownSizeToFileModeNever",
+                             writeBigStoredEntryModeNever(true),
+                             true);
+    }
+
+    @Test public void writeBigStoredEntryUnnownSizeToFileModeNever()
+        throws Throwable {
+        withTemporaryArchive("writeBigStoredEntryUnknownSizeToFile",
+                             writeBigStoredEntryModeNever(false),
+                             true);
+    }
+
     /*
      * One entry of length 5 billion bytes, written with
      * compression to a stream.
@@ -567,7 +661,6 @@ public class Zip64SupportTest {
      *
      * Creates a temporary archive of approx 4MB in size
      */
-    @Ignore
     @Test public void writeBigDeflatedEntryKnownSizeToStream()
         throws Throwable {
         withTemporaryArchive("writeBigDeflatedEntryKnownSizeToStream",
@@ -886,7 +979,6 @@ public class Zip64SupportTest {
         };
     }
 
-    @Ignore
     @Test public void writeBigDeflatedEntryKnownSizeToFile()
         throws Throwable {
         withTemporaryArchive("writeBigDeflatedEntryKnownSizeToFile",
@@ -894,7 +986,6 @@ public class Zip64SupportTest {
                              true);
     }
 
-    @Ignore
     @Test public void writeBigDeflatedEntryUnknownSizeToFile()
         throws Throwable {
         withTemporaryArchive("writeBigDeflatedEntryUnknownSizeToFile",
@@ -902,6 +993,82 @@ public class Zip64SupportTest {
                              true);
     }
 
+    @Test public void writeBigDeflatedEntryKnownSizeToStreamModeNever()
+        throws Throwable {
+        withTemporaryArchive("writeBigDeflatedEntryKnownSizeToStreamModeNever",
+                             new ZipOutputTest() {
+                                 public void test(File f,
+                                                  ZipArchiveOutputStream zos)
+                                     throws IOException {
+                                     zos.setUseZip64(Zip64Mode.Never);
+                                     try {
+                                         byte[] buf = new byte[ONE_MILLION];
+                                         ZipArchiveEntry zae =
+                                             new ZipArchiveEntry("0");
+                                         zae.setSize(FIVE_BILLION);
+                                         zae.setMethod(ZipArchiveEntry.DEFLATED);
+                                         zos.putArchiveEntry(zae);
+                                         fail("expected a"
+                                              + " Zip64RequiredException");
+                                     } catch (Zip64RequiredException ex) {
+                                         assertTrue(ex.getMessage()
+                                                    .startsWith("0's size"));
+                                     }
+                                 }
+                             },
+                             false);
+    }
+
+    /*
+     * One entry of length 5 billion bytes, written with
+     * compression to a file.
+     *
+     * Writing to a file => sizes are stored directly inside the LFH.
+     * No Data Descriptor at all.
+     *
+     * Creates a temporary archive of approx 4MB in size
+     */
+    private static ZipOutputTest writeBigDeflatedEntryToFileModeNever(final boolean knownSize)
{
+        return new ZipOutputTest() {
+            public void test(File f, ZipArchiveOutputStream zos)
+                throws IOException {
+                zos.setUseZip64(Zip64Mode.Never);
+                try {
+                    byte[] buf = new byte[ONE_MILLION];
+                    ZipArchiveEntry zae = new ZipArchiveEntry("0");
+                    if (knownSize) {
+                        zae.setSize(FIVE_BILLION);
+                    }
+                    zae.setMethod(ZipArchiveEntry.DEFLATED);
+                    zos.putArchiveEntry(zae);
+                    for (int j = 0;
+                         j < FIVE_BILLION / 1000 / 1000;
+                         j++) {
+                        zos.write(buf);
+                    }
+                    zos.closeArchiveEntry();
+                    fail("expected a Zip64RequiredException");
+                } catch (Zip64RequiredException ex) {
+                    assertTrue(ex.getMessage().startsWith("0's size"));
+                }
+            }
+        };
+    }
+
+    @Test public void writeBigDeflatedEntryKnownSizeToFileModeNever()
+        throws Throwable {
+        withTemporaryArchive("writeBigDeflatedEntryKnownSizeToFileModeNever",
+                             writeBigDeflatedEntryToFileModeNever(true),
+                             true);
+    }
+
+    @Test public void writeBigDeflatedEntryUnknownSizeToFileModeNever()
+        throws Throwable {
+        withTemporaryArchive("writeBigDeflatedEntryUnknownSizeToFileModeNever",
+                             writeBigDeflatedEntryToFileModeNever(false),
+                             true);
+    }
+
     /*
      * One entry of length 1 million bytes, written without compression.
      *
@@ -1370,13 +1537,13 @@ public class Zip64SupportTest {
             assumeTrue(false);
         } finally {
             try {
-                zos.close();
+                zos.destroy();
             } finally {
                 if (os != null) {
                     os.close();
                 }
+                f.delete();
             }
-            f.delete();
         }
     }
 



Mime
View raw message