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 <stdlib.h> 10 #include <string.h> 11 12 #include <algorithm> 13 #include <new> 14 #ifdef _KERNEL_MODE 15 #include <slab/Slab.h> 16 #endif 17 18 #include <ByteOrder.h> 19 #include <DataIO.h> 20 #include <package/hpkg/ErrorOutput.h> 21 22 #include <AutoDeleter.h> 23 #include <CompressionAlgorithm.h> 24 25 26 namespace BPackageKit { 27 28 namespace BHPKG { 29 30 namespace BPrivate { 31 32 33 #if defined(_KERNEL_MODE) 34 void* PackageFileHeapAccessorBase::sChunkCache = NULL; 35 #endif 36 37 38 // #pragma mark - OffsetArray 39 40 41 PackageFileHeapAccessorBase::OffsetArray::OffsetArray() 42 : 43 fOffsets(NULL) 44 { 45 } 46 47 48 PackageFileHeapAccessorBase::OffsetArray::~OffsetArray() 49 { 50 delete[] fOffsets; 51 } 52 53 54 bool 55 PackageFileHeapAccessorBase::OffsetArray::InitUncompressedChunksOffsets( 56 size_t totalChunkCount) 57 { 58 if (totalChunkCount <= 1) 59 return true; 60 61 const size_t max32BitChunks = (uint64(1) << 32) / kChunkSize; 62 size_t actual32BitChunks = totalChunkCount; 63 if (totalChunkCount - 1 > max32BitChunks) { 64 actual32BitChunks = max32BitChunks; 65 fOffsets = _AllocateOffsetArray(totalChunkCount, max32BitChunks); 66 } else 67 fOffsets = _AllocateOffsetArray(totalChunkCount, 0); 68 69 if (fOffsets == NULL) 70 return false; 71 72 { 73 uint32 offset = kChunkSize; 74 for (size_t i = 1; i < actual32BitChunks; i++, offset += kChunkSize) 75 fOffsets[i] = offset; 76 77 } 78 79 if (actual32BitChunks < totalChunkCount) { 80 uint64 offset = actual32BitChunks * kChunkSize; 81 uint32* offsets = fOffsets + actual32BitChunks; 82 for (size_t i = actual32BitChunks; i < totalChunkCount; 83 i++, offset += kChunkSize) { 84 *offsets++ = (uint32)offset; 85 *offsets++ = uint32(offset >> 32); 86 } 87 } 88 89 return true; 90 } 91 92 93 bool 94 PackageFileHeapAccessorBase::OffsetArray::InitChunksOffsets( 95 size_t totalChunkCount, size_t baseIndex, const uint16* chunkSizes, 96 size_t chunkCount) 97 { 98 if (totalChunkCount <= 1) 99 return true; 100 101 if (fOffsets == NULL) { 102 fOffsets = _AllocateOffsetArray(totalChunkCount, totalChunkCount); 103 if (fOffsets == NULL) 104 return false; 105 } 106 107 uint64 offset = (*this)[baseIndex]; 108 for (size_t i = 0; i < chunkCount; i++) { 109 offset += (uint64)B_BENDIAN_TO_HOST_INT16(chunkSizes[i]) + 1; 110 // the stored value is chunkSize - 1 111 size_t index = baseIndex + i + 1; 112 // (baseIndex + i) is the index of the chunk whose size is stored in 113 // chunkSizes[i]. We compute the offset of the following element 114 // which we store at index (baseIndex + i + 1). 115 116 if (offset <= ~(uint32)0) { 117 fOffsets[index] = (uint32)offset; 118 } else { 119 if (fOffsets[0] == 0) { 120 // Not scaled to allow for 64 bit offsets yet. Do that. 121 uint32* newOffsets = _AllocateOffsetArray(totalChunkCount, 122 index); 123 if (newOffsets == NULL) 124 return false; 125 126 fOffsets[0] = index; 127 memcpy(newOffsets, fOffsets, sizeof(newOffsets[0]) * index); 128 129 delete[] fOffsets; 130 fOffsets = newOffsets; 131 } 132 133 index += index - fOffsets[0]; 134 fOffsets[index] = (uint32)offset; 135 fOffsets[index + 1] = uint32(offset >> 32); 136 } 137 } 138 139 return true; 140 } 141 142 143 bool 144 PackageFileHeapAccessorBase::OffsetArray::Init(size_t totalChunkCount, 145 const OffsetArray& other) 146 { 147 if (other.fOffsets == NULL) 148 return true; 149 150 size_t elementCount = other.fOffsets[0] == 0 151 ? totalChunkCount 152 : 2 * totalChunkCount - other.fOffsets[0]; 153 154 fOffsets = new(std::nothrow) uint32[elementCount]; 155 if (fOffsets == NULL) 156 return false; 157 158 memcpy(fOffsets, other.fOffsets, elementCount * sizeof(fOffsets[0])); 159 return true; 160 } 161 162 163 /*static*/ uint32* 164 PackageFileHeapAccessorBase::OffsetArray::_AllocateOffsetArray( 165 size_t totalChunkCount, size_t offset32BitChunkCount) 166 { 167 uint32* offsets = new(std::nothrow) uint32[ 168 2 * totalChunkCount - offset32BitChunkCount]; 169 170 if (offsets != NULL) { 171 offsets[0] = offset32BitChunkCount == totalChunkCount 172 ? 0 : offset32BitChunkCount; 173 // 0 means that all offsets are 32 bit. Otherwise it's the index of 174 // the first 64 bit offset. 175 } 176 177 return offsets; 178 } 179 180 181 // #pragma mark - PackageFileHeapAccessorBase 182 183 184 PackageFileHeapAccessorBase::PackageFileHeapAccessorBase( 185 BErrorOutput* errorOutput, BPositionIO* file, off_t heapOffset, 186 DecompressionAlgorithmOwner* decompressionAlgorithm) 187 : 188 fErrorOutput(errorOutput), 189 fFile(file), 190 fHeapOffset(heapOffset), 191 fCompressedHeapSize(0), 192 fUncompressedHeapSize(0), 193 fDecompressionAlgorithm(decompressionAlgorithm) 194 { 195 if (fDecompressionAlgorithm != NULL) 196 fDecompressionAlgorithm->AcquireReference(); 197 } 198 199 200 PackageFileHeapAccessorBase::~PackageFileHeapAccessorBase() 201 { 202 if (fDecompressionAlgorithm != NULL) 203 fDecompressionAlgorithm->ReleaseReference(); 204 } 205 206 207 status_t 208 PackageFileHeapAccessorBase::ReadDataToOutput(off_t offset, size_t size, 209 BDataIO* output) 210 { 211 if (size == 0) 212 return B_OK; 213 214 if (offset < 0 || (uint64)offset > fUncompressedHeapSize 215 || size > fUncompressedHeapSize - offset) { 216 return B_BAD_VALUE; 217 } 218 219 // allocate buffers for compressed and uncompressed data 220 uint16* compressedDataBuffer, *uncompressedDataBuffer; 221 MemoryDeleter compressedMemoryDeleter, uncompressedMemoryDeleter; 222 223 #if defined(_KERNEL_MODE) && !defined(_BOOT_MODE) 224 struct ObjectCacheDeleter { 225 object_cache* cache; 226 void* object; 227 228 ObjectCacheDeleter(object_cache* c) 229 : cache(c) 230 , object(NULL) 231 { 232 } 233 234 ~ObjectCacheDeleter() 235 { 236 if (cache != NULL && object != NULL) 237 object_cache_free(cache, object, 0); 238 } 239 }; 240 241 ObjectCacheDeleter compressedCacheDeleter((object_cache*)sChunkCache), 242 uncompressedCacheDeleter((object_cache*)sChunkCache); 243 if (sChunkCache != NULL) { 244 compressedDataBuffer = (uint16*)object_cache_alloc((object_cache*)sChunkCache, 0); 245 uncompressedDataBuffer = (uint16*)object_cache_alloc((object_cache*)sChunkCache, 0); 246 compressedCacheDeleter.object = compressedDataBuffer; 247 uncompressedCacheDeleter.object = uncompressedDataBuffer; 248 } else 249 #endif 250 { 251 compressedDataBuffer = (uint16*)malloc(kChunkSize); 252 uncompressedDataBuffer = (uint16*)malloc(kChunkSize); 253 compressedMemoryDeleter.SetTo(compressedDataBuffer); 254 uncompressedMemoryDeleter.SetTo(uncompressedDataBuffer); 255 } 256 257 if (compressedDataBuffer == NULL || uncompressedDataBuffer == NULL) 258 return B_NO_MEMORY; 259 260 // read the data 261 size_t chunkIndex = size_t(offset / kChunkSize); 262 size_t inChunkOffset = (uint64)offset - (uint64)chunkIndex * kChunkSize; 263 size_t remainingBytes = size; 264 265 while (remainingBytes > 0) { 266 status_t error = ReadAndDecompressChunk(chunkIndex, 267 compressedDataBuffer, uncompressedDataBuffer); 268 if (error != B_OK) 269 return error; 270 271 size_t toWrite = std::min((size_t)kChunkSize - inChunkOffset, 272 remainingBytes); 273 // The last chunk may be shorter than kChunkSize, but since 274 // size (and thus remainingSize) had been clamped, that doesn't 275 // harm. 276 error = output->WriteExactly( 277 (char*)uncompressedDataBuffer + inChunkOffset, toWrite); 278 if (error != B_OK) 279 return error; 280 281 remainingBytes -= toWrite; 282 chunkIndex++; 283 inChunkOffset = 0; 284 } 285 286 return B_OK; 287 } 288 289 290 status_t 291 PackageFileHeapAccessorBase::ReadAndDecompressChunkData(uint64 offset, 292 size_t compressedSize, size_t uncompressedSize, void* compressedDataBuffer, 293 void* uncompressedDataBuffer) 294 { 295 // if uncompressed, read directly into the uncompressed data buffer 296 if (compressedSize == uncompressedSize) 297 return ReadFileData(offset, uncompressedDataBuffer, compressedSize); 298 299 // otherwise read into the other buffer and decompress 300 status_t error = ReadFileData(offset, compressedDataBuffer, compressedSize); 301 if (error != B_OK) 302 return error; 303 304 return DecompressChunkData(compressedDataBuffer, compressedSize, 305 uncompressedDataBuffer, uncompressedSize); 306 } 307 308 309 status_t 310 PackageFileHeapAccessorBase::DecompressChunkData(void* compressedDataBuffer, 311 size_t compressedSize, void* uncompressedDataBuffer, 312 size_t uncompressedSize) 313 { 314 size_t actualSize; 315 status_t error = fDecompressionAlgorithm->algorithm->DecompressBuffer( 316 compressedDataBuffer, compressedSize, uncompressedDataBuffer, 317 uncompressedSize, actualSize, fDecompressionAlgorithm->parameters); 318 if (error != B_OK) { 319 fErrorOutput->PrintError("Failed to decompress chunk data: %s\n", 320 strerror(error)); 321 return error; 322 } 323 324 if (actualSize != uncompressedSize) { 325 fErrorOutput->PrintError("Failed to decompress chunk data: size " 326 "mismatch\n"); 327 return B_ERROR; 328 } 329 330 return B_OK; 331 } 332 333 334 status_t 335 PackageFileHeapAccessorBase::ReadFileData(uint64 offset, void* buffer, 336 size_t size) 337 { 338 status_t error = fFile->ReadAtExactly(fHeapOffset + (off_t)offset, buffer, 339 size); 340 if (error != B_OK) { 341 fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 ", %p, %zu) failed " 342 "to read data: %s\n", offset, buffer, size, strerror(error)); 343 return error; 344 } 345 346 return B_OK; 347 } 348 349 350 } // namespace BPrivate 351 352 } // namespace BHPKG 353 354 } // namespace BPackageKit 355