1 /*
2 * Copyright (c) 2021 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_processor.h"
16 #include <cstdio>
17 #include <memory>
18 #include <string>
19 #include <unistd.h>
20 #include <pthread.h>
21 #include "securec.h"
22 #include "applypatch/data_writer.h"
23 #include "applypatch/partition_record.h"
24 #include "applypatch/update_progress.h"
25 #include "dump.h"
26 #include "log.h"
27 #include "package/hash_data_verifier.h"
28 #include "pkg_manager.h"
29 #ifdef UPDATER_USE_PTABLE
30 #include "ptable_manager.h"
31 #endif
32 #include "script_instruction.h"
33 #include "script_manager.h"
34 #include "slot_info/slot_info.h"
35 #include "update_image_block.h"
36 #include "update_image_patch.h"
37 #include "update_partitions.h"
38 #include "updater_main.h"
39 #include "updater/updater_const.h"
40 #include "update_bin/bin_process.h"
41 #include "scope_guard.h"
42
43 using namespace Uscript;
44 using namespace Hpackage;
45 using namespace Updater;
46
47 namespace Updater {
48 size_t UScriptInstructionRawImageWrite::totalSize_ = 0;
49 size_t UScriptInstructionRawImageWrite::readSize_ = 0;
50 size_t UScriptInstructionUpdateFromBin::stashDataSize_ = 0;
51
GetFinalBinaryResult(int32_t result)52 __attribute__((weak)) int32_t GetFinalBinaryResult(int32_t result)
53 {
54 return result;
55 }
56
~UpdaterEnv()57 UpdaterEnv::~UpdaterEnv()
58 {
59 if (factory_ != nullptr) {
60 delete factory_;
61 factory_ = nullptr;
62 }
63 }
64
PostMessage(const std::string & cmd,std::string content)65 void UpdaterEnv::PostMessage(const std::string &cmd, std::string content)
66 {
67 if (postMessage_ != nullptr) {
68 std::lock_guard<std::mutex> lock(messageLock_);
69 postMessage_(cmd.c_str(), content.c_str());
70 }
71 }
72
GetInstructionFactory()73 UScriptInstructionFactoryPtr UpdaterEnv::GetInstructionFactory()
74 {
75 if (factory_ == nullptr) {
76 factory_ = new UpdaterInstructionFactory();
77 }
78 return factory_;
79 }
80
GetInstructionNames() const81 const std::vector<std::string> UpdaterEnv::GetInstructionNames() const
82 {
83 static std::vector<std::string> updaterCmds = {
84 "sha_check", "first_block_check", "block_update",
85 "raw_image_write", "update_partitions", "image_patch",
86 "image_sha_check", "pkg_extract", "pkg_extract_no_ret", "update_from_bin"
87 };
88 return updaterCmds;
89 }
90
CreateInstructionInstance(UScriptInstructionPtr & instr,const std::string & name)91 int32_t UpdaterInstructionFactory::CreateInstructionInstance(UScriptInstructionPtr& instr,
92 const std::string& name)
93 {
94 if (name == "sha_check") {
95 instr = new UScriptInstructionShaCheck();
96 } else if (name == "first_block_check") {
97 instr = new UScriptInstructionBlockCheck();
98 } else if (name == "block_update") {
99 instr = new UScriptInstructionBlockUpdate();
100 } else if (name == "raw_image_write") {
101 instr = new UScriptInstructionRawImageWrite();
102 } else if (name == "update_partitions") {
103 instr = new UpdatePartitions();
104 } else if (name == "image_patch") {
105 instr = new USInstrImagePatch();
106 } else if (name == "image_sha_check") {
107 instr = new USInstrImageShaCheck();
108 } else if (name == "pkg_extract") {
109 instr = new UScriptInstructionPkgExtract();
110 } else if (name == "pkg_extract_no_ret") {
111 instr = new UScriptInstructionPkgExtractRetSuc();
112 } else if (name == "update_from_bin") {
113 instr = new UScriptInstructionBinFlowWrite();
114 }
115 return USCRIPT_SUCCESS;
116 }
117
RawImageWriteProcessor(const PkgBuffer & buffer,size_t size,size_t start,bool isFinish,const void * context)118 int UScriptInstructionRawImageWrite::RawImageWriteProcessor(const PkgBuffer &buffer, size_t size, size_t start,
119 bool isFinish, const void* context)
120 {
121 void *p = const_cast<void *>(context);
122 DataWriter *writer = static_cast<DataWriter *>(p);
123 if (writer == nullptr) {
124 LOG(ERROR) << "Data writer is null";
125 return PKG_INVALID_STREAM;
126 }
127
128 // maybe extract from package is finished. just return.
129 if (buffer.buffer == nullptr || size == 0) {
130 return PKG_SUCCESS;
131 }
132
133 bool ret = writer->Write(const_cast<uint8_t*>(buffer.buffer), size, nullptr);
134 if (!ret) {
135 LOG(ERROR) << "Write " << size << " byte(s) failed";
136 return PKG_INVALID_STREAM;
137 }
138
139 if (totalSize_ != 0) {
140 readSize_ += size;
141 writer->GetUpdaterEnv()->PostMessage("set_progress", std::to_string((float)readSize_ / totalSize_));
142 }
143
144 return PKG_SUCCESS;
145 }
146
147 bool UScriptInstructionRawImageWrite::WriteRawImage(const std::string &partitionName,
148 const std::unique_ptr<DataWriter> &writer, [[maybe_unused]] uint64_t partitionSize, Uscript::UScriptEnv &env)
149 {
150 UPDATER_INIT_RECORD;
151 // Extract partition information
152 const FileInfo *info = env.GetPkgManager()->GetFileInfo(partitionName);
153 if (info == nullptr) {
154 LOG(ERROR) << "Error to get file info";
155 UPDATER_LAST_WORD(false);
156 return false;
157 }
158 totalSize_ = info->unpackedSize;
159 #ifdef UPDATER_USE_PTABLE
160 if (partitionSize < totalSize_) {
161 LOG(ERROR) << "partition size: " << partitionSize << " is short than image size: " << totalSize_;
162 UPDATER_LAST_WORD(false);
163 return false;
164 }
165 #endif
166
167 Hpackage::PkgManager::StreamPtr outStream = nullptr;
168 int ret = env.GetPkgManager()->CreatePkgStream(outStream,
169 partitionName, RawImageWriteProcessor, writer.get());
170 if (ret != USCRIPT_SUCCESS || outStream == nullptr) {
171 LOG(ERROR) << "Error to create output stream";
172 UPDATER_LAST_WORD(false);
173 return false;
174 }
175
176 ret = env.GetPkgManager()->ExtractFile(partitionName, outStream);
177 if (ret != USCRIPT_SUCCESS) {
178 LOG(ERROR) << "Error to extract file";
179 env.GetPkgManager()->ClosePkgStream(outStream);
180 UPDATER_LAST_WORD(false);
181 return false;
182 }
183 env.GetPkgManager()->ClosePkgStream(outStream);
184 return true;
185 }
186
Execute(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)187 int32_t UScriptInstructionRawImageWrite::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
188 {
189 std::string partitionName;
190 int32_t ret = context.GetParam(0, partitionName);
191 if (ret != USCRIPT_SUCCESS) {
192 LOG(ERROR) << "Error to get partitionName";
193 UPDATER_LAST_WORD(ret);
194 return ret;
195 }
196
197 if (env.IsRetry()) {
198 LOG(DEBUG) << "Retry updater, check if current partition updated already during last time";
199 bool isUpdated = PartitionRecord::GetInstance().IsPartitionUpdated(partitionName);
200 if (isUpdated) {
201 LOG(INFO) << partitionName << " already updated, skip";
202 return USCRIPT_SUCCESS;
203 }
204 }
205 LOG(INFO) << "UScriptInstructionRawImageWrite::Execute " << partitionName;
206 if (env.GetPkgManager() == nullptr) {
207 LOG(ERROR) << "Error to get pkg manager";
208 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
209 return USCRIPT_ERROR_EXECUTE;
210 }
211
212 std::string writePath;
213 uint64_t offset = 0;
214 uint64_t partitionSize = 0;
215 if (GetWritePathAndOffset(partitionName, writePath, offset, partitionSize) != USCRIPT_SUCCESS) {
216 LOG(ERROR) << "Get partition:%s WritePathAndOffset fail \'" <<
217 partitionName.substr(1, partitionName.size()) << "\'.";
218 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
219 return USCRIPT_ERROR_EXECUTE;
220 }
221
222 std::unique_ptr<DataWriter> writer = DataWriter::CreateDataWriter(WRITE_RAW, writePath,
223 static_cast<UpdaterEnv *>(&env), offset);
224 if (writer == nullptr) {
225 LOG(ERROR) << "Error to create writer";
226 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
227 return USCRIPT_ERROR_EXECUTE;
228 }
229 if (!WriteRawImage(partitionName, writer, partitionSize, env)) {
230 DataWriter::ReleaseDataWriter(writer);
231 UPDATER_LAST_WORD(USCRIPT_ERROR_EXECUTE);
232 return USCRIPT_ERROR_EXECUTE;
233 }
234 PartitionRecord::GetInstance().RecordPartitionUpdateStatus(partitionName, true);
235 DataWriter::ReleaseDataWriter(writer);
236 totalSize_ = 0;
237 readSize_ = 0;
238 LOG(INFO) << "UScriptInstructionRawImageWrite finish";
239 return USCRIPT_SUCCESS;
240 }
241
Execute(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)242 int32_t UScriptInstructionPkgExtract::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
243 {
244 std::string pkgFileName;
245 int32_t ret = context.GetParam(0, pkgFileName);
246 if (ret != USCRIPT_SUCCESS) {
247 LOG(ERROR) << "Error to get pkgFileName";
248 UPDATER_LAST_WORD(ret);
249 return ret;
250 }
251
252 std::string destPath;
253 ret = context.GetParam(1, destPath);
254 if (ret != USCRIPT_SUCCESS) {
255 LOG(ERROR) << "Error to get destPath";
256 UPDATER_LAST_WORD(ret);
257 return ret;
258 }
259
260 LOG(INFO) << "UScriptInstructionPkgExtract::Execute " << pkgFileName;
261 PkgManager::PkgManagerPtr manager = env.GetPkgManager();
262 if (manager == nullptr) {
263 LOG(ERROR) << "Error to get pkg manager";
264 UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
265 return USCRIPT_INVALID_PARAM;
266 }
267
268 const FileInfo *info = manager->GetFileInfo(pkgFileName);
269 if (info == nullptr) {
270 LOG(ERROR) << "Error to get file info";
271 UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
272 return USCRIPT_INVALID_PARAM;
273 }
274
275 Hpackage::PkgManager::StreamPtr outStream = nullptr;
276 ret = manager->CreatePkgStream(outStream, destPath + "/" + pkgFileName, info->unpackedSize,
277 PkgStream::PkgStreamType_Write);
278 if (ret != USCRIPT_SUCCESS) {
279 LOG(ERROR) << "Error to create output stream";
280 UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
281 return USCRIPT_ERROR_EXECUTE;
282 }
283
284 ret = manager->ExtractFile(pkgFileName, outStream);
285 if (ret != USCRIPT_SUCCESS) {
286 LOG(ERROR) << "Error to extract file";
287 manager->ClosePkgStream(outStream);
288 UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
289 return USCRIPT_ERROR_EXECUTE;
290 }
291
292 manager->ClosePkgStream(outStream);
293 LOG(INFO)<<"UScriptInstructionPkgExtract finish";
294 return ret;
295 }
296
Execute(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)297 int32_t UScriptInstructionPkgExtractRetSuc::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
298 {
299 int32_t ret = UScriptInstructionPkgExtract::Execute(env, context);
300 if (ret != USCRIPT_SUCCESS) {
301 LOG(ERROR) << "Error to extract file, ret = " << ret;
302 }
303 return USCRIPT_SUCCESS;
304 }
305
Execute(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)306 int32_t UScriptInstructionUpdateFromBin::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
307 {
308 std::string upgradeFileName;
309 int32_t ret = context.GetParam(0, upgradeFileName);
310 if (ret != USCRIPT_SUCCESS) {
311 LOG(ERROR) << "Error to get partitionName";
312 return ret;
313 }
314
315 LOG(INFO) << "UScriptInstructionUpdateFromBin::Execute " << upgradeFileName;
316
317 PkgManager::PkgManagerPtr pkgManager = env.GetPkgManager();
318 if (pkgManager == nullptr) {
319 LOG(ERROR) << "Error to get pkg manager";
320 return USCRIPT_INVALID_PARAM;
321 }
322
323 RingBuffer ringBuffer;
324 if (!ringBuffer.Init(STASH_BUFFER_SIZE, BUFFER_NUM)) {
325 LOG(ERROR) << "Error to get ringbuffer";
326 return USCRIPT_INVALID_PARAM;
327 }
328
329 PkgManager::StreamPtr outStream = nullptr;
330 ret = pkgManager->CreatePkgStream(outStream, upgradeFileName, UnCompressDataProducer, &ringBuffer);
331 if (ret != USCRIPT_SUCCESS || outStream == nullptr) {
332 LOG(ERROR) << "Error to create output stream";
333 return USCRIPT_INVALID_PARAM;
334 }
335
336 ret = pkgManager->ExtractFile(upgradeFileName, outStream);
337 if (ret != USCRIPT_SUCCESS) {
338 LOG(ERROR) << "Error to extract" << upgradeFileName;
339 pkgManager->ClosePkgStream(outStream);
340 return USCRIPT_ERROR_EXECUTE;
341 }
342 pkgManager->ClosePkgStream(outStream);
343 return USCRIPT_ERROR_EXECUTE;
344 }
345
UnCompressDataProducer(const PkgBuffer & buffer,size_t size,size_t start,bool isFinish,const void * context)346 int UScriptInstructionUpdateFromBin::UnCompressDataProducer(const PkgBuffer &buffer, size_t size, size_t start,
347 bool isFinish, const void* context)
348 {
349 static PkgBuffer stashBuffer(STASH_BUFFER_SIZE);
350 size_t bufferStart = 0;
351 void *p = const_cast<void *>(context);
352 RingBuffer *ringBuffer = static_cast<RingBuffer *>(p);
353 if (ringBuffer == nullptr) {
354 LOG(ERROR) << "ring buffer is nullptr";
355 return PKG_INVALID_STREAM;
356 }
357
358 while (stashDataSize_ + size >= STASH_BUFFER_SIZE) {
359 size_t readLen = STASH_BUFFER_SIZE - stashDataSize_;
360 if (memcpy_s(stashBuffer.buffer + stashDataSize_, readLen, buffer.buffer + bufferStart, readLen) != 0) {
361 return USCRIPT_ERROR_EXECUTE;
362 }
363 ringBuffer->Push(stashBuffer.buffer, STASH_BUFFER_SIZE);
364 stashDataSize_ = 0;
365 size -= readLen;
366 bufferStart += readLen;
367 }
368 if (size == 0 && stashDataSize_ == 0) {
369 return PKG_SUCCESS;
370 } else if (size == 0 || memcpy_s(stashBuffer.buffer + stashDataSize_, STASH_BUFFER_SIZE - stashDataSize_,
371 buffer.buffer + bufferStart, size) == 0) {
372 if (isFinish) {
373 ringBuffer->Push(stashBuffer.buffer, stashDataSize_ + size);
374 stashDataSize_ = 0;
375 } else {
376 stashDataSize_ += size;
377 }
378 return PKG_SUCCESS;
379 } else {
380 return USCRIPT_ERROR_EXECUTE;
381 }
382 }
383
ExecUpdate(PkgManager::PkgManagerPtr pkgManager,int retry,const std::string & pkgPath,PostMessageFunction postMessage)384 int ExecUpdate(PkgManager::PkgManagerPtr pkgManager, int retry, const std::string &pkgPath,
385 PostMessageFunction postMessage)
386 {
387 Hpackage::HashDataVerifier scriptVerifier {pkgManager};
388 if (!scriptVerifier.LoadHashDataAndPkcs7(pkgPath)) {
389 LOG(ERROR) << "Fail to load hash data";
390 UPDATER_LAST_WORD(EXIT_VERIFY_SCRIPT_ERROR);
391 return EXIT_VERIFY_SCRIPT_ERROR;
392 }
393 UpdaterEnv* env = new (std::nothrow) UpdaterEnv(pkgManager, postMessage, retry);
394 if (env == nullptr) {
395 LOG(ERROR) << "Fail to creat env";
396 UPDATER_LAST_WORD(EXIT_PARSE_SCRIPT_ERROR);
397 return EXIT_PARSE_SCRIPT_ERROR;
398 }
399 int ret = 0;
400 ScriptManager* scriptManager = ScriptManager::GetScriptManager(env, &scriptVerifier);
401 if (scriptManager == nullptr) {
402 LOG(ERROR) << "Fail to creat scriptManager";
403 ScriptManager::ReleaseScriptManager();
404 delete env;
405 UPDATER_LAST_WORD(EXIT_PARSE_SCRIPT_ERROR);
406 return EXIT_PARSE_SCRIPT_ERROR;
407 }
408
409 UpdaterInit::GetInstance().InvokeEvent(UPDATER_BINARY_INIT_DONE_EVENT);
410
411 pthread_t thread;
412 ret = CreateProgressThread(env, thread);
413 if (ret != 0) {
414 LOG(ERROR) << "Fail to create progress thread";
415 ScriptManager::ReleaseScriptManager();
416 delete env;
417 env = nullptr;
418 UPDATER_LAST_WORD(USCRIPT_ERROR_CREATE_THREAD);
419 return USCRIPT_ERROR_CREATE_THREAD;
420 }
421
422 for (int32_t i = 0; i < ScriptManager::MAX_PRIORITY; i++) {
423 ret = scriptManager->ExecuteScript(i);
424 if (ret != USCRIPT_SUCCESS) {
425 LOG(ERROR) << "Fail to execute script";
426 break;
427 }
428 }
429 SetProgressExitFlag(thread);
430 ScriptManager::ReleaseScriptManager();
431 delete env;
432 env = nullptr;
433 return GetFinalBinaryResult(ret);
434 }
435
GetWritePathAndOffset(const std::string & partitionName,std::string & writePath,uint64_t & offset,uint64_t & partitionSize)436 int UScriptInstructionRawImageWrite::GetWritePathAndOffset(const std::string &partitionName, std::string &writePath,
437 uint64_t &offset, uint64_t &partitionSize)
438 {
439 #ifdef UPDATER_USE_PTABLE
440 DevicePtable& devicePtb = DevicePtable::GetInstance();
441 Ptable::PtnInfo ptnInfo;
442 if (!devicePtb.GetPartionInfoByName(partitionName, ptnInfo)) {
443 LOG(ERROR) << "Datawriter: cannot find device path for partition \'" <<
444 partitionName.substr(1, partitionName.size()) << "\'.";
445 return USCRIPT_ERROR_EXECUTE;
446 }
447 writePath = ptnInfo.writePath;
448 offset = ptnInfo.startAddr;
449 partitionSize = ptnInfo.partitionSize;
450 #else
451 writePath = GetBlockDeviceByMountPoint(partitionName);
452 if (writePath.empty()) {
453 LOG(ERROR) << "Datawriter: cannot find device path for partition \'" <<
454 partitionName.substr(1, partitionName.size()) << "\'.";
455 return USCRIPT_ERROR_EXECUTE;
456 }
457
458 #ifndef UPDATER_UT
459 if (partitionName != "/userdata") {
460 std::string suffix = "";
461 GetPartitionSuffix(suffix);
462 writePath += suffix;
463 }
464 LOG(INFO) << "write partition path: " << writePath;
465 #endif
466 #endif
467 return USCRIPT_SUCCESS;
468 }
469
ProcessUpdater(bool retry,int pipeFd,const std::string & packagePath,const std::string & keyPath)470 int ProcessUpdater(bool retry, int pipeFd, const std::string &packagePath, const std::string &keyPath)
471 {
472 UPDATER_INIT_RECORD;
473 UpdaterInit::GetInstance().InvokeEvent(UPDATER_BINARY_INIT_EVENT);
474 Dump::GetInstance().RegisterDump("DumpHelperLog", std::make_unique<DumpHelperLog>());
475 std::unique_ptr<FILE, decltype(&fclose)> pipeWrite(fdopen(pipeFd, "w"), fclose);
476 if (pipeWrite == nullptr) {
477 LOG(ERROR) << "Fail to fdopen, err: " << strerror(errno);
478 UPDATER_LAST_WORD(EXIT_INVALID_ARGS);
479 return EXIT_INVALID_ARGS;
480 }
481 int ret = -1;
482 Detail::ScopeGuard guard([&] {
483 (void)fprintf(pipeWrite.get(), "subProcessResult:%d\n", ret);
484 (void)fflush(pipeWrite.get());
485 });
486 // line buffered, make sure parent read per line.
487 setlinebuf(pipeWrite.get());
488 PkgManager::PkgManagerPtr pkgManager = PkgManager::CreatePackageInstance();
489 if (pkgManager == nullptr) {
490 LOG(ERROR) << "pkgManager is nullptr";
491 UPDATER_LAST_WORD(EXIT_INVALID_ARGS);
492 return EXIT_INVALID_ARGS;
493 }
494
495 std::vector<std::string> components;
496 ret = pkgManager->LoadPackage(packagePath, keyPath, components);
497 if (ret != PKG_SUCCESS) {
498 LOG(ERROR) << "Fail to load package";
499 PkgManager::ReleasePackageInstance(pkgManager);
500 UPDATER_LAST_WORD(EXIT_INVALID_ARGS);
501 return EXIT_INVALID_ARGS;
502 }
503 #ifdef UPDATER_USE_PTABLE
504 DevicePtable::GetInstance().LoadPartitionInfo();
505 #endif
506
507 ret = Updater::ExecUpdate(pkgManager, retry, packagePath,
508 [&pipeWrite](const char *cmd, const char *content) {
509 if (pipeWrite.get() != nullptr) {
510 (void)fprintf(pipeWrite.get(), "%s:%s\n", cmd, content);
511 (void)fflush(pipeWrite.get());
512 }
513 });
514 PkgManager::ReleasePackageInstance(pkgManager);
515 #ifndef UPDATER_UT
516 if (ret == 0) {
517 SetActiveSlot();
518 }
519 #endif
520 return ret;
521 }
522 } // Updater