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.tar;
20
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertFalse;
23 import static org.junit.jupiter.api.Assertions.assertThrows;
24 import static org.junit.jupiter.api.Assertions.assertTrue;
25 import static org.junit.jupiter.api.Assertions.fail;
26
27 import java.io.BufferedOutputStream;
28 import java.io.ByteArrayOutputStream;
29 import java.io.File;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.nio.ByteBuffer;
34 import java.nio.channels.SeekableByteChannel;
35 import java.nio.charset.StandardCharsets;
36 import java.nio.file.Files;
37 import java.nio.file.Path;
38 import java.util.Calendar;
39 import java.util.Date;
40 import java.util.List;
41 import java.util.TimeZone;
42 import java.util.zip.GZIPInputStream;
43
44 import org.apache.commons.compress.AbstractTest;
45 import org.apache.commons.io.IOUtils;
46 import org.junit.jupiter.api.Test;
47
48 import shaded.org.apache.commons.lang3.StringUtils;
49
50 class TarFileTest extends AbstractTest {
51
52 private void datePriorToEpoch(final String archive) throws Exception {
53 try (TarFile tarFile = new TarFile(getPath(archive))) {
54 final TarArchiveEntry entry = tarFile.getEntries().get(0);
55 assertEquals("foo", entry.getName());
56 assertEquals(TarConstants.LF_NORMAL, entry.getLinkFlag());
57 final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
58 cal.set(1969, 11, 31, 23, 59, 59);
59 cal.set(Calendar.MILLISECOND, 0);
60 assertEquals(cal.getTime(), entry.getLastModifiedDate());
61 assertTrue(entry.isCheckSumOK());
62 }
63 }
64
65
66
67
68 @Test
69 void testArchiveWithTrailer() throws IOException {
70 try (SeekableByteChannel channel = Files.newByteChannel(getPath("archive_with_trailer.tar"));
71 TarFile tarfile = new TarFile(channel, TarConstants.DEFAULT_BLKSIZE, TarConstants.DEFAULT_RCDSIZE, null, false)) {
72 final String tarAppendix = "Hello, world!\n";
73 final ByteBuffer buffer = ByteBuffer.allocate(tarAppendix.length());
74 channel.read(buffer);
75 assertEquals(tarAppendix, new String(buffer.array()));
76 }
77 }
78
79 @Test
80 void testCompress197() throws IOException {
81 try (TarFile tarFile = new TarFile(getPath("COMPRESS-197.tar"))) {
82
83 }
84 }
85
86 @Test
87 void testCompress558() throws IOException {
88 final String folderName = "apache-activemq-5.16.0/examples/openwire/advanced-scenarios/jms-example-exclusive-consumer/src/main/";
89
90 final String consumerJavaName =
91 "apache-activemq-5.16.0/examples/openwire/advanced-scenarios/jms-example-exclusive-consumer/src/main/java/example/queue/exclusive/Consumer.java";
92 final String producerJavaName =
93 "apache-activemq-5.16.0/examples/openwire/advanced-scenarios/jms-example-exclusive-consumer/src/main/java/example/queue/exclusive/Producer.java";
94
95
96 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
97 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos)) {
98 tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
99 final TarArchiveEntry rootfolder = new TarArchiveEntry(folderName);
100 tos.putArchiveEntry(rootfolder);
101 final TarArchiveEntry consumerJava = new TarArchiveEntry(consumerJavaName);
102 tos.putArchiveEntry(consumerJava);
103 final TarArchiveEntry producerJava = new TarArchiveEntry(producerJavaName);
104 tos.putArchiveEntry(producerJava);
105 tos.closeArchiveEntry();
106 }
107 final byte[] data = bos.toByteArray();
108 try (TarFile tarFile = new TarFile(data)) {
109 final List<TarArchiveEntry> entries = tarFile.getEntries();
110 assertEquals(folderName, entries.get(0).getName());
111 assertEquals(TarConstants.LF_DIR, entries.get(0).getLinkFlag());
112 assertEquals(consumerJavaName, entries.get(1).getName());
113 assertEquals(TarConstants.LF_NORMAL, entries.get(1).getLinkFlag());
114 assertEquals(producerJavaName, entries.get(2).getName());
115 assertEquals(TarConstants.LF_NORMAL, entries.get(2).getLinkFlag());
116 }
117 }
118
119 @Test
120 void testCompress657() throws IOException {
121 try (TarFile tarFile = new TarFile(getPath("COMPRESS-657/orjson-3.7.8.tar"))) {
122 for (final TarArchiveEntry entry : tarFile.getEntries()) {
123 if (entry.isDirectory()) {
124
125 assertFalse(entry.isFile(), "Entry '" + entry.getName() + "' is both a directory and a file");
126 }
127 }
128 }
129 }
130
131 @Test
132 void testDatePriorToEpochInGNUFormat() throws Exception {
133 datePriorToEpoch("preepoch-star.tar");
134 }
135
136 @Test
137 void testDatePriorToEpochInPAXFormat() throws Exception {
138 datePriorToEpoch("preepoch-posix.tar");
139 }
140
141 @Test
142 void testDirectoryWithLongNameEndsWithSlash() throws IOException {
143 final String rootPath = getTempDirFile().getAbsolutePath();
144 final String dirDirectory = "COMPRESS-509";
145 final int count = 100;
146 final File root = new File(rootPath + "/" + dirDirectory);
147 root.mkdirs();
148 for (int i = 1; i < count; i++) {
149
150 final String subDir = StringUtils.repeat('a', i);
151 final File dir = new File(rootPath + "/" + dirDirectory, "/" + subDir);
152 dir.mkdir();
153
154
155 final String fileName = "/" + dirDirectory + "/" + subDir;
156 final File tarF = new File(rootPath + "/tar" + i + ".tar");
157 try (OutputStream dest = Files.newOutputStream(tarF.toPath());
158 TarArchiveOutputStream out = new TarArchiveOutputStream(new BufferedOutputStream(dest))) {
159 out.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
160 out.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
161
162 final File file = new File(rootPath, fileName);
163 final TarArchiveEntry entry = new TarArchiveEntry(file);
164 entry.setName(fileName);
165 out.putArchiveEntry(entry);
166 out.closeArchiveEntry();
167 out.flush();
168 }
169
170 try (TarFile tarFile = new TarFile(tarF)) {
171 for (final TarArchiveEntry entry : tarFile.getEntries()) {
172 assertTrue(entry.getName().endsWith("/"), "Entry name: " + entry.getName());
173 }
174 }
175 }
176 }
177
178 @Test
179 void testMultiByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
180 final byte[] buf = new byte[2];
181 try (TarFile tarFile = new TarFile(getPath("bla.tar"));
182 InputStream input = tarFile.getInputStream(tarFile.getEntries().get(0))) {
183 IOUtils.toByteArray(input);
184 assertEquals(-1, input.read(buf));
185 assertEquals(-1, input.read(buf));
186 }
187 }
188
189 @Test
190 void testParseTarTruncatedInContent() {
191 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-544_truncated_in_content.tar")));
192 }
193
194 @Test
195 void testParseTarTruncatedInPadding() {
196 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-544_truncated_in_padding.tar")));
197 }
198
199 @Test
200 void testParseTarWithNonNumberPaxHeaders() {
201 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-529-fail.tar")));
202 }
203
204 @Test
205 void testParseTarWithSpecialPaxHeaders() {
206 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-530-fail.tar")));
207 }
208
209 @Test
210 void testReadsArchiveCompletely_COMPRESS245() {
211 try {
212 final Path tempTar = tempResultDir.toPath().resolve("COMPRESS-245.tar");
213 try (GZIPInputStream gin = new GZIPInputStream(Files.newInputStream(getPath("COMPRESS-245.tar.gz")))) {
214 Files.copy(gin, tempTar);
215 }
216 try (TarFile tarFile = new TarFile(tempTar)) {
217 assertEquals(31, tarFile.getEntries().size());
218 }
219 } catch (final IOException e) {
220 fail("COMPRESS-245: " + e.getMessage());
221 }
222 }
223
224 @Test
225 void testRejectsArchivesWithNegativeSizes() throws Exception {
226 assertThrows(IOException.class, () -> new TarFile(getFile("COMPRESS-569-fail.tar")));
227 }
228
229 @Test
230 void testShouldReadBigGid() throws Exception {
231 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
232 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos)) {
233 tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
234 final TarArchiveEntry t = new TarArchiveEntry("name");
235 t.setGroupId(4294967294L);
236 t.setSize(1);
237 tos.putArchiveEntry(t);
238 tos.write(30);
239 tos.closeArchiveEntry();
240 }
241 final byte[] data = bos.toByteArray();
242 try (TarFile tarFile = new TarFile(data)) {
243 final List<TarArchiveEntry> entries = tarFile.getEntries();
244 assertEquals(4294967294L, entries.get(0).getLongGroupId());
245 }
246 }
247
248
249
250
251 @Test
252 void testShouldReadGNULongNameEntryWithWrongName() throws Exception {
253 try (TarFile tarFile = new TarFile(getPath("COMPRESS-324.tar"))) {
254 final List<TarArchiveEntry> entries = tarFile.getEntries();
255 assertEquals(
256 "1234567890123456789012345678901234567890123456789012345678901234567890"
257 + "1234567890123456789012345678901234567890123456789012345678901234567890"
258 + "1234567890123456789012345678901234567890123456789012345678901234567890" + "1234567890123456789012345678901234567890.txt",
259 entries.get(0).getName());
260 }
261 }
262
263 @Test
264 void testShouldThrowAnExceptionOnTruncatedEntries() throws Exception {
265 createTempDirectory("COMPRESS-279");
266 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-279.tar")));
267 }
268
269 @Test
270 void testShouldUseSpecifiedEncodingWhenReadingGNULongNames() throws Exception {
271 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
272 final String encoding = StandardCharsets.UTF_16.name();
273 final String name = "1234567890123456789012345678901234567890123456789" + "01234567890123456789012345678901234567890123456789" + "01234567890\u00e4";
274 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, encoding)) {
275 tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
276 final TarArchiveEntry t = new TarArchiveEntry(name);
277 t.setSize(1);
278 tos.putArchiveEntry(t);
279 tos.write(30);
280 tos.closeArchiveEntry();
281 }
282 final byte[] data = bos.toByteArray();
283 try (TarFile tarFile = new TarFile(data, encoding)) {
284 final List<TarArchiveEntry> entries = tarFile.getEntries();
285 assertEquals(1, entries.size());
286 assertEquals(name, entries.get(0).getName());
287 }
288 }
289
290 @Test
291 void testSingleByteReadConsistentlyReturnsMinusOneAtEof() throws Exception {
292 try (TarFile tarFile = new TarFile(getPath("bla.tar"));
293 InputStream input = tarFile.getInputStream(tarFile.getEntries().get(0))) {
294 IOUtils.toByteArray(input);
295 assertEquals(-1, input.read());
296 assertEquals(-1, input.read());
297 }
298 }
299
300
301
302
303 @Test
304 void testSkipsDevNumbersWhenEntryIsNoDevice() throws Exception {
305 try (TarFile tarFile = new TarFile(getPath("COMPRESS-417.tar"))) {
306 final List<TarArchiveEntry> entries = tarFile.getEntries();
307 assertEquals(2, entries.size());
308 assertEquals("test1.xml", entries.get(0).getName());
309 assertEquals(TarConstants.LF_NORMAL, entries.get(0).getLinkFlag());
310 assertEquals("test2.xml", entries.get(1).getName());
311 assertEquals(TarConstants.LF_NORMAL, entries.get(1).getLinkFlag());
312 }
313 }
314
315
316
317
318 @Test
319 void testSurvivesBlankLinesInPaxHeader() throws Exception {
320 try (TarFile tarFile = new TarFile(getPath("COMPRESS-355.tar"))) {
321 final List<TarArchiveEntry> entries = tarFile.getEntries();
322 assertEquals(1, entries.size());
323 assertEquals("package/package.json", entries.get(0).getName());
324 assertEquals(TarConstants.LF_NORMAL, entries.get(0).getLinkFlag());
325 }
326 }
327
328
329
330
331 @Test
332 void testSurvivesPaxHeaderWithNameEndingInSlash() throws Exception {
333 try (TarFile tarFile = new TarFile(getPath("COMPRESS-356.tar"))) {
334 final List<TarArchiveEntry> entries = tarFile.getEntries();
335 assertEquals(1, entries.size());
336 assertEquals("package/package.json", entries.get(0).getName());
337 assertEquals(TarConstants.LF_NORMAL, entries.get(0).getLinkFlag());
338 }
339 }
340
341 @Test
342 void testThrowException() {
343 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-553-fail.tar")));
344 }
345
346 @Test
347 void testThrowExceptionWithNullEntry() {
348 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-554-fail.tar")));
349 }
350
351 @Test
352 void testWorkaroundForBrokenTimeHeader() throws IOException {
353 try (TarFile tarFile = new TarFile(getPath("simple-aix-native-tar.tar"))) {
354 final List<TarArchiveEntry> entries = tarFile.getEntries();
355 assertEquals(3, entries.size());
356 final TarArchiveEntry entry = entries.get(1);
357 assertEquals("sample/link-to-txt-file.lnk", entry.getName());
358 assertEquals(TarConstants.LF_SYMLINK, entry.getLinkFlag());
359 assertEquals(new Date(0), entry.getLastModifiedDate());
360 assertTrue(entry.isSymbolicLink());
361 assertTrue(entry.isCheckSumOK());
362 }
363 }
364 }