PackageFileHeapAccessorBase.cpp revision d2d1af830b3f144937ea5e4c3f0e5b74c4c3d737
1/*
2 * Copyright 2013, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <package/hpkg/PackageFileHeapAccessorBase.h>
8
9#include <errno.h>
10#include <stdlib.h>
11#include <string.h>
12
13#include <algorithm>
14#include <new>
15
16#include <ByteOrder.h>
17#include <package/hpkg/DataOutput.h>
18#include <package/hpkg/ErrorOutput.h>
19
20#include <AutoDeleter.h>
21#include <package/hpkg/ZlibDecompressor.h>
22
23
24namespace BPackageKit {
25
26namespace BHPKG {
27
28namespace BPrivate {
29
30
31// #pragma mark - OffsetArray
32
33
34PackageFileHeapAccessorBase::OffsetArray::OffsetArray()
35	:
36	fOffsets(NULL)
37{
38}
39
40
41PackageFileHeapAccessorBase::OffsetArray::~OffsetArray()
42{
43	delete[] fOffsets;
44}
45
46
47bool
48PackageFileHeapAccessorBase::OffsetArray::InitChunksOffsets(
49	size_t totalChunkCount, size_t baseIndex, const uint16* chunkSizes,
50	size_t chunkCount)
51{
52	if (totalChunkCount <= 1)
53		return true;
54
55	if (fOffsets == NULL) {
56		fOffsets = new(std::nothrow) uint32[totalChunkCount];
57		if (fOffsets == NULL)
58			return false;
59		fOffsets[0] = 0;
60			// Value that serves as a marker that all offsets are 32 bit. We'll
61			// replace it, when necessary.
62	}
63
64	uint64 offset = (*this)[baseIndex];
65	for (size_t i = 0; i < chunkCount; i++) {
66		offset += (uint64)B_BENDIAN_TO_HOST_INT16(chunkSizes[i]) + 1;
67			// the stored value is chunkSize - 1
68		size_t index = baseIndex + i + 1;
69			// (baseIndex + i) is the index of the chunk whose size is stored in
70			// chunkSizes[i]. We compute the offset of the following element
71			// which we store at index (baseIndex + i + 1).
72
73		if (offset <= ~(uint32)0) {
74			fOffsets[index] = (uint32)offset;
75		} else {
76			if (fOffsets[0] == 0) {
77				// Not scaled to allow for 64 bit offsets yet. Do that.
78				fOffsets[0] = index;
79				uint32* newOffsets = new(std::nothrow) uint32[
80					2 * totalChunkCount - fOffsets[0]];
81				if (newOffsets == NULL)
82					return false;
83
84				memcpy(newOffsets, fOffsets,
85					sizeof(newOffsets[0]) * fOffsets[0]);
86
87				delete[] fOffsets;
88				fOffsets = newOffsets;
89			}
90
91			index += index - fOffsets[0];
92			fOffsets[index] = (uint32)offset;
93			fOffsets[index + 1] = uint32(offset >> 32);
94		}
95	}
96
97	return true;
98}
99
100
101bool
102PackageFileHeapAccessorBase::OffsetArray::Init(size_t totalChunkCount,
103	const OffsetArray& other)
104{
105	if (other.fOffsets == NULL)
106		return true;
107
108	size_t elementCount = other.fOffsets[0] == 0
109		? totalChunkCount
110		: 2 * totalChunkCount - other.fOffsets[0];
111
112	fOffsets = new(std::nothrow) uint32[elementCount];
113	if (fOffsets == NULL)
114		return false;
115
116	memcpy(fOffsets, other.fOffsets, elementCount * sizeof(fOffsets[0]));
117	return true;
118}
119
120
121// #pragma mark - PackageFileHeapAccessorBase
122
123
124PackageFileHeapAccessorBase::PackageFileHeapAccessorBase(
125	BErrorOutput* errorOutput, int fd, off_t heapOffset)
126	:
127	fErrorOutput(errorOutput),
128	fFD(fd),
129	fHeapOffset(heapOffset),
130	fCompressedHeapSize(0),
131	fUncompressedHeapSize(0)
132{
133}
134
135
136PackageFileHeapAccessorBase::~PackageFileHeapAccessorBase()
137{
138}
139
140
141status_t
142PackageFileHeapAccessorBase::ReadDataToOutput(off_t offset, size_t size,
143	BDataOutput* output)
144{
145	if (size == 0)
146		return B_OK;
147
148	if (offset < 0 || (uint64)offset > fUncompressedHeapSize
149		|| size > fUncompressedHeapSize - offset) {
150		return B_BAD_VALUE;
151	}
152
153	// allocate buffers for compressed and uncompressed data
154	uint16* compressedDataBuffer = (uint16*)malloc(kChunkSize);
155	uint16* uncompressedDataBuffer = (uint16*)malloc(kChunkSize);
156	MemoryDeleter compressedDataBufferDeleter(compressedDataBuffer);
157	MemoryDeleter uncompressedDataBufferDeleter(uncompressedDataBuffer);
158	if (compressedDataBuffer == NULL || uncompressedDataBuffer == NULL)
159		return B_NO_MEMORY;
160
161	// read the data
162	size_t chunkIndex = size_t(offset / kChunkSize);
163	size_t inChunkOffset = (uint64)offset - (uint64)chunkIndex * kChunkSize;
164	size_t remainingBytes = size;
165
166	while (remainingBytes > 0) {
167		status_t error = ReadAndDecompressChunk(chunkIndex,
168			compressedDataBuffer, uncompressedDataBuffer);
169		if (error != B_OK)
170			return error;
171
172		size_t toWrite = std::min((size_t)kChunkSize - inChunkOffset,
173			remainingBytes);
174			// The last chunk may be shorter than kChunkSize, but since
175			// size (and thus remainingSize) had been clamped, that doesn't
176			// harm.
177		error = output->WriteData(
178			(char*)uncompressedDataBuffer + inChunkOffset, toWrite);
179		if (error != B_OK)
180			return error;
181
182		remainingBytes -= toWrite;
183		chunkIndex++;
184		inChunkOffset = 0;
185	}
186
187	return B_OK;
188}
189
190
191status_t
192PackageFileHeapAccessorBase::ReadAndDecompressChunkData(uint64 offset,
193	size_t compressedSize, size_t uncompressedSize, void* compressedDataBuffer,
194	void* uncompressedDataBuffer)
195{
196	// if uncompressed, read directly into the uncompressed data buffer
197	if (compressedSize == uncompressedSize)
198		return ReadFileData(offset, uncompressedDataBuffer, compressedSize);
199
200	// otherwise read into the other buffer and decompress
201	status_t error = ReadFileData(offset, compressedDataBuffer, compressedSize);
202	if (error != B_OK)
203		return error;
204
205	return DecompressChunkData(compressedDataBuffer, compressedSize,
206		uncompressedDataBuffer, uncompressedSize);
207}
208
209
210status_t
211PackageFileHeapAccessorBase::DecompressChunkData(void* compressedDataBuffer,
212	size_t compressedSize, void* uncompressedDataBuffer,
213	size_t uncompressedSize)
214{
215	size_t actualSize;
216	status_t error = ZlibDecompressor::DecompressSingleBuffer(
217		compressedDataBuffer, compressedSize, uncompressedDataBuffer,
218		uncompressedSize, actualSize);
219	if (error != B_OK) {
220		fErrorOutput->PrintError("Failed to decompress chunk data: %s\n",
221			strerror(error));
222		return error;
223	}
224
225	if (actualSize != uncompressedSize) {
226		fErrorOutput->PrintError("Failed to decompress chunk data: size "
227			"mismatch\n");
228		return B_ERROR;
229	}
230
231	return B_OK;
232}
233
234
235status_t
236PackageFileHeapAccessorBase::ReadFileData(uint64 offset, void* buffer,
237	size_t size)
238{
239	ssize_t bytesRead = pread(fFD, buffer, size, fHeapOffset + (off_t)offset);
240	if (bytesRead < 0) {
241		fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 ", %p, %zu) failed "
242			"to read data: %s\n", offset, buffer, size, strerror(errno));
243		return errno;
244	}
245	if ((size_t)bytesRead != size) {
246		fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 ", %p, %zu) could "
247			"read only %zd bytes\n", offset, buffer, size, bytesRead);
248		return B_ERROR;
249	}
250
251	return B_OK;
252}
253
254
255}	// namespace BPrivate
256
257}	// namespace BHPKG
258
259}	// namespace BPackageKit
260