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