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 
25 using namespace std;
26 using namespace Uscript;
27 using namespace Hpackage;
28 using namespace Updater;
29 constexpr int MIN_PARTITIONS_NUM = 2;
30 constexpr int MAX_PARTITIONS_NUM = 20;
31 namespace Updater {
CheckValueInt(const cJSON * item)32 static 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 
CheckValueString(const cJSON * item)41 static 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 
SetPartitionInfo(const cJSON * partitions,int idx,struct Partition * myPartition) const50 bool 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 
ParsePartitionInfo(const std::string & partitionInfo,PartitonList & newPartList) const93 int 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 
DoNewPartitions(PartitonList & newPartList)132 int 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");
144 #endif
145     }
146     return ret;
147 }
148 
SetNewPartition(const std::string & filePath,const FileInfo * info,Uscript::UScriptEnv & env)149 int 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 
Execute(Uscript::UScriptEnv & env,Uscript::UScriptContext & context)199 int32_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