1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.compress.archivers.tar;
18
19 import static org.junit.jupiter.api.Assertions.assertEquals;
20 import static org.junit.jupiter.api.Assertions.assertFalse;
21 import static org.junit.jupiter.api.Assertions.assertThrows;
22 import static org.junit.jupiter.api.Assertions.assertTrue;
23 import static org.junit.jupiter.api.Assertions.fail;
24
25 import java.io.BufferedOutputStream;
26 import java.io.ByteArrayOutputStream;
27 import java.io.File;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStream;
31 import java.nio.ByteBuffer;
32 import java.nio.channels.SeekableByteChannel;
33 import java.nio.charset.StandardCharsets;
34 import java.nio.file.Files;
35 import java.nio.file.Path;
36 import java.util.Calendar;
37 import java.util.Date;
38 import java.util.List;
39 import java.util.TimeZone;
40 import java.util.zip.GZIPInputStream;
41
42 import org.apache.commons.compress.AbstractTest;
43 import org.apache.commons.io.IOUtils;
44 import org.junit.jupiter.api.Test;
45
46 public class TarFileTest extends AbstractTest {
47
48 private void datePriorToEpoch(final String archive) throws Exception {
49 try (TarFile tarFile = new TarFile(getPath(archive))) {
50 final TarArchiveEntry entry = tarFile.getEntries().get(0);
51 assertEquals("foo", entry.getName());
52 assertEquals(TarConstants.LF_NORMAL, entry.getLinkFlag());
53 final Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
54 cal.set(1969, 11, 31, 23, 59, 59);
55 cal.set(Calendar.MILLISECOND, 0);
56 assertEquals(cal.getTime(), entry.getLastModifiedDate());
57 assertTrue(entry.isCheckSumOK());
58 }
59 }
60
61
62
63
64 @Test
65 public void testArchiveWithTrailer() throws IOException {
66 try (SeekableByteChannel channel = Files.newByteChannel(getPath("archive_with_trailer.tar"));
67 TarFile tarfile = new TarFile(channel, TarConstants.DEFAULT_BLKSIZE, TarConstants.DEFAULT_RCDSIZE, null, false)) {
68 final String tarAppendix = "Hello, world!\n";
69 final ByteBuffer buffer = ByteBuffer.allocate(tarAppendix.length());
70 channel.read(buffer);
71 assertEquals(tarAppendix, new String(buffer.array()));
72 }
73 }
74
75 @Test
76 public void testCompress197() throws IOException {
77 try (TarFile tarFile = new TarFile(getPath("COMPRESS-197.tar"))) {
78
79 }
80 }
81
82 @Test
83 public void testCompress558() throws IOException {
84 final String folderName = "apache-activemq-5.16.0/examples/openwire/advanced-scenarios/jms-example-exclusive-consumer/src/main/";
85
86 final String consumerJavaName =
87 "apache-activemq-5.16.0/examples/openwire/advanced-scenarios/jms-example-exclusive-consumer/src/main/java/example/queue/exclusive/Consumer.java";
88 final String producerJavaName =
89 "apache-activemq-5.16.0/examples/openwire/advanced-scenarios/jms-example-exclusive-consumer/src/main/java/example/queue/exclusive/Producer.java";
90
91
92 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
93 try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos)) {
94 tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
95 final TarArchiveEntry rootfolder = new TarArchiveEntry(folderName);
96 tos.putArchiveEntry(rootfolder);
97 final TarArchiveEntry consumerJava = new TarArchiveEntry(consumerJavaName);
98 tos.putArchiveEntry(consumerJava);
99 final TarArchiveEntry producerJava = new TarArchiveEntry(producerJavaName);
100 tos.putArchiveEntry(producerJava);
101 tos.closeArchiveEntry();
102 }
103 final byte[] data = bos.toByteArray();
104 try (TarFile tarFile = new TarFile(data)) {
105 final List<TarArchiveEntry> entries = tarFile.getEntries();
106 assertEquals(folderName, entries.get(0).getName());
107 assertEquals(TarConstants.LF_DIR, entries.get(0).getLinkFlag());
108 assertEquals(consumerJavaName, entries.get(1).getName());
109 assertEquals(TarConstants.LF_NORMAL, entries.get(1).getLinkFlag());
110 assertEquals(producerJavaName, entries.get(2).getName());
111 assertEquals(TarConstants.LF_NORMAL, entries.get(2).getLinkFlag());
112 }
113 }
114
115 @Test
116 public void testCompress657() throws IOException {
117 try (TarFile tarFile = new TarFile(getPath("COMPRESS-657/orjson-3.7.8.tar"))) {
118 for (final TarArchiveEntry entry : tarFile.getEntries()) {
119 if (entry.isDirectory()) {
120
121 assertFalse(entry.isFile(), "Entry '" + entry.getName() + "' is both a directory and a file");
122 }
123 }
124 }
125 }
126
127 @Test
128 public void testDatePriorToEpochInGNUFormat() throws Exception {
129 datePriorToEpoch("preepoch-star.tar");
130 }
131
132 @Test
133 public void testDatePriorToEpochInPAXFormat() throws Exception {
134 datePriorToEpoch("preepoch-posix.tar");
135 }
136
137 @Test
138 public void testDirectoryWithLongNameEndsWithSlash() throws IOException {
139 final String rootPath = getTempDirFile().getAbsolutePath();
140 final String dirDirectory = "COMPRESS-509";
141 final int count = 100;
142 final File root = new File(rootPath + "/" + dirDirectory);
143 root.mkdirs();
144 for (int i = 1; i < count; i++) {
145
146 final StringBuilder subDirBuilder = new StringBuilder();
147 for (int j = 0; j < i; j++) {
148 subDirBuilder.append("a");
149 }
150 final String subDir = subDirBuilder.toString();
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 public 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 public void testParseTarTruncatedInContent() {
191 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-544_truncated_in_content.tar")));
192 }
193
194 @Test
195 public void testParseTarTruncatedInPadding() {
196 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-544_truncated_in_padding.tar")));
197 }
198
199 @Test
200 public void testParseTarWithNonNumberPaxHeaders() {
201 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-529-fail.tar")));
202 }
203
204 @Test
205 public void testParseTarWithSpecialPaxHeaders() {
206 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-530-fail.tar")));
207 }
208
209 @Test
210 public 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 public void testRejectsArchivesWithNegativeSizes() throws Exception {
226 assertThrows(IOException.class, () -> new TarFile(getFile("COMPRESS-569-fail.tar")));
227 }
228
229 @Test
230 public 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 public 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 public void testShouldThrowAnExceptionOnTruncatedEntries() throws Exception {
265 createTempDirectory("COMPRESS-279");
266 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-279.tar")));
267 }
268
269 @Test
270 public 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 public 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 public 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 public 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 public 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 public void testThrowException() {
343 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-553-fail.tar")));
344 }
345
346 @Test
347 public void testThrowExceptionWithNullEntry() {
348 assertThrows(IOException.class, () -> new TarFile(getPath("COMPRESS-554-fail.tar")));
349 }
350
351 @Test
352 public 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 }