Source code: com/jguild/jrpm/io/archive/CPIOPayloadArchive.java
1 package com.jguild.jrpm.io.archive;
2
3 import com.jguild.jrpm.io.PayloadArchive;
4 import com.jguild.jrpm.io.RPMFile;
5
6 import java.io.*;
7 import java.util.zip.GZIPInputStream;
8 import java.net.URLEncoder;
9
10 import org.apache.log4j.Logger;
11
12
13 /**
14 * Allows to read/write CPIO archives.
15 */
16 public class CPIOPayloadArchive implements PayloadArchive {
17 private static final Logger logger = Logger.getLogger(CPIOPayloadArchive.class);
18 public static final int S_IFMT = 0170000;
19 public static final int S_IFDIR = 0040000;
20 public static final int S_IFLNK = 0120000;
21 private byte compression;
22 private File file;
23 private int offset;
24
25 /**
26 * Creates a new CPIOPayloadArchive object.
27 */
28 public CPIOPayloadArchive(byte compression, File file, int offset) {
29 this.compression = compression;
30 this.file = file;
31 this.offset = offset;
32 }
33
34 /**
35 * Set compression type.
36 * @param compression Compression type.
37 */
38 public void setCompression(byte compression) {
39 this.compression = compression;
40 }
41
42 /**
43 * Get compression type.
44 * @return Compression type.
45 */
46 public byte getCompression() {
47 return compression;
48 }
49
50 /**
51 * Extract payload.
52 * @param basedir base directory to extract payload.
53 * @throws IOException If any I/O error happens.
54 * @todo support symbolic links
55 */
56 public synchronized void extract(File basedir) throws IOException {
57 DataInputStream in;
58 if( compression == PayloadArchive.COMPRESSION_GZIP ) {
59 in = new DataInputStream(new GZIPInputStream(new FileInputStream(file)));
60 } else {
61 in = new DataInputStream(new FileInputStream(file));
62 }
63 in.skip(offset);
64 int count = 0;
65 for(;;) {
66 byte[] magicBytes = new byte[6];
67 in.readFully(magicBytes);
68 count += magicBytes.length;
69 String magic = new String(magicBytes);
70 if( logger.isDebugEnabled() ) {
71 logger.debug("CPIO Magic: "+magic);
72 }
73 if( ! magic.equals("070701")) {
74 throw new IOException("Invalid magic: "+magic);
75 }
76 CPIOHeaderCRC hdr = new CPIOHeaderCRC( magicBytes, in );
77 count += hdr.hdrsize;
78 if( logger.isDebugEnabled() ) {
79 logger.debug("CPIO Header: name="+hdr.name);
80 logger.debug("CPIO Header: inode="+new String(hdr.inode));
81 logger.debug("CPIO Header: mode="+Long.toOctalString(hdr.mode));
82 logger.debug("CPIO Header: symlink ? = "+hdr.isSymlink() );
83 logger.debug("CPIO Header: directory ? = "+hdr.isDir() );
84 logger.debug("CPIO Header: nlink="+new String(hdr.nlink));
85 logger.debug("CPIO Header: maj="+new String(hdr.maj));
86 logger.debug("CPIO Header: min="+new String(hdr.min));
87 logger.debug("CPIO Header: rmaj="+new String(hdr.rmaj));
88 logger.debug("CPIO Header: rmin="+new String(hdr.rmin));
89 }
90 if( hdr.name.equals("TRAILER!!!") ) {
91 in.close();
92 return;
93 }
94 count += pad(in,count,4);
95 if( hdr.isDir() ) {
96 new File(URLEncoder.encode(hdr.name,"UTF-8")).mkdirs();
97 } else {
98 int idx = hdr.name.lastIndexOf('/');
99 if( idx != -1 ) {
100 new File( basedir + File.separator + hdr.name.substring(0,idx) ).mkdirs();
101 }
102 FileOutputStream out = new FileOutputStream( new File( basedir + File.separator + hdr.name ) );
103 for( int i= 0 ; i < hdr.filesize ; i++ ) {
104 int data = in.read();
105 if( data == -1 ) {
106 throw new EOFException("Unexpected end of payload.");
107 }
108 count ++;
109 out.write(data);
110 }
111 out.close();
112 }
113 count += pad(in,count,4);
114 }
115 }
116
117 private int pad( DataInputStream is, int count, int boundary ) throws IOException {
118 int r = count % boundary;
119 if( r > 0 ) {
120 int skip = boundary-r;
121 is.skipBytes(skip);
122 return skip;
123 }
124 return 0;
125 }
126
127 public class CPIOHeaderCRC {
128 int hdrsize;
129 byte[] magic;
130 byte[] inode = new byte[8];
131 long mode;
132 byte[] uid = new byte[8];
133 byte[] gid = new byte[8];
134 byte[] nlink = new byte[8];
135 byte[] mtime = new byte[8];
136 long filesize;
137 byte[] maj = new byte[8];
138 byte[] min = new byte[8];
139 byte[] rmaj = new byte[8];
140 byte[] rmin = new byte[8];
141 long namesize;
142 byte[] chksum = new byte[8];
143 String name;
144
145 /**
146 * Constructor for CPIOHeaderCRC
147 * @param magic
148 * @param in
149 * @throws IOException
150 * @throws EOFException
151 * @todo I've read that namesize needs to be round to nearest 2 byte boundary?
152 */
153 public CPIOHeaderCRC(byte[] magic, DataInputStream in ) throws IOException, EOFException {
154 this.magic = magic;
155 read(in,inode);
156 mode = readAsciiLong(in);
157 read(in,uid);
158 read(in,gid);
159 read(in,nlink);
160 read(in,mtime);
161 filesize = readAsciiLong(in);
162 read(in,maj);
163 read(in,min);
164 read(in,rmaj);
165 read(in,rmin);
166 namesize = readAsciiLong(in);
167 read(in,chksum);
168 byte[] _name = new byte[(int)namesize];
169 read(in,_name);
170 name = new String(_name,0,(int)namesize-1);
171 }
172
173 private void read( DataInputStream in, byte[] data ) throws IOException {
174 in.readFully(data);
175 hdrsize += data.length;
176 }
177
178 private long readAsciiLong( DataInputStream is ) throws IOException {
179 byte[] data = new byte[8];
180 is.readFully(data);
181 return Long.decode("0x"+new String(data)).longValue();
182 }
183
184 boolean isSymlink() {
185 return (mode & S_IFMT) == S_IFLNK;
186 }
187
188 boolean isDir() {
189 return (mode & S_IFMT) == S_IFDIR;
190 }
191 }
192 }