1 /*
2 * Copyright (c) 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 "packager.h"
17
18 #include <filesystem>
19 #include <fstream>
20 #include <random>
21 #include <sstream>
22 #include <string>
23 #include <unordered_set>
24
25 #include "json/json_utils.h"
26 #include "log.h"
27 #include "utils.h"
28 #include "zip_utils.h"
29
30 namespace OHOS {
31 namespace AppPackingTool {
32
33 int32_t Packager::atomicServiceEntrySizeLimit_ = 2048;
34 int32_t Packager::atomicServiceNonEntrySizeLimit_ = 2048;
35
36 namespace {
37 const std::string EN_US_UTF_8 = "en_US.UTF-8";
38 }
39
Packager(const std::map<std::string,std::string> & parameterMap,std::string & resultReceiver)40 Packager::Packager(const std::map<std::string, std::string> ¶meterMap, std::string &resultReceiver)
41 : parameterMap_(parameterMap), resultReceiver_(resultReceiver)
42 {}
43
~Packager()44 Packager::~Packager()
45 {}
46
MakePackage()47 int32_t Packager::MakePackage()
48 {
49 int32_t ret = ERR_OK;
50 ret = InitAllowedParam();
51 if (ret != ERR_OK) {
52 LOGE("InitAllowedParam err");
53 return ret;
54 }
55
56 ret = PreProcess();
57 if (ret != ERR_OK) {
58 LOGE("PreProcess err");
59 return ret;
60 }
61 ret = Process();
62 if (ret != ERR_OK) {
63 LOGE("Process err");
64 return ret;
65 }
66
67 ret = PostProcess();
68 if (ret != ERR_OK) {
69 LOGE("PostProcess err");
70 return ret;
71 }
72 return ret;
73 }
74
PreProcess()75 int32_t Packager::PreProcess()
76 {
77 resultReceiver_.append("do PreProcess\n");
78 return ERR_OK;
79 }
80
Process()81 int32_t Packager::Process()
82 {
83 resultReceiver_.append("do Process\n");
84 return ERR_OK;
85 }
86
PostProcess()87 int32_t Packager::PostProcess()
88 {
89 resultReceiver_.append("do PostProcess\n");
90 return ERR_OK;
91 }
92
CheckForceFlag()93 bool Packager::CheckForceFlag()
94 {
95 auto it = parameterMap_.find(Constants::PARAM_FORCE);
96 if (it != parameterMap_.end() && it->second != "false" && it->second != "true") {
97 LOGE("Packager::commandVerify forceRewrite is invalid.");
98 return false;
99 }
100 return true;
101 }
102
IsPathValid(const std::string & path,const bool & isFile,const std::string suffix)103 bool Packager::IsPathValid(const std::string &path, const bool &isFile, const std::string suffix)
104 {
105 try {
106 if (isFile) {
107 if (!fs::is_regular_file(path)) {
108 return false;
109 }
110 const std::locale englishLocale(EN_US_UTF_8);
111 std::string name = fs::path(path).filename();
112 std::transform(name.begin(), name.end(), name.begin(),
113 [&englishLocale](unsigned char c) { return std::tolower(c); });
114 return Utils::EndsWith(name, suffix);
115 }
116 return fs::is_directory(path);
117 } catch (...) {
118 LOGW("Param parse error");
119 return false;
120 }
121 }
122
IsFileMatch(const std::string & path,const std::string & matchFileName)123 bool Packager::IsFileMatch(const std::string &path, const std::string &matchFileName)
124 {
125 try {
126 if (fs::is_regular_file(path)) {
127 std::string name = fs::path(path).filename().string();
128 return name == matchFileName;
129 }
130 } catch (const fs::filesystem_error& e) {
131 LOGE("Packager::commandVerify fileMatch has error : %s", e.code().message().c_str());
132 }
133 return false;
134 }
135
SplitDirList(const std::string & dirList,std::list<std::string> & fileList)136 bool Packager::SplitDirList(const std::string &dirList, std::list<std::string> &fileList)
137 {
138 std::list<std::string> pathList;
139 RemoveDuplicatePath(dirList, pathList);
140 for (const std::string &pathItem : pathList) {
141 std::string formattedPathItem;
142 if (!Utils::GetFormattedPath(pathItem, formattedPathItem)) {
143 LOGE("GetFormattedPath failed for %s", pathItem.c_str());
144 return false;
145 };
146 if (!IsPathValid(formattedPathItem, false)) {
147 return false;
148 }
149 fileList.push_back(formattedPathItem);
150 }
151 return true;
152 }
153
RemoveDuplicatePath(const std::string & path,std::list<std::string> & pathList)154 void Packager::RemoveDuplicatePath(const std::string &path, std::list<std::string> &pathList)
155 {
156 std::string pathItem;
157 std::istringstream pathStream(path);
158 while (std::getline(pathStream, pathItem, Constants::COMMA_SPLIT)) {
159 pathList.push_back(pathItem);
160 }
161 std::unordered_set<std::string> uniqueTokens(pathList.begin(), pathList.end());
162 pathList.assign(uniqueTokens.begin(), uniqueTokens.end());
163 }
164
CompatibleProcess(const std::string & inputPath,std::list<std::string> & fileList,const std::string & suffix)165 bool Packager::CompatibleProcess(const std::string &inputPath, std::list<std::string> &fileList,
166 const std::string &suffix)
167 {
168 if (IsPathValid(inputPath, false)) {
169 for (const auto& fileItem : fs::recursive_directory_iterator(inputPath)) {
170 std::string name = fileItem.path().filename();
171 std::locale englishLocale(EN_US_UTF_8);
172 std::transform(name.begin(), name.end(), name.begin(),
173 [&englishLocale](unsigned char c) { return std::tolower(c); });
174 if (Utils::EndsWith(name, suffix)) {
175 fileList.push_back(fileItem.path().string());
176 }
177 }
178 return true;
179 } else {
180 std::string formattedPathItem;
181 std::list<std::string> pathList;
182 RemoveDuplicatePath(inputPath, pathList);
183 for (std::string pathItem : pathList) {
184 if (!Utils::GetFormattedPath(pathItem, formattedPathItem)) {
185 LOGE("GetFormattedPath failed for %s", pathItem.c_str());
186 return false;
187 };
188 if (!IsPathValid(formattedPathItem, true, suffix)) {
189 return false;
190 }
191 fileList.push_back(formattedPathItem);
192 }
193 return true;
194 }
195 }
196
CompatibleProcess(const std::string & inputPath,std::list<std::string> & fileList,const std::string & suffix,const std::string & extraSuffix)197 bool Packager::CompatibleProcess(const std::string &inputPath, std::list<std::string> &fileList,
198 const std::string &suffix, const std::string &extraSuffix)
199 {
200 if (IsPathValid(inputPath, false)) {
201 for (const auto& fileItem : fs::recursive_directory_iterator(inputPath)) {
202 std::string name = fileItem.path().filename();
203 std::locale englishLocale(EN_US_UTF_8);
204 std::transform(name.begin(), name.end(), name.begin(),
205 [&englishLocale](unsigned char c) { return std::tolower(c); });
206 if (Utils::EndsWith(name, suffix) || Utils::EndsWith(name, extraSuffix)) {
207 fileList.push_back(fileItem.path().string());
208 }
209 }
210 return true;
211 } else {
212 std::string formattedPathItem;
213 std::list<std::string> pathList;
214 RemoveDuplicatePath(inputPath, pathList);
215 for (std::string pathItem : pathList) {
216 if (!Utils::GetFormattedPath(pathItem, formattedPathItem)) {
217 LOGE("GetFormattedPath failed for %s", pathItem.c_str());
218 return false;
219 };
220 if (!IsPathValid(formattedPathItem, true, suffix) &&
221 !IsPathValid(formattedPathItem, true, extraSuffix)) {
222 return false;
223 }
224 fileList.push_back(formattedPathItem);
225 }
226 return true;
227 }
228 }
229
EnsureParentDirectoryExists(const std::filesystem::path & filePath)230 bool Packager::EnsureParentDirectoryExists(const std::filesystem::path& filePath)
231 {
232 try {
233 const auto parentPath = filePath.parent_path();
234 if (!std::filesystem::exists(parentPath)) {
235 std::error_code ec;
236 if (!std::filesystem::create_directories(parentPath, ec)) {
237 LOGE("Packager::Failed to create directory: [%s]", ec.message().c_str());
238 return false;
239 }
240 }
241 return true;
242 } catch (const std::filesystem::filesystem_error& e) {
243 LOGE("Packager::Directory creation error: [%s]", e.what());
244 return false;
245 }
246 }
247
IsOutPathValid(const std::string & outPath,const std::string & forceRewrite,const std::string & suffix)248 bool Packager::IsOutPathValid(const std::string &outPath, const std::string &forceRewrite, const std::string &suffix)
249 {
250 const fs::path filePath(outPath);
251
252 if ("false" == forceRewrite && fs::exists(filePath)) {
253 LOGE("Packager::isOutPathValid out file already existed.");
254 return false;
255 }
256
257 if (!EnsureParentDirectoryExists(filePath)) {
258 return false;
259 }
260
261 if (suffix == Constants::HAP_SUFFIX) {
262 if (!Utils::EndsWith(filePath.filename().string(), Constants::HAP_SUFFIX)) {
263 LOGE("Packager::isOutPathValid out-path must end with .hap.");
264 return false;
265 } else {
266 return true;
267 }
268 } else if (suffix == Constants::HAR_SUFFIX) {
269 if (!Utils::EndsWith(filePath.filename().string(), Constants::HAR_SUFFIX)) {
270 LOGE("Packager::isOutPathValid out-path must end with .har.");
271 return false;
272 } else {
273 return true;
274 }
275 } else if (suffix == Constants::APP_SUFFIX) {
276 if (!Utils::EndsWith(filePath.filename().string(), Constants::APP_SUFFIX)) {
277 LOGE("Packager::isOutPathValid out-path must end with .app.");
278 return false;
279 } else {
280 return true;
281 }
282 } else if (suffix == Constants::RES_SUFFIX) {
283 if (!Utils::EndsWith(filePath.filename().string(), Constants::RES_SUFFIX)) {
284 LOGE("Packager::isOutPathValid out-path must end with .res.");
285 return false;
286 } else {
287 return true;
288 }
289 } else if (suffix == Constants::HSP_SUFFIX) {
290 if (!Utils::EndsWith(filePath.filename().string(), Constants::HSP_SUFFIX)) {
291 LOGE("Packager::isOutPathValid out-path must end with .hsp.");
292 return false;
293 } else {
294 return true;
295 }
296 } else if (suffix == Constants::MODE_MULTIAPP) {
297 return true;
298 }
299 return false;
300 }
301
SetGenerateBuildHash(std::string & jsonPath,bool & generateBuildHash,bool & buildHashFinish)302 bool Packager::SetGenerateBuildHash(std::string &jsonPath, bool &generateBuildHash, bool &buildHashFinish)
303 {
304 if (!fs::exists(jsonPath)) {
305 LOGE("Packager::setGenerateBuildHash failed for json file not exist");
306 return false;
307 }
308 ModuleJson moduleJson;
309 moduleJson.ParseFromFile(jsonPath);
310
311 if (buildHashFinish || !moduleJson.GetGenerateBuildHash(generateBuildHash)) {
312 return true;
313 }
314
315 if (!CopyFileToTempDir(jsonPath)) {
316 return false;
317 }
318
319 if (!fs::exists(jsonPath)) {
320 LOGE("Packager::setGenerateBuildHash failed for json file not exist");
321 return false;
322 }
323
324 ModuleJson moduleJsonTemp;
325 moduleJsonTemp.ParseFromFile(jsonPath);
326 if (!moduleJsonTemp.GetGenerateBuildHash(generateBuildHash)) {
327 LOGE("ModuleJson::GetGenerateBuildHash failed");
328 return false;
329 }
330
331 if (!moduleJsonTemp.RemoveGenerateBuildHash()) {
332 LOGE("ModuleJson::RemoveGenerateBuildHash failed");
333 return false;
334 }
335
336 std::string prettyJsonString = moduleJsonTemp.ToString();
337 if (prettyJsonString.empty()) {
338 LOGE("ModuleJson::ToString failed");
339 return false;
340 }
341
342 std::string realJsonPath;
343 if (!Utils::GetRealPath(jsonPath, realJsonPath)) {
344 LOGE("get real json Path failed! jsonPath=%s", jsonPath.c_str());
345 return false;
346 }
347 std::ofstream outFile(realJsonPath);
348 if (outFile.is_open()) {
349 outFile << prettyJsonString.c_str();
350 outFile.close();
351 } else {
352 LOGE("Failed to open file for writing");
353 return false;
354 }
355 return true;
356 }
357
CopyFileToTempDir(std::string & jsonPath)358 bool Packager::CopyFileToTempDir(std::string &jsonPath)
359 {
360 if (!fs::exists(jsonPath)) {
361 LOGE("Packager::copyFileToTempDir failed for json file not found.");
362 return false;
363 }
364 fs::path oldFileParent = fs::path(jsonPath).parent_path();
365 std::string tempDir = Constants::COMPRESSOR_TEMP_DIR + fs::path::preferred_separator + Utils::GenerateUUID();
366 fs::path tempDirFs(tempDir);
367 tempDir = oldFileParent.string() + fs::path::preferred_separator + tempDirFs.string();
368 fs::create_directories(tempDir);
369 fs::path fileName = JsonUtils::IsModuleJson(jsonPath) ? Constants::MODULE_JSON : Constants::CONFIG_JSON;
370 std::string tempPath = tempDir + fs::path::preferred_separator + fileName.string();
371 if (!Utils::CopyFile(jsonPath, tempPath)) {
372 return false;
373 }
374 jsonPath = tempPath;
375 return true;
376 }
377
BuildHash(bool & buildHashFinish,const bool & generateBuildHash,const std::map<std::string,std::string> & parameterMap,const std::string & jsonPath)378 bool Packager::BuildHash(bool &buildHashFinish, const bool &generateBuildHash,
379 const std::map<std::string, std::string> ¶meterMap, const std::string &jsonPath)
380 {
381 if (buildHashFinish || !generateBuildHash) {
382 return true;
383 }
384 std::map<std::string, std::string>::const_iterator it = parameterMap.find(Constants::PARAM_OUT_PATH);
385 if (it == parameterMap.end()) {
386 LOGE("out-path not found");
387 return false;
388 }
389 std::string filePath = it->second;
390 std::string hash = Utils::GetSha256File(filePath);
391 return PutBuildHash(jsonPath, hash, buildHashFinish);
392 }
393
PutBuildHash(const std::string & jsonPath,const std::string & hash,bool & buildHashFinish)394 bool Packager::PutBuildHash(const std::string &jsonPath, const std::string &hash, bool &buildHashFinish)
395 {
396 if (buildHashFinish) {
397 return true;
398 }
399
400 ModuleJson moduleJson;
401 moduleJson.ParseFromFile(jsonPath);
402 moduleJson.SetBuildHash(hash);
403 std::string prettyJsonString = moduleJson.ToString();
404 if (prettyJsonString.empty()) {
405 LOGE("ModuleJson::ToString failed");
406 return false;
407 }
408
409 std::string realJsonPath;
410 if (!Utils::GetRealPath(jsonPath, realJsonPath)) {
411 LOGE("get real json path failed! jsonFile=%s", jsonPath.c_str());
412 return false;
413 }
414 std::ofstream outFile(realJsonPath);
415 if (outFile.is_open()) {
416 outFile << prettyJsonString.c_str();
417 outFile.close();
418 } else {
419 LOGE("Failed to open file for writing");
420 return false;
421 }
422
423 buildHashFinish = true;
424 return true;
425 }
426
IsModuleHap(const std::string & hapPath)427 bool Packager::IsModuleHap(const std::string& hapPath)
428 {
429 if (!Utils::EndsWith(hapPath, Constants::HAP_SUFFIX)) {
430 return false;
431 }
432 if (!ZipUtils::IsFileExistsInZip(hapPath, Constants::MODULE_JSON)) {
433 return false;
434 }
435 return true;
436 }
437
CompressPackinfoIntoHap(const std::string & hapPathItem,const std::string & unzipPathString,const std::string & outPathString,const std::string & packInfoPath)438 void Packager::CompressPackinfoIntoHap(const std::string& hapPathItem, const std::string& unzipPathString,
439 const std::string& outPathString, const std::string& packInfoPath)
440 {
441 if (!fs::exists(fs::path(unzipPathString))) {
442 fs::create_directory(fs::path(unzipPathString));
443 }
444 ZipUtils::Unzip(hapPathItem, unzipPathString);
445 for (const auto& entry : fs::directory_iterator(unzipPathString)) {
446 if (entry.path().filename() == Constants::PACK_INFO) {
447 fs::remove(entry.path());
448 }
449 }
450
451 std::string realPackInfoPath;
452 if (!Utils::GetRealPath(packInfoPath, realPackInfoPath)) {
453 LOGE("get real pack info path failed! packInfoPath=%s", packInfoPath.c_str());
454 return;
455 }
456 std::string destFilePath = unzipPathString + fs::path::preferred_separator + Constants::PACK_INFO;
457 std::string realDestFilePath;
458 if (!Utils::GetRealPathOfNoneExistFile(destFilePath, realDestFilePath)) {
459 LOGE("get real dest file path failed! destFilePath=%s", destFilePath.c_str());
460 return;
461 }
462 std::ifstream packInfoFile(realPackInfoPath, std::ios::binary);
463 std::ofstream destFile(realDestFilePath, std::ios::binary | std::ios::trunc);
464 destFile << packInfoFile.rdbuf();
465 destFile.close();
466 packInfoFile.close();
467 ZipUtils::Zip(unzipPathString, outPathString);
468 if (fs::exists(fs::path(unzipPathString))) {
469 fs::remove_all(fs::path(unzipPathString));
470 }
471 }
472
IsOutDirectoryValid()473 bool Packager::IsOutDirectoryValid()
474 {
475 auto it = parameterMap_.find(Constants::PARAM_OUT_PATH);
476 if (it == parameterMap_.end()) {
477 LOGE("--out-path is empty.");
478 return false;
479 } else if (!Utils::IsDirectory(it->second)) {
480 LOGE("--out-path is not a directory.");
481 return false;
482 }
483 return true;
484 }
485
ParseAtomicServiceSizeLimit()486 bool Packager::ParseAtomicServiceSizeLimit()
487 {
488 bool parseEntry = ParseAtomicServiceEntrySizeLimitParameter();
489 bool parseNonEntry = ParseAtomicServiceNonEntrySizeLimitParameter();
490 return parseEntry && parseNonEntry;
491 }
492
ParseAtomicServiceEntrySizeLimitParameter()493 bool Packager::ParseAtomicServiceEntrySizeLimitParameter()
494 {
495 auto it = parameterMap_.find(Constants::PARAM_ATOMIC_SERVICE_ENTRY_SIZE_LIMIT);
496 int32_t entrySizeLimit = Constants::ATOMIC_SERVICE_ENTRY_SIZE_LIMIT_DEFAULT;
497 if (it != parameterMap_.end()) {
498 try {
499 entrySizeLimit = std::stoi(it->second);
500 } catch (const std::exception& e) {
501 LOGE("ParseAtomicServiceEntrySizeLimitParameter failed, "
502 "input --atomic-service-entry-size-limit value invalid.");
503 LOGE("Exception: %s", e.what());
504 return false;
505 }
506 if (entrySizeLimit < 0 || entrySizeLimit > Constants::ATOMIC_SERVICE_TOTAL_SIZE_LIMIT_MAX) {
507 LOGE("ParseAtomicServiceEntrySizeLimitParameter failed, "
508 "input --atomic-service-entry-size-limit value out of range [0,4194304].");
509 return false;
510 }
511 }
512 Packager::setAtomicServiceEntrySizeLimit(entrySizeLimit);
513 return true;
514 }
515
ParseAtomicServiceNonEntrySizeLimitParameter()516 bool Packager::ParseAtomicServiceNonEntrySizeLimitParameter()
517 {
518 auto it = parameterMap_.find(Constants::PARAM_ATOMIC_SERVICE_NON_ENTRY_SIZE_LIMIT);
519 int32_t nonEntrySizeLimit = Constants::ATOMIC_SERVICE_NON_ENTRY_SIZE_LIMIT_DEFAULT;
520 if (it != parameterMap_.end()) {
521 try {
522 nonEntrySizeLimit = std::stoi(it->second);
523 } catch (const std::exception& e) {
524 LOGE("ParseAtomicServiceEntrySizeLimitParameter failed, "
525 "input --atomic-service-non-entry-size-limit value invalid.");
526 LOGE("Exception: %s", e.what());
527 return false;
528 }
529 if (nonEntrySizeLimit < 0 || nonEntrySizeLimit > Constants::ATOMIC_SERVICE_TOTAL_SIZE_LIMIT_MAX) {
530 LOGE("ParseAtomicServiceNonEntrySizeLimitParameter failed, "
531 "input --atomic-service-non-entry-size-limit value out of range [0,4194304].");
532 return false;
533 }
534 }
535 Packager::setAtomicServiceNonEntrySizeLimit(nonEntrySizeLimit);
536 return true;
537 }
538
getAtomicServiceEntrySizeLimit()539 int32_t Packager::getAtomicServiceEntrySizeLimit()
540 {
541 return atomicServiceEntrySizeLimit_;
542 }
543
setAtomicServiceEntrySizeLimit(int32_t atomicServiceEntrySizeLimit)544 void Packager::setAtomicServiceEntrySizeLimit(int32_t atomicServiceEntrySizeLimit)
545 {
546 atomicServiceEntrySizeLimit_ = atomicServiceEntrySizeLimit;
547 }
548
getAtomicServiceNonEntrySizeLimit()549 int32_t Packager::getAtomicServiceNonEntrySizeLimit()
550 {
551 return atomicServiceNonEntrySizeLimit_;
552 }
553
setAtomicServiceNonEntrySizeLimit(int32_t atomicServiceNonEntrySizeLimit)554 void Packager::setAtomicServiceNonEntrySizeLimit(int32_t atomicServiceNonEntrySizeLimit)
555 {
556 atomicServiceNonEntrySizeLimit_ = atomicServiceNonEntrySizeLimit;
557 }
558 } // namespace AppPackingTool
559 } // namespace OHOS