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
34using namespace Uscript;
35using namespace Hpackage;
36using namespace Updater;
37
38namespace Updater {
39constexpr int32_t SHA_CHECK_SECOND = 2;
40constexpr int32_t SHA_CHECK_PARAMS = 3;
41constexpr int32_t SHA_CHECK_TARGETPAIRS_INDEX = 3;
42constexpr int32_t SHA_CHECK_TARGETSHA_INDEX = 4;
43constexpr int32_t SHA_CHECK_TARGET_PARAMS = 5;
44static 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
85static 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
95void* 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
130static int32_t ReturnAndPushParam(int32_t returnValue, Uscript::UScriptContext &context)
131{
132    context.PushParam(returnValue);
133    return returnValue;
134}
135
136struct UpdateBlockInfo {
137    std::string partitionName;
138    std::string transferName;
139    std::string newDataName;
140    std::string patchDataName;
141    std::string devPath;
142};
143
144static 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
188static 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
228static 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
243static 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
283static 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
304static 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
335static 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
390int32_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
397bool 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
436int32_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
476static 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
484int32_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
536bool 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
547int 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
567void 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
603std::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
647int32_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
677int32_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