• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  */
18 
19 package org.apache.commons.compress.archivers.zip;
20 
21 import static org.apache.commons.compress.AbstractTestCase.getFile;
22 import static org.junit.Assert.*;
23 
24 import java.io.ByteArrayOutputStream;
25 import java.io.File;
26 import java.io.FileInputStream;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStream;
31 import java.nio.charset.Charset;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.Enumeration;
36 import java.util.HashMap;
37 import java.util.Map;
38 import java.util.TreeMap;
39 import java.util.concurrent.atomic.AtomicInteger;
40 import java.util.zip.CRC32;
41 import java.util.zip.ZipEntry;
42 
43 import org.apache.commons.compress.utils.IOUtils;
44 import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
45 import org.junit.After;
46 import org.junit.Assert;
47 import org.junit.Test;
48 
49 public class ZipFileTest {
50     private ZipFile zf = null;
51 
52     @After
tearDown()53     public void tearDown() {
54         ZipFile.closeQuietly(zf);
55     }
56 
57     @Test
testCDOrder()58     public void testCDOrder() throws Exception {
59         readOrderTest();
60         final ArrayList<ZipArchiveEntry> l = Collections.list(zf.getEntries());
61         assertEntryName(l, 0, "AbstractUnicodeExtraField");
62         assertEntryName(l, 1, "AsiExtraField");
63         assertEntryName(l, 2, "ExtraFieldUtils");
64         assertEntryName(l, 3, "FallbackZipEncoding");
65         assertEntryName(l, 4, "GeneralPurposeBit");
66         assertEntryName(l, 5, "JarMarker");
67         assertEntryName(l, 6, "NioZipEncoding");
68         assertEntryName(l, 7, "Simple8BitZipEncoding");
69         assertEntryName(l, 8, "UnicodeCommentExtraField");
70         assertEntryName(l, 9, "UnicodePathExtraField");
71         assertEntryName(l, 10, "UnixStat");
72         assertEntryName(l, 11, "UnparseableExtraFieldData");
73         assertEntryName(l, 12, "UnrecognizedExtraField");
74         assertEntryName(l, 13, "ZipArchiveEntry");
75         assertEntryName(l, 14, "ZipArchiveInputStream");
76         assertEntryName(l, 15, "ZipArchiveOutputStream");
77         assertEntryName(l, 16, "ZipEncoding");
78         assertEntryName(l, 17, "ZipEncodingHelper");
79         assertEntryName(l, 18, "ZipExtraField");
80         assertEntryName(l, 19, "ZipUtil");
81         assertEntryName(l, 20, "ZipLong");
82         assertEntryName(l, 21, "ZipShort");
83         assertEntryName(l, 22, "ZipFile");
84     }
85 
86     @Test
testCDOrderInMemory()87     public void testCDOrderInMemory() throws Exception {
88         byte[] data = null;
89         try (FileInputStream fis = new FileInputStream(getFile("ordertest.zip"))) {
90             data = IOUtils.toByteArray(fis);
91         }
92 
93         zf = new ZipFile(new SeekableInMemoryByteChannel(data), ZipEncodingHelper.UTF8);
94         final ArrayList<ZipArchiveEntry> l = Collections.list(zf.getEntries());
95         assertEntryName(l, 0, "AbstractUnicodeExtraField");
96         assertEntryName(l, 1, "AsiExtraField");
97         assertEntryName(l, 2, "ExtraFieldUtils");
98         assertEntryName(l, 3, "FallbackZipEncoding");
99         assertEntryName(l, 4, "GeneralPurposeBit");
100         assertEntryName(l, 5, "JarMarker");
101         assertEntryName(l, 6, "NioZipEncoding");
102         assertEntryName(l, 7, "Simple8BitZipEncoding");
103         assertEntryName(l, 8, "UnicodeCommentExtraField");
104         assertEntryName(l, 9, "UnicodePathExtraField");
105         assertEntryName(l, 10, "UnixStat");
106         assertEntryName(l, 11, "UnparseableExtraFieldData");
107         assertEntryName(l, 12, "UnrecognizedExtraField");
108         assertEntryName(l, 13, "ZipArchiveEntry");
109         assertEntryName(l, 14, "ZipArchiveInputStream");
110         assertEntryName(l, 15, "ZipArchiveOutputStream");
111         assertEntryName(l, 16, "ZipEncoding");
112         assertEntryName(l, 17, "ZipEncodingHelper");
113         assertEntryName(l, 18, "ZipExtraField");
114         assertEntryName(l, 19, "ZipUtil");
115         assertEntryName(l, 20, "ZipLong");
116         assertEntryName(l, 21, "ZipShort");
117         assertEntryName(l, 22, "ZipFile");
118     }
119 
120     @Test
testPhysicalOrder()121     public void testPhysicalOrder() throws Exception {
122         readOrderTest();
123         final ArrayList<ZipArchiveEntry> l = Collections.list(zf.getEntriesInPhysicalOrder());
124         assertEntryName(l, 0, "AbstractUnicodeExtraField");
125         assertEntryName(l, 1, "AsiExtraField");
126         assertEntryName(l, 2, "ExtraFieldUtils");
127         assertEntryName(l, 3, "FallbackZipEncoding");
128         assertEntryName(l, 4, "GeneralPurposeBit");
129         assertEntryName(l, 5, "JarMarker");
130         assertEntryName(l, 6, "NioZipEncoding");
131         assertEntryName(l, 7, "Simple8BitZipEncoding");
132         assertEntryName(l, 8, "UnicodeCommentExtraField");
133         assertEntryName(l, 9, "UnicodePathExtraField");
134         assertEntryName(l, 10, "UnixStat");
135         assertEntryName(l, 11, "UnparseableExtraFieldData");
136         assertEntryName(l, 12, "UnrecognizedExtraField");
137         assertEntryName(l, 13, "ZipArchiveEntry");
138         assertEntryName(l, 14, "ZipArchiveInputStream");
139         assertEntryName(l, 15, "ZipArchiveOutputStream");
140         assertEntryName(l, 16, "ZipEncoding");
141         assertEntryName(l, 17, "ZipEncodingHelper");
142         assertEntryName(l, 18, "ZipExtraField");
143         assertEntryName(l, 19, "ZipFile");
144         assertEntryName(l, 20, "ZipLong");
145         assertEntryName(l, 21, "ZipShort");
146         assertEntryName(l, 22, "ZipUtil");
147     }
148 
149     @Test
testDoubleClose()150     public void testDoubleClose() throws Exception {
151         readOrderTest();
152         zf.close();
153         try {
154             zf.close();
155         } catch (final Exception ex) {
156             fail("Caught exception of second close");
157         }
158     }
159 
160     @Test
testReadingOfStoredEntry()161     public void testReadingOfStoredEntry() throws Exception {
162         final File f = File.createTempFile("commons-compress-zipfiletest", ".zip");
163         f.deleteOnExit();
164         OutputStream o = null;
165         InputStream i = null;
166         try {
167             o = new FileOutputStream(f);
168             final ZipArchiveOutputStream zo = new ZipArchiveOutputStream(o);
169             ZipArchiveEntry ze = new ZipArchiveEntry("foo");
170             ze.setMethod(ZipEntry.STORED);
171             ze.setSize(4);
172             ze.setCrc(0xb63cfbcdl);
173             zo.putArchiveEntry(ze);
174             zo.write(new byte[] { 1, 2, 3, 4 });
175             zo.closeArchiveEntry();
176             zo.close();
177             o.close();
178             o  = null;
179 
180             zf = new ZipFile(f);
181             ze = zf.getEntry("foo");
182             assertNotNull(ze);
183             i = zf.getInputStream(ze);
184             final byte[] b = new byte[4];
185             assertEquals(4, i.read(b));
186             assertEquals(-1, i.read());
187         } finally {
188             if (o != null) {
189                 o.close();
190             }
191             if (i != null) {
192                 i.close();
193             }
194             f.delete();
195         }
196     }
197 
198     /**
199      * @see "https://issues.apache.org/jira/browse/COMPRESS-176"
200      */
201     @Test
testWinzipBackSlashWorkaround()202     public void testWinzipBackSlashWorkaround() throws Exception {
203         final File archive = getFile("test-winzip.zip");
204         zf = new ZipFile(archive);
205         assertNull(zf.getEntry("\u00e4\\\u00fc.txt"));
206         assertNotNull(zf.getEntry("\u00e4/\u00fc.txt"));
207     }
208 
209     /**
210      * Test case for
211      * <a href="https://issues.apache.org/jira/browse/COMPRESS-208"
212      * >COMPRESS-208</a>.
213      */
214     @Test
testSkipsPK00Prefix()215     public void testSkipsPK00Prefix() throws Exception {
216         final File archive = getFile("COMPRESS-208.zip");
217         zf = new ZipFile(archive);
218         assertNotNull(zf.getEntry("test1.xml"));
219         assertNotNull(zf.getEntry("test2.xml"));
220     }
221 
222     @Test
testUnixSymlinkSampleFile()223     public void testUnixSymlinkSampleFile() throws Exception {
224         final String entryPrefix = "COMPRESS-214_unix_symlinks/";
225         final TreeMap<String, String> expectedVals = new TreeMap<>();
226 
227         // I threw in some Japanese characters to keep things interesting.
228         expectedVals.put(entryPrefix + "link1", "../COMPRESS-214_unix_symlinks/./a/b/c/../../../\uF999");
229         expectedVals.put(entryPrefix + "link2", "../COMPRESS-214_unix_symlinks/./a/b/c/../../../g");
230         expectedVals.put(entryPrefix + "link3", "../COMPRESS-214_unix_symlinks/././a/b/c/../../../\u76F4\u6A39");
231         expectedVals.put(entryPrefix + "link4", "\u82B1\u5B50/\u745B\u5B50");
232         expectedVals.put(entryPrefix + "\uF999", "./\u82B1\u5B50/\u745B\u5B50/\u5897\u8C37/\uF999");
233         expectedVals.put(entryPrefix + "g", "./a/b/c/d/e/f/g");
234         expectedVals.put(entryPrefix + "\u76F4\u6A39", "./g");
235 
236         // Notice how a directory link might contain a trailing slash, or it might not.
237         // Also note:  symlinks are always stored as files, even if they link to directories.
238         expectedVals.put(entryPrefix + "link5", "../COMPRESS-214_unix_symlinks/././a/b");
239         expectedVals.put(entryPrefix + "link6", "../COMPRESS-214_unix_symlinks/././a/b/");
240 
241         // I looked into creating a test with hard links, but zip does not appear to
242         // support hard links, so nevermind.
243 
244         final File archive = getFile("COMPRESS-214_unix_symlinks.zip");
245 
246         zf = new ZipFile(archive);
247         final Enumeration<ZipArchiveEntry> en = zf.getEntries();
248         while (en.hasMoreElements()) {
249             final ZipArchiveEntry zae = en.nextElement();
250             final String link = zf.getUnixSymlink(zae);
251             if (zae.isUnixSymlink()) {
252                 final String name = zae.getName();
253                 final String expected = expectedVals.get(name);
254                 assertEquals(expected, link);
255             } else {
256                 // Should be null if it's not a symlink!
257                 assertNull(link);
258             }
259         }
260     }
261 
262     /**
263      * @see "https://issues.apache.org/jira/browse/COMPRESS-227"
264      */
265     @Test
testDuplicateEntry()266     public void testDuplicateEntry() throws Exception {
267         final File archive = getFile("COMPRESS-227.zip");
268         zf = new ZipFile(archive);
269 
270         final ZipArchiveEntry ze = zf.getEntry("test1.txt");
271         assertNotNull(ze);
272         assertNotNull(zf.getInputStream(ze));
273 
274         int numberOfEntries = 0;
275         for (final ZipArchiveEntry entry : zf.getEntries("test1.txt")) {
276             numberOfEntries++;
277             assertNotNull(zf.getInputStream(entry));
278         }
279         assertEquals(2, numberOfEntries);
280     }
281 
282     /**
283      * @see "https://issues.apache.org/jira/browse/COMPRESS-228"
284      */
285     @Test
testExcessDataInZip64ExtraField()286     public void testExcessDataInZip64ExtraField() throws Exception {
287         final File archive = getFile("COMPRESS-228.zip");
288         zf = new ZipFile(archive);
289         // actually, if we get here, the test already has passed
290 
291         final ZipArchiveEntry ze = zf.getEntry("src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java");
292         assertEquals(26101, ze.getSize());
293     }
294 
295     @Test
testUnshrinking()296     public void testUnshrinking() throws Exception {
297         zf = new ZipFile(getFile("SHRUNK.ZIP"));
298         ZipArchiveEntry test = zf.getEntry("TEST1.XML");
299         FileInputStream original = new FileInputStream(getFile("test1.xml"));
300         try {
301             assertArrayEquals(IOUtils.toByteArray(original),
302                               IOUtils.toByteArray(zf.getInputStream(test)));
303         } finally {
304             original.close();
305         }
306         test = zf.getEntry("TEST2.XML");
307         original = new FileInputStream(getFile("test2.xml"));
308         try {
309             assertArrayEquals(IOUtils.toByteArray(original),
310                               IOUtils.toByteArray(zf.getInputStream(test)));
311         } finally {
312             original.close();
313         }
314     }
315 
316     /**
317      * Test case for
318      * <a href="https://issues.apache.org/jira/browse/COMPRESS-264"
319      * >COMPRESS-264</a>.
320      */
321     @Test
testReadingOfFirstStoredEntry()322     public void testReadingOfFirstStoredEntry() throws Exception {
323         final File archive = getFile("COMPRESS-264.zip");
324         zf = new ZipFile(archive);
325         final ZipArchiveEntry ze = zf.getEntry("test.txt");
326         assertEquals(5, ze.getSize());
327         assertArrayEquals(new byte[] {'d', 'a', 't', 'a', '\n'},
328                           IOUtils.toByteArray(zf.getInputStream(ze)));
329     }
330 
331     @Test
testUnzipBZip2CompressedEntry()332     public void testUnzipBZip2CompressedEntry() throws Exception {
333         final File archive = getFile("bzip2-zip.zip");
334         zf = new ZipFile(archive);
335         final ZipArchiveEntry ze = zf.getEntry("lots-of-as");
336         assertEquals(42, ze.getSize());
337         final byte[] expected = new byte[42];
338         Arrays.fill(expected , (byte)'a');
339         assertArrayEquals(expected, IOUtils.toByteArray(zf.getInputStream(ze)));
340     }
341 
342     @Test
testConcurrentReadSeekable()343     public void testConcurrentReadSeekable() throws Exception {
344         // mixed.zip contains both inflated and stored files
345         byte[] data = null;
346         try (FileInputStream fis = new FileInputStream(getFile("mixed.zip"))) {
347             data = IOUtils.toByteArray(fis);
348         }
349         zf = new ZipFile(new SeekableInMemoryByteChannel(data), ZipEncodingHelper.UTF8);
350 
351         final Map<String, byte[]> content = new HashMap<String, byte[]>();
352         for (ZipArchiveEntry entry: Collections.list(zf.getEntries())) {
353             content.put(entry.getName(), IOUtils.toByteArray(zf.getInputStream(entry)));
354         }
355 
356         final AtomicInteger passedCount = new AtomicInteger();
357         Runnable run = new Runnable() {
358             @Override
359             public void run() {
360                 for (ZipArchiveEntry entry: Collections.list(zf.getEntries())) {
361                     assertAllReadMethods(content.get(entry.getName()), zf, entry);
362                 }
363                 passedCount.incrementAndGet();
364             }
365         };
366         Thread t0 = new Thread(run);
367         Thread t1 = new Thread(run);
368         t0.start();
369         t1.start();
370         t0.join();
371         t1.join();
372         assertEquals(2, passedCount.get());
373     }
374 
375     @Test
testConcurrentReadFile()376     public void testConcurrentReadFile() throws Exception {
377         // mixed.zip contains both inflated and stored files
378         final File archive = getFile("mixed.zip");
379         zf = new ZipFile(archive);
380 
381         final Map<String, byte[]> content = new HashMap<String, byte[]>();
382         for (ZipArchiveEntry entry: Collections.list(zf.getEntries())) {
383             content.put(entry.getName(), IOUtils.toByteArray(zf.getInputStream(entry)));
384         }
385 
386         final AtomicInteger passedCount = new AtomicInteger();
387         Runnable run = new Runnable() {
388             @Override
389             public void run() {
390                 for (ZipArchiveEntry entry: Collections.list(zf.getEntries())) {
391                     assertAllReadMethods(content.get(entry.getName()), zf, entry);
392                 }
393                 passedCount.incrementAndGet();
394             }
395         };
396         Thread t0 = new Thread(run);
397         Thread t1 = new Thread(run);
398         t0.start();
399         t1.start();
400         t0.join();
401         t1.join();
402         assertEquals(2, passedCount.get());
403     }
404 
405     /**
406      * Test correct population of header and data offsets.
407      */
408     @Test
testOffsets()409     public void testOffsets() throws Exception {
410         // mixed.zip contains both inflated and stored files
411         final File archive = getFile("mixed.zip");
412         try (ZipFile zf = new ZipFile(archive)) {
413             ZipArchiveEntry inflatedEntry = zf.getEntry("inflated.txt");
414             Assert.assertEquals(0x0000, inflatedEntry.getLocalHeaderOffset());
415             Assert.assertEquals(0x0046, inflatedEntry.getDataOffset());
416             Assert.assertTrue(inflatedEntry.isStreamContiguous());
417             ZipArchiveEntry storedEntry = zf.getEntry("stored.txt");
418             Assert.assertEquals(0x5892, storedEntry.getLocalHeaderOffset());
419             Assert.assertEquals(0x58d6, storedEntry.getDataOffset());
420             Assert.assertTrue(inflatedEntry.isStreamContiguous());
421         }
422     }
423 
424     /**
425      * Test correct population of header and data offsets when they are written after stream.
426      */
427     @Test
testDelayedOffsetsAndSizes()428     public void testDelayedOffsetsAndSizes() throws Exception {
429         ByteArrayOutputStream zipContent = new ByteArrayOutputStream();
430         try (ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(zipContent)) {
431             ZipArchiveEntry inflatedEntry = new ZipArchiveEntry("inflated.txt");
432             inflatedEntry.setMethod(ZipEntry.DEFLATED);
433             zipOutput.putArchiveEntry(inflatedEntry);
434             zipOutput.write("Hello Deflated\n".getBytes());
435             zipOutput.closeArchiveEntry();
436 
437             byte[] storedContent = "Hello Stored\n".getBytes();
438             ZipArchiveEntry storedEntry = new ZipArchiveEntry("stored.txt");
439             storedEntry.setMethod(ZipEntry.STORED);
440             storedEntry.setSize(storedContent.length);
441             storedEntry.setCrc(calculateCrc32(storedContent));
442             zipOutput.putArchiveEntry(storedEntry);
443             zipOutput.write("Hello Stored\n".getBytes());
444             zipOutput.closeArchiveEntry();
445 
446         }
447 
448         try (ZipFile zf = new ZipFile(new SeekableInMemoryByteChannel(zipContent.toByteArray()))) {
449             ZipArchiveEntry inflatedEntry = zf.getEntry("inflated.txt");
450             Assert.assertNotEquals(-1L, inflatedEntry.getLocalHeaderOffset());
451             Assert.assertNotEquals(-1L, inflatedEntry.getDataOffset());
452             Assert.assertTrue(inflatedEntry.isStreamContiguous());
453             Assert.assertNotEquals(-1L, inflatedEntry.getCompressedSize());
454             Assert.assertNotEquals(-1L, inflatedEntry.getSize());
455             ZipArchiveEntry storedEntry = zf.getEntry("stored.txt");
456             Assert.assertNotEquals(-1L, storedEntry.getLocalHeaderOffset());
457             Assert.assertNotEquals(-1L, storedEntry.getDataOffset());
458             Assert.assertTrue(inflatedEntry.isStreamContiguous());
459             Assert.assertNotEquals(-1L, storedEntry.getCompressedSize());
460             Assert.assertNotEquals(-1L, storedEntry.getSize());
461         }
462     }
463 
464     /**
465      * Test entries alignment.
466      */
467     @Test
testEntryAlignment()468     public void testEntryAlignment() throws Exception {
469         SeekableInMemoryByteChannel zipContent = new SeekableInMemoryByteChannel();
470         try (ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(zipContent)) {
471             ZipArchiveEntry inflatedEntry = new ZipArchiveEntry("inflated.txt");
472             inflatedEntry.setMethod(ZipEntry.DEFLATED);
473             inflatedEntry.setAlignment(1024);
474             zipOutput.putArchiveEntry(inflatedEntry);
475             zipOutput.write("Hello Deflated\n".getBytes(Charset.forName("UTF-8")));
476             zipOutput.closeArchiveEntry();
477 
478             ZipArchiveEntry storedEntry = new ZipArchiveEntry("stored.txt");
479             storedEntry.setMethod(ZipEntry.STORED);
480             storedEntry.setAlignment(1024);
481             zipOutput.putArchiveEntry(storedEntry);
482             zipOutput.write("Hello Stored\n".getBytes(Charset.forName("UTF-8")));
483             zipOutput.closeArchiveEntry();
484 
485             ZipArchiveEntry storedEntry2 = new ZipArchiveEntry("stored2.txt");
486             storedEntry2.setMethod(ZipEntry.STORED);
487             storedEntry2.setAlignment(1024);
488             storedEntry2.addExtraField(new ResourceAlignmentExtraField(1));
489             zipOutput.putArchiveEntry(storedEntry2);
490             zipOutput.write("Hello overload-alignment Stored\n".getBytes(Charset.forName("UTF-8")));
491             zipOutput.closeArchiveEntry();
492 
493             ZipArchiveEntry storedEntry3 = new ZipArchiveEntry("stored3.txt");
494             storedEntry3.setMethod(ZipEntry.STORED);
495             storedEntry3.addExtraField(new ResourceAlignmentExtraField(1024));
496             zipOutput.putArchiveEntry(storedEntry3);
497             zipOutput.write("Hello copy-alignment Stored\n".getBytes(Charset.forName("UTF-8")));
498             zipOutput.closeArchiveEntry();
499 
500         }
501 
502         try (ZipFile zf = new ZipFile(new SeekableInMemoryByteChannel(
503                         Arrays.copyOfRange(zipContent.array(), 0, (int)zipContent.size())
504         ))) {
505             ZipArchiveEntry inflatedEntry = zf.getEntry("inflated.txt");
506             ResourceAlignmentExtraField inflatedAlignmentEx =
507                             (ResourceAlignmentExtraField)inflatedEntry.getExtraField(ResourceAlignmentExtraField.ID);
508             assertNotEquals(-1L, inflatedEntry.getCompressedSize());
509             assertNotEquals(-1L, inflatedEntry.getSize());
510             assertEquals(0L, inflatedEntry.getDataOffset()%1024);
511             assertNotNull(inflatedAlignmentEx);
512             assertEquals(1024, inflatedAlignmentEx.getAlignment());
513             assertFalse(inflatedAlignmentEx.allowMethodChange());
514             try (InputStream stream = zf.getInputStream(inflatedEntry)) {
515                 Assert.assertEquals("Hello Deflated\n",
516                                 new String(IOUtils.toByteArray(stream), Charset.forName("UTF-8")));
517             }
518             ZipArchiveEntry storedEntry = zf.getEntry("stored.txt");
519             ResourceAlignmentExtraField storedAlignmentEx =
520                             (ResourceAlignmentExtraField)storedEntry.getExtraField(ResourceAlignmentExtraField.ID);
521             assertNotEquals(-1L, storedEntry.getCompressedSize());
522             assertNotEquals(-1L, storedEntry.getSize());
523             assertEquals(0L, storedEntry.getDataOffset()%1024);
524             assertNotNull(storedAlignmentEx);
525             assertEquals(1024, storedAlignmentEx.getAlignment());
526             assertFalse(storedAlignmentEx.allowMethodChange());
527             try (InputStream stream = zf.getInputStream(storedEntry)) {
528                 Assert.assertEquals("Hello Stored\n",
529                                 new String(IOUtils.toByteArray(stream), Charset.forName("UTF-8")));
530             }
531 
532             ZipArchiveEntry storedEntry2 = zf.getEntry("stored2.txt");
533             ResourceAlignmentExtraField stored2AlignmentEx =
534                             (ResourceAlignmentExtraField)storedEntry2.getExtraField(ResourceAlignmentExtraField.ID);
535             assertNotEquals(-1L, storedEntry2.getCompressedSize());
536             assertNotEquals(-1L, storedEntry2.getSize());
537             assertEquals(0L, storedEntry2.getDataOffset()%1024);
538             assertNotNull(stored2AlignmentEx);
539             assertEquals(1024, stored2AlignmentEx.getAlignment());
540             assertFalse(stored2AlignmentEx.allowMethodChange());
541             try (InputStream stream = zf.getInputStream(storedEntry2)) {
542                 Assert.assertEquals("Hello overload-alignment Stored\n",
543                                 new String(IOUtils.toByteArray(stream), Charset.forName("UTF-8")));
544             }
545 
546             ZipArchiveEntry storedEntry3 = zf.getEntry("stored3.txt");
547             ResourceAlignmentExtraField stored3AlignmentEx =
548                             (ResourceAlignmentExtraField)storedEntry3.getExtraField(ResourceAlignmentExtraField.ID);
549             assertNotEquals(-1L, storedEntry3.getCompressedSize());
550             assertNotEquals(-1L, storedEntry3.getSize());
551             assertEquals(0L, storedEntry3.getDataOffset()%1024);
552             assertNotNull(stored3AlignmentEx);
553             assertEquals(1024, stored3AlignmentEx.getAlignment());
554             assertFalse(stored3AlignmentEx.allowMethodChange());
555             try (InputStream stream = zf.getInputStream(storedEntry3)) {
556                 Assert.assertEquals("Hello copy-alignment Stored\n",
557                                 new String(IOUtils.toByteArray(stream), Charset.forName("UTF-8")));
558             }
559         }
560     }
561 
562     /**
563      * Test too big alignment, resulting into exceeding extra field limit.
564      */
565     @Test(expected = IllegalArgumentException.class)
testEntryAlignmentExceed()566     public void testEntryAlignmentExceed() throws Exception {
567         SeekableInMemoryByteChannel zipContent = new SeekableInMemoryByteChannel();
568         try (ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(zipContent)) {
569             ZipArchiveEntry inflatedEntry = new ZipArchiveEntry("inflated.txt");
570             inflatedEntry.setMethod(ZipEntry.STORED);
571             inflatedEntry.setAlignment(0x20000);
572         }
573     }
574 
575     /**
576      * Test non power of 2 alignment.
577      */
578     @Test(expected = IllegalArgumentException.class)
testInvalidAlignment()579     public void testInvalidAlignment() throws Exception {
580         ZipArchiveEntry entry = new ZipArchiveEntry("dummy");
581         entry.setAlignment(3);
582     }
583 
584     @Test
nameSourceDefaultsToName()585     public void nameSourceDefaultsToName() throws Exception {
586         nameSource("bla.zip", "test1.xml", ZipArchiveEntry.NameSource.NAME);
587     }
588 
589     @Test
nameSourceIsSetToUnicodeExtraField()590     public void nameSourceIsSetToUnicodeExtraField() throws Exception {
591         nameSource("utf8-winzip-test.zip", "\u20AC_for_Dollar.txt",
592                    ZipArchiveEntry.NameSource.UNICODE_EXTRA_FIELD);
593     }
594 
595     @Test
nameSourceIsSetToEFS()596     public void nameSourceIsSetToEFS() throws Exception {
597         nameSource("utf8-7zip-test.zip", "\u20AC_for_Dollar.txt",
598                    ZipArchiveEntry.NameSource.NAME_WITH_EFS_FLAG);
599     }
600 
601     /**
602      * @see "https://issues.apache.org/jira/browse/COMPRESS-380"
603      */
604     @Test
readDeflate64CompressedStream()605     public void readDeflate64CompressedStream() throws Exception {
606         final File input = getFile("COMPRESS-380/COMPRESS-380-input");
607         final File archive = getFile("COMPRESS-380/COMPRESS-380.zip");
608         try (FileInputStream in = new FileInputStream(input);
609              ZipFile zf = new ZipFile(archive)) {
610             byte[] orig = IOUtils.toByteArray(in);
611             ZipArchiveEntry e = zf.getEntry("input2");
612             try (InputStream s = zf.getInputStream(e)) {
613                 byte[] fromZip = IOUtils.toByteArray(s);
614                 assertArrayEquals(orig, fromZip);
615             }
616         }
617     }
618 
619     @Test
singleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate()620     public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception {
621         singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("bla.zip"));
622     }
623 
624     @Test
singleByteReadConsistentlyReturnsMinusOneAtEofUsingStore()625     public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingStore() throws Exception {
626         singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-264.zip"));
627     }
628 
629     @Test
singleByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink()630     public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink() throws Exception {
631         singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("SHRUNK.ZIP"));
632     }
633 
634     @Test
singleByteReadConsistentlyReturnsMinusOneAtEofUsingExplode()635     public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingExplode() throws Exception {
636         singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("imploding-8Kdict-3trees.zip"));
637     }
638 
639     @Test
singleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64()640     public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64() throws Exception {
641         singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-380/COMPRESS-380.zip"));
642     }
643 
644     @Test
singleByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2()645     public void singleByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2() throws Exception {
646         singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("bzip2-zip.zip"));
647     }
648 
singleByteReadConsistentlyReturnsMinusOneAtEof(File file)649     private void singleByteReadConsistentlyReturnsMinusOneAtEof(File file) throws Exception {
650         try (ZipFile archive = new ZipFile(file)) {
651             ZipArchiveEntry e = archive.getEntries().nextElement();
652             try (InputStream is = archive.getInputStream(e)) {
653                 IOUtils.toByteArray(is);
654                 assertEquals(-1, is.read());
655                 assertEquals(-1, is.read());
656             }
657         }
658     }
659 
660     @Test
multiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate()661     public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception {
662         multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("bla.zip"));
663     }
664 
665     @Test
multiByteReadConsistentlyReturnsMinusOneAtEofUsingStore()666     public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingStore() throws Exception {
667         multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-264.zip"));
668     }
669 
670     @Test
multiByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink()671     public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink() throws Exception {
672         multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("SHRUNK.ZIP"));
673     }
674 
675     @Test
multiByteReadConsistentlyReturnsMinusOneAtEofUsingExplode()676     public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingExplode() throws Exception {
677         multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("imploding-8Kdict-3trees.zip"));
678     }
679 
680     @Test
multiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64()681     public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64() throws Exception {
682         multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-380/COMPRESS-380.zip"));
683     }
684 
685     @Test
multiByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2()686     public void multiByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2() throws Exception {
687         multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("bzip2-zip.zip"));
688     }
689 
multiByteReadConsistentlyReturnsMinusOneAtEof(File file)690     private void multiByteReadConsistentlyReturnsMinusOneAtEof(File file) throws Exception {
691         byte[] buf = new byte[2];
692         try (ZipFile archive = new ZipFile(file)) {
693             ZipArchiveEntry e = archive.getEntries().nextElement();
694             try (InputStream is = archive.getInputStream(e)) {
695                 IOUtils.toByteArray(is);
696                 assertEquals(-1, is.read(buf));
697                 assertEquals(-1, is.read(buf));
698             }
699         }
700     }
701 
assertAllReadMethods(byte[] expected, ZipFile zipFile, ZipArchiveEntry entry)702     private void assertAllReadMethods(byte[] expected, ZipFile zipFile, ZipArchiveEntry entry) {
703         // simple IOUtil read
704         try (InputStream stream = zf.getInputStream(entry)) {
705             byte[] full = IOUtils.toByteArray(stream);
706             assertArrayEquals(expected, full);
707         }
708         catch (IOException ex) {
709             throw new RuntimeException(ex);
710         }
711 
712         // big buffer at the beginning and then chunks by IOUtils read
713         try (InputStream stream = zf.getInputStream(entry)) {
714             byte[] full;
715             byte[] bytes = new byte[0x40000];
716             int read = stream.read(bytes);
717             if (read < 0) {
718                 full = new byte[0];
719             }
720             else {
721                 full = readStreamRest(bytes, read, stream);
722             }
723             assertArrayEquals(expected, full);
724         }
725         catch (IOException ex) {
726             throw new RuntimeException(ex);
727         }
728 
729         // small chunk / single byte and big buffer then
730         try (InputStream stream = zf.getInputStream(entry)) {
731             byte[] full;
732             int single = stream.read();
733             if (single < 0) {
734                 full = new byte[0];
735             }
736             else {
737                 byte[] big = new byte[0x40000];
738                 big[0] = (byte)single;
739                 int read = stream.read(big, 1, big.length-1);
740                 if (read < 0) {
741                     full = new byte[]{ (byte)single };
742                 }
743                 else {
744                     full = readStreamRest(big, read+1, stream);
745                 }
746             }
747             assertArrayEquals(expected, full);
748         }
749         catch (IOException ex) {
750             throw new RuntimeException(ex);
751         }
752     }
753 
754     /**
755      * Utility to append the rest of the stream to already read data.
756      */
readStreamRest(byte[] beginning, int length, InputStream stream)757     private byte[] readStreamRest(byte[] beginning, int length, InputStream stream) throws IOException {
758         byte[] rest = IOUtils.toByteArray(stream);
759         byte[] full = new byte[length+rest.length];
760         System.arraycopy(beginning, 0, full, 0, length);
761         System.arraycopy(rest, 0, full, length, rest.length);
762         return full;
763     }
764 
calculateCrc32(byte[] content)765     private long calculateCrc32(byte[] content) {
766         CRC32 crc = new CRC32();
767         crc.update(content);
768         return crc.getValue();
769     }
770 
771     /*
772      * ordertest.zip has been handcrafted.
773      *
774      * It contains enough files so any random coincidence of
775      * entries.keySet() and central directory order would be unlikely
776      * - in fact testCDOrder fails in svn revision 920284.
777      *
778      * The central directory has ZipFile and ZipUtil swapped so
779      * central directory order is different from entry data order.
780      */
readOrderTest()781     private void readOrderTest() throws Exception {
782         final File archive = getFile("ordertest.zip");
783         zf = new ZipFile(archive);
784     }
785 
assertEntryName(final ArrayList<ZipArchiveEntry> entries, final int index, final String expectedName)786     private static void assertEntryName(final ArrayList<ZipArchiveEntry> entries,
787                                         final int index,
788                                         final String expectedName) {
789         final ZipArchiveEntry ze = entries.get(index);
790         assertEquals("src/main/java/org/apache/commons/compress/archivers/zip/"
791                      + expectedName + ".java",
792                      ze.getName());
793     }
794 
nameSource(String archive, String entry, ZipArchiveEntry.NameSource expected)795     private static void nameSource(String archive, String entry, ZipArchiveEntry.NameSource expected) throws Exception {
796         try (ZipFile zf = new ZipFile(getFile(archive))) {
797             ZipArchiveEntry ze = zf.getEntry(entry);
798             assertEquals(entry, ze.getName());
799             assertEquals(expected, ze.getNameSource());
800         }
801     }
802 }
803