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 ¶)
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 ¶, 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 ¶,
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 ¶)
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 ¶)
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 ¶)
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