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.junit.Assert.*;
22  
23  import java.io.ByteArrayInputStream;
24  import java.io.ByteArrayOutputStream;
25  import java.io.File;
26  import java.io.FileInputStream;
27  import java.io.FileOutputStream;
28  import java.io.IOException;
29  import java.security.MessageDigest;
30  import java.util.Calendar;
31  import java.util.Date;
32  import java.util.HashMap;
33  import java.util.Map;
34  import java.util.TimeZone;
35  
36  import org.apache.commons.compress.AbstractTestCase;
37  import org.apache.commons.compress.archivers.ArchiveEntry;
38  import org.apache.commons.compress.archivers.ArchiveOutputStream;
39  import org.apache.commons.compress.archivers.ArchiveStreamFactory;
40  import org.apache.commons.compress.utils.CharsetNames;
41  import org.apache.commons.compress.utils.IOUtils;
42  import org.junit.Assert;
43  import org.junit.Test;
44  
45  public class TarArchiveOutputStreamTest extends AbstractTestCase {
46  
47      @Test
48      public void testCount() throws Exception {
49          File f = File.createTempFile("commons-compress-tarcount", ".tar");
50          f.deleteOnExit();
51          FileOutputStream fos = new FileOutputStream(f);
52  
53          ArchiveOutputStream tarOut = new ArchiveStreamFactory()
54              .createArchiveOutputStream(ArchiveStreamFactory.TAR, fos);
55  
56          File file1 = getFile("test1.xml");
57          TarArchiveEntry sEntry = new TarArchiveEntry(file1, file1.getName());
58          tarOut.putArchiveEntry(sEntry);
59  
60          FileInputStream in = new FileInputStream(file1);
61          byte[] buf = new byte[8192];
62  
63          int read = 0;
64          while ((read = in.read(buf)) > 0) {
65              tarOut.write(buf, 0, read);
66          }
67  
68          in.close();
69          tarOut.closeArchiveEntry();
70          tarOut.close();
71  
72          assertEquals(f.length(), tarOut.getBytesWritten());
73      }
74  
75      @Test
76      public void testMaxFileSizeError() throws Exception {
77          TarArchiveEntry t = new TarArchiveEntry("foo");
78          t.setSize(077777777777L);
79          TarArchiveOutputStream tos =
80              new TarArchiveOutputStream(new ByteArrayOutputStream());
81          tos.putArchiveEntry(t);
82          t.setSize(0100000000000L);
83          tos = new TarArchiveOutputStream(new ByteArrayOutputStream());
84          try {
85              tos.putArchiveEntry(t);
86              fail("Should have generated RuntimeException");
87          } catch (RuntimeException expected) {
88          }
89      }
90  
91      @Test
92      public void testBigNumberStarMode() throws Exception {
93          TarArchiveEntry t = new TarArchiveEntry("foo");
94          t.setSize(0100000000000L);
95          ByteArrayOutputStream bos = new ByteArrayOutputStream();
96          TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
97          tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
98          tos.putArchiveEntry(t);
99          // make sure header is written to byte array
100         tos.write(new byte[10 * 1024]);
101         byte[] data = bos.toByteArray();
102         assertEquals(0x80,
103                      data[TarConstants.NAMELEN
104                         + TarConstants.MODELEN
105                         + TarConstants.UIDLEN
106                         + TarConstants.GIDLEN] & 0x80);
107         TarArchiveInputStream tin =
108             new TarArchiveInputStream(new ByteArrayInputStream(data));
109         TarArchiveEntry e = tin.getNextTarEntry();
110         assertEquals(0100000000000L, e.getSize());
111         tin.close();
112         // generates IOE because of unclosed entries.
113         // However we don't really want to create such large entries.
114         closeQuietly(tos);
115     }
116 
117     @Test
118     public void testBigNumberPosixMode() throws Exception {
119         TarArchiveEntry t = new TarArchiveEntry("foo");
120         t.setSize(0100000000000L);
121         ByteArrayOutputStream bos = new ByteArrayOutputStream();
122         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
123         tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
124         tos.putArchiveEntry(t);
125         // make sure header is written to byte array
126         tos.write(new byte[10 * 1024]);
127         byte[] data = bos.toByteArray();
128         assertEquals("00000000000 ",
129                      new String(data,
130                                 1024 + TarConstants.NAMELEN
131                                 + TarConstants.MODELEN
132                                 + TarConstants.UIDLEN
133                                 + TarConstants.GIDLEN, 12,
134                                 CharsetNames.UTF_8));
135         TarArchiveInputStream tin =
136             new TarArchiveInputStream(new ByteArrayInputStream(data));
137         TarArchiveEntry e = tin.getNextTarEntry();
138         assertEquals(0100000000000L, e.getSize());
139         tin.close();
140         // generates IOE because of unclosed entries.
141         // However we don't really want to create such large entries.
142         closeQuietly(tos);
143     }
144 
145     @Test
146     public void testWriteSimplePaxHeaders() throws Exception {
147         Map<String, String> m = new HashMap<String, String>();
148         m.put("a", "b");
149         byte[] data = writePaxHeader(m);
150         assertEquals("00000000006 ",
151                      new String(data, TarConstants.NAMELEN
152                                 + TarConstants.MODELEN
153                                 + TarConstants.UIDLEN
154                                 + TarConstants.GIDLEN, 12,
155                                 CharsetNames.UTF_8));
156         assertEquals("6 a=b\n", new String(data, 512, 6, CharsetNames.UTF_8));
157     }
158 
159     @Test
160     public void testPaxHeadersWithLength99() throws Exception {
161         Map<String, String> m = new HashMap<String, String>();
162         m.put("a",
163               "0123456789012345678901234567890123456789"
164               + "01234567890123456789012345678901234567890123456789"
165               + "012");
166         byte[] data = writePaxHeader(m);
167         assertEquals("00000000143 ",
168                      new String(data, TarConstants.NAMELEN
169                                 + TarConstants.MODELEN
170                                 + TarConstants.UIDLEN
171                                 + TarConstants.GIDLEN, 12,
172                                 CharsetNames.UTF_8));
173         assertEquals("99 a=0123456789012345678901234567890123456789"
174               + "01234567890123456789012345678901234567890123456789"
175               + "012\n", new String(data, 512, 99, CharsetNames.UTF_8));
176     }
177 
178     @Test
179     public void testPaxHeadersWithLength101() throws Exception {
180         Map<String, String> m = new HashMap<String, String>();
181         m.put("a",
182               "0123456789012345678901234567890123456789"
183               + "01234567890123456789012345678901234567890123456789"
184               + "0123");
185         byte[] data = writePaxHeader(m);
186         assertEquals("00000000145 ",
187                      new String(data, TarConstants.NAMELEN
188                                 + TarConstants.MODELEN
189                                 + TarConstants.UIDLEN
190                                 + TarConstants.GIDLEN, 12,
191                                 CharsetNames.UTF_8));
192         assertEquals("101 a=0123456789012345678901234567890123456789"
193               + "01234567890123456789012345678901234567890123456789"
194               + "0123\n", new String(data, 512, 101, CharsetNames.UTF_8));
195     }
196 
197     private byte[] writePaxHeader(Map<String, String> m) throws Exception {
198         ByteArrayOutputStream bos = new ByteArrayOutputStream();
199         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
200         tos.writePaxHeaders(new TarArchiveEntry("x"), "foo", m);
201 
202         // add a dummy entry so data gets written
203         TarArchiveEntry t = new TarArchiveEntry("foo");
204         t.setSize(10 * 1024);
205         tos.putArchiveEntry(t);
206         tos.write(new byte[10 * 1024]);
207         tos.closeArchiveEntry();
208         tos.close();
209 
210         return bos.toByteArray();
211     }
212 
213     @Test
214     public void testWriteLongFileNamePosixMode() throws Exception {
215         String n = "01234567890123456789012345678901234567890123456789"
216             + "01234567890123456789012345678901234567890123456789"
217             + "01234567890123456789012345678901234567890123456789";
218         TarArchiveEntry t =
219             new TarArchiveEntry(n);
220         t.setSize(10 * 1024);
221         ByteArrayOutputStream bos = new ByteArrayOutputStream();
222         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
223         tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
224         tos.putArchiveEntry(t);
225         tos.write(new byte[10 * 1024]);
226         tos.closeArchiveEntry();
227         byte[] data = bos.toByteArray();
228         assertEquals("160 path=" + n + "\n",
229                      new String(data, 512, 160, CharsetNames.UTF_8));
230         TarArchiveInputStream tin =
231             new TarArchiveInputStream(new ByteArrayInputStream(data));
232         TarArchiveEntry e = tin.getNextTarEntry();
233         assertEquals(n, e.getName());
234         tin.close();
235         tos.close();
236     }
237 
238     @Test
239     public void testOldEntryStarMode() throws Exception {
240         TarArchiveEntry t = new TarArchiveEntry("foo");
241         t.setSize(Integer.MAX_VALUE);
242         t.setModTime(-1000);
243         ByteArrayOutputStream bos = new ByteArrayOutputStream();
244         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
245         tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
246         tos.putArchiveEntry(t);
247         // make sure header is written to byte array
248         tos.write(new byte[10 * 1024]);
249         byte[] data = bos.toByteArray();
250         assertEquals((byte) 0xff,
251                      data[TarConstants.NAMELEN
252                           + TarConstants.MODELEN
253                           + TarConstants.UIDLEN
254                           + TarConstants.GIDLEN
255                           + TarConstants.SIZELEN]);
256         TarArchiveInputStream tin =
257             new TarArchiveInputStream(new ByteArrayInputStream(data));
258         TarArchiveEntry e = tin.getNextTarEntry();
259         Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
260         cal.set(1969, 11, 31, 23, 59, 59);
261         cal.set(Calendar.MILLISECOND, 0);
262         assertEquals(cal.getTime(), e.getLastModifiedDate());
263         tin.close();
264         // generates IOE because of unclosed entries.
265         // However we don't really want to create such large entries.
266         closeQuietly(tos);
267     }
268 
269     @Test
270     public void testOldEntryPosixMode() throws Exception {
271         TarArchiveEntry t = new TarArchiveEntry("foo");
272         t.setSize(Integer.MAX_VALUE);
273         t.setModTime(-1000);
274         ByteArrayOutputStream bos = new ByteArrayOutputStream();
275         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
276         tos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
277         tos.putArchiveEntry(t);
278         // make sure header is written to byte array
279         tos.write(new byte[10 * 1024]);
280         byte[] data = bos.toByteArray();
281         assertEquals("00000000000 ",
282                      new String(data,
283                                 1024 + TarConstants.NAMELEN
284                                 + TarConstants.MODELEN
285                                 + TarConstants.UIDLEN
286                                 + TarConstants.GIDLEN
287                                 + TarConstants.SIZELEN, 12,
288                                 CharsetNames.UTF_8));
289         TarArchiveInputStream tin =
290             new TarArchiveInputStream(new ByteArrayInputStream(data));
291         TarArchiveEntry e = tin.getNextTarEntry();
292         Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
293         cal.set(1969, 11, 31, 23, 59, 59);
294         cal.set(Calendar.MILLISECOND, 0);
295         assertEquals(cal.getTime(), e.getLastModifiedDate());
296         tin.close();
297         // generates IOE because of unclosed entries.
298         // However we don't really want to create such large entries.
299         closeQuietly(tos);
300     }
301 
302     @Test
303     public void testOldEntryError() throws Exception {
304         TarArchiveEntry t = new TarArchiveEntry("foo");
305         t.setSize(Integer.MAX_VALUE);
306         t.setModTime(-1000);
307         TarArchiveOutputStream tos =
308             new TarArchiveOutputStream(new ByteArrayOutputStream());
309         try {
310             tos.putArchiveEntry(t);
311             fail("Should have generated RuntimeException");
312         } catch (RuntimeException expected) {
313         }
314         tos.close();
315     }
316 
317     @Test
318     public void testWriteNonAsciiPathNamePaxHeader() throws Exception {
319         String n = "\u00e4";
320         TarArchiveEntry t = new TarArchiveEntry(n);
321         t.setSize(10 * 1024);
322         ByteArrayOutputStream bos = new ByteArrayOutputStream();
323         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
324         tos.setAddPaxHeadersForNonAsciiNames(true);
325         tos.putArchiveEntry(t);
326         tos.write(new byte[10 * 1024]);
327         tos.closeArchiveEntry();
328         tos.close();
329         byte[] data = bos.toByteArray();
330         assertEquals("11 path=" + n + "\n",
331                      new String(data, 512, 11, CharsetNames.UTF_8));
332         TarArchiveInputStream tin =
333             new TarArchiveInputStream(new ByteArrayInputStream(data));
334         TarArchiveEntry e = tin.getNextTarEntry();
335         assertEquals(n, e.getName());
336         tin.close();
337     }
338 
339     @Test
340     public void testWriteNonAsciiLinkPathNamePaxHeader() throws Exception {
341         String n = "\u00e4";
342         TarArchiveEntry t = new TarArchiveEntry("a", TarConstants.LF_LINK);
343         t.setSize(10 * 1024);
344         t.setLinkName(n);
345         ByteArrayOutputStream bos = new ByteArrayOutputStream();
346         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
347         tos.setAddPaxHeadersForNonAsciiNames(true);
348         tos.putArchiveEntry(t);
349         tos.write(new byte[10 * 1024]);
350         tos.closeArchiveEntry();
351         tos.close();
352         byte[] data = bos.toByteArray();
353         assertEquals("15 linkpath=" + n + "\n",
354                      new String(data, 512, 15, CharsetNames.UTF_8));
355         TarArchiveInputStream tin =
356             new TarArchiveInputStream(new ByteArrayInputStream(data));
357         TarArchiveEntry e = tin.getNextTarEntry();
358         assertEquals(n, e.getLinkName());
359         tin.close();
360     }
361 
362     /**
363      * @see "https://issues.apache.org/jira/browse/COMPRESS-200"
364      */
365     @Test
366     public void testRoundtripWith67CharFileNameGnu() throws Exception {
367         testRoundtripWith67CharFileName(TarArchiveOutputStream.LONGFILE_GNU);
368     }
369 
370     /**
371      * @see "https://issues.apache.org/jira/browse/COMPRESS-200"
372      */
373     @Test
374     public void testRoundtripWith67CharFileNamePosix() throws Exception {
375         testRoundtripWith67CharFileName(TarArchiveOutputStream.LONGFILE_POSIX);
376     }
377 
378     private void testRoundtripWith67CharFileName(int mode) throws Exception {
379         String n = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
380             + "AAAAAAA";
381         assertEquals(67, n.length());
382         TarArchiveEntry t = new TarArchiveEntry(n);
383         t.setSize(10 * 1024);
384         ByteArrayOutputStream bos = new ByteArrayOutputStream();
385         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
386         tos.setLongFileMode(mode);
387         tos.putArchiveEntry(t);
388         tos.write(new byte[10 * 1024]);
389         tos.closeArchiveEntry();
390         tos.close();
391         byte[] data = bos.toByteArray();
392         TarArchiveInputStream tin =
393             new TarArchiveInputStream(new ByteArrayInputStream(data));
394         TarArchiveEntry e = tin.getNextTarEntry();
395         assertEquals(n, e.getName());
396         tin.close();
397     }
398 
399     @Test
400     public void testWriteLongDirectoryNameErrorMode() throws Exception {
401         String n = "01234567890123456789012345678901234567890123456789"
402                 + "01234567890123456789012345678901234567890123456789"
403                 + "01234567890123456789012345678901234567890123456789/";
404 
405         try {
406             TarArchiveEntry t = new TarArchiveEntry(n);
407             ByteArrayOutputStream bos = new ByteArrayOutputStream();
408             TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
409             tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_ERROR);
410             tos.putArchiveEntry(t);
411             tos.closeArchiveEntry();
412             tos.close();
413 
414             fail("Truncated name didn't throw an exception");
415         } catch (RuntimeException e) {
416             // expected
417         }
418     }
419 
420     @Test
421     public void testWriteLongDirectoryNameTruncateMode() throws Exception {
422         String n = "01234567890123456789012345678901234567890123456789"
423             + "01234567890123456789012345678901234567890123456789"
424             + "01234567890123456789012345678901234567890123456789/";
425         TarArchiveEntry t = new TarArchiveEntry(n);
426         ByteArrayOutputStream bos = new ByteArrayOutputStream();
427         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
428         tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_TRUNCATE);
429         tos.putArchiveEntry(t);
430         tos.closeArchiveEntry();
431         tos.close();
432         byte[] data = bos.toByteArray();
433         TarArchiveInputStream tin =
434             new TarArchiveInputStream(new ByteArrayInputStream(data));
435         TarArchiveEntry e = tin.getNextTarEntry();
436         assertEquals("Entry name", n.substring(0, TarConstants.NAMELEN) + "/", e.getName());
437         assertTrue("The entry is not a directory", e.isDirectory());
438         tin.close();
439     }
440 
441     /**
442      * @see "https://issues.apache.org/jira/browse/COMPRESS-203"
443      */
444     @Test
445     public void testWriteLongDirectoryNameGnuMode() throws Exception {
446         testWriteLongDirectoryName(TarArchiveOutputStream.LONGFILE_GNU);
447     }
448 
449     /**
450      * @see "https://issues.apache.org/jira/browse/COMPRESS-203"
451      */
452     @Test
453     public void testWriteLongDirectoryNamePosixMode() throws Exception {
454         testWriteLongDirectoryName(TarArchiveOutputStream.LONGFILE_POSIX);
455     }
456 
457     private void testWriteLongDirectoryName(int mode) throws Exception {
458         String n = "01234567890123456789012345678901234567890123456789"
459             + "01234567890123456789012345678901234567890123456789"
460             + "01234567890123456789012345678901234567890123456789/";
461         TarArchiveEntry t = new TarArchiveEntry(n);
462         ByteArrayOutputStream bos = new ByteArrayOutputStream();
463         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
464         tos.setLongFileMode(mode);
465         tos.putArchiveEntry(t);
466         tos.closeArchiveEntry();
467         tos.close();
468         byte[] data = bos.toByteArray();
469         TarArchiveInputStream tin =
470             new TarArchiveInputStream(new ByteArrayInputStream(data));
471         TarArchiveEntry e = tin.getNextTarEntry();
472         assertEquals(n, e.getName());
473         assertTrue(e.isDirectory());
474         tin.close();
475     }
476 
477     /**
478      * @see "https://issues.apache.org/jira/browse/COMPRESS-203"
479      */
480     @Test
481     public void testWriteNonAsciiDirectoryNamePosixMode() throws Exception {
482         String n = "f\u00f6\u00f6/";
483         TarArchiveEntry t = new TarArchiveEntry(n);
484         ByteArrayOutputStream bos = new ByteArrayOutputStream();
485         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
486         tos.setAddPaxHeadersForNonAsciiNames(true);
487         tos.putArchiveEntry(t);
488         tos.closeArchiveEntry();
489         tos.close();
490         byte[] data = bos.toByteArray();
491         TarArchiveInputStream tin =
492             new TarArchiveInputStream(new ByteArrayInputStream(data));
493         TarArchiveEntry e = tin.getNextTarEntry();
494         assertEquals(n, e.getName());
495         assertTrue(e.isDirectory());
496         tin.close();
497     }
498 
499     /**
500      * @see "https://issues.apache.org/jira/browse/COMPRESS-265"
501      */
502     @Test
503     public void testWriteNonAsciiNameWithUnfortunateNamePosixMode() throws Exception {
504         String n = "f\u00f6\u00f6\u00dc";
505         TarArchiveEntry t = new TarArchiveEntry(n);
506         ByteArrayOutputStream bos = new ByteArrayOutputStream();
507         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos);
508         tos.setAddPaxHeadersForNonAsciiNames(true);
509         tos.putArchiveEntry(t);
510         tos.closeArchiveEntry();
511         tos.close();
512         byte[] data = bos.toByteArray();
513         TarArchiveInputStream tin =
514             new TarArchiveInputStream(new ByteArrayInputStream(data));
515         TarArchiveEntry e = tin.getNextTarEntry();
516         assertEquals(n, e.getName());
517         assertFalse(e.isDirectory());
518         tin.close();
519     }
520 
521     /**
522      * @see "https://issues.apache.org/jira/browse/COMPRESS-237"
523      */
524     @Test
525     public void testWriteLongLinkNameErrorMode() throws Exception {
526         String linkname = "01234567890123456789012345678901234567890123456789"
527                 + "01234567890123456789012345678901234567890123456789"
528                 + "01234567890123456789012345678901234567890123456789/test";
529         TarArchiveEntry entry = new TarArchiveEntry("test", TarConstants.LF_SYMLINK);
530         entry.setLinkName(linkname);
531 
532         try {
533             ByteArrayOutputStream bos = new ByteArrayOutputStream();
534             TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
535             tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_ERROR);
536             tos.putArchiveEntry(entry);
537             tos.closeArchiveEntry();
538             tos.close();
539 
540             fail("Truncated link name didn't throw an exception");
541         } catch (RuntimeException e) {
542             // expected
543         }
544     }
545 
546     @Test
547     public void testWriteLongLinkNameTruncateMode() throws Exception {
548         String linkname = "01234567890123456789012345678901234567890123456789"
549             + "01234567890123456789012345678901234567890123456789"
550             + "01234567890123456789012345678901234567890123456789/";
551         TarArchiveEntry entry = new TarArchiveEntry("test" , TarConstants.LF_SYMLINK);
552         entry.setLinkName(linkname);
553 
554         ByteArrayOutputStream bos = new ByteArrayOutputStream();
555         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
556         tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_TRUNCATE);
557         tos.putArchiveEntry(entry);
558         tos.closeArchiveEntry();
559         tos.close();
560 
561         byte[] data = bos.toByteArray();
562         TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data));
563         TarArchiveEntry e = tin.getNextTarEntry();
564         assertEquals("Link name", linkname.substring(0, TarConstants.NAMELEN), e.getLinkName());
565         tin.close();
566     }
567 
568     /**
569      * @see "https://issues.apache.org/jira/browse/COMPRESS-237"
570      */
571     @Test
572     public void testWriteLongLinkNameGnuMode() throws Exception {
573         testWriteLongLinkName(TarArchiveOutputStream.LONGFILE_GNU);
574     }
575 
576     /**
577      * @see "https://issues.apache.org/jira/browse/COMPRESS-237"
578      */
579     @Test
580     public void testWriteLongLinkNamePosixMode() throws Exception {
581         testWriteLongLinkName(TarArchiveOutputStream.LONGFILE_POSIX);
582     }
583 
584     /**
585      * @see "https://issues.apache.org/jira/browse/COMPRESS-237"
586      */
587     private void testWriteLongLinkName(int mode) throws Exception {
588         String linkname = "01234567890123456789012345678901234567890123456789"
589             + "01234567890123456789012345678901234567890123456789"
590             + "01234567890123456789012345678901234567890123456789/test";
591         TarArchiveEntry entry = new TarArchiveEntry("test", TarConstants.LF_SYMLINK);
592         entry.setLinkName(linkname);
593 
594         ByteArrayOutputStream bos = new ByteArrayOutputStream();
595         TarArchiveOutputStream tos = new TarArchiveOutputStream(bos, "ASCII");
596         tos.setLongFileMode(mode);
597         tos.putArchiveEntry(entry);
598         tos.closeArchiveEntry();
599         tos.close();
600 
601         byte[] data = bos.toByteArray();
602         TarArchiveInputStream tin = new TarArchiveInputStream(new ByteArrayInputStream(data));
603         TarArchiveEntry e = tin.getNextTarEntry();
604         assertEquals("Entry name", "test", e.getName());
605         assertEquals("Link name", linkname, e.getLinkName());
606         assertTrue("The entry is not a symbolic link", e.isSymbolicLink());
607         tin.close();
608     }
609 
610     @Test
611     public void testPadsOutputToFullBlockLength() throws Exception {
612         File f = File.createTempFile("commons-compress-padding", ".tar");
613         f.deleteOnExit();
614         FileOutputStream fos = new FileOutputStream(f);
615         TarArchiveOutputStream tos = new TarArchiveOutputStream(fos);
616         File file1 = getFile("test1.xml");
617         TarArchiveEntry sEntry = new TarArchiveEntry(file1, file1.getName());
618         tos.putArchiveEntry(sEntry);
619         FileInputStream in = new FileInputStream(file1);
620         IOUtils.copy(in, tos);
621         in.close();
622         tos.closeArchiveEntry();
623         tos.close();
624         // test1.xml is small enough to fit into the default block size
625         assertEquals(TarConstants.DEFAULT_BLKSIZE, f.length());
626     }
627 
628     /**
629      * When using long file names the longLinkEntry included the
630      * current timestamp as the Entry modification date. This was
631      * never exposed to the client but it caused identical archives to
632      * have different MD5 hashes.
633      *
634      * @throws Exception
635      */
636     @Test
637     public void testLongNameMd5Hash() throws Exception {
638         final String longFileName = "a/considerably/longer/file/name/which/forces/use/of/the/long/link/header/which/appears/to/always/use/the/current/time/as/modification/date";
639         String fname = longFileName;
640         final Date modificationDate = new Date();
641 
642         byte[] archive1 = createTarArchiveContainingOneDirectory(fname, modificationDate);
643         byte[] digest1 = MessageDigest.getInstance("MD5").digest(archive1);
644 
645         // let a second elapse otherwise the modification dates will be equal
646         Thread.sleep(1000L);
647 
648         // now recreate exactly the same tar file
649         byte[] archive2 = createTarArchiveContainingOneDirectory(fname, modificationDate);
650         // and I would expect the MD5 hash to be the same, but for long names it isn't
651         byte[] digest2 = MessageDigest.getInstance("MD5").digest(archive2);
652 
653         Assert.assertArrayEquals(digest1, digest2);
654 
655         // do I still have the correct modification date?
656         // let a second elapse so we don't get the current time
657         Thread.sleep(1000);
658         TarArchiveInputStream tarIn = new TarArchiveInputStream(new ByteArrayInputStream(archive2));
659         ArchiveEntry nextEntry = tarIn.getNextEntry();
660         assertEquals(longFileName, nextEntry.getName());
661         // tar archive stores modification time to second granularity only (floored)
662         assertEquals(modificationDate.getTime() / 1000, nextEntry.getLastModifiedDate().getTime() / 1000);
663         tarIn.close();
664     }
665 
666     private static byte[] createTarArchiveContainingOneDirectory(String fname, Date modificationDate) throws IOException {
667         ByteArrayOutputStream baos = new ByteArrayOutputStream();
668         TarArchiveOutputStream tarOut = new TarArchiveOutputStream(baos, 1024);
669         tarOut.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
670         TarArchiveEntry tarEntry = new TarArchiveEntry("d");
671         tarEntry.setModTime(modificationDate);
672         tarEntry.setMode(TarArchiveEntry.DEFAULT_DIR_MODE);
673         tarEntry.setModTime(modificationDate.getTime());
674         tarEntry.setName(fname);
675         tarOut.putArchiveEntry(tarEntry);
676         tarOut.closeArchiveEntry();
677         tarOut.close();
678 
679         return baos.toByteArray();
680     }
681 
682 }