/src/main/java/org/redline_rpm/payload/CpioHeader.java
Java | 279 lines | 221 code | 38 blank | 20 comment | 5 complexity | 6f26d1b924b0d3e31a315e8e203158e1 MD5 | raw file
- package org.redline_rpm.payload;
- import org.redline_rpm.Util;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import java.io.File;
- import java.io.IOException;
- import java.net.URL;
- import java.net.URLConnection;
- import java.nio.ByteBuffer;
- import java.nio.CharBuffer;
- import java.nio.channels.ReadableByteChannel;
- import java.nio.channels.WritableByteChannel;
- import java.nio.charset.Charset;
- import java.util.Date;
- import static org.redline_rpm.Util.normalizePath;
- /**
- * This class provides a means to read file content from the compressed CPIO stream
- * that is the body of an RPM distributable. Iterative calls to to read header will
- * result in a header description being returned which includes a count of how many bytes
- * to read from the channel for the file content.
- */
- public class CpioHeader {
- private final Logger LOGGER = LoggerFactory.getLogger(CpioHeader.class);
-
- public static final int DEFAULT_FILE_PERMISSION = 0644;
- public static final int DEFAULT_DIRECTORY_PERMISSION = 0755;
- public static final String DEFAULT_USERNAME = "root";
- public static final String DEFAULT_GROUP = "root";
- public static final int FIFO = 1;
- public static final int CDEV = 2;
- public static final int DIR = 4;
- public static final int BDEV = 6;
- public static final int FILE = 8;
- public static final int SYMLINK = 10;
- public static final int SOCKET = 12;
-
- protected static final int CPIO_HEADER = 110;
- protected static final String MAGIC = "070701";
- protected static final String TRAILER = "TRAILER!!!";
- protected Charset charset = Charset.forName( "UTF-8");
- protected int inode;
- protected int type;
- protected int permissions = DEFAULT_FILE_PERMISSION;
- protected int uid;
- protected String uname;
- protected int gid;
- protected String gname;
- protected int nlink = 1;
- protected long mtime;
- protected int filesize;
- protected int devMinor = 1;
- protected int devMajor = 9;
- protected int rdevMinor;
- protected int rdevMajor;
- protected int checksum;
- protected String name;
- protected int flags;
- protected int verifyFlags = -1;
- public CpioHeader() {
- }
- public CpioHeader( final String name) {
- this.name = name;
- }
- public CpioHeader( final File file) {
- this( file.getAbsolutePath(), file);
- }
- public CpioHeader( final String name, final URL url) {
- try {
- URLConnection connection = url.openConnection();
- mtime = connection.getLastModified();
- filesize = connection.getContentLength();
- this.name = normalizePath( name);
- setType( FILE);
- } catch ( IOException e) {
- throw new RuntimeException( e);
- }
- }
- public CpioHeader( final String name, final File file) {
- mtime = file.lastModified();
- filesize = ( int ) file.length();
- this.name = normalizePath( name);
- if ( file.isDirectory()) setType( DIR);
- else setType( FILE);
- }
- public int getType() { return type; }
- public int getPermissions() { return permissions; }
- public int getRdevMajor() { return rdevMajor; }
- public int getRdevMinor() { return rdevMinor; }
- public int getDevMajor() { return devMajor; }
- public int getDevMinor() { return devMinor; }
- public int getMtime() { return ( int) ( mtime / 1000L) ; }
- public int getInode() { return inode; }
- public String getName() { return name; }
- public int getFlags() { return flags; }
- public int getVerifyFlags() { return verifyFlags; }
- public int getMode() { return ( type << 12) | ( permissions & 07777); }
- public void setPermissions( int permissions) { this.permissions = permissions; }
- public void setType( int type) { this.type = type; }
- public void setFileSize( int filesize) { this.filesize = filesize; }
- public void setMtime( long mtime) { this.mtime = mtime; }
- public void setInode( int inode) { this.inode = inode; }
- public void setFlags( int flags) { this.flags = flags; }
- public void setVerifyFlags( int verifyFlags) { this.verifyFlags = verifyFlags; }
- public String getUname() { return this.uname; }
- public String getGname() { return this.gname; }
- public void setUname( String uname) { this.uname = uname; }
- public void setGname( String gname) { this.gname = gname; }
- /**
- * Test to see if this is the last header, and is therefore the end of the
- * archive. Uses the CPIO magic trailer value to denote the last header of
- * the stream.
- * @return true if last, false if not
- */
- public boolean isLast() {
- return TRAILER.equals(name);
- }
- public void setLast() {
- name = TRAILER;
- }
- public void setName( String name) {
- this.name = name;
- }
- public int getFileSize() {
- return filesize;
- }
- protected ByteBuffer writeSix( CharSequence data) {
- return charset.encode( pad( data, 6));
- }
- protected ByteBuffer writeEight( int data) {
- return charset.encode( pad( Integer.toHexString( data), 8));
- }
- protected CharSequence readSix( CharBuffer buffer) {
- return readChars( buffer, 6);
- }
- protected int readEight( CharBuffer buffer) {
- return Integer.parseInt( readChars( buffer, 8).toString(), 16);
- }
- protected CharSequence readChars( CharBuffer buffer, int length) {
- if ( buffer.remaining() < length) throw new IllegalStateException( "Buffer has '" + buffer.remaining() + "' bytes but '" + length + "' are needed.");
- try {
- return buffer.subSequence( 0, length);
- } finally {
- buffer.position( buffer.position() + length);
- }
- }
- protected String pad( CharSequence sequence, final int length) {
- while ( sequence.length() < length) sequence = "0" + sequence;
- return sequence.toString();
- }
- protected int skip( final ReadableByteChannel channel, final int total) throws IOException {
- int skipped = Util.difference( total, 3);
- LOGGER.debug("Skipping '{}' bytes from stream at position '{}'.",skipped,total);
- Util.fill( channel, skipped);
- return skipped;
- }
- public int skip( final WritableByteChannel channel, int total) throws IOException {
- int skipped = Util.difference( total, 3);
- Util.empty( channel, ByteBuffer.allocate( skipped));
- LOGGER.debug("Skipping '{}' bytes from stream at position '{}'.",skipped,total);
- return skipped;
- }
- public int read( final ReadableByteChannel channel, int total) throws IOException {
- total += skip( channel, total);
- ByteBuffer descriptor = Util.fill( channel, CPIO_HEADER);
- CharBuffer buffer = charset.decode( descriptor);
- final CharSequence magic = readSix( buffer);
- if ( !MAGIC.equals(magic.toString())) throw new IllegalStateException( "Invalid magic number '" + magic + "' of length '" + magic.length() + "'.");
- inode = readEight( buffer);
-
- final int mode = readEight( buffer);
- permissions = mode & 07777;
- type = mode >>> 12;
-
- uid = readEight( buffer);
- gid = readEight( buffer);
- nlink = readEight( buffer);
- mtime = 1000L * readEight( buffer);
- filesize = readEight( buffer);
- devMajor = readEight( buffer);
- devMinor = readEight( buffer);
- rdevMajor = readEight( buffer);
- rdevMinor = readEight( buffer);
- int namesize = readEight( buffer);
- checksum = readEight( buffer);
- total += CPIO_HEADER;
- name = charset.decode( Util.fill( channel, namesize - 1)).toString();
- Util.fill( channel, 1);
- total += namesize;
- total += skip( channel, total);
- return total;
- }
- /**
- * Write the content for the CPIO header, including the name immediately following. The name data is rounded
- * to the nearest 2 byte boundary as CPIO requires by appending a null when needed.
- * @param channel which channel to write on
- * @param total current size of header?
- * @return total written and skipped
- * @throws IOException there was an IO error
- */
- public int write( final WritableByteChannel channel, int total) throws IOException {
- final ByteBuffer buffer = charset.encode( CharBuffer.wrap( name));
- int length = buffer.remaining() + 1;
- ByteBuffer descriptor = ByteBuffer.allocate( CPIO_HEADER);
- descriptor.put( writeSix( MAGIC));
- descriptor.put( writeEight( inode));
- descriptor.put( writeEight( getMode()));
- descriptor.put( writeEight( uid));
- descriptor.put( writeEight( gid));
- descriptor.put( writeEight( nlink));
- descriptor.put( writeEight(( int) ( mtime / 1000)));
- descriptor.put( writeEight( filesize));
- descriptor.put( writeEight( devMajor));
- descriptor.put( writeEight( devMinor));
- descriptor.put( writeEight( rdevMajor));
- descriptor.put( writeEight( rdevMinor));
- descriptor.put( writeEight( length));
- descriptor.put( writeEight( checksum));
- descriptor.flip();
- total += CPIO_HEADER + length;
- Util.empty( channel, descriptor);
- Util.empty( channel, buffer);
- Util.empty( channel, ByteBuffer.allocate( 1));
- return total + skip( channel, total);
- }
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append( "Inode: ").append( inode).append( "\n");
- builder.append( "Permission: ").append( Integer.toString( permissions, 8)).append( "\n");
- builder.append( "Type: ").append( type).append( "\n");
- builder.append( "UID: ").append( uid).append( "\n");
- builder.append( "GID: ").append( gid).append( "\n");
- builder.append( "UserName: ").append( uname).append( "\n");
- builder.append( "GroupName: ").append( gname).append( "\n");
- builder.append( "Nlink: ").append( nlink).append( "\n");
- builder.append( "MTime: ").append( new Date( mtime)).append( "\n");
- builder.append( "FileSize: ").append( filesize).append( "\n");
- builder.append( "DevMinor: ").append( devMinor).append( "\n");
- builder.append( "DevMajor: ").append( devMajor).append( "\n");
- builder.append( "RDevMinor: ").append( rdevMinor).append( "\n");
- builder.append( "RDevMajor: ").append( rdevMajor).append( "\n");
- builder.append( "NameSize: ").append( name.length() + 1).append( "\n");
- builder.append( "Name: ").append( name).append( "\n");
- return builder.toString();
- }
- }