1 /*
2  * Copyright (c) 2023 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 
16 #include "bin_process.h"
17 #include <string>
18 #include <thread>
19 #include "applypatch/partition_record.h"
20 #include "log.h"
21 #include "pkg_manager_impl.h"
22 #include "pkg_package/pkg_pkgfile.h"
23 #include "pkg_utils.h"
24 #include "ring_buffer/ring_buffer.h"
25 #include "script_manager.h"
26 #include "threadpool/thread_pool.h"
27 #include "scope_guard.h"
28 #include "securec.h"
29 
30 using namespace std;
31 using namespace Hpackage;
32 using namespace Uscript;
33 
34 namespace Updater {
35 constexpr uint32_t STASH_BUFFER_SIZE = 4 * 1024 * 1024;
36 constexpr uint32_t MAX_BUFFER_NUM = 16;
37 constexpr uint8_t ES_IMAGE = 6;
38 constexpr uint8_t CS_IMAGE = 7;
39 constexpr uint8_t NEED_VERIFY_CS_IMAGE = 8;
40 
Execute(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)41 int32_t UScriptInstructionBinFlowWrite::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
42 {
43     std::string upgradeFileName;
44     int32_t ret = context.GetParam(0, upgradeFileName);
45     if (ret != USCRIPT_SUCCESS) {
46         LOG(ERROR) << "Error to get bin file";
47         return ret;
48     }
49 
50     LOG(INFO) << "UScriptInstructionUpdateFromZip::Execute " << upgradeFileName;
51     PkgManager::PkgManagerPtr pkgManager = env.GetPkgManager();
52     if (pkgManager == nullptr) {
53         LOG(ERROR) << "Error to get pkg manager";
54         return USCRIPT_INVALID_PARAM;
55     }
56 
57     RingBuffer ringBuffer;
58     if (!ringBuffer.Init(STASH_BUFFER_SIZE, MAX_BUFFER_NUM)) {
59         LOG(ERROR) << "Error to get ringbuffer";
60         return USCRIPT_INVALID_PARAM;
61     }
62 
63     fullUpdateProportion_ = GetScriptProportion();
64     stashBuffer_.data.resize(STASH_BUFFER_SIZE);
65     stashBuffer_.buffer = stashBuffer_.data.data();
66     PkgManager::StreamPtr binFlowStream = nullptr;
67     const FileInfo *info = pkgManager->GetFileInfo(upgradeFileName);
68     if (info == nullptr) {
69         LOG(ERROR) << "Get file info fail " << upgradeFileName;
70         return PKG_INVALID_FILE;
71     }
72     ret = pkgManager->CreatePkgStream(binFlowStream, upgradeFileName, info->unpackedSize, &ringBuffer);
73     if (ret != USCRIPT_SUCCESS || binFlowStream == nullptr) {
74         LOG(ERROR) << "Error to create output stream";
75         return USCRIPT_INVALID_PARAM;
76     }
77 
78     std::thread consumer([this, &env, &context, binFlowStream] {
79         this->ProcessBinFile(env, context, binFlowStream);
80         });
81     std::thread producer([this, &env, &context, binFlowStream] {
82         this->ExtractBinFile(env, context, binFlowStream);
83         });
84     consumer.join();
85     producer.join();
86     if (isStopRun_) {
87         LOG(ERROR) << "Error to Execute bin file update";
88         return USCRIPT_ERROR_EXECUTE;
89     }
90     return USCRIPT_SUCCESS;
91 }
92 
ExtractBinFile(Uscript::UScriptEnv & env,Uscript::UScriptContext & context,PkgManager::StreamPtr stream)93 int32_t UScriptInstructionBinFlowWrite::ExtractBinFile(Uscript::UScriptEnv &env, Uscript::UScriptContext &context,
94     PkgManager::StreamPtr stream)
95 {
96     ON_SCOPE_EXIT(failExecute) {
97         isStopRun_ = true;
98         stream->Stop();
99     };
100     std::string upgradeFileName;
101     int32_t ret = context.GetParam(0, upgradeFileName);
102     if (ret != USCRIPT_SUCCESS) {
103         LOG(ERROR) << "Error to get bin file";
104         return ret;
105     }
106 
107     LOG(INFO) << "UScriptInstructionBinFlowWrite::ExtractBinFile " << upgradeFileName;
108     PkgManager::PkgManagerPtr pkgManager = env.GetPkgManager();
109     if (pkgManager == nullptr) {
110         LOG(ERROR) << "Error to get pkg manager";
111         return USCRIPT_INVALID_PARAM;
112     }
113 
114     PkgManager::StreamPtr processStream = nullptr;
115     PkgStream::ExtractFileProcessor processor =
116         [this](const PkgBuffer &buffer, size_t size, size_t start, bool isFinish, const void *context) {
117             return this->UnCompressDataProducer(buffer, size, start, isFinish, context);
118         };
119     ret = pkgManager->CreatePkgStream(processStream, upgradeFileName, processor, stream);
120     if (ret != USCRIPT_SUCCESS || processStream == nullptr) {
121         LOG(ERROR) << "Error to create output stream";
122         return USCRIPT_INVALID_PARAM;
123     }
124 
125     ret = pkgManager->ExtractFile(upgradeFileName, processStream);
126     if (ret != USCRIPT_SUCCESS) {
127         LOG(ERROR) << "Error to extract" << upgradeFileName;
128         pkgManager->ClosePkgStream(processStream);
129         return USCRIPT_ERROR_EXECUTE;
130     }
131     pkgManager->ClosePkgStream(processStream);
132     CANCEL_SCOPE_EXIT_GUARD(failExecute);
133     return USCRIPT_SUCCESS;
134 }
135 
UnCompressDataProducer(const PkgBuffer & buffer,size_t size,size_t start,bool isFinish,const void * context)136 int32_t UScriptInstructionBinFlowWrite::UnCompressDataProducer(const PkgBuffer &buffer, size_t size, size_t start,
137                                                                bool isFinish, const void *context)
138 {
139     if (isStopRun_) {
140         LOG(ERROR) << "recive stop single, UnCompressDataProducer stop run";
141         return USCRIPT_ERROR_EXECUTE;
142     }
143 
144     void *p = const_cast<void *>(context);
145     PkgStream *flowStream = static_cast<PkgStream *>(p);
146     if (flowStream == nullptr) {
147         LOG(ERROR) << "ring buffer is nullptr";
148         return PKG_INVALID_STREAM;
149     }
150 
151     if (buffer.buffer == nullptr && size == 0 && isFinish) {
152         // 最后一块数据
153         if (stashDataSize_ != 0) {
154             size_t writeOffset = flowStream->GetFileLength() - stashDataSize_;
155             if (flowStream->Write(stashBuffer_, stashDataSize_, writeOffset) != USCRIPT_SUCCESS) {
156                 LOG(ERROR) << "UnCompress flowStream write fail";
157                 return USCRIPT_ERROR_EXECUTE;
158             }
159             stashDataSize_ = 0;
160         }
161         LOG(INFO) << "extract finished, start";
162         return USCRIPT_SUCCESS;
163     }
164 
165     if (buffer.buffer == nullptr || size == 0 || start < stashDataSize_) {
166         LOG(ERROR) << "invalid para, size: " << size << "start: " << start;
167         return USCRIPT_ERROR_EXECUTE;
168     }
169 
170     size_t writeSize = 0;
171     size_t copyLen = 0;
172     // 缓存4M再写入数据流
173     while (size - writeSize > STASH_BUFFER_SIZE - stashDataSize_) {
174         copyLen = STASH_BUFFER_SIZE - stashDataSize_;
175         if (memcpy_s(stashBuffer_.buffer + stashDataSize_, copyLen, buffer.buffer + writeSize, copyLen) != EOK) {
176             return USCRIPT_ERROR_EXECUTE;
177         }
178 
179         if (flowStream->Write(stashBuffer_, STASH_BUFFER_SIZE, start - stashDataSize_) != USCRIPT_SUCCESS) {
180             LOG(ERROR) << "UnCompress flowStream write fail";
181             return USCRIPT_ERROR_EXECUTE;
182         }
183         writeSize += copyLen;
184         stashDataSize_ = 0;
185     }
186 
187     copyLen = size - writeSize;
188     if (memcpy_s(stashBuffer_.buffer + stashDataSize_, copyLen, buffer.buffer + writeSize, copyLen) != EOK) {
189         return USCRIPT_ERROR_EXECUTE;
190     }
191     stashDataSize_ += copyLen;
192     if (stashDataSize_ == STASH_BUFFER_SIZE) {
193         if (flowStream->Write(stashBuffer_, stashDataSize_, start - stashDataSize_ + copyLen) != USCRIPT_SUCCESS) {
194             LOG(ERROR) << "UnCompress flowStream write fail";
195             return USCRIPT_ERROR_EXECUTE;
196         }
197         stashDataSize_ = 0;
198     }
199     return PKG_SUCCESS;
200 }
201 
ProcessBinFile(Uscript::UScriptEnv & env,Uscript::UScriptContext & context,PkgManager::StreamPtr stream)202 int32_t UScriptInstructionBinFlowWrite::ProcessBinFile(Uscript::UScriptEnv &env, Uscript::UScriptContext &context,
203                                                        PkgManager::StreamPtr stream)
204 {
205     if (stream == nullptr) {
206         LOG(ERROR) << "Error to get file stream";
207         return USCRIPT_ERROR_EXECUTE;
208     }
209     ON_SCOPE_EXIT(failExecute) {
210         isStopRun_ = true;
211         stream->Stop();
212     };
213     std::string pkgFileName;
214     int32_t ret = context.GetParam(0, pkgFileName);
215     if (ret != USCRIPT_SUCCESS) {
216         LOG(ERROR) << "Error to get pkgFileName";
217         return ret;
218     }
219 
220     LOG(INFO) << "UScriptInstructionBinFlowWrite::Execute " << pkgFileName;
221     PkgManager::PkgManagerPtr manager = env.GetPkgManager();
222     if (manager == nullptr) {
223         LOG(ERROR) << "Error to get pkg manager";
224         return USCRIPT_INVALID_PARAM;
225     }
226     std::vector<std::string> innerFileNames;
227     ret = manager->LoadPackageWithStream(pkgFileName, Utils::GetCertName(),
228         innerFileNames, PkgFile::PKG_TYPE_UPGRADE, stream);
229     if (ret != USCRIPT_SUCCESS) {
230         LOG(ERROR) << "Error to load flow data stream";
231         return USCRIPT_ERROR_EXECUTE;
232     }
233 
234     for (const auto &iter : innerFileNames) {
235         // 根据镜像名称获取分区名称和大小
236         std::string partitionName = iter;
237         const FileInfo *info = manager->GetFileInfo(partitionName);
238         if (info == nullptr) {
239             LOG(ERROR) << "Error to get file info";
240             return USCRIPT_ERROR_EXECUTE;
241         }
242 
243         LOG(INFO) << " start process Component " << partitionName << " unpackedSize " << info->unpackedSize;
244         if (ComponentProcess(env, stream, partitionName, *info) != USCRIPT_SUCCESS) {
245             LOG(ERROR) << "Error to process " << partitionName;
246             return USCRIPT_ERROR_EXECUTE;
247         }
248     }
249     CANCEL_SCOPE_EXIT_GUARD(failExecute);
250     LOG(INFO)<<"UScriptInstructionBinFlowWrite finish";
251     return USCRIPT_SUCCESS;
252 }
253 
IsMatchedCsEsIamge(const Hpackage::FileInfo & fileInfo)254 bool UScriptInstructionBinFlowWrite::IsMatchedCsEsIamge(const Hpackage::FileInfo &fileInfo)
255 {
256     if ((fileInfo.resType == ES_IMAGE && !Utils::IsEsDevice()) ||
257         (fileInfo.resType == CS_IMAGE && Utils::IsEsDevice())) {
258         LOG(INFO) << "not matched cs es image, skip write";
259         return false;
260     }
261     return true;
262 }
263 
CheckEsDeviceUpdate(const Hpackage::FileInfo & fileInfo)264 bool UScriptInstructionBinFlowWrite::CheckEsDeviceUpdate(const Hpackage::FileInfo &fileInfo)
265 {
266     if (fileInfo.resType == NEED_VERIFY_CS_IMAGE && Utils::IsEsDevice()) {
267         LOG(ERROR) << "pkg just cs image, but device is es";
268         return false;
269     }
270     return true;
271 }
272 
ComponentProcess(Uscript::UScriptEnv & env,PkgManager::StreamPtr stream,const std::string & name,const Hpackage::FileInfo & fileInfo)273 int32_t UScriptInstructionBinFlowWrite::ComponentProcess(Uscript::UScriptEnv &env, PkgManager::StreamPtr stream,
274                                                          const std::string &name, const Hpackage::FileInfo &fileInfo)
275 {
276     size_t fileSize = fileInfo.unpackedSize;
277     // 根据镜像名获取组件处理类名
278     std::unique_ptr<ComponentProcessor> processor =
279         ComponentProcessorFactory::GetInstance().GetProcessor(name, fileSize);
280 
281     if (env.IsRetry()) {
282         LOG(DEBUG) << "Retry updater, check if current partition updated already during last time";
283         bool isUpdated = PartitionRecord::GetInstance().IsPartitionUpdated(name);
284         if (isUpdated) {
285             LOG(INFO) << name << " already updated, skip";
286             processor.reset();
287             processor = std::make_unique<SkipImgProcessor>(name, fileSize);
288         }
289     }
290 
291     if (!CheckEsDeviceUpdate(fileInfo)) {
292         LOG(ERROR) << "pkg just cs image, es device not allow update";
293         return USCRIPT_ERROR_EXECUTE;
294     }
295 
296     if ((processor == nullptr && fileInfo.resType == UPGRADE_FILE_COMP_OTHER_TPYE) ||
297         !IsMatchedCsEsIamge(fileInfo)) {
298         LOG(INFO) << name << " comp is not register and comp file is not image, or not match cs es image, skip";
299         processor.reset();
300         processor = std::make_unique<SkipImgProcessor>(name, fileSize);
301     }
302 
303     if (processor == nullptr) {
304         LOG(ERROR) << "GetProcessor failed, partition name: " << name;
305         return USCRIPT_ERROR_EXECUTE;
306     }
307     processor->SetPkgFileInfo(stream->GetReadOffset(), stream->GetFileLength(), fullUpdateProportion_);
308     LOG(INFO) << "component read offset " << stream->GetReadOffset();
309 
310     if (processor->PreProcess(env) != USCRIPT_SUCCESS) {
311         LOG(ERROR) << "Error to PreProcess " << name;
312         return USCRIPT_ERROR_EXECUTE;
313     }
314 
315     if (processor->DoProcess(env) != USCRIPT_SUCCESS) {
316         LOG(ERROR) << "Error to DoProcess " << name;
317         return USCRIPT_ERROR_EXECUTE;
318     }
319 
320     if (processor->PostProcess(env) != USCRIPT_SUCCESS) {
321         LOG(ERROR) << "Error to PostProcess " << name;
322         return USCRIPT_ERROR_EXECUTE;
323     }
324 
325     return USCRIPT_SUCCESS;
326 }
327 } // namespace Updater
328