1/* 2 * Copyright (c) 2021 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "command_process.h" 17#include <cstdio> 18#include <fcntl.h> 19#include <linux/fs.h> 20#include <memory> 21#include <pthread.h> 22#include <sys/ioctl.h> 23#include <sys/stat.h> 24#include <sys/types.h> 25#include <unistd.h> 26#include "applypatch/block_set.h" 27#include "applypatch/block_writer.h" 28#include "applypatch/data_writer.h" 29#include "applypatch/store.h" 30#include "applypatch/transfer_manager.h" 31#include "log/log.h" 32#include "securec.h" 33#include "utils.h" 34 35using namespace Hpackage; 36using namespace Updater::Utils; 37namespace Updater { 38CommandResult AbortCommandFn::Execute(const Command ¶ms) 39{ 40 return SUCCESS; 41} 42 43CommandResult NewCommandFn::Execute(const Command ¶ms) 44{ 45 BlockSet bs; 46 bs.ParserAndInsert(params.GetArgumentByPos(1)); 47 LOG(INFO) << " writing " << bs.TotalBlockSize() << " blocks of new data"; 48 auto writerThreadInfo = params.GetTransferParams()->writerThreadInfo.get(); 49 pthread_mutex_lock(&writerThreadInfo->mutex); 50 writerThreadInfo->writer = std::make_unique<BlockWriter>(params.GetFileDescriptor(), bs); 51 pthread_cond_broadcast(&writerThreadInfo->cond); 52 while (writerThreadInfo->writer != nullptr) { 53 LOG(DEBUG) << "wait for new data write done..."; 54 if (!writerThreadInfo->readyToWrite) { 55 LOG(ERROR) << "writer thread could not write blocks. " << bs.TotalBlockSize() * H_BLOCK_SIZE - 56 writerThreadInfo->writer->GetTotalWritten() << " bytes lost"; 57 pthread_mutex_unlock(&writerThreadInfo->mutex); 58 writerThreadInfo->writer.reset(); 59 writerThreadInfo->writer = nullptr; 60 return FAILED; 61 } 62 LOG(DEBUG) << "Writer already written " << writerThreadInfo->writer->GetTotalWritten() << " byte(s)"; 63 pthread_cond_wait(&writerThreadInfo->cond, &writerThreadInfo->mutex); 64 } 65 pthread_mutex_unlock(&writerThreadInfo->mutex); 66 67 writerThreadInfo->writer.reset(); 68 params.GetTransferParams()->written += bs.TotalBlockSize(); 69 return SUCCESS; 70} 71 72CommandResult ZeroAndEraseCommandFn::Execute(const Command ¶ms) 73{ 74 bool isErase = false; 75 if (params.GetCommandType() == CommandType::ERASE) { 76 isErase = true; 77 LOG(INFO) << "Start run ERASE command"; 78 } 79 if (isErase) { 80 struct stat statBlock {}; 81 if (fstat(params.GetFileDescriptor(), &statBlock) == -1) { 82 LOG(ERROR) << "Failed to fstat"; 83 return FAILED; 84 } 85#ifndef UPDATER_UT 86 if (!S_ISBLK(statBlock.st_mode)) { 87 LOG(ERROR) << "Invalid block device"; 88 return FAILED; 89 } 90#endif 91 } 92 93 BlockSet blk; 94 blk.ParserAndInsert(params.GetArgumentByPos(1)); 95 LOG(INFO) << "Parser params to block set"; 96 auto ret = CommandResult(blk.WriteZeroToBlock(params.GetFileDescriptor(), isErase)); 97 if (ret == SUCCESS && !isErase) { 98 params.GetTransferParams()->written += blk.TotalBlockSize(); 99 } 100 return ret; 101} 102 103bool LoadTarget(const Command ¶ms, size_t &pos, std::vector<uint8_t> &buffer, 104 BlockSet &targetBlock, CommandResult &result) 105{ 106 CommandType type = params.GetCommandType(); 107 // Read sha256 of source and target 108 std::string srcHash = params.GetArgumentByPos(pos++); 109 std::string tgtHash = srcHash; 110 if (type != CommandType::MOVE) { 111 tgtHash = params.GetArgumentByPos(pos++); 112 } 113 114 // Read the target's buffer to determine whether it needs to be written 115 std::string cmdTmp = params.GetArgumentByPos(pos++); 116 targetBlock.ParserAndInsert(cmdTmp); 117 size_t tgtBlockSize = targetBlock.TotalBlockSize() * H_BLOCK_SIZE; 118 std::vector<uint8_t> tgtBuffer(tgtBlockSize); 119 120 if (targetBlock.ReadDataFromBlock(params.GetFileDescriptor(), tgtBuffer) == 0) { 121 LOG(ERROR) << "Read data from block error, TotalBlockSize: " << targetBlock.TotalBlockSize(); 122 result = FAILED; 123 return false; 124 } 125 if (targetBlock.VerifySha256(tgtBuffer, targetBlock.TotalBlockSize(), tgtHash) == 0) { 126 LOG(ERROR) << "Will write same sha256 blocks to target, no need to write"; 127 result = SUCCESS; 128 return false; 129 } 130 std::vector<uint8_t>().swap(tgtBuffer); 131 std::string blockLen = params.GetArgumentByPos(pos++); 132 size_t srcBlockSize = String2Int<size_t>(blockLen, N_DEC); 133 buffer.resize(srcBlockSize * H_BLOCK_SIZE); 134 if (targetBlock.LoadTargetBuffer(params, buffer, srcBlockSize, pos, srcHash) != 0) { 135 LOG(ERROR) << "Failed to load blocks"; 136 result = FAILED; 137 return false; 138 } 139 result = SUCCESS; 140 return true; 141} 142 143int32_t DiffAndMoveCommandFn::WriteDiffToBlock(const Command ¶ms, std::vector<uint8_t> &srcBuffer, 144 uint8_t *patchBuffer, size_t patchLength, BlockSet &targetBlock) 145{ 146 CommandType type = params.GetCommandType(); 147 return targetBlock.WriteDiffToBlock(params, srcBuffer, patchBuffer, patchLength, type == CommandType::IMGDIFF); 148} 149 150CommandResult DiffAndMoveCommandFn::Execute(const Command ¶ms) 151{ 152 CommandType type = params.GetCommandType(); 153 size_t pos = H_DIFF_CMD_ARGS_START; 154 if (type == CommandType::MOVE) { 155 pos = H_MOVE_CMD_ARGS_START; 156 } 157 158 BlockSet targetBlock; 159 std::vector<uint8_t> buffer; 160 CommandResult result = FAILED; 161 if (!LoadTarget(params, pos, buffer, targetBlock, result)) { 162 return result; 163 } 164 if (!params.GetTransferParams()->canWrite) { 165 return result; 166 } 167 168 int32_t ret = -1; 169 if (type != CommandType::MOVE) { 170 pos = H_MOVE_CMD_ARGS_START; 171 size_t offset = Utils::String2Int<size_t>(params.GetArgumentByPos(pos++), Utils::N_DEC); 172 size_t patchLength = Utils::String2Int<size_t>(params.GetArgumentByPos(pos++), Utils::N_DEC); 173 uint8_t *patchBuffer = params.GetTransferParams()->patchDataBuffer + offset; 174 ret = WriteDiffToBlock(params, buffer, patchBuffer, patchLength, targetBlock); 175 } else { 176 ret = targetBlock.WriteDataToBlock(params.GetFileDescriptor(), buffer) == 0 ? -1 : 0; 177 } 178 if (ret != 0) { 179 LOG(ERROR) << "fail to write block data."; 180 return errno == EIO ? NEED_RETRY : FAILED; 181 } 182 std::string storeBase = params.GetTransferParams()->storeBase; 183 std::string freeStash = params.GetTransferParams()->freeStash; 184 if (!freeStash.empty()) { 185 if (Store::FreeStore(storeBase, freeStash) != 0) { 186 LOG(WARNING) << "fail to delete file: " << freeStash; 187 } 188 params.GetTransferParams()->freeStash.clear(); 189 } 190 params.GetTransferParams()->written += targetBlock.TotalBlockSize(); 191 return SUCCESS; 192} 193 194CommandResult FreeCommandFn::Execute(const Command ¶ms) 195{ 196 std::string shaStr = params.GetArgumentByPos(1); 197 std::string storeBase = params.GetTransferParams()->storeBase; 198 if (params.GetTransferParams()->storeCreated == 0) { 199 return CommandResult(Store::FreeStore(storeBase, shaStr)); 200 } 201 return SUCCESS; 202} 203 204CommandResult StashCommandFn::Execute(const Command ¶ms) 205{ 206 size_t pos = 1; 207 const std::string shaStr = params.GetArgumentByPos(pos++); 208 BlockSet srcBlk; 209 LOG(INFO) << "Get source block info to block set"; 210 srcBlk.ParserAndInsert(params.GetArgumentByPos(pos++)); 211 size_t srcBlockSize = srcBlk.TotalBlockSize(); 212 std::vector<uint8_t> buffer; 213 buffer.resize(srcBlockSize * H_BLOCK_SIZE); 214 std::string storeBase = params.GetTransferParams()->storeBase; 215 LOG(DEBUG) << "Confirm whether the block is stored"; 216 if (Store::LoadDataFromStore(storeBase, shaStr, buffer) == 0) { 217 LOG(INFO) << "The stash has been stored, skipped"; 218 return SUCCESS; 219 } 220 LOG(DEBUG) << "Read block data to buffer"; 221 if (srcBlk.ReadDataFromBlock(params.GetFileDescriptor(), buffer) == 0) { 222 LOG(ERROR) << "Error to load block data"; 223 return FAILED; 224 } 225 int32_t res = srcBlk.VerifySha256(buffer, srcBlockSize, shaStr); 226 if (res != 0 && !params.GetTransferParams()->canWrite) { 227 res = BlockVerify(params, buffer, srcBlockSize, shaStr, pos); 228 } 229 if (res != 0) { 230 LOG(WARNING) << "failed to load source blocks for stash"; 231 return SUCCESS; 232 } 233 LOG(INFO) << "store " << srcBlockSize << " blocks to " << storeBase << "/" << shaStr; 234 int ret = Store::WriteDataToStore(storeBase, shaStr, buffer, srcBlockSize * H_BLOCK_SIZE); 235 return CommandResult(ret); 236} 237} // namespace Updater 238