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 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