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