1 /*******************************************************************************
2 * Copyright (c) 2004, 2015 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
9 * IBM Corporation - initial API and implementation
10 * Marc-Andre Laperle <marc-andre.laperle@ericsson.com> - Copied to Trace Compass to work around bug 501379
11 *******************************************************************************/
12 package org
.eclipse
.tracecompass
.internal
.tmf
.ui
.project
.wizards
.importtrace
;
14 import java
.io
.FilterInputStream
;
15 import java
.io
.IOException
;
16 import java
.io
.InputStream
;
18 import org
.eclipse
.ui
.internal
.wizards
.datatransfer
.DataTransferMessages
;
21 * Input stream for reading files in ustar format (tar) compatible
22 * with the specification in IEEE Std 1003.1-2001. Also supports
23 * long filenames encoded using the GNU @LongLink extension.
25 @SuppressWarnings("restriction")
26 public class TarInputStream
extends FilterInputStream
28 private int nextEntry
= 0;
29 private int nextEOF
= 0;
30 private int filepos
= 0;
31 private long bytesread
= 0;
32 private TarEntry firstEntry
= null;
33 private String longLinkName
= null;
36 * Creates a new tar input stream on the given input stream.
38 * @param in input stream
39 * @throws TarException on Tar error (bad format, etc)
40 * @throws IOException on i/o error
42 public TarInputStream(InputStream in
) throws TarException
, IOException
{
45 // Read in the first TarEntry to make sure
46 // the input is a valid tar file stream.
47 firstEntry
= getNextEntry();
51 * Create a new tar input stream, skipping ahead to the given entry
54 * @param in input stream
55 * @param entry skips to this entry in the file
56 * @throws TarException
59 TarInputStream(InputStream in
, TarEntry entry
) throws TarException
, IOException
{
65 * The checksum of a tar file header is simply the sum of the bytes in
71 private static long headerChecksum(byte[] header
) {
73 for(int i
= 0; i
< 512; i
++) {
74 sum
+= header
[i
] & 0xff;
80 * Skips ahead to the position of the given entry in the file.
83 * @returns false if the entry has already been passed
84 * @throws TarException
87 boolean skipToEntry(TarEntry entry
) throws TarException
, IOException
{
88 long bytestoskip
= entry
.filepos
- bytesread
;
92 while(bytestoskip
> 0) {
93 long ret
= in
.skip(bytestoskip
);
95 throw new IOException("early end of stream"); //$NON-NLS-1$
100 filepos
= entry
.filepos
;
103 // Read next header to seek to file data.
109 * Returns true if the header checksum is correct.
112 * @return true if this header has a valid checksum
114 private static boolean isValidTarHeader(byte[] header
) {
115 long fileChecksum
, calculatedChecksum
;
119 StringBuffer checksumString
= new StringBuffer();
120 for(i
= 0; i
< 8; i
++) {
121 if(header
[pos
+ i
] == ' ') {
124 if(header
[pos
+ i
] == 0 || !Character
.isDigit((char) header
[pos
+ i
])) {
127 checksumString
.append((char) header
[pos
+ i
]);
129 if(checksumString
.length() == 0) {
132 if(checksumString
.charAt(0) != '0') {
133 checksumString
.insert(0, '0');
136 fileChecksum
= Long
.decode(checksumString
.toString()).longValue();
137 } catch(NumberFormatException exception
) {
138 //This is not valid if it cannot be parsed
142 // Blank out the checksum.
143 for(i
= 0; i
< 8; i
++) {
144 header
[pos
+ i
] = ' ';
146 calculatedChecksum
= headerChecksum(header
);
148 return (fileChecksum
== calculatedChecksum
);
152 * Returns the next entry in the tar file. Does not handle
153 * GNU @LongLink extensions.
155 * @return the next entry in the tar file
156 * @throws TarException
157 * @throws IOException
159 TarEntry
getNextEntryInternal() throws TarException
, IOException
{
160 byte[] header
= new byte[512];
164 if(firstEntry
!= null) {
165 TarEntry entryReturn
= firstEntry
;
170 while(nextEntry
> 0) {
171 long ret
= in
.skip(nextEntry
);
173 throw new IOException("early end of stream"); //$NON-NLS-1$
179 int bytestoread
= 512;
180 while(bytestoread
> 0) {
181 int ret
= super.read(header
, 512 - bytestoread
, bytestoread
);
183 throw new IOException("early end of stream"); //$NON-NLS-1$
189 // If we have a header of all zeros, this marks the end of the file.
190 if(headerChecksum(header
) == 0) {
191 // We are at the end of the file.
197 throw new TarException("not in tar format"); //$NON-NLS-1$
200 // Validate checksum.
201 if(!isValidTarHeader(header
)) {
202 throw new TarException("not in tar format"); //$NON-NLS-1$
205 while (pos
< 100 && header
[pos
] != 0) {
208 String name
= new String(header
, 0, pos
, "UTF8"); //$NON-NLS-1$
209 // Prepend the prefix here.
211 if(header
[pos
] != 0) {
212 while (pos
< 500 && header
[pos
] != 0) {
215 String prefix
= new String(header
, 345, pos
- 345, "UTF8"); //$NON-NLS-1$
216 name
= prefix
+ "/" + name
; //$NON-NLS-1$
220 if(longLinkName
!= null) {
221 entry
= new TarEntry(longLinkName
, filepos
);
224 entry
= new TarEntry(name
, filepos
);
226 if(header
[156] != 0) {
227 entry
.setFileType(header
[156]);
231 StringBuffer mode
= new StringBuffer();
232 for(i
= 0; i
< 8; i
++) {
233 if(header
[pos
+ i
] == 0) {
236 if(header
[pos
+ i
] == ' ') {
239 mode
.append((char) header
[pos
+ i
]);
241 if(mode
.length() > 0 && mode
.charAt(0) != '0') {
245 long fileMode
= Long
.decode(mode
.toString()).longValue();
246 entry
.setMode(fileMode
);
247 } catch(NumberFormatException nfe
) {
248 throw new TarException(DataTransferMessages
.TarImport_invalid_tar_format
, nfe
);
252 StringBuffer size
= new StringBuffer();
253 for(i
= 0; i
< 12; i
++) {
254 if(header
[pos
+ i
] == 0) {
257 if(header
[pos
+ i
] == ' ') {
260 size
.append((char) header
[pos
+ i
]);
262 if(size
.charAt(0) != '0') {
267 fileSize
= Integer
.decode(size
.toString()).intValue();
268 } catch(NumberFormatException nfe
) {
269 throw new TarException(DataTransferMessages
.TarImport_invalid_tar_format
, nfe
);
272 entry
.setSize(fileSize
);
274 if(fileSize
% 512 > 0) {
275 nextEntry
= fileSize
+ (512 - (fileSize
% 512));
277 nextEntry
= fileSize
;
279 filepos
+= (nextEntry
+ 512);
284 * Moves ahead to the next file in the tar archive and returns
285 * a TarEntry object describing it.
287 * @return the next entry in the tar file
288 * @throws TarException on Tar error (bad format, etc)
289 * @throws IOException on i/o errors
291 public TarEntry
getNextEntry() throws TarException
, IOException
{
292 TarEntry entry
= getNextEntryInternal();
294 if(entry
!= null && entry
.getName().equals("././@LongLink")) { //$NON-NLS-1$
295 // This is a GNU extension for doing long filenames.
296 // We get a file called ././@LongLink which just contains
297 // the real pathname.
298 byte[] longNameData
= new byte[(int) entry
.getSize()];
300 while (nbytesread
< longNameData
.length
) {
301 int cur
= read(longNameData
, nbytesread
, longNameData
.length
- nbytesread
);
303 throw new IOException("early end of stream"); //$NON-NLS-1$
309 while (pos
< longNameData
.length
&& longNameData
[pos
] != 0) {
312 longLinkName
= new String(longNameData
, 0, pos
, "UTF8"); //$NON-NLS-1$
313 return getNextEntryInternal();
319 public int read(byte[] b
, int off
, int len
) throws IOException
{
324 if(lenToRead
> nextEOF
) {
327 int size
= super.read(b
, off
, lenToRead
);
335 public int read() throws IOException
{
336 byte[] data
= new byte[1];
337 int size
= read(data
, 0, 1);