PageRenderTime 53ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/org/redline_rpm/payload/CpioHeader.java

http://github.com/craigwblake/redline
Java | 279 lines | 221 code | 38 blank | 20 comment | 5 complexity | 6f26d1b924b0d3e31a315e8e203158e1 MD5 | raw file
  1. package org.redline_rpm.payload;
  2. import org.redline_rpm.Util;
  3. import org.slf4j.Logger;
  4. import org.slf4j.LoggerFactory;
  5. import java.io.File;
  6. import java.io.IOException;
  7. import java.net.URL;
  8. import java.net.URLConnection;
  9. import java.nio.ByteBuffer;
  10. import java.nio.CharBuffer;
  11. import java.nio.channels.ReadableByteChannel;
  12. import java.nio.channels.WritableByteChannel;
  13. import java.nio.charset.Charset;
  14. import java.util.Date;
  15. import static org.redline_rpm.Util.normalizePath;
  16. /**
  17. * This class provides a means to read file content from the compressed CPIO stream
  18. * that is the body of an RPM distributable. Iterative calls to to read header will
  19. * result in a header description being returned which includes a count of how many bytes
  20. * to read from the channel for the file content.
  21. */
  22. public class CpioHeader {
  23. private final Logger LOGGER = LoggerFactory.getLogger(CpioHeader.class);
  24. public static final int DEFAULT_FILE_PERMISSION = 0644;
  25. public static final int DEFAULT_DIRECTORY_PERMISSION = 0755;
  26. public static final String DEFAULT_USERNAME = "root";
  27. public static final String DEFAULT_GROUP = "root";
  28. public static final int FIFO = 1;
  29. public static final int CDEV = 2;
  30. public static final int DIR = 4;
  31. public static final int BDEV = 6;
  32. public static final int FILE = 8;
  33. public static final int SYMLINK = 10;
  34. public static final int SOCKET = 12;
  35. protected static final int CPIO_HEADER = 110;
  36. protected static final String MAGIC = "070701";
  37. protected static final String TRAILER = "TRAILER!!!";
  38. protected Charset charset = Charset.forName( "UTF-8");
  39. protected int inode;
  40. protected int type;
  41. protected int permissions = DEFAULT_FILE_PERMISSION;
  42. protected int uid;
  43. protected String uname;
  44. protected int gid;
  45. protected String gname;
  46. protected int nlink = 1;
  47. protected long mtime;
  48. protected int filesize;
  49. protected int devMinor = 1;
  50. protected int devMajor = 9;
  51. protected int rdevMinor;
  52. protected int rdevMajor;
  53. protected int checksum;
  54. protected String name;
  55. protected int flags;
  56. protected int verifyFlags = -1;
  57. public CpioHeader() {
  58. }
  59. public CpioHeader( final String name) {
  60. this.name = name;
  61. }
  62. public CpioHeader( final File file) {
  63. this( file.getAbsolutePath(), file);
  64. }
  65. public CpioHeader( final String name, final URL url) {
  66. try {
  67. URLConnection connection = url.openConnection();
  68. mtime = connection.getLastModified();
  69. filesize = connection.getContentLength();
  70. this.name = normalizePath( name);
  71. setType( FILE);
  72. } catch ( IOException e) {
  73. throw new RuntimeException( e);
  74. }
  75. }
  76. public CpioHeader( final String name, final File file) {
  77. mtime = file.lastModified();
  78. filesize = ( int ) file.length();
  79. this.name = normalizePath( name);
  80. if ( file.isDirectory()) setType( DIR);
  81. else setType( FILE);
  82. }
  83. public int getType() { return type; }
  84. public int getPermissions() { return permissions; }
  85. public int getRdevMajor() { return rdevMajor; }
  86. public int getRdevMinor() { return rdevMinor; }
  87. public int getDevMajor() { return devMajor; }
  88. public int getDevMinor() { return devMinor; }
  89. public int getMtime() { return ( int) ( mtime / 1000L) ; }
  90. public int getInode() { return inode; }
  91. public String getName() { return name; }
  92. public int getFlags() { return flags; }
  93. public int getVerifyFlags() { return verifyFlags; }
  94. public int getMode() { return ( type << 12) | ( permissions & 07777); }
  95. public void setPermissions( int permissions) { this.permissions = permissions; }
  96. public void setType( int type) { this.type = type; }
  97. public void setFileSize( int filesize) { this.filesize = filesize; }
  98. public void setMtime( long mtime) { this.mtime = mtime; }
  99. public void setInode( int inode) { this.inode = inode; }
  100. public void setFlags( int flags) { this.flags = flags; }
  101. public void setVerifyFlags( int verifyFlags) { this.verifyFlags = verifyFlags; }
  102. public String getUname() { return this.uname; }
  103. public String getGname() { return this.gname; }
  104. public void setUname( String uname) { this.uname = uname; }
  105. public void setGname( String gname) { this.gname = gname; }
  106. /**
  107. * Test to see if this is the last header, and is therefore the end of the
  108. * archive. Uses the CPIO magic trailer value to denote the last header of
  109. * the stream.
  110. * @return true if last, false if not
  111. */
  112. public boolean isLast() {
  113. return TRAILER.equals(name);
  114. }
  115. public void setLast() {
  116. name = TRAILER;
  117. }
  118. public void setName( String name) {
  119. this.name = name;
  120. }
  121. public int getFileSize() {
  122. return filesize;
  123. }
  124. protected ByteBuffer writeSix( CharSequence data) {
  125. return charset.encode( pad( data, 6));
  126. }
  127. protected ByteBuffer writeEight( int data) {
  128. return charset.encode( pad( Integer.toHexString( data), 8));
  129. }
  130. protected CharSequence readSix( CharBuffer buffer) {
  131. return readChars( buffer, 6);
  132. }
  133. protected int readEight( CharBuffer buffer) {
  134. return Integer.parseInt( readChars( buffer, 8).toString(), 16);
  135. }
  136. protected CharSequence readChars( CharBuffer buffer, int length) {
  137. if ( buffer.remaining() < length) throw new IllegalStateException( "Buffer has '" + buffer.remaining() + "' bytes but '" + length + "' are needed.");
  138. try {
  139. return buffer.subSequence( 0, length);
  140. } finally {
  141. buffer.position( buffer.position() + length);
  142. }
  143. }
  144. protected String pad( CharSequence sequence, final int length) {
  145. while ( sequence.length() < length) sequence = "0" + sequence;
  146. return sequence.toString();
  147. }
  148. protected int skip( final ReadableByteChannel channel, final int total) throws IOException {
  149. int skipped = Util.difference( total, 3);
  150. LOGGER.debug("Skipping '{}' bytes from stream at position '{}'.",skipped,total);
  151. Util.fill( channel, skipped);
  152. return skipped;
  153. }
  154. public int skip( final WritableByteChannel channel, int total) throws IOException {
  155. int skipped = Util.difference( total, 3);
  156. Util.empty( channel, ByteBuffer.allocate( skipped));
  157. LOGGER.debug("Skipping '{}' bytes from stream at position '{}'.",skipped,total);
  158. return skipped;
  159. }
  160. public int read( final ReadableByteChannel channel, int total) throws IOException {
  161. total += skip( channel, total);
  162. ByteBuffer descriptor = Util.fill( channel, CPIO_HEADER);
  163. CharBuffer buffer = charset.decode( descriptor);
  164. final CharSequence magic = readSix( buffer);
  165. if ( !MAGIC.equals(magic.toString())) throw new IllegalStateException( "Invalid magic number '" + magic + "' of length '" + magic.length() + "'.");
  166. inode = readEight( buffer);
  167. final int mode = readEight( buffer);
  168. permissions = mode & 07777;
  169. type = mode >>> 12;
  170. uid = readEight( buffer);
  171. gid = readEight( buffer);
  172. nlink = readEight( buffer);
  173. mtime = 1000L * readEight( buffer);
  174. filesize = readEight( buffer);
  175. devMajor = readEight( buffer);
  176. devMinor = readEight( buffer);
  177. rdevMajor = readEight( buffer);
  178. rdevMinor = readEight( buffer);
  179. int namesize = readEight( buffer);
  180. checksum = readEight( buffer);
  181. total += CPIO_HEADER;
  182. name = charset.decode( Util.fill( channel, namesize - 1)).toString();
  183. Util.fill( channel, 1);
  184. total += namesize;
  185. total += skip( channel, total);
  186. return total;
  187. }
  188. /**
  189. * Write the content for the CPIO header, including the name immediately following. The name data is rounded
  190. * to the nearest 2 byte boundary as CPIO requires by appending a null when needed.
  191. * @param channel which channel to write on
  192. * @param total current size of header?
  193. * @return total written and skipped
  194. * @throws IOException there was an IO error
  195. */
  196. public int write( final WritableByteChannel channel, int total) throws IOException {
  197. final ByteBuffer buffer = charset.encode( CharBuffer.wrap( name));
  198. int length = buffer.remaining() + 1;
  199. ByteBuffer descriptor = ByteBuffer.allocate( CPIO_HEADER);
  200. descriptor.put( writeSix( MAGIC));
  201. descriptor.put( writeEight( inode));
  202. descriptor.put( writeEight( getMode()));
  203. descriptor.put( writeEight( uid));
  204. descriptor.put( writeEight( gid));
  205. descriptor.put( writeEight( nlink));
  206. descriptor.put( writeEight(( int) ( mtime / 1000)));
  207. descriptor.put( writeEight( filesize));
  208. descriptor.put( writeEight( devMajor));
  209. descriptor.put( writeEight( devMinor));
  210. descriptor.put( writeEight( rdevMajor));
  211. descriptor.put( writeEight( rdevMinor));
  212. descriptor.put( writeEight( length));
  213. descriptor.put( writeEight( checksum));
  214. descriptor.flip();
  215. total += CPIO_HEADER + length;
  216. Util.empty( channel, descriptor);
  217. Util.empty( channel, buffer);
  218. Util.empty( channel, ByteBuffer.allocate( 1));
  219. return total + skip( channel, total);
  220. }
  221. public String toString() {
  222. StringBuilder builder = new StringBuilder();
  223. builder.append( "Inode: ").append( inode).append( "\n");
  224. builder.append( "Permission: ").append( Integer.toString( permissions, 8)).append( "\n");
  225. builder.append( "Type: ").append( type).append( "\n");
  226. builder.append( "UID: ").append( uid).append( "\n");
  227. builder.append( "GID: ").append( gid).append( "\n");
  228. builder.append( "UserName: ").append( uname).append( "\n");
  229. builder.append( "GroupName: ").append( gname).append( "\n");
  230. builder.append( "Nlink: ").append( nlink).append( "\n");
  231. builder.append( "MTime: ").append( new Date( mtime)).append( "\n");
  232. builder.append( "FileSize: ").append( filesize).append( "\n");
  233. builder.append( "DevMinor: ").append( devMinor).append( "\n");
  234. builder.append( "DevMajor: ").append( devMajor).append( "\n");
  235. builder.append( "RDevMinor: ").append( rdevMinor).append( "\n");
  236. builder.append( "RDevMajor: ").append( rdevMajor).append( "\n");
  237. builder.append( "NameSize: ").append( name.length() + 1).append( "\n");
  238. builder.append( "Name: ").append( name).append( "\n");
  239. return builder.toString();
  240. }
  241. }