• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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