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 "update_image_block.h"
16 #include <cerrno>
17 #include <fcntl.h>
18 #include <pthread.h>
19 #include <sstream>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include "applypatch/block_set.h"
24 #include "applypatch/store.h"
25 #include "applypatch/transfer_manager.h"
26 #include "applypatch/partition_record.h"
27 #include "fs_manager/mount.h"
28 #include "log/dump.h"
29 #include "log/log.h"
30 #include "updater/updater_const.h"
31 #include "updater/hwfault_retry.h"
32 #include "utils.h"
33
34 using namespace Uscript;
35 using namespace Hpackage;
36 using namespace Updater;
37
38 namespace Updater {
39 constexpr int32_t SHA_CHECK_SECOND = 2;
40 constexpr int32_t SHA_CHECK_PARAMS = 3;
41 constexpr int32_t SHA_CHECK_TARGETPAIRS_INDEX = 3;
42 constexpr int32_t SHA_CHECK_TARGETSHA_INDEX = 4;
43 constexpr int32_t SHA_CHECK_TARGET_PARAMS = 5;
ExtractNewData(const PkgBuffer &buffer, size_t size, size_t start, bool isFinish, const void* context)44 static int ExtractNewData(const PkgBuffer &buffer, size_t size, size_t start, bool isFinish, const void* context)
45 {
46 void *p = const_cast<void *>(context);
47 WriterThreadInfo *info = static_cast<WriterThreadInfo *>(p);
48 uint8_t *addr = buffer.buffer;
49 while (size > 0) {
50 pthread_mutex_lock(&info->mutex);
51 while (info->writer == nullptr) {
52 if (!info->readyToWrite) {
53 LOG(WARNING) << "writer is not ready to write.";
54 pthread_mutex_unlock(&info->mutex);
55 return Hpackage::PKG_INVALID_STREAM;
56 }
57 pthread_cond_wait(&info->cond, &info->mutex);
58 }
59 pthread_mutex_unlock(&info->mutex);
60 size_t toWrite = std::min(size, info->writer->GetBlocksSize() - info->writer->GetTotalWritten());
61 // No more data to write.
62 if (toWrite == 0) {
63 break;
64 }
65 bool ret = info->writer->Write(addr, toWrite, nullptr);
66 std::ostringstream logMessage;
67 logMessage << "Write " << toWrite << " byte(s) failed";
68 if (!ret) {
69 LOG(ERROR) << logMessage.str();
70 return Hpackage::PKG_INVALID_STREAM;
71 }
72 size -= toWrite;
73 addr += toWrite;
74
75 if (info->writer->IsWriteDone()) {
76 pthread_mutex_lock(&info->mutex);
77 info->writer.reset();
78 pthread_cond_broadcast(&info->cond);
79 pthread_mutex_unlock(&info->mutex);
80 }
81 }
82 return Hpackage::PKG_SUCCESS;
83 }
84
CondBroadcast(WriterThreadInfo *info)85 static inline void CondBroadcast(WriterThreadInfo *info)
86 {
87 pthread_mutex_lock(&info->mutex);
88 info->readyToWrite = false;
89 if (info->writer != nullptr) {
90 pthread_cond_broadcast(&info->cond);
91 }
92 pthread_mutex_unlock(&info->mutex);
93 }
94
UnpackNewData(void *arg)95 void* UnpackNewData(void *arg)
96 {
97 TransferManagerPtr tm = static_cast<TransferManagerPtr>(arg);
98 WriterThreadInfo *info = tm->GetTransferParams()->writerThreadInfo.get();
99 Hpackage::PkgManager::StreamPtr stream = nullptr;
100 if (info->newPatch.empty()) {
101 LOG(ERROR) << "new patch file name is empty. thread quit.";
102 CondBroadcast(info);
103 return nullptr;
104 }
105 LOG(DEBUG) << "new patch file name: " << info->newPatch;
106 auto env = tm->GetTransferParams()->env;
107 const FileInfo *file = env->GetPkgManager()->GetFileInfo(info->newPatch);
108 if (file == nullptr) {
109 LOG(ERROR) << "Cannot get file info of :" << info->newPatch;
110 CondBroadcast(info);
111 return nullptr;
112 }
113 LOG(DEBUG) << info->newPatch << " info: size " << file->packedSize << " unpacked size " <<
114 file->unpackedSize << " name " << file->identity;
115 int32_t ret = env->GetPkgManager()->CreatePkgStream(stream, info->newPatch, ExtractNewData, info);
116 if (ret != Hpackage::PKG_SUCCESS || stream == nullptr) {
117 LOG(ERROR) << "Cannot extract " << info->newPatch << " from package.";
118 CondBroadcast(info);
119 return nullptr;
120 }
121 ret = env->GetPkgManager()->ExtractFile(info->newPatch, stream);
122 env->GetPkgManager()->ClosePkgStream(stream);
123 LOG(DEBUG) << "new data writer ending...";
124 // extract new data done.
125 // tell command.
126 CondBroadcast(info);
127 return nullptr;
128 }
129
ReturnAndPushParam(int32_t returnValue, Uscript::UScriptContext &context)130 static int32_t ReturnAndPushParam(int32_t returnValue, Uscript::UScriptContext &context)
131 {
132 context.PushParam(returnValue);
133 return returnValue;
134 }
135
136 struct UpdateBlockInfo {
137 std::string partitionName;
138 std::string transferName;
139 std::string newDataName;
140 std::string patchDataName;
141 std::string devPath;
142 };
143
GetUpdateBlockInfo(struct UpdateBlockInfo &infos, Uscript::UScriptEnv &env, Uscript::UScriptContext &context)144 static int32_t GetUpdateBlockInfo(struct UpdateBlockInfo &infos, Uscript::UScriptEnv &env,
145 Uscript::UScriptContext &context)
146 {
147 if (context.GetParamCount() != 4) { // 4:Determine the number of parameters
148 LOG(ERROR) << "Invalid param";
149 return ReturnAndPushParam(USCRIPT_INVALID_PARAM, context);
150 }
151
152 // Get partition Name first.
153 // Use partition name as zip file name. ${partition name}.zip
154 // load ${partition name}.zip from updater package.
155 // Try to unzip ${partition name}.zip, extract transfer.list, net.dat, patch.dat
156 size_t pos = 0;
157 int32_t ret = context.GetParam(pos++, infos.partitionName);
158 if (ret != USCRIPT_SUCCESS) {
159 LOG(ERROR) << "Error to get param 1";
160 return ret;
161 }
162 ret = context.GetParam(pos++, infos.transferName);
163 if (ret != USCRIPT_SUCCESS) {
164 LOG(ERROR) << "Error to get param 2";
165 return ret;
166 }
167 ret = context.GetParam(pos++, infos.newDataName);
168 if (ret != USCRIPT_SUCCESS) {
169 LOG(ERROR) << "Error to get param 3";
170 return ret;
171 }
172 ret = context.GetParam(pos++, infos.patchDataName);
173 if (ret != USCRIPT_SUCCESS) {
174 LOG(ERROR) << "Error to get param 4";
175 return ret;
176 }
177
178 LOG(INFO) << "ExecuteUpdateBlock::updating " << infos.partitionName << " ...";
179 infos.devPath = GetBlockDeviceByMountPoint(infos.partitionName);
180 LOG(INFO) << "ExecuteUpdateBlock::updating dev path : " << infos.devPath;
181 if (infos.devPath.empty()) {
182 LOG(ERROR) << "cannot get block device of partition";
183 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
184 }
185 return USCRIPT_SUCCESS;
186 }
187
ExecuteTransferCommand(int fd, const std::vector<std::string> &lines, TransferManagerPtr tm, Uscript::UScriptContext &context, const UpdateBlockInfo &infos)188 static int32_t ExecuteTransferCommand(int fd, const std::vector<std::string> &lines, TransferManagerPtr tm,
189 Uscript::UScriptContext &context, const UpdateBlockInfo &infos)
190 {
191 auto transferParams = tm->GetTransferParams();
192 auto writerThreadInfo = transferParams->writerThreadInfo.get();
193
194 transferParams->storeBase = std::string("/data/updater") + infos.partitionName + "_tmp";
195 transferParams->retryFile = std::string("/data/updater") + infos.partitionName + "_retry";
196 transferParams->devPath = infos.devPath;
197 LOG(INFO) << "Store base path is " << transferParams->storeBase;
198 int32_t ret = Store::CreateNewSpace(transferParams->storeBase, !transferParams->env->IsRetry());
199 if (ret == -1) {
200 LOG(ERROR) << "Error to create new store space";
201 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
202 }
203 transferParams->storeCreated = ret;
204
205 if (!tm->CommandsParser(fd, lines)) {
206 return USCRIPT_ERROR_EXECUTE;
207 }
208 pthread_mutex_lock(&writerThreadInfo->mutex);
209 if (writerThreadInfo->readyToWrite) {
210 LOG(WARNING) << "New data writer thread is still available...";
211 }
212
213 writerThreadInfo->readyToWrite = false;
214 pthread_cond_broadcast(&writerThreadInfo->cond);
215 pthread_mutex_unlock(&writerThreadInfo->mutex);
216 ret = pthread_join(transferParams->thread, nullptr);
217 std::ostringstream logMessage;
218 logMessage << "pthread join returned with " << ret;
219 if (ret != 0) {
220 LOG(WARNING) << logMessage.str();
221 }
222 if (transferParams->storeCreated != -1) {
223 Store::DoFreeSpace(transferParams->storeBase);
224 }
225 return USCRIPT_SUCCESS;
226 }
227
InitThread(const struct UpdateBlockInfo &infos, TransferManagerPtr tm)228 static int InitThread(const struct UpdateBlockInfo &infos, TransferManagerPtr tm)
229 {
230 auto transferParams = tm->GetTransferParams();
231 auto writerThreadInfo = transferParams->writerThreadInfo.get();
232 writerThreadInfo->readyToWrite = true;
233 pthread_mutex_init(&writerThreadInfo->mutex, nullptr);
234 pthread_cond_init(&writerThreadInfo->cond, nullptr);
235 pthread_attr_t attr;
236 pthread_attr_init(&attr);
237 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
238 writerThreadInfo->newPatch = infos.newDataName;
239 int error = pthread_create(&transferParams->thread, &attr, UnpackNewData, tm);
240 return error;
241 }
242
ExtractDiffPackageAndLoad(const UpdateBlockInfo &infos, Uscript::UScriptEnv &env, Uscript::UScriptContext &context)243 static int32_t ExtractDiffPackageAndLoad(const UpdateBlockInfo &infos, Uscript::UScriptEnv &env,
244 Uscript::UScriptContext &context)
245 {
246 Hpackage::PkgManager::StreamPtr outStream = nullptr;
247 LOG(DEBUG) << "partitionName is " << infos.partitionName;
248 const FileInfo *info = env.GetPkgManager()->GetFileInfo(infos.partitionName);
249 if (info == nullptr) {
250 LOG(WARNING) << "Error to get file info";
251 return USCRIPT_SUCCESS;
252 }
253 std::string diffPackage = std::string("/data/updater") + infos.partitionName;
254 int32_t ret = env.GetPkgManager()->CreatePkgStream(outStream,
255 diffPackage, info->unpackedSize, PkgStream::PkgStreamType_Write);
256 if (outStream == nullptr) {
257 LOG(ERROR) << "Error to create output stream";
258 return USCRIPT_ERROR_EXECUTE;
259 }
260
261 ret = env.GetPkgManager()->ExtractFile(infos.partitionName, outStream);
262 if (ret != USCRIPT_SUCCESS) {
263 LOG(ERROR) << "Error to extract file";
264 env.GetPkgManager()->ClosePkgStream(outStream);
265 return USCRIPT_ERROR_EXECUTE;
266 }
267 env.GetPkgManager()->ClosePkgStream(outStream);
268 std::string diffPackageZip = diffPackage + ".zip";
269 if (rename(diffPackage.c_str(), diffPackageZip.c_str()) != 0) {
270 LOG(ERROR) << "rename failed";
271 return USCRIPT_ERROR_EXECUTE;
272 }
273 LOG(DEBUG) << "Rename " << diffPackage << " to zip\nExtract " << diffPackage << " done\nReload " << diffPackageZip;
274 std::vector<std::string> diffPackageComponents;
275 ret = env.GetPkgManager()->LoadPackage(diffPackageZip, Updater::Utils::GetCertName(), diffPackageComponents);
276 if (diffPackageComponents.size() < 1) {
277 LOG(ERROR) << "Diff package is empty";
278 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
279 }
280 return USCRIPT_SUCCESS;
281 }
282
DoExecuteUpdateBlock(const UpdateBlockInfo &infos, TransferManagerPtr tm, Hpackage::PkgManager::StreamPtr &outStream, const std::vector<std::string> &lines, Uscript::UScriptContext &context)283 static int32_t DoExecuteUpdateBlock(const UpdateBlockInfo &infos, TransferManagerPtr tm,
284 Hpackage::PkgManager::StreamPtr &outStream, const std::vector<std::string> &lines, Uscript::UScriptContext &context)
285 {
286 int fd = open(infos.devPath.c_str(), O_RDWR | O_LARGEFILE);
287 auto env = tm->GetTransferParams()->env;
288 if (fd == -1) {
289 LOG(ERROR) << "Failed to open block";
290 env->GetPkgManager()->ClosePkgStream(outStream);
291 return USCRIPT_ERROR_EXECUTE;
292 }
293 int32_t ret = ExecuteTransferCommand(fd, lines, tm, context, infos);
294 fsync(fd);
295 close(fd);
296 fd = -1;
297 env->GetPkgManager()->ClosePkgStream(outStream);
298 if (ret == USCRIPT_SUCCESS) {
299 PartitionRecord::GetInstance().RecordPartitionUpdateStatus(infos.partitionName, true);
300 }
301 return ret;
302 }
303
ExtractFileByName(Uscript::UScriptEnv &env, const std::string &fileName, Hpackage::PkgManager::StreamPtr &outStream, uint8_t *&outBuf, size_t &buffSize)304 static int32_t ExtractFileByName(Uscript::UScriptEnv &env, const std::string &fileName,
305 Hpackage::PkgManager::StreamPtr &outStream, uint8_t *&outBuf, size_t &buffSize)
306 {
307 if (env.GetPkgManager() == nullptr) {
308 LOG(ERROR) << "Error to get pkg manager";
309 return USCRIPT_ERROR_EXECUTE;
310 }
311
312 const FileInfo *info = env.GetPkgManager()->GetFileInfo(fileName);
313 if (info == nullptr) {
314 LOG(ERROR) << "GetFileInfo fail";
315 return USCRIPT_ERROR_EXECUTE;
316 }
317 auto ret = env.GetPkgManager()->CreatePkgStream(outStream,
318 fileName, info->unpackedSize, PkgStream::PkgStreamType_MemoryMap);
319 if (ret != USCRIPT_SUCCESS || outStream == nullptr) {
320 LOG(ERROR) << "Error to create output stream";
321 return USCRIPT_ERROR_EXECUTE;
322 }
323 ret = env.GetPkgManager()->ExtractFile(fileName, outStream);
324 if (ret != USCRIPT_SUCCESS) {
325 LOG(ERROR) << "Error to extract file";
326 env.GetPkgManager()->ClosePkgStream(outStream);
327 return USCRIPT_ERROR_EXECUTE;
328 }
329 ret = outStream->GetBuffer(outBuf, buffSize);
330 LOG(DEBUG) << "outBuf data size is: " << buffSize;
331
332 return USCRIPT_SUCCESS;
333 }
334
ExecuteUpdateBlock(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)335 static int32_t ExecuteUpdateBlock(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
336 {
337 UpdateBlockInfo infos {};
338 if (GetUpdateBlockInfo(infos, env, context) != USCRIPT_SUCCESS) {
339 return USCRIPT_ERROR_EXECUTE;
340 }
341
342 if (env.IsRetry()) {
343 LOG(DEBUG) << "Retry updater, check if current partition updatered already during last time";
344 if (PartitionRecord::GetInstance().IsPartitionUpdated(infos.partitionName)) {
345 LOG(INFO) << infos.partitionName << " already updated, skip";
346 return USCRIPT_SUCCESS;
347 }
348 }
349
350 if (ExtractDiffPackageAndLoad(infos, env, context) != USCRIPT_SUCCESS) {
351 return USCRIPT_ERROR_EXECUTE;
352 }
353
354 uint8_t *transferListBuffer = nullptr;
355 size_t transferListSize = 0;
356 Hpackage::PkgManager::StreamPtr outStream = nullptr;
357 if (ExtractFileByName(env, infos.transferName, outStream,
358 transferListBuffer, transferListSize) != USCRIPT_SUCCESS) {
359 return USCRIPT_ERROR_EXECUTE;
360 }
361
362 std::unique_ptr<TransferManager> tm = std::make_unique<TransferManager>();
363
364 auto transferParams = tm->GetTransferParams();
365 /* Save Script Env to transfer manager */
366 transferParams->env = &env;
367
368 transferParams->canWrite = true;
369 std::vector<std::string> lines =
370 Updater::Utils::SplitString(std::string(reinterpret_cast<const char*>(transferListBuffer)), "\n");
371 // Close stream opened before.
372 env.GetPkgManager()->ClosePkgStream(outStream);
373
374 LOG(INFO) << "Start unpack new data thread done. Get patch data: " << infos.patchDataName;
375 if (ExtractFileByName(env, infos.patchDataName, outStream,
376 transferParams->patchDataBuffer, transferParams->patchDataSize) != USCRIPT_SUCCESS) {
377 return USCRIPT_ERROR_EXECUTE;
378 }
379
380 LOG(INFO) << "Ready to start a thread to handle new data processing";
381 if (InitThread(infos, tm.get()) != 0) {
382 LOG(ERROR) << "Failed to create pthread";
383 env.GetPkgManager()->ClosePkgStream(outStream);
384 return USCRIPT_ERROR_EXECUTE;
385 }
386
387 return DoExecuteUpdateBlock(infos, tm.get(), outStream, lines, context);
388 }
389
Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)390 int32_t UScriptInstructionBlockUpdate::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
391 {
392 int32_t result = ExecuteUpdateBlock(env, context);
393 context.PushParam(result);
394 return result;
395 }
396
ExecReadBlockInfo(const std::string &devPath, Uscript::UScriptContext &context, time_t &mountTime, uint16_t &mountCount)397 bool UScriptInstructionBlockCheck::ExecReadBlockInfo(const std::string &devPath, Uscript::UScriptContext &context,
398 time_t &mountTime, uint16_t &mountCount)
399 {
400 UPDATER_INIT_RECORD;
401 int fd = open(devPath.c_str(), O_RDWR | O_LARGEFILE);
402 if (fd == -1) {
403 LOG(ERROR) << "Failed to open file";
404 UPDATER_LAST_WORD(false);
405 return false;
406 }
407 std::vector<uint8_t> block_buff(H_BLOCK_SIZE);
408 BlockSet blk0(std::vector<BlockPair> {BlockPair{0, 1}});
409
410 size_t pos = 0;
411 std::vector<BlockPair>::iterator it = blk0.Begin();
412 for (; it != blk0.End(); ++it) {
413 LOG(INFO) << "BlockSet::ReadDataFromBlock lseek64";
414 if (lseek64(fd, static_cast<off64_t>(it->first * H_BLOCK_SIZE), SEEK_SET) == -1) {
415 LOG(ERROR) << "Failed to seek";
416 close(fd);
417 UPDATER_LAST_WORD(false);
418 return false;
419 }
420 size_t size = (it->second - it->first) * H_BLOCK_SIZE;
421 LOG(INFO) << "BlockSet::ReadDataFromBlock Read " << size << " from block";
422 if (!Utils::ReadFully(fd, block_buff.data() + pos, size)) {
423 LOG(ERROR) << "Failed to read";
424 close(fd);
425 UPDATER_LAST_WORD(false);
426 return false;
427 }
428 pos += size;
429 }
430 close(fd);
431 mountTime = *reinterpret_cast<uint32_t *>(&block_buff[0x400 + 0x2C]);
432 mountCount = *reinterpret_cast<uint16_t *>(&block_buff[0x400 + 0x34]);
433 return true;
434 }
435
Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)436 int32_t UScriptInstructionBlockCheck::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
437 {
438 if (context.GetParamCount() != 1) {
439 LOG(ERROR) << "Invalid param";
440 UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
441 return ReturnAndPushParam(USCRIPT_INVALID_PARAM, context);
442 }
443 if (env.IsRetry()) {
444 return ReturnAndPushParam(USCRIPT_SUCCESS, context);
445 }
446 std::string partitionName;
447 int32_t ret = context.GetParam(0, partitionName);
448 if (ret != USCRIPT_SUCCESS) {
449 LOG(ERROR) << "Failed to get param";
450 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
451 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
452 }
453 auto devPath = GetBlockDeviceByMountPoint(partitionName);
454 LOG(INFO) << "UScriptInstructionBlockCheck::dev path : " << devPath;
455 time_t mountTime = 0;
456 uint16_t mountCount = 0;
457 if (devPath.empty() || (!ExecReadBlockInfo(devPath, context, mountTime, mountCount))) {
458 LOG(ERROR) << "cannot get block device of partition";
459 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
460 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
461 }
462
463 if (mountCount > 0) {
464 std::ostringstream ostr;
465 ostr << "Device was remounted R/W " << mountCount << "times\nLast remount happened on " <<
466 ctime(&mountTime) << std::endl;
467 std::string message = ostr.str();
468 env.PostMessage("ui_log", message);
469 LOG(ERROR) << message;
470 }
471 LOG(INFO) << "UScriptInstructionBlockCheck::Execute Success";
472 context.PushParam(USCRIPT_SUCCESS);
473 return USCRIPT_SUCCESS;
474 }
475
GetPartName(const std::string &partitionName)476 static std::string GetPartName(const std::string &partitionName)
477 {
478 if (partitionName.empty()) {
479 return "";
480 }
481 return partitionName[0] == '/' ? partitionName.substr(1) : partitionName;
482 }
483
DoBlocksVerify(Uscript::UScriptEnv &env, const std::string &partitionName, const std::string &devPath)484 int32_t UScriptInstructionShaCheck::DoBlocksVerify(Uscript::UScriptEnv &env, const std::string &partitionName,
485 const std::string &devPath)
486 {
487 UpdateBlockInfo infos {};
488 infos.partitionName = partitionName;
489 infos.transferName = GetPartName(partitionName) + ".transfer.list";
490 uint8_t *transferListBuffer = nullptr;
491 size_t transferListSize = 0;
492 Hpackage::PkgManager::StreamPtr outStream = nullptr;
493
494 if (ExtractFileByName(env, infos.transferName, outStream,
495 transferListBuffer, transferListSize) != USCRIPT_SUCCESS) {
496 LOG(ERROR) << "Error to extract " << infos.transferName;
497 return USCRIPT_ERROR_EXECUTE;
498 }
499
500 std::vector<std::string> lines =
501 Updater::Utils::SplitString(std::string(reinterpret_cast<const char*>(transferListBuffer)), "\n");
502 // Close stream opened before.
503 env.GetPkgManager()->ClosePkgStream(outStream);
504
505 std::unique_ptr<TransferManager> tm = std::make_unique<TransferManager>();
506 auto transferParams = tm->GetTransferParams();
507 transferParams->env = &env;
508 transferParams->canWrite = false;
509 transferParams->devPath = devPath;
510 transferParams->storeBase = std::string("/data/updater") + partitionName + "_tmp";
511 transferParams->retryFile = std::string("/data/updater") + partitionName + "_retry";
512
513 LOG(INFO) << "Store base path is " << transferParams->storeBase;
514 transferParams->storeCreated = Store::CreateNewSpace(transferParams->storeBase, !transferParams->env->IsRetry());
515 if (transferParams->storeCreated == -1) {
516 LOG(ERROR) << "Error to create new store space";
517 return USCRIPT_ERROR_EXECUTE;
518 }
519 int fd = open(devPath.c_str(), O_RDWR | O_LARGEFILE);
520 if (fd == -1) {
521 LOG(ERROR) << "Failed to open block";
522 return USCRIPT_ERROR_EXECUTE;
523 }
524 if (!tm->CommandsParser(fd, lines)) {
525 close(fd);
526 LOG(ERROR) << "Failed to block verify";
527 return USCRIPT_ERROR_EXECUTE;
528 }
529 if (transferParams->storeCreated != -1) {
530 Store::DoFreeSpace(transferParams->storeBase);
531 }
532 close(fd);
533 return USCRIPT_SUCCESS;
534 }
535
IsTargetShaDiff(const std::string &devPath, const ShaInfo &shaInfo)536 bool UScriptInstructionShaCheck::IsTargetShaDiff(const std::string &devPath, const ShaInfo &shaInfo)
537 {
538 std::string tgtResultSha = CalculateBlockSha(devPath, shaInfo.targetPairs);
539 if (tgtResultSha.empty()) {
540 LOG(WARNING) << "target sha is empty";
541 return true;
542 }
543 LOG(INFO) << "tgtResultSha: " << tgtResultSha << ", shaInfo.targetSha: " << shaInfo.targetSha;
544 return (tgtResultSha != shaInfo.targetSha);
545 }
546
ExecReadShaInfo(Uscript::UScriptEnv &env, const std::string &devPath, const ShaInfo &shaInfo, const std::string &partitionName)547 int UScriptInstructionShaCheck::ExecReadShaInfo(Uscript::UScriptEnv &env, const std::string &devPath,
548 const ShaInfo &shaInfo, const std::string &partitionName)
549 {
550 UPDATER_INIT_RECORD;
551 std::string resultSha = CalculateBlockSha(devPath, shaInfo.blockPairs);
552 if (resultSha != shaInfo.contrastSha && IsTargetShaDiff(devPath, shaInfo)) {
553 if (DoBlocksVerify(env, partitionName, devPath) != USCRIPT_SUCCESS) {
554 LOG(ERROR) << "Different sha256, cannot continue";
555 LOG(ERROR) << "blockPairs:" << shaInfo.blockPairs;
556 LOG(ERROR) << "resultSha: " << resultSha << ", shaInfo.contrastSha: " << shaInfo.contrastSha;
557 PrintAbnormalBlockHash(devPath, shaInfo.blockPairs);
558 UPDATER_LAST_WORD(devPath.substr(devPath.find_last_of("/") + 1), USCRIPT_ERROR_EXECUTE);
559 env.PostMessage(UPDATER_RETRY_TAG, VERIFY_FAILED_REBOOT);
560 return USCRIPT_ERROR_EXECUTE;
561 }
562 }
563 LOG(INFO) << "UScriptInstructionShaCheck::Execute Success";
564 return USCRIPT_SUCCESS;
565 }
566
PrintAbnormalBlockHash(const std::string &devPath, const std::string &blockPairs)567 void UScriptInstructionShaCheck::PrintAbnormalBlockHash(const std::string &devPath, const std::string &blockPairs)
568 {
569 int fd = open(devPath.c_str(), O_RDWR | O_LARGEFILE);
570 if (fd == -1) {
571 LOG(ERROR) << "Failed to open file " << devPath;
572 return;
573 }
574
575 BlockSet blk;
576 blk.ParserAndInsert(blockPairs);
577 std::vector<uint8_t> block_buff(H_BLOCK_SIZE);
578 std::vector<BlockPair>::iterator it = blk.Begin();
579 for (; it != blk.End(); ++it) {
580 if (lseek64(fd, static_cast<off64_t>(it->first * H_BLOCK_SIZE), SEEK_SET) == -1) {
581 LOG(ERROR) << "Failed to seek";
582 close(fd);
583 return;
584 }
585 SHA256_CTX ctx;
586 SHA256_Init(&ctx);
587 for (size_t i = it->first; i < it->second; ++i) {
588 if (!Utils::ReadFully(fd, block_buff.data(), H_BLOCK_SIZE)) {
589 LOG(ERROR) << "Failed to read";
590 close(fd);
591 return;
592 }
593 SHA256_Update(&ctx, block_buff.data(), H_BLOCK_SIZE);
594 }
595 uint8_t digest[SHA256_DIGEST_LENGTH] = {0};
596 SHA256_Final(digest, &ctx);
597 LOG(ERROR) << "block id:" << it->first << "-" << it->second <<
598 " hex:" << Utils::ConvertSha256Hex(digest, SHA256_DIGEST_LENGTH);
599 }
600 close(fd);
601 }
602
CalculateBlockSha(const std::string &devPath, const std::string &blockPairs)603 std::string UScriptInstructionShaCheck::CalculateBlockSha(const std::string &devPath, const std::string &blockPairs)
604 {
605 if (blockPairs.empty()) {
606 LOG(ERROR) << "Failed to get blockPairs";
607 return "";
608 }
609
610 int fd = open(devPath.c_str(), O_RDWR | O_LARGEFILE);
611 if (fd == -1) {
612 LOG(ERROR) << "Failed to open file";
613 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
614 return "";
615 }
616
617 BlockSet blk;
618 blk.ParserAndInsert(blockPairs);
619 std::vector<uint8_t> block_buff(H_BLOCK_SIZE);
620 SHA256_CTX ctx;
621 SHA256_Init(&ctx);
622 std::vector<BlockPair>::iterator it = blk.Begin();
623 for (; it != blk.End(); ++it) {
624 if (lseek64(fd, static_cast<off64_t>(it->first * H_BLOCK_SIZE), SEEK_SET) == -1) {
625 LOG(ERROR) << "Failed to seek";
626 close(fd);
627 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
628 return "";
629 }
630 for (size_t i = it->first; i < it->second; ++i) {
631 if (!Utils::ReadFully(fd, block_buff.data(), H_BLOCK_SIZE)) {
632 LOG(ERROR) << "Failed to read";
633 close(fd);
634 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
635 return "";
636 }
637 SHA256_Update(&ctx, block_buff.data(), H_BLOCK_SIZE);
638 }
639 }
640 close(fd);
641
642 uint8_t digest[SHA256_DIGEST_LENGTH] = {0};
643 SHA256_Final(digest, &ctx);
644 return Utils::ConvertSha256Hex(digest, SHA256_DIGEST_LENGTH);
645 }
646
SetShaInfo(Uscript::UScriptContext &context, ShaInfo &shaInfo)647 int32_t UScriptInstructionShaCheck::SetShaInfo(Uscript::UScriptContext &context, ShaInfo &shaInfo)
648 {
649 int32_t ret = context.GetParam(1, shaInfo.blockPairs);
650 if (ret != USCRIPT_SUCCESS) {
651 LOG(ERROR) << "Failed to get param blockPairs";
652 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
653 return USCRIPT_ERROR_EXECUTE;
654 }
655
656 ret = context.GetParam(SHA_CHECK_SECOND, shaInfo.contrastSha);
657 if (ret != USCRIPT_SUCCESS) {
658 LOG(ERROR) << "Failed to get param contrastSha";
659 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
660 return USCRIPT_ERROR_EXECUTE;
661 }
662
663 // Only three parameters can be obtained for the upgrade package of an earlier version.
664 ret = context.GetParam(SHA_CHECK_TARGETPAIRS_INDEX, shaInfo.targetPairs);
665 if (ret != USCRIPT_SUCCESS) {
666 LOG(WARNING) << "Failed to get param targetPairs";
667 }
668
669 ret = context.GetParam(SHA_CHECK_TARGETSHA_INDEX, shaInfo.targetSha);
670 if (ret != USCRIPT_SUCCESS) {
671 LOG(WARNING) << "Failed to get param targetSha";
672 }
673
674 return USCRIPT_SUCCESS;
675 }
676
Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)677 int32_t UScriptInstructionShaCheck::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
678 {
679 int32_t paramCount = context.GetParamCount();
680 if (paramCount != SHA_CHECK_PARAMS && paramCount != SHA_CHECK_TARGET_PARAMS) {
681 LOG(ERROR) << "Invalid param";
682 UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
683 return ReturnAndPushParam(USCRIPT_INVALID_PARAM, context);
684 }
685 if (env.IsRetry() && !Utils::CheckFaultInfo(VERIFY_FAILED_REBOOT)) {
686 return ReturnAndPushParam(USCRIPT_SUCCESS, context);
687 }
688
689 std::string partitionName;
690 int32_t ret = context.GetParam(0, partitionName);
691 if (ret != USCRIPT_SUCCESS) {
692 LOG(ERROR) << "Failed to get param";
693 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
694 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
695 }
696
697 ShaInfo shaInfo {};
698 ret = SetShaInfo(context, shaInfo);
699 if (ret != USCRIPT_SUCCESS) {
700 LOG(ERROR) << "Failed to set sha info";
701 return ReturnAndPushParam(ret, context);
702 }
703
704 auto devPath = GetBlockDeviceByMountPoint(partitionName);
705 LOG(INFO) << "UScriptInstructionShaCheck::dev path : " << devPath;
706 if (devPath.empty()) {
707 LOG(ERROR) << "cannot get block device of partition";
708 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
709 return ReturnAndPushParam(USCRIPT_ERROR_EXECUTE, context);
710 }
711 ret = ExecReadShaInfo(env, devPath, shaInfo, partitionName);
712 return ReturnAndPushParam(ret, context);
713 }
714 }
715