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
38 extern "C" {
39 #endif
40 
41 #define ZIP_EXTERNAL_FA_OFFSET 16
42 
43 // zipOpenNewFileInZip3只识别带‘/’的路径,需要将路径中‘\’转换成‘/’
TransPath(const char * input,char * output)44 static 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
TransWidePath(const char * inPath,wchar_t * outPath)59 static 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压缩包中添加文件
ZipAddFile(const char * file,int offset,zipFile zf)72 static 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 // 判断是否为目录
IsDirPath(struct dirent * entry,char * fullPath,int * isDir)127 static 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 
149 static int ZipAddDir(const char *sourcePath, int offset, zipFile zf);
150 
ZipHandleDir(char * fullPath,int offset,zipFile zf)151 static 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--压缩文件句柄
ZipAddDir(const char * sourcePath,int offset,zipFile zf)172 static 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 
ZipDir(const char * sourcePath,int offset,const char * zipPath)223 static int ZipDir(const char *sourcePath, int offset, const char *zipPath)
224 {
225     int ret;
226     char transPath[MAX_FILE_PATH_LEN];
227 
228     zipFile zf = zipOpen(zipPath, APPEND_STATUS_CREATE);
229     if (zf == NULL) {
230         HNP_LOGE("open zip=%{public}s unsuccess ", zipPath);
231         return HNP_ERRNO_BASE_CREATE_ZIP_FAILED;
232     }
233 
234     TransPath(sourcePath, transPath);
235 
236     // 将外层文件夹信息保存到zip文件中
237     ret = zipOpenNewFileInZip3(zf, transPath + offset, NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_BEST_COMPRESSION,
238         0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0);
239     if (ret != ZIP_OK) {
240         HNP_LOGE("open new file[%{public}s] in zip unsuccess ", sourcePath + offset);
241         zipClose(zf, NULL);
242         return HNP_ERRNO_BASE_CREATE_ZIP_FAILED;
243     }
244     zipCloseFileInZip(zf);
245     ret = ZipAddDir(sourcePath, offset, zf);
246 
247     zipClose(zf, NULL);
248 
249     return ret;
250 }
251 
HnpZip(const char * inputDir,const char * outputFile)252 int HnpZip(const char *inputDir, const char *outputFile)
253 {
254     int ret;
255     char *strPtr;
256     int offset;
257     char sourcePath[MAX_FILE_PATH_LEN];
258 
259     HNP_LOGI("HnpZip dir=%{public}s, output=%{public}s ", inputDir, outputFile);
260 
261     // zip压缩文件内只保存相对路径,不保存绝对路径信息,偏移到压缩文件夹位置
262     strPtr = strrchr(inputDir, DIR_SPLIT_SYMBOL);
263     if (strPtr == NULL) {
264         offset = 0;
265     } else {
266         offset = strPtr - inputDir + 1;
267     }
268 
269     // zip函数根据后缀是否'/'区分目录还是文件
270     ret = sprintf_s(sourcePath, MAX_FILE_PATH_LEN, "%s%c", inputDir, DIR_SPLIT_SYMBOL);
271     if (ret < 0) {
272         HNP_LOGE("sprintf unsuccess.");
273         return HNP_ERRNO_BASE_SPRINTF_FAILED;
274     }
275 
276     ret = ZipDir(sourcePath, offset, outputFile);
277 
278     return ret;
279 }
280 
HnpAddFileToZip(char * zipfile,char * filename,char * buff,int size)281 int HnpAddFileToZip(char *zipfile, char *filename, char *buff, int size)
282 {
283     zipFile zf;
284     int ret;
285     char transPath[MAX_FILE_PATH_LEN];
286 
287     zf = zipOpen(zipfile, APPEND_STATUS_ADDINZIP);
288     if (zf == NULL) {
289         HNP_LOGE("open zip=%{public}s unsuccess ", zipfile);
290         return HNP_ERRNO_BASE_CREATE_ZIP_FAILED;
291     }
292 
293     TransPath(filename, transPath);
294 
295     // 将外层文件夹信息保存到zip文件中
296     ret = zipOpenNewFileInZip3(zf, transPath, NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_BEST_COMPRESSION,
297         0, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, NULL, 0);
298     if (ret != ZIP_OK) {
299         HNP_LOGE("open new file[%{public}s] in zip unsuccess ", filename);
300         zipClose(zf, NULL);
301         return HNP_ERRNO_BASE_CREATE_ZIP_FAILED;
302     }
303     zipWriteInFileInZip(zf, buff, size);
304     zipCloseFileInZip(zf);
305     zipClose(zf, NULL);
306 
307     return 0;
308 }
309 
HnpUnZipForFile(const char * filePath,unzFile zipFile,unz_file_info fileInfo)310 static int HnpUnZipForFile(const char *filePath, unzFile zipFile, unz_file_info fileInfo)
311 {
312 #ifdef _WIN32
313     return 0;
314 #else
315     int ret;
316     mode_t mode = (fileInfo.external_fa >> ZIP_EXTERNAL_FA_OFFSET) & 0xFFFF;
317 
318     /* 如果解压缩的是目录 */
319     if (filePath[strlen(filePath) - 1] == '/') {
320         mkdir(filePath, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
321     } else {
322         FILE *outFile = fopen(filePath, "wb");
323         if (outFile == NULL) {
324             HNP_LOGE("unzip open file:%{public}s unsuccess!", filePath);
325             return HNP_ERRNO_BASE_FILE_OPEN_FAILED;
326         }
327         unzOpenCurrentFile(zipFile);
328         int readSize = 0;
329         do {
330             char buffer[BUFFER_SIZE];
331             readSize = unzReadCurrentFile(zipFile, buffer, sizeof(buffer));
332             if (readSize < 0) {
333                 HNP_LOGE("unzip read zip:%{public}s file unsuccess", (char *)zipFile);
334                 fclose(outFile);
335                 unzCloseCurrentFile(zipFile);
336                 return HNP_ERRNO_BASE_UNZIP_READ_FAILED;
337             }
338 
339             fwrite(buffer, readSize, sizeof(char), outFile);
340         } while (readSize > 0);
341 
342         fclose(outFile);
343         unzCloseCurrentFile(zipFile);
344         /* 如果其他人有可执行权限,那么将解压后的权限设置成755,否则为744 */
345         if ((mode & S_IXOTH) != 0) {
346             ret = chmod(filePath, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
347         } else {
348             ret = chmod(filePath, S_IRWXU | S_IRGRP | S_IROTH);
349         }
350         if (ret != 0) {
351             HNP_LOGE("hnp install chmod unsuccess, src:%{public}s, errno:%{public}d", filePath, errno);
352             return HNP_ERRNO_BASE_CHMOD_FAILED;
353         }
354     }
355     return 0;
356 #endif
357 }
358 
HnpELFFileCheck(const char * path)359 static bool HnpELFFileCheck(const char *path)
360 {
361     FILE *fp;
362     char buff[HNP_ELF_FILE_CHECK_HEAD_LEN];
363 
364     fp = fopen(path, "rb");
365     if (fp == NULL) {
366         return false;
367     }
368 
369     size_t readLen = fread(buff, sizeof(char), HNP_ELF_FILE_CHECK_HEAD_LEN, fp);
370     if (readLen != HNP_ELF_FILE_CHECK_HEAD_LEN) {
371         (void)fclose(fp);
372         return false;
373     }
374 
375     if (buff[HNP_INDEX_0] == 0x7F && buff[HNP_INDEX_1] == 'E' && buff[HNP_INDEX_2] == 'L' && buff[HNP_INDEX_3] == 'F') {
376         (void)fclose(fp);
377         return true;
378     }
379 
380     (void)fclose(fp);
381     return false;
382 }
383 
HnpInstallAddSignMap(const char * hnpSignKeyPrefix,const char * key,const char * value,HnpSignMapInfo * hnpSignMapInfos,int * count)384 static int HnpInstallAddSignMap(const char* hnpSignKeyPrefix, const char *key, const char *value,
385     HnpSignMapInfo *hnpSignMapInfos, int *count)
386 {
387     int ret;
388     int sum = *count;
389 
390     if (HnpELFFileCheck(value) == false) {
391         return 0;
392     }
393 
394     ret = sprintf_s(hnpSignMapInfos[sum].key, MAX_FILE_PATH_LEN, "%s!/%s", hnpSignKeyPrefix, key);
395     if (ret < 0) {
396         HNP_LOGE("add sign map sprintf unsuccess.");
397         return HNP_ERRNO_BASE_SPRINTF_FAILED;
398     }
399 
400     ret = strcpy_s(hnpSignMapInfos[sum].value, MAX_FILE_PATH_LEN, value);
401     if (ret != EOK) {
402         HNP_LOGE("add sign map strcpy[%{public}s] unsuccess.", value);
403         return HNP_ERRNO_BASE_COPY_FAILED;
404     }
405 
406     *count  = sum + 1;
407     return 0;
408 }
409 
HnpFileCountGet(const char * path,int * count)410 int HnpFileCountGet(const char *path, int *count)
411 {
412     int sum = 0;
413 
414     unzFile zipFile = unzOpen(path);
415     if (zipFile == NULL) {
416         HNP_LOGE("unzip open hnp:%{public}s unsuccess!", path);
417         return HNP_ERRNO_BASE_UNZIP_OPEN_FAILED;
418     }
419 
420     int ret = unzGoToFirstFile(zipFile);
421     while (ret == UNZ_OK) {
422         if (sum == INT_MAX) {
423             unzClose(zipFile);
424             return HNP_ERRNO_BASE_FILE_COUNT_OVER;
425         }
426         sum++;
427         ret = unzGetCurrentFileInfo(zipFile, NULL, NULL, 0, NULL, 0, NULL, 0);
428         if (ret != UNZ_OK) {
429             HNP_LOGE("unzip get zip:%{public}s info unsuccess!", path);
430             unzClose(zipFile);
431             return HNP_ERRNO_BASE_UNZIP_GET_INFO_FAILED;
432         }
433 
434         ret = unzGoToNextFile(zipFile);
435     }
436 
437     unzClose(zipFile);
438     if (INT_MAX - sum < *count) {
439         return HNP_ERRNO_BASE_FILE_COUNT_OVER;
440     }
441     *count += sum;
442     return 0;
443 }
444 
HnpUnZip(const char * inputFile,const char * outputDir,const char * hnpSignKeyPrefix,HnpSignMapInfo * hnpSignMapInfos,int * count)445 int HnpUnZip(const char *inputFile, const char *outputDir, const char *hnpSignKeyPrefix,
446     HnpSignMapInfo *hnpSignMapInfos, int *count)
447 {
448     char fileName[MAX_FILE_PATH_LEN];
449     unz_file_info fileInfo;
450     char filePath[MAX_FILE_PATH_LEN];
451 
452     HNP_LOGI("HnpUnZip zip=%{public}s, output=%{public}s", inputFile, outputDir);
453 
454     unzFile zipFile = unzOpen(inputFile);
455     if (zipFile == NULL) {
456         HNP_LOGE("unzip open hnp:%{public}s unsuccess!", inputFile);
457         return HNP_ERRNO_BASE_UNZIP_OPEN_FAILED;
458     }
459 
460     int result = unzGoToFirstFile(zipFile);
461     while (result == UNZ_OK) {
462         result = unzGetCurrentFileInfo(zipFile, &fileInfo, fileName, sizeof(fileName), NULL, 0, NULL, 0);
463         if (result != UNZ_OK) {
464             HNP_LOGE("unzip get zip:%{public}s info unsuccess!", inputFile);
465             unzClose(zipFile);
466             return HNP_ERRNO_BASE_UNZIP_GET_INFO_FAILED;
467         }
468         if (strstr(fileName, "../")) {
469             unzClose(zipFile);
470             return HNP_ERRNO_BASE_UNZIP_GET_INFO_FAILED;
471         }
472         char *slash = strchr(fileName, '/');
473         if (slash != NULL) {
474             slash++;
475         } else {
476             slash = fileName;
477         }
478 
479         result = sprintf_s(filePath, MAX_FILE_PATH_LEN, "%s/%s", outputDir, slash);
480         if (result < 0) {
481             HNP_LOGE("sprintf unsuccess.");
482             unzClose(zipFile);
483             return HNP_ERRNO_BASE_SPRINTF_FAILED;
484         }
485 
486         result = HnpUnZipForFile(filePath, zipFile, fileInfo);
487         if (result != 0) {
488             HNP_LOGE("unzip for file:%{public}s unsuccess", filePath);
489             unzClose(zipFile);
490             return result;
491         }
492         result = HnpInstallAddSignMap(hnpSignKeyPrefix, fileName, filePath, hnpSignMapInfos, count);
493         if (result != 0) {
494             unzClose(zipFile);
495             return result;
496         }
497         result = unzGoToNextFile(zipFile);
498     }
499 
500     unzClose(zipFile);
501     return 0;
502 }
503 
HnpCfgGetFromZip(const char * inputFile,HnpCfgInfo * hnpCfg)504 int HnpCfgGetFromZip(const char *inputFile, HnpCfgInfo *hnpCfg)
505 {
506     char fileName[MAX_FILE_PATH_LEN];
507     unz_file_info fileInfo;
508     char *cfgStream = NULL;
509 
510     unzFile zipFile = unzOpen(inputFile);
511     if (zipFile == NULL) {
512         HNP_LOGE("unzip open hnp:%{public}s unsuccess!", inputFile);
513         return HNP_ERRNO_BASE_UNZIP_OPEN_FAILED;
514     }
515 
516     int ret = unzGoToFirstFile(zipFile);
517     while (ret == UNZ_OK) {
518         ret = unzGetCurrentFileInfo(zipFile, &fileInfo, fileName, sizeof(fileName), NULL, 0, NULL, 0);
519         if (ret != UNZ_OK) {
520             HNP_LOGE("unzip get zip:%{public}s info unsuccess!", inputFile);
521             unzClose(zipFile);
522             return HNP_ERRNO_BASE_UNZIP_GET_INFO_FAILED;
523         }
524         char *fileNameTmp = strrchr(fileName, DIR_SPLIT_SYMBOL);
525         if (fileNameTmp == NULL) {
526             fileNameTmp = fileName;
527         } else {
528             fileNameTmp++;
529         }
530         if (strcmp(fileNameTmp, HNP_CFG_FILE_NAME) != 0) {
531             ret = unzGoToNextFile(zipFile);
532             continue;
533         }
534 
535         unzOpenCurrentFile(zipFile);
536         cfgStream = malloc(fileInfo.uncompressed_size);
537         if (cfgStream == NULL) {
538             HNP_LOGE("malloc unsuccess. size=%{public}lu, errno=%{public}d", fileInfo.uncompressed_size, errno);
539             unzClose(zipFile);
540             return HNP_ERRNO_NOMEM;
541         }
542         int readSize = unzReadCurrentFile(zipFile, cfgStream, fileInfo.uncompressed_size);
543         if (readSize < 0 || (uLong)readSize != fileInfo.uncompressed_size) {
544             free(cfgStream);
545             unzClose(zipFile);
546             HNP_LOGE("unzip read zip:%{public}s info size[%{public}lu]=>[%{public}d] error!", inputFile,
547                 fileInfo.uncompressed_size, readSize);
548             return HNP_ERRNO_BASE_FILE_READ_FAILED;
549         }
550         break;
551     }
552     unzClose(zipFile);
553     ret = HnpCfgGetFromSteam(cfgStream, hnpCfg);
554     free(cfgStream);
555     return ret;
556 }
557 
558 #ifdef __cplusplus
559 }
560 #endif
561