| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package org.apache.commons.compress.archivers.zip; |
| |
| import static org.apache.commons.compress.AbstractTestCase.getFile; |
| import static org.junit.Assert.*; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.nio.channels.SeekableByteChannel; |
| import java.nio.charset.StandardCharsets; |
| import java.nio.file.Files; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.TreeMap; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.zip.CRC32; |
| import java.util.zip.Deflater; |
| import java.util.zip.ZipEntry; |
| |
| import org.apache.commons.compress.utils.IOUtils; |
| import org.apache.commons.compress.utils.SeekableInMemoryByteChannel; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Test; |
| |
| public class ZipFileTest { |
| private ZipFile zf = null; |
| |
| @After |
| public void tearDown() { |
| ZipFile.closeQuietly(zf); |
| } |
| |
| @Test |
| public void testCDOrder() throws Exception { |
| readOrderTest(); |
| final ArrayList<ZipArchiveEntry> l = Collections.list(zf.getEntries()); |
| assertEntryName(l, 0, "AbstractUnicodeExtraField"); |
| assertEntryName(l, 1, "AsiExtraField"); |
| assertEntryName(l, 2, "ExtraFieldUtils"); |
| assertEntryName(l, 3, "FallbackZipEncoding"); |
| assertEntryName(l, 4, "GeneralPurposeBit"); |
| assertEntryName(l, 5, "JarMarker"); |
| assertEntryName(l, 6, "NioZipEncoding"); |
| assertEntryName(l, 7, "Simple8BitZipEncoding"); |
| assertEntryName(l, 8, "UnicodeCommentExtraField"); |
| assertEntryName(l, 9, "UnicodePathExtraField"); |
| assertEntryName(l, 10, "UnixStat"); |
| assertEntryName(l, 11, "UnparseableExtraFieldData"); |
| assertEntryName(l, 12, "UnrecognizedExtraField"); |
| assertEntryName(l, 13, "ZipArchiveEntry"); |
| assertEntryName(l, 14, "ZipArchiveInputStream"); |
| assertEntryName(l, 15, "ZipArchiveOutputStream"); |
| assertEntryName(l, 16, "ZipEncoding"); |
| assertEntryName(l, 17, "ZipEncodingHelper"); |
| assertEntryName(l, 18, "ZipExtraField"); |
| assertEntryName(l, 19, "ZipUtil"); |
| assertEntryName(l, 20, "ZipLong"); |
| assertEntryName(l, 21, "ZipShort"); |
| assertEntryName(l, 22, "ZipFile"); |
| } |
| |
| @Test |
| public void testCDOrderInMemory() throws Exception { |
| byte[] data = null; |
| try (FileInputStream fis = new FileInputStream(getFile("ordertest.zip"))) { |
| data = IOUtils.toByteArray(fis); |
| } |
| |
| zf = new ZipFile(new SeekableInMemoryByteChannel(data), ZipEncodingHelper.UTF8); |
| final ArrayList<ZipArchiveEntry> l = Collections.list(zf.getEntries()); |
| assertEntryName(l, 0, "AbstractUnicodeExtraField"); |
| assertEntryName(l, 1, "AsiExtraField"); |
| assertEntryName(l, 2, "ExtraFieldUtils"); |
| assertEntryName(l, 3, "FallbackZipEncoding"); |
| assertEntryName(l, 4, "GeneralPurposeBit"); |
| assertEntryName(l, 5, "JarMarker"); |
| assertEntryName(l, 6, "NioZipEncoding"); |
| assertEntryName(l, 7, "Simple8BitZipEncoding"); |
| assertEntryName(l, 8, "UnicodeCommentExtraField"); |
| assertEntryName(l, 9, "UnicodePathExtraField"); |
| assertEntryName(l, 10, "UnixStat"); |
| assertEntryName(l, 11, "UnparseableExtraFieldData"); |
| assertEntryName(l, 12, "UnrecognizedExtraField"); |
| assertEntryName(l, 13, "ZipArchiveEntry"); |
| assertEntryName(l, 14, "ZipArchiveInputStream"); |
| assertEntryName(l, 15, "ZipArchiveOutputStream"); |
| assertEntryName(l, 16, "ZipEncoding"); |
| assertEntryName(l, 17, "ZipEncodingHelper"); |
| assertEntryName(l, 18, "ZipExtraField"); |
| assertEntryName(l, 19, "ZipUtil"); |
| assertEntryName(l, 20, "ZipLong"); |
| assertEntryName(l, 21, "ZipShort"); |
| assertEntryName(l, 22, "ZipFile"); |
| } |
| |
| @Test |
| public void testPhysicalOrder() throws Exception { |
| readOrderTest(); |
| final ArrayList<ZipArchiveEntry> l = Collections.list(zf.getEntriesInPhysicalOrder()); |
| assertEntryName(l, 0, "AbstractUnicodeExtraField"); |
| assertEntryName(l, 1, "AsiExtraField"); |
| assertEntryName(l, 2, "ExtraFieldUtils"); |
| assertEntryName(l, 3, "FallbackZipEncoding"); |
| assertEntryName(l, 4, "GeneralPurposeBit"); |
| assertEntryName(l, 5, "JarMarker"); |
| assertEntryName(l, 6, "NioZipEncoding"); |
| assertEntryName(l, 7, "Simple8BitZipEncoding"); |
| assertEntryName(l, 8, "UnicodeCommentExtraField"); |
| assertEntryName(l, 9, "UnicodePathExtraField"); |
| assertEntryName(l, 10, "UnixStat"); |
| assertEntryName(l, 11, "UnparseableExtraFieldData"); |
| assertEntryName(l, 12, "UnrecognizedExtraField"); |
| assertEntryName(l, 13, "ZipArchiveEntry"); |
| assertEntryName(l, 14, "ZipArchiveInputStream"); |
| assertEntryName(l, 15, "ZipArchiveOutputStream"); |
| assertEntryName(l, 16, "ZipEncoding"); |
| assertEntryName(l, 17, "ZipEncodingHelper"); |
| assertEntryName(l, 18, "ZipExtraField"); |
| assertEntryName(l, 19, "ZipFile"); |
| assertEntryName(l, 20, "ZipLong"); |
| assertEntryName(l, 21, "ZipShort"); |
| assertEntryName(l, 22, "ZipUtil"); |
| } |
| |
| @Test |
| public void testPhysicalOrderOfSpecificFile() throws Exception { |
| readOrderTest(); |
| final String entryName = "src/main/java/org/apache/commons/compress/archivers/zip/ZipExtraField.java"; |
| final Iterable<ZipArchiveEntry> entries = zf.getEntriesInPhysicalOrder(entryName); |
| final Iterator<ZipArchiveEntry> iter = entries.iterator(); |
| final ZipArchiveEntry entry = iter.next(); |
| |
| assertEquals(entryName, entry.getName()); |
| assertFalse(iter.hasNext()); |
| } |
| |
| @Test |
| public void testDoubleClose() throws Exception { |
| readOrderTest(); |
| zf.close(); |
| try { |
| zf.close(); |
| } catch (final Exception ex) { |
| fail("Caught exception of second close"); |
| } |
| } |
| |
| @Test |
| public void testReadingOfStoredEntry() throws Exception { |
| final File f = File.createTempFile("commons-compress-zipfiletest", ".zip"); |
| f.deleteOnExit(); |
| OutputStream o = null; |
| InputStream i = null; |
| try { |
| o = new FileOutputStream(f); |
| final ZipArchiveOutputStream zo = new ZipArchiveOutputStream(o); |
| ZipArchiveEntry ze = new ZipArchiveEntry("foo"); |
| ze.setMethod(ZipEntry.STORED); |
| ze.setSize(4); |
| ze.setCrc(0xb63cfbcdL); |
| zo.putArchiveEntry(ze); |
| zo.write(new byte[] { 1, 2, 3, 4 }); |
| zo.closeArchiveEntry(); |
| zo.close(); |
| o.close(); |
| o = null; |
| |
| zf = new ZipFile(f); |
| ze = zf.getEntry("foo"); |
| assertNotNull(ze); |
| i = zf.getInputStream(ze); |
| final byte[] b = new byte[4]; |
| assertEquals(4, i.read(b)); |
| assertEquals(-1, i.read()); |
| } finally { |
| if (o != null) { |
| o.close(); |
| } |
| if (i != null) { |
| i.close(); |
| } |
| f.delete(); |
| } |
| } |
| |
| /** |
| * @see "https://issues.apache.org/jira/browse/COMPRESS-176" |
| */ |
| @Test |
| public void testWinzipBackSlashWorkaround() throws Exception { |
| final File archive = getFile("test-winzip.zip"); |
| zf = new ZipFile(archive); |
| assertNull(zf.getEntry("\u00e4\\\u00fc.txt")); |
| assertNotNull(zf.getEntry("\u00e4/\u00fc.txt")); |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/COMPRESS-208" |
| * >COMPRESS-208</a>. |
| */ |
| @Test |
| public void testSkipsPK00Prefix() throws Exception { |
| final File archive = getFile("COMPRESS-208.zip"); |
| zf = new ZipFile(archive); |
| assertNotNull(zf.getEntry("test1.xml")); |
| assertNotNull(zf.getEntry("test2.xml")); |
| } |
| |
| @Test |
| public void testUnixSymlinkSampleFile() throws Exception { |
| final String entryPrefix = "COMPRESS-214_unix_symlinks/"; |
| final TreeMap<String, String> expectedVals = new TreeMap<>(); |
| |
| // I threw in some Japanese characters to keep things interesting. |
| expectedVals.put(entryPrefix + "link1", "../COMPRESS-214_unix_symlinks/./a/b/c/../../../\uF999"); |
| expectedVals.put(entryPrefix + "link2", "../COMPRESS-214_unix_symlinks/./a/b/c/../../../g"); |
| expectedVals.put(entryPrefix + "link3", "../COMPRESS-214_unix_symlinks/././a/b/c/../../../\u76F4\u6A39"); |
| expectedVals.put(entryPrefix + "link4", "\u82B1\u5B50/\u745B\u5B50"); |
| expectedVals.put(entryPrefix + "\uF999", "./\u82B1\u5B50/\u745B\u5B50/\u5897\u8C37/\uF999"); |
| expectedVals.put(entryPrefix + "g", "./a/b/c/d/e/f/g"); |
| expectedVals.put(entryPrefix + "\u76F4\u6A39", "./g"); |
| |
| // Notice how a directory link might contain a trailing slash, or it might not. |
| // Also note: symlinks are always stored as files, even if they link to directories. |
| expectedVals.put(entryPrefix + "link5", "../COMPRESS-214_unix_symlinks/././a/b"); |
| expectedVals.put(entryPrefix + "link6", "../COMPRESS-214_unix_symlinks/././a/b/"); |
| |
| // I looked into creating a test with hard links, but zip does not appear to |
| // support hard links, so nevermind. |
| |
| final File archive = getFile("COMPRESS-214_unix_symlinks.zip"); |
| |
| zf = new ZipFile(archive); |
| final Enumeration<ZipArchiveEntry> en = zf.getEntries(); |
| while (en.hasMoreElements()) { |
| final ZipArchiveEntry zae = en.nextElement(); |
| final String link = zf.getUnixSymlink(zae); |
| if (zae.isUnixSymlink()) { |
| final String name = zae.getName(); |
| final String expected = expectedVals.get(name); |
| assertEquals(expected, link); |
| } else { |
| // Should be null if it's not a symlink! |
| assertNull(link); |
| } |
| } |
| } |
| |
| /** |
| * @see "https://issues.apache.org/jira/browse/COMPRESS-227" |
| */ |
| @Test |
| public void testDuplicateEntry() throws Exception { |
| final File archive = getFile("COMPRESS-227.zip"); |
| zf = new ZipFile(archive); |
| |
| final ZipArchiveEntry ze = zf.getEntry("test1.txt"); |
| assertNotNull(ze); |
| assertNotNull(zf.getInputStream(ze)); |
| |
| int numberOfEntries = 0; |
| for (final ZipArchiveEntry entry : zf.getEntries("test1.txt")) { |
| numberOfEntries++; |
| assertNotNull(zf.getInputStream(entry)); |
| } |
| assertEquals(2, numberOfEntries); |
| } |
| |
| /** |
| * @see "https://issues.apache.org/jira/browse/COMPRESS-228" |
| */ |
| @Test |
| public void testExcessDataInZip64ExtraField() throws Exception { |
| final File archive = getFile("COMPRESS-228.zip"); |
| zf = new ZipFile(archive); |
| // actually, if we get here, the test already has passed |
| |
| final ZipArchiveEntry ze = zf.getEntry("src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java"); |
| assertEquals(26101, ze.getSize()); |
| } |
| |
| @Test |
| public void testUnshrinking() throws Exception { |
| zf = new ZipFile(getFile("SHRUNK.ZIP")); |
| ZipArchiveEntry test = zf.getEntry("TEST1.XML"); |
| FileInputStream original = new FileInputStream(getFile("test1.xml")); |
| try { |
| assertArrayEquals(IOUtils.toByteArray(original), |
| IOUtils.toByteArray(zf.getInputStream(test))); |
| } finally { |
| original.close(); |
| } |
| test = zf.getEntry("TEST2.XML"); |
| original = new FileInputStream(getFile("test2.xml")); |
| try { |
| assertArrayEquals(IOUtils.toByteArray(original), |
| IOUtils.toByteArray(zf.getInputStream(test))); |
| } finally { |
| original.close(); |
| } |
| } |
| |
| /** |
| * Test case for |
| * <a href="https://issues.apache.org/jira/browse/COMPRESS-264" |
| * >COMPRESS-264</a>. |
| */ |
| @Test |
| public void testReadingOfFirstStoredEntry() throws Exception { |
| final File archive = getFile("COMPRESS-264.zip"); |
| zf = new ZipFile(archive); |
| final ZipArchiveEntry ze = zf.getEntry("test.txt"); |
| assertEquals(5, ze.getSize()); |
| assertArrayEquals(new byte[] {'d', 'a', 't', 'a', '\n'}, |
| IOUtils.toByteArray(zf.getInputStream(ze))); |
| } |
| |
| @Test |
| public void testUnzipBZip2CompressedEntry() throws Exception { |
| final File archive = getFile("bzip2-zip.zip"); |
| zf = new ZipFile(archive); |
| final ZipArchiveEntry ze = zf.getEntry("lots-of-as"); |
| assertEquals(42, ze.getSize()); |
| final byte[] expected = new byte[42]; |
| Arrays.fill(expected , (byte)'a'); |
| assertArrayEquals(expected, IOUtils.toByteArray(zf.getInputStream(ze))); |
| } |
| |
| @Test |
| public void testConcurrentReadSeekable() throws Exception { |
| // mixed.zip contains both inflated and stored files |
| byte[] data = null; |
| try (FileInputStream fis = new FileInputStream(getFile("mixed.zip"))) { |
| data = IOUtils.toByteArray(fis); |
| } |
| zf = new ZipFile(new SeekableInMemoryByteChannel(data), ZipEncodingHelper.UTF8); |
| |
| final Map<String, byte[]> content = new HashMap<>(); |
| for (final ZipArchiveEntry entry: Collections.list(zf.getEntries())) { |
| content.put(entry.getName(), IOUtils.toByteArray(zf.getInputStream(entry))); |
| } |
| |
| final AtomicInteger passedCount = new AtomicInteger(); |
| final Runnable run = new Runnable() { |
| @Override |
| public void run() { |
| for (final ZipArchiveEntry entry: Collections.list(zf.getEntries())) { |
| assertAllReadMethods(content.get(entry.getName()), zf, entry); |
| } |
| passedCount.incrementAndGet(); |
| } |
| }; |
| final Thread t0 = new Thread(run); |
| final Thread t1 = new Thread(run); |
| t0.start(); |
| t1.start(); |
| t0.join(); |
| t1.join(); |
| assertEquals(2, passedCount.get()); |
| } |
| |
| @Test |
| public void testConcurrentReadFile() throws Exception { |
| // mixed.zip contains both inflated and stored files |
| final File archive = getFile("mixed.zip"); |
| zf = new ZipFile(archive); |
| |
| final Map<String, byte[]> content = new HashMap<>(); |
| for (final ZipArchiveEntry entry: Collections.list(zf.getEntries())) { |
| content.put(entry.getName(), IOUtils.toByteArray(zf.getInputStream(entry))); |
| } |
| |
| final AtomicInteger passedCount = new AtomicInteger(); |
| final Runnable run = new Runnable() { |
| @Override |
| public void run() { |
| for (final ZipArchiveEntry entry: Collections.list(zf.getEntries())) { |
| assertAllReadMethods(content.get(entry.getName()), zf, entry); |
| } |
| passedCount.incrementAndGet(); |
| } |
| }; |
| final Thread t0 = new Thread(run); |
| final Thread t1 = new Thread(run); |
| t0.start(); |
| t1.start(); |
| t0.join(); |
| t1.join(); |
| assertEquals(2, passedCount.get()); |
| } |
| |
| /** |
| * Test correct population of header and data offsets. |
| */ |
| @Test |
| public void testOffsets() throws Exception { |
| // mixed.zip contains both inflated and stored files |
| final File archive = getFile("mixed.zip"); |
| try (ZipFile zf = new ZipFile(archive)) { |
| final ZipArchiveEntry inflatedEntry = zf.getEntry("inflated.txt"); |
| Assert.assertEquals(0x0000, inflatedEntry.getLocalHeaderOffset()); |
| Assert.assertEquals(0x0046, inflatedEntry.getDataOffset()); |
| Assert.assertTrue(inflatedEntry.isStreamContiguous()); |
| final ZipArchiveEntry storedEntry = zf.getEntry("stored.txt"); |
| Assert.assertEquals(0x5892, storedEntry.getLocalHeaderOffset()); |
| Assert.assertEquals(0x58d6, storedEntry.getDataOffset()); |
| Assert.assertTrue(inflatedEntry.isStreamContiguous()); |
| } |
| } |
| |
| /** |
| * Test correct population of header and data offsets when they are written after stream. |
| */ |
| @Test |
| public void testDelayedOffsetsAndSizes() throws Exception { |
| final ByteArrayOutputStream zipContent = new ByteArrayOutputStream(); |
| try (ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(zipContent)) { |
| final ZipArchiveEntry inflatedEntry = new ZipArchiveEntry("inflated.txt"); |
| inflatedEntry.setMethod(ZipEntry.DEFLATED); |
| zipOutput.putArchiveEntry(inflatedEntry); |
| zipOutput.write("Hello Deflated\n".getBytes()); |
| zipOutput.closeArchiveEntry(); |
| |
| final byte[] storedContent = "Hello Stored\n".getBytes(); |
| final ZipArchiveEntry storedEntry = new ZipArchiveEntry("stored.txt"); |
| storedEntry.setMethod(ZipEntry.STORED); |
| storedEntry.setSize(storedContent.length); |
| storedEntry.setCrc(calculateCrc32(storedContent)); |
| zipOutput.putArchiveEntry(storedEntry); |
| zipOutput.write("Hello Stored\n".getBytes()); |
| zipOutput.closeArchiveEntry(); |
| |
| } |
| |
| try (ZipFile zf = new ZipFile(new SeekableInMemoryByteChannel(zipContent.toByteArray()))) { |
| final ZipArchiveEntry inflatedEntry = zf.getEntry("inflated.txt"); |
| Assert.assertNotEquals(-1L, inflatedEntry.getLocalHeaderOffset()); |
| Assert.assertNotEquals(-1L, inflatedEntry.getDataOffset()); |
| Assert.assertTrue(inflatedEntry.isStreamContiguous()); |
| Assert.assertNotEquals(-1L, inflatedEntry.getCompressedSize()); |
| Assert.assertNotEquals(-1L, inflatedEntry.getSize()); |
| final ZipArchiveEntry storedEntry = zf.getEntry("stored.txt"); |
| Assert.assertNotEquals(-1L, storedEntry.getLocalHeaderOffset()); |
| Assert.assertNotEquals(-1L, storedEntry.getDataOffset()); |
| Assert.assertTrue(inflatedEntry.isStreamContiguous()); |
| Assert.assertNotEquals(-1L, storedEntry.getCompressedSize()); |
| Assert.assertNotEquals(-1L, storedEntry.getSize()); |
| } |
| } |
| |
| /** |
| * Test entries alignment. |
| */ |
| @Test |
| public void testEntryAlignment() throws Exception { |
| final SeekableInMemoryByteChannel zipContent = new SeekableInMemoryByteChannel(); |
| try (ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(zipContent)) { |
| final ZipArchiveEntry inflatedEntry = new ZipArchiveEntry("inflated.txt"); |
| inflatedEntry.setMethod(ZipEntry.DEFLATED); |
| inflatedEntry.setAlignment(1024); |
| zipOutput.putArchiveEntry(inflatedEntry); |
| zipOutput.write("Hello Deflated\n".getBytes(StandardCharsets.UTF_8)); |
| zipOutput.closeArchiveEntry(); |
| |
| final ZipArchiveEntry storedEntry = new ZipArchiveEntry("stored.txt"); |
| storedEntry.setMethod(ZipEntry.STORED); |
| storedEntry.setAlignment(1024); |
| zipOutput.putArchiveEntry(storedEntry); |
| zipOutput.write("Hello Stored\n".getBytes(StandardCharsets.UTF_8)); |
| zipOutput.closeArchiveEntry(); |
| |
| final ZipArchiveEntry storedEntry2 = new ZipArchiveEntry("stored2.txt"); |
| storedEntry2.setMethod(ZipEntry.STORED); |
| storedEntry2.setAlignment(1024); |
| storedEntry2.addExtraField(new ResourceAlignmentExtraField(1)); |
| zipOutput.putArchiveEntry(storedEntry2); |
| zipOutput.write("Hello overload-alignment Stored\n".getBytes(StandardCharsets.UTF_8)); |
| zipOutput.closeArchiveEntry(); |
| |
| final ZipArchiveEntry storedEntry3 = new ZipArchiveEntry("stored3.txt"); |
| storedEntry3.setMethod(ZipEntry.STORED); |
| storedEntry3.addExtraField(new ResourceAlignmentExtraField(1024)); |
| zipOutput.putArchiveEntry(storedEntry3); |
| zipOutput.write("Hello copy-alignment Stored\n".getBytes(StandardCharsets.UTF_8)); |
| zipOutput.closeArchiveEntry(); |
| |
| } |
| |
| try (ZipFile zf = new ZipFile(new SeekableInMemoryByteChannel( |
| Arrays.copyOfRange(zipContent.array(), 0, (int)zipContent.size()) |
| ))) { |
| final ZipArchiveEntry inflatedEntry = zf.getEntry("inflated.txt"); |
| final ResourceAlignmentExtraField inflatedAlignmentEx = |
| (ResourceAlignmentExtraField)inflatedEntry.getExtraField(ResourceAlignmentExtraField.ID); |
| assertNotEquals(-1L, inflatedEntry.getCompressedSize()); |
| assertNotEquals(-1L, inflatedEntry.getSize()); |
| assertEquals(0L, inflatedEntry.getDataOffset()%1024); |
| assertNotNull(inflatedAlignmentEx); |
| assertEquals(1024, inflatedAlignmentEx.getAlignment()); |
| assertFalse(inflatedAlignmentEx.allowMethodChange()); |
| try (InputStream stream = zf.getInputStream(inflatedEntry)) { |
| Assert.assertEquals("Hello Deflated\n", |
| new String(IOUtils.toByteArray(stream), StandardCharsets.UTF_8)); |
| } |
| final ZipArchiveEntry storedEntry = zf.getEntry("stored.txt"); |
| final ResourceAlignmentExtraField storedAlignmentEx = |
| (ResourceAlignmentExtraField)storedEntry.getExtraField(ResourceAlignmentExtraField.ID); |
| assertNotEquals(-1L, storedEntry.getCompressedSize()); |
| assertNotEquals(-1L, storedEntry.getSize()); |
| assertEquals(0L, storedEntry.getDataOffset()%1024); |
| assertNotNull(storedAlignmentEx); |
| assertEquals(1024, storedAlignmentEx.getAlignment()); |
| assertFalse(storedAlignmentEx.allowMethodChange()); |
| try (InputStream stream = zf.getInputStream(storedEntry)) { |
| Assert.assertEquals("Hello Stored\n", |
| new String(IOUtils.toByteArray(stream), StandardCharsets.UTF_8)); |
| } |
| |
| final ZipArchiveEntry storedEntry2 = zf.getEntry("stored2.txt"); |
| final ResourceAlignmentExtraField stored2AlignmentEx = |
| (ResourceAlignmentExtraField)storedEntry2.getExtraField(ResourceAlignmentExtraField.ID); |
| assertNotEquals(-1L, storedEntry2.getCompressedSize()); |
| assertNotEquals(-1L, storedEntry2.getSize()); |
| assertEquals(0L, storedEntry2.getDataOffset()%1024); |
| assertNotNull(stored2AlignmentEx); |
| assertEquals(1024, stored2AlignmentEx.getAlignment()); |
| assertFalse(stored2AlignmentEx.allowMethodChange()); |
| try (InputStream stream = zf.getInputStream(storedEntry2)) { |
| Assert.assertEquals("Hello overload-alignment Stored\n", |
| new String(IOUtils.toByteArray(stream), StandardCharsets.UTF_8)); |
| } |
| |
| final ZipArchiveEntry storedEntry3 = zf.getEntry("stored3.txt"); |
| final ResourceAlignmentExtraField stored3AlignmentEx = |
| (ResourceAlignmentExtraField)storedEntry3.getExtraField(ResourceAlignmentExtraField.ID); |
| assertNotEquals(-1L, storedEntry3.getCompressedSize()); |
| assertNotEquals(-1L, storedEntry3.getSize()); |
| assertEquals(0L, storedEntry3.getDataOffset()%1024); |
| assertNotNull(stored3AlignmentEx); |
| assertEquals(1024, stored3AlignmentEx.getAlignment()); |
| assertFalse(stored3AlignmentEx.allowMethodChange()); |
| try (InputStream stream = zf.getInputStream(storedEntry3)) { |
| Assert.assertEquals("Hello copy-alignment Stored\n", |
| new String(IOUtils.toByteArray(stream), StandardCharsets.UTF_8)); |
| } |
| } |
| } |
| |
| /** |
| * Test too big alignment, resulting into exceeding extra field limit. |
| */ |
| @Test(expected = IllegalArgumentException.class) |
| public void testEntryAlignmentExceed() throws Exception { |
| final SeekableInMemoryByteChannel zipContent = new SeekableInMemoryByteChannel(); |
| try (ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(zipContent)) { |
| final ZipArchiveEntry inflatedEntry = new ZipArchiveEntry("inflated.txt"); |
| inflatedEntry.setMethod(ZipEntry.STORED); |
| inflatedEntry.setAlignment(0x20000); |
| } |
| } |
| |
| /** |
| * Test non power of 2 alignment. |
| */ |
| @Test(expected = IllegalArgumentException.class) |
| public void testInvalidAlignment() throws Exception { |
| final ZipArchiveEntry entry = new ZipArchiveEntry("dummy"); |
| entry.setAlignment(3); |
| } |
| |
| @Test |
| public void nameSourceDefaultsToName() throws Exception { |
| nameSource("bla.zip", "test1.xml", ZipArchiveEntry.NameSource.NAME); |
| } |
| |
| @Test |
| public void nameSourceIsSetToUnicodeExtraField() throws Exception { |
| nameSource("utf8-winzip-test.zip", "\u20AC_for_Dollar.txt", |
| ZipArchiveEntry.NameSource.UNICODE_EXTRA_FIELD); |
| } |
| |
| @Test |
| public void nameSourceIsSetToEFS() throws Exception { |
| nameSource("utf8-7zip-test.zip", "\u20AC_for_Dollar.txt", |
| ZipArchiveEntry.NameSource.NAME_WITH_EFS_FLAG); |
| } |
| |
| /** |
| * @see "https://issues.apache.org/jira/browse/COMPRESS-380" |
| */ |
| @Test |
| public void readDeflate64CompressedStream() throws Exception { |
| final File input = getFile("COMPRESS-380/COMPRESS-380-input"); |
| final File archive = getFile("COMPRESS-380/COMPRESS-380.zip"); |
| try (FileInputStream in = new FileInputStream(input); |
| ZipFile zf = new ZipFile(archive)) { |
| final byte[] orig = IOUtils.toByteArray(in); |
| final ZipArchiveEntry e = zf.getEntry("input2"); |
| try (InputStream s = zf.getInputStream(e)) { |
| final byte[] fromZip = IOUtils.toByteArray(s); |
| assertArrayEquals(orig, fromZip); |
| } |
| } |
| } |
| |
| @Test |
| public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception { |
| singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("bla.zip")); |
| } |
| |
| @Test |
| public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingStore() throws Exception { |
| singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-264.zip")); |
| } |
| |
| @Test |
| public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink() throws Exception { |
| singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("SHRUNK.ZIP")); |
| } |
| |
| @Test |
| public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingExplode() throws Exception { |
| singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("imploding-8Kdict-3trees.zip")); |
| } |
| |
| @Test |
| public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64() throws Exception { |
| singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-380/COMPRESS-380.zip")); |
| } |
| |
| @Test |
| public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2() throws Exception { |
| singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("bzip2-zip.zip")); |
| } |
| |
| private void singleByteReadConsistentlyReturnsMinusOneAtEof(final File file) throws Exception { |
| try (ZipFile archive = new ZipFile(file)) { |
| final ZipArchiveEntry e = archive.getEntries().nextElement(); |
| try (InputStream is = archive.getInputStream(e)) { |
| IOUtils.toByteArray(is); |
| assertEquals(-1, is.read()); |
| assertEquals(-1, is.read()); |
| } |
| } |
| } |
| |
| @Test |
| public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception { |
| multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("bla.zip")); |
| } |
| |
| @Test |
| public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingStore() throws Exception { |
| multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-264.zip")); |
| } |
| |
| @Test |
| public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink() throws Exception { |
| multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("SHRUNK.ZIP")); |
| } |
| |
| @Test |
| public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingExplode() throws Exception { |
| multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("imploding-8Kdict-3trees.zip")); |
| } |
| |
| @Test |
| public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64() throws Exception { |
| multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-380/COMPRESS-380.zip")); |
| } |
| |
| @Test |
| public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2() throws Exception { |
| multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("bzip2-zip.zip")); |
| } |
| |
| @Test |
| public void extractFileLiesAcrossSplitZipSegmentsCreatedByZip() throws Exception { |
| final File lastFile = getFile("COMPRESS-477/split_zip_created_by_zip/split_zip_created_by_zip.zip"); |
| final SeekableByteChannel channel = ZipSplitReadOnlySeekableByteChannel.buildFromLastSplitSegment(lastFile); |
| zf = new ZipFile(channel); |
| |
| // the compressed content of UnsupportedCompressionAlgorithmException.java lies between .z01 and .z02 |
| ZipArchiveEntry zipEntry = zf.getEntry("commons-compress/src/main/java/org/apache/commons/compress/archivers/dump/UnsupportedCompressionAlgorithmException.java"); |
| File fileToCompare = getFile("COMPRESS-477/split_zip_created_by_zip/file_to_compare_1"); |
| assertFileEqualsToEntry(fileToCompare, zipEntry, zf); |
| |
| // the compressed content of DeflateParameters.java lies between .z02 and .zip |
| zipEntry = zf.getEntry("commons-compress/src/main/java/org/apache/commons/compress/compressors/deflate/DeflateParameters.java"); |
| fileToCompare = getFile("COMPRESS-477/split_zip_created_by_zip/file_to_compare_2"); |
| assertFileEqualsToEntry(fileToCompare, zipEntry, zf); |
| } |
| |
| @Test |
| public void extractFileLiesAcrossSplitZipSegmentsCreatedByZipOfZip64() throws Exception { |
| final File lastFile = getFile("COMPRESS-477/split_zip_created_by_zip/split_zip_created_by_zip_zip64.zip"); |
| final SeekableByteChannel channel = ZipSplitReadOnlySeekableByteChannel.buildFromLastSplitSegment(lastFile); |
| zf = new ZipFile(channel); |
| |
| // the compressed content of UnsupportedCompressionAlgorithmException.java lies between .z01 and .z02 |
| ZipArchiveEntry zipEntry = zf.getEntry("commons-compress/src/main/java/org/apache/commons/compress/archivers/dump/UnsupportedCompressionAlgorithmException.java"); |
| File fileToCompare = getFile("COMPRESS-477/split_zip_created_by_zip/file_to_compare_1"); |
| assertFileEqualsToEntry(fileToCompare, zipEntry, zf); |
| |
| // the compressed content of DeflateParameters.java lies between .z02 and .zip |
| zipEntry = zf.getEntry("commons-compress/src/main/java/org/apache/commons/compress/compressors/deflate/DeflateParameters.java"); |
| fileToCompare = getFile("COMPRESS-477/split_zip_created_by_zip/file_to_compare_2"); |
| assertFileEqualsToEntry(fileToCompare, zipEntry, zf); |
| } |
| |
| @Test |
| public void extractFileLiesAcrossSplitZipSegmentsCreatedByWinrar() throws Exception { |
| final File lastFile = getFile("COMPRESS-477/split_zip_created_by_winrar/split_zip_created_by_winrar.zip"); |
| final SeekableByteChannel channel = ZipSplitReadOnlySeekableByteChannel.buildFromLastSplitSegment(lastFile); |
| zf = new ZipFile(channel); |
| |
| // the compressed content of ZipArchiveInputStream.java lies between .z01 and .z02 |
| final ZipArchiveEntry zipEntry = zf.getEntry("commons-compress/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java"); |
| final File fileToCompare = getFile("COMPRESS-477/split_zip_created_by_winrar/file_to_compare_1"); |
| assertFileEqualsToEntry(fileToCompare, zipEntry, zf); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void testSetLevelTooSmallForZipArchiveOutputStream() throws Exception { |
| final ZipArchiveOutputStream outputStream = new ZipArchiveOutputStream(new ByteArrayOutputStream()); |
| outputStream.setLevel(Deflater.DEFAULT_COMPRESSION - 1); |
| } |
| |
| @Test(expected = IllegalArgumentException.class) |
| public void testSetLevelTooBigForZipArchiveOutputStream() throws Exception { |
| final ZipArchiveOutputStream outputStream = new ZipArchiveOutputStream(new ByteArrayOutputStream()); |
| outputStream.setLevel(Deflater.BEST_COMPRESSION + 1); |
| } |
| |
| @Test(expected = IllegalStateException.class) |
| public void throwsExceptionWhenWritingPreamble() throws IOException { |
| final ZipArchiveOutputStream outputStream = new ZipArchiveOutputStream(new ByteArrayOutputStream()); |
| outputStream.putArchiveEntry(new ZipArchiveEntry()); |
| outputStream.writePreamble(new byte[0]); |
| } |
| |
| @Test |
| public void testSelfExtractingZipUsingUnzipsfx() throws IOException, InterruptedException { |
| final File unzipsfx = new File("/usr/bin/unzipsfx"); |
| if (!unzipsfx.exists()) { |
| return; |
| } |
| |
| final File testZip = File.createTempFile("commons-compress-selfExtractZipTest", ".zip"); |
| testZip.deleteOnExit(); |
| |
| final String testEntryName = "test_self_extract_zip/foo"; |
| final File extractedFile = new File(testZip.getParentFile(), testEntryName); |
| extractedFile.deleteOnExit(); |
| |
| OutputStream outputStream = null; |
| InputStream inputStream = null; |
| final byte[] testData = new byte[]{1, 2, 3, 4}; |
| byte[] buffer = new byte[512]; |
| int bytesRead; |
| try (InputStream unzipsfxInputStream = new FileInputStream(unzipsfx)) { |
| outputStream = new FileOutputStream(testZip); |
| final ZipArchiveOutputStream zo = new ZipArchiveOutputStream(outputStream); |
| |
| while ((bytesRead = unzipsfxInputStream.read(buffer)) > 0) { |
| zo.writePreamble(buffer, 0, bytesRead); |
| } |
| |
| ZipArchiveEntry ze = new ZipArchiveEntry(testEntryName); |
| ze.setMethod(ZipEntry.STORED); |
| ze.setSize(4); |
| ze.setCrc(0xb63cfbcdL); |
| zo.putArchiveEntry(ze); |
| zo.write(testData); |
| zo.closeArchiveEntry(); |
| zo.close(); |
| outputStream.close(); |
| outputStream = null; |
| |
| final ProcessBuilder pbChmod = new ProcessBuilder("chmod", "+x", testZip.getPath()); |
| pbChmod.redirectErrorStream(true); |
| final Process processChmod = pbChmod.start(); |
| assertEquals(new String(IOUtils.toByteArray(processChmod.getInputStream())), 0, processChmod.waitFor()); |
| |
| final ProcessBuilder pb = new ProcessBuilder(testZip.getPath()); |
| pb.redirectOutput(ProcessBuilder.Redirect.PIPE); |
| pb.directory(testZip.getParentFile()); |
| pb.redirectErrorStream(true); |
| final Process process = pb.start(); |
| assertEquals(new String(IOUtils.toByteArray(process.getInputStream())), 0, process.waitFor()); |
| |
| if (!extractedFile.exists()) { |
| // fail if extracted file does not exist |
| fail("Can not find the extracted file"); |
| } |
| |
| inputStream = new FileInputStream(extractedFile); |
| bytesRead = IOUtils.readFully(inputStream, buffer); |
| assertEquals(testData.length, bytesRead); |
| assertArrayEquals(testData, Arrays.copyOfRange(buffer, 0, bytesRead)); |
| } finally { |
| if (outputStream != null) { |
| outputStream.close(); |
| } |
| if (inputStream != null) { |
| inputStream.close(); |
| } |
| |
| testZip.delete(); |
| extractedFile.delete(); |
| extractedFile.getParentFile().delete(); |
| } |
| } |
| |
| private void multiByteReadConsistentlyReturnsMinusOneAtEof(final File file) throws Exception { |
| final byte[] buf = new byte[2]; |
| try (ZipFile archive = new ZipFile(file)) { |
| final ZipArchiveEntry e = archive.getEntries().nextElement(); |
| try (InputStream is = archive.getInputStream(e)) { |
| IOUtils.toByteArray(is); |
| assertEquals(-1, is.read(buf)); |
| assertEquals(-1, is.read(buf)); |
| } |
| } |
| } |
| |
| private void assertAllReadMethods(final byte[] expected, final ZipFile zipFile, final ZipArchiveEntry entry) { |
| // simple IOUtil read |
| try (InputStream stream = zf.getInputStream(entry)) { |
| final byte[] full = IOUtils.toByteArray(stream); |
| assertArrayEquals(expected, full); |
| } |
| catch (final IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| |
| // big buffer at the beginning and then chunks by IOUtils read |
| try (InputStream stream = zf.getInputStream(entry)) { |
| byte[] full; |
| final byte[] bytes = new byte[0x40000]; |
| final int read = stream.read(bytes); |
| if (read < 0) { |
| full = new byte[0]; |
| } |
| else { |
| full = readStreamRest(bytes, read, stream); |
| } |
| assertArrayEquals(expected, full); |
| } |
| catch (final IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| |
| // small chunk / single byte and big buffer then |
| try (InputStream stream = zf.getInputStream(entry)) { |
| byte[] full; |
| final int single = stream.read(); |
| if (single < 0) { |
| full = new byte[0]; |
| } |
| else { |
| final byte[] big = new byte[0x40000]; |
| big[0] = (byte)single; |
| final int read = stream.read(big, 1, big.length-1); |
| if (read < 0) { |
| full = new byte[]{ (byte)single }; |
| } |
| else { |
| full = readStreamRest(big, read+1, stream); |
| } |
| } |
| assertArrayEquals(expected, full); |
| } |
| catch (final IOException ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| |
| /** |
| * Utility to append the rest of the stream to already read data. |
| */ |
| private byte[] readStreamRest(final byte[] beginning, final int length, final InputStream stream) throws IOException { |
| final byte[] rest = IOUtils.toByteArray(stream); |
| final byte[] full = new byte[length+rest.length]; |
| System.arraycopy(beginning, 0, full, 0, length); |
| System.arraycopy(rest, 0, full, length, rest.length); |
| return full; |
| } |
| |
| private long calculateCrc32(final byte[] content) { |
| final CRC32 crc = new CRC32(); |
| crc.update(content); |
| return crc.getValue(); |
| } |
| |
| /* |
| * ordertest.zip has been handcrafted. |
| * |
| * It contains enough files so any random coincidence of |
| * entries.keySet() and central directory order would be unlikely |
| * - in fact testCDOrder fails in svn revision 920284. |
| * |
| * The central directory has ZipFile and ZipUtil swapped so |
| * central directory order is different from entry data order. |
| */ |
| private void readOrderTest() throws Exception { |
| final File archive = getFile("ordertest.zip"); |
| zf = new ZipFile(archive); |
| } |
| |
| private static void assertEntryName(final ArrayList<ZipArchiveEntry> entries, |
| final int index, |
| final String expectedName) { |
| final ZipArchiveEntry ze = entries.get(index); |
| assertEquals("src/main/java/org/apache/commons/compress/archivers/zip/" |
| + expectedName + ".java", |
| ze.getName()); |
| } |
| |
| private static void nameSource(final String archive, final String entry, final ZipArchiveEntry.NameSource expected) throws Exception { |
| try (ZipFile zf = new ZipFile(getFile(archive))) { |
| final ZipArchiveEntry ze = zf.getEntry(entry); |
| assertEquals(entry, ze.getName()); |
| assertEquals(expected, ze.getNameSource()); |
| } |
| } |
| |
| private void assertFileEqualsToEntry(final File fileToCompare, final ZipArchiveEntry entry, final ZipFile zipFile) throws IOException { |
| final byte[] buffer = new byte[10240]; |
| final File tempFile = File.createTempFile("temp","txt"); |
| final OutputStream outputStream = new FileOutputStream(tempFile); |
| final InputStream inputStream = zipFile.getInputStream(entry); |
| int readLen; |
| while((readLen = inputStream.read(buffer)) > 0) { |
| outputStream.write(buffer, 0, readLen); |
| } |
| |
| outputStream.close(); |
| inputStream.close(); |
| |
| assertFileEqualIgnoreEndOfLine(fileToCompare, tempFile); |
| } |
| |
| private void assertFileEqualIgnoreEndOfLine(final File file1, final File file2) throws IOException { |
| final List<String> linesOfFile1 = Files.readAllLines(Paths.get(file1.getCanonicalPath()), StandardCharsets.UTF_8); |
| final List<String> linesOfFile2 = Files.readAllLines(Paths.get(file2.getCanonicalPath()), StandardCharsets.UTF_8); |
| |
| if(linesOfFile1.size() != linesOfFile2.size()) { |
| fail("files not equal : " + file1.getName() + " , " + file2.getName()); |
| } |
| |
| String tempLineInFile1; |
| String tempLineInFile2; |
| for(int i = 0;i < linesOfFile1.size();i++) { |
| tempLineInFile1 = linesOfFile1.get(i).replaceAll("\r\n", "\n"); |
| tempLineInFile2 = linesOfFile2.get(i).replaceAll("\r\n", "\n"); |
| Assert.assertEquals(tempLineInFile1, tempLineInFile2); |
| } |
| } |
| } |