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 status_t 148 PackageFileHeapAccessorBase::ReadDataToOutput(off_t offset, size_t size, 149 BDataIO* output) 150 { 151 if (size == 0) 152 return B_OK; 153 154 if (offset < 0 || (uint64)offset > fUncompressedHeapSize 155 || size > fUncompressedHeapSize - offset) { 156 return B_BAD_VALUE; 157 } 158 159 // allocate buffers for compressed and uncompressed data 160 uint16* compressedDataBuffer = (uint16*)malloc(kChunkSize); 161 uint16* uncompressedDataBuffer = (uint16*)malloc(kChunkSize); 162 MemoryDeleter compressedDataBufferDeleter(compressedDataBuffer); 163 MemoryDeleter uncompressedDataBufferDeleter(uncompressedDataBuffer); 164 if (compressedDataBuffer == NULL || uncompressedDataBuffer == NULL) 165 return B_NO_MEMORY; 166 167 // read the data 168 size_t chunkIndex = size_t(offset / kChunkSize); 169 size_t inChunkOffset = (uint64)offset - (uint64)chunkIndex * kChunkSize; 170 size_t remainingBytes = size; 171 172 while (remainingBytes > 0) { 173 status_t error = ReadAndDecompressChunk(chunkIndex, 174 compressedDataBuffer, uncompressedDataBuffer); 175 if (error != B_OK) 176 return error; 177 178 size_t toWrite = std::min((size_t)kChunkSize - inChunkOffset, 179 remainingBytes); 180 // The last chunk may be shorter than kChunkSize, but since 181 // size (and thus remainingSize) had been clamped, that doesn't 182 // harm. 183 error = output->WriteExactly( 184 (char*)uncompressedDataBuffer + inChunkOffset, toWrite); 185 if (error != B_OK) 186 return error; 187 188 remainingBytes -= toWrite; 189 chunkIndex++; 190 inChunkOffset = 0; 191 } 192 193 return B_OK; 194 } 195 196 197 status_t 198 PackageFileHeapAccessorBase::ReadAndDecompressChunkData(uint64 offset, 199 size_t compressedSize, size_t uncompressedSize, void* compressedDataBuffer, 200 void* uncompressedDataBuffer) 201 { 202 // if uncompressed, read directly into the uncompressed data buffer 203 if (compressedSize == uncompressedSize) 204 return ReadFileData(offset, uncompressedDataBuffer, compressedSize); 205 206 // otherwise read into the other buffer and decompress 207 status_t error = ReadFileData(offset, compressedDataBuffer, compressedSize); 208 if (error != B_OK) 209 return error; 210 211 return DecompressChunkData(compressedDataBuffer, compressedSize, 212 uncompressedDataBuffer, uncompressedSize); 213 } 214 215 216 status_t 217 PackageFileHeapAccessorBase::DecompressChunkData(void* compressedDataBuffer, 218 size_t compressedSize, void* uncompressedDataBuffer, 219 size_t uncompressedSize) 220 { 221 size_t actualSize; 222 status_t error = fDecompressionAlgorithm->algorithm->DecompressBuffer( 223 compressedDataBuffer, compressedSize, uncompressedDataBuffer, 224 uncompressedSize, actualSize, fDecompressionAlgorithm->parameters); 225 if (error != B_OK) { 226 fErrorOutput->PrintError("Failed to decompress chunk data: %s\n", 227 strerror(error)); 228 return error; 229 } 230 231 if (actualSize != uncompressedSize) { 232 fErrorOutput->PrintError("Failed to decompress chunk data: size " 233 "mismatch\n"); 234 return B_ERROR; 235 } 236 237 return B_OK; 238 } 239 240 241 status_t 242 PackageFileHeapAccessorBase::ReadFileData(uint64 offset, void* buffer, 243 size_t size) 244 { 245 ssize_t bytesRead = pread(fFD, buffer, size, fHeapOffset + (off_t)offset); 246 if (bytesRead < 0) { 247 fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 ", %p, %zu) failed " 248 "to read data: %s\n", offset, buffer, size, strerror(errno)); 249 return errno; 250 } 251 if ((size_t)bytesRead != size) { 252 fErrorOutput->PrintError("ReadFileData(%" B_PRIu64 ", %p, %zu) could " 253 "read only %zd bytes\n", offset, buffer, size, bytesRead); 254 return B_ERROR; 255 } 256 257 return B_OK; 258 } 259 260 261 } // namespace BPrivate 262 263 } // namespace BHPKG 264 265 } // namespace BPackageKit 266