1 /*
2  * Copyright (C) 2022 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 "host_updater.h"
17 
18 #include <algorithm>
19 #include <unordered_map>
20 
21 #include "common.h"
22 #include "define.h"
23 #include "serial_struct.h"
24 
25 namespace Hdc {
26 namespace {
27 constexpr uint8_t PERCENT_FINISH = 100;
28 constexpr uint8_t PERCENT_CLEAR = UINT8_MAX;
29 constexpr int MAX_RETRY_COUNT = 3;
30 constexpr size_t FLASH_PARAM_MIN_COUNT = 2;
31 constexpr size_t FLASH_FILE_INDEX = 1;
32 constexpr size_t UPDATE_PARAM_MIN_COUNT = 1;
33 constexpr size_t UPDATE_FILE_INDEX = 0;
34 constexpr size_t FORMAT_PARAM_MIN_COUNT = 2;
35 constexpr size_t ERASE_PARAM_MIN_COUNT = 2;
36 
37 const std::string CMD_STR_UPDATE = "update ";
38 const std::string CMD_STR_FLASH = "flash ";
39 const std::string CMD_STR_ERASE = "erase ";
40 const std::string CMD_STR_FORMAT = "format ";
41 
42 const std::unordered_map<std::string, uint16_t> FLASHD_CMD = {
43     {CMD_STR_UPDATE, CMD_FLASHD_UPDATE_INIT},
44     {CMD_STR_FLASH, CMD_FLASHD_FLASH_INIT},
45     {CMD_STR_ERASE, CMD_FLASHD_ERASE},
46     {CMD_STR_FORMAT, CMD_FLASHD_FORMAT},
47 };
48 
Split(const std::string &src, const std::vector<std::string> &filter)49 std::vector<std::string> Split(const std::string &src, const std::vector<std::string> &filter)
50 {
51     std::vector<std::string> result;
52     if (src.empty()) {
53         return result;
54     }
55     const auto len = src.size() + 1;
56     auto buffer = std::vector<char>(len, 0);
57     buffer.assign(src.begin(), src.end());
58     const char delimit[] = "\t\r\n ";
59     char *nextToken = nullptr;
60     char *token = strtok_s(buffer.data(), delimit, &nextToken);
61     while (token != nullptr) {
62         if (std::find(filter.cbegin(), filter.cend(), token) == filter.cend()) {
63             result.push_back(token);
64         }
65         token = strtok_s(nullptr, delimit, &nextToken);
66     }
67     return result;
68 }
69 }
70 
HostUpdater(HTaskInfo hTaskInfo)71 HostUpdater::HostUpdater(HTaskInfo hTaskInfo) : HdcTransferBase(hTaskInfo)
72 {
73     commandBegin = CMD_FLASHD_BEGIN;
74     commandData = CMD_FLASHD_DATA;
75 }
76 
~HostUpdater()77 HostUpdater::~HostUpdater() {}
78 
RunQueue(CtxFile &context)79 bool HostUpdater::RunQueue(CtxFile &context)
80 {
81     context.localPath = context.taskQueue.back();
82     uv_fs_t *openReq = new uv_fs_t;
83     if (openReq == nullptr) {
84         WRITE_LOG(LOG_FATAL, "HostUpdater::RunQueue new uv_fs_t failed");
85         OnFileOpenFailed(&context);
86         return false;
87     }
88     memset_s(openReq, sizeof(uv_fs_t), 0, sizeof(uv_fs_t));
89     openReq->data = &context;
90     refCount++;
91     uv_fs_open(loopTask, openReq, context.localPath.c_str(), O_RDONLY, 0, OnFileOpen);
92     context.master = true;
93     return true;
94 }
95 
BeginTransfer(const std::string &function, const uint8_t *payload, int payloadSize, size_t minParam, size_t fileIndex)96 bool HostUpdater::BeginTransfer(const std::string &function, const uint8_t *payload, int payloadSize, size_t minParam,
97                                 size_t fileIndex)
98 {
99     if (payload[payloadSize - 1] != '\0') {
100         WRITE_LOG(LOG_FATAL, "payload is invalid");
101         return false;
102     }
103     std::string cmdParam(reinterpret_cast<const char *>(payload));
104     auto params = Split(cmdParam, {});
105     auto count = minParam;
106     auto index = fileIndex;
107     if (std::find(params.cbegin(), params.cend(), "-f") != params.cend()) {
108         count++;
109         index++;
110     }
111     if (params.size() != count || params.size() <= index) {
112         WRITE_LOG(LOG_FATAL, "param count is invalid");
113         return false;
114     }
115 
116     std::string localPath = params[index];
117     if (!Base::CheckDirectoryOrPath(localPath.c_str(), true, true)) {
118         WRITE_LOG(LOG_FATAL, "localPath is invalid");
119         return false;
120     }
121 
122     if (MatchPackageExtendName(localPath, ".img") || MatchPackageExtendName(localPath, ".bin") ||
123         MatchPackageExtendName(localPath, ".fd") || MatchPackageExtendName(localPath, ".cpio")) {
124         ctxNow.transferConfig.compressType = COMPRESS_NONE;
125     } else if (MatchPackageExtendName(localPath, ".zip")) {
126         WRITE_LOG(LOG_INFO, "file type is zip");
127     } else {
128         WRITE_LOG(LOG_FATAL, "file type is invalid");
129         return false;
130     }
131     ctxNow.transferConfig.functionName = function;
132     ctxNow.transferConfig.options = cmdParam;
133     ctxNow.localPath = localPath;
134     ctxNow.taskQueue.push_back(localPath);
135     return RunQueue(ctxNow);
136 }
137 
CheckMaster(CtxFile *context)138 void HostUpdater::CheckMaster(CtxFile *context)
139 {
140     uv_fs_t fs;
141     Base::ZeroStruct(fs.statbuf);
142     uv_fs_fstat(nullptr, &fs, context->openFd, nullptr);
143     context->transferConfig.fileSize = fs.statbuf.st_size;
144     uv_fs_req_cleanup(&fs);
145     context->transferConfig.optionalName = Base::GetFileNameAny(context->localPath);
146     std::string bufString = SerialStruct::SerializeToString(context->transferConfig);
147 
148     WRITE_LOG(LOG_DEBUG, "functionName = %s, fileSize = %llu", context->transferConfig.functionName.c_str(),
149         context->transferConfig.fileSize);
150 
151     std::vector<uint8_t> buffer(sizeof(uint64_t) / sizeof(uint8_t), 0);
152     buffer.insert(buffer.end(), bufString.begin(), bufString.end());
153     SendToAnother(CMD_FLASHD_CHECK, (uint8_t *)buffer.data(), buffer.size());
154 }
155 
CheckCmd(HdcCommand command, uint8_t *payload, int payloadSize, size_t paramCount)156 bool HostUpdater::CheckCmd(HdcCommand command, uint8_t *payload, int payloadSize, size_t paramCount)
157 {
158     if (payloadSize < 1 || payload[payloadSize - 1] != '\0') {
159         WRITE_LOG(LOG_FATAL, "payload is invalid");
160         return false;
161     }
162     std::string cmdParam(reinterpret_cast<char *>(payload));
163     WRITE_LOG(LOG_INFO, "cmdParam = %s, paramCount = %u", cmdParam.c_str(), paramCount);
164 
165     auto result = Split(cmdParam, {});
166     auto iter = std::find(result.cbegin(), result.cend(), "-f");
167     bool ret = (iter != result.cend()) ? (result.size() == (paramCount + 1)) : (result.size() == paramCount);
168     if (!ret) {
169         WRITE_LOG(LOG_FATAL, "CheckCmd failed");
170         return false;
171     }
172 
173     SendToAnother(command, payload, payloadSize);
174     ctxNow.taskQueue.push_back(cmdParam);
175     return true;
176 }
177 
CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)178 bool HostUpdater::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
179 {
180     if (command == CMD_FLASHD_BEGIN) {
181         if (!HdcTransferBase::CommandDispatch(command, payload, payloadSize)) {
182             return false;
183         }
184         std::string tip("Processing:    0%");
185         sendProgress_ = true;
186         SendRawData(tip);
187         return true;
188     }
189 
190     if (payload == nullptr || payloadSize <= 0) {
191         WRITE_LOG(LOG_FATAL, "payload or payloadSize is invalid");
192         return false;
193     }
194     if (!HdcTransferBase::CommandDispatch(command, payload, payloadSize)) {
195         return false;
196     }
197     bool ret = true;
198     switch (command) {
199         case CMD_FLASHD_UPDATE_INIT:
200             ret = BeginTransfer(CMDSTR_FLASHD_UPDATE, payload, payloadSize, UPDATE_PARAM_MIN_COUNT, UPDATE_FILE_INDEX);
201             break;
202         case CMD_FLASHD_FLASH_INIT:
203             ret = BeginTransfer(CMDSTR_FLASHD_FLASH, payload, payloadSize, FLASH_PARAM_MIN_COUNT, FLASH_FILE_INDEX);
204             break;
205         case CMD_FLASHD_FINISH:
206             ret = CheckUpdateContinue(command, payload, payloadSize);
207             break;
208         case CMD_FLASHD_ERASE:
209             ret = CheckCmd(CMD_FLASHD_ERASE, payload, payloadSize, ERASE_PARAM_MIN_COUNT);
210             break;
211         case CMD_FLASHD_FORMAT:
212             ret = CheckCmd(CMD_FLASHD_FORMAT, payload, payloadSize, FORMAT_PARAM_MIN_COUNT);
213             break;
214         case CMD_FLASHD_PROGRESS:
215             ProcessProgress(*payload);
216             break;
217         default:
218             break;
219     }
220     return ret;
221 }
222 
ProcessProgress(uint8_t percentage)223 void HostUpdater::ProcessProgress(uint8_t percentage)
224 {
225     if (!sendProgress_) {
226         return;
227     }
228     if (percentage == PERCENT_CLEAR) {
229         SendRawData("\n");
230         sendProgress_ = false;
231         return;
232     }
233     std::string plrogress = "\rProcessing:    " + std::to_string(percentage) + "%";
234     SendRawData(plrogress);
235     if (percentage == PERCENT_FINISH) {
236         SendRawData("\n");
237         sendProgress_ = false;
238     }
239 }
240 
CheckUpdateContinue(const uint16_t command, const uint8_t *payload, int payloadSize)241 bool HostUpdater::CheckUpdateContinue(const uint16_t command, const uint8_t *payload, int payloadSize)
242 {
243     if (static_cast<size_t>(payloadSize) < sizeof(uint16_t)) {
244         return false;
245     }
246 
247     MessageLevel level = static_cast<MessageLevel>(payload[1]);
248     if ((level == MSG_OK) && sendProgress_) {
249         ProcessProgress(PERCENT_FINISH);
250     }
251     std::string info(reinterpret_cast<char *>(const_cast<uint8_t *>(payload + sizeof(uint16_t))),
252                      payloadSize - sizeof(uint16_t));
253     if (!info.empty()) {
254         LogMsg(level, "%s", info.c_str());
255     }
256     WRITE_LOG(LOG_DEBUG, "CheckUpdateContinue payloadSize %d %d %s", payloadSize, level, info.c_str());
257     if (ctxNow.taskQueue.size() != 0) {
258         ctxNow.taskQueue.pop_back();
259     }
260     if (singalStop || !ctxNow.taskQueue.size()) {
261         return false;
262     }
263     return RunQueue(ctxNow);
264 }
265 
CheckMatchUpdate(const std::string &input, TranslateCommand::FormatCommand &outCmd)266 bool HostUpdater::CheckMatchUpdate(const std::string &input, TranslateCommand::FormatCommand &outCmd)
267 {
268     WRITE_LOG(LOG_DEBUG, "CheckMatchUpdate command:%s", input.c_str());
269     for (const auto &iter : FLASHD_CMD) {
270         if ((input.find(iter.first) == 0) && (input.size() > iter.first.size())) {
271             outCmd.cmdFlag = iter.second;
272             return true;
273         }
274     }
275     return false;
276 }
277 
ConfirmCommand(const string &commandIn, bool &closeInput)278 bool HostUpdater::ConfirmCommand(const string &commandIn, bool &closeInput)
279 {
280     std::string tip = "";
281     if (!strncmp(commandIn.c_str(), CMD_STR_UPDATE.c_str(), CMD_STR_UPDATE.size())) {
282         closeInput = true;
283     } else if (!strncmp(commandIn.c_str(), CMD_STR_FLASH.c_str(), CMD_STR_FLASH.size())) {
284         tip = "Confirm flash partition";
285         closeInput = true;
286     } else if (!strncmp(commandIn.c_str(), CMD_STR_ERASE.c_str(), CMD_STR_ERASE.size())) {
287         tip = "Confirm erase partition";
288     } else if (!strncmp(commandIn.c_str(), CMD_STR_FORMAT.c_str(), CMD_STR_FORMAT.size())) {
289         tip = "Confirm format partition";
290     }
291     if (tip.empty() || strstr(commandIn.c_str(), " -f") != nullptr) {
292         return true;
293     }
294     const size_t minLen = strlen("yes");
295     int retryCount = 0;
296     do {
297         printf("%s ? (Yes/No) ", tip.c_str());
298         fflush(stdin);
299         std::string info = {};
300         size_t i = 0;
301         while (1) {
302             char c = getchar();
303             if (c == '\r' || c == '\n') {
304                 break;
305             }
306             if (c == ' ') {
307                 continue;
308             }
309             if (i < minLen && isprint(c)) {
310                 info.append(1, std::tolower(c));
311                 i++;
312             }
313         }
314         if (info == "n" || info == "no") {
315             return false;
316         }
317         if (info == "y" || info == "yes") {
318             return true;
319         }
320         retryCount++;
321     } while (retryCount < MAX_RETRY_COUNT);
322     return (retryCount >= MAX_RETRY_COUNT) ? false : true;
323 }
324 
SendRawData(std::string rawData) const325 void HostUpdater::SendRawData(std::string rawData) const
326 {
327     HdcSessionBase *sessionBase = (HdcSessionBase *)clsSession;
328     if (sessionBase == nullptr) {
329         WRITE_LOG(LOG_FATAL, "sessionBase is null");
330         return;
331     }
332     sessionBase->ServerCommand(taskInfo->sessionId, taskInfo->channelId, CMD_KERNEL_ECHO_RAW,
333         reinterpret_cast<uint8_t *>(rawData.data()), rawData.size());
334 }
335 } // namespace Hdc