View Javadoc
1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one or more
3    *  contributor license agreements.  See the NOTICE file distributed with
4    *  this work for additional information regarding copyright ownership.
5    *  The ASF licenses this file to You under the Apache License, Version 2.0
6    *  (the "License"); you may not use this file except in compliance with
7    *  the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   *
17   */
18  
19  package org.apache.commons.compress.archivers.tar;
20  
21  import static org.apache.commons.compress.AbstractTestCase.getFile;
22  import static org.apache.commons.compress.AbstractTestCase.mkdir;
23  import static org.apache.commons.compress.AbstractTestCase.rmdir;
24  import static org.junit.Assert.assertArrayEquals;
25  import static org.junit.Assert.assertEquals;
26  import static org.junit.Assert.assertTrue;
27  import static org.junit.Assert.fail;
28  
29  import java.io.ByteArrayInputStream;
30  import java.io.ByteArrayOutputStream;
31  import java.io.File;
32  import java.io.FileInputStream;
33  import java.io.FileOutputStream;
34  import java.io.IOException;
35  import java.io.InputStream;
36  import java.util.Calendar;
37  import java.util.Date;
38  import java.util.Map;
39  import java.util.TimeZone;
40  import java.util.zip.GZIPInputStream;
41  
42  import org.apache.commons.compress.utils.CharsetNames;
43  import org.apache.commons.compress.utils.IOUtils;
44  import org.junit.Test;
45  
46  public class TarArchiveInputStreamTest {
47  
48      @Test
49      public void readSimplePaxHeader() throws Exception {
50          final InputStream is = new ByteArrayInputStream(new byte[1]);
51          final TarArchiveInputStream tais = new TarArchiveInputStream(is);
52          Map<String, String> headers = tais
53              .parsePaxHeaders(new ByteArrayInputStream("30 atime=1321711775.972059463\n"
54                                                        .getBytes(CharsetNames.UTF_8)));
55          assertEquals(1, headers.size());
56          assertEquals("1321711775.972059463", headers.get("atime"));
57          tais.close();
58      }
59  
60      @Test
61      public void secondEntryWinsWhenPaxHeaderContainsDuplicateKey() throws Exception {
62          final InputStream is = new ByteArrayInputStream(new byte[1]);
63          final TarArchiveInputStream tais = new TarArchiveInputStream(is);
64          Map<String, String> headers = tais
65              .parsePaxHeaders(new ByteArrayInputStream("11 foo=bar\n11 foo=baz\n"
66                                                        .getBytes(CharsetNames.UTF_8)));
67          assertEquals(1, headers.size());
68          assertEquals("baz", headers.get("foo"));
69          tais.close();
70      }
71  
72      @Test
73      public void paxHeaderEntryWithEmptyValueRemovesKey() throws Exception {
74          final InputStream is = new ByteArrayInputStream(new byte[1]);
75          final TarArchiveInputStream tais = new TarArchiveInputStream(is);
76          Map<String, String> headers = tais
77              .parsePaxHeaders(new ByteArrayInputStream("11 foo=bar\n7 foo=\n"
78                                                        .getBytes(CharsetNames.UTF_8)));
79          assertEquals(0, headers.size());
80      }
81  
82      @Test
83      public void readPaxHeaderWithEmbeddedNewline() throws Exception {
84          final InputStream is = new ByteArrayInputStream(new byte[1]);
85          final TarArchiveInputStream tais = new TarArchiveInputStream(is);
86          Map<String, String> headers = tais
87              .parsePaxHeaders(new ByteArrayInputStream("28 comment=line1\nline2\nand3\n"
88                                                        .getBytes(CharsetNames.UTF_8)));
89          assertEquals(1, headers.size());
90          assertEquals("line1\nline2\nand3", headers.get("comment"));
91          tais.close();
92      }
93  
94      @Test
95      public void readNonAsciiPaxHeader() throws Exception {
96          String ae = "\u00e4";
97          String line = "11 path="+ ae + "\n";
98          assertEquals(11, line.getBytes(CharsetNames.UTF_8).length);
99          final InputStream is = new ByteArrayInputStream(new byte[1]);
100         final TarArchiveInputStream tais = new TarArchiveInputStream(is);
101         Map<String, String> headers = tais
102             .parsePaxHeaders(new ByteArrayInputStream(line.getBytes(CharsetNames.UTF_8)));
103         assertEquals(1, headers.size());
104         assertEquals(ae, headers.get("path"));
105         tais.close();
106     }
107 
108     @Test
109     public void workaroundForBrokenTimeHeader() throws Exception {
110         TarArchiveInputStream in = null;
111         try {
112             in = new TarArchiveInputStream(new FileInputStream(getFile("simple-aix-native-tar.tar")));
113             TarArchiveEntry tae = in.getNextTarEntry();
114             tae = in.getNextTarEntry();
115             assertEquals("sample/link-to-txt-file.lnk", tae.getName());
116             assertEquals(new Date(0), tae.getLastModifiedDate());
117             assertTrue(tae.isSymbolicLink());
118             assertTrue(tae.isCheckSumOK());
119         } finally {
120             if (in != null) {
121                 in.close();
122             }
123         }
124     }
125 
126     @Test
127     public void datePriorToEpochInGNUFormat() throws Exception {
128         datePriorToEpoch("preepoch-star.tar");
129     }
130 
131 
132     @Test
133     public void datePriorToEpochInPAXFormat() throws Exception {
134         datePriorToEpoch("preepoch-posix.tar");
135     }
136 
137     private void datePriorToEpoch(String archive) throws Exception {
138         TarArchiveInputStream in = null;
139         try {
140             in = new TarArchiveInputStream(new FileInputStream(getFile(archive)));
141             TarArchiveEntry tae = in.getNextTarEntry();
142             assertEquals("foo", tae.getName());
143             Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
144             cal.set(1969, 11, 31, 23, 59, 59);
145             cal.set(Calendar.MILLISECOND, 0);
146             assertEquals(cal.getTime(), tae.getLastModifiedDate());
147             assertTrue(tae.isCheckSumOK());
148         } finally {
149             if (in != null) {
150                 in.close();
151             }
152         }
153     }
154 
155     @Test
156     public void testCompress197() throws Exception {
157         TarArchiveInputStream tar = getTestStream("/COMPRESS-197.tar");
158         try {
159             TarArchiveEntry entry = tar.getNextTarEntry();
160             while (entry != null) {
161                 entry = tar.getNextTarEntry();
162             }
163         } catch (IOException e) {
164             fail("COMPRESS-197: " + e.getMessage());
165         } finally {
166             tar.close();
167         }
168     }
169 
170     @Test
171     public void shouldUseSpecifiedEncodingWhenReadingGNULongNames()
172         throws Exception {
173         ByteArrayOutputStream bos = new ByteArrayOutputStream();
174         String encoding = CharsetNames.UTF_16;
175         String name = "1234567890123456789012345678901234567890123456789"
176             + "01234567890123456789012345678901234567890123456789"
177             + "01234567890\u00e4";
178         TarArchiveOutputStream tos =
179             new TarArchiveOutputStream(bos, encoding);
180         tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
181         TarArchiveEntry t = new TarArchiveEntry(name);
182         t.setSize(1);
183         tos.putArchiveEntry(t);
184         tos.write(30);
185         tos.closeArchiveEntry();
186         tos.close();
187         byte[] data = bos.toByteArray();
188         ByteArrayInputStream bis = new ByteArrayInputStream(data);
189         TarArchiveInputStream tis =
190             new TarArchiveInputStream(bis, encoding);
191         t = tis.getNextTarEntry();
192         assertEquals(name, t.getName());
193         tis.close();
194     }
195 
196     @Test
197     public void shouldConsumeArchiveCompletely() throws Exception {
198         InputStream is = TarArchiveInputStreamTest.class
199             .getResourceAsStream("/archive_with_trailer.tar");
200         TarArchiveInputStream tar = new TarArchiveInputStream(is);
201         while (tar.getNextTarEntry() != null) {
202             // just consume the archive
203         }
204         byte[] expected = new byte[] {
205             'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\n'
206         };
207         byte[] actual = new byte[expected.length];
208         is.read(actual);
209         assertArrayEquals(expected, actual);
210         tar.close();
211     }
212 
213     @Test
214     public void readsArchiveCompletely_COMPRESS245() throws Exception {
215         InputStream is = TarArchiveInputStreamTest.class
216             .getResourceAsStream("/COMPRESS-245.tar.gz");
217         try {
218             InputStream gin = new GZIPInputStream(is);
219             TarArchiveInputStream tar = new TarArchiveInputStream(gin);
220             int count = 0;
221             TarArchiveEntry entry = tar.getNextTarEntry();
222             while (entry != null) {
223                 count++;
224                 entry = tar.getNextTarEntry();
225             }
226             assertEquals(31, count);
227             tar.close();
228         } catch (IOException e) {
229             fail("COMPRESS-245: " + e.getMessage());
230         } finally {
231             is.close();
232         }
233     }
234 
235     @Test(expected = IOException.class)
236     public void shouldThrowAnExceptionOnTruncatedEntries() throws Exception {
237         File dir = mkdir("COMPRESS-279");
238         TarArchiveInputStream is = getTestStream("/COMPRESS-279.tar");
239         FileOutputStream out = null;
240         try {
241             TarArchiveEntry entry = is.getNextTarEntry();
242             int count = 0;
243             while (entry != null) {
244                 out = new FileOutputStream(new File(dir, String.valueOf(count)));
245                 IOUtils.copy(is, out);
246                 out.close();
247                 out = null;
248                 count++;
249                 entry = is.getNextTarEntry();
250             }
251         } finally {
252             is.close();
253             if (out != null) {
254                 out.close();
255             }
256             rmdir(dir);
257         }
258     }
259 
260     @Test
261     public void shouldReadBigGid() throws Exception {
262         ByteArrayOutputStream bos = new ByteArrayOutputStream();
263         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
264         tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
265         TarArchiveEntry t = new TarArchiveEntry("name");
266         t.setGroupId(4294967294l);
267         t.setSize(1);
268         tos.putArchiveEntry(t);
269         tos.write(30);
270         tos.closeArchiveEntry();
271         tos.close();
272         byte[] data = bos.toByteArray();
273         ByteArrayInputStream bis = new ByteArrayInputStream(data);
274         TarArchiveInputStream tis =
275             new TarArchiveInputStream(bis);
276         t = tis.getNextTarEntry();
277         assertEquals(4294967294l, t.getLongGroupId());
278         tis.close();
279     }
280 
281     /**
282      * @link "https://issues.apache.org/jira/browse/COMPRESS-324"
283      */
284     @Test
285     public void shouldReadGNULongNameEntryWithWrongName() throws Exception {
286         TarArchiveInputStream is = getTestStream("/COMPRESS-324.tar");
287         try {
288             TarArchiveEntry entry = is.getNextTarEntry();
289             assertEquals("1234567890123456789012345678901234567890123456789012345678901234567890"
290                          + "1234567890123456789012345678901234567890123456789012345678901234567890"
291                          + "1234567890123456789012345678901234567890123456789012345678901234567890"
292                          + "1234567890123456789012345678901234567890.txt",
293                          entry.getName());
294         } finally {
295             is.close();
296         }
297     }
298 
299     private TarArchiveInputStream getTestStream(String name) {
300         return new TarArchiveInputStream(
301                 TarArchiveInputStreamTest.class.getResourceAsStream(name));
302     }
303 
304 }