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