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