xref: /base/startup/appspawn/service/hnp/base/hnp_zip.c (revision 69570cc8)
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 <stdlib.h>
19#include <sys/stat.h>
20#include <errno.h>
21#include <unistd.h>
22
23#include <dirent.h>
24#ifdef _WIN32
25#include <windows.h>
26
27#endif
28
29#include "zlib.h"
30#include "contrib/minizip/zip.h"
31#include "contrib/minizip/unzip.h"
32
33#include "securec.h"
34
35#include "hnp_base.h"
36
37#ifdef __cplusplus
38extern "C" {
39#endif
40
41#define ZIP_EXTERNAL_FA_OFFSET 16
42
43// zipOpenNewFileInZip3只识别带‘/’的路径,需要将路径中‘\’转换成‘/’
44static void TransPath(const char *input, char *output)
45{
46    int len = strlen(input);
47    for (int i = 0; i < len; i++) {
48        if (input[i] == '\\') {
49            output[i] = '/';
50        } else {
51            output[i] = input[i];
52        }
53    }
54    output[len] = '\0';
55}
56
57#ifdef _WIN32
58// 转换char路径字符串为wchar_t宽字符串,支持路径字符串长度超过260
59static bool TransWidePath(const char *inPath, wchar_t *outPath)
60{
61    wchar_t tmpPath[MAX_FILE_PATH_LEN] = {0};
62    MultiByteToWideChar(CP_ACP, 0, inPath, -1, tmpPath, MAX_FILE_PATH_LEN);
63    if (swprintf_s(outPath, MAX_FILE_PATH_LEN, L"\\\\?\\%ls", tmpPath) < 0) {
64        HNP_LOGE("swprintf unsuccess.");
65        return false;
66    }
67    return true;
68}
69#endif
70
71// 向zip压缩包中添加文件
72static int ZipAddFile(const char* file, int offset, zipFile zf)
73{
74    int err;
75    char buf[1024];
76    char transPath[MAX_FILE_PATH_LEN];
77    size_t len;
78    FILE *f;
79    zip_fileinfo fileInfo = {0};
80
81#ifdef _WIN32
82    struct _stat buffer = {0};
83    // 使用wchar_t支持处理字符串长度超过260的路径字符串
84    wchar_t wideFullPath[MAX_FILE_PATH_LEN] = {0};
85    if (!TransWidePath(file, wideFullPath)) {
86        return HNP_ERRNO_BASE_STAT_FAILED;
87    }
88    if (_wstat(wideFullPath, &buffer) != 0) {
89        HNP_LOGE("get filefile[%{public}s] stat fail.", file);
90        return HNP_ERRNO_BASE_STAT_FAILED;
91    }
92    buffer.st_mode |= S_IXOTH;
93#else
94    struct stat buffer = {0};
95    if (stat(file, &buffer) != 0) {
96        HNP_LOGE("get filefile[%{public}s] stat fail.", file);
97        return HNP_ERRNO_BASE_STAT_FAILED;
98    }
99#endif
100    fileInfo.external_fa = (buffer.st_mode & 0xFFFF) << ZIP_EXTERNAL_FA_OFFSET;
101    TransPath(file, transPath);
102    err = zipOpenNewFileInZip3(zf, transPath + offset, &fileInfo, NULL, 0, NULL, 0, NULL, Z_DEFLATED,
103        Z_BEST_COMPRESSION, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0);
104    if (err != ZIP_OK) {
105        HNP_LOGE("open new file[%{public}s] in zip unsuccess ", file);
106        return HNP_ERRNO_BASE_CREATE_ZIP_FAILED;
107    }
108#ifdef _WIN32
109    f = _wfopen(wideFullPath, L"rb");
110#else
111    f = fopen(file, "rb");
112#endif
113    if (f == NULL) {
114        HNP_LOGE("open file[%{public}s] unsuccess ", file);
115        return HNP_ERRNO_BASE_FILE_OPEN_FAILED;
116    }
117
118    while ((len = fread(buf, 1, sizeof(buf), f)) > 0) {
119        zipWriteInFileInZip(zf, buf, len);
120    }
121    (void)fclose(f);
122    zipCloseFileInZip(zf);
123    return 0;
124}
125
126// 判断是否为目录
127static int IsDirPath(struct dirent *entry, char *fullPath, int *isDir)
128{
129#ifdef _WIN32
130    // 使用wchar_t支持处理字符串长度超过260的路径字符串
131    wchar_t wideFullPath[MAX_FILE_PATH_LEN] = {0};
132    if (!TransWidePath(fullPath, wideFullPath)) {
133        return HNP_ERRNO_GET_FILE_ATTR_FAILED;
134    }
135    DWORD fileAttr = GetFileAttributesW(wideFullPath);
136    if (fileAttr == INVALID_FILE_ATTRIBUTES) {
137        DWORD err = GetLastError();
138        HNP_LOGE("get file[%{public}s] attr unsuccess, errno[%{public}lu].", fullPath, err);
139        return HNP_ERRNO_GET_FILE_ATTR_FAILED;
140    }
141    *isDir = (int)(fileAttr & FILE_ATTRIBUTE_DIRECTORY);
142#else
143    *isDir = (int)(entry->d_type == DT_DIR);
144#endif
145
146    return 0;
147}
148
149static int ZipAddDir(const char *sourcePath, int offset, zipFile zf);
150
151static int ZipHandleDir(char *fullPath, int offset, zipFile zf)
152{
153    int ret;
154    char transPath[MAX_FILE_PATH_LEN];
155    TransPath(fullPath, transPath);
156    if (zipOpenNewFileInZip3(zf, transPath + offset, NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED,
157                             Z_BEST_COMPRESSION, 0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
158                             NULL, 0) != ZIP_OK) {
159        HNP_LOGE("open new file[%{public}s] in zip unsuccess ", fullPath);
160        return HNP_ERRNO_BASE_CREATE_ZIP_FAILED;
161    }
162    zipCloseFileInZip(zf);
163    ret = ZipAddDir(fullPath, offset, zf);
164    if (ret != 0) {
165        HNP_LOGE("zip add dir[%{public}s] unsuccess ", fullPath);
166        return ret;
167    }
168    return 0;
169}
170
171// sourcePath--文件夹路径  zf--压缩文件句柄
172static int ZipAddDir(const char *sourcePath, int offset, zipFile zf)
173{
174    struct dirent *entry;
175    char fullPath[MAX_FILE_PATH_LEN];
176    int isDir;
177
178    DIR *dir = opendir(sourcePath);
179    if (dir == NULL) {
180        HNP_LOGE("open dir=%{public}s unsuccess ", sourcePath);
181        return HNP_ERRNO_BASE_DIR_OPEN_FAILED;
182    }
183
184    while ((entry = readdir(dir)) != NULL) {
185        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
186            continue;
187        }
188        if (sprintf_s(fullPath, MAX_FILE_PATH_LEN, "%s%s", sourcePath, entry->d_name) < 0) {
189            HNP_LOGE("sprintf unsuccess.");
190            closedir(dir);
191            return HNP_ERRNO_BASE_SPRINTF_FAILED;
192        }
193        int ret = IsDirPath(entry, fullPath, &isDir);
194        if (ret != 0) {
195            closedir(dir);
196            return ret;
197        }
198        if (isDir) {
199            int endPos = strlen(fullPath);
200            if (endPos + 1 < MAX_FILE_PATH_LEN) {
201                fullPath[endPos] = DIR_SPLIT_SYMBOL;
202                fullPath[endPos + 1] = '\0';
203            } else {
204                closedir(dir);
205                return HNP_ERRNO_BASE_STRING_LEN_OVER_LIMIT;
206            }
207            ret = ZipHandleDir(fullPath, offset, zf);
208            if (ret != 0) {
209                closedir(dir);
210                return ret;
211            }
212        } else if ((ret = ZipAddFile(fullPath, offset, zf)) != 0) {
213            HNP_LOGE("zip add file[%{public}s] unsuccess ", fullPath);
214            closedir(dir);
215            return ret;
216        }
217    }
218    closedir(dir);
219
220    return 0;
221}
222
223static int ZipDir(const char *sourcePath, int offset, zipFile zf)
224{
225    int ret;
226    char transPath[MAX_FILE_PATH_LEN];
227
228    TransPath(sourcePath, transPath);
229
230    // 将外层文件夹信息保存到zip文件中
231    ret = zipOpenNewFileInZip3(zf, transPath + offset, NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_BEST_COMPRESSION,
232        0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0);
233    if (ret != ZIP_OK) {
234        HNP_LOGE("open new file[%{public}s] in zip unsuccess ", sourcePath + offset);
235        return HNP_ERRNO_BASE_CREATE_ZIP_FAILED;
236    }
237    zipCloseFileInZip(zf);
238    ret = ZipAddDir(sourcePath, offset, zf);
239
240    return ret;
241}
242
243int HnpZip(const char *inputDir, zipFile zf)
244{
245    int ret;
246    char *strPtr;
247    int offset;
248    char sourcePath[MAX_FILE_PATH_LEN];
249
250    // zip压缩文件内只保存相对路径,不保存绝对路径信息,偏移到压缩文件夹位置
251    strPtr = strrchr(inputDir, DIR_SPLIT_SYMBOL);
252    if (strPtr == NULL) {
253        offset = 0;
254    } else {
255        offset = strPtr - inputDir + 1;
256    }
257
258    // zip函数根据后缀是否'/'区分目录还是文件
259    ret = sprintf_s(sourcePath, MAX_FILE_PATH_LEN, "%s%c", inputDir, DIR_SPLIT_SYMBOL);
260    if (ret < 0) {
261        HNP_LOGE("sprintf unsuccess.");
262        return HNP_ERRNO_BASE_SPRINTF_FAILED;
263    }
264
265    ret = ZipDir(sourcePath, offset, zf);
266
267    return ret;
268}
269
270int HnpAddFileToZip(zipFile zf, char *filename, char *buff, int size)
271{
272    int ret;
273    char transPath[MAX_FILE_PATH_LEN];
274
275    TransPath(filename, transPath);
276
277    // 将外层文件夹信息保存到zip文件中
278    ret = zipOpenNewFileInZip3(zf, transPath, NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_BEST_COMPRESSION,
279        0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0);
280    if (ret != ZIP_OK) {
281        HNP_LOGE("open new file[%{public}s] in zip unsuccess ", filename);
282        return HNP_ERRNO_BASE_CREATE_ZIP_FAILED;
283    }
284    zipWriteInFileInZip(zf, buff, size);
285    zipCloseFileInZip(zf);
286
287    return 0;
288}
289
290static int HnpUnZipForFile(const char *filePath, unzFile zipFile, unz_file_info fileInfo)
291{
292#ifdef _WIN32
293    return 0;
294#else
295    int ret;
296    mode_t mode = (fileInfo.external_fa >> ZIP_EXTERNAL_FA_OFFSET) & 0xFFFF;
297
298    /* 如果解压缩的是目录 */
299    if (filePath[strlen(filePath) - 1] == '/') {
300        mkdir(filePath, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
301    } else {
302        FILE *outFile = fopen(filePath, "wb");
303        if (outFile == NULL) {
304            HNP_LOGE("unzip open file:%{public}s unsuccess!", filePath);
305            return HNP_ERRNO_BASE_FILE_OPEN_FAILED;
306        }
307        unzOpenCurrentFile(zipFile);
308        int readSize = 0;
309        do {
310            char buffer[BUFFER_SIZE];
311            readSize = unzReadCurrentFile(zipFile, buffer, sizeof(buffer));
312            if (readSize < 0) {
313                HNP_LOGE("unzip read zip:%{public}s file unsuccess", (char *)zipFile);
314                fclose(outFile);
315                unzCloseCurrentFile(zipFile);
316                return HNP_ERRNO_BASE_UNZIP_READ_FAILED;
317            }
318
319            fwrite(buffer, readSize, sizeof(char), outFile);
320        } while (readSize > 0);
321
322        fclose(outFile);
323        unzCloseCurrentFile(zipFile);
324        /* 如果其他人有可执行权限,那么将解压后的权限设置成755,否则为744 */
325        if ((mode & S_IXOTH) != 0) {
326            ret = chmod(filePath, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
327        } else {
328            ret = chmod(filePath, S_IRWXU | S_IRGRP | S_IROTH);
329        }
330        if (ret != 0) {
331            HNP_LOGE("hnp install chmod unsuccess, src:%{public}s, errno:%{public}d", filePath, errno);
332            return HNP_ERRNO_BASE_CHMOD_FAILED;
333        }
334    }
335    return 0;
336#endif
337}
338
339static bool HnpELFFileCheck(const char *path)
340{
341    FILE *fp;
342    char buff[HNP_ELF_FILE_CHECK_HEAD_LEN];
343
344    fp = fopen(path, "rb");
345    if (fp == NULL) {
346        return false;
347    }
348
349    size_t readLen = fread(buff, sizeof(char), HNP_ELF_FILE_CHECK_HEAD_LEN, fp);
350    if (readLen != HNP_ELF_FILE_CHECK_HEAD_LEN) {
351        (void)fclose(fp);
352        return false;
353    }
354
355    if (buff[HNP_INDEX_0] == 0x7F && buff[HNP_INDEX_1] == 'E' && buff[HNP_INDEX_2] == 'L' && buff[HNP_INDEX_3] == 'F') {
356        (void)fclose(fp);
357        return true;
358    }
359
360    (void)fclose(fp);
361    return false;
362}
363
364static int HnpInstallAddSignMap(const char* hnpSignKeyPrefix, const char *key, const char *value,
365    HnpSignMapInfo *hnpSignMapInfos, int *count)
366{
367    int ret;
368    int sum = *count;
369
370    if (HnpELFFileCheck(value) == false) {
371        return 0;
372    }
373
374    ret = sprintf_s(hnpSignMapInfos[sum].key, MAX_FILE_PATH_LEN, "%s!/%s", hnpSignKeyPrefix, key);
375    if (ret < 0) {
376        HNP_LOGE("add sign map sprintf unsuccess.");
377        return HNP_ERRNO_BASE_SPRINTF_FAILED;
378    }
379
380    ret = strcpy_s(hnpSignMapInfos[sum].value, MAX_FILE_PATH_LEN, value);
381    if (ret != EOK) {
382        HNP_LOGE("add sign map strcpy[%{public}s] unsuccess.", value);
383        return HNP_ERRNO_BASE_COPY_FAILED;
384    }
385
386    *count  = sum + 1;
387    return 0;
388}
389
390int HnpFileCountGet(const char *path, int *count)
391{
392    int sum = 0;
393
394    unzFile zipFile = unzOpen(path);
395    if (zipFile == NULL) {
396        HNP_LOGE("unzip open hnp:%{public}s unsuccess!", path);
397        return HNP_ERRNO_BASE_UNZIP_OPEN_FAILED;
398    }
399
400    int ret = unzGoToFirstFile(zipFile);
401    while (ret == UNZ_OK) {
402        sum++;
403        ret = unzGetCurrentFileInfo(zipFile, NULL, NULL, 0, NULL, 0, NULL, 0);
404        if (ret != UNZ_OK) {
405            HNP_LOGE("unzip get zip:%{public}s info unsuccess!", path);
406            unzClose(zipFile);
407            return HNP_ERRNO_BASE_UNZIP_GET_INFO_FAILED;
408        }
409
410        ret = unzGoToNextFile(zipFile);
411    }
412
413    unzClose(zipFile);
414    *count += sum;
415    return 0;
416}
417
418int HnpUnZip(const char *inputFile, const char *outputDir, const char *hnpSignKeyPrefix,
419    HnpSignMapInfo *hnpSignMapInfos, int *count)
420{
421    char fileName[MAX_FILE_PATH_LEN];
422    unz_file_info fileInfo;
423    char filePath[MAX_FILE_PATH_LEN];
424
425    HNP_LOGI("HnpUnZip zip=%{public}s, output=%{public}s", inputFile, outputDir);
426
427    unzFile zipFile = unzOpen(inputFile);
428    if (zipFile == NULL) {
429        HNP_LOGE("unzip open hnp:%{public}s unsuccess!", inputFile);
430        return HNP_ERRNO_BASE_UNZIP_OPEN_FAILED;
431    }
432
433    int result = unzGoToFirstFile(zipFile);
434    while (result == UNZ_OK) {
435        result = unzGetCurrentFileInfo(zipFile, &fileInfo, fileName, sizeof(fileName), NULL, 0, NULL, 0);
436        if (result != UNZ_OK) {
437            HNP_LOGE("unzip get zip:%{public}s info unsuccess!", inputFile);
438            unzClose(zipFile);
439            return HNP_ERRNO_BASE_UNZIP_GET_INFO_FAILED;
440        }
441        if (strstr(fileName, "../")) {
442            HNP_LOGE("unzip filename[%{public}s],does not allow the use of ../", fileName);
443            unzClose(zipFile);
444            return HNP_ERRNO_BASE_UNZIP_GET_INFO_FAILED;
445        }
446        char *slash = strchr(fileName, '/');
447        if (slash != NULL) {
448            slash++;
449        } else {
450            slash = fileName;
451        }
452
453        if (sprintf_s(filePath, MAX_FILE_PATH_LEN, "%s/%s", outputDir, slash) < 0) {
454            HNP_LOGE("sprintf unsuccess.");
455            unzClose(zipFile);
456            return HNP_ERRNO_BASE_SPRINTF_FAILED;
457        }
458
459        result = HnpUnZipForFile(filePath, zipFile, fileInfo);
460        if (result != 0) {
461            HNP_LOGE("unzip for file:%{public}s unsuccess", filePath);
462            unzClose(zipFile);
463            return result;
464        }
465        result = HnpInstallAddSignMap(hnpSignKeyPrefix, fileName, filePath, hnpSignMapInfos, count);
466        if (result != 0) {
467            unzClose(zipFile);
468            return result;
469        }
470        result = unzGoToNextFile(zipFile);
471    }
472
473    unzClose(zipFile);
474    return 0;
475}
476
477int HnpCfgGetFromZip(const char *inputFile, HnpCfgInfo *hnpCfg)
478{
479    char fileName[MAX_FILE_PATH_LEN];
480    unz_file_info fileInfo;
481    char *cfgStream = NULL;
482
483    unzFile zipFile = unzOpen(inputFile);
484    if (zipFile == NULL) {
485        HNP_LOGE("unzip open hnp:%{public}s unsuccess!", inputFile);
486        return HNP_ERRNO_BASE_UNZIP_OPEN_FAILED;
487    }
488
489    int ret = unzGoToFirstFile(zipFile);
490    while (ret == UNZ_OK) {
491        ret = unzGetCurrentFileInfo(zipFile, &fileInfo, fileName, sizeof(fileName), NULL, 0, NULL, 0);
492        if (ret != UNZ_OK) {
493            HNP_LOGE("unzip get zip:%{public}s info unsuccess!", inputFile);
494            unzClose(zipFile);
495            return HNP_ERRNO_BASE_UNZIP_GET_INFO_FAILED;
496        }
497        char *fileNameTmp = strrchr(fileName, DIR_SPLIT_SYMBOL);
498        if (fileNameTmp == NULL) {
499            fileNameTmp = fileName;
500        } else {
501            fileNameTmp++;
502        }
503        if (strcmp(fileNameTmp, HNP_CFG_FILE_NAME) != 0) {
504            ret = unzGoToNextFile(zipFile);
505            continue;
506        }
507
508        unzOpenCurrentFile(zipFile);
509        cfgStream = malloc(fileInfo.uncompressed_size);
510        if (cfgStream == NULL) {
511            HNP_LOGE("malloc unsuccess. size=%{public}lu, errno=%{public}d", fileInfo.uncompressed_size, errno);
512            unzClose(zipFile);
513            return HNP_ERRNO_NOMEM;
514        }
515        int readSize = unzReadCurrentFile(zipFile, cfgStream, fileInfo.uncompressed_size);
516        if (readSize < 0 || (uLong)readSize != fileInfo.uncompressed_size) {
517            free(cfgStream);
518            unzClose(zipFile);
519            HNP_LOGE("unzip read zip:%{public}s info size[%{public}lu]=>[%{public}d] error!", inputFile,
520                fileInfo.uncompressed_size, readSize);
521            return HNP_ERRNO_BASE_FILE_READ_FAILED;
522        }
523        break;
524    }
525    unzClose(zipFile);
526    ret = HnpCfgGetFromSteam(cfgStream, hnpCfg);
527    free(cfgStream);
528    return ret;
529}
530
531#ifdef __cplusplus
532}
533#endif
534