View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.commons.compress.archivers;
20  
21  import static java.nio.charset.StandardCharsets.UTF_8;
22  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
23  import static org.junit.jupiter.api.Assertions.assertEquals;
24  import static org.junit.jupiter.api.Assertions.assertFalse;
25  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
26  import static org.junit.jupiter.api.Assertions.assertNotNull;
27  import static org.junit.jupiter.api.Assertions.assertThrows;
28  import static org.junit.jupiter.api.Assertions.assertTrue;
29  
30  import java.io.ByteArrayInputStream;
31  import java.io.ByteArrayOutputStream;
32  import java.io.File;
33  import java.io.IOException;
34  import java.io.InputStream;
35  import java.io.OutputStream;
36  import java.nio.charset.StandardCharsets;
37  import java.nio.file.Files;
38  import java.util.Arrays;
39  import java.util.List;
40  
41  import org.apache.commons.compress.AbstractTest;
42  import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
43  import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
44  import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
45  import org.apache.commons.compress.archivers.tar.TarConstants;
46  import org.apache.commons.compress.archivers.tar.TarFile;
47  import org.apache.commons.compress.utils.ByteUtils;
48  import org.apache.commons.io.IOUtils;
49  import org.junit.jupiter.api.Test;
50  
51  import shaded.org.apache.commons.lang3.StringUtils;
52  
53  public final class TarTest extends AbstractTest {
54  
55      private String createLongName(final int nameLength) {
56          return StringUtils.repeat('a', nameLength);
57      }
58  
59      private byte[] createTarWithOneLongNameEntry(final String longName) throws IOException {
60          final ByteArrayOutputStream bos = new ByteArrayOutputStream();
61          try (TarArchiveOutputStream tos = new TarArchiveOutputStream(bos)) {
62              tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
63              final TarArchiveEntry longFileNameEntry = new TarArchiveEntry(longName);
64              tos.putArchiveEntry(longFileNameEntry);
65              tos.closeArchiveEntry();
66          }
67          return bos.toByteArray();
68      }
69  
70      @Test
71      void testCOMPRESS114() throws Exception {
72          final File input = getFile("COMPRESS-114.tar");
73          try (InputStream is = Files.newInputStream(input.toPath());
74                  TarArchiveInputStream in = new TarArchiveInputStream(is, StandardCharsets.ISO_8859_1.name())) {
75              TarArchiveEntry entry = in.getNextEntry();
76              assertEquals("3\u00b1\u00b1\u00b1F06\u00b1W2345\u00b1ZB\u00b1la\u00b1\u00b1\u00b1\u00b1\u00b1\u00b1\u00b1\u00b1BLA", entry.getName());
77              assertEquals(TarConstants.LF_NORMAL, entry.getLinkFlag());
78              entry = in.getNextEntry();
79              assertEquals("0302-0601-3\u00b1\u00b1\u00b1F06\u00b1W2345\u00b1ZB\u00b1la\u00b1\u00b1\u00b1\u00b1\u00b1\u00b1\u00b1\u00b1BLA", entry.getName());
80              assertEquals(TarConstants.LF_NORMAL, entry.getLinkFlag());
81          }
82      }
83  
84      @Test
85      void testCOMPRESS178() throws Exception {
86          final File input = getFile("COMPRESS-178-fail.tar");
87          try (InputStream is = Files.newInputStream(input.toPath());
88                  ArchiveInputStream<?> in = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("tar", is)) {
89              final IOException e = assertThrows(IOException.class, in::getNextEntry, "Expected IOException");
90              final Throwable t = e.getCause();
91              assertInstanceOf(IllegalArgumentException.class, t, "Expected cause = IllegalArgumentException");
92          }
93      }
94  
95      @Test
96      void testCOMPRESS178Lenient() throws Exception {
97          final File input = getFile("COMPRESS-178-fail.tar");
98          try (InputStream is = Files.newInputStream(input.toPath());
99                  ArchiveInputStream<?> in = new TarArchiveInputStream(is, true)) {
100             in.getNextEntry();
101         }
102     }
103 
104     @Test
105     void testDirectoryEntryFromFile() throws Exception {
106         final File archive = createTempFile("test.", ".tar");
107         final long beforeArchiveWrite;
108         try (TarArchiveOutputStream tos = new TarArchiveOutputStream(Files.newOutputStream(archive.toPath()))) {
109             final File dir = getTempDirFile();
110             beforeArchiveWrite = dir.lastModified();
111             final TarArchiveEntry in = new TarArchiveEntry(dir, "foo");
112             tos.putArchiveEntry(in);
113             tos.closeArchiveEntry();
114         }
115         final TarArchiveEntry out;
116         try (TarArchiveInputStream tis = new TarArchiveInputStream(Files.newInputStream(archive.toPath()))) {
117             out = tis.getNextTarEntry();
118         }
119         assertNotNull(out);
120         assertEquals("foo/", out.getName());
121         assertEquals(TarConstants.LF_DIR, out.getLinkFlag());
122         assertEquals(0, out.getSize());
123         // TAR stores time with a granularity of 1 second
124         assertEquals(beforeArchiveWrite / 1000, out.getLastModifiedDate().getTime() / 1000);
125         assertTrue(out.isDirectory());
126     }
127 
128     @Test
129     void testDirectoryRead() throws IOException {
130         final File input = getFile("directory.tar");
131         try (InputStream is = Files.newInputStream(input.toPath());
132                 TarArchiveInputStream in = new TarArchiveInputStream(is)) {
133             final TarArchiveEntry directoryEntry = in.getNextTarEntry();
134             assertEquals("directory/", directoryEntry.getName());
135             assertEquals(TarConstants.LF_DIR, directoryEntry.getLinkFlag());
136             assertTrue(directoryEntry.isDirectory());
137             final byte[] directoryRead = IOUtils.toByteArray(in);
138             assertArrayEquals(ByteUtils.EMPTY_BYTE_ARRAY, directoryRead);
139         }
140     }
141 
142     @Test
143     void testExplicitDirectoryEntry() throws Exception {
144         final File archive = createTempFile("test.", ".tar");
145         final long beforeArchiveWrite;
146         final TarArchiveEntry in = new TarArchiveEntry("foo/");
147         try (TarArchiveOutputStream tos = new TarArchiveOutputStream(Files.newOutputStream(archive.toPath()))) {
148             beforeArchiveWrite = getTempDirFile().lastModified();
149             in.setModTime(beforeArchiveWrite);
150             tos.putArchiveEntry(in);
151             tos.closeArchiveEntry();
152         }
153         final TarArchiveEntry out;
154         try (TarArchiveInputStream tis = new TarArchiveInputStream(Files.newInputStream(archive.toPath()))) {
155             out = tis.getNextTarEntry();
156         }
157         assertNotNull(out);
158         assertEquals("foo/", out.getName());
159         assertEquals(TarConstants.LF_DIR, in.getLinkFlag());
160         assertEquals(0, out.getSize());
161         assertEquals(beforeArchiveWrite / 1000, out.getLastModifiedDate().getTime() / 1000);
162         assertTrue(out.isDirectory());
163     }
164 
165     @Test
166     void testExplicitFileEntry() throws Exception {
167         final File file = createTempFile();
168         final File archive = createTempFile("test.", ".tar");
169         try (TarArchiveOutputStream outputStream = new TarArchiveOutputStream(Files.newOutputStream(archive.toPath()))) {
170             final TarArchiveEntry entryIn = new TarArchiveEntry("foo");
171             entryIn.setModTime(file.lastModified());
172             entryIn.setSize(file.length());
173             outputStream.putArchiveEntry(entryIn);
174             outputStream.write(file);
175             outputStream.closeArchiveEntry();
176         }
177         final TarArchiveEntry entryOut;
178         try (TarArchiveInputStream tis = new TarArchiveInputStream(Files.newInputStream(archive.toPath()))) {
179             entryOut = tis.getNextTarEntry();
180         }
181         assertNotNull(entryOut);
182         assertEquals("foo", entryOut.getName());
183         assertEquals(TarConstants.LF_NORMAL, entryOut.getLinkFlag());
184         assertEquals(file.length(), entryOut.getSize());
185         assertEquals(file.lastModified() / 1000, entryOut.getLastModifiedDate().getTime() / 1000);
186         assertFalse(entryOut.isDirectory());
187     }
188 
189     @Test
190     void testFileEntryFromFile() throws Exception {
191         final File file = createTempFile();
192         final File archive = createTempFile("test.", ".tar");
193         final TarArchiveEntry in = new TarArchiveEntry(file, "foo");
194         try (TarArchiveOutputStream outputStream = new TarArchiveOutputStream(Files.newOutputStream(archive.toPath()))) {
195             outputStream.putArchiveEntry(in);
196             outputStream.write(file);
197             outputStream.closeArchiveEntry();
198         }
199         final TarArchiveEntry out;
200         try (TarArchiveInputStream tis = new TarArchiveInputStream(Files.newInputStream(archive.toPath()))) {
201             out = tis.getNextTarEntry();
202         }
203         assertNotNull(out);
204         assertEquals("foo", out.getName());
205         assertEquals(TarConstants.LF_NORMAL, out.getLinkFlag());
206         assertEquals(file.length(), out.getSize());
207         assertEquals(file.lastModified() / 1000, out.getLastModifiedDate().getTime() / 1000);
208         assertFalse(out.isDirectory());
209     }
210 
211     @Test
212     void testLongNameLargerThanBuffer() throws IOException {
213         final List<Integer> nameLength = Arrays.asList(300, 4096);
214 
215         for (final Integer length : nameLength) {
216             final String fileName = createLongName(length);
217             assertEquals(length.intValue(), fileName.length());
218             final byte[] data = createTarWithOneLongNameEntry(fileName);
219             try (ByteArrayInputStream bis = new ByteArrayInputStream(data);
220                     TarArchiveInputStream tis = new TarArchiveInputStream(bis)) {
221                 assertEquals(fileName, tis.getNextTarEntry().getName());
222             }
223         }
224     }
225 
226     @Test
227     void testTarArchiveCreation() throws Exception {
228         final File output = newTempFile("bla.tar");
229         final File file1 = getFile("test1.xml");
230         try (OutputStream out = Files.newOutputStream(output.toPath());
231                 TarArchiveOutputStream os = ArchiveStreamFactory.DEFAULT.createArchiveOutputStream("tar", out)) {
232             final TarArchiveEntry entry = new TarArchiveEntry("testdata/test1.xml");
233             entry.setModTime(0);
234             entry.setSize(file1.length());
235             entry.setUserId(0);
236             entry.setGroupId(0);
237             entry.setUserName("avalon");
238             entry.setGroupName("excalibur");
239             entry.setMode(0100000);
240             os.putArchiveEntry(entry);
241             os.write(file1);
242             os.closeArchiveEntry();
243         }
244     }
245 
246     @Test
247     void testTarArchiveLongNameCreation() throws Exception {
248         final String name = "testdata/12345678901234567890123456789012345678901234567890123456789012345678901234567890123456.xml";
249         final byte[] bytes = name.getBytes(UTF_8);
250         assertEquals(bytes.length, 99);
251 
252         final File output = newTempFile("bla.tar");
253         final File file1 = getFile("test1.xml");
254         final TarArchiveEntry entry = new TarArchiveEntry(name);
255         try (OutputStream out = Files.newOutputStream(output.toPath());
256                 TarArchiveOutputStream os = ArchiveStreamFactory.DEFAULT.createArchiveOutputStream("tar", out)) {
257             entry.setModTime(0);
258             entry.setSize(file1.length());
259             entry.setUserId(0);
260             entry.setGroupId(0);
261             entry.setUserName("avalon");
262             entry.setGroupName("excalibur");
263             entry.setMode(0100000);
264             os.putArchiveEntry(entry);
265             os.write(file1);
266             os.closeArchiveEntry();
267         }
268 
269         final String toLongName = "testdata/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567.xml";
270         final File output2 = newTempFile("bla.tar");
271         try (OutputStream out2 = Files.newOutputStream(output2.toPath());
272                 TarArchiveOutputStream os2 = ArchiveStreamFactory.DEFAULT.createArchiveOutputStream("tar", out2);) {
273             final TarArchiveEntry entry2 = new TarArchiveEntry(toLongName);
274             entry2.setModTime(0);
275             entry2.setSize(file1.length());
276             entry2.setUserId(0);
277             entry2.setGroupId(0);
278             entry2.setUserName("avalon");
279             entry2.setGroupName("excalibur");
280             entry2.setMode(0100000);
281             os2.putArchiveEntry(entry);
282             os2.write(file1);
283             os2.closeArchiveEntry();
284         } catch (final IOException e) {
285             assertTrue(true);
286         }
287     }
288 
289     @Test
290     void testTarFileCOMPRESS114() throws Exception {
291         final File input = getFile("COMPRESS-114.tar");
292         try (TarFile tarFile = new TarFile(input, StandardCharsets.ISO_8859_1.name())) {
293             final List<TarArchiveEntry> entries = tarFile.getEntries();
294             TarArchiveEntry entry = entries.get(0);
295             assertEquals("3\u00b1\u00b1\u00b1F06\u00b1W2345\u00b1ZB\u00b1la\u00b1\u00b1\u00b1\u00b1\u00b1\u00b1\u00b1\u00b1BLA", entry.getName());
296             assertEquals(TarConstants.LF_NORMAL, entry.getLinkFlag());
297             entry = entries.get(1);
298             assertEquals("0302-0601-3\u00b1\u00b1\u00b1F06\u00b1W2345\u00b1ZB\u00b1la\u00b1\u00b1\u00b1\u00b1\u00b1\u00b1\u00b1\u00b1BLA", entry.getName());
299             assertEquals(TarConstants.LF_NORMAL, entry.getLinkFlag());
300         }
301     }
302 
303     @Test
304     void testTarFileCOMPRESS178() throws Exception {
305         final File input = getFile("COMPRESS-178-fail.tar");
306         final IOException e = assertThrows(IOException.class, () -> {
307             try (TarFile tarFile = new TarFile(input)) {
308                 // Compared to the TarArchiveInputStream all entries are read when instantiating the tar file
309             }
310         }, "Expected IOException");
311         final Throwable t = e.getCause();
312         assertInstanceOf(IllegalArgumentException.class, t, "Expected cause = IllegalArgumentException");
313     }
314 
315     @Test
316     void testTarFileCOMPRESS178Lenient() throws Exception {
317         final File input = getFile("COMPRESS-178-fail.tar");
318         try (TarFile tarFile = new TarFile(input, true)) {
319             // Compared to the TarArchiveInputStream all entries are read when instantiating the tar file
320         }
321     }
322 
323     @Test
324     void testTarFileDirectoryEntryFromFile() throws Exception {
325         final File archive = createTempFile("test.", ".tar");
326         final File dir = getTempDirFile();
327         try (TarArchiveOutputStream tos = new TarArchiveOutputStream(Files.newOutputStream(archive.toPath()))) {
328             final long beforeArchiveWrite = dir.lastModified();
329             final TarArchiveEntry in = new TarArchiveEntry(dir, "foo");
330             tos.putArchiveEntry(in);
331             tos.closeArchiveEntry();
332             tos.close();
333             try (TarFile tarFile = new TarFile(archive)) {
334                 final TarArchiveEntry entry = tarFile.getEntries().get(0);
335                 assertNotNull(entry);
336                 assertEquals("foo/", entry.getName());
337                 assertEquals(TarConstants.LF_DIR, entry.getLinkFlag());
338                 assertEquals(0, entry.getSize());
339                 // TAR stores time with a granularity of 1 second
340                 assertEquals(beforeArchiveWrite / 1000, entry.getLastModifiedDate().getTime() / 1000);
341                 assertTrue(entry.isDirectory());
342             }
343         }
344     }
345 
346     @Test
347     void testTarFileDirectoryRead() throws IOException {
348         final File input = getFile("directory.tar");
349         try (TarFile tarFile = new TarFile(input)) {
350             final TarArchiveEntry directoryEntry = tarFile.getEntries().get(0);
351             assertEquals("directory/", directoryEntry.getName());
352             assertEquals(TarConstants.LF_DIR, directoryEntry.getLinkFlag());
353             assertTrue(directoryEntry.isDirectory());
354             try (InputStream directoryStream = tarFile.getInputStream(directoryEntry)) {
355                 final byte[] directoryRead = IOUtils.toByteArray(directoryStream);
356                 assertArrayEquals(ByteUtils.EMPTY_BYTE_ARRAY, directoryRead);
357             }
358         }
359     }
360 
361     @Test
362     void testTarFileEntryFromFile() throws Exception {
363         final File file = createTempFile();
364         final File archive = createTempFile("test.", ".tar");
365         try (TarArchiveOutputStream outputStream = new TarArchiveOutputStream(Files.newOutputStream(archive.toPath()))) {
366             final TarArchiveEntry in = new TarArchiveEntry(file, "foo");
367             outputStream.putArchiveEntry(in);
368             outputStream.write(file);
369             outputStream.closeArchiveEntry();
370             outputStream.close();
371             try (TarFile tarFile = new TarFile(archive)) {
372                 final TarArchiveEntry entry = tarFile.getEntries().get(0);
373                 assertNotNull(entry);
374                 assertEquals("foo", entry.getName());
375                 assertEquals(TarConstants.LF_NORMAL, entry.getLinkFlag());
376                 assertEquals(file.length(), entry.getSize());
377                 assertEquals(file.lastModified() / 1000, entry.getLastModifiedDate().getTime() / 1000);
378                 assertFalse(entry.isDirectory());
379             }
380         }
381     }
382 
383     @Test
384     void testTarFileExplicitDirectoryEntry() throws Exception {
385         final File archive = createTempFile("test.", ".tar");
386         try (TarArchiveOutputStream tos = new TarArchiveOutputStream(Files.newOutputStream(archive.toPath()))) {
387             final long beforeArchiveWrite = getTempDirFile().lastModified();
388             final TarArchiveEntry in = new TarArchiveEntry("foo/");
389             in.setModTime(beforeArchiveWrite);
390             tos.putArchiveEntry(in);
391             tos.closeArchiveEntry();
392             tos.close();
393             try (TarFile tarFile = new TarFile(archive)) {
394                 final TarArchiveEntry entry = tarFile.getEntries().get(0);
395                 assertNotNull(entry);
396                 assertEquals("foo/", entry.getName());
397                 assertEquals(TarConstants.LF_DIR, entry.getLinkFlag());
398                 assertEquals(0, entry.getSize());
399                 assertEquals(beforeArchiveWrite / 1000, entry.getLastModifiedDate().getTime() / 1000);
400                 assertTrue(entry.isDirectory());
401             }
402         }
403     }
404 
405     @Test
406     void testTarFileExplicitFileEntry() throws Exception {
407         final File file = createTempFile();
408         final File archive = createTempFile("test.", ".tar");
409         try (TarArchiveOutputStream outputStream = new TarArchiveOutputStream(Files.newOutputStream(archive.toPath()))) {
410             final TarArchiveEntry in = new TarArchiveEntry("foo");
411             in.setModTime(file.lastModified());
412             in.setSize(file.length());
413             outputStream.putArchiveEntry(in);
414             outputStream.write(file);
415             outputStream.closeArchiveEntry();
416             try (TarFile tarFile = new TarFile(archive)) {
417                 final TarArchiveEntry entry = tarFile.getEntries().get(0);
418                 assertNotNull(entry);
419                 assertEquals("foo", entry.getName());
420                 assertEquals(TarConstants.LF_NORMAL, entry.getLinkFlag());
421                 assertEquals(file.length(), entry.getSize());
422                 assertEquals(file.lastModified() / 1000, entry.getLastModifiedDate().getTime() / 1000);
423                 assertFalse(entry.isDirectory());
424             }
425         }
426     }
427 
428     @Test
429     void testTarFileLongNameLargerThanBuffer() throws IOException {
430         final List<Integer> nameLength = Arrays.asList(300, 4096);
431 
432         for (final Integer length : nameLength) {
433             final String fileName = createLongName(length);
434             assertEquals(length.intValue(), fileName.length());
435             final byte[] data = createTarWithOneLongNameEntry(fileName);
436             try (TarFile tarFile = new TarFile(data)) {
437                 final List<TarArchiveEntry> entries = tarFile.getEntries();
438                 assertEquals(fileName, entries.get(0).getName());
439                 assertEquals(TarConstants.LF_NORMAL, entries.get(0).getLinkFlag());
440             }
441         }
442     }
443 
444     @Test
445     void testTarFileUnarchive() throws Exception {
446         final File file = getFile("bla.tar");
447         try (TarFile tarFile = new TarFile(file)) {
448             final TarArchiveEntry entry = tarFile.getEntries().get(0);
449             try (InputStream inputStream = tarFile.getInputStream(entry)) {
450                 Files.copy(inputStream, newTempFile(entry.getName()).toPath());
451             }
452         }
453     }
454 
455     @Test
456     void testTarUnarchive() throws Exception {
457         final File input = getFile("bla.tar");
458         try (InputStream is = Files.newInputStream(input.toPath());
459                 TarArchiveInputStream in = ArchiveStreamFactory.DEFAULT.createArchiveInputStream("tar", is)) {
460             final TarArchiveEntry entry = in.getNextEntry();
461             Files.copy(in, newTempFile(entry.getName()).toPath());
462         }
463     }
464 }