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