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