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 #include "host_updater.h"
16
17 #include <cstring>
18 #include "common.h"
19 #include "flash_define.h"
20 #include "transfer.h"
21 #include "serial_struct.h"
22
23 namespace {
24 static const std::string helpCmd = "flash";
25 static const std::string updateCmd = "update ";
26 static const std::string flashCmd = "flash ";
27 static const std::string eraseCmd = "erase ";
28 static const std::string formatCmd = "format ";
29 static const int PERCENT_FINISH = 100;
30 static const uint32_t PERCENT_CLEAR = ((uint32_t)-1);
31 }
32 namespace Hdc {
HostUpdater(HTaskInfo hTaskInfo)33 HostUpdater::HostUpdater(HTaskInfo hTaskInfo) : HdcTransferBase(hTaskInfo)
34 {
35 commandBegin = CMD_UPDATER_BEGIN;
36 commandData = CMD_UPDATER_DATA;
37 }
38
~HostUpdater()39 HostUpdater::~HostUpdater() {}
40
RunQueue(CtxFile & context)41 void HostUpdater::RunQueue(CtxFile &context)
42 {
43 refCount++;
44 context.localPath = context.taskQueue.back();
45 uv_fs_open(loopTask, &context.fsOpenReq, context.localPath.c_str(), O_RDONLY, 0, OnFileOpen);
46 context.master = true;
47 }
48
BeginTransfer(CtxFile & context,const std::string & function,const char * payload,int minParam,int fileIndex)49 bool HostUpdater::BeginTransfer(CtxFile &context,
50 const std::string &function, const char *payload, int minParam, int fileIndex)
51 {
52 int argc = 0;
53 char **argv = Base::SplitCommandToArgs(payload, &argc);
54 HOSTUPDATER_CHECK(!(argv == nullptr || argc < minParam || fileIndex >= argc), delete[]((char *)argv);
55 return false, "Invalid param for cmd \"%s\"", function.c_str());
56
57 int maxParam = minParam;
58 bool force = strstr(payload, "-f") != nullptr;
59 if (force) {
60 maxParam += 1;
61 }
62 HOSTUPDATER_CHECK(argc == maxParam, delete[]((char *)argv);
63 return false, "Invalid param for cmd \"%s\" %d", function.c_str(), maxParam);
64
65 context.transferConfig.functionName = function;
66 context.transferConfig.options = payload;
67 if (force && (fileIndex + 1 < argc) && strcmp(argv[fileIndex + 1], "-f") != 0) {
68 context.localPath = argv[fileIndex + 1];
69 } else {
70 context.localPath = argv[fileIndex];
71 }
72
73 if (MatchPackageExtendName(context.localPath, ".img")) {
74 context.transferConfig.compressType = COMPRESS_NONE;
75 } else if (MatchPackageExtendName(context.localPath, ".bin")) {
76 const char *part = strstr(payload, "fastboot");
77 HOSTUPDATER_CHECK(part != nullptr, delete[]((char *)argv);
78 return false, "Invalid image %s for cmd \"%s\"", context.localPath.c_str(), function.c_str());
79 context.transferConfig.compressType = COMPRESS_NONE;
80 } else {
81 HOSTUPDATER_CHECK((MatchPackageExtendName(context.localPath, ".zip") ||
82 MatchPackageExtendName(context.localPath, ".lz4") ||
83 MatchPackageExtendName(context.localPath, ".gz2")), delete[]((char *)argv);
84 return false,
85 "Invaid extend name \"%s\" for cmd \"%s\"", context.localPath.c_str(), function.c_str());
86 }
87
88 WRITE_LOG(LOG_DEBUG, "BeginTransfer function: %s localPath: %s command: %s ",
89 context.transferConfig.functionName.c_str(), context.localPath.c_str(), payload);
90 // check path
91 bool ret = Base::CheckDirectoryOrPath(context.localPath.c_str(), true, true);
92 HOSTUPDATER_CHECK(ret, delete[]((char *)argv);
93 return false,
94 "Invaid path \"%s\" for cmd \"%s\"", context.localPath.c_str(), function.c_str());
95 context.taskQueue.push_back(context.localPath);
96 RunQueue(context);
97 return true;
98 }
99
GetFileName(const std::string & fileName) const100 std::string HostUpdater::GetFileName(const std::string &fileName) const
101 {
102 int32_t pos = fileName.find_last_of('/');
103 if (pos < 0) { // win32
104 pos = fileName.find_last_of('\\');
105 }
106 return fileName.substr(pos + 1, fileName.size());
107 }
108
CheckMaster(CtxFile * context)109 void HostUpdater::CheckMaster(CtxFile *context)
110 {
111 uv_fs_t fs;
112 Base::ZeroStruct(fs.statbuf);
113 uv_fs_fstat(nullptr, &fs, context->fsOpenReq.result, nullptr);
114 context->transferConfig.fileSize = fs.statbuf.st_size;
115 uv_fs_req_cleanup(&fs);
116
117 WRITE_LOG(LOG_DEBUG, "CheckMaster %s %llu", context->transferConfig.functionName.c_str(), fs.statbuf.st_size);
118 context->transferConfig.optionalName = GetFileName(context->localPath);
119 std::string bufString = SerialStruct::SerializeToString(context->transferConfig);
120
121 const uint64_t verdorSize = static_cast<uint64_t>(1024 * 1024) * 256;
122 const uint64_t systemSize = static_cast<uint64_t>(1024 * 1024) * 1500;
123 const uint64_t minSize = static_cast<uint64_t>(1024 * 1024) * 10;
124 uint64_t realSize = verdorSize;
125 if (fs.statbuf.st_size > minSize) {
126 realSize += systemSize;
127 }
128 std::vector<uint8_t> buffer(sizeof(realSize) + bufString.size());
129 int ret = memcpy_s(buffer.data(), buffer.size(), &realSize, sizeof(realSize));
130 int ret2 = memcpy_s(buffer.data() + sizeof(realSize), buffer.size(), bufString.c_str(), bufString.size());
131 if ((ret == 0) && (ret2 == 0)) {
132 SendToAnother(CMD_UPDATER_CHECK, (uint8_t *)buffer.data(), buffer.size());
133 }
134 }
135
CheckCmd(const std::string & function,const char * payload,int param)136 bool HostUpdater::CheckCmd(const std::string &function, const char *payload, int param)
137 {
138 int argc = 0;
139 char **argv = Base::SplitCommandToArgs(payload, &argc);
140 HOSTUPDATER_CHECK(argv != nullptr, return false, "Can not parser cmd \"%s\"", function.c_str());
141 delete[]((char *)argv);
142 HOSTUPDATER_CHECK(argc >= param, return false, "Invalid param for cmd \"%s\" %d", function.c_str(), argc);
143 WRITE_LOG(LOG_DEBUG, "CheckCmd command: %s ", payload);
144
145 int maxParam = param;
146 if (strstr(payload, "-f") != nullptr) {
147 maxParam += 1;
148 }
149 if (strstr(payload, "-t") != nullptr) {
150 maxParam += 1;
151 maxParam += 1;
152 }
153 HOSTUPDATER_CHECK(argc == maxParam, return false,
154 "Invalid param for cmd \"%s\" %d %d", function.c_str(), argc, maxParam);
155 return true;
156 }
157
CommandDispatch(const uint16_t command,uint8_t * payload,const int payloadSize)158 bool HostUpdater::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize)
159 {
160 const int cmdFroErase = 2;
161 const int cmdFroFormat = 2;
162 #ifndef UPDATER_UT
163 if (!HdcTransferBase::CommandDispatch(command, payload, payloadSize)) {
164 return false;
165 }
166 #endif
167 bool ret = true;
168 switch (command) {
169 case CMD_UPDATER_BEGIN: {
170 std::string s("Processing: 0%%");
171 sendProgress = true;
172 SendRawData(reinterpret_cast<uint8_t *>(s.data()), s.size());
173 break;
174 }
175 case CMD_UPDATER_UPDATE_INIT:
176 ret = BeginTransfer(ctxNow, CMDSTR_UPDATE_SYSTEM, reinterpret_cast<const char *>(payload), 1, 0);
177 break;
178 case CMD_UPDATER_FLASH_INIT:
179 ret = BeginTransfer(ctxNow, CMDSTR_FLASH_PARTITION,
180 reinterpret_cast<const char *>(payload), 2, 1); // 2 cmd min param for flash
181 break;
182 case CMD_UPDATER_FINISH:
183 ret = CheckUpdateContinue(command, payload, payloadSize);
184 break;
185 case CMD_UPDATER_ERASE: {
186 if (!CheckCmd(CMDSTR_ERASE_PARTITION, reinterpret_cast<const char *>(payload), cmdFroErase)) {
187 return false;
188 }
189 SendToAnother(CMD_UPDATER_ERASE, payload, payloadSize);
190 ctxNow.taskQueue.push_back(reinterpret_cast<char *>(payload));
191 break;
192 }
193 case CMD_UPDATER_FORMAT: {
194 if (!CheckCmd(CMDSTR_FORMAT_PARTITION, reinterpret_cast<const char *>(payload), cmdFroFormat)) {
195 return false;
196 }
197 SendToAnother(CMD_UPDATER_FORMAT, payload, payloadSize);
198 ctxNow.taskQueue.push_back(reinterpret_cast<char *>(payload));
199 break;
200 }
201 case CMD_UPDATER_PROGRESS:
202 if (payloadSize >= (int)sizeof(uint32_t)) {
203 ProcessProgress(*(uint32_t *)payload);
204 }
205 break;
206 default:
207 break;
208 }
209 return ret;
210 }
211
ProcessProgress(uint32_t percentage)212 void HostUpdater::ProcessProgress(uint32_t percentage)
213 {
214 if (!sendProgress) {
215 return;
216 }
217 std::string backStr = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
218 std::string breakStr = "\n";
219 WRITE_LOG(LOG_INFO, "ProcessProgress %d", percentage);
220 const int bufferSize = 128;
221 std::vector<char> buffer(bufferSize);
222 if (percentage == PERCENT_CLEAR) { // clear
223 SendRawData(reinterpret_cast<uint8_t *>(backStr.data()), backStr.size());
224 SendRawData(reinterpret_cast<uint8_t *>(breakStr.data()), breakStr.size());
225 sendProgress = false;
226 return;
227 }
228 int len = sprintf_s(buffer.data(), buffer.size() - 1, "%sProcessing: %3d%%", backStr.c_str(), percentage);
229 HOSTUPDATER_CHECK(len > 0, return, "Failed to format progress info ");
230 SendRawData(reinterpret_cast<uint8_t *>(buffer.data()), len);
231 if (percentage == PERCENT_FINISH) {
232 SendRawData(reinterpret_cast<uint8_t *>(breakStr.data()), breakStr.size());
233 sendProgress = false;
234 }
235 }
236
CheckUpdateContinue(const uint16_t command,const uint8_t * payload,int payloadSize)237 bool HostUpdater::CheckUpdateContinue(const uint16_t command, const uint8_t *payload, int payloadSize)
238 {
239 HOSTUPDATER_CHECK(static_cast<size_t>(payloadSize) >= sizeof(uint16_t),
240 return false, "Failed to check payload size %d ", payloadSize);
241 MessageLevel level = (MessageLevel)payload[1];
242 if ((level == MSG_OK) && sendProgress) {
243 ProcessProgress(PERCENT_FINISH);
244 }
245 std::string info((char*)(payload + sizeof(uint16_t)), payloadSize - sizeof(uint16_t));
246 if (!info.empty()) {
247 LogMsg(level, "%s", info.c_str());
248 }
249 WRITE_LOG(LOG_DEBUG, "CheckUpdateContinue payloadSize %d %d %s", payloadSize, level, info.c_str());
250 if (ctxNow.taskQueue.size() != 0) {
251 ctxNow.taskQueue.pop_back();
252 }
253 if (singalStop || !ctxNow.taskQueue.size()) {
254 return false;
255 }
256 RunQueue(ctxNow);
257 return true;
258 }
259
CheckMatchUpdate(const std::string & input,std::string & stringError,uint16_t & cmdFlag,bool & bJumpDo)260 bool HostUpdater::CheckMatchUpdate(const std::string &input,
261 std::string &stringError, uint16_t &cmdFlag, bool &bJumpDo)
262 {
263 WRITE_LOG(LOG_DEBUG, "CheckMatchUpdate command:%s", input.c_str());
264 size_t cmdLen = updateCmd.size();
265 if (!strncmp(input.c_str(), updateCmd.c_str(), updateCmd.size())) {
266 cmdFlag = CMD_UPDATER_UPDATE_INIT;
267 cmdLen = updateCmd.size();
268 } else if (!strncmp(input.c_str(), flashCmd.c_str(), flashCmd.size())) {
269 cmdFlag = CMD_UPDATER_FLASH_INIT;
270 cmdLen = flashCmd.size();
271 } else if (!strncmp(input.c_str(), eraseCmd.c_str(), eraseCmd.size())) {
272 cmdFlag = CMD_UPDATER_ERASE;
273 cmdLen = eraseCmd.size();
274 } else if (!strncmp(input.c_str(), formatCmd.c_str(), formatCmd.size())) {
275 cmdFlag = CMD_UPDATER_FORMAT;
276 cmdLen = formatCmd.size();
277 } else {
278 return false;
279 }
280 if (input.size() <= cmdLen) {
281 stringError = "Incorrect command";
282 bJumpDo = true;
283 }
284 return true;
285 }
286
287 #ifdef UPDATER_UT
288 static std::string g_input = "yes";
SetInput(const std::string & input)289 void HostUpdater::SetInput(const std::string &input)
290 {
291 g_input = input;
292 }
293 #endif
ConfirmCommand(const string & commandIn,bool & closeInput)294 bool HostUpdater::ConfirmCommand(const string &commandIn, bool &closeInput)
295 {
296 std::string tip = "";
297 if (!strncmp(commandIn.c_str(), updateCmd.c_str(), updateCmd.size())) {
298 closeInput = true;
299 } else if (!strncmp(commandIn.c_str(), flashCmd.c_str(), flashCmd.size())) {
300 tip = "Confirm flash partition";
301 closeInput = true;
302 } else if (!strncmp(commandIn.c_str(), eraseCmd.c_str(), eraseCmd.size())) {
303 tip = "Confirm erase partition";
304 } else if (!strncmp(commandIn.c_str(), formatCmd.c_str(), formatCmd.size())) {
305 tip = "Confirm format partition";
306 }
307 if (tip.empty() || strstr(commandIn.c_str(), " -f") != nullptr) { // check if -f
308 return true;
309 }
310 const size_t minLen = strlen("yes");
311 int retryCount = 0;
312 do {
313 printf("%s ? (Yes/No) ", tip.c_str());
314 fflush(stdin);
315 std::string info = {};
316 size_t i = 0;
317 while (1) {
318 #ifndef UPDATER_UT
319 char c = getchar();
320 #else
321 char c = '\n';
322 info = g_input;
323 #endif
324 if (c == '\r' || c == '\n') {
325 break;
326 }
327 if (c == ' ') {
328 continue;
329 }
330 if (i < minLen && isprint(c)) {
331 info.append(1, std::tolower(c));
332 i++;
333 }
334 }
335 if (info == "n" || info == "no") {
336 return false;
337 }
338 if (info == "y" || info == "yes") {
339 return true;
340 }
341 retryCount++;
342 } while (retryCount < 3); // 3 retry max count
343 return (retryCount >= 3) ? false : true; // 3 retry max count
344 }
345
SendRawData(uint8_t * bufPtr,const int size)346 void HostUpdater::SendRawData(uint8_t *bufPtr, const int size)
347 {
348 #ifndef UPDATER_UT
349 HdcSessionBase *sessionBase = (HdcSessionBase *)clsSession;
350 sessionBase->ServerCommand(taskInfo->sessionId,
351 taskInfo->channelId, CMD_KERNEL_ECHO_RAW, bufPtr, size);
352 #endif
353 }
354 } // namespace Hdc