|
|
@@ -53,15 +53,15 @@ |
|
|
|
*/ |
|
|
|
|
|
|
|
/* |
|
|
|
* This package is based on the work done by Timothy Gerard Endres |
|
|
|
* This package is based on the work done by Timothy Gerard Endres |
|
|
|
* (time@ice.com) to whom the Ant project is very grateful for his great code. |
|
|
|
*/ |
|
|
|
|
|
|
|
package org.apache.tools.tar; |
|
|
|
|
|
|
|
import java.io.FilterInputStream; |
|
|
|
import java.io.InputStream; |
|
|
|
import java.io.IOException; |
|
|
|
import java.io.InputStream; |
|
|
|
import java.io.OutputStream; |
|
|
|
|
|
|
|
/** |
|
|
@@ -69,20 +69,20 @@ import java.io.OutputStream; |
|
|
|
* methods are provided to position at each successive entry in |
|
|
|
* the archive, and the read each entry as a normal input stream |
|
|
|
* using read(). |
|
|
|
* |
|
|
|
* |
|
|
|
* @author Timothy Gerard Endres <a href="mailto:time@ice.com">time@ice.com</a> |
|
|
|
* @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">stefano@apache.org</a> |
|
|
|
*/ |
|
|
|
public class TarInputStream extends FilterInputStream { |
|
|
|
|
|
|
|
protected boolean debug; |
|
|
|
protected boolean hasHitEOF; |
|
|
|
protected int entrySize; |
|
|
|
protected int entryOffset; |
|
|
|
protected byte[] oneBuf; |
|
|
|
protected byte[] readBuf; |
|
|
|
protected TarBuffer buffer; |
|
|
|
protected TarEntry currEntry; |
|
|
|
|
|
|
|
protected boolean debug; |
|
|
|
protected boolean hasHitEOF; |
|
|
|
protected int entrySize; |
|
|
|
protected int entryOffset; |
|
|
|
protected byte[] oneBuf; |
|
|
|
protected byte[] readBuf; |
|
|
|
protected TarBuffer buffer; |
|
|
|
protected TarEntry currEntry; |
|
|
|
|
|
|
|
public TarInputStream(InputStream is) { |
|
|
|
this(is, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE); |
|
|
@@ -104,29 +104,29 @@ public class TarInputStream extends FilterInputStream { |
|
|
|
|
|
|
|
/** |
|
|
|
* Sets the debugging flag. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param debugF True to turn on debugging. |
|
|
|
*/ |
|
|
|
public void setDebug(boolean debug) { |
|
|
|
this.debug = debug; |
|
|
|
this.buffer.setDebug(debug); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Closes this stream. Calls the TarBuffer's close() method. |
|
|
|
*/ |
|
|
|
public void close() throws IOException { |
|
|
|
this.buffer.close(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Get the record size being used by this stream's TarBuffer. |
|
|
|
* |
|
|
|
* |
|
|
|
* @return The TarBuffer record size. |
|
|
|
*/ |
|
|
|
public int getRecordSize() { |
|
|
|
return this.buffer.getRecordSize(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Get the available data that can be read from the current |
|
|
@@ -134,63 +134,62 @@ public class TarInputStream extends FilterInputStream { |
|
|
|
* is left in the entire archive, only in the current entry. |
|
|
|
* This value is determined from the entry's size header field |
|
|
|
* and the amount of data already read from the current entry. |
|
|
|
* |
|
|
|
* |
|
|
|
* |
|
|
|
* |
|
|
|
* @return The number of available bytes for the current entry. |
|
|
|
*/ |
|
|
|
public int available() throws IOException { |
|
|
|
return this.entrySize - this.entryOffset; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Skip bytes in the input buffer. This skips bytes in the |
|
|
|
* current entry's data, not the entire archive, and will |
|
|
|
* stop at the end of the current entry's data if the number |
|
|
|
* to skip extends beyond that point. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param numToSkip The number of bytes to skip. |
|
|
|
*/ |
|
|
|
public void skip(int numToSkip) throws IOException { |
|
|
|
|
|
|
|
public long skip(long numToSkip) throws IOException { |
|
|
|
// REVIEW |
|
|
|
// This is horribly inefficient, but it ensures that we |
|
|
|
// properly skip over bytes via the TarBuffer... |
|
|
|
// |
|
|
|
// |
|
|
|
byte[] skipBuf = new byte[8 * 1024]; |
|
|
|
|
|
|
|
for (int num = numToSkip; num > 0; ) { |
|
|
|
int numRead = this.read(skipBuf, 0, |
|
|
|
(num > skipBuf.length ? skipBuf.length |
|
|
|
: num)); |
|
|
|
|
|
|
|
long skip = numToSkip; |
|
|
|
while (skip > 0) { |
|
|
|
int realSkip = (int) (skip > skipBuf.length ? skipBuf.length : skip); |
|
|
|
int numRead = this.read(skipBuf, 0, realSkip); |
|
|
|
if (numRead == -1) { |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
num -= numRead; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
skip -= numRead; |
|
|
|
} |
|
|
|
return (numToSkip - skip); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Since we do not support marking just yet, we return false. |
|
|
|
* |
|
|
|
* |
|
|
|
* @return False. |
|
|
|
*/ |
|
|
|
public boolean markSupported() { |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Since we do not support marking just yet, we do nothing. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param markLimit The limit to mark. |
|
|
|
*/ |
|
|
|
public void mark(int markLimit) {} |
|
|
|
public void mark(int markLimit) { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Since we do not support marking just yet, we do nothing. |
|
|
|
*/ |
|
|
|
public void reset() {} |
|
|
|
public void reset() { |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Get the next entry in this tar archive. This will skip |
|
|
@@ -201,93 +200,93 @@ public class TarInputStream extends FilterInputStream { |
|
|
|
* If there are no more entries in the archive, null will |
|
|
|
* be returned to indicate that the end of the archive has |
|
|
|
* been reached. |
|
|
|
* |
|
|
|
* |
|
|
|
* @return The next TarEntry in the archive, or null. |
|
|
|
*/ |
|
|
|
public TarEntry getNextEntry() throws IOException { |
|
|
|
if (this.hasHitEOF) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (this.currEntry != null) { |
|
|
|
int numToSkip = this.entrySize - this.entryOffset; |
|
|
|
|
|
|
|
if (this.debug) { |
|
|
|
System.err.println("TarInputStream: SKIP currENTRY '" |
|
|
|
+ this.currEntry.getName() + "' SZ " |
|
|
|
+ this.entrySize + " OFF " |
|
|
|
+ this.entryOffset + " skipping " |
|
|
|
+ numToSkip + " bytes"); |
|
|
|
} |
|
|
|
System.err.println("TarInputStream: SKIP currENTRY '" |
|
|
|
+ this.currEntry.getName() + "' SZ " |
|
|
|
+ this.entrySize + " OFF " |
|
|
|
+ this.entryOffset + " skipping " |
|
|
|
+ numToSkip + " bytes"); |
|
|
|
} |
|
|
|
|
|
|
|
if (numToSkip > 0) { |
|
|
|
this.skip(numToSkip); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
this.readBuf = null; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
byte[] headerBuf = this.buffer.readRecord(); |
|
|
|
|
|
|
|
if (headerBuf == null) { |
|
|
|
if (this.debug) { |
|
|
|
System.err.println("READ NULL RECORD"); |
|
|
|
} |
|
|
|
} |
|
|
|
this.hasHitEOF = true; |
|
|
|
} else if (this.buffer.isEOFRecord(headerBuf)) { |
|
|
|
if (this.debug) { |
|
|
|
System.err.println("READ EOF RECORD"); |
|
|
|
} |
|
|
|
} |
|
|
|
this.hasHitEOF = true; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (this.hasHitEOF) { |
|
|
|
this.currEntry = null; |
|
|
|
} else { |
|
|
|
this.currEntry = new TarEntry(headerBuf); |
|
|
|
|
|
|
|
if (!(headerBuf[257] == 'u' && headerBuf[258] == 's' |
|
|
|
&& headerBuf[259] == 't' && headerBuf[260] == 'a' |
|
|
|
if (!(headerBuf[257] == 'u' && headerBuf[258] == 's' |
|
|
|
&& headerBuf[259] == 't' && headerBuf[260] == 'a' |
|
|
|
&& headerBuf[261] == 'r')) { |
|
|
|
this.entrySize = 0; |
|
|
|
this.entryOffset = 0; |
|
|
|
this.currEntry = null; |
|
|
|
|
|
|
|
throw new IOException("bad header in block " |
|
|
|
+ this.buffer.getCurrentBlockNum() |
|
|
|
+ " record " |
|
|
|
+ this.buffer.getCurrentRecordNum() |
|
|
|
+ ", " + |
|
|
|
"header magic is not 'ustar', but '" |
|
|
|
+ headerBuf[257] |
|
|
|
+ headerBuf[258] |
|
|
|
+ headerBuf[259] |
|
|
|
+ headerBuf[260] |
|
|
|
+ headerBuf[261] |
|
|
|
+ "', or (dec) " |
|
|
|
+ ((int) headerBuf[257]) |
|
|
|
+ ", " |
|
|
|
+ ((int) headerBuf[258]) |
|
|
|
+ ", " |
|
|
|
+ ((int) headerBuf[259]) |
|
|
|
+ ", " |
|
|
|
+ ((int) headerBuf[260]) |
|
|
|
+ ", " |
|
|
|
+ ((int) headerBuf[261])); |
|
|
|
} |
|
|
|
throw new IOException("bad header in block " |
|
|
|
+ this.buffer.getCurrentBlockNum() |
|
|
|
+ " record " |
|
|
|
+ this.buffer.getCurrentRecordNum() |
|
|
|
+ ", " + |
|
|
|
"header magic is not 'ustar', but '" |
|
|
|
+ headerBuf[257] |
|
|
|
+ headerBuf[258] |
|
|
|
+ headerBuf[259] |
|
|
|
+ headerBuf[260] |
|
|
|
+ headerBuf[261] |
|
|
|
+ "', or (dec) " |
|
|
|
+ ((int) headerBuf[257]) |
|
|
|
+ ", " |
|
|
|
+ ((int) headerBuf[258]) |
|
|
|
+ ", " |
|
|
|
+ ((int) headerBuf[259]) |
|
|
|
+ ", " |
|
|
|
+ ((int) headerBuf[260]) |
|
|
|
+ ", " |
|
|
|
+ ((int) headerBuf[261])); |
|
|
|
} |
|
|
|
|
|
|
|
if (this.debug) { |
|
|
|
System.err.println("TarInputStream: SET CURRENTRY '" |
|
|
|
+ this.currEntry.getName() |
|
|
|
+ "' size = " |
|
|
|
+ this.currEntry.getSize()); |
|
|
|
} |
|
|
|
System.err.println("TarInputStream: SET CURRENTRY '" |
|
|
|
+ this.currEntry.getName() |
|
|
|
+ "' size = " |
|
|
|
+ this.currEntry.getSize()); |
|
|
|
} |
|
|
|
|
|
|
|
this.entryOffset = 0; |
|
|
|
|
|
|
|
// REVIEW How do we resolve this discrepancy?! |
|
|
|
this.entrySize = (int) this.currEntry.getSize(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (this.currEntry != null && this.currEntry.isGNULongNameEntry()) { |
|
|
|
// read in the name |
|
|
@@ -302,13 +301,13 @@ public class TarInputStream extends FilterInputStream { |
|
|
|
} |
|
|
|
|
|
|
|
return this.currEntry; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Reads a byte from the current tar archive entry. |
|
|
|
* |
|
|
|
* |
|
|
|
* This method simply calls read( byte[], int, int ). |
|
|
|
* |
|
|
|
* |
|
|
|
* @return The byte read, or -1 at EOF. |
|
|
|
*/ |
|
|
|
public int read() throws IOException { |
|
|
@@ -319,27 +318,27 @@ public class TarInputStream extends FilterInputStream { |
|
|
|
} else { |
|
|
|
return (int) this.oneBuf[0]; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Reads bytes from the current tar archive entry. |
|
|
|
* |
|
|
|
* |
|
|
|
* This method simply calls read( byte[], int, int ). |
|
|
|
* |
|
|
|
* |
|
|
|
* @param buf The buffer into which to place bytes read. |
|
|
|
* @return The number of bytes read, or -1 at EOF. |
|
|
|
*/ |
|
|
|
public int read(byte[] buf) throws IOException { |
|
|
|
return this.read(buf, 0, buf.length); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Reads bytes from the current tar archive entry. |
|
|
|
* |
|
|
|
* |
|
|
|
* This method is aware of the boundaries of the current |
|
|
|
* entry in the archive and will deal with them as if they |
|
|
|
* were this stream's start and EOF. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param buf The buffer into which to place bytes read. |
|
|
|
* @param offset The offset at which to place bytes read. |
|
|
|
* @param numToRead The number of bytes to read. |
|
|
@@ -350,42 +349,42 @@ public class TarInputStream extends FilterInputStream { |
|
|
|
|
|
|
|
if (this.entryOffset >= this.entrySize) { |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if ((numToRead + this.entryOffset) > this.entrySize) { |
|
|
|
numToRead = (this.entrySize - this.entryOffset); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (this.readBuf != null) { |
|
|
|
int sz = (numToRead > this.readBuf.length) ? this.readBuf.length |
|
|
|
: numToRead; |
|
|
|
int sz = (numToRead > this.readBuf.length) ? this.readBuf.length |
|
|
|
: numToRead; |
|
|
|
|
|
|
|
System.arraycopy(this.readBuf, 0, buf, offset, sz); |
|
|
|
|
|
|
|
if (sz >= this.readBuf.length) { |
|
|
|
this.readBuf = null; |
|
|
|
} else { |
|
|
|
int newLen = this.readBuf.length - sz; |
|
|
|
int newLen = this.readBuf.length - sz; |
|
|
|
byte[] newBuf = new byte[newLen]; |
|
|
|
|
|
|
|
System.arraycopy(this.readBuf, sz, newBuf, 0, newLen); |
|
|
|
|
|
|
|
this.readBuf = newBuf; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
totalRead += sz; |
|
|
|
numToRead -= sz; |
|
|
|
offset += sz; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
while (numToRead > 0) { |
|
|
|
byte[] rec = this.buffer.readRecord(); |
|
|
|
|
|
|
|
if (rec == null) { |
|
|
|
// Unexpected EOF! |
|
|
|
throw new IOException("unexpected EOF with " + numToRead |
|
|
|
+ " bytes unread"); |
|
|
|
} |
|
|
|
throw new IOException("unexpected EOF with " + numToRead |
|
|
|
+ " bytes unread"); |
|
|
|
} |
|
|
|
|
|
|
|
int sz = numToRead; |
|
|
|
int recLen = rec.length; |
|
|
@@ -400,22 +399,22 @@ public class TarInputStream extends FilterInputStream { |
|
|
|
sz = recLen; |
|
|
|
|
|
|
|
System.arraycopy(rec, 0, buf, offset, recLen); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
totalRead += sz; |
|
|
|
numToRead -= sz; |
|
|
|
offset += sz; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
this.entryOffset += totalRead; |
|
|
|
|
|
|
|
return totalRead; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* Copies the contents of the current tar archive entry directly into |
|
|
|
* an output stream. |
|
|
|
* |
|
|
|
* |
|
|
|
* @param out The OutputStream into which to write the entry's data. |
|
|
|
*/ |
|
|
|
public void copyEntryContents(OutputStream out) throws IOException { |
|
|
@@ -426,9 +425,9 @@ public class TarInputStream extends FilterInputStream { |
|
|
|
|
|
|
|
if (numRead == -1) { |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
out.write(buf, 0, numRead); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |