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