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