1 /*
2 * Copyright (c) 2021-2024 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 "resource_table.h"
17 #include <cJSON.h>
18 #include "cmd_parser.h"
19 #include "file_entry.h"
20 #include "file_manager.h"
21 #include "resource_util.h"
22 #include "securec.h"
23
24 namespace OHOS {
25 namespace Global {
26 namespace Restool {
27 using namespace std;
ResourceTable()28 ResourceTable::ResourceTable()
29 {
30 auto &parser = CmdParser<PackageParser>::GetInstance();
31 auto &packageParser = parser.GetCmdParser();
32 if (!packageParser.GetIdDefinedOutput().empty()) {
33 idDefinedPath_ = FileEntry::FilePath(packageParser.GetIdDefinedOutput()).Append(ID_DEFINED_FILE).GetPath();
34 }
35 indexFilePath_ = FileEntry::FilePath(packageParser.GetOutput()).Append(RESOURCE_INDEX_FILE).GetPath();
36 }
37
~ResourceTable()38 ResourceTable::~ResourceTable()
39 {
40 }
41
CreateResourceTable()42 uint32_t ResourceTable::CreateResourceTable()
43 {
44 FileManager &fileManager = FileManager::GetInstance();
45 auto &allResource = fileManager.GetResources();
46 map<string, vector<TableData>> configs;
47 for (const auto &item : allResource) {
48 for (const auto &resourceItem : item.second) {
49 if (resourceItem.GetResType() == ResType::ID) {
50 break;
51 }
52 TableData tableData;
53 tableData.id = item.first;
54 tableData.resourceItem = resourceItem;
55 configs[resourceItem.GetLimitKey()].push_back(tableData);
56 }
57 }
58
59 if (SaveToResouorceIndex(configs) != RESTOOL_SUCCESS) {
60 return RESTOOL_ERROR;
61 }
62
63 if (!idDefinedPath_.empty()) {
64 if (CreateIdDefined(allResource) != RESTOOL_SUCCESS) {
65 return RESTOOL_ERROR;
66 }
67 }
68 return RESTOOL_SUCCESS;
69 }
70
CreateResourceTable(const map<int64_t,vector<shared_ptr<ResourceItem>>> & items)71 uint32_t ResourceTable::CreateResourceTable(const map<int64_t, vector<shared_ptr<ResourceItem>>> &items)
72 {
73 map<string, vector<TableData>> configs;
74 map<int64_t, vector<ResourceItem>> allResource;
75 for (const auto &item : items) {
76 vector<ResourceItem> resourceItems;
77 for (const auto &resourceItemPtr : item.second) {
78 if (resourceItemPtr->GetResType() == ResType::ID) {
79 break;
80 }
81 TableData tableData;
82 tableData.id = item.first;
83 tableData.resourceItem = *resourceItemPtr;
84 resourceItems.push_back(*resourceItemPtr);
85 configs[resourceItemPtr->GetLimitKey()].push_back(tableData);
86 }
87 allResource.emplace(item.first, resourceItems);
88 }
89
90 if (SaveToResouorceIndex(configs) != RESTOOL_SUCCESS) {
91 return RESTOOL_ERROR;
92 }
93
94 if (!idDefinedPath_.empty()) {
95 if (CreateIdDefined(allResource) != RESTOOL_SUCCESS) {
96 return RESTOOL_ERROR;
97 }
98 }
99 return RESTOOL_SUCCESS;
100 }
101
LoadResTable(const string path,map<int64_t,vector<ResourceItem>> & resInfos)102 uint32_t ResourceTable::LoadResTable(const string path, map<int64_t, vector<ResourceItem>> &resInfos)
103 {
104 ifstream in(path, ios::binary);
105 if (!in.is_open()) {
106 cerr << "Error: open failed." << NEW_LINE_PATH << path <<endl;
107 return RESTOOL_ERROR;
108 }
109
110 in.seekg(0, ios::end);
111 int64_t length = in.tellg();
112 if (length <= 0) {
113 in.close();
114 return RESTOOL_ERROR;
115 }
116 in.seekg(0, ios::beg);
117
118 uint64_t pos = 0;
119 IndexHeader indexHeader;
120 if (!ReadFileHeader(in, indexHeader, pos, static_cast<uint64_t>(length))) {
121 in.close();
122 return RESTOOL_ERROR;
123 }
124
125 map<int64_t, vector<KeyParam>> limitKeys;
126 if (!ReadLimitKeys(in, limitKeys, indexHeader.limitKeyConfigSize, pos, static_cast<uint64_t>(length))) {
127 in.close();
128 return RESTOOL_ERROR;
129 }
130
131 map<int64_t, pair<int64_t, int64_t>> datas;
132 if (!ReadIdTables(in, datas, indexHeader.limitKeyConfigSize, pos, static_cast<uint64_t>(length))) {
133 in.close();
134 return RESTOOL_ERROR;
135 }
136
137 while (in.tellg() < length) {
138 RecordItem record;
139 if (!ReadDataRecordPrepare(in, record, pos, static_cast<uint64_t>(length)) ||
140 !ReadDataRecordStart(in, record, limitKeys, datas, resInfos)) {
141 in.close();
142 return RESTOOL_ERROR;
143 }
144 }
145 in.close();
146 return RESTOOL_SUCCESS;
147 }
148
CreateIdDefined(const map<int64_t,vector<ResourceItem>> & allResource) const149 uint32_t ResourceTable::CreateIdDefined(const map<int64_t, vector<ResourceItem>> &allResource) const
150 {
151 cJSON *root = cJSON_CreateObject();
152 cJSON *recordArray = cJSON_CreateArray();
153 if (recordArray == nullptr) {
154 cerr << "Error: failed to create cJSON object for record array." << endl;
155 cJSON_Delete(root);
156 return RESTOOL_ERROR;
157 }
158 cJSON_AddItemToObject(root, "record", recordArray);
159 for (const auto &pairPtr : allResource) {
160 if (pairPtr.second.empty()) {
161 cerr << "Error: resource item vector is empty." << endl;
162 cJSON_Delete(root);
163 return RESTOOL_ERROR;
164 }
165 cJSON *jsonItem = cJSON_CreateObject();
166 if (jsonItem == nullptr) {
167 cerr << "Error: failed to create cJSON object for resource item." << endl;
168 cJSON_Delete(root);
169 return RESTOOL_ERROR;
170 }
171 ResourceItem item = pairPtr.second.front();
172 ResType resType = item.GetResType();
173 string type = ResourceUtil::ResTypeToString(resType);
174 string name = item.GetName();
175 int64_t id = pairPtr.first;
176 if (type.empty()) {
177 cerr << "Error : name = " << name << " ,ResType must is";
178 cerr << ResourceUtil::GetAllRestypeString() << endl;
179 cJSON_Delete(jsonItem);
180 cJSON_Delete(root);
181 return RESTOOL_ERROR;
182 }
183 cJSON_AddStringToObject(jsonItem, "type", type.c_str());
184 cJSON_AddStringToObject(jsonItem, "name", ResourceUtil::GetIdName(name, resType).c_str());
185 cJSON_AddStringToObject(jsonItem, "id", ResourceUtil::DecToHexStr(id).c_str());
186 cJSON_AddItemToArray(recordArray, jsonItem);
187 }
188 if (!ResourceUtil::SaveToJsonFile(idDefinedPath_, root)) {
189 cJSON_Delete(root);
190 return RESTOOL_ERROR;
191 }
192 cJSON_Delete(root);
193 return RESTOOL_SUCCESS;
194 }
195
196 // below private
SaveToResouorceIndex(const map<string,vector<TableData>> & configs) const197 uint32_t ResourceTable::SaveToResouorceIndex(const map<string, vector<TableData>> &configs) const
198 {
199 uint32_t pos = 0;
200 IndexHeader indexHeader;
201 if (!InitIndexHeader(indexHeader, configs.size())) {
202 return RESTOOL_ERROR;
203 }
204 pos += sizeof(IndexHeader);
205
206 map<string, LimitKeyConfig> limitKeyConfigs;
207 map<string, IdSet> idSets;
208 if (!Prepare(configs, limitKeyConfigs, idSets, pos)) {
209 return RESTOOL_ERROR;
210 }
211
212 ofstream out(indexFilePath_, ofstream::out | ofstream::binary);
213 if (!out.is_open()) {
214 cerr << "Error: open failed '" << indexFilePath_ << "', reason: " << strerror(errno) << endl;
215 return RESTOOL_ERROR;
216 }
217
218 ostringstream outStreamData;
219 if (!SaveRecordItem(configs, outStreamData, idSets, pos)) {
220 return RESTOOL_ERROR;
221 }
222
223 ostringstream outStreamHeader;
224 indexHeader.fileSize = pos;
225 SaveHeader(indexHeader, outStreamHeader);
226 SaveLimitKeyConfigs(limitKeyConfigs, outStreamHeader);
227 SaveIdSets(idSets, outStreamHeader);
228 out << outStreamHeader.str();
229 out << outStreamData.str();
230 return RESTOOL_SUCCESS;
231 }
232
InitIndexHeader(IndexHeader & indexHeader,uint32_t count) const233 bool ResourceTable::InitIndexHeader(IndexHeader &indexHeader, uint32_t count) const
234 {
235 if (memcpy_s(indexHeader.version, VERSION_MAX_LEN, RESTOOL_VERSION, VERSION_MAX_LEN) != EOK) {
236 cerr << "Error: InitIndexHeader memcpy_s fail." << endl;
237 return false;
238 }
239 indexHeader.limitKeyConfigSize = count;
240 return true;
241 }
242
Prepare(const map<string,vector<TableData>> & configs,map<string,LimitKeyConfig> & limitKeyConfigs,map<string,IdSet> & idSets,uint32_t & pos) const243 bool ResourceTable::Prepare(const map<string, vector<TableData>> &configs,
244 map<string, LimitKeyConfig> &limitKeyConfigs,
245 map<string, IdSet> &idSets, uint32_t &pos) const
246 {
247 for (const auto &config : configs) {
248 LimitKeyConfig limitKeyConfig;
249 const auto &keyParams = config.second.at(0).resourceItem.GetKeyParam();
250 limitKeyConfig.keyCount = keyParams.size();
251 pos += sizeof(limitKeyConfig.keyTag) + sizeof(limitKeyConfig.offset) + sizeof(limitKeyConfig.keyCount);
252 for (const auto &keyParam : keyParams) {
253 limitKeyConfig.data.push_back(static_cast<int32_t>(keyParam.keyType));
254 limitKeyConfig.data.push_back(static_cast<int32_t>(keyParam.value));
255 pos += sizeof(KeyParam);
256 }
257 limitKeyConfigs.emplace(config.first, limitKeyConfig);
258 }
259
260 for (const auto &config : configs) {
261 auto limitKeyConfig = limitKeyConfigs.find(config.first);
262 if (limitKeyConfig == limitKeyConfigs.end()) {
263 cerr << "Error: limit key config don't find '" << config.first << "'." << endl;
264 return false;
265 }
266 limitKeyConfig->second.offset = pos;
267
268 IdSet idSet;
269 idSet.idCount = config.second.size();
270 pos += sizeof(idSet.idTag) + sizeof(idSet.idCount);
271 for (const auto &tableData : config.second) {
272 idSet.data.emplace(tableData.id, 0);
273 pos += sizeof(uint32_t) + sizeof(uint32_t);
274 }
275 idSets.emplace(config.first, idSet);
276 }
277 return true;
278 }
279
SaveRecordItem(const map<string,vector<TableData>> & configs,ostringstream & out,map<string,IdSet> & idSets,uint32_t & pos) const280 bool ResourceTable::SaveRecordItem(const map<string, vector<TableData>> &configs,
281 ostringstream &out, map<string, IdSet> &idSets, uint32_t &pos) const
282 {
283 for (const auto &config : configs) {
284 auto idSet = idSets.find(config.first);
285 if (idSet == idSets.end()) {
286 cerr << "Error: id set don't find '" << config.first << "'." << endl;
287 return false;
288 }
289
290 for (const auto &tableData : config.second) {
291 if (idSet->second.data.find(tableData.id) == idSet->second.data.end()) {
292 cerr << "Error: resource table don't find id '" << tableData.id << "'." << endl;
293 return false;
294 }
295 idSet->second.data[tableData.id] = pos;
296 RecordItem recordItem;
297 recordItem.id = tableData.id;
298 recordItem.resType = static_cast<int32_t>(tableData.resourceItem.GetResType());
299 vector<string> contents;
300 string value(reinterpret_cast<const char *>(tableData.resourceItem.GetData()),
301 tableData.resourceItem.GetDataLength());
302 contents.push_back(value);
303 string name = ResourceUtil::GetIdName(tableData.resourceItem.GetName(),
304 tableData.resourceItem.GetResType());
305 contents.push_back(name);
306 string data = ResourceUtil::ComposeStrings(contents, true);
307 recordItem.size = sizeof(RecordItem) + data.length() - sizeof(uint32_t);
308 pos += recordItem.size + sizeof(uint32_t);
309
310 out.write(reinterpret_cast<const char *>(&recordItem.size), sizeof(uint32_t));
311 out.write(reinterpret_cast<const char *>(&recordItem.resType), sizeof(uint32_t));
312 out.write(reinterpret_cast<const char *>(&recordItem.id), sizeof(uint32_t));
313 out.write(reinterpret_cast<const char *>(data.c_str()), data.length());
314 }
315 }
316 return true;
317 }
318
SaveHeader(const IndexHeader & indexHeader,ostringstream & out) const319 void ResourceTable::SaveHeader(const IndexHeader &indexHeader, ostringstream &out) const
320 {
321 out.write(reinterpret_cast<const char *>(&indexHeader.version), VERSION_MAX_LEN);
322 out.write(reinterpret_cast<const char *>(&indexHeader.fileSize), sizeof(uint32_t));
323 out.write(reinterpret_cast<const char *>(&indexHeader.limitKeyConfigSize), sizeof(uint32_t));
324 }
325
SaveLimitKeyConfigs(const map<string,LimitKeyConfig> & limitKeyConfigs,ostringstream & out) const326 void ResourceTable::SaveLimitKeyConfigs(const map<string, LimitKeyConfig> &limitKeyConfigs, ostringstream &out) const
327 {
328 for (const auto &iter : limitKeyConfigs) {
329 out.write(reinterpret_cast<const char *>(iter.second.keyTag), TAG_LEN);
330 out.write(reinterpret_cast<const char *>(&iter.second.offset), sizeof(uint32_t));
331 out.write(reinterpret_cast<const char *>(&iter.second.keyCount), sizeof(uint32_t));
332 for (const auto &value : iter.second.data) {
333 out.write(reinterpret_cast<const char *>(&value), sizeof(int32_t));
334 }
335 }
336 }
337
SaveIdSets(const map<string,IdSet> & idSets,ostringstream & out) const338 void ResourceTable::SaveIdSets(const map<string, IdSet> &idSets, ostringstream &out) const
339 {
340 for (const auto &iter : idSets) {
341 out.write(reinterpret_cast<const char *>(iter.second.idTag), TAG_LEN);
342 out.write(reinterpret_cast<const char *>(&iter.second.idCount), sizeof(uint32_t));
343 for (const auto &keyValue : iter.second.data) {
344 out.write(reinterpret_cast<const char *>(&keyValue.first), sizeof(uint32_t));
345 out.write(reinterpret_cast<const char *>(&keyValue.second), sizeof(uint32_t));
346 }
347 }
348 }
349
ReadFileHeader(ifstream & in,IndexHeader & indexHeader,uint64_t & pos,uint64_t length) const350 bool ResourceTable::ReadFileHeader(ifstream &in, IndexHeader &indexHeader, uint64_t &pos, uint64_t length) const
351 {
352 pos += sizeof(indexHeader);
353 if (pos > length) {
354 cerr << "Error: invalid resources.index File Header." << endl;
355 return false;
356 }
357 in.read(reinterpret_cast<char *>(indexHeader.version), VERSION_MAX_LEN);
358 in.read(reinterpret_cast<char *>(&indexHeader.fileSize), INT_TO_BYTES);
359 in.read(reinterpret_cast<char *>(&indexHeader.limitKeyConfigSize), INT_TO_BYTES);
360 return true;
361 }
362
ReadLimitKeys(ifstream & in,map<int64_t,vector<KeyParam>> & limitKeys,uint32_t count,uint64_t & pos,uint64_t length) const363 bool ResourceTable::ReadLimitKeys(ifstream &in, map<int64_t, vector<KeyParam>> &limitKeys,
364 uint32_t count, uint64_t &pos, uint64_t length) const
365 {
366 for (uint32_t i = 0; i< count; i++) {
367 pos = pos + TAG_LEN + INT_TO_BYTES + INT_TO_BYTES;
368 if (pos > length) {
369 cerr << "Error: invalid resources.index KEYS." << endl;
370 return false;
371 }
372 LimitKeyConfig limitKey;
373 in.read(reinterpret_cast<char *>(limitKey.keyTag), TAG_LEN);
374 string keyTag(reinterpret_cast<const char *>(limitKey.keyTag), TAG_LEN);
375 if (keyTag != "KEYS") {
376 cerr << "Error: invalid resources.index key tag = " << keyTag << endl;
377 return false;
378 }
379 in.read(reinterpret_cast<char *>(&limitKey.offset), INT_TO_BYTES);
380 in.read(reinterpret_cast<char *>(&limitKey.keyCount), INT_TO_BYTES);
381
382 vector<KeyParam> keyParams;
383 for (uint32_t j = 0; j < limitKey.keyCount; j++) {
384 pos = pos + INT_TO_BYTES + INT_TO_BYTES;
385 if (pos > length) {
386 cerr << "Error: invalid resources.index keyParams." << endl;
387 return false;
388 }
389 KeyParam keyParam;
390 in.read(reinterpret_cast<char *>(&keyParam.keyType), INT_TO_BYTES);
391 in.read(reinterpret_cast<char *>(&keyParam.value), INT_TO_BYTES);
392 keyParams.push_back(keyParam);
393 }
394 limitKeys[limitKey.offset] = keyParams;
395 }
396 return true;
397 }
398
ReadIdTables(std::ifstream & in,std::map<int64_t,std::pair<int64_t,int64_t>> & datas,uint32_t count,uint64_t & pos,uint64_t length) const399 bool ResourceTable::ReadIdTables(std::ifstream &in, std::map<int64_t, std::pair<int64_t, int64_t>> &datas,
400 uint32_t count, uint64_t &pos, uint64_t length) const
401 {
402 for (uint32_t i = 0; i< count; i++) {
403 pos = pos + TAG_LEN + INT_TO_BYTES;
404 if (pos > length) {
405 cerr << "Error: invalid resources.index IDSS." << endl;
406 return false;
407 }
408 IdSet idss;
409 int64_t offset = in.tellg();
410 in.read(reinterpret_cast<char *>(idss.idTag), TAG_LEN);
411 string idTag(reinterpret_cast<const char *>(idss.idTag), TAG_LEN);
412 if (idTag != "IDSS") {
413 cerr << "Error: invalid resources.index id tag = " << idTag << endl;
414 return false;
415 }
416 in.read(reinterpret_cast<char *>(&idss.idCount), INT_TO_BYTES);
417
418 for (uint32_t j = 0; j < idss.idCount; j++) {
419 pos = pos + INT_TO_BYTES + INT_TO_BYTES;
420 if (pos > length) {
421 cerr << "Error: invalid resources.index id data." << endl;
422 return false;
423 }
424 IdData data;
425 in.read(reinterpret_cast<char *>(&data.id), INT_TO_BYTES);
426 in.read(reinterpret_cast<char *>(&data.dataOffset), INT_TO_BYTES);
427 datas[data.dataOffset] = make_pair(data.id, offset);
428 }
429 }
430 return true;
431 }
432
ReadDataRecordPrepare(ifstream & in,RecordItem & record,uint64_t & pos,uint64_t length) const433 bool ResourceTable::ReadDataRecordPrepare(ifstream &in, RecordItem &record, uint64_t &pos, uint64_t length) const
434 {
435 pos = pos + INT_TO_BYTES;
436 if (pos > length) {
437 cerr << "Error: invalid resources.index data record." << endl;
438 return false;
439 }
440 in.read(reinterpret_cast<char *>(&record.size), INT_TO_BYTES);
441 pos = pos + record.size;
442 if (pos > length) {
443 cerr << "Error: invalid resources.index record.size." << endl;
444 return false;
445 }
446 in.read(reinterpret_cast<char *>(&record.resType), INT_TO_BYTES);
447 in.read(reinterpret_cast<char *>(&record.id), INT_TO_BYTES);
448 return true;
449 }
450
ReadDataRecordStart(std::ifstream & in,RecordItem & record,const std::map<int64_t,std::vector<KeyParam>> & limitKeys,const std::map<int64_t,std::pair<int64_t,int64_t>> & datas,std::map<int64_t,std::vector<ResourceItem>> & resInfos) const451 bool ResourceTable::ReadDataRecordStart(std::ifstream &in, RecordItem &record,
452 const std::map<int64_t, std::vector<KeyParam>> &limitKeys,
453 const std::map<int64_t, std::pair<int64_t, int64_t>> &datas,
454 std::map<int64_t, std::vector<ResourceItem>> &resInfos) const
455 {
456 int64_t offset = in.tellg();
457 offset = offset - INT_TO_BYTES - INT_TO_BYTES - INT_TO_BYTES;
458 uint16_t value_size = 0;
459 in.read(reinterpret_cast<char *>(&value_size), sizeof(uint16_t));
460 if (value_size + sizeof(uint16_t) > record.size) {
461 cerr << "Error: invalid resources.index value size." << endl;
462 return false;
463 }
464 int8_t values[value_size];
465 in.read(reinterpret_cast<char *>(&values), value_size);
466
467 uint16_t name_size = 0;
468 in.read(reinterpret_cast<char *>(&name_size), sizeof(uint16_t));
469 if (value_size + sizeof(uint16_t) + name_size + sizeof(uint16_t) > record.size) {
470 cerr << "Error: invalid resources.index name size." << endl;
471 return false;
472 }
473 int8_t name[name_size];
474 in.read(reinterpret_cast<char *>(name), name_size);
475 string filename(reinterpret_cast<char *>(name));
476
477 auto idTableOffset = datas.find(offset);
478 if (idTableOffset == datas.end()) {
479 cerr << "Error: invalid resources.index id offset." << endl;
480 return false;
481 }
482
483 if (idTableOffset->second.first != record.id) {
484 cerr << "Error: invalid resources.index id." << endl;
485 return false;
486 }
487
488 if (limitKeys.find(idTableOffset->second.second) == limitKeys.end()) {
489 cerr << "Error: invalid resources.index limit key offset." << endl;
490 return false;
491 }
492 const vector<KeyParam> &keyparams = limitKeys.find(datas.find(offset)->second.second)->second;
493 ResourceItem resourceitem(filename, keyparams, g_resTypeMap.find(record.resType)->second);
494 resourceitem.SetLimitKey(ResourceUtil::PaserKeyParam(keyparams));
495 resourceitem.SetData(values, value_size);
496 resInfos[record.id].push_back(resourceitem);
497 return true;
498 }
499
500 }
501 }
502 }
503