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