1 /* 2 * Copyright 2009-2014, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include <package/hpkg/v1/PackageDataReader.h> 8 9 #include <string.h> 10 11 #include <algorithm> 12 #include <new> 13 14 #include <DataIO.h> 15 #include <package/hpkg/BufferPool.h> 16 #include <package/hpkg/PoolBuffer.h> 17 #include <package/hpkg/v1/HPKGDefsPrivate.h> 18 #include <package/hpkg/v1/PackageData.h> 19 #include <ZlibCompressionAlgorithm.h> 20 21 22 namespace BPackageKit { 23 24 namespace BHPKG { 25 26 namespace V1 { 27 28 29 using BHPKG::BPrivate::PoolBufferPutter; 30 31 32 // minimum/maximum zlib chunk size we consider sane 33 static const size_t kMinSaneZlibChunkSize = 1024; 34 static const size_t kMaxSaneZlibChunkSize = 10 * 1024 * 1024; 35 36 // maximum number of entries in the zlib offset table buffer 37 static const uint32 kMaxZlibOffsetTableBufferSize = 512; 38 39 static const size_t kUncompressedReaderBufferSize 40 = B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB; 41 42 43 // #pragma mark - PackageDataReader 44 45 46 class PackageDataReader : public BAbstractBufferedDataReader { 47 public: 48 PackageDataReader(BDataReader* dataReader) 49 : 50 fDataReader(dataReader) 51 { 52 } 53 54 virtual ~PackageDataReader() 55 { 56 } 57 58 virtual status_t Init(const BPackageData& data) = 0; 59 60 protected: 61 BDataReader* fDataReader; 62 }; 63 64 65 // #pragma mark - UncompressedPackageDataReader 66 67 68 class UncompressedPackageDataReader : public PackageDataReader { 69 public: 70 UncompressedPackageDataReader(BDataReader* dataReader, 71 BBufferPool* bufferPool) 72 : 73 PackageDataReader(dataReader), 74 fBufferPool(bufferPool) 75 { 76 } 77 78 status_t Init(const BPackageData& data) 79 { 80 fOffset = data.Offset(); 81 fSize = data.UncompressedSize(); 82 return B_OK; 83 } 84 85 virtual status_t ReadData(off_t offset, void* buffer, size_t size) 86 { 87 if (size == 0) 88 return B_OK; 89 90 if (offset < 0) 91 return B_BAD_VALUE; 92 93 if ((uint64)offset > fSize || size > fSize - offset) 94 return B_BAD_VALUE; 95 96 return fDataReader->ReadData(fOffset + offset, buffer, size); 97 } 98 99 virtual status_t ReadDataToOutput(off_t offset, size_t size, 100 BDataIO* output) 101 { 102 if (size == 0) 103 return B_OK; 104 105 if (offset < 0) 106 return B_BAD_VALUE; 107 108 if ((uint64)offset > fSize || size > fSize - offset) 109 return B_BAD_VALUE; 110 111 // get a temporary buffer 112 PoolBuffer* buffer = fBufferPool->GetBuffer( 113 kUncompressedReaderBufferSize); 114 if (buffer == NULL) 115 return B_NO_MEMORY; 116 PoolBufferPutter bufferPutter(fBufferPool, &buffer); 117 118 while (size > 0) { 119 // read into the buffer 120 size_t toRead = std::min(size, buffer->Size()); 121 status_t error = fDataReader->ReadData(fOffset + offset, 122 buffer->Buffer(), toRead); 123 if (error != B_OK) 124 return error; 125 126 // write to the output 127 error = output->WriteExactly(buffer->Buffer(), toRead); 128 if (error != B_OK) 129 return error; 130 131 offset += toRead; 132 size -= toRead; 133 } 134 135 return B_OK; 136 } 137 138 private: 139 BBufferPool* fBufferPool; 140 uint64 fOffset; 141 uint64 fSize; 142 }; 143 144 145 // #pragma mark - ZlibPackageDataReader 146 147 148 class ZlibPackageDataReader : public PackageDataReader { 149 public: 150 ZlibPackageDataReader(BDataReader* dataReader, BBufferPool* bufferPool) 151 : 152 PackageDataReader(dataReader), 153 fBufferPool(bufferPool), 154 fUncompressBuffer(NULL), 155 fOffsetTable(NULL) 156 { 157 } 158 159 ~ZlibPackageDataReader() 160 { 161 delete[] fOffsetTable; 162 163 fBufferPool->PutBuffer(&fUncompressBuffer); 164 } 165 166 status_t Init(const BPackageData& data) 167 { 168 fOffset = data.Offset(); 169 fCompressedSize = data.CompressedSize(); 170 fUncompressedSize = data.UncompressedSize(); 171 fChunkSize = data.ChunkSize(); 172 173 // validate chunk size 174 if (fChunkSize == 0) 175 fChunkSize = B_HPKG_DEFAULT_DATA_CHUNK_SIZE_ZLIB; 176 if (fChunkSize < kMinSaneZlibChunkSize 177 || fChunkSize > kMaxSaneZlibChunkSize) { 178 return B_BAD_DATA; 179 } 180 181 fChunkCount = (fUncompressedSize + (fChunkSize - 1)) / fChunkSize; 182 fOffsetTableSize = (fChunkCount - 1) * sizeof(uint64); 183 if (fOffsetTableSize >= fCompressedSize) 184 return B_BAD_DATA; 185 186 // allocate a buffer for the offset table 187 if (fChunkCount > 1) { 188 fOffsetTableBufferEntryCount = std::min(fChunkCount - 1, 189 (uint64)kMaxZlibOffsetTableBufferSize); 190 fOffsetTable = new(std::nothrow) uint64[ 191 fOffsetTableBufferEntryCount]; 192 if (fOffsetTable == NULL) 193 return B_NO_MEMORY; 194 195 fOffsetTableIndex = -1; 196 // mark the table content invalid 197 } else 198 fChunkSize = fUncompressedSize; 199 200 // mark uncompressed content invalid 201 fUncompressedChunk = -1; 202 203 return B_OK; 204 } 205 206 virtual status_t ReadDataToOutput(off_t offset, size_t size, 207 BDataIO* output) 208 { 209 // check offset and size 210 if (size == 0) 211 return B_OK; 212 213 if (offset < 0) 214 return B_BAD_VALUE; 215 216 if ((uint64)offset > fUncompressedSize 217 || size > fUncompressedSize - offset) { 218 return B_BAD_VALUE; 219 } 220 221 // get our uncompressed chunk buffer back, if possible 222 bool newBuffer; 223 if (fBufferPool->GetBuffer(fChunkSize, &fUncompressBuffer, &newBuffer) 224 == NULL) { 225 return B_NO_MEMORY; 226 } 227 PoolBufferPutter uncompressBufferPutter(fBufferPool, 228 &fUncompressBuffer); 229 230 if (newBuffer) 231 fUncompressedChunk = -1; 232 233 // uncompress 234 int64 chunkIndex = offset / fChunkSize; 235 off_t chunkOffset = chunkIndex * fChunkSize; 236 size_t inChunkOffset = offset - chunkOffset; 237 238 while (size > 0) { 239 // read and uncompress the chunk 240 status_t error = _ReadChunk(chunkIndex); 241 if (error != B_OK) 242 return error; 243 244 // write data to output 245 size_t toCopy = std::min(size, (size_t)fChunkSize - inChunkOffset); 246 error = output->WriteExactly( 247 (uint8*)fUncompressBuffer->Buffer() + inChunkOffset, toCopy); 248 if (error != B_OK) 249 return error; 250 251 size -= toCopy; 252 253 chunkIndex++; 254 chunkOffset += fChunkSize; 255 inChunkOffset = 0; 256 } 257 258 return B_OK; 259 } 260 261 private: 262 status_t _ReadChunk(int64 chunkIndex) 263 { 264 if (chunkIndex == fUncompressedChunk) 265 return B_OK; 266 267 // get the chunk offset and size 268 uint64 offset = 0; 269 uint32 compressedSize = 0; 270 status_t error = _GetCompressedChunkOffsetAndSize(chunkIndex, offset, 271 compressedSize); 272 if (error != B_OK) 273 return error; 274 275 uint32 uncompressedSize = (uint64)chunkIndex + 1 < fChunkCount 276 ? fChunkSize : fUncompressedSize - chunkIndex * fChunkSize; 277 278 // read the chunk 279 if (compressedSize == uncompressedSize) { 280 // the chunk is not compressed -- read it directly into the 281 // uncompressed buffer 282 error = fDataReader->ReadData(offset, fUncompressBuffer->Buffer(), 283 compressedSize); 284 } else { 285 // read to a read buffer and uncompress 286 PoolBuffer* readBuffer = fBufferPool->GetBuffer(fChunkSize); 287 if (readBuffer == NULL) 288 return B_NO_MEMORY; 289 PoolBufferPutter readBufferPutter(fBufferPool, readBuffer); 290 291 error = fDataReader->ReadData(offset, readBuffer->Buffer(), 292 compressedSize); 293 if (error != B_OK) 294 return error; 295 296 size_t actuallyUncompressedSize; 297 BZlibCompressionAlgorithm().DecompressBuffer( 298 readBuffer->Buffer(), compressedSize, 299 fUncompressBuffer->Buffer(), uncompressedSize, 300 actuallyUncompressedSize); 301 if (error == B_OK && actuallyUncompressedSize != uncompressedSize) 302 error = B_BAD_DATA; 303 } 304 305 if (error != B_OK) { 306 // error reading/decompressing data -- mark the cached data invalid 307 fUncompressedChunk = -1; 308 return error; 309 } 310 311 fUncompressedChunk = chunkIndex; 312 return B_OK; 313 } 314 315 status_t _GetCompressedChunkOffsetAndSize(int64 chunkIndex, uint64& _offset, 316 uint32& _size) 317 { 318 // get the offset 319 uint64 offset; 320 if (chunkIndex == 0) { 321 // first chunk is at 0 322 offset = 0; 323 } else { 324 status_t error = _GetCompressedChunkRelativeOffset(chunkIndex, 325 offset); 326 if (error != B_OK) 327 return error; 328 } 329 330 // get the end offset 331 uint64 endOffset; 332 if ((uint64)chunkIndex + 1 == fChunkCount) { 333 // last chunk end with the end of the data 334 endOffset = fCompressedSize - fOffsetTableSize; 335 } else { 336 status_t error = _GetCompressedChunkRelativeOffset(chunkIndex + 1, 337 endOffset); 338 if (error != B_OK) 339 return error; 340 } 341 342 // sanity check 343 if (endOffset < offset) 344 return B_BAD_DATA; 345 346 _offset = fOffset + fOffsetTableSize + offset; 347 _size = endOffset - offset; 348 return B_OK; 349 } 350 351 status_t _GetCompressedChunkRelativeOffset(int64 chunkIndex, 352 uint64& _offset) 353 { 354 if (fOffsetTableIndex < 0 || fOffsetTableIndex > chunkIndex 355 || fOffsetTableIndex + fOffsetTableBufferEntryCount <= chunkIndex) { 356 // read the table at the given index, or, if we can, the whole table 357 int64 readAtIndex = fChunkCount - 1 > fOffsetTableBufferEntryCount 358 ? chunkIndex : 1; 359 uint32 entriesToRead = std::min( 360 (uint64)fOffsetTableBufferEntryCount, 361 fChunkCount - readAtIndex); 362 363 status_t error = fDataReader->ReadData( 364 fOffset + (readAtIndex - 1) * sizeof(uint64), 365 fOffsetTable, entriesToRead * sizeof(uint64)); 366 if (error != B_OK) { 367 fOffsetTableIndex = -1; 368 return error; 369 } 370 371 fOffsetTableIndex = readAtIndex; 372 } 373 374 // get and check the offset 375 _offset = fOffsetTable[chunkIndex - fOffsetTableIndex]; 376 if (_offset > fCompressedSize - fOffsetTableSize) 377 return B_BAD_DATA; 378 379 return B_OK; 380 } 381 382 private: 383 BBufferPool* fBufferPool; 384 PoolBuffer* fUncompressBuffer; 385 int64 fUncompressedChunk; 386 387 uint64 fOffset; 388 uint64 fUncompressedSize; 389 uint64 fCompressedSize; 390 uint64 fOffsetTableSize; 391 uint64 fChunkCount; 392 uint32 fChunkSize; 393 uint32 fOffsetTableBufferEntryCount; 394 uint64* fOffsetTable; 395 int32 fOffsetTableIndex; 396 }; 397 398 399 // #pragma mark - PackageDataHeapReader 400 401 402 class PackageDataInlineReader : public BBufferDataReader { 403 public: 404 PackageDataInlineReader(const BPackageData& data) 405 : 406 BBufferDataReader(data.InlineData(), data.UncompressedSize()), 407 fData(data) 408 { 409 } 410 411 private: 412 BPackageData fData; 413 }; 414 415 416 // #pragma mark - BPackageDataReaderFactory 417 418 419 BPackageDataReaderFactory::BPackageDataReaderFactory(BBufferPool* bufferPool) 420 : 421 fBufferPool(bufferPool) 422 { 423 } 424 425 426 status_t 427 BPackageDataReaderFactory::CreatePackageDataReader(BDataReader* dataReader, 428 const BPackageData& data, BAbstractBufferedDataReader*& _reader) 429 { 430 if (data.IsEncodedInline()) { 431 BAbstractBufferedDataReader* reader 432 = new(std::nothrow) PackageDataInlineReader(data); 433 if (reader == NULL) 434 return B_NO_MEMORY; 435 436 _reader = reader; 437 return B_OK; 438 } 439 440 PackageDataReader* reader; 441 442 switch (data.Compression()) { 443 case B_HPKG_COMPRESSION_NONE: 444 reader = new(std::nothrow) UncompressedPackageDataReader( 445 dataReader, fBufferPool); 446 break; 447 case B_HPKG_COMPRESSION_ZLIB: 448 reader = new(std::nothrow) ZlibPackageDataReader(dataReader, 449 fBufferPool); 450 break; 451 default: 452 return B_BAD_VALUE; 453 } 454 455 if (reader == NULL) 456 return B_NO_MEMORY; 457 458 status_t error = reader->Init(data); 459 if (error != B_OK) { 460 delete reader; 461 return error; 462 } 463 464 _reader = reader; 465 return B_OK; 466 } 467 468 469 } // namespace V1 470 471 } // namespace BHPKG 472 473 } // namespace BPackageKit 474