• 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) {
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 
Execute(const Command & params)199 CommandResult DiffAndMoveCommandFn::Execute(const Command &params)
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 &params)
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 &params)
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