1/*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
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 "file.h"
17#include <cerrno>
18#include <fcntl.h>
19#include <fstream>
20#include <iostream>
21#include <sys/stat.h>
22#include <unistd.h>
23
24#include "log.h"
25#if defined(_WIN32)
26#include <direct.h>
27#include <io.h>
28#include <windows.h>
29#endif
30#if defined(is_linux) || defined(_WIN32)
31#include <filesystem>
32#endif
33#include "zlib.h"
34#include "contrib/minizip/zip.h"
35#include "contrib/minizip/unzip.h"
36
37namespace SysTuning {
38namespace base {
39static TraceParserStatus g_status = TRACE_PARSER_ABNORMAL;
40
41void SetAnalysisResult(TraceParserStatus stat)
42{
43    g_status = stat;
44}
45TraceParserStatus GetAnalysisResult()
46{
47    return g_status;
48}
49
50ssize_t Read(int32_t fd, uint8_t *dst, size_t dstSize)
51{
52#if defined(_WIN32)
53    return _read(fd, dst, static_cast<unsigned>(dstSize));
54#else
55    ssize_t ret = -1;
56    do {
57        ret = read(fd, dst, dstSize);
58    } while (ret == -1 && errno == EINTR);
59    return ret;
60#endif
61}
62int32_t OpenFile(const std::string &path, int32_t flags, uint32_t mode)
63{
64    TS_ASSERT((flags & O_CREAT) == 0 || mode != K_FILE_MODE_INVALID);
65#if defined(_WIN32)
66    int32_t fd(_open(path.c_str(), flags | O_BINARY, mode));
67#else
68    int32_t fd(open(path.c_str(), flags | O_CLOEXEC, mode));
69#endif
70    return fd;
71}
72
73std::string GetExecutionDirectoryPath()
74{
75    char currPath[1024] = {0};
76#if defined(_WIN32)
77    ::GetModuleFileNameA(NULL, currPath, MAX_PATH);
78    (strrchr(currPath, '\\'))[1] = 0;
79#else
80    (void)readlink("/proc/self/exe", currPath, sizeof(currPath) - 1);
81#endif
82    std::string str(currPath);
83    return str.substr(0, str.find_last_of('/'));
84}
85#if defined(is_linux) || defined(_WIN32)
86std::vector<std::string> GetFilesNameFromDir(const std::string &path, bool onlyFileName)
87{
88    std::vector<std::string> soFiles;
89
90    std::filesystem::path dirPath(path);
91    // 检查文件是否存在
92    if (!std::filesystem::exists(dirPath)) {
93        TS_LOGI("!std::filesystem::exists(dirPath), dirPath: %s\n", path.data());
94        return soFiles;
95    }
96    // 遍历目录
97    for (const auto &entry : std::filesystem::recursive_directory_iterator(dirPath)) {
98        if (entry.is_directory()) {
99            continue;
100        }
101        soFiles.emplace_back(onlyFileName ? entry.path().filename().string() : entry.path().string());
102    }
103    return soFiles;
104}
105#endif
106
107bool UnZipFile(const std::string &zipFile, std::string &traceFile)
108{
109    std::filesystem::path stdZipFile(zipFile);
110    // 检查文件是否存在
111    if (!std::filesystem::exists(stdZipFile)) {
112        TS_LOGI("!std::filesystem::exists(dirPath), dirPath: %s\n", zipFile.data());
113        return false;
114    }
115    std::ifstream zipIfstream(stdZipFile);
116    char buf[2];
117    zipIfstream.read(buf, 2);
118    if (buf[0] != 'P' || buf[1] != 'K') {
119        // 不是zip文件, 返回true走正常解析流程
120        return true;
121    } else {
122        auto unzipDirPath = stdZipFile.parent_path().append("tmp").string();
123        LocalUnzip(zipFile, unzipDirPath);
124        auto files = GetFilesNameFromDir(unzipDirPath, false);
125        if (files.size() == 1) {
126            traceFile = files[0];
127            return true;
128        }
129        return false;
130    }
131}
132
133bool LocalUnzip(const std::string &zipFile, const std::string &dstDir)
134{
135    int err = 0;
136    void *buf;
137    uInt size_buf;
138    // 创建 dstDir
139    if (std::filesystem::exists(dstDir)) {
140        std::filesystem::remove_all(dstDir);
141        std::filesystem::create_directories(dstDir);
142    }
143
144    // 打开zip文件
145    unzFile uf = unzOpen(zipFile.c_str());
146    if (uf == NULL) {
147        std::cout << "unzOpen error." << std::endl;
148        return false;
149    }
150
151    // 获取zip文件信息
152    unz_global_info gi;
153    err = unzGetGlobalInfo(uf, &gi);
154    if (err != UNZ_OK) {
155        std::cout << "unzGetGlobalInfo error." << std::endl;
156        return false;
157    }
158
159    size_buf = 512000000;
160    buf = (void *)malloc(size_buf);
161
162    // 遍历zip
163    for (int i = 0; i < gi.number_entry; i++) {
164        char filename_inzip[256];
165        unz_file_info file_info;
166        uLong ratio = 0;
167        const char *string_method = "";
168        char charCrypt = ' ';
169        err = unzGetCurrentFileInfo(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
170        if (err != UNZ_OK) {
171            std::cout << "error " << err << " with zipfile in unzGetCurrentFileInfo." << std::endl;
172            break;
173        }
174        std::string isdir;
175        std::string filename = filename_inzip;
176        if (filename.back() == '/' || filename.back() == '\\') {
177            //* 是目录,则创建目录
178            isdir = " is directory";
179            std::string dir = dstDir + "/" + filename;
180            if (!std::filesystem::exists(dir)) {
181                std::filesystem::create_directories(dir);
182            }
183        } else {
184            //* 是文件,打开 -> 读取 -> 写入解压文件 -> 关闭
185            isdir = " is file";
186
187            err = unzOpenCurrentFile(uf);
188            if (err != UNZ_OK) {
189                std::cout << "error " << err << " with zipfile in unzOpenCurrentFile." << std::endl;
190            } else {
191                std::string writefile = dstDir + "/" + filename;
192                std::filesystem::path fpath(writefile);
193                std::string par = fpath.parent_path().string();
194                if (!fpath.parent_path().empty() && !std::filesystem::exists(fpath.parent_path())) {
195                    std::filesystem::create_directories(fpath.parent_path());
196                }
197                err = unzReadCurrentFile(uf, buf, size_buf);
198                if (err < 0) {
199                    std::cout << "error " << err << " with zipfile in unzReadCurrentFile." << std::endl;
200                    break;
201                }
202                if (err > 0) {
203                    FILE *fout = fopen(writefile.c_str(), "wb");
204                    if (fwrite(buf, (unsigned)err, 1, fout) != 1) {
205                        std::cout << "error in writing extracted filee." << std::endl;
206                        err = UNZ_ERRNO;
207                        break;
208                    }
209                    fclose(fout);
210                }
211                unzCloseCurrentFile(uf);
212            }
213        }
214
215        if ((i + 1) < gi.number_entry) {
216            err = unzGoToNextFile(uf);
217            if (err != UNZ_OK) {
218                std::cout << "error " << err << " with zipfile in unzGoToNextFile." << std::endl;
219                break;
220            }
221        }
222    }
223
224    unzClose(uf);
225
226    return true;
227}
228} // namespace base
229} // namespace SysTuning
230