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