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