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     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 & params)72 CommandResult ZeroAndEraseCommandFn::Execute(const Command &params)
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 & params,size_t & pos,std::vector<uint8_t> & buffer,BlockSet & targetBlock,CommandResult & result)103 bool LoadTarget(const Command &params, 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     return true;
140 }
141 
WriteDiffToBlock(const Command & params,std::vector<uint8_t> & srcBuffer,uint8_t * patchBuffer,size_t patchLength,BlockSet & targetBlock)142 int32_t DiffAndMoveCommandFn::WriteDiffToBlock(const Command &params, std::vector<uint8_t> &srcBuffer,
143                                                uint8_t *patchBuffer, size_t patchLength, BlockSet &targetBlock)
144 {
145     CommandType type = params.GetCommandType();
146     return targetBlock.WriteDiffToBlock(params, srcBuffer, patchBuffer, patchLength, type == CommandType::IMGDIFF);
147 }
148 
Execute(const Command & params)149 CommandResult DiffAndMoveCommandFn::Execute(const Command &params)
150 {
151     CommandType type = params.GetCommandType();
152     size_t pos = H_DIFF_CMD_ARGS_START;
153     if (type == CommandType::MOVE) {
154         pos = H_MOVE_CMD_ARGS_START;
155     }
156 
157     BlockSet targetBlock;
158     std::vector<uint8_t> buffer;
159     CommandResult result = FAILED;
160     if (!LoadTarget(params, pos, buffer, targetBlock, result)) {
161         return result;
162     }
163 
164     int32_t ret = -1;
165     if (type != CommandType::MOVE) {
166         pos = H_MOVE_CMD_ARGS_START;
167         size_t offset = Utils::String2Int<size_t>(params.GetArgumentByPos(pos++), Utils::N_DEC);
168         size_t patchLength = Utils::String2Int<size_t>(params.GetArgumentByPos(pos++), Utils::N_DEC);
169         uint8_t *patchBuffer = params.GetTransferParams()->patchDataBuffer + offset;
170         ret = WriteDiffToBlock(params, buffer, patchBuffer, patchLength, targetBlock);
171     } else {
172         ret = targetBlock.WriteDataToBlock(params.GetFileDescriptor(), buffer) == 0 ? -1 : 0;
173     }
174     if (ret != 0) {
175         LOG(ERROR) << "fail to write block data.";
176         return errno == EIO ? NEED_RETRY : FAILED;
177     }
178     std::string storeBase = params.GetTransferParams()->storeBase;
179     std::string freeStash = params.GetTransferParams()->freeStash;
180     if (!freeStash.empty()) {
181         if (Store::FreeStore(storeBase, freeStash) != 0) {
182             LOG(WARNING) << "fail to delete file: " << freeStash;
183         }
184         params.GetTransferParams()->freeStash.clear();
185     }
186     params.GetTransferParams()->written += targetBlock.TotalBlockSize();
187     return SUCCESS;
188 }
189 
Execute(const Command & params)190 CommandResult FreeCommandFn::Execute(const Command &params)
191 {
192     std::string shaStr = params.GetArgumentByPos(1);
193     std::string storeBase = params.GetTransferParams()->storeBase;
194     if (params.GetTransferParams()->storeCreated == 0) {
195         return CommandResult(Store::FreeStore(storeBase, shaStr));
196     }
197     return SUCCESS;
198 }
199 
Execute(const Command & params)200 CommandResult StashCommandFn::Execute(const Command &params)
201 {
202     size_t pos = 1;
203     const std::string shaStr = params.GetArgumentByPos(pos++);
204     BlockSet srcBlk;
205     LOG(INFO) << "Get source block info to block set";
206     srcBlk.ParserAndInsert(params.GetArgumentByPos(pos++));
207     size_t srcBlockSize = srcBlk.TotalBlockSize();
208     std::vector<uint8_t> buffer;
209     buffer.resize(srcBlockSize * H_BLOCK_SIZE);
210     std::string storeBase = params.GetTransferParams()->storeBase;
211     LOG(DEBUG) << "Confirm whether the block is stored";
212     if (Store::LoadDataFromStore(storeBase, shaStr, buffer) == 0) {
213         LOG(INFO) << "The stash has been stored, skipped";
214         return SUCCESS;
215     }
216     LOG(DEBUG) << "Read block data to buffer";
217     if (srcBlk.ReadDataFromBlock(params.GetFileDescriptor(), buffer) == 0) {
218         LOG(ERROR) << "Error to load block data";
219         return FAILED;
220     }
221     if (srcBlk.VerifySha256(buffer, srcBlockSize, shaStr) != 0) {
222         LOG(WARNING) << "failed to load source blocks for stash";
223         return SUCCESS;
224     }
225     LOG(INFO) << "store " << srcBlockSize << " blocks to " << storeBase << "/" << shaStr;
226     int ret = Store::WriteDataToStore(storeBase, shaStr, buffer, srcBlockSize * H_BLOCK_SIZE);
227     return CommandResult(ret);
228 }
229 } // namespace Updater
230