* 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 "applypatch/transfer_manager.h"
#include <fcntl.h>
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include "applypatch/command_function.h"
#include "applypatch/command_iterator.h"
#include "log/log.h"
#include "updater/updater_const.h"
#include "utils.h"
#include "applypatch/update_progress.h"
#include "thread_pool.h"
namespace Updater {
using namespace Updater::Utils;
constexpr static size_t TRANSFER_VERSION_IDX = 0;
constexpr static size_t TRANSFER_BLOCK_COUNT_IDX = 1;
constexpr static size_t TRANSFER_MAX_ENTRIES_IDX = 2;
constexpr static size_t TRANSFER_MAX_BLOCKS_IDX = 3;
TransferManager::TransferManager()
{
transferParams_ = std::make_unique<TransferParams>();
transferParams_->writerThreadInfo = std::make_unique<WriterThreadInfo>();
}
bool TransferManager::CommandsExecute(int sourceFd, int targetFd, Command &cmd)
{
cmd.SetSrcFileDescriptor(sourceFd);
cmd.SetTargetFileDescriptor(targetFd);
CommandFunction* cf = CommandFunctionFactory::GetInstance().GetCommandFunction(cmd.GetCommandHead());
if (cf == nullptr) {
LOG(ERROR) << "Failed to get cmd exec";
return false;
}
CommandResult ret = cf->Execute(cmd);
if (!cmd.GetTransferParams()->canWrite) {
return ret == SUCCESS;
}
if (!CheckResult(ret, cmd.GetCommandLine(), cmd.GetCommandType())) {
return false;
}
return true;
}
static bool JudgeBlockVerifyCmdType(Command &cmd)
{
if (cmd.GetCommandType() == CommandType::NEW ||
cmd.GetCommandType() == CommandType::ERASE ||
cmd.GetCommandType() == CommandType::FREE ||
cmd.GetCommandType() == CommandType::ZERO) {
return false;
}
return true;
}
bool TransferManager::InitCommandParser(const std::vector<std::string> &headers, std::string &retryCmd)
{
if (headers.size() != TRANSFER_HEADERS_COUNT) {
LOG(ERROR) << "header size invalid " << headers.size();
return false;
}
transferParams_->version = Utils::String2Int<size_t>(headers[TRANSFER_VERSION_IDX], Utils::N_DEC);
transferParams_->blockCount = Utils::String2Int<size_t>(headers[TRANSFER_BLOCK_COUNT_IDX], Utils::N_DEC);
transferParams_->maxEntries = Utils::String2Int<size_t>(headers[TRANSFER_MAX_ENTRIES_IDX], Utils::N_DEC);
transferParams_->maxBlocks = Utils::String2Int<size_t>(headers[TRANSFER_MAX_BLOCKS_IDX], Utils::N_DEC);
if (transferParams_->env != nullptr && transferParams_->env->IsRetry()) {
retryCmd = ReloadForRetry();
}
return true;
}
bool TransferManager::CommandParserPreCheck(CommandIterator &cmdIter)
{
if (!cmdIter->Check()) {
LOG(ERROR) << "command iterator check failed";
return false;
}
if (transferParams_ == nullptr) {
LOG(ERROR) << "transferParams_ is nullptr";
return false;
}
return true;
}
bool TransferManager::CommandsParser(int fd, const std::vector<std::string> &context)
{
return CommandsParser(fd, fd, context);
}
bool TransferManager::CommandsParser(int sourceFd, int targetFd, const std::vector<std::string> &context)
{
CommandIterator cmdIter(context.cbegin() + TRANSFER_HEADERS_COUNT, context.cend());
return CommandsParser(sourceFd, targetFd, std::vector<std::string> {context.cbegin(),
context.cbegin() + TRANSFER_HEADERS_COUNT}, cmdIter);
}
bool TransferManager::CommandsParser(int sourceFd, int targetFd, const std::vector<std::string> &headers,
CommandIterator &cmdIter, bool isStream)
{
std::string retryCmd = "";
if (!CommandParserPreCheck(cmdIter) || !InitCommandParser(headers, retryCmd)) {
return false;
}
size_t totalSize = transferParams_->blockCount;
size_t initBlock = 0;
for (cmdIter.Start(); !cmdIter.Done(); cmdIter.Next()) {
std::unique_ptr<Command> cmd = std::make_unique<Command>(transferParams_.get());
if (cmd == nullptr) {
LOG(ERROR) << "Failed to parse command line.";
return false;
}
cmd->SetIsStreamCmd(isStream);
if (!cmd->Init(*cmdIter) || transferParams_->env == nullptr) {
continue;
}
if (!retryCmd.empty() && transferParams_->env->IsRetry()) {
if (*cmdIter == retryCmd) {
LOG(INFO) << "Start Retry from Command: " << retryCmd;
retryCmd.clear();
}
if (cmd->GetCommandType() != CommandType::NEW) {
LOG(DEBUG) << "Retry: Command " << *cmdIter << " passed";
continue;
}
}
if (!transferParams_->canWrite && !JudgeBlockVerifyCmdType(*cmd)) {
continue;
}
if (!CommandsExecute(sourceFd, targetFd, *cmd)) {
LOG(ERROR) << "Running command : " << cmd->GetCommandLine() << " fail";
return false;
}
if (!transferParams_->canWrite) {
continue;
}
if (initBlock == 0) {
initBlock = transferParams_->written;
}
if (totalSize != 0 && (transferParams_->written - initBlock) > 0) {
UpdateProgress(initBlock, totalSize);
}
}
if (fabs(Uscript::GetScriptProportion() - 1.0f) < 1e-6) {
FillUpdateProgress();
}
return true;
}
void TransferManager::UpdateProgress(size_t &initBlock, size_t totalSize)
{
float p = static_cast<float>(transferParams_->written - initBlock) / totalSize\
* Uscript::GetScriptProportion() * Uscript::GetTotalProportion();
SetUpdateProgress(p);
initBlock = transferParams_->written;
}
bool TransferManager::RegisterForRetry(const std::string &cmd)
{
std::string path = transferParams_->retryFile;
int fd = open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
if (fd == -1) {
LOG(ERROR) << "Failed to create";
return false;
}
fdsan_exchange_owner_tag(fd, 0, FDSAN_UPDATER_TAG);
bool ret = Utils::WriteStringToFile(fd, cmd);
if (ret == false) {
LOG(ERROR) << "Write retry flag error";
}
fsync(fd);
fdsan_close_with_tag(fd, FDSAN_UPDATER_TAG);
return ret;
}
std::string TransferManager::ReloadForRetry() const
{
std::string path = transferParams_->retryFile;
int fd = open(path.c_str(), O_RDONLY);
if (fd < 0) {
LOG(ERROR) << "Failed to open";
return "";
}
fdsan_exchange_owner_tag(fd, 0, FDSAN_UPDATER_TAG);
(void)lseek(fd, 0, SEEK_SET);
std::string cmd = "";
if (!Utils::ReadFileToString(fd, cmd)) {
LOG(ERROR) << "Error to read retry flag";
}
fdsan_close_with_tag(fd, FDSAN_UPDATER_TAG);
return cmd;
}
bool TransferManager::CheckResult(const CommandResult result, const std::string &cmd, const CommandType &type)
{
switch (result) {
case SUCCESS:
if (type != CommandType::NEW) {
RegisterForRetry(cmd);
}
break;
case NEED_RETRY:
LOG(INFO) << "IO failed. Running command need retry!";
if (transferParams_->env != nullptr) {
transferParams_->env->PostMessage("retry_update", IO_FAILED_REBOOT);
}
return false;
case FAILED:
LOG(INFO) << "Block update failed. Running command need retry!";
if (transferParams_->env != nullptr) {
transferParams_->env->PostMessage("retry_update", BLOCK_UPDATE_FAILED_REBOOT);
}
return false;
default:
LOG(ERROR) << "Running command failed";
return false;
}
return true;
}
}