1
2 /*
3 * Copyright (c) 2022 Huawei Device Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "zip_writer.h"
17
18 #include <cerrno>
19 #include <stdio.h>
20
21 #include "app_log_wrapper.h"
22 #include "contrib/minizip/zip.h"
23 #include "directory_ex.h"
24 #include "zip_internal.h"
25
26 using namespace OHOS::AppExecFwk;
27
28 namespace OHOS {
29 namespace AppExecFwk {
30 namespace LIBZIP {
31 namespace {
32 // Numbers of pending entries that trigger writting them to the ZIP file.
33 constexpr size_t g_MaxPendingEntriesCount = 50;
34 const std::string SEPARATOR = "/";
35 std::mutex g_mutex;;
36
AddFileContentToZip(zipFile zip_file,FilePath & file_path)37 bool AddFileContentToZip(zipFile zip_file, FilePath &file_path)
38 {
39 APP_LOGD("%{public}s called", __func__);
40 char buf[kZipBufSize];
41 if (!FilePathCheckValid(file_path.Value())) {
42 APP_LOGI(
43 "%{public}s called, filePath is invalid!!! file_path=%{public}s", __func__, file_path.Value().c_str());
44 return false;
45 }
46 if (!FilePath::PathIsValid(file_path)) {
47 APP_LOGI("!!! %{public}s called PathIsValid returns false !!!", __func__);
48 return false;
49 }
50
51 FILE *fp = fopen(file_path.Value().c_str(), "rb");
52 if (fp == nullptr) {
53 APP_LOGI("%{public}s called, filePath to realPath failed! filePath:%{private}s,errno:%{public}s",
54 __func__,
55 file_path.Value().c_str(), strerror(errno));
56 return false;
57 }
58
59 uint32_t num_bytes;
60 while (!feof(fp)) {
61 num_bytes = fread(buf, 1, kZipBufSize, fp);
62 if (num_bytes > 0) {
63 if (zipWriteInFileInZip(zip_file, buf, num_bytes) != ZIP_OK) {
64 APP_LOGI("%{public}s called, Could not write data to zip for path:%{private}s ",
65 __func__, file_path.Value().c_str());
66 fclose(fp);
67 fp = nullptr;
68 return false;
69 }
70 } else {
71 break;
72 }
73 }
74 fclose(fp);
75 fp = nullptr;
76 return true;
77 }
78
OpenNewFileEntry(zipFile zip_file,FilePath & path,bool isDirectory,struct tm * lastModified,const OPTIONS & options)79 bool OpenNewFileEntry(
80 zipFile zip_file, FilePath &path, bool isDirectory, struct tm *lastModified, const OPTIONS &options)
81 {
82 APP_LOGD("%{public}s called", __func__);
83 std::string strPath = path.Value();
84
85 if (isDirectory) {
86 strPath += SEPARATOR;
87 }
88
89 return ZipOpenNewFileInZip(zip_file, strPath, options, lastModified);
90 }
91
CloseNewFileEntry(zipFile zip_file)92 bool CloseNewFileEntry(zipFile zip_file)
93 {
94 APP_LOGD("%{public}s called", __func__);
95 return zipCloseFileInZip(zip_file) == ZIP_OK;
96 }
97
AddFileEntryToZip(zipFile zip_file,FilePath & relativePath,FilePath & absolutePath,const OPTIONS & options)98 bool AddFileEntryToZip(zipFile zip_file, FilePath &relativePath, FilePath &absolutePath, const OPTIONS &options)
99 {
100 APP_LOGD("%{public}s called", __func__);
101
102 struct tm *lastModified = GetCurrentSystemTime();
103 if (lastModified == nullptr) {
104 return false;
105 }
106 if (!OpenNewFileEntry(zip_file, relativePath, false, lastModified, options)) {
107 return false;
108 }
109 bool success = AddFileContentToZip(zip_file, absolutePath);
110 if (!CloseNewFileEntry(zip_file)) {
111 APP_LOGI("!!! CloseNewFileEntry returnValule is false !!!");
112 return false;
113 }
114 return success;
115 }
116
AddDirectoryEntryToZip(zipFile zip_file,FilePath & path,struct tm * lastModified,const OPTIONS & options)117 bool AddDirectoryEntryToZip(zipFile zip_file, FilePath &path, struct tm *lastModified, const OPTIONS &options)
118 {
119 APP_LOGI("%{public}s called", __func__);
120 return OpenNewFileEntry(zip_file, path, true, lastModified, options) && CloseNewFileEntry(zip_file);
121 }
122
123 } // namespace
124
InitZipFileWithFd(PlatformFile zipFilefd)125 zipFile ZipWriter::InitZipFileWithFd(PlatformFile zipFilefd)
126 {
127 std::lock_guard<std::mutex> lock(g_mutex);
128 if (zipFilefd == kInvalidPlatformFile) {
129 return nullptr;
130 }
131
132 zipFile zip_file = OpenFdForZipping(zipFilefd, APPEND_STATUS_CREATE);
133 if (!zip_file) {
134 APP_LOGI("%{public}s called, Couldn't create ZIP file for FD", __func__);
135 return nullptr;
136 }
137 return zip_file;
138 }
139
InitZipFileWithFile(const FilePath & zip_file_path)140 zipFile ZipWriter::InitZipFileWithFile(const FilePath &zip_file_path)
141 {
142 std::lock_guard<std::mutex> lock(g_mutex);
143 FilePath zipFilePath = zip_file_path;
144 APP_LOGD("%{public}s called", __func__);
145 if (zipFilePath.Value().empty()) {
146 APP_LOGI("%{public}s called, Path is empty", __func__);
147 return nullptr;
148 }
149
150 zipFile zip_file = OpenForZipping(zipFilePath.Value(), APPEND_STATUS_CREATE);
151 if (!zip_file) {
152 APP_LOGI("%{public}s called, Couldn't create ZIP file at path", __func__);
153 return nullptr;
154 }
155 return zip_file;
156 }
157
ZipWriter(zipFile zip_file,const FilePath & rootDir)158 ZipWriter::ZipWriter(zipFile zip_file, const FilePath &rootDir) : zipFile_(zip_file), rootDir_(rootDir)
159 {}
160
~ZipWriter()161 ZipWriter::~ZipWriter()
162 {
163 pendingEntries_.clear();
164 }
165
WriteEntries(const std::vector<FilePath> & paths,const OPTIONS & options)166 bool ZipWriter::WriteEntries(const std::vector<FilePath> &paths, const OPTIONS &options)
167 {
168 APP_LOGD("%{public}s called", __func__);
169 return AddEntries(paths, options) && Close(options);
170 }
171
AddEntries(const std::vector<FilePath> & paths,const OPTIONS & options)172 bool ZipWriter::AddEntries(const std::vector<FilePath> &paths, const OPTIONS &options)
173 {
174 if (!zipFile_) {
175 return false;
176 }
177 pendingEntries_.insert(pendingEntries_.end(), paths.begin(), paths.end());
178 return FlushEntriesIfNeeded(false, options);
179 }
180
Close(const OPTIONS & options)181 bool ZipWriter::Close(const OPTIONS &options)
182 {
183 bool success = FlushEntriesIfNeeded(true, options) && zipClose(zipFile_, nullptr) == ZIP_OK;
184 zipFile_ = nullptr;
185 return success;
186 }
187
FlushEntriesIfNeeded(bool force,const OPTIONS & options)188 bool ZipWriter::FlushEntriesIfNeeded(bool force, const OPTIONS &options)
189 {
190 if (pendingEntries_.size() < g_MaxPendingEntriesCount && !force) {
191 return true;
192 }
193 while (pendingEntries_.size() >= g_MaxPendingEntriesCount || (force && !pendingEntries_.empty())) {
194
195 size_t entry_count = std::min(pendingEntries_.size(), g_MaxPendingEntriesCount);
196 std::vector<FilePath> relativePaths;
197 std::vector<FilePath> absolutePaths;
198
199 relativePaths.insert(relativePaths.begin(), pendingEntries_.begin(), pendingEntries_.begin() + entry_count);
200 for (auto iter = pendingEntries_.begin(); iter != pendingEntries_.begin() + entry_count; ++iter) {
201 // The FileAccessor requires absolute paths.
202 if (FilePath::IsDir(rootDir_)) {
203 absolutePaths.push_back(FilePath(rootDir_.Value() + iter->Value()));
204 } else {
205 absolutePaths.push_back(FilePath(rootDir_.Value()));
206 }
207 }
208 pendingEntries_.erase(pendingEntries_.begin(), pendingEntries_.begin() + entry_count);
209 for (size_t i = 0; i < absolutePaths.size(); i++) {
210 FilePath &relativePath = relativePaths[i];
211 FilePath &absolutePath = absolutePaths[i];
212 bool isValid = FilePath::PathIsValid(absolutePath);
213 bool isDir = FilePath::IsDir(absolutePath);
214 if (isValid && !isDir) {
215 if (!AddFileEntryToZip(zipFile_, relativePath, absolutePath, options)) {
216 APP_LOGI("%{public}s called, Failed to write file", __func__);
217 return false;
218 }
219 } else {
220 // Missing file or directory case.
221 struct tm *last_modified = GetCurrentSystemTime();
222 if (!AddDirectoryEntryToZip(zipFile_, relativePath, last_modified, options)) {
223 APP_LOGI("%{public}s called, Failed to write directory", __func__);
224 return false;
225 }
226 }
227 }
228 }
229 return true;
230 }
231
232 } // namespace LIBZIP
233 } // namespace AppExecFwk
234 } // namespace OHOS
235