1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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
119 try (InputStream stream = zf.getInputStream(entry)) {
120 final byte[] full = IOUtils.toByteArray(stream);
121 assertArrayEquals(expected, full);
122 }
123
124
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
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
203
204
205
206
207
208
209 private void readOrderTest() throws Exception {
210 zf = ZipFile.builder().setFile(getFile("ordertest.zip")).get();
211 }
212
213
214
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
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
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
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
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
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
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
548
549 @Test
550 void testExcessDataInZip64ExtraField() throws Exception {
551 final File archive = getFile("COMPRESS-228.zip");
552 zf = new ZipFile(archive);
553
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
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
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
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
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
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
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
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
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
697
698 @Test
699 void testOffsets() throws Exception {
700
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
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
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
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
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
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
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
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
978
979 expectedVals.put(entryPrefix + "link5", "../COMPRESS-214_unix_symlinks/././a/b");
980 expectedVals.put(entryPrefix + "link6", "../COMPRESS-214_unix_symlinks/././a/b/");
981
982
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
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
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 }