1 /*
2 * Copyright 2009-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7 #include <package/hpkg/v1/PackageDataReader.h>
8
9 #include <string.h>
10
11 #include <algorithm>
12 #include <new>
13
14 #include <DataIO.h>
15 #include <package/hpkg/BufferPool.h>
16 #include <package/hpkg/PoolBuffer.h>
17 #include <package/hpkg/v1/HPKGDefsPrivate.h>
18 #include <package/hpkg/v1/PackageData.h>
19 #include <ZlibCompressionAlgorithm.h>
20
21
22 namespace BPackageKit {
23
24 namespace BHPKG {
25
26 namespace V1 {
27
28
29 using BHPKG::BPrivate::PoolBufferPutter;
30
31
32 // minimum/maximum zlib chunk size we consider sane
33 static const size_t kMinSaneZlibChunkSize = 1024;
34 static const size_t kMaxSaneZlibChunkSize = 10 * 1024 * 1024;
35
36 // maximum number of entries in the zlib offset table buffer
37 static const uint32 kMaxZlibOffsetTableBufferSize = 512;
38
39 static const size_t kUncompressedReaderBufferSize
40 = B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB;
41
42
43 // #pragma mark - PackageDataReader
44
45
46 class PackageDataReader : public BAbstractBufferedDataReader {
47 public:
PackageDataReader(BDataReader * dataReader)48 PackageDataReader(BDataReader* dataReader)
49 :
50 fDataReader(dataReader)
51 {
52 }
53
~PackageDataReader()54 virtual ~PackageDataReader()
55 {
56 }
57
58 virtual status_t Init(const BPackageData& data) = 0;
59
60 protected:
61 BDataReader* fDataReader;
62 };
63
64
65 // #pragma mark - UncompressedPackageDataReader
66
67
68 class UncompressedPackageDataReader : public PackageDataReader {
69 public:
UncompressedPackageDataReader(BDataReader * dataReader,BBufferPool * bufferPool)70 UncompressedPackageDataReader(BDataReader* dataReader,
71 BBufferPool* bufferPool)
72 :
73 PackageDataReader(dataReader),
74 fBufferPool(bufferPool)
75 {
76 }
77
Init(const BPackageData & data)78 status_t Init(const BPackageData& data)
79 {
80 fOffset = data.Offset();
81 fSize = data.UncompressedSize();
82 return B_OK;
83 }
84
ReadData(off_t offset,void * buffer,size_t size)85 virtual status_t ReadData(off_t offset, void* buffer, size_t size)
86 {
87 if (size == 0)
88 return B_OK;
89
90 if (offset < 0)
91 return B_BAD_VALUE;
92
93 if ((uint64)offset > fSize || size > fSize - offset)
94 return B_BAD_VALUE;
95
96 return fDataReader->ReadData(fOffset + offset, buffer, size);
97 }
98
ReadDataToOutput(off_t offset,size_t size,BDataIO * output)99 virtual status_t ReadDataToOutput(off_t offset, size_t size,
100 BDataIO* output)
101 {
102 if (size == 0)
103 return B_OK;
104
105 if (offset < 0)
106 return B_BAD_VALUE;
107
108 if ((uint64)offset > fSize || size > fSize - offset)
109 return B_BAD_VALUE;
110
111 // get a temporary buffer
112 PoolBuffer* buffer = fBufferPool->GetBuffer(
113 kUncompressedReaderBufferSize);
114 if (buffer == NULL)
115 return B_NO_MEMORY;
116 PoolBufferPutter bufferPutter(fBufferPool, &buffer);
117
118 while (size > 0) {
119 // read into the buffer
120 size_t toRead = std::min(size, buffer->Size());
121 status_t error = fDataReader->ReadData(fOffset + offset,
122 buffer->Buffer(), toRead);
123 if (error != B_OK)
124 return error;
125
126 // write to the output
127 error = output->WriteExactly(buffer->Buffer(), toRead);
128 if (error != B_OK)
129 return error;
130
131 offset += toRead;
132 size -= toRead;
133 }
134
135 return B_OK;
136 }
137
138 private:
139 BBufferPool* fBufferPool;
140 uint64 fOffset;
141 uint64 fSize;
142 };
143
144
145 // #pragma mark - ZlibPackageDataReader
146
147
148 class ZlibPackageDataReader : public PackageDataReader {
149 public:
ZlibPackageDataReader(BDataReader * dataReader,BBufferPool * bufferPool)150 ZlibPackageDataReader(BDataReader* dataReader, BBufferPool* bufferPool)
151 :
152 PackageDataReader(dataReader),
153 fBufferPool(bufferPool),
154 fUncompressBuffer(NULL),
155 fOffsetTable(NULL)
156 {
157 }
158
~ZlibPackageDataReader()159 ~ZlibPackageDataReader()
160 {
161 delete[] fOffsetTable;
162
163 fBufferPool->PutBuffer(&fUncompressBuffer);
164 }
165
Init(const BPackageData & data)166 status_t Init(const BPackageData& data)
167 {
168 fOffset = data.Offset();
169 fCompressedSize = data.CompressedSize();
170 fUncompressedSize = data.UncompressedSize();
171 fChunkSize = data.ChunkSize();
172
173 // validate chunk size
174 if (fChunkSize == 0)
175 fChunkSize = B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB;
176 if (fChunkSize < kMinSaneZlibChunkSize
177 || fChunkSize > kMaxSaneZlibChunkSize) {
178 return B_BAD_DATA;
179 }
180
181 fChunkCount = (fUncompressedSize + (fChunkSize - 1)) / fChunkSize;
182 fOffsetTableSize = (fChunkCount - 1) * sizeof(uint64);
183 if (fOffsetTableSize >= fCompressedSize)
184 return B_BAD_DATA;
185
186 // allocate a buffer for the offset table
187 if (fChunkCount > 1) {
188 fOffsetTableBufferEntryCount = std::min(fChunkCount - 1,
189 (uint64)kMaxZlibOffsetTableBufferSize);
190 fOffsetTable = new(std::nothrow) uint64[
191 fOffsetTableBufferEntryCount];
192 if (fOffsetTable == NULL)
193 return B_NO_MEMORY;
194
195 fOffsetTableIndex = -1;
196 // mark the table content invalid
197 } else
198 fChunkSize = fUncompressedSize;
199
200 // mark uncompressed content invalid
201 fUncompressedChunk = -1;
202
203 return B_OK;
204 }
205
ReadDataToOutput(off_t offset,size_t size,BDataIO * output)206 virtual status_t ReadDataToOutput(off_t offset, size_t size,
207 BDataIO* output)
208 {
209 // check offset and size
210 if (size == 0)
211 return B_OK;
212
213 if (offset < 0)
214 return B_BAD_VALUE;
215
216 if ((uint64)offset > fUncompressedSize
217 || size > fUncompressedSize - offset) {
218 return B_BAD_VALUE;
219 }
220
221 // get our uncompressed chunk buffer back, if possible
222 bool newBuffer;
223 if (fBufferPool->GetBuffer(fChunkSize, &fUncompressBuffer, &newBuffer)
224 == NULL) {
225 return B_NO_MEMORY;
226 }
227 PoolBufferPutter uncompressBufferPutter(fBufferPool,
228 &fUncompressBuffer);
229
230 if (newBuffer)
231 fUncompressedChunk = -1;
232
233 // uncompress
234 int64 chunkIndex = offset / fChunkSize;
235 off_t chunkOffset = chunkIndex * fChunkSize;
236 size_t inChunkOffset = offset - chunkOffset;
237
238 while (size > 0) {
239 // read and uncompress the chunk
240 status_t error = _ReadChunk(chunkIndex);
241 if (error != B_OK)
242 return error;
243
244 // write data to output
245 size_t toCopy = std::min(size, (size_t)fChunkSize - inChunkOffset);
246 error = output->WriteExactly(
247 (uint8*)fUncompressBuffer->Buffer() + inChunkOffset, toCopy);
248 if (error != B_OK)
249 return error;
250
251 size -= toCopy;
252
253 chunkIndex++;
254 chunkOffset += fChunkSize;
255 inChunkOffset = 0;
256 }
257
258 return B_OK;
259 }
260
261 private:
_ReadChunk(int64 chunkIndex)262 status_t _ReadChunk(int64 chunkIndex)
263 {
264 if (chunkIndex == fUncompressedChunk)
265 return B_OK;
266
267 // get the chunk offset and size
268 uint64 offset = 0;
269 uint32 compressedSize = 0;
270 status_t error = _GetCompressedChunkOffsetAndSize(chunkIndex, offset,
271 compressedSize);
272 if (error != B_OK)
273 return error;
274
275 uint32 uncompressedSize = (uint64)chunkIndex + 1 < fChunkCount
276 ? fChunkSize : fUncompressedSize - chunkIndex * fChunkSize;
277
278 // read the chunk
279 if (compressedSize == uncompressedSize) {
280 // the chunk is not compressed -- read it directly into the
281 // uncompressed buffer
282 error = fDataReader->ReadData(offset, fUncompressBuffer->Buffer(),
283 compressedSize);
284 } else {
285 // read to a read buffer and uncompress
286 PoolBuffer* readBuffer = fBufferPool->GetBuffer(fChunkSize);
287 if (readBuffer == NULL)
288 return B_NO_MEMORY;
289 PoolBufferPutter readBufferPutter(fBufferPool, readBuffer);
290
291 error = fDataReader->ReadData(offset, readBuffer->Buffer(),
292 compressedSize);
293 if (error != B_OK)
294 return error;
295
296 size_t actuallyUncompressedSize;
297 BZlibCompressionAlgorithm().DecompressBuffer(
298 readBuffer->Buffer(), compressedSize,
299 fUncompressBuffer->Buffer(), uncompressedSize,
300 actuallyUncompressedSize);
301 if (error == B_OK && actuallyUncompressedSize != uncompressedSize)
302 error = B_BAD_DATA;
303 }
304
305 if (error != B_OK) {
306 // error reading/decompressing data -- mark the cached data invalid
307 fUncompressedChunk = -1;
308 return error;
309 }
310
311 fUncompressedChunk = chunkIndex;
312 return B_OK;
313 }
314
_GetCompressedChunkOffsetAndSize(int64 chunkIndex,uint64 & _offset,uint32 & _size)315 status_t _GetCompressedChunkOffsetAndSize(int64 chunkIndex, uint64& _offset,
316 uint32& _size)
317 {
318 // get the offset
319 uint64 offset;
320 if (chunkIndex == 0) {
321 // first chunk is at 0
322 offset = 0;
323 } else {
324 status_t error = _GetCompressedChunkRelativeOffset(chunkIndex,
325 offset);
326 if (error != B_OK)
327 return error;
328 }
329
330 // get the end offset
331 uint64 endOffset;
332 if ((uint64)chunkIndex + 1 == fChunkCount) {
333 // last chunk end with the end of the data
334 endOffset = fCompressedSize - fOffsetTableSize;
335 } else {
336 status_t error = _GetCompressedChunkRelativeOffset(chunkIndex + 1,
337 endOffset);
338 if (error != B_OK)
339 return error;
340 }
341
342 // sanity check
343 if (endOffset < offset)
344 return B_BAD_DATA;
345
346 _offset = fOffset + fOffsetTableSize + offset;
347 _size = endOffset - offset;
348 return B_OK;
349 }
350
_GetCompressedChunkRelativeOffset(int64 chunkIndex,uint64 & _offset)351 status_t _GetCompressedChunkRelativeOffset(int64 chunkIndex,
352 uint64& _offset)
353 {
354 if (fOffsetTableIndex < 0 || fOffsetTableIndex > chunkIndex
355 || fOffsetTableIndex + fOffsetTableBufferEntryCount <= chunkIndex) {
356 // read the table at the given index, or, if we can, the whole table
357 int64 readAtIndex = fChunkCount - 1 > fOffsetTableBufferEntryCount
358 ? chunkIndex : 1;
359 uint32 entriesToRead = std::min(
360 (uint64)fOffsetTableBufferEntryCount,
361 fChunkCount - readAtIndex);
362
363 status_t error = fDataReader->ReadData(
364 fOffset + (readAtIndex - 1) * sizeof(uint64),
365 fOffsetTable, entriesToRead * sizeof(uint64));
366 if (error != B_OK) {
367 fOffsetTableIndex = -1;
368 return error;
369 }
370
371 fOffsetTableIndex = readAtIndex;
372 }
373
374 // get and check the offset
375 _offset = fOffsetTable[chunkIndex - fOffsetTableIndex];
376 if (_offset > fCompressedSize - fOffsetTableSize)
377 return B_BAD_DATA;
378
379 return B_OK;
380 }
381
382 private:
383 BBufferPool* fBufferPool;
384 PoolBuffer* fUncompressBuffer;
385 int64 fUncompressedChunk;
386
387 uint64 fOffset;
388 uint64 fUncompressedSize;
389 uint64 fCompressedSize;
390 uint64 fOffsetTableSize;
391 uint64 fChunkCount;
392 uint32 fChunkSize;
393 uint32 fOffsetTableBufferEntryCount;
394 uint64* fOffsetTable;
395 int32 fOffsetTableIndex;
396 };
397
398
399 // #pragma mark - PackageDataHeapReader
400
401
402 class PackageDataInlineReader : public BBufferDataReader {
403 public:
PackageDataInlineReader(const BPackageData & data)404 PackageDataInlineReader(const BPackageData& data)
405 :
406 BBufferDataReader(data.InlineData(), data.UncompressedSize()),
407 fData(data)
408 {
409 }
410
411 private:
412 BPackageData fData;
413 };
414
415
416 // #pragma mark - BPackageDataReaderFactory
417
418
BPackageDataReaderFactory(BBufferPool * bufferPool)419 BPackageDataReaderFactory::BPackageDataReaderFactory(BBufferPool* bufferPool)
420 :
421 fBufferPool(bufferPool)
422 {
423 }
424
425
426 status_t
CreatePackageDataReader(BDataReader * dataReader,const BPackageData & data,BAbstractBufferedDataReader * & _reader)427 BPackageDataReaderFactory::CreatePackageDataReader(BDataReader* dataReader,
428 const BPackageData& data, BAbstractBufferedDataReader*& _reader)
429 {
430 if (data.IsEncodedInline()) {
431 BAbstractBufferedDataReader* reader
432 = new(std::nothrow) PackageDataInlineReader(data);
433 if (reader == NULL)
434 return B_NO_MEMORY;
435
436 _reader = reader;
437 return B_OK;
438 }
439
440 PackageDataReader* reader;
441
442 switch (data.Compression()) {
443 case B_HPKG_COMPRESSION_NONE:
444 reader = new(std::nothrow) UncompressedPackageDataReader(
445 dataReader, fBufferPool);
446 break;
447 case B_HPKG_COMPRESSION_ZLIB:
448 reader = new(std::nothrow) ZlibPackageDataReader(dataReader,
449 fBufferPool);
450 break;
451 default:
452 return B_BAD_VALUE;
453 }
454
455 if (reader == NULL)
456 return B_NO_MEMORY;
457
458 status_t error = reader->Init(data);
459 if (error != B_OK) {
460 delete reader;
461 return error;
462 }
463
464 _reader = reader;
465 return B_OK;
466 }
467
468
469 } // namespace V1
470
471 } // namespace BHPKG
472
473 } // namespace BPackageKit
474