View Javadoc
1   /*
2    * The MIT License
3    *
4    * Copyright (c) 2004, The Codehaus
5    *
6    * Permission is hereby granted, free of charge, to any person obtaining a copy of
7    * this software and associated documentation files (the "Software"), to deal in
8    * the Software without restriction, including without limitation the rights to
9    * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
10   * of the Software, and to permit persons to whom the Software is furnished to do
11   * so, subject to the following conditions:
12   *
13   * The above copyright notice and this permission notice shall be included in all
14   * copies or substantial portions of the Software.
15   *
16   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22   * SOFTWARE.
23   */
24  package org.codehaus.plexus.archiver.tar;
25  
26  import java.io.BufferedInputStream;
27  import java.io.File;
28  import java.io.FileInputStream;
29  import java.io.FileWriter;
30  import java.io.IOException;
31  import java.util.Enumeration;
32  import java.util.LinkedHashMap;
33  import java.util.Map;
34  import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
35  import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
36  import org.codehaus.plexus.PlexusTestCase;
37  import org.codehaus.plexus.archiver.Archiver;
38  import org.codehaus.plexus.archiver.ArchiverException;
39  import org.codehaus.plexus.archiver.UnixStat;
40  import org.codehaus.plexus.archiver.bzip2.BZip2Compressor;
41  import org.codehaus.plexus.archiver.exceptions.EmptyArchiveException;
42  import org.codehaus.plexus.archiver.gzip.GZipCompressor;
43  import org.codehaus.plexus.archiver.util.ArchiveEntryUtils;
44  import org.codehaus.plexus.archiver.util.Compressor;
45  import org.codehaus.plexus.archiver.util.DefaultArchivedFileSet;
46  import org.codehaus.plexus.archiver.zip.ArchiveFileComparator;
47  import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributeUtils;
48  import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributes;
49  import org.codehaus.plexus.util.FileUtils;
50  import org.codehaus.plexus.util.IOUtil;
51  import org.codehaus.plexus.util.Os;
52  import org.junit.Assert;
53  import static org.codehaus.plexus.archiver.util.Streams.bufferedInputStream;
54  import static org.codehaus.plexus.components.io.resources.ResourceFactory.createResource;
55  
56  /**
57   * @author Emmanuel Venisse
58   */
59  public class TarArchiverTest
60      extends PlexusTestCase
61  {
62  
63      public void testCreateArchiveWithDetectedModes()
64          throws Exception
65      {
66  
67          String[] executablePaths =
68          {
69              "path/to/executable", "path/to/executable.bat"
70          };
71  
72          String[] confPaths =
73          {
74              "path/to/etc/file", "path/to/etc/file2"
75          };
76  
77          String[] logPaths =
78          {
79              "path/to/logs/log.txt"
80          };
81  
82          int exeMode = 0777;
83          int confMode = 0600;
84          int logMode = 0640;
85  
86          if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
87          {
88              StackTraceElement e = new Throwable().getStackTrace()[0];
89              System.out.println( "Cannot execute test: " + e.getMethodName() + " on "
90                                      + System.getProperty( "os.name" ) );
91  
92              return;
93          }
94  
95          File tmpDir = null;
96          try
97          {
98              tmpDir = File.createTempFile( "tbz2-with-chmod.", ".dir" );
99              tmpDir.delete();
100 
101             tmpDir.mkdirs();
102 
103             for ( String executablePath : executablePaths )
104             {
105                 writeFile( tmpDir, executablePath, exeMode );
106             }
107 
108             for ( String confPath : confPaths )
109             {
110                 writeFile( tmpDir, confPath, confMode );
111             }
112 
113             for ( String logPath : logPaths )
114             {
115                 writeFile( tmpDir, logPath, logMode );
116             }
117 
118             {
119                 Map attributesByPath = PlexusIoResourceAttributeUtils.getFileAttributesByPath( tmpDir );
120                 for ( String path : executablePaths )
121                 {
122                     PlexusIoResourceAttributes attrs = (PlexusIoResourceAttributes) attributesByPath.get( path );
123                     if ( attrs == null )
124                     {
125                         attrs = (PlexusIoResourceAttributes) attributesByPath.get(
126                             new File( tmpDir, path ).getAbsolutePath() );
127                     }
128 
129                     assertNotNull( attrs );
130                     assertEquals( "Wrong mode for: " + path + "; expected: " + exeMode, exeMode, attrs.getOctalMode() );
131                 }
132 
133                 for ( String path : confPaths )
134                 {
135                     PlexusIoResourceAttributes attrs = (PlexusIoResourceAttributes) attributesByPath.get( path );
136                     if ( attrs == null )
137                     {
138                         attrs = (PlexusIoResourceAttributes) attributesByPath.get(
139                             new File( tmpDir, path ).getAbsolutePath() );
140                     }
141 
142                     assertNotNull( attrs );
143                     assertEquals( "Wrong mode for: " + path + "; expected: " + confMode, confMode,
144                                   attrs.getOctalMode() );
145                 }
146 
147                 for ( String path : logPaths )
148                 {
149                     PlexusIoResourceAttributes attrs = (PlexusIoResourceAttributes) attributesByPath.get( path );
150                     if ( attrs == null )
151                     {
152                         attrs = (PlexusIoResourceAttributes) attributesByPath.get(
153                             new File( tmpDir, path ).getAbsolutePath() );
154                     }
155 
156                     assertNotNull( attrs );
157                     assertEquals( "Wrong mode for: " + path + "; expected: " + logMode, logMode, attrs.getOctalMode() );
158                 }
159             }
160 
161             File tarFile = getTestFile( "target/output/tar-with-modes.tar" );
162 
163             TarArchiver archiver = getPosixTarArchiver();
164             archiver.setDestFile( tarFile );
165 
166             archiver.addDirectory( tmpDir );
167             archiver.createArchive();
168 
169             assertTrue( tarFile.exists() );
170 
171             File tarFile2 = getTestFile( "target/output/tar-with-modes-L2.tar" );
172 
173             archiver = getPosixTarArchiver();
174             archiver.setDestFile( tarFile2 );
175 
176             archiver.addArchivedFileSet( tarFile );
177             archiver.createArchive();
178 
179             TarFile tf = new TarFile( tarFile2 );
180 
181             Map<String, TarArchiveEntry> entriesByPath = new LinkedHashMap<String, TarArchiveEntry>();
182             for ( Enumeration e = tf.getEntries(); e.hasMoreElements(); )
183             {
184                 TarArchiveEntry te = (TarArchiveEntry) e.nextElement();
185                 entriesByPath.put( te.getName(), te );
186             }
187 
188             for ( String path : executablePaths )
189             {
190                 TarArchiveEntry te = entriesByPath.get( path );
191 
192                 int mode = te.getMode() & UnixStat.PERM_MASK;
193 
194                 assertEquals( "Wrong mode for: " + path + "; expected: " + exeMode, exeMode, mode );
195             }
196 
197             for ( String path : confPaths )
198             {
199                 TarArchiveEntry te = entriesByPath.get( path );
200 
201                 int mode = te.getMode() & UnixStat.PERM_MASK;
202 
203                 assertEquals( "Wrong mode for: " + path + "; expected: " + confMode, confMode, mode );
204             }
205 
206             for ( String path : logPaths )
207             {
208                 TarArchiveEntry te = entriesByPath.get( path );
209 
210                 int mode = te.getMode() & UnixStat.PERM_MASK;
211 
212                 assertEquals( "Wrong mode for: " + path + "; expected: " + logMode, logMode, mode );
213             }
214         }
215         finally
216         {
217             if ( tmpDir != null && tmpDir.exists() )
218             {
219                 try
220                 {
221                     FileUtils.forceDelete( tmpDir );
222                 }
223                 catch ( IOException e )
224                 {
225                     e.printStackTrace();
226                 }
227             }
228         }
229     }
230 
231     public void testCreateEmptyArchive()
232         throws Exception
233     {
234         TarArchiver archiver = getPosixTarArchiver();
235         archiver.setDestFile( getTestFile( "target/output/empty.tar" ) );
236         try
237         {
238             archiver.createArchive();
239 
240             fail( "Creating empty archive should throw EmptyArchiveException" );
241         }
242         catch ( EmptyArchiveException ignore )
243         {
244         }
245     }
246 
247     public void testUnicode() throws Exception
248     {
249         File tmpDir = getTestFile( "src/test/resources/utf8" );
250         TarArchiver archiver = getPosixTarArchiver();
251         File tarFile = getTestFile( "target/output/tar-with-longFileName.tar" );
252         archiver.setDestFile( tarFile );
253         archiver.setLongfile( TarLongFileMode.posix ); // Todo: should be gnu. But will fail with high userod
254         archiver.addDirectory( tmpDir );
255         archiver.createArchive();
256         assertTrue( tarFile.exists() );
257     }
258 
259     private void writeFile( File dir, String fname, int mode )
260         throws IOException, ArchiverException
261     {
262         File file = new File( dir, fname );
263         FileWriter writer = null;
264 
265         try
266         {
267             if ( file.getParentFile() != null )
268             {
269                 file.getParentFile().mkdirs();
270             }
271 
272             writer = new FileWriter( file );
273             writer.write( "This is a test file." );
274             writer.close();
275             writer = null;
276         }
277         finally
278         {
279             IOUtil.close( writer );
280         }
281 
282         ArchiveEntryUtils.chmod( file, mode );
283     }
284 
285     public void testCreateArchive()
286         throws Exception
287     {
288         createArchive( 0500,
289                        new int[]
290                        {
291                            0400, 0640, 0664
292                        } );
293         createArchive( 0500,
294                        new int[]
295                        {
296                            0400, 0640, 0664
297                        } );
298     }
299 
300     public void createArchive( final int directoryMode, final int fileModes[] )
301         throws Exception
302     {
303         int defaultFileMode = fileModes[0];
304         int oneFileMode = fileModes[1];
305         int twoFileMode = fileModes[2];
306 
307         TarArchiver archiver = getPosixTarArchiver();
308 
309         archiver.setDirectoryMode( directoryMode );
310 
311         archiver.setFileMode( defaultFileMode );
312 
313         archiver.addDirectory( getTestFile( "src/main" ) );
314         archiver.setFileMode( oneFileMode );
315 
316         archiver.addFile( getTestFile( "src/test/resources/manifests/manifest1.mf" ), "one.txt" );
317         archiver.addFile( getTestFile( "src/test/resources/manifests/manifest2.mf" ), "two.txt", twoFileMode );
318         archiver.setDestFile( getTestFile( "target/output/archive.tar" ) );
319 
320         archiver.addSymlink( "link_to_test_destinaton", "../test_destination/" );
321 
322         archiver.createArchive();
323 
324         TarArchiveInputStream tis;
325 
326         tis = new TarArchiveInputStream( bufferedInputStream( new FileInputStream( archiver.getDestFile() ) ) );
327         TarArchiveEntry te;
328 
329         while ( ( te = tis.getNextTarEntry() ) != null )
330         {
331             if ( te.isDirectory() )
332             {
333                 assertEquals( "un-expected tar-entry mode for [te.name=" + te.getName() + "]", directoryMode,
334                               te.getMode() & UnixStat.PERM_MASK );
335             }
336             else if ( te.isSymbolicLink() )
337             {
338                 assertEquals( "../test_destination/", te.getLinkName() );
339                 assertEquals( "link_to_test_destinaton", te.getName() );
340                 assertEquals( 0640, te.getMode() & UnixStat.PERM_MASK );
341             }
342             else
343             {
344                 if ( te.getName().equals( "one.txt" ) )
345                 {
346                     assertEquals( oneFileMode, te.getMode() & UnixStat.PERM_MASK );
347                 }
348                 else if ( te.getName().equals( "two.txt" ) )
349                 {
350                     assertEquals( twoFileMode, te.getMode() & UnixStat.PERM_MASK );
351                 }
352                 else
353                 {
354                     assertEquals( "un-expected tar-entry mode for [te.name=" + te.getName() + "]", defaultFileMode,
355                                   te.getMode() & UnixStat.PERM_MASK );
356                 }
357 
358             }
359         }
360         IOUtil.close( tis );
361 
362     }
363 
364     public void testCreateArchiveWithJiustASymlink()
365         throws Exception
366     {
367         TarArchiver archiver = getPosixTarArchiver();
368 
369         archiver.setDirectoryMode( 0500 );
370 
371         archiver.setFileMode( 0400 );
372 
373         archiver.setFileMode( 0640 );
374 
375         archiver.setDestFile( getTestFile( "target/output/symlinkarchive.tar" ) );
376 
377         archiver.addSymlink( "link_to_test_destinaton", "../test_destination/" );
378 
379         archiver.createArchive();
380 
381         TarArchiveInputStream tis;
382 
383         tis = new TarArchiveInputStream( new BufferedInputStream( new FileInputStream( archiver.getDestFile() ) ) );
384         TarArchiveEntry te;
385 
386         while ( ( te = tis.getNextTarEntry() ) != null )
387         {
388             if ( te.isDirectory() )
389             {
390                 assertEquals( "un-expected tar-entry mode for [te.name=" + te.getName() + "]", 0500,
391                               te.getMode() & UnixStat.PERM_MASK );
392             }
393             else if ( te.isSymbolicLink() )
394             {
395                 assertEquals( "../test_destination/", te.getLinkName() );
396                 assertEquals( "link_to_test_destinaton", te.getName() );
397                 assertEquals( 0640, te.getMode() & UnixStat.PERM_MASK );
398             }
399             else
400             {
401                 assertEquals( "un-expected tar-entry mode for [te.name=" + te.getName() + "]", 0400,
402                               te.getMode() & UnixStat.PERM_MASK );
403             }
404         }
405         tis.close();
406 
407     }
408 
409     private TarArchiver getPosixTarArchiver() throws Exception
410     {
411         TarArchiver archiver = (TarArchiver) lookup( Archiver.ROLE, "tar" );
412         archiver.setLongfile( TarLongFileMode.posix );
413         return archiver;
414     }
415 
416     private class TarHandler
417     {
418 
419         File createTarFile()
420             throws Exception
421         {
422             final File srcDir = new File( "src" );
423             final File tarFile = new File( "target/output/src.tar" );
424             TarArchiver tarArchiver = getPosixTarArchiver();
425             tarArchiver.setDestFile( tarFile );
426             tarArchiver.addDirectory( srcDir, null, FileUtils.getDefaultExcludes() );
427             FileUtils.removePath( tarFile.getPath() );
428             tarArchiver.createArchive();
429             return tarFile;
430         }
431 
432         File createTarfile2( File tarFile )
433             throws Exception
434         {
435             final File tarFile2 = new File( "target/output/src2.tar" );
436             TarArchiver tarArchiver2 = getPosixTarArchiver();
437             tarArchiver2.setDestFile( tarFile2 );
438             tarArchiver2.addArchivedFileSet( tarFile, "prfx/" );
439             FileUtils.removePath( tarFile2.getPath() );
440             tarArchiver2.createArchive();
441             return tarFile2;
442         }
443 
444         TarFile newTarFile( File tarFile )
445         {
446             return new TarFile( tarFile );
447         }
448 
449     }
450 
451     private class GZipTarHandler
452         extends TarHandler
453     {
454 
455         @Override
456         File createTarFile()
457             throws Exception
458         {
459             File file = super.createTarFile();
460             File compressedFile = new File( file.getPath() + ".gz" );
461             Compressor compressor = new GZipCompressor();
462             compressor.setSource( createResource( file, file.getName() ) );
463             compressor.setDestFile( compressedFile );
464             compressor.compress();
465             compressor.close();
466             return compressedFile;
467         }
468 
469         @Override
470         TarFile newTarFile( File tarFile )
471         {
472             return new GZipTarFile( tarFile );
473         }
474 
475     }
476 
477     private class BZip2TarHandler
478         extends TarHandler
479     {
480 
481         @Override
482         File createTarFile()
483             throws Exception
484         {
485             File file = super.createTarFile();
486             File compressedFile = new File( file.getPath() + ".bz2" );
487             Compressor compressor = new BZip2Compressor();
488             compressor.setSource( createResource( file ) );
489             compressor.setDestFile( compressedFile );
490             compressor.compress();
491             compressor.close();
492             return compressedFile;
493         }
494 
495         @Override
496         TarFile newTarFile( File tarFile )
497         {
498             return new BZip2TarFile( tarFile );
499         }
500 
501     }
502 
503     public void testUncompressedResourceCollection()
504         throws Exception
505     {
506         testCreateResourceCollection( new TarHandler() );
507     }
508 
509     public void testGzipCompressedResourceCollection()
510         throws Exception
511     {
512         testCreateResourceCollection( new GZipTarHandler() );
513     }
514 
515     public void testGzipFIleHandleLeak()
516         throws Exception
517     {
518         GZipTarHandler tarHandler = new GZipTarHandler();
519         final File tarFile = tarHandler.createTarFile();
520         final File tarFile2 = tarHandler.createTarfile2( tarFile );
521         final TarFile cmp1 = tarHandler.newTarFile( tarFile );
522         final TarFile cmp2 = new TarFile( tarFile2 );
523         ArchiveFileComparator.forEachTarArchiveEntry( cmp1, new ArchiveFileComparator.TarArchiveEntryConsumer()
524         {
525 
526             @Override
527             public void accept( TarArchiveEntry ze1 )
528                 throws IOException
529             {
530                 final String name1 = ze1.getName();
531                 Assert.assertNotNull( name1 );
532 
533             }
534 
535         } );
536         cmp1.close();
537         cmp2.close();
538 
539     }
540 
541     public void testBzip2CompressedResourceCollection()
542         throws Exception
543     {
544         testCreateResourceCollection( new BZip2TarHandler() );
545     }
546 
547     public void testTarFileNotClosingInputStream()
548         throws Exception
549     {
550         // Supposedly not closing the stream according to yjp.
551         TarHandler tarHandler = new BZip2TarHandler();
552         final File fileName = tarHandler.createTarFile();
553         final TarFile tarFile = tarHandler.newTarFile( fileName );
554         tarFile.getEntries();
555         tarFile.close();
556     }
557 
558     private void testCreateResourceCollection( TarHandler tarHandler )
559         throws Exception
560     {
561         final File tarFile = tarHandler.createTarFile();
562         final File tarFile2 = tarHandler.createTarfile2( tarFile );
563         final TarFile cmp1 = tarHandler.newTarFile( tarFile );
564         final TarFile cmp2 = new TarFile( tarFile2 );
565         ArchiveFileComparator.assertEquals( cmp1, cmp2, "prfx/" );
566         cmp1.close();
567         cmp2.close();
568     }
569 
570     public void testSymlinkArchivedFileSet()
571         throws Exception
572     {
573         final File tarFile = getTestFile( "src/test/resources/symlinks/symlinks.tar" );
574         final File tarFile2 = getTestFile( "target/output/pasymlinks-archivedFileset.tar" );
575         final TarArchiver tarArchiver = getPosixTarArchiver();
576         tarArchiver.setDestFile( tarFile2 );
577         DefaultArchivedFileSet archivedFileSet = DefaultArchivedFileSet.archivedFileSet( tarFile );
578         archivedFileSet.setUsingDefaultExcludes( false );
579         tarArchiver.addArchivedFileSet( archivedFileSet );
580         tarArchiver.createArchive();
581 
582         final TarFile cmp1 = new TarFile( tarFile );
583         final TarFile cmp2 = new TarFile( tarFile2 );
584         ArchiveFileComparator.assertEquals( cmp1, cmp2, "" );
585     }
586 
587 }