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