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_partitions.h"
16#include <cerrno>
17#include <cstdio>
18#include <sstream>
19#include <string>
20#include "log/dump.h"
21#include "log/log.h"
22#include "updater/updater_const.h"
23#include "utils.h"
24
25using namespace std;
26using namespace Uscript;
27using namespace Hpackage;
28using namespace Updater;
29constexpr int MIN_PARTITIONS_NUM = 2;
30constexpr int MAX_PARTITIONS_NUM = 20;
31namespace Updater {
32static bool CheckValueInt(const cJSON *item)
33{
34    if (item == nullptr || !cJSON_IsNumber(item)) {
35        LOG(ERROR) << "Error get valueint";
36        return false;
37    }
38    return true;
39}
40
41static bool CheckValueString(const cJSON *item)
42{
43    if (item == nullptr || !cJSON_IsString(item) || strlen(item->valuestring) == 0) {
44        LOG(ERROR) << "Error get valuestring";
45        return false;
46    }
47    return true;
48}
49
50bool UpdatePartitions::SetPartitionInfo(const cJSON *partitions, int idx, struct Partition *myPartition) const
51{
52    cJSON *thisPartition = cJSON_GetArrayItem(partitions, idx);
53    if (thisPartition == nullptr) {
54        LOG(ERROR) << "Error get thisPartion: " << idx;
55        return false;
56    }
57    cJSON *item = cJSON_GetObjectItem(thisPartition, "start");
58    if (!CheckValueInt(item)) {
59        LOG(ERROR) << "Error get start";
60        return false;
61    }
62    myPartition->start = static_cast<size_t>(item->valueint);
63
64    item = cJSON_GetObjectItem(thisPartition, "length");
65    if (!CheckValueInt(item)) {
66        LOG(ERROR) << "Error get length";
67        return false;
68    }
69    myPartition->length = static_cast<size_t>(item->valueint);
70    myPartition->partNum = 0;
71    myPartition->devName = "mmcblk0px";
72
73    item = cJSON_GetObjectItem(thisPartition, "partName");
74    if (!CheckValueString(item)) {
75        LOG(ERROR) << "Error get partName";
76        return false;
77    }
78    myPartition->partName = (item->valuestring);
79
80    item = cJSON_GetObjectItem(thisPartition, "fsType");
81    if (!CheckValueString(item)) {
82        LOG(ERROR) << "Error get fsType";
83        return false;
84    }
85    myPartition->fsType = (item->valuestring);
86
87    LOG(INFO) << "<start> <length> <devname> <partname> <fstype>";
88    LOG(INFO) << myPartition->start << " " << myPartition->length << " " << myPartition->devName << " " <<
89        myPartition->partName << " " << myPartition->fsType;
90    return true;
91}
92
93int UpdatePartitions::ParsePartitionInfo(const std::string &partitionInfo, PartitonList &newPartList) const
94{
95    cJSON* root = cJSON_Parse(partitionInfo.c_str());
96    if (root == nullptr) {
97        LOG(ERROR) << "Error get root";
98        return -1;
99    }
100    cJSON* partitions = cJSON_GetObjectItem(root, "Partition");
101    if (partitions == nullptr) {
102        LOG(ERROR) << "Error get Partitions";
103        cJSON_Delete(root);
104        return -1;
105    }
106    int number = cJSON_GetArraySize(partitions);
107    if (number <= MIN_PARTITIONS_NUM || number >= MAX_PARTITIONS_NUM) {
108        LOG(ERROR) << "Error partitions number: " << number;
109        cJSON_Delete(root);
110        return -1;
111    }
112    LOG(INFO) << "Partitions numbers " << number;
113
114    for (int i = 0; i < number; i++) {
115        struct Partition* myPartition = static_cast<struct Partition*>(calloc(1, sizeof(struct Partition)));
116        if (!myPartition) {
117            LOG(ERROR) << "Allocate memory for partition failed: " << errno;
118            cJSON_Delete(root);
119            return 0;
120        }
121        if (!SetPartitionInfo(partitions, i, myPartition)) {
122            free(myPartition);
123            myPartition = nullptr;
124            break;
125        }
126        newPartList.push_back(myPartition);
127    }
128    cJSON_Delete(root);
129    return 1;
130}
131
132int UpdatePartitions::DoNewPartitions(PartitonList &newPartList)
133{
134    int ret = DoPartitions(newPartList);
135    newPartList.clear();
136    if (ret <= 0) {
137        LOG(INFO) << "do_partitions FAIL ";
138    } else if (ret == 1) {
139        LOG(INFO) << "partitions not changed,Skip.";
140    } else if (ret > 1) {
141        LOG(INFO) << "do_partitions success reboot";
142#ifndef UPDATER_UT
143        Utils::UpdaterDoReboot("updater", "Updater finish do new partitions");
144#endif
145    }
146    return ret;
147}
148
149int UpdatePartitions::SetNewPartition(const std::string &filePath, const FileInfo *info, Uscript::UScriptEnv &env)
150{
151    std::string tmpPath = "/data/updater" + filePath;
152    char realPath[PATH_MAX + 1] = {};
153    if (realpath(tmpPath.c_str(), realPath) == nullptr) {
154        LOG(ERROR) << "Error to create: " << tmpPath;
155        return USCRIPT_ERROR_EXECUTE;
156    }
157    Hpackage::PkgManager::StreamPtr outStream = nullptr;
158    int ret = env.GetPkgManager()->CreatePkgStream(outStream,
159        std::string(realPath), info->unpackedSize, PkgStream::PkgStreamType_Write);
160    if (ret != USCRIPT_SUCCESS || outStream == nullptr) {
161        LOG(ERROR) << "Error to create output stream";
162        return USCRIPT_ERROR_EXECUTE;
163    }
164    ret = env.GetPkgManager()->ExtractFile(filePath, outStream);
165    if (ret != USCRIPT_SUCCESS) {
166        LOG(ERROR) << "Error to extract file";
167        env.GetPkgManager()->ClosePkgStream(outStream);
168        return USCRIPT_ERROR_EXECUTE;
169    }
170    FILE *fp = fopen(realPath, "rb");
171    if (!fp) {
172        LOG(ERROR) << "Open " << tmpPath << " failed: " << errno;
173        env.GetPkgManager()->ClosePkgStream(outStream);
174        return USCRIPT_ERROR_EXECUTE;
175    }
176    char partitionInfo[MAX_LOG_BUF_SIZE];
177    size_t partitionCount = fread(partitionInfo, 1, MAX_LOG_BUF_SIZE, fp);
178    fclose(fp);
179    if (partitionCount <= LEAST_PARTITION_COUNT) {
180        env.GetPkgManager()->ClosePkgStream(outStream);
181        LOG(ERROR) << "Invalid partition size, too small";
182        return USCRIPT_ERROR_EXECUTE;
183    }
184    PartitonList newPartList {};
185    if (ParsePartitionInfo(std::string(partitionInfo), newPartList) == 0) {
186        env.GetPkgManager()->ClosePkgStream(outStream);
187        return USCRIPT_ABOART;
188    }
189    if (newPartList.empty()) {
190        LOG(ERROR) << "Partition is empty ";
191        env.GetPkgManager()->ClosePkgStream(outStream);
192        return USCRIPT_SUCCESS; // Partitions table is empty not require partition.
193    }
194    DoNewPartitions(newPartList);
195    env.GetPkgManager()->ClosePkgStream(outStream);
196    return USCRIPT_SUCCESS;
197}
198
199int32_t UpdatePartitions::Execute(Uscript::UScriptEnv &env, Uscript::UScriptContext &context)
200{
201    LOG(INFO) << "enter UpdatePartitions::Execute ";
202    if (context.GetParamCount() != 1) {
203        LOG(ERROR) << "Invalid UpdatePartitions::Execute param";
204        UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
205        return USCRIPT_INVALID_PARAM;
206    }
207    std::string filePath;
208    int32_t ret = context.GetParam(0, filePath);
209    if (ret != USCRIPT_SUCCESS) {
210        LOG(ERROR) << "Fail to get filePath";
211        UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
212        return USCRIPT_INVALID_PARAM;
213    } else {
214        LOG(INFO) << "UpdatePartitions::Execute filePath " << filePath;
215    }
216    const FileInfo *info = env.GetPkgManager()->GetFileInfo(filePath);
217    if (info == nullptr) {
218        LOG(ERROR) << "Error to get file info";
219        UPDATER_LAST_WORD(USCRIPT_INVALID_PARAM);
220        return USCRIPT_ERROR_EXECUTE;
221    }
222    return SetNewPartition(filePath, info, env);
223}
224} // namespace Updater
225