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