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 <stdio.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <getopt.h>
20 
21 #include "securec.h"
22 
23 #include "hnp_pack.h"
24 
25 #ifdef __cplusplus
26 extern "C" {
27 #endif
28 
AddHnpCfgFileToZip(char * zipPath,const char * hnpSrcPath,HnpCfgInfo * hnpCfg)29 static int AddHnpCfgFileToZip(char *zipPath, const char *hnpSrcPath, HnpCfgInfo *hnpCfg)
30 {
31     int ret;
32     char *strPtr;
33     int offset;
34     char hnpCfgFile[MAX_FILE_PATH_LEN];
35     char *buff;
36 
37     // zip压缩文件内只保存相对路径,不保存绝对路径信息,偏移到压缩文件夹位置
38     strPtr = strrchr(hnpSrcPath, DIR_SPLIT_SYMBOL);
39     if (strPtr == NULL) {
40         offset = 0;
41     } else {
42         offset = strPtr - hnpSrcPath + 1;
43     }
44 
45     // zip函数根据后缀是否'/'区分目录还是文件
46     ret = sprintf_s(hnpCfgFile, MAX_FILE_PATH_LEN, "%s%c"HNP_CFG_FILE_NAME, hnpSrcPath + offset, DIR_SPLIT_SYMBOL);
47     if (ret < 0) {
48         HNP_LOGE("sprintf unsuccess.");
49         return HNP_ERRNO_BASE_SPRINTF_FAILED;
50     }
51     // 根据配置信息生成hnp.json内容
52     ret = GetHnpJsonBuff(hnpCfg, &buff);
53     if (ret != 0) {
54         HNP_LOGE("get hnp json content by cfg info unsuccess.");
55         return ret;
56     }
57     // 将hnp.json文件写入到.hnp压缩文件中
58     ret = HnpAddFileToZip(zipPath, hnpCfgFile, buff, strlen(buff) + 1);
59     free(buff);
60     if (ret != 0) {
61         HNP_LOGE("add file to zip failed.zip=%{public}s, file=%{public}s", zipPath, hnpCfgFile);
62         return ret;
63     }
64 
65     return 0;
66 }
67 
PackHnp(const char * hnpSrcPath,const char * hnpDstPath,HnpPackInfo * hnpPack)68 static int PackHnp(const char *hnpSrcPath, const char *hnpDstPath, HnpPackInfo *hnpPack)
69 {
70     int ret;
71     char hnp_file_path[MAX_FILE_PATH_LEN];
72     HnpCfgInfo *hnpCfg = &hnpPack->cfgInfo;
73 
74     HNP_LOGI("PackHnp start. srcPath=%{public}s, hnpName=%{public}s, hnpVer=%{public}s, hnpDstPath=%{public}s ",
75         hnpSrcPath, hnpCfg->name, hnpCfg->version, hnpDstPath);
76 
77     /* 拼接hnp文件名 */
78     ret = sprintf_s(hnp_file_path, MAX_FILE_PATH_LEN, "%s%c%s.hnp", hnpDstPath, DIR_SPLIT_SYMBOL, hnpCfg->name);
79     if (ret < 0) {
80         HNP_LOGE("sprintf unsuccess.");
81         return HNP_ERRNO_PACK_GET_HNP_PATH_FAILED;
82     }
83 
84     /* 将软件包压缩成独立的.hnp文件 */
85     ret = HnpZip(hnpSrcPath, hnp_file_path);
86     if (ret != 0) {
87         HNP_LOGE("zip dir unsuccess! srcPath=%{public}s, hnpName=%{public}s, hnpVer=%{public}s, hnpDstPath=%{public}s"
88             "ret=%{public}d", hnpSrcPath, hnpCfg->name, hnpCfg->version, hnpDstPath, ret);
89         return HNP_ERRNO_PACK_ZIP_DIR_FAILED;
90     }
91 
92     /* 如果软件包中不存在hnp.json文件,则需要在hnp压缩文件中添加 */
93     if (hnpPack->hnpCfgExist == 0) {
94         ret = AddHnpCfgFileToZip(hnp_file_path, hnpSrcPath, &hnpPack->cfgInfo);
95         if (ret != 0) {
96             HNP_LOGE("add file to zip failed ret=%d. zip=%s, src=%s",
97                 ret, hnp_file_path, hnpSrcPath);
98             return ret;
99         }
100     }
101 
102     HNP_LOGI("PackHnp end. srcPath=%{public}s, hnpName=%{public}s, hnpVer=%{public}s, hnpDstPath=%{public}s,"
103         "linkNum=%{public}d, ret=%{public}d", hnpSrcPath, hnpCfg->name, hnpCfg->version, hnpDstPath, hnpCfg->linkNum,
104         ret);
105 
106     return ret;
107 }
108 
GetHnpCfgInfo(const char * hnpCfgPath,const char * sourcePath,HnpCfgInfo * hnpCfg)109 static int GetHnpCfgInfo(const char *hnpCfgPath, const char *sourcePath, HnpCfgInfo *hnpCfg)
110 {
111     NativeBinLink *linkArr = NULL;
112     char linksource[MAX_FILE_PATH_LEN] = {0};
113 
114     int ret = ParseHnpCfgFile(hnpCfgPath, hnpCfg);
115     if (ret != 0) {
116         HNP_LOGE("parse hnp cfg[%{public}s] unsuccess! ret=%{public}d", hnpCfgPath, ret);
117         return ret;
118     }
119     /* 校验软连接的source文件是否存在 */
120     linkArr = hnpCfg->links;
121     for (unsigned int i = 0; i < hnpCfg->linkNum; i++, linkArr++) {
122         ret = sprintf_s(linksource, MAX_FILE_PATH_LEN, "%s/%s", sourcePath, linkArr->source);
123         if (ret < 0) {
124             free(hnpCfg->links);
125             hnpCfg->links = NULL;
126             HNP_LOGE("sprintf unsuccess.");
127             return HNP_ERRNO_BASE_SPRINTF_FAILED;
128         }
129         if (access(linksource, F_OK) != 0) {
130             free(hnpCfg->links);
131             hnpCfg->links = NULL;
132             HNP_LOGE("links source[%{public}s] not exist.", linksource);
133             return HNP_ERRNO_PACK_GET_REALPATH_FAILED;
134         }
135     }
136     return 0;
137 }
138 
ParsePackArgs(HnpPackArgv * packArgv,HnpPackInfo * packInfo)139 static int ParsePackArgs(HnpPackArgv *packArgv, HnpPackInfo *packInfo)
140 {
141     char cfgPath[MAX_FILE_PATH_LEN];
142 
143     if (packArgv->source == NULL) {
144         HNP_LOGE("source dir is null.");
145         return HNP_ERRNO_OPERATOR_ARGV_MISS;
146     }
147     if (GetRealPath(packArgv->source, packInfo->source) != 0) {
148         HNP_LOGE("source dir path=%{public}s is invalid.", packArgv->source);
149         return HNP_ERRNO_PACK_GET_REALPATH_FAILED;
150     }
151     if (packArgv->output == NULL) {
152         packArgv->output = ".";
153     }
154 
155     if (GetRealPath(packArgv->output, packInfo->output) != 0) {
156         HNP_LOGE("output dir path=%{public}s is invalid.", packArgv->output);
157         return HNP_ERRNO_PACK_GET_REALPATH_FAILED;
158     }
159     /* 确认hnp.json文件是否存在,存在则对hnp.json文件进行解析并校验内容是否正确 */
160     int ret = sprintf_s(cfgPath, MAX_FILE_PATH_LEN, "%s%c"HNP_CFG_FILE_NAME, packInfo->source, DIR_SPLIT_SYMBOL);
161     if (ret < 0) {
162         HNP_LOGE("sprintf unsuccess.");
163         return HNP_ERRNO_BASE_SPRINTF_FAILED;
164     }
165     if (access(cfgPath, F_OK) != 0) {
166         /* hnp.json文件不存在则要求用户传入name和version信息 */
167         if ((packArgv->name == NULL) || (packArgv->version == NULL)) {
168             HNP_LOGE("name or version argv is miss.");
169             return HNP_ERRNO_OPERATOR_ARGV_MISS;
170         }
171         if (strcpy_s(packInfo->cfgInfo.name, MAX_FILE_PATH_LEN, packArgv->name) != EOK) {
172             HNP_LOGE("strcpy name argv unsuccess.");
173             return HNP_ERRNO_BASE_COPY_FAILED;
174         }
175         if (strcpy_s(packInfo->cfgInfo.version, HNP_VERSION_LEN, packArgv->version) != EOK) {
176             HNP_LOGE("strcpy version argv unsuccess.");
177             return HNP_ERRNO_BASE_COPY_FAILED;
178         }
179         packInfo->hnpCfgExist = 0;
180     } else {
181         ret = GetHnpCfgInfo(cfgPath, packInfo->source, &packInfo->cfgInfo);
182         if (ret != 0) {
183             return ret;
184         }
185         packInfo->hnpCfgExist = 1;
186     }
187     return 0;
188 }
189 
HnpCmdPack(int argc,char * argv[])190 int HnpCmdPack(int argc, char *argv[])
191 {
192     HnpPackArgv packArgv = {0};
193     HnpPackInfo packInfo = {0};
194     int opt;
195 
196     optind = 1; // 从头开始遍历参数
197     while ((opt = getopt_long(argc, argv, "hi:o:n:v:", NULL, NULL)) != -1) {
198         switch (opt) {
199             case 'h' :
200                 return HNP_ERRNO_OPERATOR_ARGV_MISS;
201             case 'i' :
202                 packArgv.source = optarg;
203                 break;
204             case 'o' :
205                 packArgv.output = optarg;
206                 break;
207             case 'n' :
208                 packArgv.name = optarg;
209                 break;
210             case 'v' :
211                 packArgv.version = optarg;
212                 break;
213             default:
214                 break;
215         }
216     }
217 
218     // 解析参数并生成打包信息
219     int ret = ParsePackArgs(&packArgv, &packInfo);
220     if (ret != 0) {
221         return ret;
222     }
223 
224     // 根据打包信息进行打包操作
225     ret = PackHnp(packInfo.source, packInfo.output, &packInfo);
226 
227     // 释放软链接占用的内存
228     if (packInfo.cfgInfo.links != NULL) {
229         free(packInfo.cfgInfo.links);
230         packInfo.cfgInfo.links = NULL;
231     }
232 
233     return ret;
234 }
235 
236 #ifdef __cplusplus
237 }
238 #endif