* 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_zipfile.h"
#include <ctime>
#include <limits>
#include "dump.h"
#include "log/dynamic_log.h"
#include "pkg_algorithm.h"
#include "pkg_manager.h"
#include "pkg_stream.h"
#include "zip_pkg_parse.h"
#include "zlib.h"
namespace Hpackage {
constexpr uint32_t TM_YEAR_BITS = 9;
constexpr uint32_t TM_MON_BITS = 5;
constexpr uint32_t TM_MIN_BITS = 5;
constexpr uint32_t TM_HOUR_BITS = 11;
constexpr uint32_t BIG_SIZE_HEADER = 20;
constexpr uint32_t START_YEAR = 1900;
constexpr uint32_t MAX_FILE_NAME = 256;
constexpr uint32_t LOCAL_HEADER_SIGNATURE = 0x04034b50;
constexpr uint32_t CENTRAL_SIGNATURE = 0x02014b50;
constexpr uint32_t END_CENTRAL_SIGNATURE = 0x06054b50;
constexpr uint32_t DATA_DESC_SIGNATURE = 0x08074b50;
constexpr uint32_t GPBDD_FLAG_MASK = 0x0008;
constexpr uint32_t ZIP_PKG_ALIGNMENT_DEF = 1;
constexpr int32_t DEF_MEM_LEVEL = 8;
constexpr int32_t Z_STORED = 0;
int32_t ZipPkgFile::AddEntry(const PkgManager::FileInfoPtr file, const PkgStreamPtr inStream)
{
if (!CheckState({PKG_FILE_STATE_IDLE, PKG_FILE_STATE_WORKING}, PKG_FILE_STATE_WORKING)) {
PKG_LOGE("Error state curr %d ", state_);
return PKG_INVALID_STATE;
}
if (file == nullptr || inStream == nullptr) {
PKG_LOGE("AddEntry failed, invalid param");
return PKG_INVALID_PARAM;
}
PKG_LOGI("ZipPkgFile::AddEntry %s ", file->identity.c_str());
int32_t ret = PKG_SUCCESS;
ZipFileEntry* entry = (ZipFileEntry*)AddPkgEntry(file->identity);
if (entry == nullptr) {
PKG_LOGE("Failed to create pkg node for %s", file->identity.c_str());
return PKG_NONE_MEMORY;
}
entry->Init(file, inStream);
size_t encodeLen = 0;
ret = entry->EncodeHeader(inStream, currentOffset_, encodeLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("Failed to encode for %s", file->identity.c_str());
return ret;
}
currentOffset_ += encodeLen;
ret = entry->Pack(inStream, currentOffset_, encodeLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("Failed to pack for %s", file->identity.c_str());
return ret;
}
currentOffset_ += encodeLen;
return PKG_SUCCESS;
}
int32_t ZipPkgFile::SavePackage(size_t &signOffset)
{
UNUSED(signOffset);
if (!CheckState({PKG_FILE_STATE_WORKING}, PKG_FILE_STATE_CLOSE)) {
PKG_LOGE("error state curr %d ", state_);
return PKG_INVALID_STATE;
}
int32_t ret = PKG_SUCCESS;
size_t offset = currentOffset_;
for (auto &it : pkgEntryMapId_) {
ZipFileEntry* entry = (ZipFileEntry*)it.second;
if (entry == nullptr) {
PKG_LOGE("Failed to write CentralDirEntry");
return PKG_INVALID_PARAM;
}
size_t encodeLen = 0;
entry->EncodeCentralDirEntry(pkgStream_, offset, encodeLen);
offset += encodeLen;
}
std::vector<uint8_t> buff(sizeof(EndCentralDir));
WriteLE32(buff.data() + offsetof(EndCentralDir, signature), END_CENTRAL_SIGNATURE);
WriteLE16(buff.data() + offsetof(EndCentralDir, numDisk), 0);
WriteLE16(buff.data() + offsetof(EndCentralDir, startDiskOfCentralDir), 0);
WriteLE16(buff.data() + offsetof(EndCentralDir, totalEntriesInThisDisk), pkgEntryMapId_.size());
WriteLE16(buff.data() + offsetof(EndCentralDir, totalEntries), pkgEntryMapId_.size());
WriteLE32(buff.data() + offsetof(EndCentralDir, sizeOfCentralDir), offset - currentOffset_);
WriteLE32(buff.data() + offsetof(EndCentralDir, offset), currentOffset_);
WriteLE16(buff.data() + offsetof(EndCentralDir, commentLen), 0);
PkgBuffer buffer(buff);
ret = pkgStream_->Write(buffer, sizeof(EndCentralDir), offset);
if (ret != PKG_SUCCESS) {
PKG_LOGE("Failed to write CentralDirEntry for %s", pkgStream_->GetFileName().c_str());
return ret;
}
currentOffset_ = offset + sizeof(EndCentralDir);
pkgStream_->Flush(currentOffset_);
return PKG_SUCCESS;
}
int32_t ZipPkgFile::LoadPackage(std::vector<std::string> &fileNames, PkgBuffer &buffer,
uint32_t endDirLen, size_t endDirPos, size_t &readLen)
{
Updater::UPDATER_INIT_RECORD;
size_t fileLen = pkgStream_->GetFileLength();
EndCentralDir endDir;
endDir.signature = ReadLE32(buffer.buffer + offsetof(EndCentralDir, signature));
endDir.numDisk = ReadLE16(buffer.buffer + offsetof(EndCentralDir, numDisk));
endDir.startDiskOfCentralDir = ReadLE16(buffer.buffer + offsetof(EndCentralDir, startDiskOfCentralDir));
endDir.totalEntriesInThisDisk = ReadLE16(buffer.buffer + offsetof(EndCentralDir, totalEntriesInThisDisk));
endDir.totalEntries = ReadLE16(buffer.buffer + offsetof(EndCentralDir, totalEntries));
endDir.sizeOfCentralDir = ReadLE32(buffer.buffer + offsetof(EndCentralDir, sizeOfCentralDir));
endDir.offset = ReadLE32(buffer.buffer + offsetof(EndCentralDir, offset));
endDir.commentLen = ReadLE16(buffer.buffer + offsetof(EndCentralDir, commentLen));
if ((endDir.numDisk != 0) || (endDir.signature != END_CENTRAL_SIGNATURE) ||
(endDir.startDiskOfCentralDir != 0)
#ifndef UPDATER_UT
|| (endDir.offset >= fileLen) || (endDir.totalEntriesInThisDisk != endDir.totalEntries) ||
((endDir.offset + endDir.sizeOfCentralDir + endDirLen) > fileLen)
#endif
) {
PKG_LOGE("end dir format error %s", pkgStream_->GetFileName().c_str());
UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT);
return PKG_INVALID_PKG_FORMAT;
}
size_t currentPos = endDir.offset;
if (endDir.offset == UINT_MAX) {
int32_t ret = pkgStream_->Read(buffer, endDirPos - sizeof(Zip64EndCentralDirLocator),
sizeof(Zip64EndCentralDirLocator), readLen);
uint32_t signature = ReadLE32(buffer.buffer + offsetof(Zip64EndCentralDirLocator, signature));
if (ret != PKG_SUCCESS || signature != 0x07064b50) {
return ParseFileEntries(fileNames, endDir, currentPos, fileLen);
}
currentPos = ReadLE64(buffer.buffer + offsetof(Zip64EndCentralDirLocator, endOfCentralDirectoryRecord));
ret = pkgStream_->Read(buffer, currentPos, sizeof(Zip64EndCentralDirRecord), readLen);
signature = ReadLE32(buffer.buffer + offsetof(Zip64EndCentralDirRecord, signature));
if (ret == PKG_SUCCESS && signature == 0x06064b50) {
currentPos = ReadLE64(buffer.buffer + offsetof(Zip64EndCentralDirRecord, offset));
}
}
return ParseFileEntries(fileNames, endDir, currentPos, fileLen);
}
int32_t ZipPkgFile::GetFileLength(size_t &fileLen)
{
if (!CheckState({PKG_FILE_STATE_IDLE}, PKG_FILE_STATE_WORKING)) {
PKG_LOGE("Error state curr %d ", state_);
return PKG_INVALID_STATE;
}
fileLen = pkgStream_->GetFileLength();
if (fileLen == 0) {
PKG_LOGE("invalid file to load");
return PKG_INVALID_STATE;
}
if (fileLen > SIZE_MAX) {
PKG_LOGE("Invalid file len %zu to load %s", fileLen, pkgStream_->GetFileName().c_str());
return PKG_INVALID_FILE;
}
if (fileLen < static_cast<size_t>(sizeof(EndCentralDir))) {
PKG_LOGE("Too small to be zip %s", pkgStream_->GetFileName().c_str());
return PKG_INVALID_FILE;
}
return PKG_SUCCESS;
}
int32_t ZipPkgFile::LoadPackage(std::vector<std::string>& fileNames, VerifyFunction verifier)
{
Updater::UPDATER_INIT_RECORD;
UNUSED(verifier);
PKG_LOGI("LoadPackage %s :%zu", pkgStream_->GetFileName().c_str(), pkgStream_->GetFileLength());
size_t fileLen = 0;
int32_t ret = GetFileLength(fileLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("GetFileLength FAIL");
UPDATER_LAST_WORD(ret, "GetFileLength FAIL");
return ret;
}
size_t buffSize = sizeof(EndCentralDir);
if (buffSize < sizeof(Zip64EndCentralDirRecord)) {
buffSize = sizeof(Zip64EndCentralDirRecord);
}
size_t signatureLen = 0;
uint32_t magic = 0;
uint32_t endDirLen = sizeof(EndCentralDir);
size_t endDirPos = fileLen - endDirLen;
size_t readLen = 0;
PkgBuffer buffer(nullptr, buffSize);
ret = pkgStream_->Read(buffer, endDirPos, sizeof(EndCentralDir), readLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("read EOCD struct failed %s", pkgStream_->GetFileName().c_str());
UPDATER_LAST_WORD(ret, "read EOCD struct failed " + pkgStream_->GetFileName());
return ret;
}
magic = ReadLE32(buffer.buffer);
if (magic != END_CENTRAL_SIGNATURE) {
ZipPkgParse zipParse;
PkgSignComment pkgSignComment {};
ret = zipParse.ParsePkg(pkgStream_, pkgSignComment);
signatureLen = pkgSignComment.signCommentTotalLen;
if (ret != PKG_SUCCESS) {
PKG_LOGE("Parse zip package signature failed");
UPDATER_LAST_WORD(ret, "Parse zip package signature failed");
return ret;
}
endDirPos -= signatureLen;
ret = pkgStream_->Read(buffer, endDirPos, sizeof(EndCentralDir), readLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("read EOCD struct failed %s", pkgStream_->GetFileName().c_str());
UPDATER_LAST_WORD(ret, "read EOCD struct failed " + pkgStream_->GetFileName());
return ret;
}
}
return LoadPackage(fileNames, buffer, endDirLen, endDirPos, readLen);
}
int32_t ZipPkgFile::ParseFileEntries(std::vector<std::string> &fileNames,
const EndCentralDir &endDir, size_t currentPos, size_t fileLen)
{
Updater::UPDATER_INIT_RECORD;
int32_t ret = PKG_SUCCESS;
size_t buffLen = MAX_FILE_NAME + sizeof(LocalFileHeader) + sizeof(DataDescriptor)
+ sizeof(CentralDirEntry) + BIG_SIZE_HEADER;
PkgBuffer buffer(buffLen);
for (int32_t i = 0; i < endDir.totalEntries; i++) {
if (fileLen <= currentPos) {
PKG_LOGE("too small to be zip");
UPDATER_LAST_WORD(PKG_INVALID_FILE, "too small to be zip");
return PKG_INVALID_FILE;
}
ZipFileEntry* entry = new ZipFileEntry(this, nodeId_++);
if (entry == nullptr) {
PKG_LOGE("Failed to create zip node for %s", pkgStream_->GetFileName().c_str());
UPDATER_LAST_WORD(PKG_NONE_MEMORY, "Failed to create zip node for " + pkgStream_->GetFileName());
return PKG_NONE_MEMORY;
}
size_t decodeLen = 0;
ret = entry->DecodeHeader(buffer, currentPos, 0, decodeLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("DecodeHeader failed");
delete entry;
UPDATER_LAST_WORD(ret, "DecodeHeader failed");
return ret;
}
pkgEntryMapId_.insert(std::pair<uint32_t, PkgEntryPtr>(entry->GetNodeId(), (PkgEntryPtr)entry));
pkgEntryMapFileName_.insert(std::pair<std::string, PkgEntryPtr>(entry->GetFileName(), (PkgEntryPtr)entry));
fileNames.push_back(entry->GetFileName());
currentPos += decodeLen;
}
return ret;
}
int32_t ZipFileEntry::EncodeHeader(PkgStreamPtr inStream, size_t startOffset, size_t &encodeLen)
{
encodeLen = 0;
fileInfo_.fileInfo.headerOffset = startOffset;
return PKG_SUCCESS;
}
int32_t ZipFileEntry::PackStream(PkgStreamPtr inStream, size_t startOffset, size_t &encodeLen,
const PkgAlgorithm::PkgAlgorithmPtr algorithm, const PkgStreamPtr outStream)
{
if (inStream == nullptr || outStream == nullptr || algorithm == nullptr) {
PKG_LOGE("Invalid stream or algorithm");
return PKG_INVALID_STREAM;
}
std::vector<uint8_t> buff(MAX_FILE_NAME + sizeof(LocalFileHeader) + ZIP_PKG_ALIGNMENT_DEF);
size_t nameLen = 0;
PkgFileImpl::ConvertStringToBuffer(fileInfo_.fileInfo.identity,
{ buff.data() + sizeof(LocalFileHeader), buff.capacity() }, nameLen);
size_t headerLen = nameLen + sizeof(LocalFileHeader);
bool hasDataDesc = HasDataDesc(buff);
fileInfo_.fileInfo.dataOffset = startOffset + headerLen;
PkgAlgorithmContext context = {
{0, startOffset + headerLen},
{fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize},
0, fileInfo_.fileInfo.digestMethod
};
int32_t ret = algorithm->Pack(inStream, outStream, context);
if (ret != PKG_SUCCESS) {
PKG_LOGE("Failed to compress for %s", fileInfo_.fileInfo.identity.c_str());
return ret;
}
fileInfo_.fileInfo.packedSize = context.packedSize;
crc32_ = context.crc;
ret = EncodeLocalFileHeader(buff.data(), sizeof(LocalFileHeader), hasDataDesc, nameLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("Failed to encodeFileHeader for %s", fileInfo_.fileInfo.identity.c_str());
return ret;
}
PkgBuffer buffer(buff);
ret = outStream->Write(buffer, headerLen, startOffset);
if (ret != PKG_SUCCESS) {
PKG_LOGE("Failed to write header for %s", fileInfo_.fileInfo.identity.c_str());
return ret;
}
if (hasDataDesc) {
uint32_t encodeDataDescLen = 0;
ret = EncodeDataDescriptor(outStream,
startOffset + headerLen + fileInfo_.fileInfo.packedSize, encodeDataDescLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("Failed to encodeDataDescriptor for %s", fileInfo_.fileInfo.identity.c_str());
return ret;
}
headerLen += encodeDataDescLen;
}
encodeLen = headerLen + fileInfo_.fileInfo.packedSize;
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;
}
bool ZipFileEntry::HasDataDesc(std::vector<uint8_t> &buff)
{
bool hasDataDesc = true;
if (fileInfo_.method == Z_DEFLATED) {
#ifndef UPDATER_UT
hasDataDesc = false;
#endif
}
return hasDataDesc;
}
int32_t ZipFileEntry::Pack(PkgStreamPtr inStream, size_t startOffset, size_t &encodeLen)
{
PkgAlgorithm::PkgAlgorithmPtr algorithm = PkgAlgorithmFactory::GetAlgorithm(&fileInfo_.fileInfo);
PkgStreamPtr outStream = pkgFile_->GetPkgStream();
if (fileInfo_.fileInfo.headerOffset != startOffset) {
PKG_LOGE("Offset error %zu %zu %s", fileInfo_.fileInfo.headerOffset,
startOffset, 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;
}
return PackStream(inStream, startOffset, encodeLen, algorithm, outStream);
}
int32_t ZipFileEntry::EncodeCentralDirEntry(const PkgStreamPtr stream, size_t startOffset, size_t &encodeLen)
{
std::vector<uint8_t> buff(sizeof(CentralDirEntry) + MAX_FILE_NAME);
size_t realLen = 0;
PkgFileImpl::ConvertStringToBuffer(fileInfo_.fileInfo.identity, {
buff.data() + sizeof(CentralDirEntry), buff.capacity()
}, realLen);
CentralDirEntry* centralDir = reinterpret_cast<CentralDirEntry*>(buff.data());
centralDir->signature = CENTRAL_SIGNATURE;
centralDir->versionMade = 0;
centralDir->versionNeeded = 0;
if (fileInfo_.method == Z_DEFLATED) {
centralDir->flags |= GPBDD_FLAG_MASK;
}
centralDir->compressionMethod = static_cast<uint16_t>(fileInfo_.method);
centralDir->crc = crc32_;
uint16_t date;
uint16_t time;
ExtraTimeAndDate(fileInfo_.fileInfo.modifiedTime, date, time);
centralDir->modifiedDate = date;
centralDir->modifiedTime = time;
centralDir->compressedSize = fileInfo_.fileInfo.packedSize;
centralDir->uncompressedSize = fileInfo_.fileInfo.unpackedSize;
centralDir->nameSize = realLen;
centralDir->extraSize = 0;
centralDir->commentSize = 0;
centralDir->diskNumStart = 0;
centralDir->internalAttr = 0;
centralDir->externalAttr = 0;
centralDir->localHeaderOffset = fileInfo_.fileInfo.headerOffset;
PkgBuffer buffer(buff);
int32_t ret = stream->Write(buffer, sizeof(CentralDirEntry) + realLen, startOffset);
if (ret != PKG_SUCCESS) {
PKG_LOGE("Failed to write CentralDirEntry for %s", fileInfo_.fileInfo.identity.c_str());
return ret;
}
encodeLen = sizeof(CentralDirEntry) + realLen;
return PKG_SUCCESS;
}
int32_t ZipFileEntry::EncodeLocalFileHeader(uint8_t *buffer, size_t bufferLen, bool hasDataDesc,
size_t nameLen)
{
if (bufferLen < sizeof(LocalFileHeader)) {
PKG_LOGE("invalid buffer for decode");
return PKG_INVALID_PARAM;
}
LocalFileHeader* header = reinterpret_cast<LocalFileHeader*>(buffer);
header->signature = LOCAL_HEADER_SIGNATURE;
header->versionNeeded = 0;
header->flags = 0;
header->compressionMethod = static_cast<uint16_t>(fileInfo_.method);
uint16_t date;
uint16_t time;
ExtraTimeAndDate(fileInfo_.fileInfo.modifiedTime, date, time);
header->modifiedDate = date;
header->modifiedTime = time;
header->crc = crc32_;
header->compressedSize = fileInfo_.fileInfo.packedSize;
header->uncompressedSize = fileInfo_.fileInfo.unpackedSize;
header->nameSize = nameLen;
header->extraSize = 0;
if (hasDataDesc) {
header->flags |= GPBDD_FLAG_MASK;
header->compressedSize = 0u;
header->uncompressedSize = 0u;
header->crc = 0u;
}
return PKG_SUCCESS;
}
int32_t ZipFileEntry::EncodeDataDescriptor(const PkgStreamPtr stream, size_t startOffset,
uint32_t &encodeLen) const
{
if (stream == nullptr) {
PKG_LOGE("Invalid stream");
return PKG_INVALID_STREAM;
}
int32_t ret = PKG_SUCCESS;
size_t offset = startOffset;
DataDescriptor dataDesc = {};
dataDesc.signature = DATA_DESC_SIGNATURE;
dataDesc.crc = crc32_;
dataDesc.compressedSize = fileInfo_.fileInfo.packedSize;
dataDesc.uncompressedSize = fileInfo_.fileInfo.unpackedSize;
PkgBuffer buffer((uint8_t *)&dataDesc, sizeof(dataDesc));
ret = stream->Write(buffer, sizeof(dataDesc), offset);
if (ret != PKG_SUCCESS) {
PKG_LOGE("Failed to write DataDescriptor for %s", fileInfo_.fileInfo.identity.c_str());
return ret;
}
offset += sizeof(dataDesc);
encodeLen = offset - startOffset;
return ret;
}
int32_t ZipFileEntry::DoDecodeCentralDirEntry(PkgBuffer &buffer, size_t &decodeLen,
size_t currLen, uint16_t nameSize, uint16_t extraSize)
{
fileInfo_.method = static_cast<int32_t>(ReadLE16(buffer.buffer + offsetof(CentralDirEntry, compressionMethod)));
uint16_t modifiedTime = ReadLE16(buffer.buffer + offsetof(CentralDirEntry, modifiedTime));
uint16_t modifiedDate = ReadLE16(buffer.buffer + offsetof(CentralDirEntry, modifiedDate));
CombineTimeAndDate(fileInfo_.fileInfo.modifiedTime, modifiedTime, modifiedDate);
crc32_ = ReadLE32(buffer.buffer + offsetof(CentralDirEntry, crc));
fileInfo_.fileInfo.packedSize = ReadLE32(buffer.buffer + offsetof(CentralDirEntry, compressedSize));
fileInfo_.fileInfo.unpackedSize = ReadLE32(buffer.buffer + offsetof(CentralDirEntry, uncompressedSize));
fileInfo_.fileInfo.headerOffset = ReadLE32(buffer.buffer + offsetof(CentralDirEntry, localHeaderOffset));
decodeLen = currLen;
if (extraSize <= 0) {
return PKG_SUCCESS;
}
uint8_t* extraData = buffer.buffer + nameSize + sizeof(CentralDirEntry);
uint16_t headerId = ReadLE16(extraData);
if (headerId != 1) {
return PKG_SUCCESS;
}
size_t unpackedSize = ReadLE64(extraData + sizeof(uint32_t));
size_t packedSize = ReadLE64(extraData + sizeof(uint32_t) + sizeof(uint64_t));
if (fileInfo_.fileInfo.packedSize == UINT_MAX || fileInfo_.fileInfo.unpackedSize == UINT_MAX) {
fileInfo_.fileInfo.unpackedSize =
(fileInfo_.fileInfo.unpackedSize == UINT_MAX) ? unpackedSize : fileInfo_.fileInfo.unpackedSize;
fileInfo_.fileInfo.packedSize =
(fileInfo_.fileInfo.packedSize == UINT_MAX) ? packedSize : fileInfo_.fileInfo.packedSize;
fileInfo_.fileInfo.headerOffset = (fileInfo_.fileInfo.headerOffset == UINT_MAX) ?
ReadLE64(extraData + BIG_SIZE_HEADER) : fileInfo_.fileInfo.headerOffset;
} else if (fileInfo_.fileInfo.headerOffset == UINT_MAX) {
fileInfo_.fileInfo.headerOffset = unpackedSize;
}
return PKG_SUCCESS;
}
0x0001 2 bytes Tag for this "extra" block type
Size 2 bytes Size of this "extra" block
Original
Size 8 bytes Original uncompressed file size
Compressed
Size 8 bytes Size of compressed data
Relative Header
Offset 8 bytes Offset of local header record
Disk Start
Number 4 bytes Number of the disk on which
this file starts
*/
int32_t ZipFileEntry::DecodeCentralDirEntry(PkgStreamPtr inStream, PkgBuffer &buffer, size_t currentPos,
size_t &decodeLen)
{
size_t readLen = buffer.length;
if (readLen < sizeof(CentralDirEntry)) {
PKG_LOGE("data not not enough %zu", readLen);
return PKG_INVALID_PKG_FORMAT;
}
uint32_t signature = ReadLE32(buffer.buffer + offsetof(CentralDirEntry, signature));
if (signature != CENTRAL_SIGNATURE) {
PKG_LOGE("Check centralDir signature failed 0x%x", signature);
return PKG_INVALID_PKG_FORMAT;
}
uint16_t nameSize = ReadLE16(buffer.buffer + offsetof(CentralDirEntry, nameSize));
uint16_t extraSize = ReadLE16(buffer.buffer + offsetof(CentralDirEntry, extraSize));
uint16_t commentSize = ReadLE16(buffer.buffer + offsetof(CentralDirEntry, commentSize));
size_t currLen = sizeof(CentralDirEntry) + nameSize + extraSize + commentSize;
if (currentPos >= (std::numeric_limits<size_t>::max() - currLen)) {
PKG_LOGE("check centralDir len failed");
return PKG_INVALID_PKG_FORMAT;
}
size_t fileNameLength = nameSize;
if (nameSize >= MAX_FILE_NAME) {
PKG_LOGE("file name size too longer %d", nameSize);
fileNameLength = MAX_FILE_NAME - 1;
}
if (readLen < sizeof(CentralDirEntry) + fileNameLength) {
PKG_LOGE("data not not enough %zu", readLen);
return PKG_INVALID_PKG_FORMAT;
}
fileInfo_.fileInfo.identity.assign(reinterpret_cast<char*>(buffer.buffer + sizeof(CentralDirEntry)),
fileNameLength);
return DoDecodeCentralDirEntry(buffer, decodeLen, currLen, nameSize, extraSize);
}
int32_t ZipFileEntry::DecodeLocalFileHeaderCheck(PkgStreamPtr inStream, PkgBuffer &data,
size_t currentPos)
{
uint16_t flags = ReadLE16(data.buffer + offsetof(LocalFileHeader, flags));
uint32_t crc32 = ReadLE32(data.buffer + offsetof(LocalFileHeader, crc));
uint32_t packedSize = ReadLE32(data.buffer + offsetof(LocalFileHeader, compressedSize));
uint32_t unpackedSize = ReadLE32(data.buffer + offsetof(LocalFileHeader, uncompressedSize));
size_t readLen = 0;
if ((flags & GPBDD_FLAG_MASK) == GPBDD_FLAG_MASK) {
currentPos += fileInfo_.fileInfo.packedSize;
int ret = inStream->Read(data, currentPos, data.length, readLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("parse entry read centralDir failed");
return ret;
}
if (readLen < sizeof(DataDescriptor)) {
PKG_LOGE("data not not enough %zu", readLen);
return PKG_INVALID_PKG_FORMAT;
}
uint32_t signature = ReadLE32(data.buffer + offsetof(DataDescriptor, signature));
if (signature != DATA_DESC_SIGNATURE) {
PKG_LOGE("check DataDescriptor signature failed");
return PKG_INVALID_PKG_FORMAT;
}
crc32 = ReadLE32(data.buffer + offsetof(DataDescriptor, crc));
packedSize = ReadLE32(data.buffer + offsetof(DataDescriptor, compressedSize));
unpackedSize = ReadLE32(data.buffer + offsetof(DataDescriptor, uncompressedSize));
}
if (crc32_ != crc32) {
PKG_LOGE("Check crc failed %u %u packedSize: %zu unpackedSize: %zu", crc32_, crc32, packedSize, unpackedSize);
return PKG_INVALID_PKG_FORMAT;
}
return PKG_SUCCESS;
}
int32_t ZipFileEntry::DecodeLocalFileHeader(PkgStreamPtr inStream, PkgBuffer &data, size_t currentPos,
size_t &decodeLen)
{
size_t readLen = 0;
int32_t ret = inStream->Read(data, currentPos, data.length, readLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("parse entry read centralDir failed");
return ret;
}
if (readLen < sizeof(LocalFileHeader)) {
PKG_LOGE("data not not enough %zu", readLen);
return PKG_INVALID_PKG_FORMAT;
}
uint32_t signature = ReadLE32(data.buffer + offsetof(LocalFileHeader, signature));
if (signature != LOCAL_HEADER_SIGNATURE) {
PKG_LOGE("check localHeader signature failed");
return PKG_INVALID_PKG_FORMAT;
}
uint16_t nameSize = ReadLE16(data.buffer + offsetof(LocalFileHeader, nameSize));
uint16_t extraSize = ReadLE16(data.buffer + offsetof(LocalFileHeader, extraSize));
size_t currLen = sizeof(LocalFileHeader) + nameSize + extraSize;
if (currentPos >= (std::numeric_limits<size_t>::max() - currLen)) {
PKG_LOGE("check centralDir len failed");
return PKG_INVALID_PKG_FORMAT;
}
size_t fileNameLength = (nameSize >= MAX_FILE_NAME) ? MAX_FILE_NAME - 1 : nameSize;
if (readLen < sizeof(LocalFileHeader) + fileNameLength) {
PKG_LOGE("data not not enough %zu", readLen);
return PKG_INVALID_PKG_FORMAT;
}
std::string fileName(reinterpret_cast<char*>(data.buffer + sizeof(LocalFileHeader)), fileNameLength);
uint16_t compressionMethod = ReadLE16(data.buffer + offsetof(LocalFileHeader, compressionMethod));
fileInfo_.method = static_cast<int32_t>(compressionMethod);
fileInfo_.level = Z_BEST_COMPRESSION;
fileInfo_.windowBits = -MAX_WBITS;
fileInfo_.memLevel = DEF_MEM_LEVEL;
fileInfo_.strategy = Z_DEFAULT_STRATEGY;
if (fileInfo_.fileInfo.identity.compare(fileName)) {
PKG_LOGE("check file name %s %s failed", fileInfo_.fileInfo.identity.c_str(), fileName.c_str());
return PKG_INVALID_PKG_FORMAT;
}
fileName_.assign(fileInfo_.fileInfo.identity);
decodeLen = currLen;
ret = DecodeLocalFileHeaderCheck(inStream, data, currentPos + currLen);
if (ret != PKG_SUCCESS) {
return ret;
}
return PKG_SUCCESS;
}
int32_t ZipFileEntry::Unpack(PkgStreamPtr outStream)
{
PkgAlgorithm::PkgAlgorithmPtr algorithm = PkgAlgorithmFactory::GetAlgorithm(&fileInfo_.fileInfo);
if (algorithm == nullptr) {
PKG_LOGE("ZipFileEntry::Unpack : can not algorithm for %s", fileInfo_.fileInfo.identity.c_str());
return PKG_INVALID_PARAM;
}
PkgStreamPtr inStream = pkgFile_->GetPkgStream();
if (outStream == nullptr || inStream == nullptr) {
PKG_LOGE("ZipFileEntry::Unpack : outStream or inStream null for %s", fileInfo_.fileInfo.identity.c_str());
return PKG_INVALID_PARAM;
}
PkgAlgorithmContext context = {
{this->fileInfo_.fileInfo.dataOffset, 0},
{fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize},
crc32_, fileInfo_.fileInfo.digestMethod
};
int32_t ret = PKG_SUCCESS;
switch (fileInfo_.method) {
case Z_STORED:
algorithm = std::make_shared<PkgAlgorithm>();
[[fallthrough]];
case Z_DEFLATED:
ret = algorithm->Unpack(inStream, outStream, context);
break;
default:
ret = PKG_INVALID_PARAM;
break;
}
if (ret != PKG_SUCCESS) {
PKG_LOGE("Failed to decompress for %s, packedSize: %zu unpackedSize: %zu offset header: %zu data: %zu",
fileInfo_.fileInfo.identity.c_str(), fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize,
fileInfo_.fileInfo.headerOffset, fileInfo_.fileInfo.dataOffset);
return ret;
}
LOGDYN("Unpack packedSize: %zu unpackedSize: %zu offset header: %zu data: %zu", fileInfo_.fileInfo.packedSize,
fileInfo_.fileInfo.unpackedSize, fileInfo_.fileInfo.headerOffset, fileInfo_.fileInfo.dataOffset);
ret = outStream->Flush(fileInfo_.fileInfo.unpackedSize);
if (ret != PKG_SUCCESS) {
PKG_LOGE("Failed to Flush for %s", fileInfo_.fileInfo.identity.c_str());
return ret;
}
algorithm->UpdateFileInfo(&fileInfo_.fileInfo);
return PKG_SUCCESS;
}
std::pair<size_t, size_t> ZipFileEntry::GetEntryRange(void)
{
PKG_LOGW("unimplemented for ZipFileEntry");
return {0, 0};
}
void ZipFileEntry::CombineTimeAndDate(time_t &time, uint16_t modifiedTime, uint16_t modifiedDate) const
{
struct tm newTime;
newTime.tm_year = ((modifiedDate >> TM_YEAR_BITS) & 0x7f) + START_YEAR;
newTime.tm_mon = (modifiedDate >> TM_MON_BITS) & 0xf;
newTime.tm_mday = modifiedDate & 0x1f;
newTime.tm_hour = (modifiedTime >> TM_HOUR_BITS) & 0x1f;
newTime.tm_min = (modifiedTime >> TM_MIN_BITS) & 0x2f;
newTime.tm_sec = (modifiedTime << 1) & 0x1f;
newTime.tm_isdst = 0;
time = mktime(&newTime);
}
int32_t ZipFileEntry::DecodeHeader(PkgBuffer &buffer, size_t headerOffset, size_t dataOffset,
size_t &decodeLen)
{
PkgStreamPtr inStream = pkgFile_->GetPkgStream();
if (inStream == nullptr) {
PKG_LOGE("outStream or inStream null for %s", fileInfo_.fileInfo.identity.c_str());
return PKG_INVALID_PARAM;
}
if (headerOffset >= (std::numeric_limits<size_t>::max() - buffer.length)) {
PKG_LOGE("check centralDir len failed");
return PKG_INVALID_PKG_FORMAT;
}
size_t readLen = 0;
int32_t ret = inStream->Read(buffer, headerOffset, buffer.length, readLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("parse entry read centralDir failed");
return ret;
}
PkgBuffer centralBuff(buffer.buffer, readLen);
ret = DecodeCentralDirEntry(inStream, centralBuff, headerOffset, decodeLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("decode CentralDir failed");
return ret;
}
size_t headerLen = 0;
ret = DecodeLocalFileHeader(inStream, buffer, fileInfo_.fileInfo.headerOffset, headerLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("decode LocalFileHeader failed for %s", fileInfo_.fileInfo.identity.c_str());
return ret;
}
fileInfo_.fileInfo.packMethod = PKG_COMPRESS_METHOD_ZIP;
fileInfo_.fileInfo.digestMethod = PKG_DIGEST_TYPE_CRC;
fileInfo_.fileInfo.dataOffset = fileInfo_.fileInfo.headerOffset + headerLen;
LOGDYN("zip packed: %zu unpacked: %zu offset: %zu data: %zu %s",
fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize,
fileInfo_.fileInfo.headerOffset, fileInfo_.fileInfo.dataOffset, fileInfo_.fileInfo.identity.c_str());
return PKG_SUCCESS;
}
int32_t ZipFileEntry::Init(const PkgManager::FileInfoPtr fileInfo, PkgStreamPtr inStream)
{
fileInfo_.level = Z_BEST_COMPRESSION;
fileInfo_.method = Z_DEFLATED;
fileInfo_.windowBits = -MAX_WBITS;
fileInfo_.memLevel = DEF_MEM_LEVEL;
fileInfo_.strategy = Z_DEFAULT_STRATEGY;
int32_t ret = PkgEntry::Init(&fileInfo_.fileInfo, fileInfo, inStream);
if (ret != PKG_SUCCESS) {
PKG_LOGE("Failed to check input param");
return PKG_INVALID_PARAM;
}
ZipFileInfo* info = (ZipFileInfo*)fileInfo;
if (info != nullptr && info->method != -1) {
fileInfo_.level = info->level;
fileInfo_.memLevel = info->memLevel;
fileInfo_.method = info->method;
fileInfo_.strategy = info->strategy;
fileInfo_.windowBits = info->windowBits;
}
return PKG_SUCCESS;
}
size_t ZipFileEntry::GetOriginalSize() const
{
return fileInfo_.fileInfo.unpackedSize;
}
}