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 char buf[kZipBufSize];
40 if (!FilePathCheckValid(file_path.Value())) {
41 APP_LOGI("filePath is invalid file_path=%{public}s", file_path.Value().c_str());
42 return false;
43 }
44 if (!FilePath::PathIsValid(file_path)) {
45 APP_LOGI("PathIsValid returns false");
46 return false;
47 }
48
49 FILE *fp = fopen(file_path.Value().c_str(), "rb");
50 if (fp == nullptr) {
51 APP_LOGI("filePath to realPath failed filePath:%{private}s errno:%{public}s",
52 file_path.Value().c_str(), strerror(errno));
53 return false;
54 }
55
56 uint32_t num_bytes;
57 while (!feof(fp)) {
58 num_bytes = fread(buf, 1, kZipBufSize, fp);
59 if (num_bytes > 0) {
60 if (zipWriteInFileInZip(zip_file, buf, num_bytes) != ZIP_OK) {
61 APP_LOGI("Could not write data to zip for path:%{private}s ", file_path.Value().c_str());
62 fclose(fp);
63 fp = nullptr;
64 return false;
65 }
66 } else {
67 break;
68 }
69 }
70 fclose(fp);
71 fp = nullptr;
72 return true;
73 }
74
OpenNewFileEntry(zipFile zip_file,FilePath & path,bool isDirectory,struct tm * lastModified,const OPTIONS & options)75 bool OpenNewFileEntry(
76 zipFile zip_file, FilePath &path, bool isDirectory, struct tm *lastModified, const OPTIONS &options)
77 {
78 std::string strPath = path.Value();
79
80 if (isDirectory) {
81 strPath += SEPARATOR;
82 }
83
84 return ZipOpenNewFileInZip(zip_file, strPath, options, lastModified);
85 }
86
CloseNewFileEntry(zipFile zip_file)87 bool CloseNewFileEntry(zipFile zip_file)
88 {
89 return zipCloseFileInZip(zip_file) == ZIP_OK;
90 }
91
AddFileEntryToZip(zipFile zip_file,FilePath & relativePath,FilePath & absolutePath,const OPTIONS & options)92 bool AddFileEntryToZip(zipFile zip_file, FilePath &relativePath, FilePath &absolutePath, const OPTIONS &options)
93 {
94 struct tm *lastModified = GetCurrentSystemTime();
95 if (lastModified == nullptr) {
96 return false;
97 }
98 if (!OpenNewFileEntry(zip_file, relativePath, false, lastModified, options)) {
99 return false;
100 }
101 bool success = AddFileContentToZip(zip_file, absolutePath);
102 if (!CloseNewFileEntry(zip_file)) {
103 APP_LOGI("CloseNewFileEntry returnValule is false");
104 return false;
105 }
106 return success;
107 }
108
AddDirectoryEntryToZip(zipFile zip_file,FilePath & path,struct tm * lastModified,const OPTIONS & options)109 bool AddDirectoryEntryToZip(zipFile zip_file, FilePath &path, struct tm *lastModified, const OPTIONS &options)
110 {
111 return OpenNewFileEntry(zip_file, path, true, lastModified, options) && CloseNewFileEntry(zip_file);
112 }
113
114 } // namespace
115
InitZipFileWithFd(PlatformFile zipFilefd)116 zipFile ZipWriter::InitZipFileWithFd(PlatformFile zipFilefd)
117 {
118 std::lock_guard<std::mutex> lock(g_mutex);
119 if (zipFilefd == kInvalidPlatformFile) {
120 return nullptr;
121 }
122
123 zipFile zip_file = OpenFdForZipping(zipFilefd, APPEND_STATUS_CREATE);
124 if (!zip_file) {
125 APP_LOGI("Couldn't create ZIP file for FD");
126 return nullptr;
127 }
128 return zip_file;
129 }
130
InitZipFileWithFile(const FilePath & zip_file_path)131 zipFile ZipWriter::InitZipFileWithFile(const FilePath &zip_file_path)
132 {
133 std::lock_guard<std::mutex> lock(g_mutex);
134 FilePath zipFilePath = zip_file_path;
135 if (zipFilePath.Value().empty()) {
136 APP_LOGI("Path is empty");
137 return nullptr;
138 }
139
140 zipFile zip_file = OpenForZipping(zipFilePath.Value(), APPEND_STATUS_CREATE);
141 if (!zip_file) {
142 APP_LOGI("Couldn't create ZIP file at path");
143 return nullptr;
144 }
145 return zip_file;
146 }
147
ZipWriter(zipFile zip_file)148 ZipWriter::ZipWriter(zipFile zip_file) : zipFile_(zip_file)
149 {}
150
~ZipWriter()151 ZipWriter::~ZipWriter()
152 {
153 pendingEntries_.clear();
154 }
155
WriteEntries(const std::vector<std::pair<FilePath,FilePath>> & paths,const OPTIONS & options)156 bool ZipWriter::WriteEntries(const std::vector<std::pair<FilePath, FilePath>> &paths, const OPTIONS &options)
157 {
158 return AddEntries(paths, options) && Close(options);
159 }
160
AddEntries(const std::vector<std::pair<FilePath,FilePath>> & paths,const OPTIONS & options)161 bool ZipWriter::AddEntries(const std::vector<std::pair<FilePath, FilePath>> &paths, const OPTIONS &options)
162 {
163 if (!zipFile_) {
164 return false;
165 }
166 pendingEntries_.insert(pendingEntries_.end(), paths.begin(), paths.end());
167 return FlushEntriesIfNeeded(false, options);
168 }
169
Close(const OPTIONS & options)170 bool ZipWriter::Close(const OPTIONS &options)
171 {
172 bool success = FlushEntriesIfNeeded(true, options) && zipClose(zipFile_, nullptr) == ZIP_OK;
173 zipFile_ = nullptr;
174 return success;
175 }
176
FlushEntriesIfNeeded(bool force,const OPTIONS & options)177 bool ZipWriter::FlushEntriesIfNeeded(bool force, const OPTIONS &options)
178 {
179 if (pendingEntries_.size() < g_MaxPendingEntriesCount && !force) {
180 return true;
181 }
182 while (pendingEntries_.size() >= g_MaxPendingEntriesCount || (force && !pendingEntries_.empty())) {
183 size_t entry_count = std::min(pendingEntries_.size(), g_MaxPendingEntriesCount);
184 std::vector<std::pair<FilePath, FilePath>> relativePaths;
185 relativePaths.insert(relativePaths.begin(), pendingEntries_.begin(), pendingEntries_.begin() + entry_count);
186 pendingEntries_.erase(pendingEntries_.begin(), pendingEntries_.begin() + entry_count);
187 for (size_t i = 0; i < relativePaths.size(); i++) {
188 FilePath &relativePath = relativePaths[i].first;
189 FilePath &absolutePath = relativePaths[i].second;
190 bool isValid = FilePath::PathIsValid(absolutePath);
191 bool isDir = FilePath::IsDir(absolutePath);
192 if (isValid && !isDir) {
193 if (!AddFileEntryToZip(zipFile_, relativePath, absolutePath, options)) {
194 APP_LOGI("Failed to write file");
195 return false;
196 }
197 } else {
198 // Missing file or directory case.
199 struct tm *last_modified = GetCurrentSystemTime();
200 if (!AddDirectoryEntryToZip(zipFile_, relativePath, last_modified, options)) {
201 APP_LOGI("Failed to write directory");
202 return false;
203 }
204 }
205 }
206 }
207 return true;
208 }
209
210 } // namespace LIBZIP
211 } // namespace AppExecFwk
212 } // namespace OHOS
213