package org.codehaus.plexus.archiver.tar;
/*
* The MIT License
*
* Copyright (c) 2004, The Codehaus
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.codehaus.plexus.PlexusTestCase;
import org.codehaus.plexus.archiver.Archiver;
import org.codehaus.plexus.archiver.ArchiverException;
import org.codehaus.plexus.archiver.UnixStat;
import org.codehaus.plexus.archiver.bzip2.BZip2Compressor;
import org.codehaus.plexus.archiver.gzip.GZipCompressor;
import org.codehaus.plexus.archiver.util.ArchiveEntryUtils;
import org.codehaus.plexus.archiver.util.Compressor;
import org.codehaus.plexus.archiver.zip.ArchiveFileComparator;
import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributeUtils;
import org.codehaus.plexus.components.io.attributes.PlexusIoResourceAttributes;
import org.codehaus.plexus.logging.Logger;
import org.codehaus.plexus.logging.console.ConsoleLogger;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.Os;
import org.junit.Assert;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.codehaus.plexus.archiver.util.Streams.bufferedInputStream;
import static org.codehaus.plexus.components.io.resources.ResourceFactory.createResource;
/**
* @author Emmanuel Venisse
* @version $Id$
*/
public class TarArchiverTest
extends PlexusTestCase
{
private Logger logger;
public void setUp()
throws Exception
{
super.setUp();
logger = new ConsoleLogger( Logger.LEVEL_DEBUG, "test" );
}
public void testCreateArchiveWithDetectedModes()
throws Exception
{
String[] executablePaths = {"path/to/executable", "path/to/executable.bat"};
String[] confPaths = {"path/to/etc/file", "path/to/etc/file2"};
String[] logPaths = {"path/to/logs/log.txt"};
int exeMode = 0777;
int confMode = 0600;
int logMode = 0640;
if ( Os.isFamily( Os.FAMILY_WINDOWS ) )
{
StackTraceElement e = new Throwable().getStackTrace()[0];
System.out.println(
"Cannot execute test: " + e.getMethodName() + " on " + System.getProperty( "os.name" ) );
return;
}
File tmpDir = null;
try
{
tmpDir = File.createTempFile( "tbz2-with-chmod.", ".dir" );
tmpDir.delete();
tmpDir.mkdirs();
for ( String executablePath : executablePaths )
{
writeFile( tmpDir, executablePath, exeMode );
}
for ( String confPath : confPaths )
{
writeFile( tmpDir, confPath, confMode );
}
for ( String logPath : logPaths )
{
writeFile( tmpDir, logPath, logMode );
}
{
Map attributesByPath = PlexusIoResourceAttributeUtils.getFileAttributesByPath( tmpDir );
for ( String path : executablePaths )
{
PlexusIoResourceAttributes attrs = (PlexusIoResourceAttributes) attributesByPath.get( path );
if ( attrs == null )
{
attrs = (PlexusIoResourceAttributes) attributesByPath.get(
new File( tmpDir, path ).getAbsolutePath() );
}
assertNotNull( attrs );
assertEquals( "Wrong mode for: " + path + "; expected: " + exeMode, exeMode, attrs.getOctalMode() );
}
for ( String path : confPaths )
{
PlexusIoResourceAttributes attrs = (PlexusIoResourceAttributes) attributesByPath.get( path );
if ( attrs == null )
{
attrs = (PlexusIoResourceAttributes) attributesByPath.get(
new File( tmpDir, path ).getAbsolutePath() );
}
assertNotNull( attrs );
assertEquals( "Wrong mode for: " + path + "; expected: " + confMode, confMode,
attrs.getOctalMode() );
}
for ( String path : logPaths )
{
PlexusIoResourceAttributes attrs = (PlexusIoResourceAttributes) attributesByPath.get( path );
if ( attrs == null )
{
attrs = (PlexusIoResourceAttributes) attributesByPath.get(
new File( tmpDir, path ).getAbsolutePath() );
}
assertNotNull( attrs );
assertEquals( "Wrong mode for: " + path + "; expected: " + logMode, logMode, attrs.getOctalMode() );
}
}
File tarFile = getTestFile( "target/output/tar-with-modes.tar" );
TarArchiver archiver = getPosixTarArchiver();
archiver.setDestFile( tarFile );
archiver.addDirectory( tmpDir );
archiver.createArchive();
assertTrue( tarFile.exists() );
File tarFile2 = getTestFile( "target/output/tar-with-modes-L2.tar" );
archiver = getPosixTarArchiver();
archiver.setDestFile( tarFile2 );
archiver.addArchivedFileSet( tarFile );
archiver.createArchive();
TarFile tf = new TarFile( tarFile2 );
Map<String, TarArchiveEntry> entriesByPath = new LinkedHashMap<String, TarArchiveEntry>();
for ( Enumeration e = tf.getEntries(); e.hasMoreElements(); )
{
TarArchiveEntry te = (TarArchiveEntry) e.nextElement();
entriesByPath.put( te.getName(), te );
}
for ( String path : executablePaths )
{
TarArchiveEntry te = entriesByPath.get( path );
int mode = te.getMode() & UnixStat.PERM_MASK;
assertEquals( "Wrong mode for: " + path + "; expected: " + exeMode, exeMode, mode );
}
for ( String path : confPaths )
{
TarArchiveEntry te = entriesByPath.get( path );
int mode = te.getMode() & UnixStat.PERM_MASK;
assertEquals( "Wrong mode for: " + path + "; expected: " + confMode, confMode, mode );
}
for ( String path : logPaths )
{
TarArchiveEntry te = entriesByPath.get( path );
int mode = te.getMode() & UnixStat.PERM_MASK;
assertEquals( "Wrong mode for: " + path + "; expected: " + logMode, logMode, mode );
}
}
finally
{
if ( tmpDir != null && tmpDir.exists() )
{
try
{
FileUtils.forceDelete( tmpDir );
}
catch ( IOException e )
{
e.printStackTrace();
}
}
}
}
public void testUnicode() throws Exception {
File tmpDir = getTestFile( "src/test/resources/utf8" );
TarArchiver archiver = getPosixTarArchiver();
File tarFile = getTestFile( "target/output/tar-with-longFileName.tar" );
archiver.setDestFile(tarFile);
archiver.setLongfile(TarLongFileMode.posix ); // Todo: should be gnu. But will fail with high userod
archiver.addDirectory( tmpDir );
archiver.createArchive();
assertTrue( tarFile.exists() );
}
private void writeFile( File dir, String fname, int mode )
throws IOException, ArchiverException
{
File file = new File( dir, fname );
FileWriter writer = null;
try
{
if ( file.getParentFile() != null )
{
file.getParentFile().mkdirs();
}
writer = new FileWriter( file );
writer.write( "This is a test file." );
}
finally
{
IOUtil.close( writer );
}
ArchiveEntryUtils.chmod( file, mode, logger, false );
}
public void testCreateArchive()
throws Exception
{
createArchive(0500, new int[] {0400, 0640, 0664});
createArchive(0500, new int[] {0400, 0640, 0664});
}
public void createArchive(final int directoryMode, final int fileModes[])
throws Exception
{
int defaultFileMode = fileModes[0];
int oneFileMode = fileModes[1];
int twoFileMode = fileModes[2];
TarArchiver archiver = getPosixTarArchiver();
archiver.setDirectoryMode( directoryMode );
archiver.setFileMode( defaultFileMode );
archiver.addDirectory( getTestFile( "src/main" ) );
archiver.setFileMode( oneFileMode );
archiver.addFile( getTestFile( "src/test/resources/manifests/manifest1.mf" ), "one.txt" );
archiver.addFile( getTestFile( "src/test/resources/manifests/manifest2.mf" ), "two.txt", twoFileMode );
archiver.setDestFile( getTestFile( "target/output/archive.tar" ) );
archiver.addSymlink("link_to_test_destinaton", "../test_destination/");
archiver.createArchive();
TarArchiveInputStream tis;
tis = new TarArchiveInputStream( bufferedInputStream( new FileInputStream( archiver.getDestFile() ) ) );
TarArchiveEntry te;
while ( ( te = tis.getNextTarEntry() ) != null )
{
if ( te.isDirectory() )
{
assertEquals( "un-expected tar-entry mode for [te.name=" + te.getName() + "]", directoryMode,
te.getMode() & UnixStat.PERM_MASK );
}
else if ( te.isSymbolicLink() )
{
assertEquals( "../test_destination/", te.getLinkName() );
assertEquals( "link_to_test_destinaton", te.getName() );
assertEquals( 0640, te.getMode() & UnixStat.PERM_MASK );
}
else
{
if ( te.getName().equals( "one.txt" ) )
{
assertEquals( oneFileMode, te.getMode() & UnixStat.PERM_MASK );
}
else if ( te.getName().equals( "two.txt" ) )
{
assertEquals( twoFileMode, te.getMode() & UnixStat.PERM_MASK );
}
else
{
assertEquals( "un-expected tar-entry mode for [te.name=" + te.getName() + "]", defaultFileMode,
te.getMode() & UnixStat.PERM_MASK );
}
}
}
IOUtil.close( tis );
}
public void testCreateArchiveWithJiustASymlink()
throws Exception
{
TarArchiver archiver = getPosixTarArchiver();
archiver.setDirectoryMode( 0500 );
archiver.setFileMode( 0400 );
archiver.setFileMode( 0640 );
archiver.setDestFile( getTestFile( "target/output/symlinkarchive.tar" ) );
archiver.addSymlink("link_to_test_destinaton", "../test_destination/");
archiver.createArchive();
TarArchiveInputStream tis;
tis = new TarArchiveInputStream( new BufferedInputStream( new FileInputStream( archiver.getDestFile() ) ) );
TarArchiveEntry te;
while ( ( te = tis.getNextTarEntry() ) != null )
{
if ( te.isDirectory() )
{
assertEquals( "un-expected tar-entry mode for [te.name=" + te.getName() + "]", 0500,
te.getMode() & UnixStat.PERM_MASK );
}
else if ( te.isSymbolicLink() )
{
assertEquals( "../test_destination/", te.getLinkName() );
assertEquals( "link_to_test_destinaton", te.getName() );
assertEquals( 0640, te.getMode() & UnixStat.PERM_MASK );
}
else
{
assertEquals( "un-expected tar-entry mode for [te.name=" + te.getName() + "]", 0400,
te.getMode() & UnixStat.PERM_MASK );
}
}
tis.close();
}
private TarArchiver getPosixTarArchiver() throws Exception {
TarArchiver archiver = (TarArchiver) lookup( Archiver.ROLE, "tar" );
archiver.setLongfile(TarLongFileMode.posix );
return archiver;
}
private class TarHandler
{
File createTarFile()
throws Exception
{
final File srcDir = new File( "src" );
final File tarFile = new File( "target/output/src.tar" );
TarArchiver tarArchiver = getPosixTarArchiver();
tarArchiver.setDestFile( tarFile );
tarArchiver.addDirectory( srcDir, null, FileUtils.getDefaultExcludes() );
FileUtils.removePath( tarFile.getPath() );
tarArchiver.createArchive();
return tarFile;
}
File createTarfile2( File tarFile )
throws Exception
{
final File tarFile2 = new File( "target/output/src2.tar" );
TarArchiver tarArchiver2 = getPosixTarArchiver();
tarArchiver2.setDestFile( tarFile2 );
tarArchiver2.addArchivedFileSet( tarFile, "prfx/" );
FileUtils.removePath( tarFile2.getPath() );
tarArchiver2.createArchive();
return tarFile2;
}
TarFile newTarFile( File tarFile )
{
return new TarFile( tarFile );
}
}
private class GZipTarHandler
extends TarHandler
{
File createTarFile()
throws Exception
{
File file = super.createTarFile();
File compressedFile = new File( file.getPath() + ".gz" );
Compressor compressor = new GZipCompressor();
compressor.setSource( createResource( file, file.getName() ) );
compressor.setDestFile( compressedFile );
compressor.compress();
compressor.close();
return compressedFile;
}
TarFile newTarFile( File tarFile )
{
return new GZipTarFile( tarFile );
}
}
private class BZip2TarHandler
extends TarHandler
{
File createTarFile()
throws Exception
{
File file = super.createTarFile();
File compressedFile = new File( file.getPath() + ".bz2" );
Compressor compressor = new BZip2Compressor();
compressor.setSource( createResource( file ) );
compressor.setDestFile( compressedFile );
compressor.compress();
compressor.close();
return compressedFile;
}
TarFile newTarFile( File tarFile )
{
return new BZip2TarFile( tarFile );
}
}
public void testUncompressedResourceCollection()
throws Exception
{
testCreateResourceCollection( new TarHandler() );
}
public void testGzipCompressedResourceCollection()
throws Exception
{
testCreateResourceCollection( new GZipTarHandler() );
}
public void testGzipFIleHandleLeak()
throws Exception
{
GZipTarHandler tarHandler = new GZipTarHandler();
final File tarFile = tarHandler.createTarFile();
final File tarFile2 = tarHandler.createTarfile2( tarFile );
final TarFile cmp1 = tarHandler.newTarFile( tarFile );
final TarFile cmp2 = new TarFile( tarFile2 );
ArchiveFileComparator.forEachTarArchiveEntry( cmp1, new ArchiveFileComparator.TarArchiveEntryConsumer()
{
public void accept( TarArchiveEntry ze1 )
throws IOException
{
final String name1 = ze1.getName();
Assert.assertNotNull( name1 );
}
} );
cmp1.close();
cmp2.close();
}
public void testBzip2CompressedResourceCollection()
throws Exception
{
testCreateResourceCollection( new BZip2TarHandler() );
}
public void testTarFileNotClosingInputStream()
throws Exception
{
// Supposedly not closing the stream according to yjp.
TarHandler tarHandler = new BZip2TarHandler();
final File fileName = tarHandler.createTarFile();
final TarFile tarFile = tarHandler.newTarFile( fileName );
tarFile.getEntries();
tarFile.close();
}
private void testCreateResourceCollection( TarHandler tarHandler )
throws Exception
{
final File tarFile = tarHandler.createTarFile();
final File tarFile2 = tarHandler.createTarfile2( tarFile );
final TarFile cmp1 = tarHandler.newTarFile( tarFile );
final TarFile cmp2 = new TarFile( tarFile2 );
ArchiveFileComparator.assertEquals( cmp1, cmp2, "prfx/" );
cmp1.close();
cmp2.close();
}
}