1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15#include "pkg_gzipfile.h" 16#include "dump.h" 17 18using namespace std; 19 20namespace Hpackage { 21/* gzip flag byte */ 22constexpr uint16_t HEADER_CRC = 0x02; /* bit 1 set: CRC16 for the gzip header */ 23constexpr uint16_t EXTRA_FIELD = 0x04; /* bit 2 set: extra field present */ 24constexpr uint16_t ORIG_NAME = 0x08; /* bit 3 set: original file name present */ 25constexpr uint16_t COMMENT = 0x10; /* bit 4 set: file comment present */ 26constexpr uint16_t ENCRYPTED = 0x20; /* bit 5 set: file is encrypted */ 27constexpr int32_t DEF_MEM_LEVEL = 8; 28constexpr uint16_t GZIP_MAGIC = 0x8b1f; 29constexpr int32_t BUFFER_SIZE = 1024; 30#ifdef SUPPORT_EXTRA_FIELD 31constexpr int32_t EXTRA_FIELD_LEN = 20; 32#endif 33constexpr int32_t BLOCK_SIZE = 8; 34 35/* 36 Each member has the following structure: 37 +---+---+---+---+---+---+---+---+---+---+ 38 |ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->) 39 +---+---+---+---+---+---+---+---+---+---+ 40 41 (if FLG.FEXTRA set) 42 43 +---+---+=================================+ 44 | XLEN |...XLEN bytes of "extra field"...| (more-->) 45 +---+---+=================================+ 46 47 (if FLG.FNAME set) 48 49 +=========================================+ 50 |...original file name, zero-terminated...| (more-->) 51 +=========================================+ 52 53 (if FLG.FCOMMENT set) 54 55 +===================================+ 56 |...file comment, zero-terminated...| (more-->) 57 +===================================+ 58 59 (if FLG.FHCRC set) 60 61 +---+---+ 62 | CRC16 | 63 +---+---+ 64 */ 65void GZipFileEntry::GetUpGradeCompInfo(size_t &offset, PkgBuffer &buffer) 66{ 67 GZipHeader *header = (GZipHeader *)buffer.buffer; 68 header->magic = GZIP_MAGIC; 69 header->method = Z_DEFLATED; 70 header->flags = 0; 71 header->mtime = fileInfo_.fileInfo.modifiedTime; 72 offset += sizeof(GZipHeader); 73#ifdef SUPPORT_EXTRA_FIELD 74 header->flags |= EXTRA_FIELD; 75 { 76 WriteLE16(buffer.buffer + offset, EXTRA_FIELD_LEN); 77 offset += sizeof(uint16_t) + EXTRA_FIELD_LEN; 78 } 79#endif 80 header->flags |= ORIG_NAME; 81 { 82 size_t fileNameLen = 0; 83 PkgFileImpl::ConvertStringToBuffer( 84 fileInfo_.fileInfo.identity, {buffer.buffer + offset, buffer.length - offset}, fileNameLen); 85 offset += fileNameLen; 86 buffer.buffer[offset] = 0; 87 offset += 1; 88 } 89#ifdef SUPPORT_EXTRA_FIELD 90 header->flags |= COMMENT; 91 { 92 size_t fileNameLen = 0; 93 PkgFileImpl::ConvertStringToBuffer( 94 fileInfo_.fileInfo.identity, {buffer.buffer + offset, buffer.length - offset}, fileNameLen); 95 offset += fileNameLen; 96 buffer.buffer[offset] = 0; 97 offset += 1; 98 } 99#endif 100 return ; 101} 102 103int32_t GZipFileEntry::EncodeHeader(PkgStreamPtr inStream, size_t startOffset, size_t &encodeLen) 104{ 105 PkgStreamPtr outStream = pkgFile_->GetPkgStream(); 106 if (outStream == nullptr) { 107 PKG_LOGE("Check outstream fail %s", fileInfo_.fileInfo.identity.c_str()); 108 return PKG_INVALID_PARAM; 109 } 110 size_t offset = 0; 111 PkgBuffer buffer(BUFFER_SIZE); 112 113 GetUpGradeCompInfo(offset, buffer); 114 115 fileInfo_.fileInfo.headerOffset = startOffset; 116 fileInfo_.fileInfo.dataOffset = startOffset + offset; 117 int32_t ret = outStream->Write(buffer, offset, startOffset); 118 if (ret != PKG_SUCCESS) { 119 PKG_LOGE("Fail write header for %s", fileInfo_.fileInfo.identity.c_str()); 120 return ret; 121 } 122 encodeLen = offset; 123 return PKG_SUCCESS; 124} 125 126int32_t GZipFileEntry::Pack(PkgStreamPtr inStream, size_t startOffset, size_t &encodeLen) 127{ 128 PkgAlgorithm::PkgAlgorithmPtr algorithm = PkgAlgorithmFactory::GetAlgorithm(&fileInfo_.fileInfo); 129 PkgStreamPtr outStream = pkgFile_->GetPkgStream(); 130 if (fileInfo_.fileInfo.dataOffset != startOffset) { 131 PKG_LOGE("start offset error for %s", fileInfo_.fileInfo.identity.c_str()); 132 return PKG_INVALID_PARAM; 133 } 134 if (algorithm == nullptr || outStream == nullptr || inStream == nullptr) { 135 PKG_LOGE("outStream or inStream null for %s", fileInfo_.fileInfo.identity.c_str()); 136 return PKG_INVALID_PARAM; 137 } 138 fileInfo_.fileInfo.dataOffset = startOffset; 139 PkgAlgorithmContext context = { 140 {0, startOffset}, 141 {fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize}, 142 0, fileInfo_.fileInfo.digestMethod 143 }; 144 int32_t ret = algorithm->Pack(inStream, outStream, context); 145 if (ret != PKG_SUCCESS) { 146 PKG_LOGE("Fail Compress for %s", fileInfo_.fileInfo.identity.c_str()); 147 return ret; 148 } 149 fileInfo_.fileInfo.packedSize = context.packedSize; 150 151 /* 152 0 1 2 3 4 5 6 7 153 +---+---+---+---+---+---+---+---+ 154 | CRC32 | ISIZE | 155 +---+---+---+---+---+---+---+---+ 156 */ 157 PkgBuffer buffer(BLOCK_SIZE); 158 WriteLE32(buffer.buffer, context.crc); 159 WriteLE32(buffer.buffer + sizeof(uint32_t), fileInfo_.fileInfo.unpackedSize); 160 ret = outStream->Write(buffer, BLOCK_SIZE, fileInfo_.fileInfo.dataOffset + fileInfo_.fileInfo.packedSize); 161 if (ret != PKG_SUCCESS) { 162 PKG_LOGE("Fail write header for %s", fileInfo_.fileInfo.identity.c_str()); 163 return ret; 164 } 165 encodeLen = fileInfo_.fileInfo.packedSize + BLOCK_SIZE; 166 PKG_LOGI("Pack packedSize:%zu unpackedSize: %zu offset: %zu %zu", fileInfo_.fileInfo.packedSize, 167 fileInfo_.fileInfo.unpackedSize, fileInfo_.fileInfo.headerOffset, fileInfo_.fileInfo.dataOffset); 168 return PKG_SUCCESS; 169} 170 171int32_t GZipFileEntry::CheckFileInfo(PkgAlgorithmContext context, PkgStreamPtr inStream) 172{ 173 size_t readLen = 0; 174 fileInfo_.fileInfo.packedSize = context.packedSize; 175 PkgBuffer buffer(BLOCK_SIZE); // Read last 8 bytes at the end of package 176 int32_t ret = inStream->Read(buffer, context.packedSize + fileInfo_.fileInfo.dataOffset, BLOCK_SIZE, readLen); 177 if (ret != PKG_SUCCESS) { 178 PKG_LOGE("Fail to read file %s", inStream->GetFileName().c_str()); 179 return ret; 180 } 181 crc32_ = ReadLE32(buffer.buffer); 182 fileInfo_.fileInfo.unpackedSize = ReadLE32(buffer.buffer + sizeof(uint32_t)); 183 if (crc32_ != context.crc) { 184 PKG_LOGE("Crc error %u %u", crc32_, context.crc); 185 return PKG_VERIFY_FAIL; 186 } 187 188 if (fileInfo_.fileInfo.unpackedSize != context.unpackedSize) { 189 PKG_LOGE("Crc error %u %u", crc32_, context.crc); 190 return PKG_VERIFY_FAIL; 191 } 192 return PKG_SUCCESS; 193} 194 195int32_t GZipFileEntry::Unpack(PkgStreamPtr outStream) 196{ 197 PkgAlgorithm::PkgAlgorithmPtr algorithm = PkgAlgorithmFactory::GetAlgorithm(&fileInfo_.fileInfo); 198 if (algorithm == nullptr) { 199 PKG_LOGE("can not algorithm for %s", fileInfo_.fileInfo.identity.c_str()); 200 return PKG_INVALID_PARAM; 201 } 202 203 PKG_LOGI("packedSize: %zu unpackedSize: %zu offset header: %zu data: %zu", fileInfo_.fileInfo.packedSize, 204 fileInfo_.fileInfo.unpackedSize, fileInfo_.fileInfo.headerOffset, fileInfo_.fileInfo.dataOffset); 205 206 PkgStreamPtr inStream = pkgFile_->GetPkgStream(); 207 if (outStream == nullptr || inStream == nullptr) { 208 PKG_LOGE("outStream or inStream null for %s", fileInfo_.fileInfo.identity.c_str()); 209 return PKG_INVALID_PARAM; 210 } 211 212 PkgAlgorithmContext context = { 213 {fileInfo_.fileInfo.dataOffset, 0}, 214 {fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize}, 215 0, fileInfo_.fileInfo.digestMethod 216 }; 217 int32_t ret = algorithm->Unpack(inStream, outStream, context); 218 if (ret != PKG_SUCCESS) { 219 PKG_LOGE("Fail Decompress for %s", fileInfo_.fileInfo.identity.c_str()); 220 return ret; 221 } 222 223 ret = CheckFileInfo(context, inStream); 224 if (ret != PKG_SUCCESS) { 225 PKG_LOGE("unpack failed ret is %d", ret); 226 return ret; 227 } 228 229 PKG_LOGI("packedSize: %zu unpackedSize: %zu offset header: %zu data: %zu", fileInfo_.fileInfo.packedSize, 230 fileInfo_.fileInfo.unpackedSize, fileInfo_.fileInfo.headerOffset, fileInfo_.fileInfo.dataOffset); 231 outStream->Flush(fileInfo_.fileInfo.unpackedSize); 232 algorithm->UpdateFileInfo(&fileInfo_.fileInfo); 233 return PKG_SUCCESS; 234} 235 236void GZipFileEntry::DecodeHeaderCalOffset(uint8_t flags, const PkgBuffer &buffer, size_t &offset, 237 std::string &fileName) const 238{ 239 if (flags & EXTRA_FIELD) { 240 uint16_t extLen = ReadLE16(buffer.buffer + offset); 241 offset += sizeof(uint16_t) + extLen; 242 } 243 if ((buffer.length > offset) && (flags & ORIG_NAME)) { 244 PkgFileImpl::ConvertBufferToString(fileName, {buffer.buffer + offset, buffer.length - offset}); 245 offset += fileName.size() + 1; 246 } 247 if ((buffer.length > offset) && (flags & COMMENT)) { 248 std::string comment; 249 PkgFileImpl::ConvertBufferToString(comment, {buffer.buffer + offset, buffer.length - offset}); 250 offset += comment.size() + 1; 251 } 252 if (flags & HEADER_CRC) { // Skip CRC 253 offset += sizeof(uint16_t); 254 } 255 return; 256} 257 258int32_t GZipFileEntry::DecodeHeader(PkgBuffer &buffer, size_t headerOffset, size_t dataOffset, 259 size_t &decodeLen) 260{ 261 Updater::UPDATER_INIT_RECORD; 262 PkgStreamPtr inStream = pkgFile_->GetPkgStream(); 263 if (inStream == nullptr || buffer.buffer == nullptr) { 264 PKG_LOGE("outStream or inStream null for %s", fileInfo_.fileInfo.identity.c_str()); 265 UPDATER_LAST_WORD(PKG_INVALID_PARAM); 266 return PKG_INVALID_PARAM; 267 } 268 size_t offset = sizeof(GZipHeader); 269 270 uint8_t flags = *(buffer.buffer + offsetof(GZipHeader, flags)); 271 272 DecodeHeaderCalOffset(flags, buffer, offset, fileName_); 273 if (fileName_.empty()) { 274 fileInfo_.fileInfo.identity = "gzip_"; 275 fileInfo_.fileInfo.identity.append(std::to_string(nodeId_)); 276 fileName_ = fileInfo_.fileInfo.identity; 277 } else { 278 fileInfo_.fileInfo.identity = fileName_; 279 } 280 fileInfo_.fileInfo.digestMethod = PKG_DIGEST_TYPE_CRC; 281 fileInfo_.fileInfo.packMethod = PKG_COMPRESS_METHOD_GZIP; 282 fileInfo_.level = Z_BEST_COMPRESSION; 283 fileInfo_.method = Z_DEFLATED; 284 fileInfo_.windowBits = -MAX_WBITS; 285 fileInfo_.memLevel = DEF_MEM_LEVEL; 286 fileInfo_.strategy = Z_DEFAULT_STRATEGY; 287 288 fileInfo_.fileInfo.headerOffset = headerOffset; 289 fileInfo_.fileInfo.dataOffset = headerOffset + offset; 290 // Read data length here. 291 // The length read here maybe incorrect, so should adjust it 292 // when unpack. 293 size_t readLen = 0; 294 size_t blockOffset = inStream->GetFileLength() - BLOCK_SIZE; 295 int32_t ret = inStream->Read(buffer, blockOffset, buffer.length, readLen); 296 if (ret != PKG_SUCCESS) { 297 PKG_LOGE("Fail to read file %s", inStream->GetFileName().c_str()); 298 UPDATER_LAST_WORD(ret); 299 return ret; 300 } 301 fileInfo_.fileInfo.unpackedSize = ReadLE32(buffer.buffer + sizeof(uint32_t)); 302 fileInfo_.fileInfo.packedSize = blockOffset - fileInfo_.fileInfo.dataOffset; 303 PKG_LOGI("GZipFileEntry::DecodeHeader dataOffset %zu, packedSize: %zu %zu", fileInfo_.fileInfo.dataOffset, 304 fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize); 305 decodeLen = offset; 306 return PKG_SUCCESS; 307} 308 309int32_t GZipPkgFile::AddEntry(const PkgManager::FileInfoPtr file, const PkgStreamPtr inStream) 310{ 311 if (file == nullptr || inStream == nullptr) { 312 PKG_LOGE("Fail to check input param"); 313 return PKG_INVALID_PARAM; 314 } 315 if (!CheckState({PKG_FILE_STATE_IDLE, PKG_FILE_STATE_WORKING}, PKG_FILE_STATE_CLOSE)) { 316 PKG_LOGE("error state curr %d ", state_); 317 return PKG_INVALID_STATE; 318 } 319 PKG_LOGI("Add file %s to package", file->identity.c_str()); 320 321 GZipFileEntry *entry = static_cast<GZipFileEntry *>(AddPkgEntry(file->identity)); 322 if (entry == nullptr) { 323 PKG_LOGE("Fail create pkg node for %s", file->identity.c_str()); 324 return PKG_NONE_MEMORY; 325 } 326 int32_t ret = entry->Init(file, inStream); 327 if (ret != PKG_SUCCESS) { 328 PKG_LOGE("Fail init entry for %s", file->identity.c_str()); 329 return ret; 330 } 331 332 size_t encodeLen = 0; 333 ret = entry->EncodeHeader(inStream, currentOffset_, encodeLen); 334 if (ret != PKG_SUCCESS) { 335 PKG_LOGE("Fail encode header for %s", file->identity.c_str()); 336 return ret; 337 } 338 currentOffset_ += encodeLen; 339 ret = entry->Pack(inStream, currentOffset_, encodeLen); 340 if (ret != PKG_SUCCESS) { 341 PKG_LOGE("Fail Pack for %s", file->identity.c_str()); 342 return ret; 343 } 344 currentOffset_ += encodeLen; 345 pkgStream_->Flush(currentOffset_); 346 return PKG_SUCCESS; 347} 348 349int32_t GZipPkgFile::SavePackage(size_t &offset) 350{ 351 AddSignData(pkgInfo_.digestMethod, currentOffset_, offset); 352 return PKG_SUCCESS; 353} 354 355int32_t GZipPkgFile::LoadPackage(std::vector<std::string> &fileNames, VerifyFunction verifier) 356{ 357 UNUSED(verifier); 358 if (!CheckState({ PKG_FILE_STATE_IDLE }, PKG_FILE_STATE_WORKING)) { 359 PKG_LOGE("error state curr %d ", state_); 360 UPDATER_LAST_WORD(PKG_INVALID_STATE); 361 return PKG_INVALID_STATE; 362 } 363 PKG_LOGI("LoadPackage %s ", pkgStream_->GetFileName().c_str()); 364 size_t srcOffset = 0; 365 size_t readLen = 0; 366 PkgBuffer buffer(nullptr, BUFFER_SIZE); 367 int32_t ret = pkgStream_->Read(buffer, srcOffset, buffer.length, readLen); 368 if (ret != PKG_SUCCESS) { 369 PKG_LOGE("Fail to read file %s", pkgStream_->GetFileName().c_str()); 370 UPDATER_LAST_WORD(ret); 371 return ret; 372 } 373 374 GZipHeader *header = (GZipHeader *)buffer.buffer; 375 // Check magic number 376 if (header->magic != GZIP_MAGIC) { 377 PKG_LOGE("Invalid gzip file %s", pkgStream_->GetFileName().c_str()); 378 UPDATER_LAST_WORD(PKG_INVALID_STATE); 379 return PKG_INVALID_FILE; 380 } 381 // Does not support encryption 382 if ((header->flags & ENCRYPTED) != 0) { 383 PKG_LOGE("Not support encrypted "); 384 UPDATER_LAST_WORD(PKG_INVALID_STATE); 385 return PKG_INVALID_FILE; 386 } 387 388 GZipFileEntry *entry = new GZipFileEntry(this, nodeId_++); 389 if (entry == nullptr) { 390 PKG_LOGE("Fail create gzip node for %s", pkgStream_->GetFileName().c_str()); 391 UPDATER_LAST_WORD(PKG_INVALID_STATE); 392 return PKG_LZ4_FINISH; 393 } 394 ret = entry->DecodeHeader(buffer, srcOffset, srcOffset, readLen); 395 srcOffset += readLen; 396 397 // Save entry 398 pkgEntryMapId_.insert(std::pair<uint32_t, PkgEntryPtr>(entry->GetNodeId(), entry)); 399 pkgEntryMapFileName_.insert(std::pair<std::string, PkgEntryPtr>(entry->GetFileName(), entry)); 400 fileNames.push_back(entry->GetFileName()); 401 return ret; 402} 403} // namespace Hpackage 404