2 * Copyright 2012 Kamran Zafar
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 package org.kamranzafar.jtar;
20 import java.io.FilterInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
25 * @author Kamran Zafar
28 public class TarInputStream extends FilterInputStream {
30 private static final int SKIP_BUFFER_SIZE = 2048;
31 private TarEntry currentEntry;
32 private long currentFileSize;
33 private long bytesRead;
34 private boolean defaultSkip = false;
36 public TarInputStream(InputStream in) {
43 public boolean markSupported() {
52 public synchronized void mark(int readlimit) {
60 public synchronized void reset() throws IOException {
61 throw new IOException("mark/reset not supported");
67 * @see java.io.FilterInputStream#read()
70 public int read() throws IOException {
71 byte[] buf = new byte[1];
73 int res = this.read(buf, 0, 1);
83 * Checks if the bytes being read exceed the entry size and adjusts the byte
84 * array length. Updates the byte counters
87 * @see java.io.FilterInputStream#read(byte[], int, int)
90 public int read(byte[] b, int off, int len) throws IOException {
91 if (currentEntry != null) {
92 if (currentFileSize == currentEntry.getSize()) {
94 } else if ((currentEntry.getSize() - currentFileSize) < len) {
95 len = (int) (currentEntry.getSize() - currentFileSize);
99 int br = super.read(b, off, len);
102 if (currentEntry != null) {
103 currentFileSize += br;
113 * Returns the next entry in the tar file
116 * @throws IOException
118 public TarEntry getNextEntry() throws IOException {
121 byte[] header = new byte[TarConstants.HEADER_BLOCK];
122 byte[] theader = new byte[TarConstants.HEADER_BLOCK];
126 while (tr < TarConstants.HEADER_BLOCK) {
127 int res = read(theader, 0, TarConstants.HEADER_BLOCK - tr);
133 System.arraycopy(theader, 0, header, tr, res);
137 // Check if record is null
139 for (byte b : header) {
147 currentEntry = new TarEntry(header);
154 * Returns the current offset (in bytes) from the beginning of the stream.
155 * This can be used to find out at which point in a tar file an entry's content begins, for instance.
157 public long getCurrentOffset() {
162 * Closes the current tar entry
164 * @throws IOException
166 protected void closeCurrentEntry() throws IOException {
167 if (currentEntry != null) {
168 if (currentEntry.getSize() > currentFileSize) {
169 // Not fully read, skip rest of the bytes
171 while (bs < currentEntry.getSize() - currentFileSize) {
172 long res = skip(currentEntry.getSize() - currentFileSize - bs);
174 if (res == 0 && currentEntry.getSize() - currentFileSize > 0) {
175 // I suspect file corruption
176 throw new IOException("Possible tar file corruption");
184 currentFileSize = 0L;
190 * Skips the pad at the end of each tar entry file content
192 * @throws IOException
194 protected void skipPad() throws IOException {
196 int extra = (int) (bytesRead % TarConstants.DATA_BLOCK);
200 while (bs < TarConstants.DATA_BLOCK - extra) {
201 long res = skip(TarConstants.DATA_BLOCK - extra - bs);
209 * Skips 'n' bytes on the InputStream<br>
210 * Overrides default implementation of skip
214 public long skip(long n) throws IOException {
216 // use skip method of parent stream
217 // may not work if skip not implemented by parent
218 long bs = super.skip(n);
229 byte[] sBuff = new byte[SKIP_BUFFER_SIZE];
232 int res = read(sBuff, 0, (int) (left < SKIP_BUFFER_SIZE ? left : SKIP_BUFFER_SIZE));
242 public boolean isDefaultSkip() {
246 public void setDefaultSkip(boolean defaultSkip) {
247 this.defaultSkip = defaultSkip;