• 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 "multiapp_packager.h"
17 
18 #include <fstream>
19 
20 #include "constants.h"
21 #include "json/module_json.h"
22 #include "json/module_json_utils.h"
23 #include "json/pack_info_utils.h"
24 #include "log.h"
25 #include "utils.h"
26 #include "zip_utils.h"
27 
28 namespace OHOS {
29 namespace AppPackingTool {
MultiAppPackager(const std::map<std::string,std::string> & parameterMap,std::string & resultReceiver)30 MultiAppPackager::MultiAppPackager(const std::map<std::string, std::string> &parameterMap, std::string &resultReceiver)
31     : Packager(parameterMap, resultReceiver)
32 {}
33 
InitAllowedParam()34 int32_t MultiAppPackager::InitAllowedParam()
35 {
36     allowedParameters_ = {
37         {}
38     };
39     return ERR_OK;
40 }
41 
PreProcess()42 int32_t MultiAppPackager::PreProcess()
43 {
44     if (!CheckForceFlag()) {
45         return ERR_INVALID_VALUE;
46     }
47     bool ret = IsVerifyValidInMultiAppMode();
48     if (!ret) {
49         return ERR_INVALID_VALUE;
50     }
51     if (!Packager::ParseAtomicServiceSizeLimit()) {
52         return ERR_INVALID_VALUE;
53     }
54     return ERR_OK;
55 }
56 
Process()57 int32_t MultiAppPackager::Process()
58 {
59     bool ret = CompressAppModeForMultiProject();
60     if (!ret) {
61         std::string outPath;
62         if (parameterMap_.find(Constants::PARAM_OUT_PATH) != parameterMap_.end()) {
63             outPath = parameterMap_.at(Constants::PARAM_OUT_PATH);
64         }
65         if (fs::exists(outPath)) {
66             fs::remove_all(outPath);
67         }
68         LOGE("MultiApp Process failed.");
69         return ERR_INVALID_VALUE;
70     }
71     return ERR_OK;
72 }
73 
PostProcess()74 int32_t MultiAppPackager::PostProcess()
75 {
76     return ERR_OK;
77 }
78 
GetAndCheckOutPath(std::string & outPath)79 bool MultiAppPackager::GetAndCheckOutPath(std::string &outPath)
80 {
81     if (parameterMap_.find(Constants::PARAM_OUT_PATH) == parameterMap_.end()) {
82         LOGE("input out-path are null.");
83         return false;
84     }
85     outPath = parameterMap_.at(Constants::PARAM_OUT_PATH);
86     if (outPath.empty()) {
87         LOGE("input out-path are empty.");
88         return false;
89     }
90     if (outPath.find('.') == std::string::npos ||
91         outPath.substr(outPath.size() - Constants::APP_SUFFIX_LENGTH) != Constants::APP_SUFFIX) {
92         LOGE("out-path must end with .app.");
93         return false;
94     }
95     return true;
96 }
97 
GetAndCheckHapAndHspAndAppListStr(std::string & hapListStr,std::string & hspListStr,std::string & appListStr)98 bool MultiAppPackager::GetAndCheckHapAndHspAndAppListStr(std::string &hapListStr, std::string &hspListStr,
99     std::string &appListStr)
100 {
101     if (parameterMap_.find(Constants::PARAM_HAP_LIST) == parameterMap_.end() &&
102         parameterMap_.find(Constants::PARAM_HSP_LIST) == parameterMap_.end() &&
103         parameterMap_.find(Constants::PARAM_APP_LIST) == parameterMap_.end()) {
104         LOGE("input hap-list, hsp-list and app-list are all null.");
105         return false;
106     }
107     if (parameterMap_.find(Constants::PARAM_HAP_LIST) != parameterMap_.end()) {
108         hapListStr = parameterMap_.at(Constants::PARAM_HAP_LIST);
109     }
110     if (parameterMap_.find(Constants::PARAM_HSP_LIST) != parameterMap_.end()) {
111         hspListStr = parameterMap_.at(Constants::PARAM_HSP_LIST);
112     }
113     if (parameterMap_.find(Constants::PARAM_APP_LIST) != parameterMap_.end()) {
114         appListStr = parameterMap_.at(Constants::PARAM_APP_LIST);
115     }
116     if (hapListStr.empty() && hspListStr.empty() && appListStr.empty()) {
117         LOGE("input hap-list, hsp-list and app-list are all empty.");
118         return false;
119     }
120     if (!hapListStr.empty()) {
121         if (!CompatibleProcess(hapListStr, formattedHapAndHspList_, Constants::HAP_SUFFIX)) {
122             LOGE("hap-list is invalid.");
123             return false;
124         }
125     }
126     if (!hspListStr.empty()) {
127         if (!CompatibleProcess(hspListStr, formattedHapAndHspList_, Constants::HSP_SUFFIX)) {
128             LOGE("hsp-list is invalid.");
129             return false;
130         }
131     }
132     if (!appListStr.empty()) {
133         if (!CompatibleProcess(appListStr, formattedAppList_, Constants::APP_SUFFIX)) {
134             LOGE("app-list is invalid.");
135             return false;
136         }
137     }
138     return true;
139 }
140 
IsVerifyValidInMultiAppMode()141 bool MultiAppPackager::IsVerifyValidInMultiAppMode()
142 {
143     std::string hapListStr;
144     std::string hspListStr;
145     std::string appListStr;
146     if (!GetAndCheckHapAndHspAndAppListStr(hapListStr, hspListStr, appListStr)) {
147         LOGE("GetAndCheckHapAndHspAndAppListStr failed!");
148         return false;
149     }
150 
151     auto it = parameterMap_.find(Constants::PARAM_PAC_JSON_PATH);
152     if (it != parameterMap_.end() && !it->second.empty() &&
153         !IsFileMatch(it->second, Constants::PAC_JSON)) {
154         LOGE("MultiAppPackager::IsVerifyValidInMultiAppMode pac-json-path is invalid.");
155         return false;
156     }
157 
158     std::string outPath;
159     if (!GetAndCheckOutPath(outPath)) {
160         LOGE("GetAndCheckOutPath failed!");
161         return false;
162     }
163 
164     std::string force;
165     it = parameterMap_.find(Constants::PARAM_FORCE);
166     if (it != parameterMap_.end() && !it->second.empty()) {
167         force = it->second;
168         if (Utils::IsFileExists(outPath) && force == "false") {
169             LOGE("out-path file already existed.");
170             return false;
171         }
172     }
173 
174     return IsOutPathValid(outPath, force, Constants::MODE_MULTIAPP);
175 }
176 
CopyHapAndHspFromApp(const std::string & appPath,std::list<std::string> & selectedHapsInApp,std::list<std::string> & selectedHaps,const std::string & tempDir)177 bool MultiAppPackager::CopyHapAndHspFromApp(const std::string &appPath, std::list<std::string> &selectedHapsInApp,
178                                             std::list<std::string> &selectedHaps, const std::string &tempDir)
179 {
180     fs::path tempPath;
181     if (fs::exists(fs::path(tempDir).parent_path().parent_path()) &&
182         fs::path(tempDir).parent_path().parent_path() != fs::path("/")) {
183         tempPath = fs::path(tempDir).parent_path().parent_path() / ((Constants::COMPRESSOR_MULTIAPP_TEMP_DIR) +
184             Utils::GenerateUUID());
185     } else {
186         tempPath = fs::path(tempDir).parent_path() / ((Constants::COMPRESSOR_MULTIAPP_TEMP_DIR) +
187             Utils::GenerateUUID());
188     }
189     ZipUtils::Unzip(appPath, tempPath);
190     fs::path filePath;
191     for (const auto &entry : fs::directory_iterator(tempPath)) {
192         if (!Utils::EndsWith(entry.path().filename(), Constants::HAP_SUFFIX) &&
193             !Utils::EndsWith(entry.path().filename(), Constants::HSP_SUFFIX)) {
194             continue;
195         }
196         if (std::find(selectedHaps.begin(), selectedHaps.end(), entry.path().filename()) != selectedHaps.end()) {
197             LOGE("CopyHapAndHspFromApp file duplicated, file is %s ", entry.path().filename().c_str());
198             if (fs::exists(tempPath)) {
199                 fs::remove_all(tempPath);
200             }
201             return false;
202         } else {
203             filePath = fs::path(tempDir) / entry.path().filename();
204             selectedHaps.push_back(filePath.filename());
205             selectedHapsInApp.push_back(filePath.filename());
206         }
207         std::ofstream outputStream(filePath, std::ofstream::binary);
208         std::ifstream inputStream(entry, std::ofstream::binary);
209         std::vector<char> buf(Constants::BUFFER_SIZE);
210         while (inputStream.read(buf.data(), Constants::BUFFER_SIZE) || inputStream.gcount() != 0) {
211             outputStream.write(buf.data(), inputStream.gcount());
212         }
213         outputStream.close();
214         inputStream.close();
215     }
216     if (fs::exists(tempPath)) {
217         fs::remove_all(tempPath);
218     }
219     return true;
220 }
221 
GetJsonInZips(const std::string & filePath,const std::string & jsonName)222 std::string MultiAppPackager::GetJsonInZips(const std::string &filePath, const std::string &jsonName)
223 {
224     std::string jsonStr;
225     if (ZipUtils::IsFileExistsInZip(filePath, jsonName)) {
226         if (ZipUtils::GetFileContentFromZip(filePath, jsonName, jsonStr)) {
227             jsonStr.erase(std::remove_if(jsonStr.begin(), jsonStr.end(),
228                 [](char c) { return c == '\r' || c == '\n' || c == '\t'; }),
229                 jsonStr.end());
230         }
231     }
232     return jsonStr;
233 }
234 
ReadModuleNameFromHap(const std::string & hapPath)235 std::string MultiAppPackager::ReadModuleNameFromHap(const std::string &hapPath)
236 {
237     std::string moduleName;
238     fs::path hapFile(hapPath);
239     ModuleJson module;
240     if (IsModuleHap(hapFile)) {
241         std::string jsonString = GetJsonInZips(hapFile, Constants::MODULE_JSON);
242         module.ParseFromString(jsonString);
243         module.GetStageModuleName(moduleName);
244     } else {
245         std::string jsonString = GetJsonInZips(hapFile, Constants::CONFIG_JSON);
246         module.ParseFromString(jsonString);
247         module.GetFaModuleName(moduleName);
248     }
249     return moduleName;
250 }
251 
SelectHapInApp(const std::string & appPath,std::list<std::string> & selectedHaps,const std::string & tempDir,std::string & finalAppPackInfo)252 std::string MultiAppPackager::SelectHapInApp(const std::string &appPath, std::list<std::string> &selectedHaps,
253                                              const std::string &tempDir, std::string &finalAppPackInfo)
254 {
255     std::list<std::string> selectedHapsInApp;
256     CopyHapAndHspFromApp(appPath, selectedHapsInApp, selectedHaps, tempDir);
257     std::string packInfoStr = GetJsonInZips(appPath, Constants::PACK_INFO);
258     if (packInfoStr.empty()) {
259         LOGE("MultiAppPackager:SelectHapInApp failed, app has no pack.info.");
260     }
261     if (finalAppPackInfo.empty()) {
262         finalAppPackInfo = packInfoStr;
263         return finalAppPackInfo;
264     }
265 
266     std::map<std::string, std::string> packagePair;
267     for (const auto &hapName : selectedHapsInApp) {
268         packagePair[hapName] = ReadModuleNameFromHap(tempDir + "/" + hapName);
269     }
270     std::string packInfoJsonStr;
271     if (!PackInfoUtils::MergeTwoPackInfosByPackagePair(finalAppPackInfo, packInfoStr, packagePair, packInfoJsonStr)) {
272         LOGE("PackInfoUtils::MergeTwoPackInfosByPackagePair failed.");
273     }
274     return packInfoJsonStr;
275 }
276 
DisposeApp(std::list<std::string> & selectedHaps,const std::string & tempDir)277 std::string MultiAppPackager::DisposeApp(std::list<std::string> &selectedHaps, const std::string &tempDir)
278 {
279     std::string finalAppPackInfo;
280     if (formattedAppList_.empty()) {
281         return finalAppPackInfo;
282     }
283     for (const auto &appPath : formattedAppList_) {
284         finalAppPackInfo = SelectHapInApp(appPath, selectedHaps, tempDir, finalAppPackInfo);
285     }
286     return finalAppPackInfo;
287 }
288 
DisposeHapAndHsp(std::list<std::string> & selectedHaps,const std::string & tempDir,std::string finalPackInfoStr)289 std::string MultiAppPackager::DisposeHapAndHsp(std::list<std::string> &selectedHaps,
290                                                const std::string &tempDir, std::string finalPackInfoStr)
291 {
292     if (formattedHapAndHspList_.empty()) {
293         return finalPackInfoStr;
294     }
295     for (const auto &hapPath : formattedHapAndHspList_) {
296         fs::path hapPathFile(hapPath);
297         if (std::find(selectedHaps.begin(), selectedHaps.end(), hapPathFile.filename()) != selectedHaps.end()) {
298             LOGE("file duplicated, file is %s", hapPathFile.filename().c_str());
299         }
300         fs::path hapFile(hapPath);
301         selectedHaps.push_back(hapFile.filename());
302         std::string dstDirString = tempDir + "/" + static_cast<std::string>(hapFile.filename());
303         Utils::CopyFile(hapPath, dstDirString);
304         std::string packInfo = GetJsonInZips(hapFile, Constants::PACK_INFO);
305         if (packInfo.empty()) {
306             LOGW("hap has no pack.info.");
307         }
308         if (finalPackInfoStr.empty()) {
309             finalPackInfoStr = packInfo;
310         } else {
311             std::string packInfoJsonStr;
312             if (!PackInfoUtils::MergeTwoPackInfos(finalPackInfoStr, packInfo, packInfoJsonStr)) {
313                 LOGE("PackInfoUtils::MergeTwoPackInfos failed.");
314             }
315             finalPackInfoStr = packInfoJsonStr;
316         }
317     }
318     return finalPackInfoStr;
319 }
320 
WritePackInfo(const std::string & filePath,const std::string & packInfoStr)321 void MultiAppPackager::WritePackInfo(const std::string &filePath, const std::string &packInfoStr)
322 {
323     std::string realFilePath;
324     if (!Utils::GetRealPathOfNoneExistFile(filePath, realFilePath)) {
325         LOGE("get real pack info path failed! packInfoPath=%s", filePath.c_str());
326         return;
327     }
328     std::ofstream fwriter(realFilePath);
329     if (!fwriter) {
330         LOGE("open file failed![filePath=%s][realFilePath=%s]", filePath.c_str(), realFilePath.c_str());
331         return;
332     }
333     fwriter << packInfoStr;
334     if (fwriter.fail()) {
335         LOGE("write pack info failed. Error writing to file: %s", filePath.c_str());
336         return;
337     }
338     fwriter.close();
339 }
340 
PrepareFilesForCompression(std::list<std::string> & fileList,fs::path & tempHapDirPath,fs::path & tempSelectedHapDirPath,std::string & finalPackInfoStr,std::string & finalPackInfoPath)341 bool MultiAppPackager::PrepareFilesForCompression(std::list<std::string> &fileList, fs::path &tempHapDirPath,
342     fs::path &tempSelectedHapDirPath, std::string &finalPackInfoStr, std::string &finalPackInfoPath)
343 {
344     std::string outPath = parameterMap_.at(Constants::PARAM_OUT_PATH);
345     zipWrapper_.Open(outPath);
346     if (!zipWrapper_.IsOpen()) {
347         LOGE("MultiAppPackager::CompressAppModeForMultiProject: zipWrapper Open failed!");
348         return false;
349     }
350 
351     char path[PATH_MAX] = {0};
352     if (outPath.length() >= PATH_MAX || realpath(outPath.c_str(), path) == nullptr) {
353         LOGE("get realpath failed");
354         return false;
355     }
356     if (fs::exists(fs::path(path).parent_path().parent_path()) &&
357         fs::path(path).parent_path().parent_path() != fs::path("/")) {
358         tempHapDirPath =  fs::path(path).parent_path().parent_path() / ((Constants::COMPRESSOR_MULTIAPP_TEMP_DIR) +
359             Utils::GenerateUUID());
360         tempSelectedHapDirPath = fs::path(path).parent_path().parent_path() /
361             ((Constants::COMPRESSOR_MULTIAPP_TEMP_DIR) + Utils::GenerateUUID());
362     } else {
363         tempHapDirPath =  fs::path(path).parent_path() / ((Constants::COMPRESSOR_MULTIAPP_TEMP_DIR) +
364             Utils::GenerateUUID());
365         tempSelectedHapDirPath = fs::path(path).parent_path() / ((Constants::COMPRESSOR_MULTIAPP_TEMP_DIR) +
366             Utils::GenerateUUID());
367     }
368     if (!fs::exists(tempHapDirPath)) {
369         fs::create_directories(tempHapDirPath);
370     }
371     if (!fs::exists(tempSelectedHapDirPath)) {
372         fs::create_directories(tempSelectedHapDirPath);
373     }
374     std::list<std::string> selectedHaps;
375     finalPackInfoStr = DisposeApp(selectedHaps, tempSelectedHapDirPath);
376     finalPackInfoStr = DisposeHapAndHsp(selectedHaps, tempSelectedHapDirPath, finalPackInfoStr);
377     finalPackInfoPath = tempSelectedHapDirPath.string() + "/" + Constants::PACK_INFO;
378     WritePackInfo(finalPackInfoPath, finalPackInfoStr);
379     for (const auto &selectedHapName : selectedHaps) {
380         std::string hapPathItem = tempSelectedHapDirPath.string() + "/" + selectedHapName;
381         fs::path hapFile(hapPathItem);
382         std::string hapTempPath = tempHapDirPath.string() + "/" + hapFile.filename().string();
383         fs::path hapUnzipTempPath = tempHapDirPath / ((Constants::COMPRESSOR_MULTIAPP_TEMP_DIR) +
384             Utils::GenerateUUID());
385         fileList.push_back(hapTempPath);
386         CompressPackinfoIntoHap(hapPathItem, hapUnzipTempPath, hapTempPath, finalPackInfoPath);
387     }
388     return true;
389 }
390 
CompressAppModeForMultiProject()391 bool MultiAppPackager::CompressAppModeForMultiProject()
392 {
393     std::list<std::string> fileList;
394     fs::path tempHapDirPath;
395     fs::path tempSelectedHapDirPath;
396     std::string finalPackInfoStr;
397     std::string finalPackInfoPath;
398     if (!PrepareFilesForCompression(fileList, tempHapDirPath, tempSelectedHapDirPath, finalPackInfoStr,
399         finalPackInfoPath)) {
400         LOGE("CompressAppModeForMultiProject PrepareFilesForCompression failed.");
401         return false;
402     }
403     if (!ModuleJsonUtils::CheckHapsIsValid(fileList, false)) {
404         LOGE("here are somehaps with different version code or duplicated moduleName or packageName.");
405         if (fs::exists(tempHapDirPath)) {
406             fs::remove_all(tempHapDirPath);
407         }
408         if (fs::exists(tempSelectedHapDirPath)) {
409             fs::remove_all(tempSelectedHapDirPath);
410         }
411         return false;
412     }
413     if (!ModuleJsonUtils::GetHapVerifyInfosMapfromFileList(fileList, hapVerifyInfoMap_)) {
414         LOGE("MultiAppPackager::CompressAppModeForMultiProject GetHapVerifyInfosMapfromFileList failed.");
415         return false;
416     }
417     for (const auto &hapPath : fileList) {
418         std::string zipPath = fs::path(hapPath).filename().string();
419         if (zipWrapper_.AddFileOrDirectoryToZip(hapPath, zipPath) != ZipErrCode::ZIP_ERR_SUCCESS) {
420             return false;
421         }
422     }
423     if (zipWrapper_.AddFileOrDirectoryToZip(finalPackInfoPath, Constants::PACK_INFO) != ZipErrCode::ZIP_ERR_SUCCESS) {
424         return false;
425     }
426     std::map<std::string, std::string>::const_iterator it = parameterMap_.find(Constants::PARAM_PAC_JSON_PATH);
427     if (it != parameterMap_.end() && !it->second.empty()) {
428         if (zipWrapper_.AddFileOrDirectoryToZip(it->second, Constants::PAC_JSON) != ZipErrCode::ZIP_ERR_SUCCESS) {
429             LOGE("MultiAppPackager::CompressAppModeForMultiProject: zipWrapper pac.json failed!");
430             return false;
431         }
432     }
433     zipWrapper_.Close();
434     if (fs::exists(tempHapDirPath)) {
435         fs::remove_all(tempHapDirPath);
436     }
437     if (fs::exists(tempSelectedHapDirPath)) {
438         fs::remove_all(tempSelectedHapDirPath);
439     }
440     if (!ModuleJsonUtils::CheckAppAtomicServiceCompressedSizeValid(parameterMap_, hapVerifyInfoMap_)) {
441         LOGE("MultiAppPackager::CompressAppModeForMultiProject: CheckAppAtomicServiceCompressedSizeValid() failed!");
442         return false;
443     }
444     return true;
445 }
446 } // namespace AppPackingTool
447 } // namespace OHOS