• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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> &parameterMap, 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> &parameterMap, 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