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