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