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 
39 using namespace Uscript;
40 using namespace Hpackage;
41 using namespace Updater;
42 
43 namespace Updater {
44 constexpr uint32_t IMAGE_PATCH_CMD_LEN = 6;
45 constexpr uint32_t IMAGE_PATCH_CHECK_CMD_LEN = 5;
46 
Execute(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)47 int32_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 
GetParam(Uscript::UScriptContext & context,ImagePatchPara & para)54 int32_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 
GetFileHash(const std::string & file)80 std::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 
ApplyPatch(const ImagePatchPara & para,const UpdatePatch::MemMapInfo & srcData,const PkgBuffer & patchData)93 int32_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 
CreatePatchStream(Uscript::UScriptEnv & env,const ImagePatchPara & para,PkgManager::StreamPtr & patchStream)119 int32_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 
GetSourceFile(const ImagePatchPara & para)157 std::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 
ExecuteImagePatch(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)174 int32_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 
Execute(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)229 int32_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 
GetParam(Uscript::UScriptContext & context,CheckPara & para)236 int32_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 
CheckHash(const CheckPara & para)262 int32_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 
ExecuteShaCheck(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)284 int32_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