All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.redline_rpm.payload.CpioHeader Maven / Gradle / Ivy

Go to download

Redline is a pure Java library for manipulating RPM Package Manager packages.

There is a newer version: 1.2.10
Show newest version
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( "ASCII");

	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();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy