1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.compress.archivers.sevenz;
20
21 import static java.nio.charset.StandardCharsets.UTF_16LE;
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.assertEquals;
25 import static org.junit.jupiter.api.Assertions.assertFalse;
26 import static org.junit.jupiter.api.Assertions.assertNotEquals;
27 import static org.junit.jupiter.api.Assertions.assertNotNull;
28 import static org.junit.jupiter.api.Assertions.assertNull;
29 import static org.junit.jupiter.api.Assertions.assertThrows;
30 import static org.junit.jupiter.api.Assertions.assertTrue;
31
32 import java.io.ByteArrayOutputStream;
33 import java.io.File;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.nio.file.Files;
37 import java.nio.file.Path;
38 import java.nio.file.attribute.FileTime;
39 import java.security.NoSuchAlgorithmException;
40 import java.time.Instant;
41 import java.util.ArrayList;
42 import java.util.Arrays;
43 import java.util.Collections;
44 import java.util.Date;
45 import java.util.HashMap;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Random;
50 import java.util.function.Function;
51
52 import javax.crypto.Cipher;
53
54 import org.apache.commons.compress.AbstractTest;
55 import org.apache.commons.compress.MemoryLimitException;
56 import org.apache.commons.compress.PasswordRequiredException;
57 import org.apache.commons.compress.utils.MultiReadOnlySeekableByteChannel;
58 import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
59 import org.apache.commons.io.IOUtils;
60 import org.junit.jupiter.api.Test;
61
62 class SevenZFileTest extends AbstractTest {
63 private static final String TEST2_CONTENT = "<?xml version = '1.0'?>\r\n<!DOCTYPE connections>\r\n<meinxml>\r\n\t<leer />\r\n</meinxml>\n";
64
65 private static boolean isStrongCryptoAvailable() throws NoSuchAlgorithmException {
66 return Cipher.getMaxAllowedKeyLength("AES/ECB/PKCS5Padding") >= 256;
67 }
68
69 private void assertDate(final SevenZArchiveEntry entry, final String value, final Function<SevenZArchiveEntry, Boolean> hasValue,
70 final Function<SevenZArchiveEntry, FileTime> timeFunction, final Function<SevenZArchiveEntry, Date> dateFunction) {
71 if (value != null) {
72 assertTrue(hasValue.apply(entry));
73 final Instant parsedInstant = Instant.parse(value);
74 final FileTime parsedFileTime = FileTime.from(parsedInstant);
75 assertEquals(parsedFileTime, timeFunction.apply(entry));
76 assertEquals(Date.from(parsedInstant), dateFunction.apply(entry));
77 } else {
78 assertFalse(hasValue.apply(entry));
79 assertThrows(UnsupportedOperationException.class, () -> timeFunction.apply(entry));
80 assertThrows(UnsupportedOperationException.class, () -> dateFunction.apply(entry));
81 }
82 }
83
84 private void assertDates(final SevenZArchiveEntry entry, final String modified, final String access, final String creation) {
85 assertDate(entry, modified, SevenZArchiveEntry::getHasLastModifiedDate, SevenZArchiveEntry::getLastModifiedTime,
86 SevenZArchiveEntry::getLastModifiedDate);
87 assertDate(entry, access, SevenZArchiveEntry::getHasAccessDate, SevenZArchiveEntry::getAccessTime, SevenZArchiveEntry::getAccessDate);
88 assertDate(entry, creation, SevenZArchiveEntry::getHasCreationDate, SevenZArchiveEntry::getCreationTime, SevenZArchiveEntry::getCreationDate);
89 }
90
91 private void checkHelloWorld(final String fileName) throws Exception {
92 try (SevenZFile sevenZFile = getSevenZFile(fileName)) {
93 final SevenZArchiveEntry entry = sevenZFile.getNextEntry();
94 assertEquals("Hello world.txt", entry.getName());
95 assertDates(entry, "2013-05-07T19:40:48Z", null, null);
96 final byte[] contents = new byte[(int) entry.getSize()];
97 int off = 0;
98 while (off < contents.length) {
99 final int bytesRead = sevenZFile.read(contents, off, contents.length - off);
100 assertTrue(bytesRead >= 0, "bytesRead exp: >=0, act: " + bytesRead);
101 off += bytesRead;
102 }
103 assertEquals("Hello, world!\n", new String(contents, UTF_8));
104 assertNull(sevenZFile.getNextEntry());
105 }
106 }
107
108 private SevenZFile getSevenZFile(final String specialPath) throws IOException {
109 return SevenZFile.builder().setFile(getFile(specialPath)).get();
110 }
111
112 private byte[] read(final SevenZFile sevenZFile, final SevenZArchiveEntry entry) throws IOException {
113 try (InputStream inputStream = sevenZFile.getInputStream(entry)) {
114 return IOUtils.toByteArray(inputStream);
115 }
116 }
117
118 private byte[] readFully(final SevenZFile archive) throws IOException {
119 final byte[] buf = new byte[1024];
120 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
121 for (int len = 0; (len = archive.read(buf)) > 0;) {
122 baos.write(buf, 0, len);
123 }
124 return baos.toByteArray();
125 }
126
127 @Test
128 void test7zDecryptUnarchive() throws Exception {
129 if (isStrongCryptoAvailable()) {
130 test7zUnarchive(getFile("bla.encrypted.7z"), SevenZMethod.LZMA,
131 "foo".getBytes(UTF_16LE));
132 }
133 }
134
135 @Test
136 void test7zDecryptUnarchiveUsingCharArrayPassword() throws Exception {
137 if (isStrongCryptoAvailable()) {
138 test7zUnarchive(getFile("bla.encrypted.7z"), SevenZMethod.LZMA,
139 "foo".toCharArray());
140 }
141 }
142
143 @Test
144 void test7zDeflate64Unarchive() throws Exception {
145 test7zUnarchive(getFile("bla.deflate64.7z"), SevenZMethod.DEFLATE64);
146 }
147
148 @Test
149 void test7zDeflateUnarchive() throws Exception {
150 test7zUnarchive(getFile("bla.deflate.7z"), SevenZMethod.DEFLATE);
151 }
152
153 @Test
154 void test7zMultiVolumeUnarchive() throws Exception {
155 try (@SuppressWarnings("deprecation")
156 SevenZFile sevenZFile = new SevenZFile(MultiReadOnlySeekableByteChannel.forFiles(getFile("bla-multi.7z.001"), getFile("bla-multi.7z.002")))) {
157 test7zUnarchive(sevenZFile, SevenZMethod.LZMA2);
158 }
159 try (SevenZFile sevenZFile = SevenZFile.builder()
160 .setSeekableByteChannel(MultiReadOnlySeekableByteChannel.forFiles(getFile("bla-multi.7z.001"), getFile("bla-multi.7z.002"))).get()) {
161 test7zUnarchive(sevenZFile, SevenZMethod.LZMA2);
162 }
163 }
164
165 @Test
166 void test7zUnarchive() throws Exception {
167 test7zUnarchive(getFile("bla.7z"), SevenZMethod.LZMA);
168 }
169
170 private void test7zUnarchive(final File file, final SevenZMethod method) throws Exception {
171 test7zUnarchive(file, method, false);
172 }
173
174 private void test7zUnarchive(final File file, final SevenZMethod method, final boolean tryToRecoverBrokenArchives) throws Exception {
175 test7zUnarchive(file, method, (char[]) null, tryToRecoverBrokenArchives);
176 }
177
178 private void test7zUnarchive(final File file, final SevenZMethod method, final byte[] password) throws Exception {
179 try (@SuppressWarnings("deprecation")
180 SevenZFile sevenZFile = new SevenZFile(file, password)) {
181 test7zUnarchive(sevenZFile, method);
182 }
183 try (SevenZFile sevenZFile = SevenZFile.builder().setFile(file).setPassword(password).get()) {
184 test7zUnarchive(sevenZFile, method);
185 }
186 }
187
188 private void test7zUnarchive(final File file, final SevenZMethod m, final char[] password) throws Exception {
189 test7zUnarchive(file, m, password, false);
190 }
191
192 private void test7zUnarchive(final File file, final SevenZMethod m, final char[] password, final boolean tryToRecoverBrokenArchives) throws Exception {
193 try (@SuppressWarnings("deprecation")
194 SevenZFile sevenZFile = new SevenZFile(file, password,
195 SevenZFileOptions.builder().withTryToRecoverBrokenArchives(tryToRecoverBrokenArchives).build())) {
196 test7zUnarchive(sevenZFile, m);
197 }
198 try (SevenZFile sevenZFile = SevenZFile.builder().setFile(file).setPassword(password).setTryToRecoverBrokenArchives(tryToRecoverBrokenArchives).get()) {
199 test7zUnarchive(sevenZFile, m);
200 }
201 }
202
203 private void test7zUnarchive(final SevenZFile sevenZFile, final SevenZMethod m) throws Exception {
204 SevenZArchiveEntry entry = sevenZFile.getNextEntry();
205 assertEquals("test1.xml", entry.getName());
206 assertDates(entry, "2007-11-14T10:19:02Z", null, null);
207 assertEquals(m, entry.getContentMethods().iterator().next().getMethod());
208 entry = sevenZFile.getNextEntry();
209 assertEquals("test2.xml", entry.getName());
210 assertDates(entry, "2007-11-14T10:19:02Z", null, null);
211 assertEquals(m, entry.getContentMethods().iterator().next().getMethod());
212 final byte[] contents = new byte[(int) entry.getSize()];
213 int off = 0;
214 while (off < contents.length) {
215 final int bytesRead = sevenZFile.read(contents, off, contents.length - off);
216 assertTrue(bytesRead >= 0, "bytesRead exp: >=0, act: " + bytesRead);
217 off += bytesRead;
218 }
219 assertEquals(TEST2_CONTENT, new String(contents, UTF_8));
220 assertNull(sevenZFile.getNextEntry());
221 }
222
223 @Test
224 void test7zUnarchiveWithDefectHeader() throws Exception {
225 test7zUnarchive(getFile("bla.noendheaderoffset.7z"), SevenZMethod.LZMA, true);
226 }
227
228 @Test
229 void test7zUnarchiveWithDefectHeaderFailsByDefault() throws Exception {
230 assertThrows(IOException.class, () -> test7zUnarchive(getFile("bla.noendheaderoffset.7z"), SevenZMethod.LZMA));
231 }
232
233 @Test
234 void testAllEmptyFilesArchive() throws Exception {
235 try (SevenZFile archive = getSevenZFile("7z-empty-mhc-off.7z")) {
236 final SevenZArchiveEntry e = archive.getNextEntry();
237 assertNotNull(e);
238 assertEquals("empty", e.getName());
239 assertDates(e, "2013-05-14T17:50:19Z", null, null);
240 assertNull(archive.getNextEntry());
241 }
242 }
243
244
245
246
247 @Test
248 void testCompressedHeaderWithNonDefaultDictionarySize() throws Exception {
249 try (SevenZFile sevenZFile = getSevenZFile("COMPRESS-256.7z")) {
250 int count = 0;
251 while (sevenZFile.getNextEntry() != null) {
252 count++;
253 }
254 assertEquals(446, count);
255 }
256 }
257
258 @Test
259 void testEncryptedArchiveRequiresPassword() throws Exception {
260 final PasswordRequiredException ex = assertThrows(PasswordRequiredException.class, () -> getSevenZFile("bla.encrypted.7z").close(),
261 "shouldn't decrypt without a password");
262 final String msg = ex.getMessage();
263 assertTrue(msg.startsWith("Cannot read encrypted content from "), "Should start with whining about being unable to decrypt");
264 assertTrue(msg.endsWith(" without a password."), "Should finish the sentence properly");
265 assertTrue(msg.contains("bla.encrypted.7z"), "Should contain archive's name");
266 }
267
268 @Test
269 void testExtractNonExistSpecifiedFile() throws Exception {
270 try (SevenZFile sevenZFile = getSevenZFile("COMPRESS-256.7z");
271 SevenZFile anotherSevenZFile = getSevenZFile("bla.7z")) {
272 for (final SevenZArchiveEntry nonExistEntry : anotherSevenZFile.getEntries()) {
273 assertThrows(IllegalArgumentException.class, () -> sevenZFile.getInputStream(nonExistEntry));
274 }
275 }
276 }
277
278 @Test
279 void testExtractSpecifiedFile() throws Exception {
280 try (SevenZFile sevenZFile = getSevenZFile("COMPRESS-256.7z")) {
281 final String testTxtContents = "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
282 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
283 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
284 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011";
285
286 for (final SevenZArchiveEntry entry : sevenZFile.getEntries()) {
287 if (entry.getName().equals("commons-compress-1.7-src/src/test/resources/test.txt")) {
288 final byte[] contents = new byte[(int) entry.getSize()];
289 int off = 0;
290 final InputStream inputStream = sevenZFile.getInputStream(entry);
291 while (off < contents.length) {
292 final int bytesRead = inputStream.read(contents, off, contents.length - off);
293 assertTrue(bytesRead >= 0, "bytesRead exp: >=0, act: " + bytesRead);
294 off += bytesRead;
295 }
296 assertEquals(testTxtContents, new String(contents, UTF_8));
297 break;
298 }
299 }
300 }
301 }
302
303 @Test
304 void testExtractSpecifiedFileDeprecated() throws Exception {
305 try (@SuppressWarnings("deprecation")
306 SevenZFile sevenZFile = new SevenZFile(getFile("COMPRESS-256.7z"))) {
307 final String testTxtContents = "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
308 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
309 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
310 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011";
311
312 for (final SevenZArchiveEntry entry : sevenZFile.getEntries()) {
313 if (entry.getName().equals("commons-compress-1.7-src/src/test/resources/test.txt")) {
314 final byte[] contents = new byte[(int) entry.getSize()];
315 int off = 0;
316 final InputStream inputStream = sevenZFile.getInputStream(entry);
317 while (off < contents.length) {
318 final int bytesRead = inputStream.read(contents, off, contents.length - off);
319 assertTrue(bytesRead >= 0, "bytesRead exp: >=0, act: " + bytesRead);
320 off += bytesRead;
321 }
322 assertEquals(testTxtContents, new String(contents, UTF_8));
323 break;
324 }
325 }
326 }
327 }
328
329 @Test
330 void testGetDefaultName() throws Exception {
331 try (SevenZFile sevenZFile = getSevenZFile("bla.deflate64.7z")) {
332 assertEquals("bla.deflate64", sevenZFile.getDefaultName());
333 }
334 try (SevenZFile sevenZFile = SevenZFile.builder().setSeekableByteChannel(Files.newByteChannel(getFile("bla.deflate64.7z").toPath())).get()) {
335 assertNull(sevenZFile.getDefaultName());
336 }
337 try (@SuppressWarnings("deprecation")
338 SevenZFile sevenZFile = new SevenZFile(Files.newByteChannel(getFile("bla.deflate64.7z").toPath()), "foo")) {
339 assertEquals("foo~", sevenZFile.getDefaultName());
340 }
341 try (SevenZFile sevenZFile = SevenZFile.builder().setSeekableByteChannel(Files.newByteChannel(getFile("bla.deflate64.7z").toPath()))
342 .setDefaultName("foo").get()) {
343 assertEquals("foo~", sevenZFile.getDefaultName());
344 }
345 try (SevenZFile sevenZFile = SevenZFile.builder().setSeekableByteChannel(Files.newByteChannel(getFile("bla.deflate64.7z").toPath()))
346 .setDefaultName(".foo").get()) {
347 assertEquals(".foo~", sevenZFile.getDefaultName());
348 }
349 }
350
351 @Test
352 void testGetEntriesOfUnarchiveInMemoryTest() throws IOException {
353 final byte[] data = readAllBytes("bla.7z");
354 try (SevenZFile sevenZFile = SevenZFile.builder().setSeekableByteChannel(new SeekableInMemoryByteChannel(data)).get()) {
355 final Iterable<SevenZArchiveEntry> entries = sevenZFile.getEntries();
356 final Iterator<SevenZArchiveEntry> iter = entries.iterator();
357 SevenZArchiveEntry entry = iter.next();
358 assertEquals("test1.xml", entry.getName());
359 entry = iter.next();
360 assertEquals("test2.xml", entry.getName());
361 assertFalse(iter.hasNext());
362 }
363 }
364
365 @Test
366 void testGetEntriesOfUnarchiveTest() throws IOException {
367 try (SevenZFile sevenZFile = getSevenZFile("bla.7z")) {
368 final Iterable<SevenZArchiveEntry> entries = sevenZFile.getEntries();
369 final Iterator<SevenZArchiveEntry> iter = entries.iterator();
370 SevenZArchiveEntry entry = iter.next();
371 assertEquals("test1.xml", entry.getName());
372 entry = iter.next();
373 assertEquals("test2.xml", entry.getName());
374 assertFalse(iter.hasNext());
375 }
376 }
377
378 @Test
379 void testGivenNameWinsOverDefaultName() throws Exception {
380 try (@SuppressWarnings("deprecation")
381 SevenZFile sevenZFile = new SevenZFile(getFile("bla.7z"), SevenZFileOptions.builder().withUseDefaultNameForUnnamedEntries(true).build())) {
382 SevenZArchiveEntry ae = sevenZFile.getNextEntry();
383 assertNotNull(ae);
384 assertEquals("test1.xml", ae.getName());
385 ae = sevenZFile.getNextEntry();
386 assertNotNull(ae);
387 assertEquals("test2.xml", ae.getName());
388 assertNull(sevenZFile.getNextEntry());
389 }
390 try (SevenZFile sevenZFile = SevenZFile.builder().setFile(getFile("bla.7z")).setUseDefaultNameForUnnamedEntries(true).get()) {
391 SevenZArchiveEntry ae = sevenZFile.getNextEntry();
392 assertNotNull(ae);
393 assertEquals("test1.xml", ae.getName());
394 ae = sevenZFile.getNextEntry();
395 assertNotNull(ae);
396 assertEquals("test2.xml", ae.getName());
397 assertNull(sevenZFile.getNextEntry());
398 }
399 }
400
401
402
403
404 @Test
405 void testHandlesEmptyArchiveWithFilesInfo() throws Exception {
406 final File file = newTempFile("empty.7z");
407 try (SevenZOutputFile s = new SevenZOutputFile(file)) {
408
409 }
410 try (SevenZFile z = SevenZFile.builder().setFile(file).get()) {
411 assertFalse(z.getEntries().iterator().hasNext());
412 assertNull(z.getNextEntry());
413 }
414 }
415
416
417
418
419 @Test
420 void testHandlesEmptyArchiveWithoutFilesInfo() throws Exception {
421 try (SevenZFile z = getSevenZFile("COMPRESS-492.7z")) {
422 assertFalse(z.getEntries().iterator().hasNext());
423 assertNull(z.getNextEntry());
424 }
425 }
426
427 @Test
428 void testHelloWorldHeaderCompressionOffCopy() throws Exception {
429 checkHelloWorld("7z-hello-mhc-off-copy.7z");
430 }
431
432 @Test
433 void testHelloWorldHeaderCompressionOffLZMA2() throws Exception {
434 checkHelloWorld("7z-hello-mhc-off-lzma2.7z");
435 }
436
437 @Test
438 void testLimitExtractionMemory() {
439 assertThrows(MemoryLimitException.class, () -> {
440 try (SevenZFile sevenZFile = SevenZFile.builder().setFile(getFile("bla.7z")).setMaxMemoryLimitKb(1).get()) {
441
442 }
443 });
444 assertThrows(MemoryLimitException.class, () -> {
445 try (SevenZFile sevenZFile = SevenZFile.builder().setFile(getFile("bla.7z")).setMaxMemoryLimitKiB(1).get()) {
446
447 }
448 });
449 }
450
451 @Test
452 void testNoNameCanBeReplacedByDefaultName() throws Exception {
453 try (SevenZFile sevenZFile = SevenZFile.builder().setFile(getFile("bla-nonames.7z")).setUseDefaultNameForUnnamedEntries(true).get()) {
454 SevenZArchiveEntry ae = sevenZFile.getNextEntry();
455 assertNotNull(ae);
456 assertEquals("bla-nonames", ae.getName());
457 ae = sevenZFile.getNextEntry();
458 assertNotNull(ae);
459 assertEquals("bla-nonames", ae.getName());
460 assertNull(sevenZFile.getNextEntry());
461 }
462 }
463
464 @Test
465 void testNoNameMeansNoNameByDefault() throws Exception {
466 try (SevenZFile sevenZFile = getSevenZFile("bla-nonames.7z")) {
467 SevenZArchiveEntry ae = sevenZFile.getNextEntry();
468 assertNotNull(ae);
469 assertNull(ae.getName());
470 ae = sevenZFile.getNextEntry();
471 assertNotNull(ae);
472 assertNull(ae.getName());
473 assertNull(sevenZFile.getNextEntry());
474 }
475 }
476
477 @Test
478 void testNoOOMOnCorruptedHeader() throws IOException {
479 final List<Path> testFiles = new ArrayList<>();
480 testFiles.add(getPath("COMPRESS-542-1.7z"));
481 testFiles.add(getPath("COMPRESS-542-2.7z"));
482 testFiles.add(getPath("COMPRESS-542-endheadercorrupted.7z"));
483 testFiles.add(getPath("COMPRESS-542-endheadercorrupted2.7z"));
484 for (final Path file : testFiles) {
485 {
486 final IOException e = assertThrows(IOException.class, () -> {
487 try (@SuppressWarnings("deprecation")
488 SevenZFile sevenZFile = new SevenZFile(Files.newByteChannel(file),
489 SevenZFileOptions.builder().withTryToRecoverBrokenArchives(true).build())) {
490
491 }
492 }, "Expected IOException: start header corrupt and unable to guess end header");
493 assertEquals("Start header corrupt and unable to guess end header", e.getMessage());
494 }
495 {
496 final IOException e = assertThrows(IOException.class, () -> {
497 try (SevenZFile sevenZFile = SevenZFile.builder().setSeekableByteChannel(Files.newByteChannel(file)).setTryToRecoverBrokenArchives(true)
498 .get()) {
499
500 }
501 }, "Expected IOException: start header corrupt and unable to guess end header");
502 assertEquals("Start header corrupt and unable to guess end header", e.getMessage());
503 }
504 }
505 }
506
507 @Test
508 void testRandomAccessMultipleReadSameFile() throws Exception {
509 try (SevenZFile sevenZFile = getSevenZFile("COMPRESS-256.7z")) {
510 final String testTxtContents = "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
511 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
512 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
513 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011";
514
515 SevenZArchiveEntry entry;
516 SevenZArchiveEntry testTxtEntry = null;
517 while ((entry = sevenZFile.getNextEntry()) != null) {
518 if (entry.getName().equals("commons-compress-1.7-src/src/test/resources/test.txt")) {
519 testTxtEntry = entry;
520 break;
521 }
522 }
523
524 assertNotNull(testTxtEntry, "testTxtEntry");
525 final byte[] contents = new byte[(int) testTxtEntry.getSize()];
526 int numberOfReads = 10;
527 while (numberOfReads-- > 0) {
528 try (InputStream inputStream = sevenZFile.getInputStream(testTxtEntry)) {
529 int off = 0;
530 while (off < contents.length) {
531 final int bytesRead = inputStream.read(contents, off, contents.length - off);
532 assertTrue(bytesRead >= 0, "bytesRead exp: >=0, act: " + bytesRead);
533 off += bytesRead;
534 }
535 assertEquals(SevenZMethod.LZMA2, testTxtEntry.getContentMethods().iterator().next().getMethod());
536 assertEquals(testTxtContents, new String(contents, UTF_8));
537 }
538 }
539 }
540 }
541
542 @Test
543 void testRandomAccessTogetherWithSequentialAccess() throws Exception {
544 try (SevenZFile sevenZFile = getSevenZFile("COMPRESS-256.7z")) {
545
546 final String testTxtContents = "111111111111111111111111111000101011\n"
547 + "111111111111111111111111111000101011\n"
548 + "111111111111111111111111111000101011\n"
549 + "111111111111111111111111111000101011\n"
550 + "111111111111111111111111111000101011\n"
551 + "111111111111111111111111111000101011\n"
552 + "111111111111111111111111111000101011\n"
553 + "111111111111111111111111111000101011\n"
554 + "111111111111111111111111111000101011\n"
555 + "111111111111111111111111111000101011";
556 final String filesTxtContents = "0xxxxxxxxx10xxxxxxxx20xxxxxxxx30xxxxxxxx40xxxxxxxx50xxxxxxxx60xxxxxxxx70xxxxxxxx80xxxxxxxx90xxxxxxxx100"
557 + "xxxxxxx110xxxxxxx120xxxxxxx130xxxxxxx -> 0yyyyyyyyy10yyyyyyyy20yyyyyyyy30yyyyyyyy40yyyyyyyy50yyyyyyyy60yyyyyyyy70yyyyyyyy80"
558 + "yyyyyyyy90yyyyyyyy100yyyyyyy110yyyyyyy120yyyyyyy130yyyyyyy\n";
559
560 int off;
561 byte[] contents;
562
563
564 sevenZFile.getNextEntry();
565 SevenZArchiveEntry nextEntry = sevenZFile.getNextEntry();
566 contents = new byte[(int) nextEntry.getSize()];
567 off = 0;
568
569 assertEquals(SevenZMethod.LZMA2, nextEntry.getContentMethods().iterator().next().getMethod());
570
571
572 while (off < contents.length) {
573 final int bytesRead = sevenZFile.read(contents, off, contents.length - off);
574 assertTrue(bytesRead >= 0, "bytesRead exp: >=0, act: " + bytesRead);
575 off += bytesRead;
576 }
577
578 sevenZFile.getNextEntry();
579 sevenZFile.getNextEntry();
580
581 for (final SevenZArchiveEntry entry : sevenZFile.getEntries()) {
582
583 if (entry.getName().equals("commons-compress-1.7-src/src/test/resources/longsymlink/files.txt")) {
584 contents = new byte[(int) entry.getSize()];
585 off = 0;
586 final InputStream inputStream = sevenZFile.getInputStream(entry);
587 while (off < contents.length) {
588 final int bytesRead = inputStream.read(contents, off, contents.length - off);
589 assertTrue(bytesRead >= 0, "bytesRead exp: >=0, act: " + bytesRead);
590 off += bytesRead;
591 }
592 assertEquals(SevenZMethod.LZMA2, entry.getContentMethods().iterator().next().getMethod());
593 assertEquals(filesTxtContents, new String(contents, UTF_8));
594 break;
595 }
596 }
597
598
599 nextEntry = sevenZFile.getNextEntry();
600 while (!nextEntry.getName().equals("commons-compress-1.7-src/src/test/resources/test.txt")) {
601 nextEntry = sevenZFile.getNextEntry();
602 }
603
604 contents = new byte[(int) nextEntry.getSize()];
605 off = 0;
606 while (off < contents.length) {
607 final int bytesRead = sevenZFile.read(contents, off, contents.length - off);
608 assertTrue(bytesRead >= 0, "bytesRead exp: >=0, act: " + bytesRead);
609 off += bytesRead;
610 }
611 assertEquals(SevenZMethod.LZMA2, nextEntry.getContentMethods().iterator().next().getMethod());
612 assertEquals(testTxtContents, new String(contents, UTF_8));
613 }
614 }
615
616 @Test
617 void testRandomAccessWhenJumpingBackwards() throws Exception {
618 try (SevenZFile sevenZFile = getSevenZFile("COMPRESS-256.7z")) {
619 final String testTxtContents = "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
620 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
621 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
622 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011";
623
624 SevenZArchiveEntry entry;
625 SevenZArchiveEntry testTxtEntry = null;
626 while ((entry = sevenZFile.getNextEntry()) != null) {
627 if (entry.getName().equals("commons-compress-1.7-src/src/test/resources/test.txt")) {
628 testTxtEntry = entry;
629 break;
630 }
631 }
632
633
634 final SevenZArchiveEntry entryAfterTestTxtEntry = sevenZFile.getNextEntry();
635 final byte[] entryAfterTestTxtEntryContents = new byte[(int) entryAfterTestTxtEntry.getSize()];
636 int off = 0;
637 while (off < entryAfterTestTxtEntryContents.length) {
638 final int bytesRead = sevenZFile.read(entryAfterTestTxtEntryContents, off, entryAfterTestTxtEntryContents.length - off);
639 assertTrue(bytesRead >= 0, "bytesRead exp: >=0, act: " + bytesRead);
640 off += bytesRead;
641 }
642
643
644 assertNotNull(testTxtEntry, "testTxtEntry");
645 final byte[] contents = new byte[(int) testTxtEntry.getSize()];
646 try (InputStream inputStream = sevenZFile.getInputStream(testTxtEntry)) {
647 off = 0;
648 while (off < contents.length) {
649 final int bytesRead = inputStream.read(contents, off, contents.length - off);
650 assertTrue(bytesRead >= 0, "bytesRead exp: >=0, act: " + bytesRead);
651 off += bytesRead;
652 }
653 assertEquals(SevenZMethod.LZMA2, testTxtEntry.getContentMethods().iterator().next().getMethod());
654 assertEquals(testTxtContents, new String(contents, UTF_8));
655 }
656
657
658 final SevenZArchiveEntry nextTestTxtEntry = sevenZFile.getNextEntry();
659 final byte[] nextTestContents = new byte[(int) nextTestTxtEntry.getSize()];
660 off = 0;
661 while (off < nextTestContents.length) {
662 final int bytesRead = sevenZFile.read(nextTestContents, off, nextTestContents.length - off);
663 assertTrue(bytesRead >= 0, "bytesRead exp: >=0, act: " + bytesRead);
664 off += bytesRead;
665 }
666
667 assertEquals(nextTestTxtEntry.getName(), entryAfterTestTxtEntry.getName());
668 assertEquals(nextTestTxtEntry.getSize(), entryAfterTestTxtEntry.getSize());
669 assertArrayEquals(nextTestContents, entryAfterTestTxtEntryContents);
670 }
671 }
672
673 @Test
674 void testRandomAccessWhenJumpingForwards() throws Exception {
675 try (SevenZFile sevenZFile = getSevenZFile("COMPRESS-256.7z")) {
676 final String testTxtContents = "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
677 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
678 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011\n"
679 + "111111111111111111111111111000101011\n" + "111111111111111111111111111000101011";
680
681 SevenZArchiveEntry testTxtEntry = null;
682 final Iterable<SevenZArchiveEntry> entries = sevenZFile.getEntries();
683 for (final SevenZArchiveEntry Entry : entries) {
684 testTxtEntry = Entry;
685 if (testTxtEntry.getName().equals("commons-compress-1.7-src/src/test/resources/test.txt")) {
686 break;
687 }
688 }
689 final SevenZArchiveEntry firstEntry = sevenZFile.getNextEntry();
690
691 byte[] contents = new byte[(int) firstEntry.getSize() / 2];
692 sevenZFile.read(contents);
693
694
695 sevenZFile.getNextEntry();
696 final SevenZArchiveEntry thirdEntry = sevenZFile.getNextEntry();
697 contents = new byte[(int) thirdEntry.getSize() / 2];
698 sevenZFile.read(contents);
699
700
701 assertNotNull(testTxtEntry, "testTxtEntry");
702 contents = new byte[(int) testTxtEntry.getSize()];
703 int numberOfReads = 10;
704 while (numberOfReads-- > 0) {
705 try (InputStream inputStream = sevenZFile.getInputStream(testTxtEntry)) {
706 int off = 0;
707 while (off < contents.length) {
708 final int bytesRead = inputStream.read(contents, off, contents.length - off);
709 assertTrue(bytesRead >= 0, "bytesRead exp: >=0, act: " + bytesRead);
710 off += bytesRead;
711 }
712 assertEquals(SevenZMethod.LZMA2, testTxtEntry.getContentMethods().iterator().next().getMethod());
713 assertEquals(testTxtContents, new String(contents, UTF_8));
714 }
715 }
716 }
717 }
718
719
720 @Test
721 void testRandomlySkippingEntries() throws Exception {
722
723 final Map<String, byte[]> entriesByName = new HashMap<>();
724 try (SevenZFile archive = getSevenZFile("COMPRESS-320/Copy.7z")) {
725 SevenZArchiveEntry entry;
726 while ((entry = archive.getNextEntry()) != null) {
727 if (entry.hasStream()) {
728 entriesByName.put(entry.getName(), readFully(archive));
729 }
730 }
731 }
732
733 final String[] variants = { "BZip2-solid.7z", "BZip2.7z", "Copy-solid.7z", "Copy.7z", "Deflate-solid.7z", "Deflate.7z", "LZMA-solid.7z", "LZMA.7z",
734 "LZMA2-solid.7z", "LZMA2.7z",
735
736
737
738 };
739
740
741 final Random rnd = new Random(0xdeadbeef);
742 for (final String fileName : variants) {
743 try (SevenZFile archive = getSevenZFile("COMPRESS-320/" + fileName)) {
744
745 SevenZArchiveEntry entry;
746 while ((entry = archive.getNextEntry()) != null) {
747
748 if (rnd.nextBoolean()) {
749 continue;
750 }
751
752 if (entry.hasStream()) {
753 assertTrue(entriesByName.containsKey(entry.getName()));
754 final byte[] content = readFully(archive);
755 assertArrayEquals(content, entriesByName.get(entry.getName()), "Content mismatch on: " + fileName + "!" + entry.getName());
756 }
757 }
758
759 }
760 }
761 }
762
763 @Test
764 void testReadBigSevenZipFile() throws IOException {
765 try (SevenZFile sevenZFile = getSevenZFile("COMPRESS-592.7z")) {
766 SevenZArchiveEntry entry = sevenZFile.getNextEntry();
767 while (entry != null) {
768 if (entry.hasStream()) {
769 final byte[] content = new byte[(int) entry.getSize()];
770 sevenZFile.read(content);
771 }
772 entry = sevenZFile.getNextEntry();
773 }
774 }
775 }
776
777
778
779
780 @Test
781 void testReadEntriesOfSize0() throws IOException {
782 try (SevenZFile sevenZFile = getSevenZFile("COMPRESS-348.7z")) {
783 int entries = 0;
784 SevenZArchiveEntry entry = sevenZFile.getNextEntry();
785 while (entry != null) {
786 entries++;
787 final int b = sevenZFile.read();
788 if ("2.txt".equals(entry.getName()) || "5.txt".equals(entry.getName())) {
789 assertEquals(-1, b);
790 } else {
791 assertNotEquals(-1, b);
792 }
793 entry = sevenZFile.getNextEntry();
794 }
795 assertEquals(5, entries);
796 }
797 }
798
799
800
801
802 @Test
803 void testReadingArchiveProperties() throws IOException {
804 final String entryName = "COMPRESS-681.txt";
805 final String entryContent = "https://issues.apache.org/jira/browse/COMPRESS-681";
806 try (SevenZFile archive = getSevenZFile("COMPRESS-681.7z")) {
807 final SevenZArchiveEntry entry = archive.getNextEntry();
808 assertEquals(entryName, entry.getName());
809 final byte[] contents = new byte[(int) entry.getSize()];
810 int off = 0;
811 while (off < contents.length) {
812 final int bytesRead = archive.read(contents, off, contents.length - off);
813 assertTrue(bytesRead >= 0, "bytesRead exp: >=0, act: " + bytesRead);
814 off += bytesRead;
815 }
816 assertEquals(entryContent, new String(contents, UTF_8));
817 assertNull(archive.getNextEntry());
818 }
819 }
820
821 @Test
822 void testReadingBackDeltaDistance() throws Exception {
823 final File output = newTempFile("delta-distance.7z");
824 try (SevenZOutputFile outArchive = new SevenZOutputFile(output)) {
825 outArchive.setContentMethods(
826 Arrays.asList(new SevenZMethodConfiguration(SevenZMethod.DELTA_FILTER, 32), new SevenZMethodConfiguration(SevenZMethod.LZMA2)));
827 final SevenZArchiveEntry entry = new SevenZArchiveEntry();
828 entry.setName("foo.txt");
829 outArchive.putArchiveEntry(entry);
830 outArchive.write(new byte[] { 'A' });
831 outArchive.closeArchiveEntry();
832 }
833
834 try (SevenZFile archive = SevenZFile.builder().setFile(output).get()) {
835 final SevenZArchiveEntry entry = archive.getNextEntry();
836 final SevenZMethodConfiguration m = entry.getContentMethods().iterator().next();
837 assertEquals(SevenZMethod.DELTA_FILTER, m.getMethod());
838 assertEquals(32, m.getOptions());
839 }
840 }
841
842 @Test
843 void testReadingBackLZMA2DictSize() throws Exception {
844 final File output = newTempFile("lzma2-dictsize.7z");
845 try (SevenZOutputFile outArchive = new SevenZOutputFile(output)) {
846 outArchive.setContentMethods(Arrays.asList(new SevenZMethodConfiguration(SevenZMethod.LZMA2, 1 << 20)));
847 final SevenZArchiveEntry entry = new SevenZArchiveEntry();
848 entry.setName("foo.txt");
849 outArchive.putArchiveEntry(entry);
850 outArchive.write(new byte[] { 'A' });
851 outArchive.closeArchiveEntry();
852 }
853
854 try (SevenZFile archive = SevenZFile.builder().setFile(output).get()) {
855 final SevenZArchiveEntry entry = archive.getNextEntry();
856 final SevenZMethodConfiguration m = entry.getContentMethods().iterator().next();
857 assertEquals(SevenZMethod.LZMA2, m.getMethod());
858 assertEquals(1 << 20, m.getOptions());
859 }
860 }
861
862 @Test
863 void testReadTimesFromFile() throws IOException {
864 try (SevenZFile sevenZFile = getSevenZFile("times.7z")) {
865 SevenZArchiveEntry entry = sevenZFile.getNextEntry();
866 assertNotNull(entry);
867 assertEquals("test", entry.getName());
868 assertTrue(entry.isDirectory());
869 assertDates(entry, "2022-03-21T14:50:46.2099751Z", "2022-03-21T14:50:46.2099751Z", "2022-03-16T10:19:24.1051115Z");
870
871 entry = sevenZFile.getNextEntry();
872 assertNotNull(entry);
873 assertEquals("test/test-times.txt", entry.getName());
874 assertFalse(entry.isDirectory());
875 assertDates(entry, "2022-03-18T10:00:15Z", "2022-03-18T10:14:37.8130002Z", "2022-03-18T10:14:37.8110032Z");
876
877 entry = sevenZFile.getNextEntry();
878 assertNotNull(entry);
879 assertEquals("test/test-times2.txt", entry.getName());
880 assertFalse(entry.isDirectory());
881 assertDates(entry, "2022-03-18T10:00:19Z", "2022-03-18T10:14:37.8170038Z", "2022-03-18T10:14:37.8140004Z");
882
883 entry = sevenZFile.getNextEntry();
884 assertNull(entry);
885 }
886 }
887
888 @Test
889 void testRetrieveInputStreamForAllEntriesMultipleTimes() throws IOException {
890 try (SevenZFile sevenZFile = getSevenZFile("bla.7z")) {
891 for (final SevenZArchiveEntry entry : sevenZFile.getEntries()) {
892 final byte[] firstRead = read(sevenZFile, entry);
893 final byte[] secondRead = read(sevenZFile, entry);
894 assertArrayEquals(firstRead, secondRead);
895 }
896 }
897 }
898
899 @Test
900 void testRetrieveInputStreamForAllEntriesWithoutCRCMultipleTimes() throws IOException {
901 try (SevenZOutputFile out = new SevenZOutputFile(newTempFile("test.7z"))) {
902 final Path inputFile = Files.createTempFile("SevenZTestTemp", "");
903
904 final SevenZArchiveEntry entry = out.createArchiveEntry(inputFile.toFile(), "test.txt");
905 out.putArchiveEntry(entry);
906 out.write("Test".getBytes(UTF_8));
907 out.closeArchiveEntry();
908
909 Files.deleteIfExists(inputFile);
910 }
911
912 try (SevenZFile sevenZFile = SevenZFile.builder().setFile(newTempFile("test.7z")).get()) {
913 for (final SevenZArchiveEntry entry : sevenZFile.getEntries()) {
914 final byte[] firstRead = read(sevenZFile, entry);
915 final byte[] secondRead = read(sevenZFile, entry);
916 assertArrayEquals(firstRead, secondRead);
917 }
918 }
919 }
920
921 @Test
922 void testRetrieveInputStreamForShuffledEntries() throws IOException {
923 try (SevenZFile sevenZFile = getSevenZFile("COMPRESS-348.7z")) {
924 final List<SevenZArchiveEntry> entries = (List<SevenZArchiveEntry>) sevenZFile.getEntries();
925 Collections.shuffle(entries);
926 for (final SevenZArchiveEntry entry : entries) {
927 read(sevenZFile, entry);
928 }
929 }
930 }
931
932 @Test
933 void testSevenZWithEOS() throws IOException {
934 try (SevenZFile sevenZFile = getSevenZFile("lzma-with-eos.7z")) {
935 final List<SevenZArchiveEntry> entries = (List<SevenZArchiveEntry>) sevenZFile.getEntries();
936 for (final SevenZArchiveEntry entry : entries) {
937 read(sevenZFile, entry);
938 }
939 }
940 }
941
942 @Test
943 void testSignatureCheck() {
944 assertTrue(SevenZFile.matches(SevenZFile.sevenZSignature, SevenZFile.sevenZSignature.length));
945 assertTrue(SevenZFile.matches(SevenZFile.sevenZSignature, SevenZFile.sevenZSignature.length + 1));
946 assertFalse(SevenZFile.matches(SevenZFile.sevenZSignature, SevenZFile.sevenZSignature.length - 1));
947 assertFalse(SevenZFile.matches(new byte[] { 1, 2, 3, 4, 5, 6 }, 6));
948 assertTrue(SevenZFile.matches(new byte[] { '7', 'z', (byte) 0xBC, (byte) 0xAF, 0x27, 0x1C }, 6));
949 assertFalse(SevenZFile.matches(new byte[] { '7', 'z', (byte) 0xBC, (byte) 0xAF, 0x27, 0x1D }, 6));
950 }
951 }