package org.openslx.satserver.util;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;
import java.util.GregorianCalendar;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveException;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.apache.commons.compress.archivers.ar.ArArchiveEntry;
import org.apache.commons.compress.archivers.cpio.CpioArchiveEntry;
import org.apache.commons.compress.archivers.dump.DumpArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.compressors.CompressorStreamFactory;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.compress.compressors.lz4.BlockLZ4CompressorOutputStream;
import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
public class Archive
{
/**
* Create input stream from given archive file. The archive format will be guessed
* from the firt couple of bytes in the file, which should work for most archive
* formats (see apache commons-compress docs for detailed information)
*
* @param fileName archive to open for reading
* @return ArchiveInputStream stream to read archive from
* @throws FileNotFoundException
* @throws ArchiveException
* @throws IllegalArgumentException
*/
public static ArchiveInputStream<? extends ArchiveEntry> getArchiveInputStream( String fileName )
throws FileNotFoundException, ArchiveException, IllegalArgumentException
{
// Get an input stream for the archive handler
// 1) Get input stream from file
InputStream fileStream = new BufferedInputStream( new FileInputStream( fileName ) );
// 2) Might be compressed, so try to get a decompressed stream
InputStream archiveStream;
try {
archiveStream = new BufferedInputStream( new CompressorStreamFactory().createCompressorInputStream( fileStream ) );
} catch ( Exception e ) {
// Might be uncompressed archive, like tar - just try file input stream
archiveStream = fileStream;
}
// 3) Try to create archive input stream - if it succeeds we can read the archive :)
return new ArchiveStreamFactory().createArchiveInputStream( archiveStream );
}
/**
* Create tar archive with the given name. Optional compression method is
* determined by the file extension.
*
* @param outputFile name of archive to create; can be relative or absolute path
* @return {@link TarArchiveOutputStream} to write archive entries to
* @throws IOException
*/
@SuppressWarnings( "resource" )
public static TarArchiveOutputStream createTarArchive( String outputFile ) throws IOException
{
OutputStream outputStream = new BufferedOutputStream( new FileOutputStream( outputFile ) );
OutputStream compressedOutputStream;
if ( outputFile.endsWith( ".tar.gz" ) || outputFile.endsWith( ".tgz" ) ) {
compressedOutputStream = new BufferedOutputStream( new GzipCompressorOutputStream( outputStream ) );
} else if ( outputFile.endsWith( ".tar.bz2" ) || outputFile.endsWith( ".tbz" ) ) {
compressedOutputStream = new BufferedOutputStream( new BZip2CompressorOutputStream( outputStream ) );
} else if ( outputFile.endsWith( ".tar.xz" ) || outputFile.endsWith( ".txz" ) ) {
compressedOutputStream = new BufferedOutputStream( new XZCompressorOutputStream( outputStream ) );
} else if ( outputFile.endsWith( ".tar.Z" ) || outputFile.endsWith( ".tar.z" ) || outputFile.endsWith( ".tz" ) ) {
compressedOutputStream = new BufferedOutputStream( new XZCompressorOutputStream( outputStream ) );
} else if ( outputFile.endsWith( ".tar.lzma" ) ) {
compressedOutputStream = new BufferedOutputStream( new XZCompressorOutputStream( outputStream ) );
} else if ( outputFile.endsWith( ".tar.lz4" ) ) {
compressedOutputStream = new BufferedOutputStream( new BlockLZ4CompressorOutputStream( outputStream ) );
} else if ( outputFile.endsWith( ".tar" ) ) {
compressedOutputStream = outputStream;
} else {
outputStream.close();
return null;
}
try {
TarArchiveOutputStream stream = new TarArchiveOutputStream( compressedOutputStream );
stream.setLongFileMode( TarArchiveOutputStream.LONGFILE_POSIX );
return stream;
} catch ( Throwable t ) {
compressedOutputStream.close();
return null;
}
}
public static TarArchiveEntry createTarArchiveEntry( ArchiveEntry inEntry,
int defaultUser, int defaultGroup, int defaultDirMode, int defaultFileMode, boolean overrideUserGroup )
{
String name = inEntry.getName();
if ( Util.isEmpty( name ) )
return null;
if ( !name.startsWith( "/" ) ) {
name = "/" + name;
}
name = FilenameUtils.normalize( name );
if ( name == null )
return null;
if ( Util.isEmpty( name ) )
return null;
final TarArchiveEntry outEntry = new TarArchiveEntry( name );
outEntry.setSize( inEntry.getSize() );
if ( inEntry instanceof TarArchiveEntry ) {
// Source is tar - easy
byte[] buffer = new byte[1000];
( (TarArchiveEntry)inEntry ).writeEntryHeader( buffer );
outEntry.parseTarHeader( buffer );
outEntry.setName( name ); // Reset, as this was overwritten
if ( overrideUserGroup ) {
// Always replace these
outEntry.setUserId( defaultUser );
outEntry.setGroupId( defaultGroup );
// TODO
if ( defaultUser == 0 ) {
outEntry.setUserName( "root" );
}
if ( defaultGroup == 0 ) {
outEntry.setGroupName( "root" );
}
}
} else if ( inEntry instanceof ArArchiveEntry ) {
// Source is ar - has most of the stuff tar supports; transform
outEntry.setIds( ( (ArArchiveEntry)inEntry ).getUserId(), ( (ArArchiveEntry)inEntry ).getGroupId() );
outEntry.setMode( ( (ArArchiveEntry)inEntry ).getMode() );
outEntry.setModTime( ( (ArArchiveEntry)inEntry ).getLastModifiedDate() );
} else if ( inEntry instanceof DumpArchiveEntry ) {
// Source is dump (whatever the fuck that is) - has most of the stuff tar supports; transform
outEntry.setIds( ( (DumpArchiveEntry)inEntry ).getUserId(), ( (DumpArchiveEntry)inEntry ).getGroupId() );
outEntry.setMode( ( (DumpArchiveEntry)inEntry ).getMode() );
outEntry.setModTime( ( (DumpArchiveEntry)inEntry ).getLastModifiedDate() );
} else if ( inEntry instanceof CpioArchiveEntry ) {
// Source is cpio - has most of the stuff tar supports; transform
outEntry.setIds( (int) ( (CpioArchiveEntry)inEntry ).getUID(), (int) ( (CpioArchiveEntry)inEntry ).getGID() );
outEntry.setMode( (int) ( ( (CpioArchiveEntry)inEntry ).getMode() & 07777 ) );
outEntry.setModTime( ( (CpioArchiveEntry)inEntry ).getLastModifiedDate() );
} else if ( inEntry instanceof ZipArchiveEntry ) {
// Source is ZIP - might not have unix stuff
outEntry.setIds( defaultUser, defaultGroup );
int mode = ( (ZipArchiveEntry)inEntry ).getUnixMode();
if ( mode != 0 ) { // Has unix permissions
outEntry.setMode( mode );
} else if ( inEntry.isDirectory() ) { // Use default passed in
outEntry.setMode( defaultDirMode );
} else { // Use default passed in
outEntry.setMode( defaultFileMode );
}
outEntry.setModTime( ( (ZipArchiveEntry)inEntry ).getLastModifiedDate() );
} else {
outEntry.setIds( defaultUser, defaultGroup );
if ( inEntry.isDirectory() ) {
outEntry.setMode( defaultDirMode );
} else {
outEntry.setMode( defaultFileMode );
Calendar cal = GregorianCalendar.getInstance();
cal.set( 2014, 4, 2, 13, 57 );
outEntry.setModTime( cal.getTime() );
}
}
return outEntry;
}
public static TarArchiveEntry createTarArchiveEntry( ArchiveEntry inEntry,
int defaultUser, int defaultGroup, int defaultDirMode, int defaultFileMode )
{
return createTarArchiveEntry( inEntry, defaultUser, defaultGroup, defaultDirMode, defaultFileMode, false );
}
public static boolean tarAddFile( TarArchiveOutputStream tar, String inArchiveFileName, File sourceFile, int mode )
{
if ( !sourceFile.exists() || !sourceFile.isFile() )
return false;
InputStream in;
try {
in = new FileInputStream( sourceFile );
} catch ( Exception e ) {
e.printStackTrace();
return false;
}
TarArchiveEntry entry = new TarArchiveEntry( inArchiveFileName );
long size = FileUtils.sizeOf( sourceFile );
entry.setIds( 0, 0 );
entry.setMode( mode );
entry.setSize( size );
try {
tar.putArchiveEntry( entry );
Util.streamCopy( in, tar, size );
tar.closeArchiveEntry();
} catch ( IOException e ) {
e.printStackTrace();
return false;
} finally {
Util.multiClose( in );
}
return true;
}
public static boolean tarCreateFileFromString( TarArchiveOutputStream tar, String fileName, String contents, int mode )
{
return tarCreateFileFromString( tar, fileName, contents.getBytes( StandardCharsets.UTF_8 ), mode );
}
public static boolean tarCreateFileFromString( TarArchiveOutputStream tar, String fileName, byte[] contents, int mode )
{
TarArchiveEntry entry = new TarArchiveEntry( fileName );
entry.setIds( 0, 0 );
entry.setMode( mode );
entry.setSize( contents.length );
try {
tar.putArchiveEntry( entry );
tar.write( contents );
tar.closeArchiveEntry();
} catch ( IOException e ) {
e.printStackTrace();
return false;
}
return true;
}
public static boolean tarCreateSymlink( TarArchiveOutputStream tar, String target, String linkName )
{
TarArchiveEntry entry = new TarArchiveEntry( linkName, TarArchiveEntry.LF_SYMLINK );
entry.setIds( 0, 0 );
entry.setMode( 0777 );
entry.setLinkName( target );
try {
tar.putArchiveEntry( entry );
tar.closeArchiveEntry();
} catch ( IOException e ) {
e.printStackTrace();
return false;
}
return true;
}
}