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 java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.File;
24  import java.io.FileInputStream;
25  import java.io.FileOutputStream;
26  import java.util.Calendar;
27  import java.util.HashMap;
28  import java.util.Map;
29  import java.util.TimeZone;
30  
31  import org.apache.commons.compress.AbstractTestCase;
32  import org.apache.commons.compress.archivers.ArchiveOutputStream;
33  import org.apache.commons.compress.archivers.ArchiveStreamFactory;
34  import org.apache.commons.compress.utils.CharsetNames;
35  
36  public class TarArchiveOutputStreamTest extends AbstractTestCase {
37  
38      public void testCount() throws Exception {
39          File f = File.createTempFile("commons-compress-tarcount", ".tar");
40          f.deleteOnExit();
41          FileOutputStream fos = new FileOutputStream(f);
42  
43          ArchiveOutputStream tarOut = new ArchiveStreamFactory()
44              .createArchiveOutputStream(ArchiveStreamFactory.TAR, fos);
45  
46          File file1 = getFile("test1.xml");
47          TarArchiveEntry sEntry = new TarArchiveEntry(file1);
48          tarOut.putArchiveEntry(sEntry);
49  
50          FileInputStream in = new FileInputStream(file1);
51          byte[] buf = new byte[8192];
52  
53          int read = 0;
54          while ((read = in.read(buf)) > 0) {
55              tarOut.write(buf, 0, read);
56          }
57  
58          in.close();
59          tarOut.closeArchiveEntry();
60          tarOut.close();
61  
62          assertEquals(f.length(), tarOut.getBytesWritten());
63      }
64  
65      public void testMaxFileSizeError() throws Exception {
66          TarArchiveEntry t = new TarArchiveEntry("foo");
67          t.setSize(077777777777L);
68          TarArchiveOutputStream tos =
69              new TarArchiveOutputStream(new ByteArrayOutputStream());
70          tos.putArchiveEntry(t);
71          t.setSize(0100000000000L);
72          tos = new TarArchiveOutputStream(new ByteArrayOutputStream());
73          try {
74              tos.putArchiveEntry(t);
75              fail("Should have generated RuntimeException");
76          } catch (RuntimeException expected) {
77          }
78      }
79  
80      public void testBigNumberStarMode() throws Exception {
81          TarArchiveEntry t = new TarArchiveEntry("foo");
82          t.setSize(0100000000000L);
83          ByteArrayOutputStream bos = new ByteArrayOutputStream();
84          TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
85          tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
86          tos.putArchiveEntry(t);
87          // make sure header is written to byte array
88          tos.write(new byte[10 * 1024]);
89          byte[] data = bos.toByteArray();
90          assertEquals(0x80,
91                       data[TarConstants.NAMELEN
92                          + TarConstants.MODELEN
93                          + TarConstants.UIDLEN
94                          + TarConstants.GIDLEN] & 0x80);
95          TarArchiveInputStream tin =
96              new TarArchiveInputStream(new ByteArrayInputStream(data));
97          TarArchiveEntry e = tin.getNextTarEntry();
98          assertEquals(0100000000000L, e.getSize());
99          tin.close();
100         // generates IOE because of unclosed entries.
101         // However we don't really want to create such large entries.
102         closeQuietly(tos);
103     }
104 
105     public void testBigNumberPosixMode() throws Exception {
106         TarArchiveEntry t = new TarArchiveEntry("foo");
107         t.setSize(0100000000000L);
108         ByteArrayOutputStream bos = new ByteArrayOutputStream();
109         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
110         tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
111         tos.putArchiveEntry(t);
112         // make sure header is written to byte array
113         tos.write(new byte[10 * 1024]);
114         byte[] data = bos.toByteArray();
115         assertEquals("00000000000 ",
116                      new String(data,
117                                 1024 + TarConstants.NAMELEN
118                                 + TarConstants.MODELEN
119                                 + TarConstants.UIDLEN
120                                 + TarConstants.GIDLEN, 12,
121                                 CharsetNames.UTF_8));
122         TarArchiveInputStream tin =
123             new TarArchiveInputStream(new ByteArrayInputStream(data));
124         TarArchiveEntry e = tin.getNextTarEntry();
125         assertEquals(0100000000000L, e.getSize());
126         tin.close();
127         // generates IOE because of unclosed entries.
128         // However we don't really want to create such large entries.
129         closeQuietly(tos);
130     }
131 
132     public void testWriteSimplePaxHeaders() throws Exception {
133         Map<String, String> m = new HashMap<String, String>();
134         m.put("a", "b");
135         byte[] data = writePaxHeader(m);
136         assertEquals("00000000006 ",
137                      new String(data, TarConstants.NAMELEN
138                                 + TarConstants.MODELEN
139                                 + TarConstants.UIDLEN
140                                 + TarConstants.GIDLEN, 12,
141                                 CharsetNames.UTF_8));
142         assertEquals("6 a=b\n", new String(data, 512, 6, CharsetNames.UTF_8));
143     }
144 
145     public void testPaxHeadersWithLength99() throws Exception {
146         Map<String, String> m = new HashMap<String, String>();
147         m.put("a",
148               "0123456789012345678901234567890123456789"
149               + "01234567890123456789012345678901234567890123456789"
150               + "012");
151         byte[] data = writePaxHeader(m);
152         assertEquals("00000000143 ",
153                      new String(data, TarConstants.NAMELEN
154                                 + TarConstants.MODELEN
155                                 + TarConstants.UIDLEN
156                                 + TarConstants.GIDLEN, 12,
157                                 CharsetNames.UTF_8));
158         assertEquals("99 a=0123456789012345678901234567890123456789"
159               + "01234567890123456789012345678901234567890123456789"
160               + "012\n", new String(data, 512, 99, CharsetNames.UTF_8));
161     }
162 
163     public void testPaxHeadersWithLength101() throws Exception {
164         Map<String, String> m = new HashMap<String, String>();
165         m.put("a",
166               "0123456789012345678901234567890123456789"
167               + "01234567890123456789012345678901234567890123456789"
168               + "0123");
169         byte[] data = writePaxHeader(m);
170         assertEquals("00000000145 ",
171                      new String(data, TarConstants.NAMELEN
172                                 + TarConstants.MODELEN
173                                 + TarConstants.UIDLEN
174                                 + TarConstants.GIDLEN, 12,
175                                 CharsetNames.UTF_8));
176         assertEquals("101 a=0123456789012345678901234567890123456789"
177               + "01234567890123456789012345678901234567890123456789"
178               + "0123\n", new String(data, 512, 101, CharsetNames.UTF_8));
179     }
180 
181     private byte[] writePaxHeader(Map<String, String> m) throws Exception {
182         ByteArrayOutputStream bos = new ByteArrayOutputStream();
183         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
184         tos.writePaxHeaders("foo", m);
185 
186         // add a dummy entry so data gets written
187         TarArchiveEntry t = new TarArchiveEntry("foo");
188         t.setSize(10 * 1024);
189         tos.putArchiveEntry(t);
190         tos.write(new byte[10 * 1024]);
191         tos.closeArchiveEntry();
192         tos.close();
193 
194         return bos.toByteArray();
195     }
196 
197     public void testWriteLongFileNamePosixMode() throws Exception {
198         String n = "01234567890123456789012345678901234567890123456789"
199             + "01234567890123456789012345678901234567890123456789"
200             + "01234567890123456789012345678901234567890123456789";
201         TarArchiveEntry t =
202             new TarArchiveEntry(n);
203         t.setSize(10 * 1024);
204         ByteArrayOutputStream bos = new ByteArrayOutputStream();
205         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
206         tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
207         tos.putArchiveEntry(t);
208         tos.write(new byte[10 * 1024]);
209         tos.closeArchiveEntry();
210         byte[] data = bos.toByteArray();
211         assertEquals("160 path=" + n + "\n",
212                      new String(data, 512, 160, CharsetNames.UTF_8));
213         TarArchiveInputStream tin =
214             new TarArchiveInputStream(new ByteArrayInputStream(data));
215         TarArchiveEntry e = tin.getNextTarEntry();
216         assertEquals(n, e.getName());
217         tin.close();
218         tos.close();
219     }
220 
221     public void testOldEntryStarMode() throws Exception {
222         TarArchiveEntry t = new TarArchiveEntry("foo");
223         t.setSize(Integer.MAX_VALUE);
224         t.setModTime(-1000);
225         ByteArrayOutputStream bos = new ByteArrayOutputStream();
226         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
227         tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
228         tos.putArchiveEntry(t);
229         // make sure header is written to byte array
230         tos.write(new byte[10 * 1024]);
231         byte[] data = bos.toByteArray();
232         assertEquals((byte) 0xff,
233                      data[TarConstants.NAMELEN
234                           + TarConstants.MODELEN
235                           + TarConstants.UIDLEN
236                           + TarConstants.GIDLEN
237                           + TarConstants.SIZELEN]);
238         TarArchiveInputStream tin =
239             new TarArchiveInputStream(new ByteArrayInputStream(data));
240         TarArchiveEntry e = tin.getNextTarEntry();
241         Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
242         cal.set(1969, 11, 31, 23, 59, 59);
243         cal.set(Calendar.MILLISECOND, 0);
244         assertEquals(cal.getTime(), e.getLastModifiedDate());
245         tin.close();
246         // generates IOE because of unclosed entries.
247         // However we don't really want to create such large entries.
248         closeQuietly(tos);
249     }
250 
251     public void testOldEntryPosixMode() throws Exception {
252         TarArchiveEntry t = new TarArchiveEntry("foo");
253         t.setSize(Integer.MAX_VALUE);
254         t.setModTime(-1000);
255         ByteArrayOutputStream bos = new ByteArrayOutputStream();
256         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
257         tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
258         tos.putArchiveEntry(t);
259         // make sure header is written to byte array
260         tos.write(new byte[10 * 1024]);
261         byte[] data = bos.toByteArray();
262         assertEquals("00000000000 ",
263                      new String(data,
264                                 1024 + TarConstants.NAMELEN
265                                 + TarConstants.MODELEN
266                                 + TarConstants.UIDLEN
267                                 + TarConstants.GIDLEN
268                                 + TarConstants.SIZELEN, 12,
269                                 CharsetNames.UTF_8));
270         TarArchiveInputStream tin =
271             new TarArchiveInputStream(new ByteArrayInputStream(data));
272         TarArchiveEntry e = tin.getNextTarEntry();
273         Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
274         cal.set(1969, 11, 31, 23, 59, 59);
275         cal.set(Calendar.MILLISECOND, 0);
276         assertEquals(cal.getTime(), e.getLastModifiedDate());
277         tin.close();
278         // generates IOE because of unclosed entries.
279         // However we don't really want to create such large entries.
280         closeQuietly(tos);
281     }
282 
283     public void testOldEntryError() throws Exception {
284         TarArchiveEntry t = new TarArchiveEntry("foo");
285         t.setSize(Integer.MAX_VALUE);
286         t.setModTime(-1000);
287         TarArchiveOutputStream tos =
288             new TarArchiveOutputStream(new ByteArrayOutputStream());
289         try {
290             tos.putArchiveEntry(t);
291             fail("Should have generated RuntimeException");
292         } catch (RuntimeException expected) {
293         }
294         tos.close();
295     }
296 
297     public void testWriteNonAsciiPathNamePaxHeader() throws Exception {
298         String n = "\u00e4";
299         TarArchiveEntry t = new TarArchiveEntry(n);
300         t.setSize(10 * 1024);
301         ByteArrayOutputStream bos = new ByteArrayOutputStream();
302         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
303         tos.setAddPaxHeadersForNonAsciiNames(true);
304         tos.putArchiveEntry(t);
305         tos.write(new byte[10 * 1024]);
306         tos.closeArchiveEntry();
307         tos.close();
308         byte[] data = bos.toByteArray();
309         assertEquals("11 path=" + n + "\n",
310                      new String(data, 512, 11, CharsetNames.UTF_8));
311         TarArchiveInputStream tin =
312             new TarArchiveInputStream(new ByteArrayInputStream(data));
313         TarArchiveEntry e = tin.getNextTarEntry();
314         assertEquals(n, e.getName());
315         tin.close();
316     }
317 
318     public void testWriteNonAsciiLinkPathNamePaxHeader() throws Exception {
319         String n = "\u00e4";
320         TarArchiveEntry t = new TarArchiveEntry("a", TarConstants.LF_LINK);
321         t.setSize(10 * 1024);
322         t.setLinkName(n);
323         ByteArrayOutputStream bos = new ByteArrayOutputStream();
324         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
325         tos.setAddPaxHeadersForNonAsciiNames(true);
326         tos.putArchiveEntry(t);
327         tos.write(new byte[10 * 1024]);
328         tos.closeArchiveEntry();
329         tos.close();
330         byte[] data = bos.toByteArray();
331         assertEquals("15 linkpath=" + n + "\n",
332                      new String(data, 512, 15, CharsetNames.UTF_8));
333         TarArchiveInputStream tin =
334             new TarArchiveInputStream(new ByteArrayInputStream(data));
335         TarArchiveEntry e = tin.getNextTarEntry();
336         assertEquals(n, e.getLinkName());
337         tin.close();
338     }
339 
340     /**
341      * @see "https://issues.apache.org/jira/browse/COMPRESS-200"
342      */
343     public void testRoundtripWith67CharFileNameGnu() throws Exception {
344         testRoundtripWith67CharFileName(TarArchiveOutputStream.LONGFILE_GNU);
345     }
346 
347     /**
348      * @see "https://issues.apache.org/jira/browse/COMPRESS-200"
349      */
350     public void testRoundtripWith67CharFileNamePosix() throws Exception {
351         testRoundtripWith67CharFileName(TarArchiveOutputStream.LONGFILE_POSIX);
352     }
353 
354     private void testRoundtripWith67CharFileName(int mode) throws Exception {
355         String n = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
356             + "AAAAAAA";
357         assertEquals(67, n.length());
358         TarArchiveEntry t = new TarArchiveEntry(n);
359         t.setSize(10 * 1024);
360         ByteArrayOutputStream bos = new ByteArrayOutputStream();
361         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
362         tos.setLongFileMode(mode);
363         tos.putArchiveEntry(t);
364         tos.write(new byte[10 * 1024]);
365         tos.closeArchiveEntry();
366         tos.close();
367         byte[] data = bos.toByteArray();
368         TarArchiveInputStream tin =
369             new TarArchiveInputStream(new ByteArrayInputStream(data));
370         TarArchiveEntry e = tin.getNextTarEntry();
371         assertEquals(n, e.getName());
372         tin.close();
373     }
374 
375     /**
376      * @see "https://issues.apache.org/jira/browse/COMPRESS-203"
377      */
378     public void testWriteLongDirectoryNameGnuMode() throws Exception {
379         testWriteLongDirectoryName(TarArchiveOutputStream.LONGFILE_GNU);
380     }
381 
382     /**
383      * @see "https://issues.apache.org/jira/browse/COMPRESS-203"
384      */
385     public void testWriteLongDirectoryNamePosixMode() throws Exception {
386         testWriteLongDirectoryName(TarArchiveOutputStream.LONGFILE_POSIX);
387     }
388 
389     private void testWriteLongDirectoryName(int mode) throws Exception {
390         String n = "01234567890123456789012345678901234567890123456789"
391             + "01234567890123456789012345678901234567890123456789"
392             + "01234567890123456789012345678901234567890123456789/";
393         TarArchiveEntry t = new TarArchiveEntry(n);
394         ByteArrayOutputStream bos = new ByteArrayOutputStream();
395         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
396         tos.setLongFileMode(mode);
397         tos.putArchiveEntry(t);
398         tos.closeArchiveEntry();
399         tos.close();
400         byte[] data = bos.toByteArray();
401         TarArchiveInputStream tin =
402             new TarArchiveInputStream(new ByteArrayInputStream(data));
403         TarArchiveEntry e = tin.getNextTarEntry();
404         assertEquals(n, e.getName());
405         assertTrue(e.isDirectory());
406         tin.close();
407     }
408 
409     /**
410      * @see "https://issues.apache.org/jira/browse/COMPRESS-203"
411      */
412     public void testWriteNonAsciiDirectoryNamePosixMode() throws Exception {
413         String n = "f\u00f6\u00f6/";
414         TarArchiveEntry t = new TarArchiveEntry(n);
415         ByteArrayOutputStream bos = new ByteArrayOutputStream();
416         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
417         tos.setAddPaxHeadersForNonAsciiNames(true);
418         tos.putArchiveEntry(t);
419         tos.closeArchiveEntry();
420         tos.close();
421         byte[] data = bos.toByteArray();
422         TarArchiveInputStream tin =
423             new TarArchiveInputStream(new ByteArrayInputStream(data));
424         TarArchiveEntry e = tin.getNextTarEntry();
425         assertEquals(n, e.getName());
426         assertTrue(e.isDirectory());
427         tin.close();
428     }
429 
430 }