1
2 /*
3 * Copyright (c) 2021 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 #include <stdio.h>
18 #include "zip_internal.h"
19 #include "hilog_wrapper.h"
20 #include "directory_ex.h"
21 #include "contrib/minizip/zip.h"
22
23 namespace OHOS {
24 namespace AAFwk {
25 namespace LIBZIP {
26 namespace {
27 // Numbers of pending entries that trigger writting them to the ZIP file.
28 constexpr size_t kMaxPendingEntriesCount = 50;
29 const std::string SEPARATOR = "/";
30
31 #define CALLING_CALL_BACK(callback, result) \
32 if (callback != nullptr) { \
33 callback(result); \
34 }
35
AddFileContentToZip(zipFile zip_file,FilePath & file_path)36 bool AddFileContentToZip(zipFile zip_file, FilePath &file_path)
37 {
38 HILOG_INFO("%{public}s called", __func__);
39 int num_bytes;
40 char buf[kZipBufSize];
41 if (!FilePathCheckValid(file_path.Value())) {
42 HILOG_INFO(
43 "%{public}s called, filePath is invalid!!! file_path=%{public}s", __func__, file_path.Value().c_str());
44 return false;
45 }
46
47 if (!FilePath::PathIsValid(file_path)) {
48 return false;
49 }
50
51 FILE *fp = fopen(file_path.Value().c_str(), "rb");
52 if (fp == nullptr) {
53 HILOG_INFO("%{public}s called, filePath to realPath failed! filePath:%{private}s ",
54 __func__,
55 file_path.Value().c_str());
56 return false;
57 }
58
59 while (!feof(fp)) {
60 num_bytes = fread(buf, 1, kZipBufSize, fp);
61 if (num_bytes > 0) {
62 if (zipWriteInFileInZip(zip_file, buf, num_bytes) != ZIP_OK) {
63 HILOG_INFO("%{public}s called, Could not write data to zip for path:%{public}s ",
64 __func__,
65 file_path.Value().c_str());
66 fclose(fp);
67 fp = nullptr;
68 return false;
69 }
70 }
71 }
72 fclose(fp);
73 fp = nullptr;
74 return true;
75 }
76
OpenNewFileEntry(zipFile zip_file,FilePath & path,bool is_directory,struct tm * last_modified,const OPTIONS & options)77 bool OpenNewFileEntry(
78 zipFile zip_file, FilePath &path, bool is_directory, struct tm *last_modified, const OPTIONS &options)
79 {
80 HILOG_INFO("%{public}s called", __func__);
81 std::string strPath = path.Value();
82 return ZipOpenNewFileInZip(zip_file, strPath, options, last_modified);
83 }
84
CloseNewFileEntry(zipFile zip_file)85 bool CloseNewFileEntry(zipFile zip_file)
86 {
87 HILOG_INFO("%{public}s called", __func__);
88 return zipCloseFileInZip(zip_file) == ZIP_OK;
89 }
90
AddFileEntryToZip(zipFile zip_file,FilePath & relativePath,FilePath & absolutePath,const OPTIONS & options)91 bool AddFileEntryToZip(zipFile zip_file, FilePath &relativePath, FilePath &absolutePath, const OPTIONS &options)
92 {
93 HILOG_INFO("%{public}s called", __func__);
94
95 struct tm *lastModified = GetCurrentSystemTime();
96 if (lastModified == nullptr) {
97 return false;
98 }
99 if (!OpenNewFileEntry(zip_file, relativePath, false, lastModified, options)) {
100 return false;
101 }
102 bool success = AddFileContentToZip(zip_file, absolutePath);
103 if (!CloseNewFileEntry(zip_file)) {
104 return false;
105 }
106
107 return success;
108 }
109
AddDirectoryEntryToZip(zipFile zip_file,FilePath & path,struct tm * lastModified,const OPTIONS & options)110 bool AddDirectoryEntryToZip(zipFile zip_file, FilePath &path, struct tm *lastModified, const OPTIONS &options)
111 {
112 HILOG_INFO("%{public}s called", __func__);
113 return OpenNewFileEntry(zip_file, path, true, lastModified, options) && CloseNewFileEntry(zip_file);
114 }
115
116 } // namespace
117
CreateWithFd(PlatformFile zipFilefd,const FilePath & rootDir)118 std::unique_ptr<ZipWriter> ZipWriter::CreateWithFd(PlatformFile zipFilefd, const FilePath &rootDir)
119 {
120 HILOG_INFO("%{public}s called", __func__);
121 if (zipFilefd == kInvalidPlatformFile) {
122 return nullptr;
123 }
124
125 zipFile zip_file = OpenFdForZipping(zipFilefd, APPEND_STATUS_CREATE);
126 if (!zip_file) {
127 HILOG_INFO("%{public}s called, Couldn't create ZIP file for FD", __func__);
128 return nullptr;
129 }
130 return std::unique_ptr<ZipWriter>(new ZipWriter(zip_file, rootDir));
131 }
132
133 // static
Create(const FilePath & zip_file_path,const FilePath & rootDir)134 std::unique_ptr<ZipWriter> ZipWriter::Create(const FilePath &zip_file_path, const FilePath &rootDir)
135 {
136 FilePath zipFilePath = zip_file_path;
137 HILOG_INFO("%{public}s called", __func__);
138 if (zipFilePath.Value().empty()) {
139 HILOG_INFO("%{public}s called, Path is empty", __func__);
140 return nullptr;
141 }
142
143 zipFile zip_file = OpenForZipping(zipFilePath.Value(), APPEND_STATUS_CREATE);
144 if (!zip_file) {
145 HILOG_INFO("%{public}s called, Couldn't create ZIP file at path", __func__);
146 return nullptr;
147 }
148 return std::unique_ptr<ZipWriter>(new (std::nothrow) ZipWriter(zip_file, rootDir));
149 }
150
ZipWriter(zipFile zip_file,const FilePath & rootDir)151 ZipWriter::ZipWriter(zipFile zip_file, const FilePath &rootDir) : zipFile_(zip_file), rootDir_(rootDir)
152 {}
153
~ZipWriter()154 ZipWriter::~ZipWriter()
155 {
156 pendingEntries_.clear();
157 }
158
WriteEntries(const std::vector<FilePath> & paths,const OPTIONS & options,CALLBACK callback)159 bool ZipWriter::WriteEntries(const std::vector<FilePath> &paths, const OPTIONS &options, CALLBACK callback)
160 {
161 HILOG_INFO("%{public}s called", __func__);
162 return AddEntries(paths, options, callback) && Close(options, callback);
163 }
164
AddEntries(const std::vector<FilePath> & paths,const OPTIONS & options,CALLBACK callback)165 bool ZipWriter::AddEntries(const std::vector<FilePath> &paths, const OPTIONS &options, CALLBACK callback)
166 {
167 if (!zipFile_) {
168 return false;
169 }
170 pendingEntries_.insert(pendingEntries_.end(), paths.begin(), paths.end());
171 return FlushEntriesIfNeeded(false, options, callback);
172 }
173
Close(const OPTIONS & options,CALLBACK callback)174 bool ZipWriter::Close(const OPTIONS &options, CALLBACK callback)
175 {
176 bool success = FlushEntriesIfNeeded(true, options, callback) && zipClose(zipFile_, nullptr) == ZIP_OK;
177 zipFile_ = nullptr;
178 return success;
179 }
180
FlushEntriesIfNeeded(bool force,const OPTIONS & options,CALLBACK callback)181 bool ZipWriter::FlushEntriesIfNeeded(bool force, const OPTIONS &options, CALLBACK callback)
182 {
183 if (pendingEntries_.size() < kMaxPendingEntriesCount && !force) {
184 return true;
185 }
186
187 std::string rootDir = rootDir_.Value();
188 if (EndsWith(rootDir_.Value(), SEPARATOR)) {
189 rootDir = rootDir.substr(0, rootDir.size() - 1);
190 }
191 while (pendingEntries_.size() >= kMaxPendingEntriesCount || (force && !pendingEntries_.empty())) {
192
193 size_t entry_count = std::min(pendingEntries_.size(), kMaxPendingEntriesCount);
194 std::vector<FilePath> relativePaths;
195 std::vector<FilePath> absolutePaths;
196
197 relativePaths.insert(relativePaths.begin(), pendingEntries_.begin(), pendingEntries_.begin() + entry_count);
198 for (auto iter = pendingEntries_.begin(); iter != pendingEntries_.begin() + entry_count; ++iter) {
199 // The FileAccessor requires absolute paths.
200 absolutePaths.push_back(FilePath(rootDir_.Value() + iter->Value()));
201 }
202 pendingEntries_.erase(pendingEntries_.begin(), pendingEntries_.begin() + entry_count);
203
204 // We don't know which paths are files and which ones are directories, and
205 // we want to avoid making a call to file_accessor_ for each entry. Open the
206 // files instead, invalid files are returned for directories.
207
208 for (size_t i = 0; i < absolutePaths.size(); i++) {
209 FilePath &relativePath = relativePaths[i];
210 FilePath &absolutePath = absolutePaths[i];
211 if (FilePath::PathIsValid(absolutePath)) {
212 if (!AddFileEntryToZip(zipFile_, relativePath, absolutePath, options)) {
213 CALLING_CALL_BACK(callback, ERROR_CODE_ERRNO)
214 HILOG_INFO("%{public}s called, Failed to write file", __func__);
215 return false;
216 }
217 } else {
218 // Missing file or directory case.
219 struct tm *last_modified = GetCurrentSystemTime();
220 if (!AddDirectoryEntryToZip(zipFile_, relativePath, last_modified, options)) {
221 CALLING_CALL_BACK(callback, ERROR_CODE_ERRNO)
222 HILOG_INFO("%{public}s called, Failed to write directory", __func__);
223 return false;
224 }
225 }
226 }
227 }
228 CALLING_CALL_BACK(callback, ERROR_CODE_OK)
229 return true;
230 }
231
232 } // namespace LIBZIP
233 } // namespace AAFwk
234 } // namespace OHOS
235