1
2
3
4
5
6
7
8
9
10 package edu.internet2.middleware.grouperInstallerExt.org.tukaani.xz;
11
12 import java.io.InputStream;
13 import java.io.DataInputStream;
14 import java.io.ByteArrayInputStream;
15 import java.io.IOException;
16 import java.util.Arrays;
17 import edu.internet2.middleware.grouperInstallerExt.org.tukaani.xz.common.DecoderUtil;
18 import edu.internet2.middleware.grouperInstallerExt.org.tukaani.xz.check.Check;
19
20 class BlockInputStream extends InputStream {
21 private final DataInputStream inData;
22 private final CountingInputStream inCounted;
23 private InputStream filterChain;
24 private final Check check;
25
26 private long uncompressedSizeInHeader = -1;
27 private long compressedSizeInHeader = -1;
28 private long compressedSizeLimit;
29 private final int headerSize;
30 private long uncompressedSize = 0;
31 private boolean endReached = false;
32
33 private final byte[] tempBuf = new byte[1];
34
35 public BlockInputStream(InputStream in, Check check, int memoryLimit,
36 long unpaddedSizeInIndex,
37 long uncompressedSizeInIndex)
38 throws IOException, IndexIndicatorException {
39 this.check = check;
40 inData = new DataInputStream(in);
41
42 byte[] buf = new byte[DecoderUtil.BLOCK_HEADER_SIZE_MAX];
43
44
45 inData.readFully(buf, 0, 1);
46
47
48 if (buf[0] == 0x00)
49 throw new IndexIndicatorException();
50
51
52 headerSize = 4 * ((buf[0] & 0xFF) + 1);
53 inData.readFully(buf, 1, headerSize - 1);
54
55
56 if (!DecoderUtil.isCRC32Valid(buf, 0, headerSize - 4, headerSize - 4))
57 throw new CorruptedInputException("XZ Block Header is corrupt");
58
59
60 if ((buf[1] & 0x3C) != 0)
61 throw new UnsupportedOptionsException(
62 "Unsupported options in XZ Block Header");
63
64
65 int filterCount = (buf[1] & 0x03) + 1;
66 long[] filterIDs = new long[filterCount];
67 byte[][] filterProps = new byte[filterCount][];
68
69
70
71 ByteArrayInputStream bufStream = new ByteArrayInputStream(
72 buf, 2, headerSize - 6);
73
74 try {
75
76
77 compressedSizeLimit = (DecoderUtil.VLI_MAX & ~3)
78 - headerSize - check.getSize();
79
80
81
82 if ((buf[1] & 0x40) != 0x00) {
83 compressedSizeInHeader = DecoderUtil.decodeVLI(bufStream);
84
85 if (compressedSizeInHeader == 0
86 || compressedSizeInHeader > compressedSizeLimit)
87 throw new CorruptedInputException();
88
89 compressedSizeLimit = compressedSizeInHeader;
90 }
91
92
93
94 if ((buf[1] & 0x80) != 0x00)
95 uncompressedSizeInHeader = DecoderUtil.decodeVLI(bufStream);
96
97
98 for (int i = 0; i < filterCount; ++i) {
99 filterIDs[i] = DecoderUtil.decodeVLI(bufStream);
100
101 long filterPropsSize = DecoderUtil.decodeVLI(bufStream);
102 if (filterPropsSize > bufStream.available())
103 throw new CorruptedInputException();
104
105 filterProps[i] = new byte[(int)filterPropsSize];
106 bufStream.read(filterProps[i]);
107 }
108
109 } catch (IOException e) {
110 throw new CorruptedInputException("XZ Block Header is corrupt");
111 }
112
113
114 for (int i = bufStream.available(); i > 0; --i)
115 if (bufStream.read() != 0x00)
116 throw new UnsupportedOptionsException(
117 "Unsupported options in XZ Block Header");
118
119
120
121 if (unpaddedSizeInIndex != -1) {
122
123
124
125 int headerAndCheckSize = headerSize + check.getSize();
126 if (headerAndCheckSize >= unpaddedSizeInIndex)
127 throw new CorruptedInputException(
128 "XZ Index does not match a Block Header");
129
130
131
132
133 long compressedSizeFromIndex
134 = unpaddedSizeInIndex - headerAndCheckSize;
135 if (compressedSizeFromIndex > compressedSizeLimit
136 || (compressedSizeInHeader != -1
137 && compressedSizeInHeader != compressedSizeFromIndex))
138 throw new CorruptedInputException(
139 "XZ Index does not match a Block Header");
140
141
142
143
144 if (uncompressedSizeInHeader != -1
145 && uncompressedSizeInHeader != uncompressedSizeInIndex)
146 throw new CorruptedInputException(
147 "XZ Index does not match a Block Header");
148
149
150
151 compressedSizeLimit = compressedSizeFromIndex;
152 compressedSizeInHeader = compressedSizeFromIndex;
153 uncompressedSizeInHeader = uncompressedSizeInIndex;
154 }
155
156
157
158
159 FilterDecoderperInstallerExt/org/tukaani/xz/FilterDecoder.html#FilterDecoder">FilterDecoder[] filters = new FilterDecoder[filterIDs.length];
160
161 for (int i = 0; i < filters.length; ++i) {
162 if (filterIDs[i] == LZMA2Coder.FILTER_ID)
163 filters[i] = new LZMA2Decoder(filterProps[i]);
164
165 else if (filterIDs[i] == DeltaCoder.FILTER_ID)
166 filters[i] = new DeltaDecoder(filterProps[i]);
167
168 else if (BCJDecoder.isBCJFilterID(filterIDs[i]))
169 filters[i] = new BCJDecoder(filterIDs[i], filterProps[i]);
170
171 else
172 throw new UnsupportedOptionsException(
173 "Unknown Filter ID " + filterIDs[i]);
174 }
175
176 RawCoder.validate(filters);
177
178
179 if (memoryLimit >= 0) {
180 int memoryNeeded = 0;
181 for (int i = 0; i < filters.length; ++i)
182 memoryNeeded += filters[i].getMemoryUsage();
183
184 if (memoryNeeded > memoryLimit)
185 throw new MemoryLimitException(memoryNeeded, memoryLimit);
186 }
187
188
189
190 inCounted = new CountingInputStream(in);
191
192
193 filterChain = inCounted;
194 for (int i = filters.length - 1; i >= 0; --i)
195 filterChain = filters[i].getInputStream(filterChain);
196 }
197
198 public int read() throws IOException {
199 return read(tempBuf, 0, 1) == -1 ? -1 : (tempBuf[0] & 0xFF);
200 }
201
202 public int read(byte[] buf, int off, int len) throws IOException {
203 if (endReached)
204 return -1;
205
206 int ret = filterChain.read(buf, off, len);
207
208 if (ret > 0) {
209 check.update(buf, off, ret);
210 uncompressedSize += ret;
211
212
213 long compressedSize = inCounted.getSize();
214 if (compressedSize < 0
215 || compressedSize > compressedSizeLimit
216 || uncompressedSize < 0
217 || (uncompressedSizeInHeader != -1
218 && uncompressedSize > uncompressedSizeInHeader))
219 throw new CorruptedInputException();
220
221
222
223
224
225
226
227
228 if (ret < len || uncompressedSize == uncompressedSizeInHeader) {
229 if (filterChain.read() != -1)
230 throw new CorruptedInputException();
231
232 validate();
233 endReached = true;
234 }
235 } else if (ret == -1) {
236 validate();
237 endReached = true;
238 }
239
240 return ret;
241 }
242
243 private void validate() throws IOException {
244 long compressedSize = inCounted.getSize();
245
246
247
248 if ((compressedSizeInHeader != -1
249 && compressedSizeInHeader != compressedSize)
250 || (uncompressedSizeInHeader != -1
251 && uncompressedSizeInHeader != uncompressedSize))
252 throw new CorruptedInputException();
253
254
255 while ((compressedSize++ & 3) != 0)
256 if (inData.readUnsignedByte() != 0x00)
257 throw new CorruptedInputException();
258
259
260 byte[] storedCheck = new byte[check.getSize()];
261 inData.readFully(storedCheck);
262 if (!Arrays.equals(check.finish(), storedCheck))
263 throw new CorruptedInputException("Integrity check ("
264 + check.getName() + ") does not match");
265 }
266
267 public int available() throws IOException {
268 return filterChain.available();
269 }
270
271 public long getUnpaddedSize() {
272 return headerSize + inCounted.getSize() + check.getSize();
273 }
274
275 public long getUncompressedSize() {
276 return uncompressedSize;
277 }
278 }