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