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