• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &params, const OPTIONS &options, CALLBACK callback)
118 {
119     const std::vector<FilePath> *filesToAdd = &params.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