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 "hiview_zip_util.h"
17
18 #include "file_util.h"
19 #include "hiview_logger.h"
20
21 namespace OHOS {
22 namespace HiviewDFX {
23 namespace {
24 DEFINE_LOG_TAG("HiView-ZipUtil");
25 constexpr uLong MAX_ENTRY_NUM = 10000;
26 constexpr int MAX_UNZIP_SIZE = 300 * 1024 * 1024;
27 constexpr uint32_t BUFFER_SIZE = 100 * 1024;
28 constexpr size_t BLOCK_COUNT = 1;
29 constexpr int32_t ERROR_GET_HANDLE = 1001;
30 constexpr int32_t ERROR_INVALID_FILE = 1003;
31 constexpr int32_t ERROR_OPEN_NEW_FILE = 1004;
32 constexpr int32_t ERROR_CREATE_ZIP = 1005;
33 }
34
HiviewZipUnit(const std::string & zipPath,int32_t zipMode)35 HiviewZipUnit::HiviewZipUnit(const std::string& zipPath, int32_t zipMode)
36 {
37 zipFile_ = zipOpen(zipPath.c_str(), zipMode);
38 }
39
~HiviewZipUnit()40 HiviewZipUnit::~HiviewZipUnit()
41 {
42 if (zipFile_ != nullptr) {
43 zipClose(zipFile_, nullptr);
44 zipFile_ = nullptr;
45 }
46 }
47
AddFileInZip(const std::string & srcFile,ZipFileLevel zipFileLevel)48 int32_t HiviewZipUnit::AddFileInZip(const std::string& srcFile, ZipFileLevel zipFileLevel)
49 {
50 if (zipFile_ == nullptr) {
51 return ERROR_CREATE_ZIP;
52 }
53
54 std::string realPath;
55 FILE* srcFp = GetFileHandle(srcFile, realPath);
56 if (srcFp == nullptr) {
57 HIVIEW_LOGE("get file handle failed: %{public}s, errno: %{public}d", srcFile.c_str(), errno);
58 return ERROR_GET_HANDLE;
59 }
60
61 std::string srcFileName(GetDstFilePath(realPath, zipFileLevel));
62 if (srcFileName.empty()) {
63 HIVIEW_LOGW("get target path failed.");
64 (void)fclose(srcFp);
65 return ERROR_INVALID_FILE;
66 }
67 if (zipOpenNewFileInZip(zipFile_, srcFileName.c_str(),
68 nullptr, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION) != ZIP_OK) {
69 HIVIEW_LOGW("open new file in zip failed.");
70 (void)fclose(srcFp);
71 return ERROR_OPEN_NEW_FILE;
72 }
73
74 int32_t errCode = 0;
75 char buf[BUFFER_SIZE] = { 0 };
76 while (!feof(srcFp)) {
77 size_t numBytes = fread(buf, 1, sizeof(buf), srcFp);
78 if (numBytes == 0) {
79 HIVIEW_LOGI("read an empty file.");
80 }
81 if (ferror(srcFp)) {
82 HIVIEW_LOGE("zip file failed, file: %{public}s, errno: %{public}d", srcFile.c_str(), errno);
83 errCode = errno;
84 break;
85 }
86 zipWriteInFileInZip(zipFile_, buf, static_cast<unsigned int>(numBytes));
87 }
88 (void)fclose(srcFp);
89 zipCloseFileInZip(zipFile_);
90 return errCode;
91 }
92
GetFileHandle(const std::string & file,std::string & realPath)93 FILE* HiviewZipUnit::GetFileHandle(const std::string& file, std::string& realPath)
94 {
95 if (!FileUtil::PathToRealPath(file, realPath)) {
96 return nullptr;
97 }
98 return fopen(realPath.c_str(), "rb");
99 }
100
GetDstFilePath(const std::string & srcFile,ZipFileLevel zipFileLevel)101 std::string HiviewZipUnit::GetDstFilePath(const std::string& srcFile, ZipFileLevel zipFileLevel)
102 {
103 if (zipFileLevel == ZipFileLevel::KEEP_NONE_PARENT_PATH) {
104 return FileUtil::ExtractFileName(srcFile);
105 }
106
107 std::string::size_type lastSlashPos = srcFile.rfind("/");
108 if (lastSlashPos == std::string::npos) {
109 return "";
110 }
111 std::string::size_type secondSlashPos = srcFile.substr(0, lastSlashPos).rfind("/");
112 if (secondSlashPos == std::string::npos) {
113 return "";
114 }
115 return srcFile.substr(secondSlashPos + 1);
116 }
117
HiviewUnzipUnit(const std::string & zipPath,const std::string & dstDir)118 HiviewUnzipUnit::HiviewUnzipUnit(const std::string& zipPath, const std::string& dstDir) : dstDir_(dstDir)
119 {
120 zipFile_ = unzOpen(zipPath.c_str());
121 }
122
~HiviewUnzipUnit()123 HiviewUnzipUnit::~HiviewUnzipUnit()
124 {
125 if (zipFile_ != nullptr) {
126 unzClose(zipFile_);
127 zipFile_ = nullptr;
128 }
129 }
130
UnzipFile() const131 bool HiviewUnzipUnit::UnzipFile() const
132 {
133 if (zipFile_ == nullptr) {
134 return false;
135 }
136 // Get info about the zip file
137 unz_global_info globalInfo;
138 if (unzGetGlobalInfo(zipFile_, &globalInfo) != UNZ_OK || globalInfo.number_entry > MAX_ENTRY_NUM) {
139 HIVIEW_LOGW("entry num exceeds: %{public}lu", globalInfo.number_entry);
140 return false;
141 }
142
143 int totalUnzipSize = 0;
144 for (uLong index = 0; index < globalInfo.number_entry; ++index) {
145 // Get info about current file
146 unz_file_info fileInfo;
147 char fileName[NAME_MAX] = { 0 };
148 if (unzGetCurrentFileInfo(zipFile_, &fileInfo, fileName, sizeof(fileName), NULL, 0, NULL, 0) != UNZ_OK
149 || fileInfo.size_filename >= NAME_MAX) {
150 HIVIEW_LOGE("file numbers exceeded: %{public}d", static_cast<int>(fileInfo.size_filename));
151 return false;
152 }
153 std::string path(dstDir_ + fileName);
154 if (path.back() == '/') {
155 // entry is a directory, so create it
156 if (!FileUtil::ForceCreateDirectory(path)) {
157 HIVIEW_LOGW("create dir failed.");
158 return false;
159 }
160 } else {
161 std::string filePath(FileUtil::ExtractFilePath(path));
162 if (!FileUtil::ForceCreateDirectory(filePath)) {
163 HIVIEW_LOGW("create dir for file failed.");
164 return false;
165 }
166 int fileSize = 0;
167 if (!DoUnzipFile(path, fileSize)) {
168 HIVIEW_LOGW("unzip file failed.");
169 return false;
170 }
171 totalUnzipSize += fileSize;
172 if (totalUnzipSize > MAX_UNZIP_SIZE) {
173 HIVIEW_LOGW("unzip file size exceeded.");
174 return false;
175 }
176 }
177 // go to the next entry listed in the zip file
178 if ((index + 1) < globalInfo.number_entry && unzGoToNextFile(zipFile_) != UNZ_OK) {
179 return false;
180 }
181 }
182 return true;
183 }
184
DoUnzipFile(const std::string & path,int & fileSize) const185 bool HiviewUnzipUnit::DoUnzipFile(const std::string& path, int& fileSize) const
186 {
187 if (unzOpenCurrentFile(zipFile_) != UNZ_OK) {
188 HIVIEW_LOGE("unzip current file failed.");
189 return false;
190 }
191
192 FILE* outFile = fopen(path.c_str(), "wb");
193 if (outFile == nullptr) {
194 HIVIEW_LOGW("create zip sub file failed.");
195 unzCloseCurrentFile(zipFile_);
196 return false;
197 }
198
199 int readSize = 0;
200 bool isSuccess = true;
201 char readBuffer[BUFFER_SIZE] = { 0 };
202 do {
203 readSize = unzReadCurrentFile(zipFile_, readBuffer, sizeof(readBuffer));
204 if (readSize < 0) {
205 HIVIEW_LOGE("read file from zip failed.");
206 isSuccess = false;
207 break;
208 }
209 if (readSize > 0) {
210 if (fwrite(readBuffer, readSize, BLOCK_COUNT, outFile) != BLOCK_COUNT) {
211 HIVIEW_LOGE("write date to file failed.");
212 isSuccess = false;
213 break;
214 }
215 fileSize += readSize;
216 if (fileSize > MAX_UNZIP_SIZE) {
217 HIVIEW_LOGE("unzip readSize exceeded.");
218 isSuccess = false;
219 break;
220 }
221 }
222 } while (readSize > 0);
223 if (fclose(outFile) != 0) {
224 HIVIEW_LOGE("close file failed.");
225 isSuccess = false;
226 }
227 unzCloseCurrentFile(zipFile_);
228 return isSuccess;
229 }
230 } // HiviewDFX
231 } // OHOS