1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.commons.compress.archivers.dump;
20
21 import java.io.FilterInputStream;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.util.Arrays;
25 import java.util.zip.DataFormatException;
26 import java.util.zip.Inflater;
27
28 import org.apache.commons.compress.utils.ExactMath;
29 import org.apache.commons.compress.utils.IOUtils;
30
31
32
33
34
35
36 final class TapeInputStream extends FilterInputStream {
37 private static final int RECORD_SIZE = DumpArchiveConstants.TP_SIZE;
38 private byte[] blockBuffer = new byte[DumpArchiveConstants.TP_SIZE];
39 private int currBlkIdx = -1;
40 private int blockSize = DumpArchiveConstants.TP_SIZE;
41 private int readOffset = DumpArchiveConstants.TP_SIZE;
42 private boolean isCompressed;
43 private long bytesRead;
44
45
46
47
48
49
50 TapeInputStream(final InputStream in) {
51 super(in);
52 }
53
54
55
56
57 @Override
58 public int available() throws IOException {
59 if (readOffset < blockSize) {
60 return blockSize - readOffset;
61 }
62
63 return in.available();
64 }
65
66
67
68
69
70
71 @Override
72 public void close() throws IOException {
73 if (in != null && in != System.in) {
74 in.close();
75 }
76 }
77
78
79
80
81
82
83 public long getBytesRead() {
84 return bytesRead;
85 }
86
87
88
89
90
91
92
93 public byte[] peek() throws IOException {
94
95
96
97 if (readOffset == blockSize) {
98 try {
99 readBlock(true);
100 } catch (final ShortFileException sfe) {
101 return null;
102 }
103 }
104
105
106 final byte[] b = new byte[RECORD_SIZE];
107 System.arraycopy(blockBuffer, readOffset, b, 0, b.length);
108
109 return b;
110 }
111
112
113
114
115 @Override
116 public int read() throws IOException {
117 throw new IllegalArgumentException("All reads must be multiple of record size (" + RECORD_SIZE + " bytes.");
118 }
119
120
121
122
123
124
125
126
127
128
129 @Override
130 public int read(final byte[] b, int off, final int len) throws IOException {
131 if (len == 0) {
132 return 0;
133 }
134 if (len % RECORD_SIZE != 0) {
135 throw new IllegalArgumentException("All reads must be multiple of record size (" + RECORD_SIZE + " bytes.");
136 }
137
138 int bytes = 0;
139
140 while (bytes < len) {
141
142
143
144 if (readOffset == blockSize) {
145 try {
146 readBlock(true);
147 } catch (final ShortFileException sfe) {
148 return -1;
149 }
150 }
151
152 int n = 0;
153
154 if (readOffset + len - bytes <= blockSize) {
155
156 n = len - bytes;
157 } else {
158
159 n = blockSize - readOffset;
160 }
161
162
163 System.arraycopy(blockBuffer, readOffset, b, off, n);
164 readOffset += n;
165 bytes += n;
166 off += n;
167 }
168
169 return bytes;
170 }
171
172
173
174
175
176
177 private void readBlock(final boolean decompress) throws IOException {
178 if (in == null) {
179 throw new IOException("Input buffer is closed");
180 }
181
182 if (!isCompressed || currBlkIdx == -1) {
183
184 readFully(blockBuffer, 0, blockSize);
185 bytesRead += blockSize;
186 } else {
187 readFully(blockBuffer, 0, 4);
188 bytesRead += 4;
189
190 final int h = DumpArchiveUtil.convert32(blockBuffer, 0);
191 final boolean compressed = (h & 0x01) == 0x01;
192
193 if (!compressed) {
194
195 readFully(blockBuffer, 0, blockSize);
196 bytesRead += blockSize;
197 } else {
198
199 final int flags = h >> 1 & 0x07;
200 int length = h >> 4 & 0x0FFFFFFF;
201 final byte[] compBuffer = readRange(length);
202 bytesRead += length;
203
204 if (!decompress) {
205
206 Arrays.fill(blockBuffer, (byte) 0);
207 } else {
208 switch (DumpArchiveConstants.COMPRESSION_TYPE.find(flags & 0x03)) {
209 case ZLIB:
210
211 final Inflater inflator = new Inflater();
212 try {
213 inflator.setInput(compBuffer, 0, compBuffer.length);
214 length = inflator.inflate(blockBuffer);
215
216 if (length != blockSize) {
217 throw new ShortFileException();
218 }
219 } catch (final DataFormatException e) {
220 throw new DumpArchiveException("Bad data", e);
221 } finally {
222 inflator.end();
223 }
224
225 break;
226
227 case BZLIB:
228 throw new UnsupportedCompressionAlgorithmException("BZLIB2");
229
230 case LZO:
231 throw new UnsupportedCompressionAlgorithmException("LZO");
232
233 default:
234 throw new UnsupportedCompressionAlgorithmException();
235 }
236 }
237 }
238 }
239
240 currBlkIdx++;
241 readOffset = 0;
242 }
243
244
245
246
247 private void readFully(final byte[] b, final int off, final int len) throws IOException {
248 final int count = IOUtils.readFully(in, b, off, len);
249 if (count < len) {
250 throw new ShortFileException();
251 }
252 }
253
254 private byte[] readRange(final int len) throws IOException {
255 final byte[] ret = IOUtils.readRange(in, len);
256 if (ret.length < len) {
257 throw new ShortFileException();
258 }
259 return ret;
260 }
261
262
263
264
265
266
267
268 public byte[] readRecord() throws IOException {
269 final byte[] result = new byte[RECORD_SIZE];
270
271
272
273 if (-1 == read(result, 0, result.length)) {
274 throw new ShortFileException();
275 }
276
277 return result;
278 }
279
280
281
282
283
284
285
286
287
288
289
290 public void resetBlockSize(final int recsPerBlock, final boolean isCompressed) throws IOException {
291 this.isCompressed = isCompressed;
292
293 if (recsPerBlock < 1) {
294 throw new IOException("Block with " + recsPerBlock + " records found, must be at least 1");
295 }
296 blockSize = RECORD_SIZE * recsPerBlock;
297 if (blockSize < 1) {
298 throw new IOException("Block size cannot be less than or equal to 0: " + blockSize);
299 }
300
301
302 final byte[] oldBuffer = blockBuffer;
303
304
305 blockBuffer = new byte[blockSize];
306 System.arraycopy(oldBuffer, 0, blockBuffer, 0, RECORD_SIZE);
307 readFully(blockBuffer, RECORD_SIZE, blockSize - RECORD_SIZE);
308
309 this.currBlkIdx = 0;
310 this.readOffset = RECORD_SIZE;
311 }
312
313
314
315
316
317
318
319
320
321
322 @Override
323 public long skip(final long len) throws IOException {
324 if (len % RECORD_SIZE != 0) {
325 throw new IllegalArgumentException("All reads must be multiple of record size (" + RECORD_SIZE + " bytes.");
326 }
327
328 long bytes = 0;
329
330 while (bytes < len) {
331
332
333
334
335 if (readOffset == blockSize) {
336 try {
337 readBlock(len - bytes < blockSize);
338 } catch (final ShortFileException sfe) {
339 return -1;
340 }
341 }
342
343 long n = 0;
344
345 if (readOffset + (len - bytes) <= blockSize) {
346
347 n = len - bytes;
348 } else {
349
350 n = (long) blockSize - readOffset;
351 }
352
353
354 readOffset = ExactMath.add(readOffset, n);
355 bytes += n;
356 }
357
358 return bytes;
359 }
360 }