1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.compress.archivers.tar;
19
20 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertNotNull;
23 import static org.junit.jupiter.api.Assertions.assertNotSame;
24 import static org.junit.jupiter.api.Assertions.assertNull;
25 import static org.junit.jupiter.api.Assertions.assertSame;
26 import static org.junit.jupiter.api.Assertions.assertThrows;
27 import static org.junit.jupiter.api.Assertions.assertTrue;
28 import static org.junit.jupiter.api.Assertions.fail;
29
30 import java.io.BufferedOutputStream;
31 import java.io.ByteArrayInputStream;
32 import java.io.ByteArrayOutputStream;
33 import java.io.File;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.io.OutputStream;
37 import java.nio.charset.StandardCharsets;
38 import java.nio.file.Files;
39 import java.nio.file.Path;
40 import java.util.Arrays;
41 import java.util.Calendar;
42 import java.util.Date;
43 import java.util.TimeZone;
44 import java.util.zip.GZIPInputStream;
45
46 import org.apache.commons.compress.AbstractTest;
47 import org.apache.commons.compress.archivers.ArchiveException;
48 import org.apache.commons.compress.archivers.ArchiveStreamFactory;
49 import org.apache.commons.io.IOUtils;
50 import org.junit.jupiter.api.Test;
51
52 public class TarArchiveInputStreamTest extends AbstractTest {
53
54 private void datePriorToEpoch(final String archive) throws Exception {
55 try (TarArchiveInputStream in = new TarArchiveInputStream(Files.newInputStream(getFile(archive).toPath()))) {
56 final TarArchiveEntry tae = in.getNextTarEntry();
57 assertEquals("foo", tae.getName());
58 assertEquals(TarConstants.LF_NORMAL, tae.getLinkFlag());
59 final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
60 cal.set(1969, 11, 31, 23, 59, 59);
61 cal.set(Calendar.MILLISECOND, 0);
62 assertEquals(cal.getTime(), tae.getLastModifiedDate());
63 assertTrue(tae.isCheckSumOK());
64 }
65 }
66
67 private void getNextEntryUntilIOException(final TarArchiveInputStream archive) {
68 assertThrows(IOException.class, () -> {
69 while (archive.getNextTarEntry() != null) {
70
71 }
72 });
73 }
74
75 private TarArchiveInputStream getTestStream(final String name) {
76 return new TarArchiveInputStream(TarArchiveInputStreamTest.class.getResourceAsStream(name));
77 }
78
79 @Test
80 public void testCompress197() {
81 try (TarArchiveInputStream tar = getTestStream("/COMPRESS-197.tar")) {
82 TarArchiveEntry entry = tar.getNextTarEntry();
83 while (entry != null) {
84 entry = tar.getNextTarEntry();
85 }
86 } catch (final IOException e) {
87 fail("COMPRESS-197: " + e.getMessage());
88 }
89 }
90
91 @Test
92 public void testCompress558() throws IOException {
93 final String folderName = "apache-activemq-5.16.0/examples/openwire/advanced-scenarios/jms-example-exclusive-consumer/src/main/";
94
95 final String consumerJavaName =
96 "apache-activemq-5.16.0/examples/openwire/advanced-scenarios/jms-example-exclusive-consumer/src/main/java/example/queue/exclusive/Consumer.java";
97 final String producerJavaName =
98 "apache-activemq-5.16.0/examples/openwire/advanced-scenarios/jms-example-exclusive-consumer/src/main/java/example/queue/exclusive/Producer.java";
99
100
101 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
102 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos)) {
103 tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
104 final TarArchiveEntry rootfolder = new TarArchiveEntry(folderName);
105 tos.putArchiveEntry(rootfolder);
106 final TarArchiveEntry consumerJava = new TarArchiveEntry(consumerJavaName);
107 tos.putArchiveEntry(consumerJava);
108 final TarArchiveEntry producerJava = new TarArchiveEntry(producerJavaName);
109 tos.putArchiveEntry(producerJava);
110 tos.closeArchiveEntry();
111 }
112 final byte[] data = bos.toByteArray();
113 try (ByteArrayInputStream bis = new ByteArrayInputStream(data);
114 TarArchiveInputStream tis = new TarArchiveInputStream(bis)) {
115 assertEquals(folderName, tis.getNextTarEntry().getName());
116 assertEquals(TarConstants.LF_DIR, tis.getCurrentEntry().getLinkFlag());
117 assertEquals(consumerJavaName, tis.getNextTarEntry().getName());
118 assertEquals(TarConstants.LF_NORMAL, tis.getCurrentEntry().getLinkFlag());
119 assertEquals(producerJavaName, tis.getNextTarEntry().getName());
120 assertEquals(TarConstants.LF_NORMAL, tis.getCurrentEntry().getLinkFlag());
121 }
122 }
123
124 @Test
125 public void testDatePriorToEpochInGNUFormat() throws Exception {
126 datePriorToEpoch("preepoch-star.tar");
127 }
128
129 @Test
130 public void testDatePriorToEpochInPAXFormat() throws Exception {
131 datePriorToEpoch("preepoch-posix.tar");
132 }
133
134 @Test
135 public void testDirectoryWithLongNameEndsWithSlash() throws IOException, ArchiveException {
136 final String rootPath = getTempDirFile().getAbsolutePath();
137 final String dirDirectory = "COMPRESS-509";
138 final int count = 100;
139 final File root = new File(rootPath + "/" + dirDirectory);
140 root.mkdirs();
141 for (int i = 1; i < count; i++) {
142
143 String subDir = "";
144 for (int j = 0; j < i; j++) {
145 subDir += "a";
146 }
147 final File dir = new File(rootPath + "/" + dirDirectory, "/" + subDir);
148 dir.mkdir();
149
150
151 final String fileName = "/" + dirDirectory + "/" + subDir;
152 final File tarF = new File(rootPath + "/tar" + i + ".tar");
153 try (OutputStream dest = Files.newOutputStream(tarF.toPath())) {
154 final TarArchiveOutputStream out = new TarArchiveOutputStream(new BufferedOutputStream(dest));
155 out.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
156 out.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
157
158 final File file = new File(rootPath, fileName);
159 final TarArchiveEntry entry = new TarArchiveEntry(file);
160 entry.setName(fileName);
161 out.putArchiveEntry(entry);
162 out.closeArchiveEntry();
163 out.flush();
164 }
165
166
167 try (InputStream is = Files.newInputStream(tarF.toPath());
168 TarArchiveInputStream debInputStream = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("tar", is)) {
169 TarArchiveEntry outEntry;
170 while ((outEntry = debInputStream.getNextEntry()) != null) {
171 assertTrue(outEntry.getName().endsWith("/"), outEntry.getName());
172 }
173 }
174 }
175 }
176
177 @Test
178 public void testGetAndSetOfPaxEntry() throws Exception {
179 try (TarArchiveInputStream is = getTestStream("/COMPRESS-356.tar")) {
180 final TarArchiveEntry entry = is.getNextTarEntry();
181 assertEquals("package/package.json", entry.getName());
182 assertEquals(TarConstants.LF_NORMAL, entry.getLinkFlag());
183 assertEquals(is.getCurrentEntry(), entry);
184 final TarArchiveEntry weaselEntry = new TarArchiveEntry(entry.getName());
185 weaselEntry.setSize(entry.getSize());
186 is.setCurrentEntry(weaselEntry);
187 assertEquals(entry, is.getCurrentEntry());
188 assertNotSame(entry, is.getCurrentEntry());
189 assertSame(weaselEntry, is.getCurrentEntry());
190 assertThrows(IllegalStateException.class, () -> {
191 is.setCurrentEntry(null);
192 is.read();
193 }, "should abort because current entry is nulled");
194 is.setCurrentEntry(entry);
195 is.read();
196 }
197 }
198
199 @Test
200 public void testMultiByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
201 final byte[] buf = new byte[2];
202 try (InputStream in = newInputStream("bla.tar");
203 TarArchiveInputStream archive = new TarArchiveInputStream(in)) {
204 assertNotNull(archive.getNextEntry());
205 IOUtils.toByteArray(archive);
206 assertEquals(-1, archive.read(buf));
207 assertEquals(-1, archive.read(buf));
208 }
209 }
210
211 @Test
212 public void testParseTarTruncatedInContent() throws IOException {
213 try (InputStream in = newInputStream("COMPRESS-544_truncated_in_content-fail.tar");
214 TarArchiveInputStream archive = new TarArchiveInputStream(in)) {
215 getNextEntryUntilIOException(archive);
216 }
217 }
218
219 @Test
220 public void testParseTarTruncatedInPadding() throws IOException {
221 try (InputStream in = newInputStream("COMPRESS-544_truncated_in_padding-fail.tar");
222 TarArchiveInputStream archive = new TarArchiveInputStream(in)) {
223 getNextEntryUntilIOException(archive);
224 }
225 }
226
227 @Test
228 public void testParseTarWithNonNumberPaxHeaders() throws IOException {
229 try (InputStream in = newInputStream("COMPRESS-529-fail.tar");
230 TarArchiveInputStream archive = new TarArchiveInputStream(in)) {
231 assertThrows(IOException.class, () -> archive.getNextEntry());
232 }
233 }
234
235 @Test
236 public void testParseTarWithSpecialPaxHeaders() throws IOException {
237 try (InputStream in = newInputStream("COMPRESS-530-fail.tar");
238 TarArchiveInputStream archive = new TarArchiveInputStream(in)) {
239 assertThrows(IOException.class, () -> archive.getNextEntry());
240 assertThrows(IOException.class, () -> IOUtils.toByteArray(archive));
241 }
242 }
243
244 @Test
245 public void testReadsArchiveCompletely_COMPRESS245() {
246 try (InputStream is = TarArchiveInputStreamTest.class.getResourceAsStream("/COMPRESS-245.tar.gz")) {
247 final InputStream gin = new GZIPInputStream(is);
248 try (TarArchiveInputStream tar = new TarArchiveInputStream(gin)) {
249 int count = 0;
250 TarArchiveEntry entry = tar.getNextTarEntry();
251 while (entry != null) {
252 count++;
253 entry = tar.getNextTarEntry();
254 }
255 assertEquals(31, count);
256 }
257 } catch (final IOException e) {
258 fail("COMPRESS-245: " + e.getMessage());
259 }
260 }
261
262 @Test
263 public void testRejectsArchivesWithNegativeSizes() throws Exception {
264 try (InputStream in = newInputStream("COMPRESS-569-fail.tar");
265 TarArchiveInputStream archive = new TarArchiveInputStream(in)) {
266 getNextEntryUntilIOException(archive);
267 }
268 }
269
270
271
272
273 @Test
274 public void testShouldConsumeArchiveCompletely() throws Exception {
275 try (InputStream is = TarArchiveInputStreamTest.class.getResourceAsStream("/archive_with_trailer.tar");
276 TarArchiveInputStream tar = new TarArchiveInputStream(is)) {
277 while (tar.getNextTarEntry() != null) {
278
279 }
280 final byte[] expected = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n' };
281 final byte[] actual = new byte[expected.length];
282 is.read(actual);
283 assertArrayEquals(expected, actual, () -> Arrays.toString(actual));
284 }
285 }
286
287 @Test
288 public void testShouldReadBigGid() throws Exception {
289 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
290 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos)) {
291 tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
292 final TarArchiveEntry t = new TarArchiveEntry("name");
293 t.setGroupId(4294967294L);
294 t.setSize(1);
295 tos.putArchiveEntry(t);
296 tos.write(30);
297 tos.closeArchiveEntry();
298 }
299 final byte[] data = bos.toByteArray();
300 final ByteArrayInputStream bis = new ByteArrayInputStream(data);
301 try (TarArchiveInputStream tis = new TarArchiveInputStream(bis)) {
302 final TarArchiveEntry t = tis.getNextTarEntry();
303 assertEquals(4294967294L, t.getLongGroupId());
304 }
305 }
306
307
308
309
310 @Test
311 public void testShouldReadGNULongNameEntryWithWrongName() throws Exception {
312 try (TarArchiveInputStream is = getTestStream("/COMPRESS-324.tar")) {
313 final TarArchiveEntry entry = is.getNextTarEntry();
314 assertEquals(
315 "1234567890123456789012345678901234567890123456789012345678901234567890"
316 + "1234567890123456789012345678901234567890123456789012345678901234567890"
317 + "1234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890.txt",
318 entry.getName());
319 }
320 }
321
322 @Test
323 public void testShouldThrowAnExceptionOnTruncatedEntries() throws Exception {
324 final Path dir = createTempDirectory("COMPRESS-279");
325 try (TarArchiveInputStream is = getTestStream("/COMPRESS-279-fail.tar")) {
326 assertThrows(IOException.class, () -> {
327 TarArchiveEntry entry = is.getNextTarEntry();
328 int count = 0;
329 while (entry != null) {
330 Files.copy(is, dir.resolve(String.valueOf(count)));
331 count++;
332 entry = is.getNextTarEntry();
333 }
334 });
335 }
336 }
337
338 @Test
339 public void testShouldUseSpecifiedEncodingWhenReadingGNULongNames() throws Exception {
340 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
341 final String encoding = StandardCharsets.UTF_16.name();
342 final String name = "1234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890\u00e4";
343 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, encoding)) {
344 tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
345 final TarArchiveEntry t = new TarArchiveEntry(name);
346 t.setSize(1);
347 tos.putArchiveEntry(t);
348 tos.write(30);
349 tos.closeArchiveEntry();
350 }
351 final byte[] data = bos.toByteArray();
352 final ByteArrayInputStream bis = new ByteArrayInputStream(data);
353 try (TarArchiveInputStream tis = new TarArchiveInputStream(bis, encoding)) {
354 final TarArchiveEntry t = tis.getNextTarEntry();
355 assertEquals(name, t.getName());
356 }
357 }
358
359 @Test
360 public void testSingleByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
361 try (InputStream in = newInputStream("bla.tar");
362 TarArchiveInputStream archive = new TarArchiveInputStream(in)) {
363 assertNotNull(archive.getNextEntry());
364 IOUtils.toByteArray(archive);
365 assertEquals(-1, archive.read());
366 assertEquals(-1, archive.read());
367 }
368 }
369
370
371
372
373 @Test
374 public void testSkipsDevNumbersWhenEntryIsNoDevice() throws Exception {
375 try (TarArchiveInputStream is = getTestStream("/COMPRESS-417.tar")) {
376 assertEquals("test1.xml", is.getNextTarEntry().getName());
377 assertEquals(TarConstants.LF_NORMAL, is.getCurrentEntry().getLinkFlag());
378 assertEquals("test2.xml", is.getNextTarEntry().getName());
379 assertEquals(TarConstants.LF_NORMAL, is.getCurrentEntry().getLinkFlag());
380 assertNull(is.getNextTarEntry());
381 }
382 }
383
384
385
386
387 @Test
388 public void testSurvivesBlankLinesInPaxHeader() throws Exception {
389 try (TarArchiveInputStream is = getTestStream("/COMPRESS-355.tar")) {
390 final TarArchiveEntry entry = is.getNextTarEntry();
391 assertEquals("package/package.json", entry.getName());
392 assertEquals(TarConstants.LF_NORMAL, entry.getLinkFlag());
393 assertNull(is.getNextTarEntry());
394 }
395 }
396
397
398
399
400 @Test
401 public void testSurvivesPaxHeaderWithNameEndingInSlash() throws Exception {
402 try (TarArchiveInputStream is = getTestStream("/COMPRESS-356.tar")) {
403 final TarArchiveEntry entry = is.getNextTarEntry();
404 assertEquals("package/package.json", entry.getName());
405 assertEquals(TarConstants.LF_NORMAL, entry.getLinkFlag());
406 assertNull(is.getNextTarEntry());
407 }
408 }
409
410 @Test
411 public void testThrowException() throws IOException {
412 try (InputStream in = newInputStream("COMPRESS-553-fail.tar");
413 TarArchiveInputStream archive = new TarArchiveInputStream(in)) {
414 getNextEntryUntilIOException(archive);
415 }
416 }
417
418 @Test
419 public void testThrowExceptionWithNullEntry() throws IOException {
420 try (InputStream in = newInputStream("COMPRESS-554-fail.tar");
421 TarArchiveInputStream archive = new TarArchiveInputStream(in)) {
422 getNextEntryUntilIOException(archive);
423 }
424 }
425
426 @Test
427 public void testWorkaroundForBrokenTimeHeader() throws Exception {
428 try (TarArchiveInputStream in = new TarArchiveInputStream(newInputStream("simple-aix-native-tar.tar"))) {
429 TarArchiveEntry tae = in.getNextTarEntry();
430 tae = in.getNextTarEntry();
431 assertEquals("sample/link-to-txt-file.lnk", tae.getName());
432 assertEquals(TarConstants.LF_SYMLINK, tae.getLinkFlag());
433 assertEquals(new Date(0), tae.getLastModifiedDate());
434 assertTrue(tae.isSymbolicLink());
435 assertTrue(tae.isCheckSumOK());
436 }
437 }
438
439 }