• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &params)
39 {
40     return SUCCESS;
41 }
42 
Execute(const Command & params)43 CommandResult NewCommandFn::Execute(const Command &params)
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 &params)
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 &params)
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 &params, 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 &params, 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 &params, 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 &params)
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 &params)
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 &params)
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