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/PackageFileHeapReader.h> 8 9 #include <algorithm> 10 #include <new> 11 12 #include <package/hpkg/ErrorOutput.h> 13 #include <package/hpkg/HPKGDefs.h> 14 15 #include <AutoDeleter.h> 16 #include <package/hpkg/PoolBuffer.h> 17 18 19 namespace BPackageKit { 20 21 namespace BHPKG { 22 23 namespace BPrivate { 24 25 26 PackageFileHeapReader::PackageFileHeapReader(BErrorOutput* errorOutput, 27 BPositionIO* file, off_t heapOffset, off_t compressedHeapSize, 28 uint64 uncompressedHeapSize, 29 DecompressionAlgorithmOwner* decompressionAlgorithm) 30 : 31 PackageFileHeapAccessorBase(errorOutput, file, heapOffset, 32 decompressionAlgorithm), 33 fOffsets() 34 { 35 fCompressedHeapSize = compressedHeapSize; 36 fUncompressedHeapSize = uncompressedHeapSize; 37 } 38 39 40 PackageFileHeapReader::~PackageFileHeapReader() 41 { 42 } 43 44 45 status_t 46 PackageFileHeapReader::Init() 47 { 48 if (fUncompressedHeapSize == 0) { 49 if (fCompressedHeapSize != 0) { 50 fErrorOutput->PrintError( 51 "Invalid total compressed heap size (!= 0, empty heap)\n"); 52 return B_BAD_DATA; 53 } 54 return B_OK; 55 } 56 57 // Determine number of chunks and adjust the compressed heap size (subtract 58 // the size of the chunk size array at the end). Note that the size of the 59 // last chunk has not been saved, since its size is implied. 60 ssize_t chunkCount = (fUncompressedHeapSize + kChunkSize - 1) / kChunkSize; 61 if (chunkCount == 0) 62 return B_OK; 63 64 // If no compression is used at all, the chunk size table is omitted. Handle 65 // this case. 66 if (fDecompressionAlgorithm == NULL) { 67 if (fUncompressedHeapSize != fCompressedHeapSize) { 68 fErrorOutput->PrintError( 69 "Compressed and uncompressed heap sizes (%" B_PRIu64 " vs. " 70 "%" B_PRIu64 ") don't match for uncompressed heap.\n", 71 fCompressedHeapSize, fUncompressedHeapSize); 72 return B_BAD_DATA; 73 } 74 75 if (!fOffsets.InitUncompressedChunksOffsets(chunkCount)) 76 return B_NO_MEMORY; 77 78 return B_OK; 79 } 80 81 size_t chunkSizeTableSize = (chunkCount - 1) * 2; 82 if (fCompressedHeapSize <= chunkSizeTableSize) { 83 fErrorOutput->PrintError( 84 "Invalid total compressed heap size (%" B_PRIu64 ", " 85 "uncompressed %" B_PRIu64 ")\n", fCompressedHeapSize, 86 fUncompressedHeapSize); 87 return B_BAD_DATA; 88 } 89 90 fCompressedHeapSize -= chunkSizeTableSize; 91 92 // allocate a buffer 93 uint16* buffer = (uint16*)malloc(kChunkSize); 94 if (buffer == NULL) 95 return B_NO_MEMORY; 96 MemoryDeleter bufferDeleter(buffer); 97 98 // read the chunk size array 99 size_t remainingChunks = chunkCount - 1; 100 size_t index = 0; 101 uint64 offset = fCompressedHeapSize; 102 while (remainingChunks > 0) { 103 size_t toRead = std::min(remainingChunks, kChunkSize / 2); 104 status_t error = ReadFileData(offset, buffer, toRead * 2); 105 if (error != B_OK) 106 return error; 107 108 if (!fOffsets.InitChunksOffsets(chunkCount, index, buffer, toRead)) 109 return B_NO_MEMORY; 110 111 remainingChunks -= toRead; 112 index += toRead; 113 offset += toRead * 2; 114 } 115 116 // Sanity check: The sum of the chunk sizes must match the compressed heap 117 // size. The information aren't stored redundantly, so we check, if things 118 // look at least plausible. 119 uint64 lastChunkOffset = fOffsets[chunkCount - 1]; 120 if (lastChunkOffset >= fCompressedHeapSize 121 || fCompressedHeapSize - lastChunkOffset > kChunkSize 122 || fCompressedHeapSize - lastChunkOffset 123 > fUncompressedHeapSize - (chunkCount - 1) * kChunkSize) { 124 fErrorOutput->PrintError( 125 "Invalid total compressed heap size (%" B_PRIu64 ", uncompressed: " 126 "%" B_PRIu64 ", last chunk offset: %" B_PRIu64 ")\n", 127 fCompressedHeapSize, fUncompressedHeapSize, lastChunkOffset); 128 return B_BAD_DATA; 129 } 130 131 return B_OK; 132 } 133 134 135 PackageFileHeapReader* 136 PackageFileHeapReader::Clone() const 137 { 138 PackageFileHeapReader* clone = new(std::nothrow) PackageFileHeapReader( 139 fErrorOutput, fFile, fHeapOffset, fCompressedHeapSize, 140 fUncompressedHeapSize, fDecompressionAlgorithm); 141 if (clone == NULL) 142 return NULL; 143 144 ssize_t chunkCount = (fUncompressedHeapSize + kChunkSize - 1) / kChunkSize; 145 if (!clone->fOffsets.Init(chunkCount, fOffsets)) { 146 delete clone; 147 return NULL; 148 } 149 150 return clone; 151 } 152 153 154 status_t 155 PackageFileHeapReader::ReadAndDecompressChunk(size_t chunkIndex, 156 void* compressedDataBuffer, void* uncompressedDataBuffer) 157 { 158 uint64 offset = fOffsets[chunkIndex]; 159 bool isLastChunk 160 = uint64(chunkIndex + 1) * kChunkSize >= fUncompressedHeapSize; 161 size_t compressedSize = isLastChunk 162 ? fCompressedHeapSize - offset 163 : fOffsets[chunkIndex + 1] - offset; 164 size_t uncompressedSize = isLastChunk 165 ? fUncompressedHeapSize - (uint64)chunkIndex * kChunkSize 166 : kChunkSize; 167 168 return ReadAndDecompressChunkData(offset, compressedSize, uncompressedSize, 169 compressedDataBuffer, uncompressedDataBuffer); 170 } 171 172 173 } // namespace BPrivate 174 175 } // namespace BHPKG 176 177 } // namespace BPackageKit 178