KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > wizards > datatransfer > TarInputStream


1 /*******************************************************************************
2  * Copyright (c) 2004, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.ui.internal.wizards.datatransfer;
12
13 import java.io.FilterInputStream JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.io.InputStream JavaDoc;
16
17 /**
18  * Input stream for reading files in ustar format (tar) compatible
19  * with the specification in IEEE Std 1003.1-2001. Also supports
20  * long filenames encoded using the GNU @LongLink extension.
21  *
22  * @since 3.1
23  */

24 public class TarInputStream extends FilterInputStream JavaDoc
25 {
26     private int nextEntry = 0;
27     private int nextEOF = 0;
28     private int filepos = 0;
29     private int bytesread = 0;
30     private TarEntry firstEntry = null;
31     private String JavaDoc longLinkName = null;
32
33     /**
34      * Creates a new tar input stream on the given input stream.
35      *
36      * @param in input stream
37      * @throws TarException
38      * @throws IOException
39      */

40     public TarInputStream(InputStream JavaDoc in) throws TarException, IOException JavaDoc {
41         super(in);
42
43         // Read in the first TarEntry to make sure
44
// the input is a valid tar file stream.
45
firstEntry = getNextEntry();
46     }
47
48     /**
49      * Create a new tar input stream, skipping ahead to the given entry
50      * in the file.
51      *
52      * @param in input stream
53      * @param entry skips to this entry in the file
54      * @throws TarException
55      * @throws IOException
56      */

57     TarInputStream(InputStream JavaDoc in, TarEntry entry) throws TarException, IOException JavaDoc {
58         super(in);
59         skipToEntry(entry);
60     }
61
62     /**
63      * The checksum of a tar file header is simply the sum of the bytes in
64      * the header.
65      *
66      * @param header
67      * @return checksum
68      */

69     private long headerChecksum(byte[] header) {
70         long sum = 0;
71         for(int i = 0; i < 512; i++) {
72             sum += header[i] & 0xff;
73         }
74         return sum;
75     }
76
77     /**
78      * Skips ahead to the position of the given entry in the file.
79      *
80      * @param entry
81      * @returns false if the entry has already been passed
82      * @throws TarException
83      * @throws IOException
84      */

85     boolean skipToEntry(TarEntry entry) throws TarException, IOException JavaDoc {
86         int bytestoskip = entry.filepos - bytesread;
87         if(bytestoskip < 0) {
88             return false;
89         }
90         while(bytestoskip > 0) {
91             long ret = in.skip(bytestoskip);
92             if(ret < 0) {
93                 throw new IOException JavaDoc("early end of stream"); //$NON-NLS-1$
94
}
95             bytestoskip -= ret;
96             bytesread += ret;
97         }
98         filepos = entry.filepos;
99         nextEntry = 0;
100         nextEOF = 0;
101         // Read next header to seek to file data.
102
getNextEntry();
103         return true;
104     }
105
106     /**
107      * Returns true if the header checksum is correct.
108      *
109      * @param header
110      * @return true if this header has a valid checksum
111      */

112     private boolean isValidTarHeader(byte[] header) {
113         long fileChecksum, calculatedChecksum;
114         int pos, i;
115         
116         pos = 148;
117         StringBuffer JavaDoc checksumString = new StringBuffer JavaDoc();
118         for(i = 0; i < 8; i++) {
119             if(header[pos + i] == ' ') {
120                 continue;
121             }
122             if(header[pos + i] == 0 || !Character.isDigit((char) header[pos + i])) {
123                 break;
124             }
125             checksumString.append((char) header[pos + i]);
126         }
127         if(checksumString.length() == 0) {
128             return false;
129         }
130         if(checksumString.charAt(0) != '0') {
131             checksumString.insert(0, '0');
132         }
133         try {
134             fileChecksum = Long.decode(checksumString.toString()).longValue();
135         } catch(NumberFormatException JavaDoc exception) {
136             //This is not valid if it cannot be parsed
137
return false;
138         }
139
140         // Blank out the checksum.
141
for(i = 0; i < 8; i++) {
142             header[pos + i] = ' ';
143         }
144         calculatedChecksum = headerChecksum(header);
145
146         return (fileChecksum == calculatedChecksum);
147     }
148
149     /**
150      * Returns the next entry in the tar file. Does not handle
151      * GNU @LongLink extensions.
152      *
153      * @return the next entry in the tar file
154      * @throws TarException
155      * @throws IOException
156      */

157     TarEntry getNextEntryInternal() throws TarException, IOException JavaDoc {
158         byte[] header = new byte[512];
159         int pos = 0;
160         int i;
161
162         if(firstEntry != null) {
163             TarEntry entryReturn = firstEntry;
164             firstEntry = null;
165             return entryReturn;
166         }
167
168         while(nextEntry > 0) {
169             long ret = in.skip(nextEntry);
170             if(ret < 0) {
171                 throw new IOException JavaDoc("early end of stream"); //$NON-NLS-1$
172
}
173             nextEntry -= ret;
174             bytesread += ret;
175         }
176
177         int bytestoread = 512;
178         while(bytestoread > 0) {
179             int ret = super.read(header, 512 - bytestoread, bytestoread);
180             if( ret < 0 ) {
181                 throw new IOException JavaDoc("early end of stream"); //$NON-NLS-1$
182
}
183             bytestoread -= ret;
184             bytesread += ret;
185         }
186
187         // If we have a header of all zeros, this marks the end of the file.
188
if(headerChecksum(header) == 0) {
189             // We are at the end of the file.
190
if(filepos > 0) {
191                 return null;
192             }
193             
194             // Invalid stream.
195
throw new TarException("not in tar format"); //$NON-NLS-1$
196
}
197         
198         // Validate checksum.
199
if(!isValidTarHeader(header)) {
200             throw new TarException("not in tar format"); //$NON-NLS-1$
201
}
202
203         while (pos < 100 && header[pos] != 0) {
204             pos++;
205         }
206         String JavaDoc name = new String JavaDoc(header, 0, pos, "UTF8"); //$NON-NLS-1$
207
// Prepend the prefix here.
208
pos = 345;
209         if(header[pos] != 0) {
210             while (pos < 500 && header[pos] != 0) {
211                 pos++;
212             }
213             String JavaDoc prefix = new String JavaDoc(header, 345, pos - 345, "UTF8"); //$NON-NLS-1$
214
name = prefix + "/" + name; //$NON-NLS-1$
215
}
216         
217         TarEntry entry;
218         if(longLinkName != null) {
219             entry = new TarEntry(longLinkName, filepos);
220             longLinkName = null;
221         } else {
222             entry = new TarEntry(name, filepos);
223         }
224         if(header[156] != 0) {
225             entry.setFileType(header[156]);
226         }
227         
228         pos = 100;
229         StringBuffer JavaDoc mode = new StringBuffer JavaDoc();
230         for(i = 0; i < 8; i++) {
231             if(header[pos + i] == 0) {
232                 break;
233             }
234             if(header[pos + i] == ' ') {
235                 continue;
236             }
237             mode.append((char) header[pos + i]);
238         }
239         if(mode.length() > 0 && mode.charAt(0) != '0') {
240             mode.insert(0, '0');
241         }
242         try {
243             long fileMode = Long.decode(mode.toString()).longValue();
244             entry.setMode(fileMode);
245         } catch(NumberFormatException JavaDoc nfe) {
246             throw new TarException(DataTransferMessages.TarImport_invalid_tar_format, nfe);
247         }
248         
249         pos = 100 + 24;
250         StringBuffer JavaDoc size = new StringBuffer JavaDoc();
251         for(i = 0; i < 12; i++) {
252             if(header[pos + i] == 0) {
253                 break;
254             }
255             if(header[pos + i] == ' ') {
256                 continue;
257             }
258             size.append((char) header[pos + i]);
259         }
260         if(size.charAt(0) != '0') {
261             size.insert(0, '0');
262         }
263         int fileSize;
264         try {
265             fileSize = Integer.decode(size.toString()).intValue();
266         } catch(NumberFormatException JavaDoc nfe) {
267             throw new TarException(DataTransferMessages.TarImport_invalid_tar_format, nfe);
268         }
269
270         entry.setSize(fileSize);
271         nextEOF = fileSize;
272         if(fileSize % 512 > 0) {
273             nextEntry = fileSize + (512 - (fileSize % 512));
274         } else {
275             nextEntry = fileSize;
276         }
277         filepos += (nextEntry + 512);
278         return entry;
279     }
280
281     /**
282      * Moves ahead to the next file in the tar archive and returns
283      * a TarEntry object describing it.
284      *
285      * @return the next entry in the tar file
286      * @throws TarException
287      * @throws IOException
288      */

289     public TarEntry getNextEntry() throws TarException, IOException JavaDoc {
290         TarEntry entry = getNextEntryInternal();
291
292         if(entry != null && entry.getName().equals("././@LongLink")) { //$NON-NLS-1$
293
// This is a GNU extension for doing long filenames.
294
// We get a file called ././@LongLink which just contains
295
// the real pathname.
296
byte[] longNameData = new byte[(int) entry.getSize()];
297             int bytesread = 0;
298             while (bytesread < longNameData.length) {
299                 int cur = read(longNameData, bytesread, longNameData.length - bytesread);
300                 if (cur < 0) {
301                     throw new IOException JavaDoc("early end of stream"); //$NON-NLS-1$
302
}
303                 bytesread += cur;
304             }
305
306             int pos = 0;
307             while (pos < longNameData.length && longNameData[pos] != 0) {
308                 pos++;
309             }
310             longLinkName = new String JavaDoc(longNameData, 0, pos, "UTF8"); //$NON-NLS-1$
311
return getNextEntryInternal();
312         }
313         return entry;
314     }
315
316     /* (non-Javadoc)
317      * @see java.io.FilterInputStream#read(byte[], int, int)
318      */

319     public int read(byte[] b, int off, int len) throws IOException JavaDoc {
320         if(nextEOF == 0) {
321             return -1;
322         }
323         if(len > nextEOF) {
324             len = nextEOF;
325         }
326         int size = super.read(b, off, len);
327         nextEntry -= size;
328         nextEOF -= size;
329         bytesread += size;
330         return size;
331     }
332
333     /* (non-Javadoc)
334      * @see java.io.FilterInputStream#read()
335      */

336     public int read() throws IOException JavaDoc {
337         byte[] data = new byte[1];
338         int size = read(data, 0, 1);
339         if (size < 0) {
340             return size;
341         }
342         return data[0];
343     }
344 }
345
Popular Tags