View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.commons.compress.archivers.zip;
21  
22  import static java.nio.charset.StandardCharsets.UTF_8;
23  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
24  import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
25  import static org.junit.jupiter.api.Assertions.assertEquals;
26  import static org.junit.jupiter.api.Assertions.assertFalse;
27  import static org.junit.jupiter.api.Assertions.assertNotEquals;
28  import static org.junit.jupiter.api.Assertions.assertNotNull;
29  import static org.junit.jupiter.api.Assertions.assertNull;
30  import static org.junit.jupiter.api.Assertions.assertThrows;
31  import static org.junit.jupiter.api.Assertions.assertTrue;
32  import static org.junit.jupiter.api.Assertions.fail;
33  
34  import java.io.ByteArrayOutputStream;
35  import java.io.File;
36  import java.io.IOException;
37  import java.io.InputStream;
38  import java.io.OutputStream;
39  import java.nio.channels.SeekableByteChannel;
40  import java.nio.charset.StandardCharsets;
41  import java.nio.file.Files;
42  import java.nio.file.Path;
43  import java.nio.file.Paths;
44  import java.util.ArrayList;
45  import java.util.Arrays;
46  import java.util.Collections;
47  import java.util.HashMap;
48  import java.util.Iterator;
49  import java.util.List;
50  import java.util.Map;
51  import java.util.TreeMap;
52  import java.util.concurrent.atomic.AtomicInteger;
53  import java.util.zip.CRC32;
54  import java.util.zip.Deflater;
55  import java.util.zip.ZipEntry;
56  
57  import org.apache.commons.compress.AbstractTest;
58  import org.apache.commons.compress.utils.ByteUtils;
59  import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
60  import org.apache.commons.io.IOUtils;
61  import org.apache.commons.io.function.IORunnable;
62  import org.apache.commons.lang3.ArrayFill;
63  import org.apache.commons.lang3.SystemUtils;
64  import org.junit.Assume;
65  import org.junit.jupiter.api.AfterEach;
66  import org.junit.jupiter.api.Assumptions;
67  import org.junit.jupiter.api.Test;
68  
69  import io.airlift.compress.zstd.ZstdInputStream;
70  
71  class ZipFileTest extends AbstractTest {
72  
73      /**
74       * This Class simulates the case where the Zip File uses the aircompressors {@link ZstdInputStream}
75       */
76      private final class AirliftZstdZipFile extends ZipFile {
77          private boolean used;
78  
79          private AirliftZstdZipFile(final File file) throws IOException {
80              super(file);
81          }
82  
83          @Override
84          protected InputStream createZstdInputStream(final InputStream is) throws IOException {
85              return new ZstdInputStream(is) {
86  
87                  @Override
88                  public int read(final byte[] outputBuffer, final int outputOffset, final int outputLength) throws IOException {
89                      used = true;
90                      return super.read(outputBuffer, outputOffset, outputLength);
91                  }
92              };
93          }
94  
95          public boolean isUsed() {
96              return used;
97          }
98      }
99  
100     private static final int OUT_OF_MEMORY = 137;
101 
102     private static void assertEntryName(final ArrayList<ZipArchiveEntry> entries, final int index, final String expectedName) {
103         final ZipArchiveEntry ze = entries.get(index);
104         assertEquals("src/main/java/org/apache/commons/compress/archivers/zip/" + expectedName + ".java", ze.getName());
105     }
106 
107     private static void nameSource(final String archive, final String entry, final ZipArchiveEntry.NameSource expected) throws Exception {
108         try (ZipFile zf = ZipFile.builder().setFile(getFile(archive)).get()) {
109             final ZipArchiveEntry ze = zf.getEntry(entry);
110             assertEquals(entry, ze.getName());
111             assertEquals(expected, ze.getNameSource());
112         }
113     }
114 
115     private ZipFile zf;
116 
117     private void assertAllReadMethods(final byte[] expected, final ZipFile zipFile, final ZipArchiveEntry entry) throws IOException {
118         // simple IOUtil read
119         try (InputStream stream = zf.getInputStream(entry)) {
120             final byte[] full = IOUtils.toByteArray(stream);
121             assertArrayEquals(expected, full);
122         }
123 
124         // big buffer at the beginning and then chunks by IOUtils read
125         try (InputStream stream = zf.getInputStream(entry)) {
126             final byte[] full;
127             final byte[] bytes = new byte[0x40000];
128             final int read = stream.read(bytes);
129             if (read < 0) {
130                 full = ByteUtils.EMPTY_BYTE_ARRAY;
131             } else {
132                 full = readStreamRest(bytes, read, stream);
133             }
134             assertArrayEquals(expected, full);
135         }
136 
137         // small chunk / single byte and big buffer then
138         try (InputStream stream = zf.getInputStream(entry)) {
139             final byte[] full;
140             final int single = stream.read();
141             if (single < 0) {
142                 full = ByteUtils.EMPTY_BYTE_ARRAY;
143             } else {
144                 final byte[] big = new byte[0x40000];
145                 big[0] = (byte) single;
146                 final int read = stream.read(big, 1, big.length - 1);
147                 if (read < 0) {
148                     full = new byte[] { (byte) single };
149                 } else {
150                     full = readStreamRest(big, read + 1, stream);
151                 }
152             }
153             assertArrayEquals(expected, full);
154         }
155     }
156 
157     private void assertFileEqualIgnoreEndOfLine(final File file1, final File file2) throws IOException {
158         final List<String> linesOfFile1 = Files.readAllLines(Paths.get(file1.getCanonicalPath()), UTF_8);
159         final List<String> linesOfFile2 = Files.readAllLines(Paths.get(file2.getCanonicalPath()), UTF_8);
160 
161         if (linesOfFile1.size() != linesOfFile2.size()) {
162             fail("files not equal : " + file1.getName() + " , " + file2.getName());
163         }
164 
165         String tempLineInFile1;
166         String tempLineInFile2;
167         for (int i = 0; i < linesOfFile1.size(); i++) {
168             tempLineInFile1 = linesOfFile1.get(i).replace("\r\n", "\n");
169             tempLineInFile2 = linesOfFile2.get(i).replace("\r\n", "\n");
170             assertEquals(tempLineInFile1, tempLineInFile2);
171         }
172     }
173 
174     private void assertFileEqualsToEntry(final File fileToCompare, final ZipArchiveEntry entry, final ZipFile zipFile) throws IOException {
175         final File tempFile = createTempFile("temp", "txt");
176         try (OutputStream outputStream = Files.newOutputStream(tempFile.toPath());
177                 InputStream inputStream = zipFile.getInputStream(entry)) {
178             IOUtils.copyLarge(inputStream, outputStream, new byte[10240]);
179         }
180         assertFileEqualIgnoreEndOfLine(fileToCompare, tempFile);
181     }
182 
183     private long calculateCrc32(final byte[] content) {
184         final CRC32 crc = new CRC32();
185         crc.update(content);
186         return crc.getValue();
187     }
188 
189     private void multiByteReadConsistentlyReturnsMinusOneAtEof(final File file) throws Exception {
190         final byte[] buf = new byte[2];
191         try (ZipFile zipFile = ZipFile.builder().setFile(file).get()) {
192             final ZipArchiveEntry e = zipFile.getEntries().nextElement();
193             try (InputStream is = zipFile.getInputStream(e)) {
194                 IOUtils.toByteArray(is);
195                 assertEquals(-1, is.read(buf));
196                 assertEquals(-1, is.read(buf));
197             }
198         }
199     }
200 
201     /*
202      * ordertest.zip has been handcrafted.
203      *
204      * It contains enough files so any random coincidence of entries.keySet() and central directory order would be unlikely - in fact testCDOrder fails in svn
205      * revision 920284.
206      *
207      * The central directory has ZipFile and ZipUtil swapped so central directory order is different from entry data order.
208      */
209     private void readOrderTest() throws Exception {
210         zf = ZipFile.builder().setFile(getFile("ordertest.zip")).get();
211     }
212 
213     /**
214      * Utility to append the rest of the stream to already read data.
215      */
216     private byte[] readStreamRest(final byte[] beginning, final int length, final InputStream stream) throws IOException {
217         final byte[] rest = IOUtils.toByteArray(stream);
218         final byte[] full = new byte[length + rest.length];
219         System.arraycopy(beginning, 0, full, 0, length);
220         System.arraycopy(rest, 0, full, length, rest.length);
221         return full;
222     }
223 
224     private void singleByteReadConsistentlyReturnsMinusOneAtEof(final File file) throws Exception {
225         try (ZipFile zipFile = ZipFile.builder().setFile(file).get();) {
226             final ZipArchiveEntry e = zipFile.getEntries().nextElement();
227             try (InputStream is = zipFile.getInputStream(e)) {
228                 IOUtils.toByteArray(is);
229                 assertEquals(-1, is.read());
230                 assertEquals(-1, is.read());
231             }
232         }
233     }
234 
235     @AfterEach
236     public void tearDownClose() {
237         ZipFile.closeQuietly(zf);
238     }
239 
240     @Test
241     void testAlternativeZstdInputStream() throws Exception {
242         final File archive = getFile("COMPRESS-692/compress-692.zip");
243         try (AirliftZstdZipFile zf = new AirliftZstdZipFile(archive)) {
244             final byte[] buffer = new byte[7000];
245             final ZipArchiveEntry ze = zf.getEntry("dolor.txt");
246             assertNotNull(ze);
247             try (InputStream inputStream = zf.getInputStream(ze)) {
248                 assertNotNull(inputStream);
249                 assertFalse(zf.isUsed());
250                 final int bytesRead = org.apache.commons.compress.utils.IOUtils.readFully(inputStream, buffer);
251                 assertEquals(6066, bytesRead);
252                 assertTrue(zf.isUsed());
253             }
254         }
255 
256         try (ZipFile builtZipFile = ZipFile.builder().setPath(archive.getAbsolutePath()).setZstdInputStreamFactory(ZstdInputStream::new).get()) {
257             final byte[] buffer = new byte[7000];
258             final ZipArchiveEntry ze = builtZipFile.getEntry("dolor.txt");
259             assertNotNull(ze);
260             try (InputStream inputStream = builtZipFile.getInputStream(ze)) {
261                 assertTrue(inputStream instanceof ZstdInputStream);
262                 assertNotNull(inputStream);
263                 final int bytesRead = org.apache.commons.compress.utils.IOUtils.readFully(inputStream, buffer);
264                 assertEquals(6066, bytesRead);
265             }
266         }
267     }
268 
269     @Test
270     void testCDOrder() throws Exception {
271         readOrderTest();
272         testCDOrderInMemory();
273     }
274 
275     @Test
276     void testCDOrderInMemory() throws Exception {
277         final byte[] data = readAllBytes("ordertest.zip");
278         zf = ZipFile.builder().setByteArray(data).setCharset(StandardCharsets.UTF_8).get();
279         testCDOrderInMemory(zf);
280         try (SeekableInMemoryByteChannel channel = new SeekableInMemoryByteChannel(data)) {
281             zf = ZipFile.builder().setSeekableByteChannel(channel).setCharset(StandardCharsets.UTF_8).get();
282             testCDOrderInMemory(zf);
283         }
284         try (SeekableInMemoryByteChannel channel = new SeekableInMemoryByteChannel(data)) {
285             zf = new ZipFile(channel, StandardCharsets.UTF_8.name());
286             testCDOrderInMemory(zf);
287         }
288     }
289 
290     private void testCDOrderInMemory(final ZipFile zipFile) {
291         final ArrayList<ZipArchiveEntry> list = Collections.list(zipFile.getEntries());
292         assertEntryName(list, 0, "AbstractUnicodeExtraField");
293         assertEntryName(list, 1, "AsiExtraField");
294         assertEntryName(list, 2, "ExtraFieldUtils");
295         assertEntryName(list, 3, "FallbackZipEncoding");
296         assertEntryName(list, 4, "GeneralPurposeBit");
297         assertEntryName(list, 5, "JarMarker");
298         assertEntryName(list, 6, "NioZipEncoding");
299         assertEntryName(list, 7, "Simple8BitZipEncoding");
300         assertEntryName(list, 8, "UnicodeCommentExtraField");
301         assertEntryName(list, 9, "UnicodePathExtraField");
302         assertEntryName(list, 10, "UnixStat");
303         assertEntryName(list, 11, "UnparseableExtraFieldData");
304         assertEntryName(list, 12, "UnrecognizedExtraField");
305         assertEntryName(list, 13, "ZipArchiveEntry");
306         assertEntryName(list, 14, "ZipArchiveInputStream");
307         assertEntryName(list, 15, "ZipArchiveOutputStream");
308         assertEntryName(list, 16, "ZipEncoding");
309         assertEntryName(list, 17, "ZipEncodingHelper");
310         assertEntryName(list, 18, "ZipExtraField");
311         assertEntryName(list, 19, "ZipUtil");
312         assertEntryName(list, 20, "ZipLong");
313         assertEntryName(list, 21, "ZipShort");
314         assertEntryName(list, 22, "ZipFile");
315     }
316 
317     @Test
318     void testConcurrentReadFile() throws Exception {
319         // mixed.zip contains both inflated and stored files
320         final File archive = getFile("mixed.zip");
321         zf = new ZipFile(archive);
322         final Map<String, byte[]> content = new HashMap<>();
323         zf.stream().forEach(entry -> {
324             try (InputStream inputStream = zf.getInputStream(entry)) {
325                 content.put(entry.getName(), IOUtils.toByteArray(inputStream));
326             }
327         });
328         final AtomicInteger passedCount = new AtomicInteger();
329         final IORunnable run = () -> {
330             zf.stream().forEach(entry -> assertAllReadMethods(content.get(entry.getName()), zf, entry));
331             passedCount.incrementAndGet();
332         };
333         final Thread t0 = new Thread(run.asRunnable());
334         final Thread t1 = new Thread(run.asRunnable());
335         t0.start();
336         t1.start();
337         t0.join();
338         t1.join();
339         assertEquals(2, passedCount.get());
340     }
341 
342     @Test
343     void testConcurrentReadSeekable() throws Exception {
344         // mixed.zip contains both inflated and stored files
345         final byte[] data;
346         try (InputStream fis = newInputStream("mixed.zip")) {
347             data = IOUtils.toByteArray(fis);
348         }
349         try (SeekableInMemoryByteChannel channel = new SeekableInMemoryByteChannel(data)) {
350             zf = ZipFile.builder().setSeekableByteChannel(channel).setCharset(StandardCharsets.UTF_8).get();
351             final Map<String, byte[]> content = new HashMap<>();
352             zf.stream().forEach(entry -> {
353                 try (InputStream inputStream = zf.getInputStream(entry)) {
354                     content.put(entry.getName(), IOUtils.toByteArray(inputStream));
355                 }
356             });
357             final AtomicInteger passedCount = new AtomicInteger();
358             final IORunnable run = () -> {
359                 zf.stream().forEach(entry -> assertAllReadMethods(content.get(entry.getName()), zf, entry));
360                 passedCount.incrementAndGet();
361             };
362             final Thread t0 = new Thread(run.asRunnable());
363             final Thread t1 = new Thread(run.asRunnable());
364             t0.start();
365             t1.start();
366             t0.join();
367             t1.join();
368             assertEquals(2, passedCount.get());
369         }
370     }
371 
372     /**
373      * Test correct population of header and data offsets when they are written after stream.
374      */
375     @Test
376     void testDelayedOffsetsAndSizes() throws Exception {
377         final ByteArrayOutputStream zipContent = new ByteArrayOutputStream();
378         try (ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(zipContent)) {
379             final ZipArchiveEntry inflatedEntry = new ZipArchiveEntry("inflated.txt");
380             inflatedEntry.setMethod(ZipEntry.DEFLATED);
381             zipOutput.putArchiveEntry(inflatedEntry);
382             zipOutput.write("Hello Deflated\n".getBytes());
383             zipOutput.closeArchiveEntry();
384 
385             final byte[] storedContent = "Hello Stored\n".getBytes();
386             final ZipArchiveEntry storedEntry = new ZipArchiveEntry("stored.txt");
387             storedEntry.setMethod(ZipEntry.STORED);
388             storedEntry.setSize(storedContent.length);
389             storedEntry.setCrc(calculateCrc32(storedContent));
390             zipOutput.putArchiveEntry(storedEntry);
391             zipOutput.write("Hello Stored\n".getBytes());
392             zipOutput.closeArchiveEntry();
393 
394         }
395 
396         try (ZipFile zf = ZipFile.builder().setByteArray(zipContent.toByteArray()).get()) {
397             final ZipArchiveEntry inflatedEntry = zf.getEntry("inflated.txt");
398             assertNotEquals(-1L, inflatedEntry.getLocalHeaderOffset());
399             assertNotEquals(-1L, inflatedEntry.getDataOffset());
400             assertTrue(inflatedEntry.isStreamContiguous());
401             assertNotEquals(-1L, inflatedEntry.getCompressedSize());
402             assertNotEquals(-1L, inflatedEntry.getSize());
403             final ZipArchiveEntry storedEntry = zf.getEntry("stored.txt");
404             assertNotEquals(-1L, storedEntry.getLocalHeaderOffset());
405             assertNotEquals(-1L, storedEntry.getDataOffset());
406             assertTrue(inflatedEntry.isStreamContiguous());
407             assertNotEquals(-1L, storedEntry.getCompressedSize());
408             assertNotEquals(-1L, storedEntry.getSize());
409         }
410     }
411 
412     @Test
413     void testDoubleClose() throws Exception {
414         readOrderTest();
415         zf.close();
416         assertDoesNotThrow(zf::close, "Caught exception of second close");
417     }
418 
419     /**
420      * @see "https://issues.apache.org/jira/browse/COMPRESS-227"
421      */
422     @Test
423     void testDuplicateEntry() throws Exception {
424         final File archive = getFile("COMPRESS-227.zip");
425         zf = new ZipFile(archive);
426 
427         final ZipArchiveEntry ze = zf.getEntry("test1.txt");
428         assertNotNull(ze);
429         try (InputStream inputStream = zf.getInputStream(ze)) {
430             assertNotNull(inputStream);
431 
432             int numberOfEntries = 0;
433             for (final ZipArchiveEntry entry : zf.getEntries("test1.txt")) {
434                 numberOfEntries++;
435                 try (InputStream inputStream2 = zf.getInputStream(entry)) {
436                     assertNotNull(inputStream2);
437                 }
438             }
439             assertEquals(2, numberOfEntries);
440         }
441     }
442 
443     /**
444      * Test entries alignment.
445      */
446     @Test
447     void testEntryAlignment() throws Exception {
448         try (SeekableInMemoryByteChannel zipContent = new SeekableInMemoryByteChannel()) {
449             try (ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(zipContent)) {
450                 final ZipArchiveEntry inflatedEntry = new ZipArchiveEntry("inflated.txt");
451                 inflatedEntry.setMethod(ZipEntry.DEFLATED);
452                 inflatedEntry.setAlignment(1024);
453                 zipOutput.putArchiveEntry(inflatedEntry);
454                 zipOutput.writeUtf8("Hello Deflated\n");
455                 zipOutput.closeArchiveEntry();
456 
457                 final ZipArchiveEntry storedEntry = new ZipArchiveEntry("stored.txt");
458                 storedEntry.setMethod(ZipEntry.STORED);
459                 storedEntry.setAlignment(1024);
460                 zipOutput.putArchiveEntry(storedEntry);
461                 zipOutput.writeUtf8("Hello Stored\n");
462                 zipOutput.closeArchiveEntry();
463 
464                 final ZipArchiveEntry storedEntry2 = new ZipArchiveEntry("stored2.txt");
465                 storedEntry2.setMethod(ZipEntry.STORED);
466                 storedEntry2.setAlignment(1024);
467                 storedEntry2.addExtraField(new ResourceAlignmentExtraField(1));
468                 zipOutput.putArchiveEntry(storedEntry2);
469                 zipOutput.writeUtf8("Hello overload-alignment Stored\n");
470                 zipOutput.closeArchiveEntry();
471 
472                 final ZipArchiveEntry storedEntry3 = new ZipArchiveEntry("stored3.txt");
473                 storedEntry3.setMethod(ZipEntry.STORED);
474                 storedEntry3.addExtraField(new ResourceAlignmentExtraField(1024));
475                 zipOutput.putArchiveEntry(storedEntry3);
476                 zipOutput.writeUtf8("Hello copy-alignment Stored\n");
477                 zipOutput.closeArchiveEntry();
478 
479             }
480 
481             try (ZipFile zf = ZipFile.builder().setByteArray(Arrays.copyOfRange(zipContent.array(), 0, (int) zipContent.size())).get()) {
482                 final ZipArchiveEntry inflatedEntry = zf.getEntry("inflated.txt");
483                 final ResourceAlignmentExtraField inflatedAlignmentEx = (ResourceAlignmentExtraField) inflatedEntry
484                         .getExtraField(ResourceAlignmentExtraField.ID);
485                 assertNotEquals(-1L, inflatedEntry.getCompressedSize());
486                 assertNotEquals(-1L, inflatedEntry.getSize());
487                 assertEquals(0L, inflatedEntry.getDataOffset() % 1024);
488                 assertNotNull(inflatedAlignmentEx);
489                 assertEquals(1024, inflatedAlignmentEx.getAlignment());
490                 assertFalse(inflatedAlignmentEx.allowMethodChange());
491                 try (InputStream stream = zf.getInputStream(inflatedEntry)) {
492                     assertEquals("Hello Deflated\n", new String(IOUtils.toByteArray(stream), UTF_8));
493                 }
494                 final ZipArchiveEntry storedEntry = zf.getEntry("stored.txt");
495                 final ResourceAlignmentExtraField storedAlignmentEx = (ResourceAlignmentExtraField) storedEntry.getExtraField(ResourceAlignmentExtraField.ID);
496                 assertNotEquals(-1L, storedEntry.getCompressedSize());
497                 assertNotEquals(-1L, storedEntry.getSize());
498                 assertEquals(0L, storedEntry.getDataOffset() % 1024);
499                 assertNotNull(storedAlignmentEx);
500                 assertEquals(1024, storedAlignmentEx.getAlignment());
501                 assertFalse(storedAlignmentEx.allowMethodChange());
502                 try (InputStream stream = zf.getInputStream(storedEntry)) {
503                     assertEquals("Hello Stored\n", new String(IOUtils.toByteArray(stream), UTF_8));
504                 }
505 
506                 final ZipArchiveEntry storedEntry2 = zf.getEntry("stored2.txt");
507                 final ResourceAlignmentExtraField stored2AlignmentEx = (ResourceAlignmentExtraField) storedEntry2.getExtraField(ResourceAlignmentExtraField.ID);
508                 assertNotEquals(-1L, storedEntry2.getCompressedSize());
509                 assertNotEquals(-1L, storedEntry2.getSize());
510                 assertEquals(0L, storedEntry2.getDataOffset() % 1024);
511                 assertNotNull(stored2AlignmentEx);
512                 assertEquals(1024, stored2AlignmentEx.getAlignment());
513                 assertFalse(stored2AlignmentEx.allowMethodChange());
514                 try (InputStream stream = zf.getInputStream(storedEntry2)) {
515                     assertEquals("Hello overload-alignment Stored\n", new String(IOUtils.toByteArray(stream), UTF_8));
516                 }
517 
518                 final ZipArchiveEntry storedEntry3 = zf.getEntry("stored3.txt");
519                 final ResourceAlignmentExtraField stored3AlignmentEx = (ResourceAlignmentExtraField) storedEntry3.getExtraField(ResourceAlignmentExtraField.ID);
520                 assertNotEquals(-1L, storedEntry3.getCompressedSize());
521                 assertNotEquals(-1L, storedEntry3.getSize());
522                 assertEquals(0L, storedEntry3.getDataOffset() % 1024);
523                 assertNotNull(stored3AlignmentEx);
524                 assertEquals(1024, stored3AlignmentEx.getAlignment());
525                 assertFalse(stored3AlignmentEx.allowMethodChange());
526                 try (InputStream stream = zf.getInputStream(storedEntry3)) {
527                     assertEquals("Hello copy-alignment Stored\n", new String(IOUtils.toByteArray(stream), UTF_8));
528                 }
529             }
530         }
531     }
532 
533     /**
534      * Test too big alignment, resulting into exceeding extra field limit.
535      */
536     @Test
537     void testEntryAlignmentExceed() throws Exception {
538         try (SeekableInMemoryByteChannel zipContent = new SeekableInMemoryByteChannel();
539                 ZipArchiveOutputStream zipOutput = new ZipArchiveOutputStream(zipContent)) {
540             final ZipArchiveEntry inflatedEntry = new ZipArchiveEntry("inflated.txt");
541             inflatedEntry.setMethod(ZipEntry.STORED);
542             assertThrows(IllegalArgumentException.class, () -> inflatedEntry.setAlignment(0x20000));
543         }
544     }
545 
546     /**
547      * @see "https://issues.apache.org/jira/browse/COMPRESS-228"
548      */
549     @Test
550     void testExcessDataInZip64ExtraField() throws Exception {
551         final File archive = getFile("COMPRESS-228.zip");
552         zf = new ZipFile(archive);
553         // actually, if we get here, the test already has passed
554 
555         final ZipArchiveEntry ze = zf.getEntry("src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java");
556         assertEquals(26101, ze.getSize());
557     }
558 
559     @Test
560     void testExtractFileLiesAcrossSplitZipSegmentsCreatedByWinrar() throws Exception {
561         final File lastFile = getFile("COMPRESS-477/split_zip_created_by_winrar/split_zip_created_by_winrar.zip");
562         try (SeekableByteChannel channel = ZipSplitReadOnlySeekableByteChannel.buildFromLastSplitSegment(lastFile)) {
563             zf = ZipFile.builder().setSeekableByteChannel(channel).get();
564 
565             // the compressed content of ZipArchiveInputStream.java lies between .z01 and .z02
566             final ZipArchiveEntry zipEntry = zf.getEntry("commons-compress/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveInputStream.java");
567             final File fileToCompare = getFile("COMPRESS-477/split_zip_created_by_winrar/file_to_compare_1");
568             assertFileEqualsToEntry(fileToCompare, zipEntry, zf);
569         }
570     }
571 
572     @Test
573     void testExtractFileLiesAcrossSplitZipSegmentsCreatedByZip() throws Exception {
574         final File lastFile = getFile("COMPRESS-477/split_zip_created_by_zip/split_zip_created_by_zip.zip");
575         try (SeekableByteChannel channel = ZipSplitReadOnlySeekableByteChannel.buildFromLastSplitSegment(lastFile)) {
576             zf = new ZipFile(channel);
577 
578             // the compressed content of UnsupportedCompressionAlgorithmException.java lies between .z01 and .z02
579             ZipArchiveEntry zipEntry = zf
580                     .getEntry("commons-compress/src/main/java/org/apache/commons/compress/archivers/dump/UnsupportedCompressionAlgorithmException.java");
581             File fileToCompare = getFile("COMPRESS-477/split_zip_created_by_zip/file_to_compare_1");
582             assertFileEqualsToEntry(fileToCompare, zipEntry, zf);
583 
584             // the compressed content of DeflateParameters.java lies between .z02 and .zip
585             zipEntry = zf.getEntry("commons-compress/src/main/java/org/apache/commons/compress/compressors/deflate/DeflateParameters.java");
586             fileToCompare = getFile("COMPRESS-477/split_zip_created_by_zip/file_to_compare_2");
587             assertFileEqualsToEntry(fileToCompare, zipEntry, zf);
588         }
589     }
590 
591     @Test
592     void testExtractFileLiesAcrossSplitZipSegmentsCreatedByZipOfZip64() throws Exception {
593         final File lastFile = getFile("COMPRESS-477/split_zip_created_by_zip/split_zip_created_by_zip_zip64.zip");
594         try (SeekableByteChannel channel = ZipSplitReadOnlySeekableByteChannel.buildFromLastSplitSegment(lastFile)) {
595             zf = new ZipFile(channel);
596 
597             // the compressed content of UnsupportedCompressionAlgorithmException.java lies between .z01 and .z02
598             ZipArchiveEntry zipEntry = zf
599                     .getEntry("commons-compress/src/main/java/org/apache/commons/compress/archivers/dump/UnsupportedCompressionAlgorithmException.java");
600             File fileToCompare = getFile("COMPRESS-477/split_zip_created_by_zip/file_to_compare_1");
601             assertFileEqualsToEntry(fileToCompare, zipEntry, zf);
602 
603             // the compressed content of DeflateParameters.java lies between .z02 and .zip
604             zipEntry = zf.getEntry("commons-compress/src/main/java/org/apache/commons/compress/compressors/deflate/DeflateParameters.java");
605             fileToCompare = getFile("COMPRESS-477/split_zip_created_by_zip/file_to_compare_2");
606             assertFileEqualsToEntry(fileToCompare, zipEntry, zf);
607         }
608     }
609 
610     @Test
611     void testGetEntries() throws Exception {
612         // mixed.zip contains both inflated and stored files
613         final File archive = getFile("mixed.zip");
614         zf = new ZipFile(archive);
615         final Map<String, byte[]> content = new HashMap<>();
616         for (final ZipArchiveEntry entry : Collections.list(zf.getEntries())) {
617             try (InputStream inputStream = zf.getInputStream(entry)) {
618                 content.put(entry.getName(), IOUtils.toByteArray(inputStream));
619             }
620         }
621         for (final ZipArchiveEntry entry : Collections.list(zf.getEntries())) {
622             assertAllReadMethods(content.get(entry.getName()), zf, entry);
623         }
624     }
625 
626     @Test
627     void testGetEntriesInPhysicalOrder() throws Exception {
628         // mixed.zip contains both inflated and stored files
629         final File archive = getFile("mixed.zip");
630         zf = new ZipFile(archive);
631         final Map<String, byte[]> content = new HashMap<>();
632         for (final ZipArchiveEntry entry : Collections.list(zf.getEntriesInPhysicalOrder())) {
633             try (InputStream inputStream = zf.getInputStream(entry)) {
634                 content.put(entry.getName(), IOUtils.toByteArray(inputStream));
635             }
636         }
637         for (final ZipArchiveEntry entry : Collections.list(zf.getEntries())) {
638             assertAllReadMethods(content.get(entry.getName()), zf, entry);
639         }
640     }
641 
642     /**
643      * Test non power of 2 alignment.
644      */
645     @Test
646     void testInvalidAlignment() {
647         assertThrows(IllegalArgumentException.class, () -> new ZipArchiveEntry("dummy").setAlignment(3));
648     }
649 
650     @Test
651     void testMultiByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2() throws Exception {
652         multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("bzip2-zip.zip"));
653     }
654 
655     @Test
656     void testMultiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception {
657         multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("bla.zip"));
658     }
659 
660     @Test
661     void testMultiByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64() throws Exception {
662         multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-380/COMPRESS-380.zip"));
663     }
664 
665     @Test
666     void testMultiByteReadConsistentlyReturnsMinusOneAtEofUsingExplode() throws Exception {
667         multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("imploding-8Kdict-3trees.zip"));
668     }
669 
670     @Test
671     void testMultiByteReadConsistentlyReturnsMinusOneAtEofUsingStore() throws Exception {
672         multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-264.zip"));
673     }
674 
675     @Test
676     void testMultiByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink() throws Exception {
677         multiByteReadConsistentlyReturnsMinusOneAtEof(getFile("SHRUNK.ZIP"));
678     }
679 
680     @Test
681     void testNameSourceDefaultsToName() throws Exception {
682         nameSource("bla.zip", "test1.xml", ZipArchiveEntry.NameSource.NAME);
683     }
684 
685     @Test
686     void testNameSourceIsSetToEFS() throws Exception {
687         nameSource("utf8-7zip-test.zip", "\u20AC_for_Dollar.txt", ZipArchiveEntry.NameSource.NAME_WITH_EFS_FLAG);
688     }
689 
690     @Test
691     void testNameSourceIsSetToUnicodeExtraField() throws Exception {
692         nameSource("utf8-winzip-test.zip", "\u20AC_for_Dollar.txt", ZipArchiveEntry.NameSource.UNICODE_EXTRA_FIELD);
693     }
694 
695     /**
696      * Test correct population of header and data offsets.
697      */
698     @Test
699     void testOffsets() throws Exception {
700         // mixed.zip contains both inflated and stored files
701         final File archive = getFile("mixed.zip");
702         try (ZipFile zf = new ZipFile(archive)) {
703             final ZipArchiveEntry inflatedEntry = zf.getEntry("inflated.txt");
704             assertEquals(0x0000, inflatedEntry.getLocalHeaderOffset());
705             assertEquals(0x0046, inflatedEntry.getDataOffset());
706             assertTrue(inflatedEntry.isStreamContiguous());
707             final ZipArchiveEntry storedEntry = zf.getEntry("stored.txt");
708             assertEquals(0x5892, storedEntry.getLocalHeaderOffset());
709             assertEquals(0x58d6, storedEntry.getDataOffset());
710             assertTrue(inflatedEntry.isStreamContiguous());
711         }
712     }
713 
714     @Test
715     void testPhysicalOrder() throws Exception {
716         readOrderTest();
717         final ArrayList<ZipArchiveEntry> l = Collections.list(zf.getEntriesInPhysicalOrder());
718         assertEntryName(l, 0, "AbstractUnicodeExtraField");
719         assertEntryName(l, 1, "AsiExtraField");
720         assertEntryName(l, 2, "ExtraFieldUtils");
721         assertEntryName(l, 3, "FallbackZipEncoding");
722         assertEntryName(l, 4, "GeneralPurposeBit");
723         assertEntryName(l, 5, "JarMarker");
724         assertEntryName(l, 6, "NioZipEncoding");
725         assertEntryName(l, 7, "Simple8BitZipEncoding");
726         assertEntryName(l, 8, "UnicodeCommentExtraField");
727         assertEntryName(l, 9, "UnicodePathExtraField");
728         assertEntryName(l, 10, "UnixStat");
729         assertEntryName(l, 11, "UnparseableExtraFieldData");
730         assertEntryName(l, 12, "UnrecognizedExtraField");
731         assertEntryName(l, 13, "ZipArchiveEntry");
732         assertEntryName(l, 14, "ZipArchiveInputStream");
733         assertEntryName(l, 15, "ZipArchiveOutputStream");
734         assertEntryName(l, 16, "ZipEncoding");
735         assertEntryName(l, 17, "ZipEncodingHelper");
736         assertEntryName(l, 18, "ZipExtraField");
737         assertEntryName(l, 19, "ZipFile");
738         assertEntryName(l, 20, "ZipLong");
739         assertEntryName(l, 21, "ZipShort");
740         assertEntryName(l, 22, "ZipUtil");
741     }
742 
743     @Test
744     void testPhysicalOrderOfSpecificFile() throws Exception {
745         readOrderTest();
746         final String entryName = "src/main/java/org/apache/commons/compress/archivers/zip/ZipExtraField.java";
747         final Iterable<ZipArchiveEntry> entries = zf.getEntriesInPhysicalOrder(entryName);
748         final Iterator<ZipArchiveEntry> iter = entries.iterator();
749         final ZipArchiveEntry entry = iter.next();
750 
751         assertEquals(entryName, entry.getName());
752         assertFalse(iter.hasNext());
753     }
754 
755     /**
756      * @see "https://issues.apache.org/jira/browse/COMPRESS-380"
757      */
758     @Test
759     void testReadDeflate64CompressedStream() throws Exception {
760         final File input = getFile("COMPRESS-380/COMPRESS-380-input");
761         final File archive = getFile("COMPRESS-380/COMPRESS-380.zip");
762         try (InputStream in = Files.newInputStream(input.toPath());
763                 ZipFile zf = new ZipFile(archive)) {
764             final byte[] orig = IOUtils.toByteArray(in);
765             final ZipArchiveEntry e = zf.getEntry("input2");
766             try (InputStream s = zf.getInputStream(e)) {
767                 final byte[] fromZip = IOUtils.toByteArray(s);
768                 assertArrayEquals(orig, fromZip);
769             }
770         }
771     }
772 
773     /**
774      * Test case for <a href="https://issues.apache.org/jira/browse/COMPRESS-621">COMPRESS-621</a>.
775      */
776     @Test
777     void testReadingOfExtraDataBeforeZip() throws IOException {
778         final byte[] fileHeader = "Before Zip file".getBytes(UTF_8);
779         final String entryName = "COMPRESS-621.txt";
780         final byte[] entryContent = "https://issues.apache.org/jira/browse/COMPRESS-621".getBytes(UTF_8);
781         try (ZipFile archive = new ZipFile(getFile("COMPRESS-621.zip"))) {
782             assertEquals(fileHeader.length, archive.getFirstLocalFileHeaderOffset());
783             try (InputStream input = archive.getContentBeforeFirstLocalFileHeader()) {
784                 assertArrayEquals(fileHeader, IOUtils.toByteArray(input));
785             }
786 
787             final ZipArchiveEntry e = archive.getEntry(entryName);
788             assertEquals(entryContent.length, e.getSize());
789             try (InputStream input = archive.getInputStream(e)) {
790                 assertArrayEquals(entryContent, IOUtils.toByteArray(input));
791             }
792         }
793     }
794 
795     /**
796      * Test case for <a href="https://issues.apache.org/jira/browse/COMPRESS-264">COMPRESS-264</a>.
797      */
798     @Test
799     void testReadingOfFirstStoredEntry() throws Exception {
800         final File archive = getFile("COMPRESS-264.zip");
801         zf = new ZipFile(archive);
802         final ZipArchiveEntry ze = zf.getEntry("test.txt");
803         assertEquals(5, ze.getSize());
804         try (InputStream inputStream = zf.getInputStream(ze)) {
805             assertArrayEquals(new byte[] { 'd', 'a', 't', 'a', '\n' }, IOUtils.toByteArray(inputStream));
806         }
807     }
808 
809     @Test
810     void testReadingOfStoredEntry() throws Exception {
811         final File file = createTempFile("commons-compress-zipfiletest", ".zip");
812         ZipArchiveEntry ze;
813         try (OutputStream o = Files.newOutputStream(file.toPath());
814                 ZipArchiveOutputStream zo = new ZipArchiveOutputStream(o)) {
815             ze = new ZipArchiveEntry("foo");
816             ze.setMethod(ZipEntry.STORED);
817             ze.setSize(4);
818             ze.setCrc(0xb63cfbcdL);
819             zo.putArchiveEntry(ze);
820             zo.write(new byte[] { 1, 2, 3, 4 });
821             zo.closeArchiveEntry();
822         }
823 
824         zf = new ZipFile(file);
825         ze = zf.getEntry("foo");
826         assertNotNull(ze);
827         try (InputStream i = zf.getInputStream(ze)) {
828             final byte[] b = new byte[4];
829             assertEquals(4, i.read(b));
830             assertEquals(-1, i.read());
831         }
832     }
833 
834     @Test
835     void testSelfExtractingZipUsingUnzipsfx() throws IOException, InterruptedException {
836         final File unzipsfx = new File("/usr/bin/unzipsfx");
837         Assumptions.assumeTrue(unzipsfx.exists());
838 
839         final File testZip = createTempFile("commons-compress-selfExtractZipTest", ".zip");
840 
841         final String testEntryName = "test_self_extract_zip/foo";
842         final File extractedFile = new File(testZip.getParentFile(), testEntryName);
843 
844         final byte[] testData = { 1, 2, 3, 4 };
845         final byte[] buffer = new byte[512];
846         int bytesRead;
847         try (InputStream unzipsfxInputStream = Files.newInputStream(unzipsfx.toPath())) {
848             try (OutputStream outputStream = Files.newOutputStream(testZip.toPath());
849                     ZipArchiveOutputStream zo = new ZipArchiveOutputStream(outputStream)) {
850 
851                 while ((bytesRead = unzipsfxInputStream.read(buffer)) > 0) {
852                     zo.writePreamble(buffer, 0, bytesRead);
853                 }
854 
855                 final ZipArchiveEntry ze = new ZipArchiveEntry(testEntryName);
856                 ze.setMethod(ZipEntry.STORED);
857                 ze.setSize(4);
858                 ze.setCrc(0xb63cfbcdL);
859                 zo.putArchiveEntry(ze);
860                 zo.write(testData);
861                 zo.closeArchiveEntry();
862             }
863 
864             final ProcessBuilder pbChmod = new ProcessBuilder("chmod", "+x", testZip.getPath());
865             pbChmod.redirectErrorStream(true);
866             final Process processChmod = pbChmod.start();
867             try (InputStream processInputStream = processChmod.getInputStream()) {
868                 assertEquals(0, processChmod.waitFor(), new String(IOUtils.toByteArray(processInputStream)));
869             }
870 
871             final ProcessBuilder pb = new ProcessBuilder(testZip.getPath());
872             pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
873             pb.directory(testZip.getParentFile());
874             pb.redirectErrorStream(true);
875             final Process process = pb.start();
876             final int rc = process.waitFor();
877             if (rc == OUT_OF_MEMORY && SystemUtils.IS_OS_MAC) {
878                 // On my old Mac mini, this test runs out of memory, so allow the build to continue.
879                 Assume.assumeTrue(Boolean.getBoolean("skipReturnCode137"));
880                 return;
881             }
882             try (InputStream processInputStream = process.getInputStream()) {
883                 assertEquals(0, rc, new String(IOUtils.toByteArray(processInputStream)));
884             }
885             if (!extractedFile.exists()) {
886                 // fail if extracted file does not exist
887                 fail("Can not find the extracted file");
888             }
889 
890             try (InputStream inputStream = Files.newInputStream(extractedFile.toPath())) {
891                 bytesRead = org.apache.commons.compress.utils.IOUtils.readFully(inputStream, buffer);
892                 assertEquals(testData.length, bytesRead);
893                 assertArrayEquals(testData, Arrays.copyOfRange(buffer, 0, bytesRead));
894             }
895         } finally {
896             extractedFile.delete();
897             extractedFile.getParentFile().delete();
898         }
899     }
900 
901     @Test
902     void testSetLevelTooBigForZipArchiveOutputStream() throws IOException {
903         try (ZipArchiveOutputStream fixture = new ZipArchiveOutputStream(new ByteArrayOutputStream())) {
904             assertThrows(IllegalArgumentException.class, () -> fixture.setLevel(Deflater.BEST_COMPRESSION + 1));
905         }
906     }
907 
908     @Test
909     void testSetLevelTooSmallForZipArchiveOutputStream() throws IOException {
910         try (ZipArchiveOutputStream fixture = new ZipArchiveOutputStream(new ByteArrayOutputStream())) {
911         assertThrows(IllegalArgumentException.class, () -> fixture.setLevel(Deflater.DEFAULT_COMPRESSION - 1));
912         }
913     }
914 
915     @Test
916     void testSingleByteReadConsistentlyReturnsMinusOneAtEofUsingBzip2() throws Exception {
917         singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("bzip2-zip.zip"));
918     }
919 
920     @Test
921     void testSingleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate() throws Exception {
922         singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("bla.zip"));
923     }
924 
925     @Test
926     void testSingleByteReadConsistentlyReturnsMinusOneAtEofUsingDeflate64() throws Exception {
927         singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-380/COMPRESS-380.zip"));
928     }
929 
930     @Test
931     void testSingleByteReadConsistentlyReturnsMinusOneAtEofUsingExplode() throws Exception {
932         singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("imploding-8Kdict-3trees.zip"));
933     }
934 
935     @Test
936     void testSingleByteReadConsistentlyReturnsMinusOneAtEofUsingStore() throws Exception {
937         singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("COMPRESS-264.zip"));
938     }
939 
940     @Test
941     void testSingleByteReadConsistentlyReturnsMinusOneAtEofUsingUnshrink() throws Exception {
942         singleByteReadConsistentlyReturnsMinusOneAtEof(getFile("SHRUNK.ZIP"));
943     }
944 
945     /**
946      * Test case for <a href="https://issues.apache.org/jira/browse/COMPRESS-208">COMPRESS-208</a>.
947      */
948     @Test
949     void testSkipsPK00Prefix() throws Exception {
950         final File archive = getFile("COMPRESS-208.zip");
951         zf = new ZipFile(archive);
952         assertNotNull(zf.getEntry("test1.xml"));
953         assertNotNull(zf.getEntry("test2.xml"));
954     }
955 
956     @Test
957     void testThrowsExceptionWhenWritingPreamble() throws IOException {
958         try (ZipArchiveOutputStream outputStream = new ZipArchiveOutputStream(new ByteArrayOutputStream())) {
959             outputStream.putArchiveEntry(new ZipArchiveEntry());
960             assertThrows(IllegalStateException.class, () -> outputStream.writePreamble(ByteUtils.EMPTY_BYTE_ARRAY));
961             outputStream.closeArchiveEntry();
962         }
963     }
964 
965     @Test
966     void testUnixSymlinkSampleFile() throws Exception {
967         final String entryPrefix = "COMPRESS-214_unix_symlinks/";
968         final TreeMap<String, String> expectedVals = new TreeMap<>();
969         // I threw in some Japanese characters to keep things interesting.
970         expectedVals.put(entryPrefix + "link1", "../COMPRESS-214_unix_symlinks/./a/b/c/../../../\uF999");
971         expectedVals.put(entryPrefix + "link2", "../COMPRESS-214_unix_symlinks/./a/b/c/../../../g");
972         expectedVals.put(entryPrefix + "link3", "../COMPRESS-214_unix_symlinks/././a/b/c/../../../\u76F4\u6A39");
973         expectedVals.put(entryPrefix + "link4", "\u82B1\u5B50/\u745B\u5B50");
974         expectedVals.put(entryPrefix + "\uF999", "./\u82B1\u5B50/\u745B\u5B50/\u5897\u8C37/\uF999");
975         expectedVals.put(entryPrefix + "g", "./a/b/c/d/e/f/g");
976         expectedVals.put(entryPrefix + "\u76F4\u6A39", "./g");
977         // Notice how a directory link might contain a trailing slash, or it might not.
978         // Also note: symlinks are always stored as files, even if they link to directories.
979         expectedVals.put(entryPrefix + "link5", "../COMPRESS-214_unix_symlinks/././a/b");
980         expectedVals.put(entryPrefix + "link6", "../COMPRESS-214_unix_symlinks/././a/b/");
981         // I looked into creating a test with hard links, but ZIP does not appear to
982         // support hard links, so nevermind.
983         final File archive = getFile("COMPRESS-214_unix_symlinks.zip");
984         zf = new ZipFile(archive);
985         zf.stream().forEach(zae -> {
986             final String link = zf.getUnixSymlink(zae);
987             if (zae.isUnixSymlink()) {
988                 final String name = zae.getName();
989                 final String expected = expectedVals.get(name);
990                 assertEquals(expected, link);
991             } else {
992                 // Should be null if it's not a symlink!
993                 assertNull(link);
994             }
995         });
996     }
997 
998     @Test
999     void testUnshrinking() throws Exception {
1000         zf = new ZipFile(getFile("SHRUNK.ZIP"));
1001         ZipArchiveEntry test = zf.getEntry("TEST1.XML");
1002         try (InputStream original = newInputStream("test1.xml");
1003                 InputStream inputStream = zf.getInputStream(test)) {
1004             assertArrayEquals(IOUtils.toByteArray(original), IOUtils.toByteArray(inputStream));
1005         }
1006         test = zf.getEntry("TEST2.XML");
1007         try (InputStream original = newInputStream("test2.xml");
1008                 InputStream inputStream = zf.getInputStream(test)) {
1009             assertArrayEquals(IOUtils.toByteArray(original), IOUtils.toByteArray(inputStream));
1010         }
1011     }
1012 
1013     @Test
1014     void testUnzipBZip2CompressedEntry() throws Exception {
1015         final File archive = getFile("bzip2-zip.zip");
1016         zf = new ZipFile(archive);
1017         final ZipArchiveEntry ze = zf.getEntry("lots-of-as");
1018         assertEquals(42, ze.getSize());
1019         final byte[] expected = ArrayFill.fill(new byte[42], (byte) 'a');
1020         try (InputStream inputStream = zf.getInputStream(ze)) {
1021             assertArrayEquals(expected, IOUtils.toByteArray(inputStream));
1022         }
1023     }
1024 
1025     /**
1026      * @see "https://issues.apache.org/jira/browse/COMPRESS-176"
1027      */
1028     @Test
1029     void testWinzipBackSlashWorkaround() throws Exception {
1030         final File archive = getFile("test-winzip.zip");
1031         zf = new ZipFile(archive);
1032         assertNull(zf.getEntry("\u00e4\\\u00fc.txt"));
1033         assertNotNull(zf.getEntry("\u00e4/\u00fc.txt"));
1034     }
1035 
1036     @Test
1037     void testZipWithShortBeginningGarbage() throws IOException {
1038         final Path path = createTempPath("preamble", ".zip");
1039         try (OutputStream fos = Files.newOutputStream(path)) {
1040             fos.write("#!/usr/bin/unzip\n".getBytes(StandardCharsets.UTF_8));
1041             try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(fos)) {
1042                 final ZipArchiveEntry entry = new ZipArchiveEntry("file-1.txt");
1043                 entry.setMethod(ZipEntry.DEFLATED);
1044                 zos.putArchiveEntry(entry);
1045                 zos.writeUtf8("entry-content\n");
1046                 zos.closeArchiveEntry();
1047             }
1048         }
1049         try (ZipFile zipFile = ZipFile.builder().setPath(path).get()) {
1050             final ZipArchiveEntry entry = zipFile.getEntry("file-1.txt");
1051             assertEquals("file-1.txt", entry.getName());
1052             try (InputStream inputStream = zipFile.getInputStream(entry)) {
1053                 final byte[] content = IOUtils.toByteArray(inputStream);
1054                 assertArrayEquals("entry-content\n".getBytes(StandardCharsets.UTF_8), content);
1055             }
1056         }
1057     }
1058 
1059 }