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