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