xref: /haiku/src/kits/package/hpkg/v1/PackageDataReaderV1.cpp (revision 47bedf16012092c1a0e3f640ae671939486576bc)
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