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 ¶ms, const OPTIONS &options)
122 {
123 const std::vector<std::pair<FilePath, FilePath>> *filesToAdd = ¶ms.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 }