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
26extern "C" {
27#endif
28
29static int AddHnpCfgFileToZip(zipFile zf, 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(zf, hnpCfgFile, buff, strlen(buff) + 1);
59    free(buff);
60    if (ret != 0) {
61        HNP_LOGE("add file to zip failed, file=%{public}s", hnpCfgFile);
62        return ret;
63    }
64
65    return 0;
66}
67
68static 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_LOGI("HnpZip dir=%{public}s, output=%{public}s ", hnpSrcPath, hnp_file_path);
85
86    zipFile zf = zipOpen(hnp_file_path, APPEND_STATUS_CREATE);
87    if (zf == NULL) {
88        HNP_LOGE("open zip=%{public}s unsuccess ", hnp_file_path);
89        return HNP_ERRNO_BASE_CREATE_ZIP_FAILED;
90    }
91
92    /* 将软件包压缩成独立的.hnp文件 */
93    ret = HnpZip(hnpSrcPath, zf);
94    if (ret != 0) {
95        HNP_LOGE("zip dir unsuccess! srcPath=%{public}s, hnpName=%{public}s, hnpVer=%{public}s, hnpDstPath=%{public}s"
96            "ret=%{public}d", hnpSrcPath, hnpCfg->name, hnpCfg->version, hnpDstPath, ret);
97        zipClose(zf, NULL);
98        return HNP_ERRNO_PACK_ZIP_DIR_FAILED;
99    }
100
101    /* 如果软件包中不存在hnp.json文件,则需要在hnp压缩文件中添加 */
102    if (hnpPack->hnpCfgExist == 0) {
103        ret = AddHnpCfgFileToZip(zf, hnpSrcPath, &hnpPack->cfgInfo);
104        if (ret != 0) {
105            HNP_LOGE("add file to zip failed ret=%d. zip=%s, src=%s",
106                ret, hnp_file_path, hnpSrcPath);
107            zipClose(zf, NULL);
108            return ret;
109        }
110    }
111
112    zipClose(zf, NULL);
113
114    HNP_LOGI("PackHnp end. srcPath=%{public}s, hnpName=%{public}s, hnpVer=%{public}s, hnpDstPath=%{public}s,"
115        "linkNum=%{public}d, ret=%{public}d", hnpSrcPath, hnpCfg->name, hnpCfg->version, hnpDstPath, hnpCfg->linkNum,
116        ret);
117
118    return ret;
119}
120
121static int GetHnpCfgInfo(const char *hnpCfgPath, const char *sourcePath, HnpCfgInfo *hnpCfg)
122{
123    NativeBinLink *linkArr = NULL;
124    char linksource[MAX_FILE_PATH_LEN] = {0};
125
126    int ret = ParseHnpCfgFile(hnpCfgPath, hnpCfg);
127    if (ret != 0) {
128        HNP_LOGE("parse hnp cfg[%{public}s] unsuccess! ret=%{public}d", hnpCfgPath, ret);
129        return ret;
130    }
131    /* 校验软连接的source文件是否存在 */
132    linkArr = hnpCfg->links;
133    for (unsigned int i = 0; i < hnpCfg->linkNum; i++, linkArr++) {
134        ret = sprintf_s(linksource, MAX_FILE_PATH_LEN, "%s/%s", sourcePath, linkArr->source);
135        if (ret < 0) {
136            free(hnpCfg->links);
137            hnpCfg->links = NULL;
138            HNP_LOGE("sprintf unsuccess.");
139            return HNP_ERRNO_BASE_SPRINTF_FAILED;
140        }
141        if (access(linksource, F_OK) != 0) {
142            free(hnpCfg->links);
143            hnpCfg->links = NULL;
144            HNP_LOGE("links source[%{public}s] not exist.", linksource);
145            return HNP_ERRNO_PACK_GET_REALPATH_FAILED;
146        }
147    }
148    return 0;
149}
150
151static int ParsePackArgs(HnpPackArgv *packArgv, HnpPackInfo *packInfo)
152{
153    char cfgPath[MAX_FILE_PATH_LEN];
154
155    if (packArgv->source == NULL) {
156        HNP_LOGE("source dir is null.");
157        return HNP_ERRNO_OPERATOR_ARGV_MISS;
158    }
159    if (GetRealPath(packArgv->source, packInfo->source) != 0) {
160        HNP_LOGE("source dir path=%{public}s is invalid.", packArgv->source);
161        return HNP_ERRNO_PACK_GET_REALPATH_FAILED;
162    }
163    if (packArgv->output == NULL) {
164        packArgv->output = ".";
165    }
166
167    if (GetRealPath(packArgv->output, packInfo->output) != 0) {
168        HNP_LOGE("output dir path=%{public}s is invalid.", packArgv->output);
169        return HNP_ERRNO_PACK_GET_REALPATH_FAILED;
170    }
171    /* 确认hnp.json文件是否存在,存在则对hnp.json文件进行解析并校验内容是否正确 */
172    int ret = sprintf_s(cfgPath, MAX_FILE_PATH_LEN, "%s%c"HNP_CFG_FILE_NAME, packInfo->source, DIR_SPLIT_SYMBOL);
173    if (ret < 0) {
174        HNP_LOGE("sprintf unsuccess.");
175        return HNP_ERRNO_BASE_SPRINTF_FAILED;
176    }
177    if (access(cfgPath, F_OK) != 0) {
178        /* hnp.json文件不存在则要求用户传入name和version信息 */
179        if ((packArgv->name == NULL) || (packArgv->version == NULL)) {
180            HNP_LOGE("name or version argv is miss.");
181            return HNP_ERRNO_OPERATOR_ARGV_MISS;
182        }
183        if (strcpy_s(packInfo->cfgInfo.name, MAX_FILE_PATH_LEN, packArgv->name) != EOK) {
184            HNP_LOGE("strcpy name argv unsuccess.");
185            return HNP_ERRNO_BASE_COPY_FAILED;
186        }
187        if (strcpy_s(packInfo->cfgInfo.version, HNP_VERSION_LEN, packArgv->version) != EOK) {
188            HNP_LOGE("strcpy version argv unsuccess.");
189            return HNP_ERRNO_BASE_COPY_FAILED;
190        }
191        packInfo->hnpCfgExist = 0;
192    } else {
193        ret = GetHnpCfgInfo(cfgPath, packInfo->source, &packInfo->cfgInfo);
194        if (ret != 0) {
195            return ret;
196        }
197        packInfo->hnpCfgExist = 1;
198    }
199    return 0;
200}
201
202int HnpCmdPack(int argc, char *argv[])
203{
204    HnpPackArgv packArgv = {0};
205    HnpPackInfo packInfo = {0};
206    int opt;
207
208    optind = 1; // 从头开始遍历参数
209    while ((opt = getopt_long(argc, argv, "hi:o:n:v:", NULL, NULL)) != -1) {
210        switch (opt) {
211            case 'h' :
212                return HNP_ERRNO_OPERATOR_ARGV_MISS;
213            case 'i' :
214                packArgv.source = optarg;
215                break;
216            case 'o' :
217                packArgv.output = optarg;
218                break;
219            case 'n' :
220                packArgv.name = optarg;
221                break;
222            case 'v' :
223                packArgv.version = optarg;
224                break;
225            default:
226                break;
227        }
228    }
229
230    // 解析参数并生成打包信息
231    int ret = ParsePackArgs(&packArgv, &packInfo);
232    if (ret != 0) {
233        return ret;
234    }
235
236    // 根据打包信息进行打包操作
237    ret = PackHnp(packInfo.source, packInfo.output, &packInfo);
238
239    // 释放软链接占用的内存
240    if (packInfo.cfgInfo.links != NULL) {
241        free(packInfo.cfgInfo.links);
242        packInfo.cfgInfo.links = NULL;
243    }
244
245    return ret;
246}
247
248#ifdef __cplusplus
249}
250#endif