/* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "pkg_gzipfile.h" #include "dump.h" using namespace std; namespace Hpackage { /* gzip flag byte */ constexpr uint16_t HEADER_CRC = 0x02; /* bit 1 set: CRC16 for the gzip header */ constexpr uint16_t EXTRA_FIELD = 0x04; /* bit 2 set: extra field present */ constexpr uint16_t ORIG_NAME = 0x08; /* bit 3 set: original file name present */ constexpr uint16_t COMMENT = 0x10; /* bit 4 set: file comment present */ constexpr uint16_t ENCRYPTED = 0x20; /* bit 5 set: file is encrypted */ constexpr int32_t DEF_MEM_LEVEL = 8; constexpr uint16_t GZIP_MAGIC = 0x8b1f; constexpr int32_t BUFFER_SIZE = 1024; #ifdef SUPPORT_EXTRA_FIELD constexpr int32_t EXTRA_FIELD_LEN = 20; #endif constexpr int32_t BLOCK_SIZE = 8; /* Each member has the following structure: +---+---+---+---+---+---+---+---+---+---+ |ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->) +---+---+---+---+---+---+---+---+---+---+ (if FLG.FEXTRA set) +---+---+=================================+ | XLEN |...XLEN bytes of "extra field"...| (more-->) +---+---+=================================+ (if FLG.FNAME set) +=========================================+ |...original file name, zero-terminated...| (more-->) +=========================================+ (if FLG.FCOMMENT set) +===================================+ |...file comment, zero-terminated...| (more-->) +===================================+ (if FLG.FHCRC set) +---+---+ | CRC16 | +---+---+ */ void GZipFileEntry::GetUpGradeCompInfo(size_t &offset, PkgBuffer &buffer) { GZipHeader *header = (GZipHeader *)buffer.buffer; header->magic = GZIP_MAGIC; header->method = Z_DEFLATED; header->flags = 0; header->mtime = fileInfo_.fileInfo.modifiedTime; offset += sizeof(GZipHeader); #ifdef SUPPORT_EXTRA_FIELD header->flags |= EXTRA_FIELD; { WriteLE16(buffer.buffer + offset, EXTRA_FIELD_LEN); offset += sizeof(uint16_t) + EXTRA_FIELD_LEN; } #endif header->flags |= ORIG_NAME; { size_t fileNameLen = 0; PkgFileImpl::ConvertStringToBuffer( fileInfo_.fileInfo.identity, {buffer.buffer + offset, buffer.length - offset}, fileNameLen); offset += fileNameLen; buffer.buffer[offset] = 0; offset += 1; } #ifdef SUPPORT_EXTRA_FIELD header->flags |= COMMENT; { size_t fileNameLen = 0; PkgFileImpl::ConvertStringToBuffer( fileInfo_.fileInfo.identity, {buffer.buffer + offset, buffer.length - offset}, fileNameLen); offset += fileNameLen; buffer.buffer[offset] = 0; offset += 1; } #endif return ; } int32_t GZipFileEntry::EncodeHeader(PkgStreamPtr inStream, size_t startOffset, size_t &encodeLen) { PkgStreamPtr outStream = pkgFile_->GetPkgStream(); if (outStream == nullptr) { PKG_LOGE("Check outstream fail %s", fileInfo_.fileInfo.identity.c_str()); return PKG_INVALID_PARAM; } size_t offset = 0; PkgBuffer buffer(BUFFER_SIZE); GetUpGradeCompInfo(offset, buffer); fileInfo_.fileInfo.headerOffset = startOffset; fileInfo_.fileInfo.dataOffset = startOffset + offset; int32_t ret = outStream->Write(buffer, offset, startOffset); if (ret != PKG_SUCCESS) { PKG_LOGE("Fail write header for %s", fileInfo_.fileInfo.identity.c_str()); return ret; } encodeLen = offset; return PKG_SUCCESS; } int32_t GZipFileEntry::Pack(PkgStreamPtr inStream, size_t startOffset, size_t &encodeLen) { PkgAlgorithm::PkgAlgorithmPtr algorithm = PkgAlgorithmFactory::GetAlgorithm(&fileInfo_.fileInfo); PkgStreamPtr outStream = pkgFile_->GetPkgStream(); if (fileInfo_.fileInfo.dataOffset != startOffset) { PKG_LOGE("start offset error for %s", fileInfo_.fileInfo.identity.c_str()); return PKG_INVALID_PARAM; } if (algorithm == nullptr || outStream == nullptr || inStream == nullptr) { PKG_LOGE("outStream or inStream null for %s", fileInfo_.fileInfo.identity.c_str()); return PKG_INVALID_PARAM; } fileInfo_.fileInfo.dataOffset = startOffset; PkgAlgorithmContext context = { {0, startOffset}, {fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize}, 0, fileInfo_.fileInfo.digestMethod }; int32_t ret = algorithm->Pack(inStream, outStream, context); if (ret != PKG_SUCCESS) { PKG_LOGE("Fail Compress for %s", fileInfo_.fileInfo.identity.c_str()); return ret; } fileInfo_.fileInfo.packedSize = context.packedSize; /* 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | CRC32 | ISIZE | +---+---+---+---+---+---+---+---+ */ PkgBuffer buffer(BLOCK_SIZE); WriteLE32(buffer.buffer, context.crc); WriteLE32(buffer.buffer + sizeof(uint32_t), fileInfo_.fileInfo.unpackedSize); ret = outStream->Write(buffer, BLOCK_SIZE, fileInfo_.fileInfo.dataOffset + fileInfo_.fileInfo.packedSize); if (ret != PKG_SUCCESS) { PKG_LOGE("Fail write header for %s", fileInfo_.fileInfo.identity.c_str()); return ret; } encodeLen = fileInfo_.fileInfo.packedSize + BLOCK_SIZE; PKG_LOGI("Pack packedSize:%zu unpackedSize: %zu offset: %zu %zu", fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize, fileInfo_.fileInfo.headerOffset, fileInfo_.fileInfo.dataOffset); return PKG_SUCCESS; } int32_t GZipFileEntry::CheckFileInfo(PkgAlgorithmContext context, PkgStreamPtr inStream) { size_t readLen = 0; fileInfo_.fileInfo.packedSize = context.packedSize; PkgBuffer buffer(BLOCK_SIZE); // Read last 8 bytes at the end of package int32_t ret = inStream->Read(buffer, context.packedSize + fileInfo_.fileInfo.dataOffset, BLOCK_SIZE, readLen); if (ret != PKG_SUCCESS) { PKG_LOGE("Fail to read file %s", inStream->GetFileName().c_str()); return ret; } crc32_ = ReadLE32(buffer.buffer); fileInfo_.fileInfo.unpackedSize = ReadLE32(buffer.buffer + sizeof(uint32_t)); if (crc32_ != context.crc) { PKG_LOGE("Crc error %u %u", crc32_, context.crc); return PKG_VERIFY_FAIL; } if (fileInfo_.fileInfo.unpackedSize != context.unpackedSize) { PKG_LOGE("Crc error %u %u", crc32_, context.crc); return PKG_VERIFY_FAIL; } return PKG_SUCCESS; } int32_t GZipFileEntry::Unpack(PkgStreamPtr outStream) { PkgAlgorithm::PkgAlgorithmPtr algorithm = PkgAlgorithmFactory::GetAlgorithm(&fileInfo_.fileInfo); if (algorithm == nullptr) { PKG_LOGE("can not algorithm for %s", fileInfo_.fileInfo.identity.c_str()); return PKG_INVALID_PARAM; } PKG_LOGI("packedSize: %zu unpackedSize: %zu offset header: %zu data: %zu", fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize, fileInfo_.fileInfo.headerOffset, fileInfo_.fileInfo.dataOffset); PkgStreamPtr inStream = pkgFile_->GetPkgStream(); if (outStream == nullptr || inStream == nullptr) { PKG_LOGE("outStream or inStream null for %s", fileInfo_.fileInfo.identity.c_str()); return PKG_INVALID_PARAM; } PkgAlgorithmContext context = { {fileInfo_.fileInfo.dataOffset, 0}, {fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize}, 0, fileInfo_.fileInfo.digestMethod }; int32_t ret = algorithm->Unpack(inStream, outStream, context); if (ret != PKG_SUCCESS) { PKG_LOGE("Fail Decompress for %s", fileInfo_.fileInfo.identity.c_str()); return ret; } ret = CheckFileInfo(context, inStream); if (ret != PKG_SUCCESS) { PKG_LOGE("unpack failed ret is %d", ret); return ret; } PKG_LOGI("packedSize: %zu unpackedSize: %zu offset header: %zu data: %zu", fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize, fileInfo_.fileInfo.headerOffset, fileInfo_.fileInfo.dataOffset); outStream->Flush(fileInfo_.fileInfo.unpackedSize); algorithm->UpdateFileInfo(&fileInfo_.fileInfo); return PKG_SUCCESS; } void GZipFileEntry::DecodeHeaderCalOffset(uint8_t flags, const PkgBuffer &buffer, size_t &offset, std::string &fileName) const { if (flags & EXTRA_FIELD) { uint16_t extLen = ReadLE16(buffer.buffer + offset); offset += sizeof(uint16_t) + extLen; } if ((buffer.length > offset) && (flags & ORIG_NAME)) { PkgFileImpl::ConvertBufferToString(fileName, {buffer.buffer + offset, buffer.length - offset}); offset += fileName.size() + 1; } if ((buffer.length > offset) && (flags & COMMENT)) { std::string comment; PkgFileImpl::ConvertBufferToString(comment, {buffer.buffer + offset, buffer.length - offset}); offset += comment.size() + 1; } if (flags & HEADER_CRC) { // Skip CRC offset += sizeof(uint16_t); } return; } int32_t GZipFileEntry::DecodeHeader(PkgBuffer &buffer, size_t headerOffset, size_t dataOffset, size_t &decodeLen) { Updater::UPDATER_INIT_RECORD; PkgStreamPtr inStream = pkgFile_->GetPkgStream(); if (inStream == nullptr || buffer.buffer == nullptr) { PKG_LOGE("outStream or inStream null for %s", fileInfo_.fileInfo.identity.c_str()); UPDATER_LAST_WORD(PKG_INVALID_PARAM); return PKG_INVALID_PARAM; } size_t offset = sizeof(GZipHeader); uint8_t flags = *(buffer.buffer + offsetof(GZipHeader, flags)); DecodeHeaderCalOffset(flags, buffer, offset, fileName_); if (fileName_.empty()) { fileInfo_.fileInfo.identity = "gzip_"; fileInfo_.fileInfo.identity.append(std::to_string(nodeId_)); fileName_ = fileInfo_.fileInfo.identity; } else { fileInfo_.fileInfo.identity = fileName_; } fileInfo_.fileInfo.digestMethod = PKG_DIGEST_TYPE_CRC; fileInfo_.fileInfo.packMethod = PKG_COMPRESS_METHOD_GZIP; fileInfo_.level = Z_BEST_COMPRESSION; fileInfo_.method = Z_DEFLATED; fileInfo_.windowBits = -MAX_WBITS; fileInfo_.memLevel = DEF_MEM_LEVEL; fileInfo_.strategy = Z_DEFAULT_STRATEGY; fileInfo_.fileInfo.headerOffset = headerOffset; fileInfo_.fileInfo.dataOffset = headerOffset + offset; // Read data length here. // The length read here maybe incorrect, so should adjust it // when unpack. size_t readLen = 0; size_t blockOffset = inStream->GetFileLength() - BLOCK_SIZE; int32_t ret = inStream->Read(buffer, blockOffset, buffer.length, readLen); if (ret != PKG_SUCCESS) { PKG_LOGE("Fail to read file %s", inStream->GetFileName().c_str()); UPDATER_LAST_WORD(ret); return ret; } fileInfo_.fileInfo.unpackedSize = ReadLE32(buffer.buffer + sizeof(uint32_t)); fileInfo_.fileInfo.packedSize = blockOffset - fileInfo_.fileInfo.dataOffset; PKG_LOGI("GZipFileEntry::DecodeHeader dataOffset %zu, packedSize: %zu %zu", fileInfo_.fileInfo.dataOffset, fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize); decodeLen = offset; return PKG_SUCCESS; } int32_t GZipPkgFile::AddEntry(const PkgManager::FileInfoPtr file, const PkgStreamPtr inStream) { if (file == nullptr || inStream == nullptr) { PKG_LOGE("Fail to check input param"); return PKG_INVALID_PARAM; } if (!CheckState({PKG_FILE_STATE_IDLE, PKG_FILE_STATE_WORKING}, PKG_FILE_STATE_CLOSE)) { PKG_LOGE("error state curr %d ", state_); return PKG_INVALID_STATE; } PKG_LOGI("Add file %s to package", file->identity.c_str()); GZipFileEntry *entry = static_cast(AddPkgEntry(file->identity)); if (entry == nullptr) { PKG_LOGE("Fail create pkg node for %s", file->identity.c_str()); return PKG_NONE_MEMORY; } int32_t ret = entry->Init(file, inStream); if (ret != PKG_SUCCESS) { PKG_LOGE("Fail init entry for %s", file->identity.c_str()); return ret; } size_t encodeLen = 0; ret = entry->EncodeHeader(inStream, currentOffset_, encodeLen); if (ret != PKG_SUCCESS) { PKG_LOGE("Fail encode header for %s", file->identity.c_str()); return ret; } currentOffset_ += encodeLen; ret = entry->Pack(inStream, currentOffset_, encodeLen); if (ret != PKG_SUCCESS) { PKG_LOGE("Fail Pack for %s", file->identity.c_str()); return ret; } currentOffset_ += encodeLen; pkgStream_->Flush(currentOffset_); return PKG_SUCCESS; } int32_t GZipPkgFile::SavePackage(size_t &offset) { AddSignData(pkgInfo_.digestMethod, currentOffset_, offset); return PKG_SUCCESS; } int32_t GZipPkgFile::LoadPackage(std::vector &fileNames, VerifyFunction verifier) { UNUSED(verifier); if (!CheckState({ PKG_FILE_STATE_IDLE }, PKG_FILE_STATE_WORKING)) { PKG_LOGE("error state curr %d ", state_); UPDATER_LAST_WORD(PKG_INVALID_STATE); return PKG_INVALID_STATE; } PKG_LOGI("LoadPackage %s ", pkgStream_->GetFileName().c_str()); size_t srcOffset = 0; size_t readLen = 0; PkgBuffer buffer(nullptr, BUFFER_SIZE); int32_t ret = pkgStream_->Read(buffer, srcOffset, buffer.length, readLen); if (ret != PKG_SUCCESS) { PKG_LOGE("Fail to read file %s", pkgStream_->GetFileName().c_str()); UPDATER_LAST_WORD(ret); return ret; } GZipHeader *header = (GZipHeader *)buffer.buffer; // Check magic number if (header->magic != GZIP_MAGIC) { PKG_LOGE("Invalid gzip file %s", pkgStream_->GetFileName().c_str()); UPDATER_LAST_WORD(PKG_INVALID_STATE); return PKG_INVALID_FILE; } // Does not support encryption if ((header->flags & ENCRYPTED) != 0) { PKG_LOGE("Not support encrypted "); UPDATER_LAST_WORD(PKG_INVALID_STATE); return PKG_INVALID_FILE; } GZipFileEntry *entry = new GZipFileEntry(this, nodeId_++); if (entry == nullptr) { PKG_LOGE("Fail create gzip node for %s", pkgStream_->GetFileName().c_str()); UPDATER_LAST_WORD(PKG_INVALID_STATE); return PKG_LZ4_FINISH; } ret = entry->DecodeHeader(buffer, srcOffset, srcOffset, readLen); srcOffset += readLen; // Save entry pkgEntryMapId_.insert(std::pair(entry->GetNodeId(), entry)); pkgEntryMapFileName_.insert(std::pair(entry->GetFileName(), entry)); fileNames.push_back(entry->GetFileName()); return ret; } } // namespace Hpackage