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