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> ¶meterMap, 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