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.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 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" ) ) { 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; } }