1 /* 2 * Copyright 2013-2014, 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 <DataIO.h> 18 #include <package/hpkg/ErrorOutput.h> 19 20 #include <AutoDeleter.h> 21 #include <CompressionAlgorithm.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 DecompressionAlgorithmOwner* decompressionAlgorithm) 127 : 128 fErrorOutput(errorOutput), 129 fFD(fd), 130 fHeapOffset(heapOffset), 131 fCompressedHeapSize(0), 132 fUncompressedHeapSize(0), 133 fDecompressionAlgorithm(decompressionAlgorithm) 134 { 135 if (fDecompressionAlgorithm != NULL) 136 fDecompressionAlgorithm->AcquireReference(); 137 } 138 139 140 PackageFileHeapAccessorBase::~PackageFileHeapAccessorBase() 141 { 142 if (fDecompressionAlgorithm != NULL) 143 fDecompressionAlgorithm->ReleaseReference(); 144 } 145 146 147 uint64 148 PackageFileHeapAccessorBase::HeapOverhead(uint64 uncompressedSize) const 149 { 150 // Determine number of chunks and the size of the chunk size table. Note 151 // that the size of the last chunk is not saved, since its size is implied. 152 size_t chunkCount = (uncompressedSize + kChunkSize - 1) / kChunkSize; 153 return chunkCount > 1 ? (chunkCount - 1) * 2 : 0; 154 } 155 156 157 status_t 158 PackageFileHeapAccessorBase::ReadDataToOutput(off_t offset, size_t size, 159 BDataIO* output) 160 { 161 if (size == 0) 162 return B_OK; 163 164 if (offset < 0 || (uint64)offset > fUncompressedHeapSize 165 || size > fUncompressedHeapSize - offset) { 166 return B_BAD_VALUE; 167 } 168 169 // allocate buffers for compressed and uncompressed data 170 uint16* compressedDataBuffer = (uint16*)malloc(kChunkSize); 171 uint16* uncompressedDataBuffer = (uint16*)malloc(kChunkSize); 172 MemoryDeleter compressedDataBufferDeleter(compressedDataBuffer); 173 MemoryDeleter uncompressedDataBufferDeleter(uncompressedDataBuffer); 174 if (compressedDataBuffer == NULL || uncompressedDataBuffer == NULL) 175 return B_NO_MEMORY; 176 177 // read the data 178 size_t chunkIndex = size_t(offset / kChunkSize); 179 size_t inChunkOffset = (uint64)offset - (uint64)chunkIndex * kChunkSize; 180 size_t remainingBytes = size; 181 182 while (remainingBytes > 0) { 183 status_t error = ReadAndDecompressChunk(chunkIndex, 184 compressedDataBuffer, uncompressedDataBuffer); 185 if (error != B_OK) 186 return error; 187 188 size_t toWrite = std::min((size_t)kChunkSize - inChunkOffset, 189 remainingBytes); 190 // The last chunk may be shorter than kChunkSize, but since 191 // size (and thus remainingSize) had been clamped, that doesn't 192 // harm. 193 error = output->WriteExactly( 194 (char*)uncompressedDataBuffer + inChunkOffset, toWrite); 195 if (error != B_OK) 196 return error; 197 198 remainingBytes -= toWrite; 199 chunkIndex++; 200 inChunkOffset = 0; 201 } 202 203 return B_OK; 204 } 205 206 207 status_t 208 PackageFileHeapAccessorBase::ReadAndDecompressChunkData(uint64 offset, 209 size_t compressedSize, size_t uncompressedSize, void* compressedDataBuffer, 210 void* uncompressedDataBuffer) 211 { 212 // if uncompressed, read directly into the uncompressed data buffer 213 if (compressedSize == uncompressedSize) 214 return ReadFileData(offset, uncompressedDataBuffer, compressedSize); 215 216 // otherwise read into the other buffer and decompress 217 status_t error = ReadFileData(offset, compressedDataBuffer, compressedSize); 218 if (error != B_OK) 219 return error; 220 221 return DecompressChunkData(compressedDataBuffer, compressedSize, 222 uncompressedDataBuffer, uncompressedSize); 223 } 224 225 226 status_t 227 PackageFileHeapAccessorBase::DecompressChunkData(void* compressedDataBuffer, 228 size_t compressedSize, void* uncompressedDataBuffer, 229 size_t uncompressedSize) 230 { 231 size_t actualSize; 232 status_t error = fDecompressionAlgorithm->algorithm->DecompressBuffer( 233 compressedDataBuffer, compressedSize, uncompressedDataBuffer, 234 uncompressedSize, actualSize, fDecompressionAlgorithm->parameters); 235 if (error != B_OK) { 236 fErrorOutput->PrintError("Failed to decompress chunk data: %s\n", 237 strerror(error)); 238 return error; 239 } 240 241 if (actualSize != uncompressedSize) { 242 fErrorOutput->PrintError("Failed to decompress chunk data: size " 243 "mismatch\n"); 244 return B_ERROR; 245 } 246 247 return B_OK; 248 } 249 250 251 status_t 252 PackageFileHeapAccessorBase::ReadFileData(uint64 offset, void* buffer, 253 size_t size) 254 { 255 ssize_t bytesRead = pread(fFD, buffer, size, fHeapOffset + (off_t)offset); 256 if (bytesRead < 0) { 257 fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 ", %p, %zu) failed " 258 "to read data: %s\n", offset, buffer, size, strerror(errno)); 259 return errno; 260 } 261 if ((size_t)bytesRead != size) { 262 fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 ", %p, %zu) could " 263 "read only %zd bytes\n", offset, buffer, size, bytesRead); 264 return B_ERROR; 265 } 266 267 return B_OK; 268 } 269 270 271 } // namespace BPrivate 272 273 } // namespace BHPKG 274 275 } // namespace BPackageKit 276