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 && Utils::IsUpdaterMode()) {
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
WriteFileToBlock(const Command & params,std::vector<uint8_t> & srcBuffer,size_t offset,size_t patchLength,BlockSet & targetBlock)199 int32_t DiffAndMoveCommandFn::WriteFileToBlock(const Command ¶ms, std::vector<uint8_t> &srcBuffer,
200 size_t offset, size_t patchLength, BlockSet &targetBlock)
201 {
202 std::ifstream fin(params.GetTransferParams()->patchDatFile, std::ios::in | std::ios::binary);
203 if (!fin.is_open()) {
204 LOG(ERROR) << "open dat file failed " << params.GetTransferParams()->patchDatFile;
205 return static_cast<int>(FAILED);
206 }
207 std::unique_ptr<uint8_t[]> patchBuffer = std::make_unique<uint8_t[]>(patchLength);
208 (void)memset_s(patchBuffer.get(), patchLength, 0, patchLength);
209 if ((!fin.seekg(static_cast<int>(offset), std::ios::beg)) ||
210 (!fin.read(reinterpret_cast<char *>(patchBuffer.get()), patchLength))) {
211 LOG(ERROR) << "read dat file failed gcount " << fin.gcount() << ", patch len " << patchLength;
212 fin.close();
213 return static_cast<int>(FAILED);
214 }
215 fin.close();
216 return WriteDiffToBlock(params, srcBuffer, patchBuffer.get(), patchLength, targetBlock);
217 }
218
Execute(const Command & params)219 CommandResult DiffAndMoveCommandFn::Execute(const Command ¶ms)
220 {
221 CommandType type = params.GetCommandType();
222 size_t pos = H_DIFF_CMD_ARGS_START;
223 if (type == CommandType::MOVE) {
224 pos = H_MOVE_CMD_ARGS_START;
225 } else if (type == CommandType::COPY) {
226 pos = H_COPY_CMD_ARGS_START;
227 }
228
229 BlockSet targetBlock;
230 std::vector<uint8_t> buffer;
231 CommandResult result = FAILED;
232 if (!LoadTarget(params, pos, buffer, targetBlock, result) || !params.GetTransferParams()->canWrite) {
233 return result;
234 }
235
236 int32_t ret = -1;
237 if (type != CommandType::MOVE && type != CommandType::COPY) {
238 pos = H_MOVE_CMD_ARGS_START;
239 size_t offset;
240 if (params.IsStreamCmd()) {
241 offset = 0;
242 pos++;
243 } else {
244 offset = Utils::String2Int<size_t>(params.GetArgumentByPos(pos++), Utils::N_DEC);
245 }
246 size_t patchLength = Utils::String2Int<size_t>(params.GetArgumentByPos(pos++), Utils::N_DEC);
247 if (Utils::IsUpdaterMode() || params.IsStreamCmd()) {
248 uint8_t *patchBuffer = params.GetTransferParams()->dataBuffer + offset;
249 ret = WriteDiffToBlock(params, buffer, patchBuffer, patchLength, targetBlock);
250 } else {
251 ret = WriteFileToBlock(params, buffer, offset, patchLength, targetBlock);
252 }
253 } else {
254 ret = targetBlock.WriteDataToBlock(params.GetTargetFileDescriptor(), buffer) == 0 ? -1 : 0;
255 }
256 if (ret != 0) {
257 LOG(ERROR) << "fail to write block data.";
258 return errno == EIO ? NEED_RETRY : FAILED;
259 }
260 std::string storeBase = params.GetTransferParams()->storeBase;
261 std::string freeStash = params.GetTransferParams()->freeStash;
262 if (!freeStash.empty()) {
263 if (Store::FreeStore(storeBase, freeStash) != 0) {
264 LOG(WARNING) << "fail to delete file: " << freeStash;
265 }
266 params.GetTransferParams()->freeStash.clear();
267 }
268 params.GetTransferParams()->written += targetBlock.TotalBlockSize();
269 return SUCCESS;
270 }
271
Execute(const Command & params)272 CommandResult FreeCommandFn::Execute(const Command ¶ms)
273 {
274 std::string shaStr = params.GetArgumentByPos(1);
275 std::string storeBase = params.GetTransferParams()->storeBase;
276 if (params.GetTransferParams()->storeCreated == 0) {
277 return CommandResult(Store::FreeStore(storeBase, shaStr));
278 }
279 return SUCCESS;
280 }
281
Execute(const Command & params)282 CommandResult StashCommandFn::Execute(const Command ¶ms)
283 {
284 size_t pos = 1;
285 const std::string shaStr = params.GetArgumentByPos(pos++);
286 BlockSet srcBlk;
287 LOG(INFO) << "Get source block info to block set";
288 srcBlk.ParserAndInsert(params.GetArgumentByPos(pos++));
289 size_t srcBlockSize = srcBlk.TotalBlockSize();
290 std::vector<uint8_t> buffer;
291 buffer.resize(srcBlockSize * H_BLOCK_SIZE);
292 std::string storeBase = params.GetTransferParams()->storeBase;
293 LOG(DEBUG) << "Confirm whether the block is stored";
294 if (Store::LoadDataFromStore(storeBase, shaStr, buffer) == 0) {
295 LOG(INFO) << "The stash has been stored, skipped";
296 return SUCCESS;
297 }
298 LOG(DEBUG) << "Read block data to buffer";
299 if (srcBlk.ReadDataFromBlock(params.GetSrcFileDescriptor(), buffer) == 0) {
300 LOG(ERROR) << "Error to load block data";
301 return FAILED;
302 }
303 int32_t res = srcBlk.VerifySha256(buffer, srcBlockSize, shaStr);
304 if (res != 0 && !params.GetTransferParams()->canWrite) {
305 res = BlockVerify(params, buffer, srcBlockSize, shaStr, pos);
306 }
307 if (res != 0) {
308 LOG(WARNING) << "failed to load source blocks for stash";
309 return SUCCESS;
310 }
311 LOG(INFO) << "store " << srcBlockSize << " blocks to " << storeBase << "/" << shaStr;
312 int ret = Store::WriteDataToStore(storeBase, shaStr, buffer, srcBlockSize * H_BLOCK_SIZE);
313 return CommandResult(ret);
314 }
315 } // namespace Updater
316