1 /*
2 * Copyright (c) 2021 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 #include "zip.h"
16 #include <list>
17 #include <string>
18 #include <vector>
19 #include <stdio.h>
20 #include <fcntl.h>
21 #include <unistd.h>
22 #include "file_path.h"
23 #include "zip_internal.h"
24 #include "runnable.h"
25 #include "zip_reader.h"
26 #include "zip_writer.h"
27 #include "directory_ex.h"
28 #include "hilog_wrapper.h"
29
30 namespace OHOS {
31 namespace AAFwk {
32 namespace LIBZIP {
33 namespace {
34 using FilterCallback = std::function<bool(const FilePath &)>;
35 using DirectoryCreator = std::function<bool(FilePath &, FilePath &)>;
36 using WriterFactory = std::function<std::unique_ptr<WriterDelegate>(FilePath &, FilePath &)>;
37
38 const std::string SEPARATOR = "/";
39 const char HIDDEN_SEPARATOR = '.';
40
41 #define CALLING_CALL_BACK(callback, result) \
42 if (callback != nullptr) { \
43 callback(result); \
44 }
45
46 struct UnzipParam {
47 CALLBACK callback = nullptr;
48 FilterCallback filterCB = nullptr;
49 bool logSkippedFiles = false;
50 };
IsHiddenFile(const FilePath & filePath)51 bool IsHiddenFile(const FilePath &filePath)
52 {
53 FilePath localFilePath = filePath;
54 std::string path = localFilePath.BaseName().Value();
55 if (!localFilePath.Value().empty()) {
56 return localFilePath.Value().c_str()[0] == HIDDEN_SEPARATOR;
57 } else {
58 return false;
59 }
60 }
ExcludeNoFilesFilter(const FilePath & filePath)61 bool ExcludeNoFilesFilter(const FilePath &filePath)
62 {
63 return true;
64 }
65
ExcludeHiddenFilesFilter(const FilePath & filePath)66 bool ExcludeHiddenFilesFilter(const FilePath &filePath)
67 {
68 return !IsHiddenFile(filePath);
69 }
70
ListDirectoryContent(const FilePath & filePath)71 std::vector<FileAccessor::DirectoryContentEntry> ListDirectoryContent(const FilePath &filePath)
72 {
73 FilePath curPath = filePath;
74 std::vector<FileAccessor::DirectoryContentEntry> fileDirectoryVector;
75 std::vector<std::string> filelist;
76 GetDirFiles(curPath.Value(), filelist);
77 HILOG_INFO("filelist ========filelist.size=%{public}zu", filelist.size());
78 for (size_t i = 0; i < filelist.size(); i++) {
79 std::string str(filelist[i]);
80 if (!str.empty()) {
81 fileDirectoryVector.push_back(
82 FileAccessor::DirectoryContentEntry(FilePath(str), FilePath::DirectoryExists(FilePath(str))));
83 }
84 }
85 return fileDirectoryVector;
86 }
87
88 // Creates a directory at |extractDir|/|entryPath|, including any parents.
CreateDirectory(FilePath & extractDir,FilePath & entryPath)89 bool CreateDirectory(FilePath &extractDir, FilePath &entryPath)
90 {
91 std::string path = extractDir.Value();
92 if (EndsWith(path, SEPARATOR)) {
93 return FilePath::CreateDirectory(FilePath(extractDir.Value() + entryPath.Value()));
94 } else {
95 return FilePath::CreateDirectory(FilePath(extractDir.Value() + "/" + entryPath.Value()));
96 }
97 }
98
99 // Creates a WriterDelegate that can write a file at |extractDir|/|entryPath|.
CreateFilePathWriterDelegate(FilePath & extractDir,FilePath entryPath)100 std::unique_ptr<WriterDelegate> CreateFilePathWriterDelegate(FilePath &extractDir, FilePath entryPath)
101 {
102 if (EndsWith(extractDir.Value(), SEPARATOR)) {
103 return std::make_unique<FilePathWriterDelegate>(FilePath(extractDir.Value() + entryPath.Value()));
104 } else {
105 return std::make_unique<FilePathWriterDelegate>(FilePath(extractDir.Value() + "/" + entryPath.Value()));
106 }
107 }
108 } // namespace
109
ZipParams(const FilePath & srcDir,const FilePath & destFile)110 ZipParams::ZipParams(const FilePath &srcDir, const FilePath &destFile) : srcDir_(srcDir), destFile_(destFile)
111 {}
112
113 // Does not take ownership of |fd|.
ZipParams(const FilePath & srcDir,int destFd)114 ZipParams::ZipParams(const FilePath &srcDir, int destFd) : srcDir_(srcDir), destFd_(destFd)
115 {}
116
Zip(const ZipParams & params,const OPTIONS & options,CALLBACK callback)117 bool Zip(const ZipParams ¶ms, const OPTIONS &options, CALLBACK callback)
118 {
119 const std::vector<FilePath> *filesToAdd = ¶ms.GetFilesTozip();
120 std::vector<FilePath> allRelativeFiles;
121 FilePath paramPath = params.SrcDir();
122 bool endIsSeparator = EndsWith(paramPath.Value(), SEPARATOR);
123 if (filesToAdd->empty()) {
124 filesToAdd = &allRelativeFiles;
125 std::list<FileAccessor::DirectoryContentEntry> entries;
126 if (endIsSeparator) {
127 entries.push_back(FileAccessor::DirectoryContentEntry(params.SrcDir(), true));
128 FilterCallback filterCallback = params.GetFilterCallback();
129 for (auto iter = entries.begin(); iter != entries.end(); ++iter) {
130 const FilePath &constEntryPath = iter->path;
131 if (iter != entries.begin() && ((!params.GetIncludeHiddenFiles() && IsHiddenFile(constEntryPath)) ||
132 (filterCallback && !filterCallback(constEntryPath)))) {
133 continue;
134 }
135 if (iter != entries.begin()) {
136 FilePath relativePath;
137 FilePath entryPath = constEntryPath;
138 FilePath paramsSrcPath = params.SrcDir();
139 bool success = paramsSrcPath.AppendRelativePath(entryPath, &relativePath);
140 if (success) {
141 allRelativeFiles.push_back(relativePath);
142 }
143 }
144 if (iter->isDirectory) {
145 std::vector<FileAccessor::DirectoryContentEntry> subEntries = ListDirectoryContent(constEntryPath);
146 entries.insert(entries.end(), subEntries.begin(), subEntries.end());
147 }
148 }
149 } else {
150 allRelativeFiles.push_back(paramPath.BaseName());
151 }
152 }
153 std::unique_ptr<ZipWriter> zipWriter = nullptr;
154 FilePath rootPath = (endIsSeparator == false) ? FilePath(paramPath.DirName().Value() + SEPARATOR) : params.SrcDir();
155 if (params.DestFd() != kInvalidPlatformFile) {
156 zipWriter = ZipWriter::CreateWithFd(params.DestFd(), rootPath);
157 if (!zipWriter) {
158 CALLING_CALL_BACK(callback, ERROR_CODE_STREAM_ERROR)
159 return false;
160 }
161 }
162 if (!zipWriter) {
163 zipWriter = ZipWriter::Create(params.DestFile(), rootPath);
164 if (!zipWriter) {
165 CALLING_CALL_BACK(callback, ERROR_CODE_STREAM_ERROR)
166 return false;
167 }
168 }
169 return zipWriter->WriteEntries(*filesToAdd, options, callback);
170 }
171
UnzipWithFilterAndWriters(const PlatformFile & srcFile,FilePath & destDir,WriterFactory writerFactory,DirectoryCreator directoryCreator,UnzipParam & unzipParam)172 bool UnzipWithFilterAndWriters(const PlatformFile &srcFile, FilePath &destDir, WriterFactory writerFactory,
173 DirectoryCreator directoryCreator, UnzipParam &unzipParam)
174 {
175 HILOG_INFO("%{public}s called, destDir=%{public}s", __func__, destDir.Value().c_str());
176 ZipReader reader;
177 if (!reader.OpenFromPlatformFile(srcFile)) {
178 CALLING_CALL_BACK(unzipParam.callback, ERROR_CODE_ERRNO)
179 HILOG_INFO("%{public}s called, Failed to open srcFile.", __func__);
180 return false;
181 }
182 while (reader.HasMore()) {
183 if (!reader.OpenCurrentEntryInZip()) {
184 CALLING_CALL_BACK(unzipParam.callback, ERROR_CODE_ERRNO)
185 HILOG_INFO("%{public}s called, Failed to open the current file in zip.", __func__);
186 return false;
187 }
188 const FilePath &constEntryPath = reader.CurrentEntryInfo()->GetFilePath();
189 FilePath entryPath = constEntryPath;
190 if (reader.CurrentEntryInfo()->IsUnsafe()) {
191 CALLING_CALL_BACK(unzipParam.callback, ERROR_CODE_ERRNO)
192 HILOG_INFO("%{public}s called, Found an unsafe file in zip.", __func__);
193 return false;
194 }
195 // callback
196 if (unzipParam.filterCB(entryPath)) {
197 if (reader.CurrentEntryInfo()->IsDirectory()) {
198 if (!directoryCreator(destDir, entryPath)) {
199 HILOG_INFO("!!!directory_creator(%{public}s) Failed!!!.", entryPath.Value().c_str());
200 CALLING_CALL_BACK(unzipParam.callback, ERROR_CODE_ERRNO)
201 return false;
202 }
203
204 } else {
205 std::unique_ptr<WriterDelegate> writer = writerFactory(destDir, entryPath);
206 if (!reader.ExtractCurrentEntry(writer.get(), std::numeric_limits<uint64_t>::max())) {
207 CALLING_CALL_BACK(unzipParam.callback, ERROR_CODE_ERRNO)
208 HILOG_INFO("%{public}s called, Failed to extract.", __func__);
209 return false;
210 }
211 }
212 } else if (unzipParam.logSkippedFiles) {
213 HILOG_INFO("%{public}s called, Skipped file.", __func__);
214 }
215
216 if (!reader.AdvanceToNextEntry()) {
217 CALLING_CALL_BACK(unzipParam.callback, ERROR_CODE_ERRNO)
218 HILOG_INFO("%{public}s called, Failed to advance to the next file.", __func__);
219 return false;
220 }
221 }
222 CALLING_CALL_BACK(unzipParam.callback, ERROR_CODE_OK)
223 return true;
224 }
UnzipWithFilterCallback(const FilePath & srcFile,const FilePath & destDir,const OPTIONS & options,UnzipParam & unzipParam)225 bool UnzipWithFilterCallback(
226 const FilePath &srcFile, const FilePath &destDir, const OPTIONS &options, UnzipParam &unzipParam)
227 {
228 FilePath src = srcFile;
229 if (!FilePathCheckValid(src.Value())) {
230 CALLING_CALL_BACK(unzipParam.callback, ERROR_CODE_DATA_ERROR)
231 return false;
232 }
233
234 FilePath dest = destDir;
235
236 HILOG_INFO("%{public}s called, srcFile=%{public}s, destFile=%{public}s",
237 __func__,
238 src.Value().c_str(),
239 dest.Value().c_str());
240
241 if (!FilePath::PathIsValid(srcFile)) {
242 CALLING_CALL_BACK(unzipParam.callback, ERROR_CODE_DATA_ERROR)
243 HILOG_INFO("%{public}s called, Failed to open.", __func__);
244 return false;
245 }
246
247 PlatformFile zipFd = open(src.Value().c_str(), S_IREAD);
248 if (zipFd == kInvalidPlatformFile) {
249 CALLING_CALL_BACK(unzipParam.callback, ERROR_CODE_STREAM_ERROR)
250 HILOG_INFO("%{public}s called, Failed to open.", __func__);
251 return false;
252 }
253 bool ret = UnzipWithFilterAndWriters(zipFd,
254 dest,
255 std::bind(&CreateFilePathWriterDelegate, std::placeholders::_1, std::placeholders::_2),
256 std::bind(&CreateDirectory, std::placeholders::_1, std::placeholders::_2),
257 unzipParam);
258
259 close(zipFd);
260
261 return ret;
262 }
Unzip(const FilePath & srcFile,const FilePath & destDir,const OPTIONS & options,CALLBACK callback)263 bool Unzip(const FilePath &srcFile, const FilePath &destDir, const OPTIONS &options, CALLBACK callback)
264 {
265 FilePath srcFileDir = srcFile;
266 FilePath destDirTemp = destDir;
267 HILOG_INFO("%{public}s called, srcFile=%{public}s, destFile=%{public}s",
268 __func__,
269 srcFileDir.Value().c_str(),
270 destDirTemp.Value().c_str());
271
272 std::shared_ptr<Runnable> innerTask = std::make_shared<Runnable>([srcFile, destDir, options, callback]() {
273 UnzipParam unzipParam{
274 .callback = callback,
275 .filterCB = ExcludeNoFilesFilter,
276 .logSkippedFiles = true
277 };
278 UnzipWithFilterCallback(srcFile, destDir, options, unzipParam);
279 });
280
281 PostTask(innerTask);
282 return true;
283 }
284
ZipWithFilterCallback(const FilePath & srcDir,const FilePath & destFile,const OPTIONS & options,CALLBACK callback,FilterCallback filterCB)285 bool ZipWithFilterCallback(const FilePath &srcDir, const FilePath &destFile, const OPTIONS &options, CALLBACK callback,
286 FilterCallback filterCB)
287 {
288 FilePath srcPath = srcDir;
289 if (!EndsWith(srcPath.Value(), SEPARATOR)) {
290 if (!FilePath::DirectoryExists(srcPath.DirName())) {
291 CALLING_CALL_BACK(callback, ERROR_CODE_DATA_ERROR)
292 return false;
293 }
294 } else if (!FilePath::DirectoryExists(srcDir)) {
295 CALLING_CALL_BACK(callback, ERROR_CODE_DATA_ERROR)
296 return false;
297 }
298 ZipParams params(srcDir, destFile);
299 params.SetFilterCallback(filterCB);
300 return Zip(params, options, callback);
301 }
302
Zip(const FilePath & srcDir,const FilePath & destFile,const OPTIONS & options,CALLBACK callback,bool includeHiddenFiles)303 bool Zip(const FilePath &srcDir, const FilePath &destFile, const OPTIONS &options, CALLBACK callback,
304 bool includeHiddenFiles)
305 {
306 FilePath srctemp = srcDir;
307 FilePath desttemp = destFile;
308 HILOG_INFO("%{public}s called, srcDir=%{public}s, destFile=%{public}s",
309 __func__,
310 srctemp.Value().c_str(),
311 desttemp.Value().c_str());
312
313 FilePath srcdir = srcDir;
314
315 std::shared_ptr<Runnable> innerTask =
316 std::make_shared<Runnable>([srcDir, destFile, includeHiddenFiles, callback]() {
317 OPTIONS options;
318 if (includeHiddenFiles) {
319 ZipWithFilterCallback(srcDir, destFile, options, callback, ExcludeNoFilesFilter);
320 } else {
321 ZipWithFilterCallback(srcDir, destFile, options, callback, ExcludeHiddenFilesFilter);
322 }
323 });
324 PostTask(innerTask);
325
326 return true;
327 }
328 } // namespace LIBZIP
329 } // namespace AAFwk
330 } // namespace OHOS
331