* 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 "update_image_block.h"
#include <cerrno>
#include <fcntl.h>
#include <fstream>
#include <pthread.h>
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <thread>
#include <unistd.h>
#include "applypatch/block_set.h"
#include "applypatch/store.h"
#include "applypatch/transfer_manager.h"
#include "applypatch/partition_record.h"
#include "fs_manager/mount.h"
#include "log/dump.h"
#include "log/log.h"
#ifdef UPDATER_USE_PTABLE
#include "ptable_manager.h"
#endif
#include "updater/updater_const.h"
#include "updater/hardware_fault_retry.h"
#include "utils.h"
#include "slot_info/slot_info.h"
#include "uncache.h"
using namespace Uscript;
using namespace Hpackage;
using namespace Updater;
using namespace Updater::Utils;
namespace Updater {
constexpr int32_t SHA_CHECK_SECOND = 2;
constexpr int32_t SHA_CHECK_PARAMS = 3;
constexpr int32_t SHA_CHECK_TARGETPAIRS_INDEX = 3;
constexpr int32_t SHA_CHECK_TARGETSHA_INDEX = 4;
constexpr int32_t SHA_CHECK_TARGET_PARAMS = 5;
constexpr int32_t SHA_DELAY_MILLIS_SECOND = 1;
constexpr int32_t SHA_DELAY_CYCLE_COUNT = 250;
constexpr const char *STASHED_PATH = "/data/updater/partitions";
constexpr const char *IMG_SUFFIX = ".img";
__attribute__((weak)) void GetWriteDevPath(const std::string &path, [[maybe_unused]] const std::string &partitionName,
std::string &devPath)
{
devPath = path;
LOG(INFO) << "GetWriteDevPath: " << devPath;
return;
}
__attribute__((weak)) void SyncWriteDevPath(int fd, [[maybe_unused]] const std::string &partitionName)
{
if (fd < 0) {
LOG(ERROR) << "fd is invalid";
return;
}
if (fsync(fd) == -1) {
LOG(ERROR) << "fsync failed for " << partitionName;
}
return;
}
void UpdateFdInfo::Close()
{
if (sourceFd == targetFd && sourceFd != -1) {
close(sourceFd);
sourceFd = -1;
targetFd = -1;
return;
}
if (sourceFd != -1) {
close(sourceFd);
sourceFd = -1;
}
if (targetFd != -1) {
close(targetFd);
targetFd = -1;
}
}
static bool BuildSafeStashedDevPath(const std::string &subPath, std::string &stashedDevPath)
{
const std::string prefix = std::string(STASHED_PATH) + "/";
std::string fullPath = prefix + subPath + std::string(IMG_SUFFIX);
if (!Utils::PathToRealPath(fullPath, stashedDevPath)) {
LOG(WARNING) << "Failed to build stashed dev path for " << subPath;
return false;
}
if (stashedDevPath.find(prefix) != 0) {
LOG(WARNING) << "Invalid stashed dev path for " << subPath;
return false;
}
return true;
}
static std::string GetPartName(const std::string &partitionName)
{
if (partitionName.empty()) {
return "";
}
return partitionName[0] == '/' ? partitionName.substr(1) : partitionName;
}
static int ExtractNewData(const PkgBuffer &buffer, size_t size, size_t start, bool isFinish, const void* context)
{
void *p = const_cast<void *>(context);
WriterThreadInfo *info = static_cast<WriterThreadInfo *>(p);
uint8_t *addr = buffer.buffer;
while (size > 0) {
pthread_mutex_lock(&info->mutex);
while (info->writer == nullptr) {
if (!info->readyToWrite) {
LOG(WARNING) << "writer is not ready to write.";
pthread_mutex_unlock(&info->mutex);
return Hpackage::PKG_INVALID_STREAM;
}
pthread_cond_wait(&info->cond, &info->mutex);
}
pthread_mutex_unlock(&info->mutex);
size_t toWrite = std::min(size, info->writer->GetBlocksSize() - info->writer->GetTotalWritten());
if (toWrite == 0) {
break;
}
bool ret = info->writer->Write(addr, toWrite, nullptr);
std::ostringstream logMessage;
logMessage << "Write " << toWrite << " byte(s) failed";
if (!ret) {
LOG(ERROR) << logMessage.str();
return Hpackage::PKG_INVALID_STREAM;
}
size -= toWrite;
addr += toWrite;
if (info->writer->IsWriteDone()) {
pthread_mutex_lock(&info->mutex);
info->writer.reset();
pthread_cond_broadcast(&info->cond);
pthread_mutex_unlock(&info->mutex);
}
}
return Hpackage::PKG_SUCCESS;
}
static inline void CondBroadcast(WriterThreadInfo *info)
{
pthread_mutex_lock(&info->mutex);
info->readyToWrite = false;
if (info->writer != nullptr) {
pthread_cond_broadcast(&info->cond);
}
pthread_mutex_unlock(&info->mutex);
}
void* UnpackNewData(void *arg)
{
TransferManagerPtr tm = static_cast<TransferManagerPtr>(arg);
WriterThreadInfo *info = tm->GetTransferParams()->writerThreadInfo.get();
Hpackage::PkgManager::StreamPtr stream = nullptr;
if (info->newPatch.empty()) {
LOG(ERROR) << "new patch file name is empty. thread quit.";
CondBroadcast(info);
return nullptr;
}
LOG(DEBUG) << "new patch file name: " << info->newPatch;
auto env = tm->GetTransferParams()->env;
const FileInfo *file = env->GetPkgManager()->GetFileInfo(info->newPatch);
if (file == nullptr) {
LOG(ERROR) << "Cannot get file info of :" << info->newPatch;
CondBroadcast(info);
return nullptr;
}
LOG(DEBUG) << info->newPatch << " info: size " << file->packedSize << " unpacked size " <<
file->unpackedSize << " name " << file->identity;
int32_t ret = env->GetPkgManager()->CreatePkgStream(stream, info->newPatch, ExtractNewData, info);
if (ret != Hpackage::PKG_SUCCESS || stream == nullptr) {
LOG(ERROR) << "Cannot extract " << info->newPatch << " from package.";
CondBroadcast(info);
return nullptr;
}
ret = env->GetPkgManager()->ExtractFile(info->newPatch, stream);
env->GetPkgManager()->ClosePkgStream(stream);
LOG(DEBUG) << "new data writer ending...";
CondBroadcast(info);
return nullptr;
}
int32_t ReturnAndPushParam(int32_t returnValue, Uscript::UScriptContext &context)
{
context.PushParam(returnValue);
return returnValue;
}
int32_t UScriptInstructionBlockUpdate::GetUpdateBlockInfo(struct UpdateBlockInfo &infos, Uscript::UScriptEnv &env,
Uscript::UScriptContext &context)
{
if (context.GetParamCount() != 4) {
LOG(ERROR) << "Invalid param";
return ReturnAndPushParam(USCRIPT_INVALID_PARAM, context);
}
size_t pos = 0;
int32_t ret = context.GetParam(pos++, infos.partitionName);
if (ret != USCRIPT_SUCCESS) {
LOG(ERROR) << "Error to get param 1";
return ret;
}
ret = context.GetParam(pos++, infos.transferName);
if (ret != USCRIPT_SUCCESS) {
LOG(ERROR) << "Error to get param 2";
return ret;
}
ret = context.GetParam(pos++, infos.newDataName);
if (ret != USCRIPT_SUCCESS) {
LOG(ERROR) << "Error to get param 3";
return ret;
}
ret = context.GetParam(pos++, infos.patchDataName);
if (ret != USCRIPT_SUCCESS) {
LOG(ERROR) << "Error to get param 4";
return ret;
}
LOG(INFO) << "ExecuteUpdateBlock::updating " << infos.partitionName << " ...";
infos.devPath = GetBlockDeviceByMountPoint(infos.partitionName);
#ifndef UPDATER_UT
if (infos.partitionName != "/userdata") {
std::string suffix = Utils::GetUpdateSuffix();
infos.devPath += suffix;
}
#else
infos.devPath = "/data/updater" + infos.partitionName;
#endif
LOG(INFO) << "ExecuteUpdateBlock::updating dev path : " << infos.devPath;
if (infos.devPath.empty()) {
LOG(ERROR) << "cannot get block device of partition";
return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
}
return USCRIPT_SUCCESS;
}
bool UScriptInstructionBlockUpdate::ExecuteTransferCommands(TransferManagerPtr tm, const UpdateFdInfo &fdInfo,
const UpdateBlockInfo &infos, const std::vector<std::string> &lines)
{
if (tm == nullptr) {
LOG(ERROR) << "TransferManager is nullptr";
return false;
}
return tm->CommandsParser(fdInfo.sourceFd, fdInfo.targetFd, lines);
}
int32_t UScriptInstructionBlockUpdate::ExecuteTransferCommand(const UpdateFdInfo &fdInfo,
const std::vector<std::string> &lines, TransferManagerPtr tm, Uscript::UScriptContext &context,
const UpdateBlockInfo &infos)
{
auto transferParams = tm->GetTransferParams();
transferParams->storeBase = GetDiffTempPath() + infos.partitionName + "_tmp";
transferParams->retryFile = GetDiffTempPath() + infos.partitionName + "_retry";
transferParams->devPath = infos.devPath;
LOG(INFO) << "Store base path is " << transferParams->storeBase;
int32_t ret = Store::CreateNewSpace(transferParams->storeBase, !transferParams->env->IsRetry());
if (ret == -1) {
LOG(ERROR) << "Error to create new store space";
return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
}
transferParams->storeCreated = ret;
if (!ExecuteTransferCommands(tm, fdInfo, infos, lines)) {
return USCRIPT_ERROR_EXECUTE;
}
JoinThread(tm);
if (transferParams->storeCreated != -1) {
Store::DoFreeSpace(transferParams->storeBase);
}
return USCRIPT_SUCCESS;
}
void UScriptInstructionBlockUpdate::JoinThread(TransferManagerPtr tm)
{
if (tm == nullptr) {
LOG(ERROR) << "transfermanager is null";
return;
}
auto transferParams = tm->GetTransferParams();
auto writerThreadInfo = transferParams->writerThreadInfo.get();
pthread_mutex_lock(&writerThreadInfo->mutex);
if (writerThreadInfo->readyToWrite) {
LOG(WARNING) << "New data writer thread is still available...";
}
writerThreadInfo->readyToWrite = false;
pthread_cond_broadcast(&writerThreadInfo->cond);
pthread_mutex_unlock(&writerThreadInfo->mutex);
int32_t ret = pthread_join(transferParams->thread, nullptr);
std::ostringstream logMessage;
logMessage << "pthread join returned with " << ret;
if (ret != 0) {
LOG(WARNING) << logMessage.str();
}
}
int UScriptInstructionBlockUpdate::InitThread(const struct UpdateBlockInfo &infos, TransferManagerPtr tm)
{
LOG(INFO) << "Ready to start a thread to handle new data processing";
auto transferParams = tm->GetTransferParams();
auto writerThreadInfo = transferParams->writerThreadInfo.get();
writerThreadInfo->readyToWrite = true;
pthread_mutex_init(&writerThreadInfo->mutex, nullptr);
pthread_cond_init(&writerThreadInfo->cond, nullptr);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
writerThreadInfo->newPatch = infos.newDataName;
int error = pthread_create(&transferParams->thread, &attr, UnpackNewData, tm);
return error;
}
int32_t UScriptInstructionBlockUpdate::ExtractDiffPackageAndLoad(const UpdateBlockInfo &infos, Uscript::UScriptEnv &env,
Uscript::UScriptContext &context)
{
Hpackage::PkgManager::StreamPtr outStream = nullptr;
LOG(DEBUG) << "partitionName is " << infos.partitionName;
const FileInfo *info = env.GetPkgManager()->GetFileInfo(infos.partitionName);
if (info == nullptr) {
LOG(WARNING) << "Error to get file info";
return USCRIPT_SUCCESS;
}
std::string diffPackage = std::string(GetDiffTempPath()) + infos.partitionName;
int32_t ret = env.GetPkgManager()->CreatePkgStream(outStream,
diffPackage, info->unpackedSize, PkgStream::PkgStreamType_Write);
if (outStream == nullptr) {
LOG(ERROR) << "Error to create output stream";
return USCRIPT_ERROR_EXECUTE;
}
ret = env.GetPkgManager()->ExtractFile(infos.partitionName, outStream);
if (ret != USCRIPT_SUCCESS) {
LOG(ERROR) << "Error to extract file";
env.GetPkgManager()->ClosePkgStream(outStream);
return USCRIPT_ERROR_EXECUTE;
}
env.GetPkgManager()->ClosePkgStream(outStream);
std::string diffPackageZip = diffPackage + ".zip";
if (rename(diffPackage.c_str(), diffPackageZip.c_str()) != 0) {
LOG(ERROR) << "rename failed";
return USCRIPT_ERROR_EXECUTE;
}
LOG(DEBUG) << "Rename " << diffPackage << " to zip\nExtract " << diffPackage << " done\nReload " << diffPackageZip;
std::vector<std::string> diffPackageComponents;
ret = env.GetPkgManager()->LoadPackage(diffPackageZip, Updater::Utils::GetCertName(), diffPackageComponents);
if (diffPackageComponents.size() < 1) {
LOG(ERROR) << "Diff package is empty";
return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
}
return USCRIPT_SUCCESS;
}
UpdateFdInfo UScriptInstructionBlockUpdate::CreateFdInfo(const UpdateBlockInfo &infos, TransferManagerPtr tm)
{
UpdateFdInfo fdInfo;
std::string devPath = "";
GetWriteDevPath(infos.devPath, infos.partitionName, devPath);
std::string dataDevPath = GetStashedPath(infos);
if (devPath != infos.devPath) {
fdInfo.sourceFd = open(infos.devPath.c_str(), O_RDWR | O_LARGEFILE | O_UNCACHE_FLAG);
fdInfo.targetFd = open(devPath.c_str(), O_RDWR | O_LARGEFILE | O_UNCACHE_FLAG);
tm->GetTransferParams()->inPlaceDiff = false;
return fdInfo;
}
std::string targetPartitionPath = infos.devPath;
size_t targetPartitionOffset = 0;
if (Utils::IsFileExist(dataDevPath) && GetPartitionInfo(infos.partitionName,
targetPartitionPath, targetPartitionOffset) && !targetPartitionPath.empty()) {
fdInfo.sourceFd = open(dataDevPath.c_str(), O_RDWR | O_LARGEFILE | O_UNCACHE_FLAG);
fdInfo.targetFd = open(targetPartitionPath.c_str(), O_RDWR | O_LARGEFILE | O_UNCACHE_FLAG);
tm->GetTransferParams()->offset = targetPartitionOffset;
tm->GetTransferParams()->inPlaceDiff = false;
usedStashPtn_ = true;
return fdInfo;
}
LOG(DEBUG) << "partition " << infos.partitionName;
fdInfo.sourceFd = open(infos.devPath.c_str(), O_RDWR | O_LARGEFILE | O_UNCACHE_FLAG);
fdInfo.targetFd = fdInfo.sourceFd;
return fdInfo;
}
int32_t UScriptInstructionBlockUpdate::DoExecuteUpdateBlock(const UpdateBlockInfo &infos, TransferManagerPtr tm,
Hpackage::PkgManager::StreamPtr &outStream, const std::vector<std::string> &lines, Uscript::UScriptContext &context)
{
UpdateFdInfo fdInfo = CreateFdInfo(infos, tm);
auto env = tm->GetTransferParams()->env;
if (fdInfo.targetFd == -1 || fdInfo.sourceFd == -1) {
LOG(ERROR) << "Failed to open block";
fdInfo.Close();
env->GetPkgManager()->ClosePkgStream(outStream);
return USCRIPT_ERROR_EXECUTE;
}
int32_t ret = ExecuteTransferCommand(fdInfo, lines, tm, context, infos);
SyncWriteDevPath(fdInfo.targetFd, infos.partitionName);
fdInfo.Close();
env->GetPkgManager()->ClosePkgStream(outStream);
if (ret == USCRIPT_SUCCESS) {
HandleUpdateSuccess(infos);
}
return ret;
}
static int32_t ExtractFileByName(Uscript::UScriptEnv &env, const std::string &fileName,
Hpackage::PkgManager::StreamPtr &outStream, uint8_t *&outBuf, size_t &buffSize)
{
if (env.GetPkgManager() == nullptr) {
LOG(ERROR) << "Error to get pkg manager";
return USCRIPT_ERROR_EXECUTE;
}
const FileInfo *info = env.GetPkgManager()->GetFileInfo(fileName);
if (info == nullptr) {
LOG(ERROR) << "GetFileInfo fail";
return USCRIPT_ERROR_EXECUTE;
}
auto ret = env.GetPkgManager()->CreatePkgStream(outStream,
fileName, info->unpackedSize, PkgStream::PkgStreamType_MemoryMap);
if (ret != USCRIPT_SUCCESS || outStream == nullptr) {
LOG(ERROR) << "Error to create output stream";
return USCRIPT_ERROR_EXECUTE;
}
ret = env.GetPkgManager()->ExtractFile(fileName, outStream);
if (ret != USCRIPT_SUCCESS) {
LOG(ERROR) << "Error to extract file";
env.GetPkgManager()->ClosePkgStream(outStream);
return USCRIPT_ERROR_EXECUTE;
}
ret = outStream->GetBuffer(outBuf, buffSize);
LOG(DEBUG) << "outBuf data size is: " << buffSize;
return USCRIPT_SUCCESS;
}
static int32_t ExtractPatchDatFile(Uscript::UScriptEnv &env, const UpdateBlockInfo &infos,
Hpackage::PkgManager::StreamPtr &outStream, std::string &datFile)
{
if (env.GetPkgManager() == nullptr) {
LOG(ERROR) << "Error to get pkg manager";
return USCRIPT_ERROR_EXECUTE;
}
if ((!Utils::IsDirExist(GetDiffTempPath())) && (Utils::MkdirRecursive(GetDiffTempPath(), S_IRWXU) != 0)) {
LOG(ERROR) << "Failed to make path";
return USCRIPT_ERROR_EXECUTE;
}
std::string fileName = std::string(GetDiffTempPath()) + "/" + infos.patchDataName;
auto ret = env.GetPkgManager()->CreatePkgStream(outStream, fileName, 0, PkgStream::PkgStreamType_Write);
if (ret != USCRIPT_SUCCESS || outStream == nullptr) {
LOG(ERROR) << "Error to create output stream";
return USCRIPT_ERROR_EXECUTE;
}
ret = env.GetPkgManager()->ExtractFile(infos.patchDataName, outStream);
if (ret != USCRIPT_SUCCESS) {
LOG(ERROR) << "Error to extract file";
env.GetPkgManager()->ClosePkgStream(outStream);
return USCRIPT_ERROR_EXECUTE;
}
datFile = fileName;
return USCRIPT_SUCCESS;
}
int32_t UScriptInstructionBlockUpdate::ExtractPatchFile(Uscript::UScriptEnv &env, const UpdateBlockInfo &infos,
Hpackage::PkgManager::StreamPtr outStream, TransferParams *transferParams)
{
if (transferParams == nullptr) {
LOG(ERROR) << "transfer param is null";
return USCRIPT_ERROR_EXECUTE;
}
LOG(INFO) << "Start unpack new data thread done. Get patch data: " << infos.patchDataName;
transferParams->isUpdaterMode = Utils::IsUpdaterMode();
return transferParams->isUpdaterMode ? ExtractFileByName(env, infos.patchDataName, outStream,
transferParams->dataBuffer, transferParams->dataBufferSize) : ExtractPatchDatFile(env,
infos, outStream, transferParams->patchDatFile);
}
int32_t UScriptInstructionBlockUpdate::ExecuteUpdateBlock(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
{
UpdateBlockInfo infos {};
if (GetUpdateBlockInfo(infos, env, context) != USCRIPT_SUCCESS) {
return USCRIPT_ERROR_EXECUTE;
}
if (env.IsRetry()) {
LOG(DEBUG) << "Retry updater, check if current partition updatered already during last time";
if (PartitionRecord::GetInstance().IsPartitionUpdated(infos.partitionName)) {
LOG(INFO) << infos.partitionName << " already updated, skip";
return USCRIPT_SUCCESS;
}
}
if (ExtractDiffPackageAndLoad(infos, env, context) != USCRIPT_SUCCESS) {
return USCRIPT_ERROR_EXECUTE;
}
uint8_t *transferListBuffer = nullptr;
size_t transferListSize = 0;
Hpackage::PkgManager::StreamPtr outStream = nullptr;
if (ExtractFileByName(env, infos.transferName, outStream,
transferListBuffer, transferListSize) != USCRIPT_SUCCESS) {
return USCRIPT_ERROR_EXECUTE;
}
std::unique_ptr<TransferManager> tm = std::make_unique<TransferManager>();
auto transferParams = tm->GetTransferParams();
transferParams->env = &env;
transferParams->canWrite = true;
std::vector<std::string> lines =
Updater::Utils::SplitString(std::string(reinterpret_cast<const char*>(transferListBuffer)), "\n");
env.GetPkgManager()->ClosePkgStream(outStream);
if (ExtractPatchFile(env, infos, outStream, transferParams) != USCRIPT_SUCCESS) {
return USCRIPT_ERROR_EXECUTE;
}
if (InitThread(infos, tm.get()) != 0) {
LOG(ERROR) << "Failed to create pthread";
env.GetPkgManager()->ClosePkgStream(outStream);
return USCRIPT_ERROR_EXECUTE;
}
std::vector<pid_t> tids = GetAllTids(getpid());
env.PostMessage("set_binary_tids", VectorToString(tids));
return DoExecuteUpdateBlock(infos, tm.get(), outStream, lines, context);
}
int32_t UScriptInstructionBlockUpdate::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
{
int32_t result = ExecuteUpdateBlock(env, context);
context.PushParam(result);
return result;
}
bool UScriptInstructionBlockUpdate::GetPartitionInfo(const std::string &partition, std::string &partitionPath,
size_t &partitionOffset) const
{
#ifdef UPDATER_USE_PTABLE
DevicePtable &devicePtb = DevicePtable::GetInstance();
Ptable::PtnInfo ptnInfo;
if (!devicePtb.GetPartionInfoByName(partition, ptnInfo)) {
return false;
}
partitionPath = ptnInfo.writePath;
partitionOffset = ptnInfo.startAddr;
#endif
return true;
}
void UScriptInstructionBlockUpdate::HandleUpdateSuccess(const UpdateBlockInfo &infos) const
{
PartitionRecord::GetInstance().RecordPartitionUpdateStatus(infos.partitionName, true);
#ifndef UPDATER_UT
if (!usedStashPtn_) {
return;
}
std::string dataDevPath = GetStashedPath(infos);
if (!Utils::DeleteFile(dataDevPath)) {
LOG(WARNING) << "Failed to delete stashed partition " << dataDevPath;
}
std::string doneListPath;
if (!BuildSafeStashedDevPath("done.list", doneListPath)) {
LOG(WARNING) << "Failed to build done.list path";
return;
}
std::ofstream fout(doneListPath, std::ios::app | std::ios::out);
if (!fout.is_open()) {
LOG(WARNING) << "Failed to open done.list";
return;
}
fout << GetPartName(infos.partitionName) + std::string(IMG_SUFFIX) << "\n";
fout.flush();
fout.close();
int fd = open(doneListPath.c_str(), O_RDWR);
if (fd < 0) {
LOG(WARNING) << "Failed to sync done.list";
} else {
fsync(fd);
close(fd);
fd = -1;
}
LOG(INFO) << "Update partition " << infos.partitionName << " success";
#endif
}
std::string UScriptInstructionBlockUpdate::GetStashedPath(const UpdateBlockInfo &infos) const
{
std::string dataDevPath;
BuildSafeStashedDevPath(GetPartName(infos.partitionName), dataDevPath);
return dataDevPath;
}
bool UScriptInstructionBlockCheck::ExecReadBlockInfo(const std::string &devPath, Uscript::UScriptContext &context,
time_t &mountTime, uint16_t &mountCount)
{
UPDATER_INIT_RECORD;
int fd = open(devPath.c_str(), O_RDWR | O_LARGEFILE | O_UNCACHE_FLAG);
if (fd == -1) {
LOG(ERROR) << "Failed to open file";
UPDATER_LAST_WORD("Failed to open file", devPath);
return false;
}
std::vector<uint8_t> block_buff(H_BLOCK_SIZE);
BlockSet blk0(std::vector<BlockPair> {BlockPair{0, 1}});
size_t pos = 0;
std::vector<BlockPair>::iterator it = blk0.Begin();
for (; it != blk0.End(); ++it) {
LOG(INFO) << "BlockSet::ReadDataFromBlock lseek64";
if (lseek64(fd, static_cast<off64_t>(it->first * H_BLOCK_SIZE), SEEK_SET) == -1) {
LOG(ERROR) << "Failed to seek";
close(fd);
UPDATER_LAST_WORD(false, "Failed to seek");
return false;
}
size_t size = (it->second - it->first) * H_BLOCK_SIZE;
LOG(INFO) << "BlockSet::ReadDataFromBlock Read " << size << " from block";
if (!Utils::ReadFully(fd, block_buff.data() + pos, size)) {
LOG(ERROR) << "Failed to read";
close(fd);
UPDATER_LAST_WORD(false, "Failed to read");
return false;
}
pos += size;
}
close(fd);
mountTime = *reinterpret_cast<uint32_t *>(&block_buff[0x400 + 0x2C]);
mountCount = *reinterpret_cast<uint16_t *>(&block_buff[0x400 + 0x34]);
return true;
}
int32_t UScriptInstructionBlockCheck::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
{
UPDATER_INIT_RECORD;
if (context.GetParamCount() != 1) {
LOG(ERROR) << "Invalid param";
UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
return ReturnAndPushParam(USCRIPT_INVALID_PARAM, context);
}
if (env.IsRetry()) {
return ReturnAndPushParam(USCRIPT_SUCCESS, context);
}
std::string partitionName;
int32_t ret = context.GetParam(0, partitionName);
if (ret != USCRIPT_SUCCESS) {
LOG(ERROR) << "Failed to get param";
UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, "Failed to get param " + partitionName);
return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
}
auto devPath = GetBlockDeviceByMountPoint(partitionName);
#ifndef UPDATER_UT
if (partitionName != "/userdata") {
std::string suffix = Utils::GetUpdateSuffix();
devPath += suffix;
}
#else
devPath = "/data/updater" + partitionName;
#endif
std::string stashedDevPath;
if (BuildSafeStashedDevPath(partitionName, stashedDevPath) && Utils::IsFileExist(stashedDevPath)) {
devPath = stashedDevPath;
}
LOG(INFO) << "UScriptInstructionBlockCheck::dev path : " << devPath;
time_t mountTime = 0;
uint16_t mountCount = 0;
if (devPath.empty() || (!ExecReadBlockInfo(devPath, context, mountTime, mountCount))) {
LOG(ERROR) << "cannot get block device of partition";
UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, devPath);
return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
}
if (mountCount > 0) {
std::ostringstream ostr;
ostr << "Device was remounted R/W " << mountCount << "times\nLast remount happened on " <<
ctime(&mountTime) << std::endl;
std::string message = ostr.str();
env.PostMessage("ui_log", message);
LOG(ERROR) << message;
}
LOG(INFO) << "UScriptInstructionBlockCheck::Execute Success";
context.PushParam(USCRIPT_SUCCESS);
return USCRIPT_SUCCESS;
}
int32_t UScriptInstructionShaCheck::DoBlocksVerify(Uscript::UScriptEnv &env, const std::string &partitionName,
const std::string &devPath)
{
UpdateBlockInfo infos {};
infos.partitionName = partitionName;
infos.transferName = GetPartName(partitionName) + ".transfer.list";
uint8_t *transferListBuffer = nullptr;
size_t transferListSize = 0;
Hpackage::PkgManager::StreamPtr outStream = nullptr;
if (ExtractFileByName(env, infos.transferName, outStream,
transferListBuffer, transferListSize) != USCRIPT_SUCCESS) {
LOG(ERROR) << "Error to extract " << infos.transferName;
return USCRIPT_ERROR_EXECUTE;
}
std::vector<std::string> lines =
Updater::Utils::SplitString(std::string(reinterpret_cast<const char*>(transferListBuffer)), "\n");
env.GetPkgManager()->ClosePkgStream(outStream);
std::unique_ptr<TransferManager> tm = std::make_unique<TransferManager>();
auto transferParams = tm->GetTransferParams();
transferParams->env = &env;
transferParams->canWrite = false;
transferParams->devPath = devPath;
transferParams->storeBase = GetDiffTempPath() + partitionName + "_tmp";
transferParams->retryFile = GetDiffTempPath() + partitionName + "_retry";
LOG(INFO) << "Store base path is " << transferParams->storeBase;
transferParams->storeCreated = Store::CreateNewSpace(transferParams->storeBase, !transferParams->env->IsRetry());
if (transferParams->storeCreated == -1) {
LOG(ERROR) << "Error to create new store space";
return USCRIPT_ERROR_EXECUTE;
}
int fd = open(devPath.c_str(), O_RDWR | O_LARGEFILE | O_UNCACHE_FLAG);
if (fd == -1) {
LOG(ERROR) << "Failed to open block";
return USCRIPT_ERROR_EXECUTE;
}
if (!tm->CommandsParser(fd, fd, lines)) {
close(fd);
LOG(ERROR) << "Failed to block verify";
return USCRIPT_ERROR_EXECUTE;
}
if (transferParams->storeCreated != -1) {
Store::DoFreeSpace(transferParams->storeBase);
}
close(fd);
return USCRIPT_SUCCESS;
}
bool UScriptInstructionShaCheck::IsTargetShaDiff(const std::string &devPath, const ShaInfo &shaInfo)
{
std::string tgtResultSha = CalculateBlockSha(devPath, shaInfo.targetPairs);
if (tgtResultSha.empty()) {
LOG(WARNING) << "target sha is empty";
return true;
}
LOG(INFO) << "tgtResultSha: " << tgtResultSha << ", shaInfo.targetSha: " << shaInfo.targetSha;
return (tgtResultSha != shaInfo.targetSha);
}
int UScriptInstructionShaCheck::ExecReadShaInfo(Uscript::UScriptEnv &env, const std::string &devPath,
const ShaInfo &shaInfo, const std::string &partitionName)
{
UPDATER_INIT_RECORD;
std::string resultSha = CalculateBlockSha(devPath, shaInfo.blockPairs);
if (resultSha != shaInfo.contrastSha && IsTargetShaDiff(devPath, shaInfo)) {
if (DoBlocksVerify(env, partitionName, devPath) != USCRIPT_SUCCESS) {
LOG(ERROR) << "Different sha256, cannot continue";
LOG(ERROR) << "blockPairs:" << shaInfo.blockPairs;
LOG(ERROR) << "resultSha: " << resultSha << ", shaInfo.contrastSha: " << shaInfo.contrastSha;
PrintAbnormalBlockHash(devPath, shaInfo.blockPairs);
UPDATER_LAST_WORD(devPath.substr(devPath.find_last_of("/") + 1), USCRIPT_ERROR_EXECUTE);
env.PostMessage(UPDATER_RETRY_TAG, VERIFY_FAILED_REBOOT);
return USCRIPT_ERROR_EXECUTE;
}
}
LOG(INFO) << "UScriptInstructionShaCheck::Execute Success";
return USCRIPT_SUCCESS;
}
void UScriptInstructionShaCheck::PrintAbnormalBlockHash(const std::string &devPath, const std::string &blockPairs)
{
int fd = open(devPath.c_str(), O_RDWR | O_LARGEFILE | O_UNCACHE_FLAG);
if (fd == -1) {
LOG(ERROR) << "Failed to open file " << devPath;
return;
}
const bool isUpdaterMode = Utils::IsUpdaterMode();
int64_t cycleCount = 0;
BlockSet blk;
blk.ParserAndInsert(blockPairs);
std::vector<uint8_t> block_buff(H_BLOCK_SIZE);
std::vector<BlockPair>::iterator it = blk.Begin();
for (; it != blk.End(); ++it) {
if (lseek64(fd, static_cast<off64_t>(it->first * H_BLOCK_SIZE), SEEK_SET) == -1) {
LOG(ERROR) << "Failed to seek";
close(fd);
return;
}
SHA256_CTX ctx;
SHA256_Init(&ctx);
for (size_t i = it->first; i < it->second; ++i) {
if (!Utils::ReadFully(fd, block_buff.data(), H_BLOCK_SIZE)) {
LOG(ERROR) << "Failed to read";
close(fd);
return;
}
SHA256_Update(&ctx, block_buff.data(), H_BLOCK_SIZE);
++cycleCount;
if (!isUpdaterMode && cycleCount % SHA_DELAY_CYCLE_COUNT == 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(SHA_DELAY_MILLIS_SECOND));
}
}
uint8_t digest[SHA256_DIGEST_LENGTH] = {0};
SHA256_Final(digest, &ctx);
LOG(ERROR) << "block id:" << it->first << "-" << it->second <<
" hex:" << Utils::ConvertSha256Hex(digest, SHA256_DIGEST_LENGTH);
}
close(fd);
}
std::string UScriptInstructionShaCheck::CalculateBlockSha(const std::string &devPath, const std::string &blockPairs)
{
UPDATER_INIT_RECORD;
if (blockPairs.empty()) {
LOG(ERROR) << "Failed to get blockPairs";
return "";
}
int fd = open(devPath.c_str(), O_RDWR | O_LARGEFILE | O_UNCACHE_FLAG);
if (fd == -1) {
LOG(ERROR) << "Failed to open file";
UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, devPath);
return "";
}
const bool isUpdaterMode = Utils::IsUpdaterMode();
int64_t cycleCount = 0;
BlockSet blk;
blk.ParserAndInsert(blockPairs);
std::vector<uint8_t> block_buff(H_BLOCK_SIZE);
SHA256_CTX ctx;
SHA256_Init(&ctx);
std::vector<BlockPair>::iterator it = blk.Begin();
for (; it != blk.End(); ++it) {
if (lseek64(fd, static_cast<off64_t>(it->first * H_BLOCK_SIZE), SEEK_SET) == -1) {
LOG(ERROR) << "Failed to seek";
close(fd);
UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, "Failed to seek");
return "";
}
for (size_t i = it->first; i < it->second; ++i) {
if (!Utils::ReadFully(fd, block_buff.data(), H_BLOCK_SIZE)) {
LOG(ERROR) << "Failed to read";
close(fd);
UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, "Failed to read");
return "";
}
SHA256_Update(&ctx, block_buff.data(), H_BLOCK_SIZE);
++cycleCount;
if (!isUpdaterMode && cycleCount % SHA_DELAY_CYCLE_COUNT == 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(SHA_DELAY_MILLIS_SECOND));
}
}
}
close(fd);
uint8_t digest[SHA256_DIGEST_LENGTH] = {0};
SHA256_Final(digest, &ctx);
return Utils::ConvertSha256Hex(digest, SHA256_DIGEST_LENGTH);
}
int32_t UScriptInstructionShaCheck::SetShaInfo(Uscript::UScriptContext &context, ShaInfo &shaInfo)
{
UPDATER_INIT_RECORD;
int32_t ret = context.GetParam(1, shaInfo.blockPairs);
if (ret != USCRIPT_SUCCESS) {
LOG(ERROR) << "Failed to get param blockPairs";
UPDATER_LAST_WORD(ret, "Failed to get param blockPairs");
return USCRIPT_ERROR_EXECUTE;
}
ret = context.GetParam(SHA_CHECK_SECOND, shaInfo.contrastSha);
if (ret != USCRIPT_SUCCESS) {
LOG(ERROR) << "Failed to get param contrastSha";
UPDATER_LAST_WORD(ret, "Failed to get param contrastSha");
return USCRIPT_ERROR_EXECUTE;
}
ret = context.GetParam(SHA_CHECK_TARGETPAIRS_INDEX, shaInfo.targetPairs);
if (ret != USCRIPT_SUCCESS) {
LOG(WARNING) << "Failed to get param targetPairs";
}
ret = context.GetParam(SHA_CHECK_TARGETSHA_INDEX, shaInfo.targetSha);
if (ret != USCRIPT_SUCCESS) {
LOG(WARNING) << "Failed to get param targetSha";
}
return USCRIPT_SUCCESS;
}
int32_t UScriptInstructionShaCheck::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
{
UPDATER_INIT_RECORD;
int32_t paramCount = context.GetParamCount();
if (paramCount != SHA_CHECK_PARAMS && paramCount != SHA_CHECK_TARGET_PARAMS) {
LOG(ERROR) << "Invalid param";
UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM, "Invalid param");
return ReturnAndPushParam(USCRIPT_INVALID_PARAM, context);
}
if (env.IsRetry() && Utils::IsUpdaterMode() && !Utils::CheckFaultInfo(VERIFY_FAILED_REBOOT)) {
return ReturnAndPushParam(USCRIPT_SUCCESS, context);
}
std::string partitionName;
int32_t ret = context.GetParam(0, partitionName);
if (ret != USCRIPT_SUCCESS) {
LOG(ERROR) << "Failed to get param";
UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, "Failed to get param");
return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
}
ShaInfo shaInfo {};
ret = SetShaInfo(context, shaInfo);
if (ret != USCRIPT_SUCCESS) {
LOG(ERROR) << "Failed to set sha info";
return ReturnAndPushParam(ret, context);
}
auto devPath = GetBlockDeviceByMountPoint(partitionName);
LOG(INFO) << "UScriptInstructionShaCheck::dev path : " << devPath;
if (devPath.empty()) {
LOG(ERROR) << "cannot get block device of partition";
UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE, "cannot get block device of partition");
return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
}
#ifndef UPDATER_UT
if (partitionName != "/userdata") {
std::string suffix = Utils::GetUpdateSuffix();
devPath += suffix;
}
#else
devPath = "/data/updater" + partitionName;
#endif
std::string stashedDevPath;
if (BuildSafeStashedDevPath(partitionName, stashedDevPath) && Utils::IsFileExist(stashedDevPath)) {
devPath = stashedDevPath;
}
LOG(INFO) << "write partition path: " << devPath;
ret = ExecReadShaInfo(env, devPath, shaInfo, partitionName);
return ReturnAndPushParam(ret, context);
}
}