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