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 & params)38 CommandResult AbortCommandFn::Execute(const Command ¶ms)
39 {
40 return SUCCESS;
41 }
42
Execute(const Command & params)43 CommandResult NewCommandFn::Execute(const Command ¶ms)
44 {
45 if (params.IsStreamCmd()) {
46 return (StreamExecute(params) != 0) ? FAILED : SUCCESS;
47 }
48 BlockSet bs;
49 size_t pos = H_NEW_CMD_ARGS_START;
50 bs.ParserAndInsert(params.GetArgumentByPos(pos));
51 LOG(INFO) << " writing " << bs.TotalBlockSize() << " blocks of new data";
52 auto writerThreadInfo = params.GetTransferParams()->writerThreadInfo.get();
53 pthread_mutex_lock(&writerThreadInfo->mutex);
54 writerThreadInfo->writer = std::make_unique<BlockWriter>(params.GetTargetFileDescriptor(), bs);
55 pthread_cond_broadcast(&writerThreadInfo->cond);
56 while (writerThreadInfo->writer != nullptr) {
57 LOG(DEBUG) << "wait for new data write done...";
58 if (!writerThreadInfo->readyToWrite) {
59 LOG(ERROR) << "writer thread could not write blocks. " << bs.TotalBlockSize() * H_BLOCK_SIZE -
60 writerThreadInfo->writer->GetTotalWritten() << " bytes lost";
61 pthread_mutex_unlock(&writerThreadInfo->mutex);
62 writerThreadInfo->writer.reset();
63 writerThreadInfo->writer = nullptr;
64 return FAILED;
65 }
66 LOG(DEBUG) << "Writer already written " << writerThreadInfo->writer->GetTotalWritten() << " byte(s)";
67 pthread_cond_wait(&writerThreadInfo->cond, &writerThreadInfo->mutex);
68 }
69 pthread_mutex_unlock(&writerThreadInfo->mutex);
70
71 writerThreadInfo->writer.reset();
72 params.GetTransferParams()->written += bs.TotalBlockSize();
73 return SUCCESS;
74 }
75
StreamExecute(const Command & params)76 int32_t NewCommandFn::StreamExecute(const Command ¶ms)
77 {
78 size_t pos = H_NEW_CMD_ARGS_START;
79 uint8_t *addr = params.GetTransferParams()->dataBuffer;
80 size_t size = params.GetTransferParams()->dataBufferSize;
81 std::string tgtHash = "";
82 tgtHash = params.GetArgumentByPos(pos++);
83 LOG(INFO) << "StreamExecute size:" << size << " cmd:" << params.GetCommandLine();
84 BlockSet bs;
85 bs.ParserAndInsert(params.GetArgumentByPos(pos++));
86 std::unique_ptr<BlockWriter> writer = std::make_unique<BlockWriter>(params.GetTargetFileDescriptor(), bs);
87 while (size > 0) {
88 size_t toWrite = std::min(size, writer->GetBlocksSize() - writer->GetTotalWritten());
89 LOG(INFO) << "StreamExecute toWrite:" << toWrite;
90 // No more data to write.
91 if (toWrite == 0) {
92 break;
93 }
94 bool ret = writer->Write(addr, toWrite, nullptr);
95 if (!ret) {
96 return -1;
97 }
98 size -= toWrite;
99 addr += toWrite;
100 }
101 size_t tgtBlockSize = bs.TotalBlockSize() * H_BLOCK_SIZE;
102 std::vector<uint8_t> tgtBuffer(tgtBlockSize);
103
104 if (bs.ReadDataFromBlock(params.GetTargetFileDescriptor(), tgtBuffer) == 0) {
105 LOG(ERROR) << "Read data from block error, TotalBlockSize: " << bs.TotalBlockSize();
106 return -1;
107 }
108 if (bs.VerifySha256(tgtBuffer, bs.TotalBlockSize(), tgtHash) == 0) {
109 LOG(ERROR) << "Will write same sha256 blocks to target, no need to write";
110 return -1;
111 }
112 std::vector<uint8_t>().swap(tgtBuffer);
113 return 0;
114 }
115
Execute(const Command & params)116 CommandResult ZeroAndEraseCommandFn::Execute(const Command ¶ms)
117 {
118 bool isErase = false;
119 if (params.GetCommandType() == CommandType::ERASE) {
120 isErase = true;
121 LOG(INFO) << "Start run ERASE command";
122 }
123 if (isErase) {
124 struct stat statBlock {};
125 if (fstat(params.GetTargetFileDescriptor(), &statBlock) == -1) {
126 LOG(ERROR) << "Failed to fstat";
127 return FAILED;
128 }
129 #ifndef UPDATER_UT
130 if (!S_ISBLK(statBlock.st_mode)) {
131 LOG(ERROR) << "Invalid block device";
132 return FAILED;
133 }
134 #endif
135 }
136
137 BlockSet blk;
138 blk.ParserAndInsert(params.GetArgumentByPos(1));
139 LOG(INFO) << "Parser params to block set";
140 auto ret = CommandResult(blk.WriteZeroToBlock(params.GetTargetFileDescriptor(), isErase));
141 if (ret == SUCCESS && !isErase) {
142 params.GetTransferParams()->written += blk.TotalBlockSize();
143 }
144 return ret;
145 }
146
LoadTarget(const Command & params,size_t & pos,std::vector<uint8_t> & buffer,BlockSet & targetBlock,CommandResult & result)147 bool LoadTarget(const Command ¶ms, size_t &pos, std::vector<uint8_t> &buffer,
148 BlockSet &targetBlock, CommandResult &result)
149 {
150 CommandType type = params.GetCommandType();
151 std::string srcHash = "";
152 std::string tgtHash = "";
153 // Read sha256 of source and target
154 if (type == CommandType::BSDIFF || type == CommandType::IMGDIFF) {
155 srcHash = params.GetArgumentByPos(pos++);
156 tgtHash = params.GetArgumentByPos(pos++);
157 } else if (type == CommandType::MOVE) {
158 srcHash = params.GetArgumentByPos(pos++);
159 tgtHash = srcHash;
160 }
161
162 // Read the target's buffer to determine whether it needs to be written
163 std::string cmdTmp = params.GetArgumentByPos(pos++);
164 targetBlock.ParserAndInsert(cmdTmp);
165 if (type != CommandType::COPY) {
166 size_t tgtBlockSize = targetBlock.TotalBlockSize() * H_BLOCK_SIZE;
167 std::vector<uint8_t> tgtBuffer(tgtBlockSize);
168
169 if (targetBlock.ReadDataFromBlock(params.GetTargetFileDescriptor(), tgtBuffer) == 0) {
170 LOG(ERROR) << "Read data from block error, TotalBlockSize: " << targetBlock.TotalBlockSize();
171 result = FAILED;
172 return false;
173 }
174 if (targetBlock.VerifySha256(tgtBuffer, targetBlock.TotalBlockSize(), tgtHash) == 0) {
175 result = SUCCESS;
176 return false;
177 }
178 std::vector<uint8_t>().swap(tgtBuffer);
179 }
180 std::string blockLen = params.GetArgumentByPos(pos++);
181 size_t srcBlockSize = String2Int<size_t>(blockLen, N_DEC);
182 buffer.resize(srcBlockSize * H_BLOCK_SIZE);
183 if (targetBlock.LoadTargetBuffer(params, buffer, srcBlockSize, pos, srcHash) != 0) {
184 LOG(ERROR) << "Failed to load blocks";
185 result = FAILED;
186 return false;
187 }
188 result = SUCCESS;
189 return true;
190 }
191
WriteDiffToBlock(const Command & params,std::vector<uint8_t> & srcBuffer,uint8_t * patchBuffer,size_t patchLength,BlockSet & targetBlock)192 int32_t DiffAndMoveCommandFn::WriteDiffToBlock(const Command ¶ms, std::vector<uint8_t> &srcBuffer,
193 uint8_t *patchBuffer, size_t patchLength, BlockSet &targetBlock)
194 {
195 CommandType type = params.GetCommandType();
196 return targetBlock.WriteDiffToBlock(params, srcBuffer, patchBuffer, patchLength, type == CommandType::IMGDIFF);
197 }
198
Execute(const Command & params)199 CommandResult DiffAndMoveCommandFn::Execute(const Command ¶ms)
200 {
201 CommandType type = params.GetCommandType();
202 size_t pos = H_DIFF_CMD_ARGS_START;
203 if (type == CommandType::MOVE) {
204 pos = H_MOVE_CMD_ARGS_START;
205 } else if (type == CommandType::COPY) {
206 pos = H_COPY_CMD_ARGS_START;
207 }
208
209 BlockSet targetBlock;
210 std::vector<uint8_t> buffer;
211 CommandResult result = FAILED;
212 if (!LoadTarget(params, pos, buffer, targetBlock, result)) {
213 return result;
214 }
215 if (!params.GetTransferParams()->canWrite) {
216 return result;
217 }
218
219 int32_t ret = -1;
220 if (type != CommandType::MOVE && type != CommandType::COPY) {
221 pos = H_MOVE_CMD_ARGS_START;
222 size_t offset;
223 if (params.IsStreamCmd()) {
224 offset = 0;
225 pos++;
226 } else {
227 offset = Utils::String2Int<size_t>(params.GetArgumentByPos(pos++), Utils::N_DEC);
228 }
229 size_t patchLength = Utils::String2Int<size_t>(params.GetArgumentByPos(pos++), Utils::N_DEC);
230 uint8_t *patchBuffer = params.GetTransferParams()->dataBuffer + offset;
231 ret = WriteDiffToBlock(params, buffer, patchBuffer, patchLength, targetBlock);
232 } else {
233 ret = targetBlock.WriteDataToBlock(params.GetTargetFileDescriptor(), buffer) == 0 ? -1 : 0;
234 }
235 if (ret != 0) {
236 LOG(ERROR) << "fail to write block data.";
237 return errno == EIO ? NEED_RETRY : FAILED;
238 }
239 std::string storeBase = params.GetTransferParams()->storeBase;
240 std::string freeStash = params.GetTransferParams()->freeStash;
241 if (!freeStash.empty()) {
242 if (Store::FreeStore(storeBase, freeStash) != 0) {
243 LOG(WARNING) << "fail to delete file: " << freeStash;
244 }
245 params.GetTransferParams()->freeStash.clear();
246 }
247 params.GetTransferParams()->written += targetBlock.TotalBlockSize();
248 return SUCCESS;
249 }
250
Execute(const Command & params)251 CommandResult FreeCommandFn::Execute(const Command ¶ms)
252 {
253 std::string shaStr = params.GetArgumentByPos(1);
254 std::string storeBase = params.GetTransferParams()->storeBase;
255 if (params.GetTransferParams()->storeCreated == 0) {
256 return CommandResult(Store::FreeStore(storeBase, shaStr));
257 }
258 return SUCCESS;
259 }
260
Execute(const Command & params)261 CommandResult StashCommandFn::Execute(const Command ¶ms)
262 {
263 size_t pos = 1;
264 const std::string shaStr = params.GetArgumentByPos(pos++);
265 BlockSet srcBlk;
266 LOG(INFO) << "Get source block info to block set";
267 srcBlk.ParserAndInsert(params.GetArgumentByPos(pos++));
268 size_t srcBlockSize = srcBlk.TotalBlockSize();
269 std::vector<uint8_t> buffer;
270 buffer.resize(srcBlockSize * H_BLOCK_SIZE);
271 std::string storeBase = params.GetTransferParams()->storeBase;
272 LOG(DEBUG) << "Confirm whether the block is stored";
273 if (Store::LoadDataFromStore(storeBase, shaStr, buffer) == 0) {
274 LOG(INFO) << "The stash has been stored, skipped";
275 return SUCCESS;
276 }
277 LOG(DEBUG) << "Read block data to buffer";
278 if (srcBlk.ReadDataFromBlock(params.GetSrcFileDescriptor(), buffer) == 0) {
279 LOG(ERROR) << "Error to load block data";
280 return FAILED;
281 }
282 int32_t res = srcBlk.VerifySha256(buffer, srcBlockSize, shaStr);
283 if (res != 0 && !params.GetTransferParams()->canWrite) {
284 res = BlockVerify(params, buffer, srcBlockSize, shaStr, pos);
285 }
286 if (res != 0) {
287 LOG(WARNING) << "failed to load source blocks for stash";
288 return SUCCESS;
289 }
290 LOG(INFO) << "store " << srcBlockSize << " blocks to " << storeBase << "/" << shaStr;
291 int ret = Store::WriteDataToStore(storeBase, shaStr, buffer, srcBlockSize * H_BLOCK_SIZE);
292 return CommandResult(ret);
293 }
294 } // namespace Updater
295