1/*
2 * Copyright (c) 2022 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_patch.h"
16#include <cerrno>
17#include <fcntl.h>
18#include <pthread.h>
19#include <sstream>
20#include <sys/mman.h>
21#include <sys/stat.h>
22#include <sys/types.h>
23#include <unistd.h>
24#include <memory>
25#include <vector>
26#include "applypatch/block_set.h"
27#include "applypatch/store.h"
28#include "applypatch/transfer_manager.h"
29#include "applypatch/partition_record.h"
30#include "diffpatch/diffpatch.h"
31#include "dump.h"
32#include "fs_manager/mount.h"
33#include "log/log.h"
34#include "patch/update_patch.h"
35#include "updater/updater_const.h"
36#include "updater/hwfault_retry.h"
37#include "utils.h"
38
39using namespace Uscript;
40using namespace Hpackage;
41using namespace Updater;
42
43namespace Updater {
44constexpr uint32_t IMAGE_PATCH_CMD_LEN = 6;
45constexpr uint32_t IMAGE_PATCH_CHECK_CMD_LEN = 5;
46
47int32_t USInstrImagePatch::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
48{
49    int32_t result = ExecuteImagePatch(env, context);
50    context.PushParam(result);
51    return result;
52}
53
54int32_t USInstrImagePatch::GetParam(Uscript::UScriptContext &context, ImagePatchPara &para)
55{
56    if (context.GetParamCount() != IMAGE_PATCH_CMD_LEN) {
57        LOG(ERROR) << "para count error " << context.GetParamCount();
58        return USCRIPT_INVALID_PARAM;
59    }
60
61    int index = 0;
62    uint32_t ret = static_cast<uint32_t>(context.GetParam(index++, para.partName));
63    ret |= static_cast<uint32_t>(context.GetParam(index++, para.srcSize));
64    ret |= static_cast<uint32_t>(context.GetParam(index++, para.srcHash));
65    ret |= static_cast<uint32_t>(context.GetParam(index++, para.destSize));
66    ret |= static_cast<uint32_t>(context.GetParam(index++, para.destHash));
67    ret |= static_cast<uint32_t>(context.GetParam(index++, para.patchFile));
68    if (ret != USCRIPT_SUCCESS) {
69        LOG(ERROR) << "para get error";
70        return USCRIPT_INVALID_PARAM;
71    }
72    para.devPath = GetBlockDeviceByMountPoint(para.partName);
73    if (para.devPath.empty()) {
74        LOG(ERROR) << "get " << para.partName << " dev path error";
75        return USCRIPT_ERROR_EXECUTE;
76    }
77    return USCRIPT_SUCCESS;
78}
79
80std::string USInstrImagePatch::GetFileHash(const std::string &file)
81{
82    UpdatePatch::MemMapInfo mapBuffer {};
83    if (PatchMapFile(file, mapBuffer) != UpdatePatch::PATCH_SUCCESS) {
84        LOG(ERROR) << "PatchMapFile error";
85        return "";
86    }
87    UpdatePatch::BlockBuffer data = { mapBuffer.memory, mapBuffer.length };
88    std::string resultSha = UpdatePatch::GeneraterBufferHash(data);
89    std::transform(resultSha.begin(), resultSha.end(), resultSha.begin(), ::toupper);
90    return resultSha;
91}
92
93int32_t USInstrImagePatch::ApplyPatch(const ImagePatchPara &para, const UpdatePatch::MemMapInfo &srcData,
94    const PkgBuffer &patchData)
95{
96    std::vector<uint8_t> empty;
97    UpdatePatch::PatchParam patchParam = {
98        srcData.memory, srcData.length, patchData.buffer, patchData.length
99    };
100    std::unique_ptr<DataWriter> writer = DataWriter::CreateDataWriter(WRITE_RAW, para.devPath);
101    if (writer.get() == nullptr) {
102        LOG(ERROR) << "Cannot create block writer, pkgdiff patch abort!";
103        return -1;
104    }
105    std::string resultSha = para.destHash;
106    std::transform(resultSha.begin(), resultSha.end(), resultSha.begin(), ::tolower);
107    int32_t ret = UpdatePatch::UpdateApplyPatch::ApplyImagePatch(patchParam, empty,
108        [&](size_t start, const UpdatePatch::BlockBuffer &data, size_t size) -> int {
109            return (writer->Write(data.buffer, size, nullptr)) ? 0 : -1;
110        }, resultSha);
111    writer.reset();
112    if (ret != 0) {
113        LOG(ERROR) << "Fail to ApplyImagePatch";
114        return -1;
115    }
116    return USCRIPT_SUCCESS;
117}
118
119int32_t USInstrImagePatch::CreatePatchStream(Uscript::UScriptEnv &env, const ImagePatchPara &para,
120    PkgManager::StreamPtr &patchStream)
121{
122    if (env.GetPkgManager() == nullptr) {
123        LOG(ERROR) << "Error to get pkg manager";
124        return -1;
125    }
126
127    std::string patchName = para.patchFile;
128    const FileInfo *info = env.GetPkgManager()->GetFileInfo(patchName);
129    if (info == nullptr) {
130        LOG(WARNING) << "Error to get file info " << para.patchFile; // 兼容旧升级包
131        patchName = para.partName;
132        info = env.GetPkgManager()->GetFileInfo(patchName);
133        if (info == nullptr) {
134            return -1;
135        }
136    }
137
138    std::string patchFile = UPDATER_PATH + para.patchFile;
139    int32_t ret = env.GetPkgManager()->CreatePkgStream(patchStream,
140        patchFile, info->unpackedSize, PkgStream::PkgStreamType_MemoryMap);
141    if (ret != PKG_SUCCESS || patchStream == nullptr) {
142        LOG(ERROR) << "Error to create output stream";
143        return -1;
144    }
145
146    ret = env.GetPkgManager()->ExtractFile(patchName, patchStream);
147    if (ret != PKG_SUCCESS) {
148        env.GetPkgManager()->ClosePkgStream(patchStream);
149        LOG(ERROR) << "Error to extract file " << para.patchFile;
150        return -1;
151    }
152
153    LOG(INFO) << "USInstrImagePatch::CreatePatchStream " << para.partName;
154    return USCRIPT_SUCCESS;
155}
156
157std::string USInstrImagePatch::GetSourceFile(const ImagePatchPara &para)
158{
159    // Back up partitions to prevent power failures during the upgrade.
160    std::string srcFile = UPDATER_PATH + para.partName + ".backup";
161
162    if (access(srcFile.c_str(), F_OK) == 0 && GetFileHash(srcFile) != para.srcHash) {
163        LOG(INFO) << "using backup file:" << srcFile;
164        return srcFile;
165    }
166
167    if (!Utils::CopyFile(para.devPath, srcFile)) {
168        LOG(ERROR) << "copy " << para.devPath << " to " << srcFile << " failed";
169        return "";
170    }
171    return srcFile;
172}
173
174int32_t USInstrImagePatch::ExecuteImagePatch(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
175{
176    ImagePatchPara para {};
177    int32_t ret = GetParam(context, para);
178    if (ret != USCRIPT_SUCCESS) {
179        UPDATER_LAST_WORD(ret);
180        LOG(ERROR) << "GetParam error";
181        return ret;
182    }
183
184    if (env.IsRetry()) {
185        LOG(DEBUG) << "Retry updater, check if current partition updatered already during last time";
186        if (PartitionRecord::GetInstance().IsPartitionUpdated(para.partName)) {
187            LOG(INFO) << para.partName << " already updated, skip";
188            return USCRIPT_SUCCESS;
189        }
190    }
191
192    std::string srcFile = GetSourceFile(para);
193    if (srcFile.empty()) {
194        UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
195        LOG(ERROR) << "get source file error";
196        return USCRIPT_ERROR_EXECUTE;
197    }
198    UpdatePatch::MemMapInfo srcData {};
199    ret = UpdatePatch::PatchMapFile(srcFile, srcData);
200    if (ret != 0) {
201        UPDATER_LAST_WORD(ret);
202        LOG(ERROR) << "Failed to mmap src file error:" << ret;
203        return -1;
204    }
205
206    PkgManager::StreamPtr patchStream = nullptr;
207    ret = CreatePatchStream(env, para, patchStream);
208    if (ret != USCRIPT_SUCCESS) {
209        UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
210        LOG(ERROR) << "CreatePatchStream error";
211        return USCRIPT_ERROR_EXECUTE;
212    }
213    PkgBuffer patchData = {};
214    patchStream->GetBuffer(patchData);
215
216    ret = ApplyPatch(para, srcData, patchData);
217    if (ret != USCRIPT_SUCCESS) {
218        env.GetPkgManager()->ClosePkgStream(patchStream);
219        return ret;
220    }
221
222    PartitionRecord::GetInstance().RecordPartitionUpdateStatus(para.partName, true);
223    env.GetPkgManager()->ClosePkgStream(patchStream);
224    unlink(srcFile.c_str());
225    LOG(INFO) << "USInstrImageCheck::Execute ret:" << ret;
226    return ret;
227}
228
229int32_t USInstrImageShaCheck::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
230{
231    int32_t result = ExecuteShaCheck(env, context);
232    context.PushParam(result);
233    return result;
234}
235
236int32_t USInstrImageShaCheck::GetParam(Uscript::UScriptContext &context, CheckPara &para)
237{
238    if (context.GetParamCount() != IMAGE_PATCH_CHECK_CMD_LEN) {
239        LOG(ERROR) << "para count error " << context.GetParamCount();
240        return USCRIPT_INVALID_PARAM;
241    }
242    int index = 0;
243    uint32_t ret = static_cast<uint32_t>(context.GetParam(index++, para.partName));
244    ret |= static_cast<uint32_t>(context.GetParam(index++, para.srcSize));
245    ret |= static_cast<uint32_t>(context.GetParam(index++, para.srcHash));
246    ret |= static_cast<uint32_t>(context.GetParam(index++, para.destSize));
247    ret |= static_cast<uint32_t>(context.GetParam(index++, para.destHash));
248    if (ret != USCRIPT_SUCCESS) {
249        LOG(ERROR) << "para get error";
250        return USCRIPT_INVALID_PARAM;
251    }
252
253    para.devPath = GetBlockDeviceByMountPoint(para.partName);
254    if (para.devPath.empty()) {
255        LOG(ERROR) << "cannot get block device of partition" << para.partName;
256        return USCRIPT_ERROR_EXECUTE;
257    }
258    LOG(INFO) << "dev path: " << para.devPath;
259    return USCRIPT_SUCCESS;
260}
261
262int32_t USInstrImageShaCheck::CheckHash(const CheckPara &para)
263{
264    UpdatePatch::MemMapInfo mapBuffer {};
265    if (PatchMapFile(para.devPath, mapBuffer) != UpdatePatch::PATCH_SUCCESS) {
266        LOG(ERROR) << "PatchMapFile error";
267        return USCRIPT_ERROR_EXECUTE;
268    }
269    if (!std::all_of(para.srcSize.begin(), para.srcSize.end(), ::isdigit)) {
270        LOG(ERROR) << "para size error " << para.srcSize;
271        return USCRIPT_ERROR_EXECUTE;
272    }
273    size_t length = std::stoul(para.srcSize);
274    UpdatePatch::BlockBuffer data = { mapBuffer.memory, length };
275    std::string resultSha = UpdatePatch::GeneraterBufferHash(data);
276    std::transform(resultSha.begin(), resultSha.end(), resultSha.begin(), ::toupper);
277    if (resultSha != para.srcHash) {
278        LOG(ERROR) << "resultSha:" << resultSha << " srcHash:" << para.srcHash;
279        return USCRIPT_INVALID_PARAM;
280    }
281    return USCRIPT_SUCCESS;
282}
283
284int32_t USInstrImageShaCheck::ExecuteShaCheck(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
285{
286    UPDATER_INIT_RECORD;
287    if (env.IsRetry() && !Utils::CheckFaultInfo(VERIFY_FAILED_REBOOT)) {
288        return USCRIPT_SUCCESS;
289    }
290
291    CheckPara para {};
292    int32_t ret = GetParam(context, para);
293    if (ret != USCRIPT_SUCCESS) {
294        UPDATER_LAST_WORD(ret);
295        LOG(ERROR) << "GetParam error";
296        return ret;
297    }
298
299    ret = CheckHash(para);
300    if (ret != USCRIPT_SUCCESS) {
301        UPDATER_LAST_WORD(ret);
302        env.PostMessage(UPDATER_RETRY_TAG, VERIFY_FAILED_REBOOT);
303        LOG(ERROR) << "CheckHash error";
304        return ret;
305    }
306
307    LOG(INFO) << "USInstrImageCheck::Execute Success";
308    return USCRIPT_SUCCESS;
309}
310}
311
312