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
30using namespace std;
31using namespace Hpackage;
32using namespace Uscript;
33
34namespace Updater {
35constexpr uint32_t STASH_BUFFER_SIZE = 4 * 1024 * 1024;
36constexpr uint32_t MAX_BUFFER_NUM = 16;
37constexpr uint8_t ES_IMAGE = 6;
38constexpr uint8_t CS_IMAGE = 7;
39constexpr uint8_t NEED_VERIFY_CS_IMAGE = 8;
40
41int32_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
93int32_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
136int32_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
202int32_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
254bool 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
264bool 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
273int32_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