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
35 using namespace Hpackage;
36 using namespace Updater::Utils;
37 namespace Updater {
Execute(const Command ¶ms)38 CommandResult AbortCommandFn::Execute(const Command ¶ms)
39 {
40 return SUCCESS;
41 }
42
Execute(const Command ¶ms)43 CommandResult 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
Execute(const Command ¶ms)72 CommandResult 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
LoadTarget(const Command ¶ms, size_t &pos, std::vector<uint8_t> &buffer, BlockSet &targetBlock, CommandResult &result)103 bool 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
WriteDiffToBlock(const Command ¶ms, std::vector<uint8_t> &srcBuffer, uint8_t *patchBuffer, size_t patchLength, BlockSet &targetBlock)143 int32_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
Execute(const Command ¶ms)150 CommandResult 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
Execute(const Command ¶ms)194 CommandResult 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
Execute(const Command ¶ms)204 CommandResult 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