xref: /haiku/src/kits/package/hpkg/PackageFileHeapAccessorBase.cpp (revision b3fe70844e087a579563b43cf4c1b2525946ca27)
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/BufferDataOutput.h>
18 #include <package/hpkg/ErrorOutput.h>
19 
20 #include <AutoDeleter.h>
21 #include <ZlibDecompressor.h>
22 
23 
24 namespace BPackageKit {
25 
26 namespace BHPKG {
27 
28 namespace BPrivate {
29 
30 
31 // #pragma mark - OffsetArray
32 
33 
34 PackageFileHeapAccessorBase::OffsetArray::OffsetArray()
35 	:
36 	fOffsets(NULL)
37 {
38 }
39 
40 
41 PackageFileHeapAccessorBase::OffsetArray::~OffsetArray()
42 {
43 	delete[] fOffsets;
44 }
45 
46 
47 bool
48 PackageFileHeapAccessorBase::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 
101 bool
102 PackageFileHeapAccessorBase::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 
124 PackageFileHeapAccessorBase::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 
136 PackageFileHeapAccessorBase::~PackageFileHeapAccessorBase()
137 {
138 }
139 
140 
141 status_t
142 PackageFileHeapAccessorBase::ReadDataToOutput(off_t offset, size_t size,
143 	BDataIO* 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->Write((char*)uncompressedDataBuffer + inChunkOffset,
178 			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 
191 status_t
192 PackageFileHeapAccessorBase::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 
210 status_t
211 PackageFileHeapAccessorBase::DecompressChunkData(void* compressedDataBuffer,
212 	size_t compressedSize, void* uncompressedDataBuffer,
213 	size_t uncompressedSize)
214 {
215 	size_t actualSize;
216 	status_t error = ::BPrivate::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 
235 status_t
236 PackageFileHeapAccessorBase::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