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;
20  
21  import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
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.assertInstanceOf;
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.BufferedInputStream;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.nio.file.Files;
34  import java.nio.file.Path;
35  import java.nio.file.attribute.FileTime;
36  import java.time.Instant;
37  import java.util.Collections;
38  
39  import org.apache.commons.compress.archivers.ArchiveEntry;
40  import org.apache.commons.compress.archivers.ArchiveException;
41  import org.apache.commons.compress.archivers.ArchiveInputStream;
42  import org.apache.commons.compress.archivers.ArchiveStreamFactory;
43  import org.apache.commons.compress.archivers.ar.ArArchiveInputStream;
44  import org.apache.commons.compress.archivers.arj.ArjArchiveInputStream;
45  import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream;
46  import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
47  import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
48  import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
49  import org.junit.jupiter.api.Test;
50  
51  public final class DetectArchiverTest extends AbstractTest {
52  
53      final ClassLoader classLoader = getClass().getClassLoader();
54  
55      private void checkDetectedType(final String type, final Path path) throws ArchiveException, IOException {
56          try (BufferedInputStream inputStream = new BufferedInputStream(Files.newInputStream(path))) {
57              // SPECIAL case:
58              // For now JAR files are detected as ZIP files.
59              assertEquals(ArchiveStreamFactory.JAR.equals(type) ? ArchiveStreamFactory.ZIP : type, ArchiveStreamFactory.detect(inputStream));
60          }
61      }
62  
63      private void checkEmptyArchive(final String type) throws Exception {
64          final Path path = createEmptyArchive(type); // will be deleted by tearDown()
65          assertDoesNotThrow(() -> {
66              try (BufferedInputStream inputStream = new BufferedInputStream(Files.newInputStream(path));
67                      ArchiveInputStream<?> ais = factory.createArchiveInputStream(inputStream)) {
68                  // empty
69              }
70          }, "Should have recognized empty archive for " + type);
71          checkDetectedType(type, path);
72      }
73  
74      @SuppressWarnings("resource") // Caller closes
75      private <T extends ArchiveInputStream<? extends E>, E extends ArchiveEntry> T createArchiveInputStream(final String resource)
76              throws ArchiveException, IOException {
77          return factory.createArchiveInputStream(createBufferedInputStream(resource));
78      }
79  
80      private BufferedInputStream createBufferedInputStream(final String resource) throws IOException {
81          return new BufferedInputStream(newInputStream(resource));
82      }
83  
84      private boolean isValidName(final String value) {
85         return value.isEmpty() || value.chars().allMatch(ch -> ch > 31 && ch < 128);
86      }
87  
88      @Test
89      void testCOMPRESS_117() throws Exception {
90          try (ArchiveInputStream<?> tar = createArchiveInputStream("COMPRESS-117.tar")) {
91              assertNotNull(tar);
92              assertInstanceOf(TarArchiveInputStream.class, tar);
93          }
94      }
95  
96      @Test
97      void testCOMPRESS_335() throws Exception {
98          try (ArchiveInputStream<?> tar = createArchiveInputStream("COMPRESS-335.tar")) {
99              assertNotNull(tar);
100             assertInstanceOf(TarArchiveInputStream.class, tar);
101         }
102     }
103 
104     @Test
105     void testDetection() throws Exception {
106 
107         try (ArchiveInputStream<?> ar = createArchiveInputStream("bla.ar")) {
108             assertNotNull(ar);
109             assertInstanceOf(ArArchiveInputStream.class, ar);
110         }
111 
112         try (ArchiveInputStream<?> tar = createArchiveInputStream("bla.tar")) {
113             assertNotNull(tar);
114             assertInstanceOf(TarArchiveInputStream.class, tar);
115         }
116 
117         try (ArchiveInputStream<?> zip = createArchiveInputStream("bla.zip")) {
118             assertNotNull(zip);
119             assertInstanceOf(ZipArchiveInputStream.class, zip);
120         }
121 
122         try (ArchiveInputStream<?> jar = createArchiveInputStream("bla.jar")) {
123             assertNotNull(jar);
124             assertInstanceOf(ZipArchiveInputStream.class, jar);
125         }
126 
127         try (ArchiveInputStream<?> cpio = createArchiveInputStream("bla.cpio")) {
128             assertNotNull(cpio);
129             assertInstanceOf(CpioArchiveInputStream.class, cpio);
130         }
131 
132         try (ArchiveInputStream<?> arj = createArchiveInputStream("bla.arj")) {
133             assertNotNull(arj);
134             assertInstanceOf(ArjArchiveInputStream.class, arj);
135         }
136 
137 // Not yet implemented
138 //        final ArchiveInputStream<?> tgz = getStreamFor("bla.tgz");
139 //        assertNotNull(tgz);
140 //        assertInstanceOf(TarArchiveInputStream.class, tgz);
141 
142     }
143 
144     // Check that the empty archives created by the code are readable
145 
146     // Not possible to detect empty "ar" archive as it is completely empty
147 //    void testEmptyArArchive() throws Exception {
148 //        emptyArchive("ar");
149 //    }
150 
151     @Test
152     void testDetectionNotArchive() {
153         assertThrows(ArchiveException.class, () -> createArchiveInputStream("test.txt"));
154     }
155 
156     @Test
157     void testDetectOldTarFormatArchive() throws Exception {
158         try (ArchiveInputStream<?> tar = createArchiveInputStream("COMPRESS-612/test-times-star-folder.tar")) {
159             assertNotNull(tar);
160             assertInstanceOf(TarArchiveInputStream.class, tar);
161         }
162     }
163 
164     @Test
165     void testEmptyCpioArchive() throws Exception {
166         checkEmptyArchive("cpio");
167     }
168 
169     @Test
170     void testEmptyJarArchive() throws Exception {
171         checkEmptyArchive("jar");
172     }
173 
174     @Test
175     void testEmptyTarArchive() throws Exception {
176         // Can't detect empty tar archive from its contents.
177         final Path path = createEmptyArchive("tar"); // will be deleted by tearDown()
178         assertThrows(ArchiveException.class, () -> checkDetectedType("tar", path));
179     }
180 
181     @Test
182     void testEmptyZipArchive() throws Exception {
183         checkEmptyArchive("zip");
184     }
185 
186     /**
187      * Tests COMPRESS-644.
188      */
189     @Test
190     void testIcoFile() {
191         assertThrows(ArchiveException.class, () -> {
192             try (InputStream in = createBufferedInputStream("org/apache/commons/compress/COMPRESS-644/ARW05UP.ICO")) {
193                 assertNull(ArchiveStreamFactory.detect(in));
194             }
195         });
196     }
197 
198     @Test
199     void testIcoFileFirstTarArchiveEntry() throws Exception {
200         try (TarArchiveInputStream inputStream = new TarArchiveInputStream(createBufferedInputStream("org/apache/commons/compress/COMPRESS-644/ARW05UP.ICO"))) {
201             final TarArchiveEntry entry = inputStream.getNextEntry();
202             // Find hints that the file is not a TAR file.
203             assertNull(entry.getCreationTime());
204             assertEquals(-1, entry.getDataOffset());
205             assertEquals(0, entry.getDevMajor());
206             assertEquals(0, entry.getDevMinor());
207             assertEquals(Collections.emptyMap(), entry.getExtraPaxHeaders());
208             assertEquals(0, entry.getGroupId());
209             assertFalse(isValidName(entry.getGroupName()), entry::getGroupName); // hint
210             assertNull(entry.getLastAccessTime());
211             assertEquals(0, entry.getLastModifiedDate().getTime());  // hint
212             assertEquals(FileTime.from(Instant.EPOCH), entry.getLastModifiedTime()); // hint
213             assertEquals(0, entry.getLinkFlag()); // NUL (not really a hint)
214             assertEquals("", entry.getLinkName());
215             assertEquals(0, entry.getLongGroupId());
216             assertEquals(16777215, entry.getLongUserId()); // hint?
217             assertEquals(0, entry.getMode()); // hint?
218             assertEquals(0, entry.getModTime().getTime()); // hint?
219             assertFalse(isValidName(entry.getName()), entry::getName); // hint
220             assertNull(entry.getPath());
221             assertEquals(0, entry.getRealSize());
222             assertEquals(0, entry.getSize());
223             assertNull(entry.getStatusChangeTime());
224             assertEquals(16777215, entry.getUserId()); // hint?
225             assertFalse(isValidName(entry.getUserName()), entry::getUserName); // hint
226             assertTrue(entry.isFile());
227             assertFalse(entry.isBlockDevice());
228             assertFalse(entry.isCharacterDevice());
229             assertTrue(entry.isCheckSumOK());
230             assertFalse(entry.isDirectory());
231             assertFalse(entry.isExtended());
232             assertFalse(entry.isFIFO());
233             assertFalse(entry.isGlobalPaxHeader());
234             assertFalse(entry.isGNULongLinkEntry());
235             assertFalse(entry.isGNULongNameEntry());
236             assertFalse(entry.isGNUSparse());
237             assertFalse(entry.isLink());
238             assertFalse(entry.isOldGNUSparse());
239             assertFalse(entry.isPaxGNU1XSparse());
240             assertFalse(entry.isPaxGNUSparse());
241             assertFalse(entry.isPaxHeader());
242             assertFalse(entry.isSparse());
243             assertFalse(entry.isStarSparse());
244             assertTrue(entry.isStreamContiguous());
245             assertFalse(entry.isSymbolicLink());
246         }
247     }
248 }