* 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 "patch/update_patch.h"
#include <memory>
#include <vector>
#include "blocks_patch.h"
#include "diffpatch.h"
#include "image_patch.h"
#include "openssl/sha.h"
#include "securec.h"
using namespace Hpackage;
namespace UpdatePatch {
int32_t UpdateApplyPatch::ApplyImagePatch(const PatchParam ¶m, const std::vector<uint8_t> &bonusData,
ImageProcessor writer, const std::string& expected)
{
if (writer == nullptr) {
PATCH_LOGE("ApplyImagePatch : processor is null");
return -1;
}
std::unique_ptr<ImagePatchWriter> patchWriter = std::make_unique<ImagePatchWriter>(writer, expected, "");
if (patchWriter == nullptr) {
PATCH_LOGE("ApplyImagePatch : Failed to create patch writer");
return -1;
}
int32_t ret = patchWriter->Init();
if (ret != 0) {
PATCH_LOGE("ApplyImagePatch : Failed to init patch writer");
return -1;
}
ret = ApplyImagePatch(param, patchWriter.get(), bonusData);
if (ret != 0) {
PATCH_LOGE("ApplyImagePatch : Failed to apply image patch");
return -1;
}
return patchWriter->Finish();
}
bool UpdateApplyPatch::PreCheck(const PatchParam ¶m, const UpdatePatchWriterPtr writer)
{
if (writer == nullptr) {
PATCH_LOGE("check param fail ");
return false;
}
if (param.patchSize < (std::char_traits<char>::length(PKGDIFF_MAGIC) + sizeof(int32_t))) {
PATCH_LOGE("patch too short to contain header ");
return false;
}
if (memcmp(param.patch, PKGDIFF_MAGIC, std::char_traits<char>::length(PKGDIFF_MAGIC)) != 0) {
PATCH_LOGE("corrupt patch file header (magic number) ");
return false;
}
return true;
}
int32_t UpdateApplyPatch::ApplyImagePatch(const PatchParam ¶m,
UpdatePatchWriterPtr writer, const std::vector<uint8_t> &bonusData)
{
if (!PreCheck(param, writer)) {
return -1;
}
size_t offset = std::char_traits<char>::length(PKGDIFF_MAGIC);
int32_t numChunks = ImagePatch::ReadLE<int32_t>(param.patch + offset);
offset += sizeof(int32_t);
std::vector<uint8_t> empty;
for (int i = 0; i < numChunks; ++i) {
if ((offset + sizeof(int32_t)) > param.patchSize) {
PATCH_LOGE("Failed to read chunk record ");
return -1;
}
int32_t type = ImagePatch::ReadLE<int32_t>(param.patch + offset);
PATCH_DEBUG("ApplyImagePatch numChunks[%d] type %d offset %d", i, type, offset);
offset += sizeof(int32_t);
std::unique_ptr<ImagePatch> imagePatch = nullptr;
switch (type) {
case BLOCK_NORMAL:
imagePatch = std::make_unique<NormalImagePatch>(writer);
break;
case BLOCK_RAW:
imagePatch = std::make_unique<RowImagePatch>(writer);
break;
case BLOCK_DEFLATE:
imagePatch = std::make_unique<ZipImagePatch>(writer, ((i == 1) ? bonusData : empty));
break;
case BLOCK_LZ4:
imagePatch = std::make_unique<Lz4ImagePatch>(writer, ((i == 1) ? bonusData : empty));
break;
default:
break;
}
if (imagePatch == nullptr) {
PATCH_LOGE("Failed to creareimg patch ");
return -1;
}
int32_t ret = imagePatch->ApplyImagePatch(param, offset);
if (ret != 0) {
PATCH_LOGE("Apply image patch fail ");
return -1;
}
}
return 0;
}
int32_t UpdateApplyPatch::ApplyBlockPatch(const PatchBuffer &patchInfo,
const BlockBuffer &oldInfo, std::vector<uint8_t> &newData)
{
std::unique_ptr<BlocksBufferPatch> patch = std::make_unique<BlocksBufferPatch>(patchInfo, oldInfo, newData);
if (patch == nullptr) {
PATCH_LOGE("Failed to creare patch ");
return -1;
}
return patch->ApplyPatch();
}
int32_t UpdateApplyPatch::ApplyBlockPatch(const PatchBuffer &patchInfo,
const BlockBuffer &oldInfo, UpdatePatchWriterPtr writer)
{
PkgManager* pkgManager = Hpackage::PkgManager::CreatePackageInstance();
if (pkgManager == nullptr) {
PATCH_LOGE("Failed to get pkg manager");
return -1;
}
Hpackage::PkgManager::StreamPtr stream = nullptr;
int32_t ret = pkgManager->CreatePkgStream(stream, "", {oldInfo.buffer, oldInfo.length});
if (stream == nullptr || ret != PKG_SUCCESS) {
PATCH_LOGE("Failed to create stream");
pkgManager->ClosePkgStream(stream);
Hpackage::PkgManager::ReleasePackageInstance(pkgManager);
return -1;
}
std::unique_ptr<BlocksStreamPatch> patch = std::make_unique<BlocksStreamPatch>(patchInfo, stream, writer);
if (patch == nullptr) {
PATCH_LOGE("Failed to creare patch ");
pkgManager->ClosePkgStream(stream);
Hpackage::PkgManager::ReleasePackageInstance(pkgManager);
return -1;
}
ret = patch->ApplyPatch();
pkgManager->ClosePkgStream(stream);
Hpackage::PkgManager::ReleasePackageInstance(pkgManager);
return ret;
}
int32_t UpdateApplyPatch::ApplyBlockPatch(const PatchBuffer &patchInfo,
const BlockBuffer &oldInfo, ImageProcessor writer, const std::string& expected)
{
if (writer == nullptr) {
PATCH_LOGE("ApplyBlockPatch : processor is null");
return -1;
}
std::unique_ptr<ImagePatchWriter> patchWriter = std::make_unique<ImagePatchWriter>(writer, expected, "");
if (patchWriter == nullptr) {
PATCH_LOGE("ApplyBlockPatch : Failed to create patch writer");
return -1;
}
int32_t ret = patchWriter->Init();
if (ret != 0) {
PATCH_LOGE("ApplyBlockPatch : Failed to init patch writer");
return -1;
}
PkgManager* pkgManager = Hpackage::PkgManager::CreatePackageInstance();
if (pkgManager == nullptr) {
PATCH_LOGE("ApplyBlockPatch ::Failed to get pkg manager");
return -1;
}
Hpackage::PkgManager::StreamPtr stream = nullptr;
ret = pkgManager->CreatePkgStream(stream, "", {oldInfo.buffer, oldInfo.length});
if (stream == nullptr) {
PATCH_LOGE("Failed to create stream");
pkgManager->ClosePkgStream(stream);
Hpackage::PkgManager::ReleasePackageInstance(pkgManager);
return -1;
}
std::unique_ptr<BlocksStreamPatch> patch = std::make_unique<BlocksStreamPatch>(patchInfo,
stream, patchWriter.get());
if (patch == nullptr) {
PATCH_LOGE("Failed to creare patch ");
pkgManager->ClosePkgStream(stream);
Hpackage::PkgManager::ReleasePackageInstance(pkgManager);
return -1;
}
ret = patch->ApplyPatch();
pkgManager->ClosePkgStream(stream);
Hpackage::PkgManager::ReleasePackageInstance(pkgManager);
if (ret != 0) {
PATCH_LOGE("Failed to applay patch ");
return -1;
}
return patchWriter->Finish();
}
int32_t UpdateApplyPatch::ApplyBlockPatch(const PatchBuffer &patchInfo,
Hpackage::PkgManager::StreamPtr stream, UpdatePatchWriterPtr writer)
{
std::unique_ptr<BlocksStreamPatch> patch = std::make_unique<BlocksStreamPatch>(patchInfo, stream, writer);
if (patch == nullptr) {
PATCH_LOGE("Failed to creare patch ");
return -1;
}
return patch->ApplyPatch();
}
int32_t UpdateApplyPatch::ApplyPatch(const std::string &patchName,
const std::string &oldName, const std::string &newName)
{
PATCH_DEBUG("UpdatePatch::ApplyPatch : %s ", patchName.c_str());
std::vector<uint8_t> empty;
MemMapInfo patchData {};
MemMapInfo oldData {};
if (PatchMapFile(patchName, patchData) != 0) {
PATCH_LOGE("ApplyPatch : Failed to read patch file");
return -1;
}
if (PatchMapFile(oldName, oldData) != 0) {
PATCH_LOGE("ApplyPatch : Failed to read old file");
return -1;
}
PATCH_LOGI("UpdatePatch::ApplyPatch patchData %zu oldData %zu ", patchData.length, oldData.length);
std::unique_ptr<FilePatchWriter> writer = std::make_unique<FilePatchWriter>(newName);
if (writer == nullptr) {
PATCH_LOGE("Failed to create writer");
return -1;
}
writer->Init();
if (patchData.length < std::char_traits<char>::length(PKGDIFF_MAGIC)) {
PATCH_LOGE("length error");
return -1;
}
if (memcmp(patchData.memory, PKGDIFF_MAGIC, std::char_traits<char>::length(PKGDIFF_MAGIC)) == 0) {
PatchParam param {};
param.patch = patchData.memory;
param.patchSize = patchData.length;
param.oldBuff = oldData.memory;
param.oldSize = oldData.length;
if (UpdatePatch::UpdateApplyPatch::ApplyImagePatch(param, writer.get(), empty) != 0) {
PATCH_LOGE("Failed to apply image patch file");
return -1;
}
} else if (memcmp(patchData.memory, BSDIFF_MAGIC, std::char_traits<char>::length(BSDIFF_MAGIC)) == 0) {
PatchBuffer patchInfo = {patchData.memory, 0, patchData.length};
BlockBuffer oldInfo = {oldData.memory, oldData.length};
if (ApplyBlockPatch(patchInfo, oldInfo, writer.get()) != 0) {
PATCH_LOGE("Failed to apply block patch");
return -1;
}
} else {
PATCH_LOGE("Invalid patch file");
return -1;
}
writer->Finish();
return 0;
}
int32_t ImagePatchWriter::Init()
{
if (init_) {
PATCH_LOGE("Has beed init");
return -1;
}
if (writer_ == nullptr) {
PATCH_LOGE("Writer is null");
return -1;
}
SHA256_Init(&sha256Ctx_);
init_ = true;
return 0;
}
int32_t ImagePatchWriter::Write(size_t start, const BlockBuffer &buffer, size_t len)
{
if (!init_) {
PATCH_LOGE("Failed to check init");
return -1;
}
if (len == 0) {
return 0;
}
SHA256_Update(&sha256Ctx_, buffer.buffer, len);
return writer_(start, buffer, len);
}
int32_t ImagePatchWriter::Finish()
{
if (!init_) {
PATCH_LOGE("Failed to check init");
return -1;
}
std::vector<uint8_t> digest(SHA256_DIGEST_LENGTH);
SHA256_Final(digest.data(), &sha256Ctx_);
BlockBuffer data = { digest.data(), digest.size() };
std::string hexDigest = ConvertSha256Hex(data);
init_ = false;
int32_t ret = hexDigest.compare(expected_);
if (ret != 0) {
PATCH_LOGE("VerifySha256 SHA256 : %s expected SHA256 : %s", hexDigest.c_str(), expected_.c_str());
return ret;
}
return 0;
}
int32_t FilePatchWriter::Init()
{
if (init_) {
PATCH_LOGE("Has beed init");
return -1;
}
if (!stream_.is_open()) {
stream_.open(newFileName_, std::ios::out | std::ios::binary);
if (stream_.fail()) {
PATCH_LOGE("Failed to open %s", newFileName_.c_str());
return -1;
}
}
init_ = true;
return 0;
}
int32_t FilePatchWriter::Write(size_t start, const BlockBuffer &buffer, size_t len)
{
if (!init_) {
PATCH_LOGE("Failed to check init");
return -1;
}
if (len == 0) {
return 0;
}
if (!stream_.is_open()) {
stream_.open(newFileName_, std::ios::out | std::ios::binary);
if (stream_.fail()) {
PATCH_LOGE("Failed to open %s", newFileName_.c_str());
return -1;
}
}
stream_.write(reinterpret_cast<const char*>(buffer.buffer), len);
return 0;
}
int32_t FilePatchWriter::Finish()
{
if (!init_) {
PATCH_LOGE("Failed to check init");
return -1;
}
PATCH_LOGI("FilePatchWriter %zu", static_cast<size_t>(stream_.tellp()));
stream_.close();
init_ = false;
return 0;
}
}