• 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 
16 #include "zip_reader.h"
17 #include <stdio.h>
18 #include <utility>
19 #include <unistd.h>
20 #include <time.h>
21 #include "zip_utils.h"
22 #include "string_ex.h"
23 #include "checked_cast.h"
24 #include "zip_internal.h"
25 #include "contrib/minizip/unzip.h"
26 #include "zip_utils.h"
27 #include "hilog_wrapper.h"
28 
29 namespace OHOS {
30 namespace AAFwk {
31 namespace LIBZIP {
32 
33 // TODO(satorux): The implementation assumes that file names in zip files
34 // are encoded in UTF-8. This is true for zip files created by Zip()
35 // function in zip.h, but not true for user-supplied random zip files.
EntryInfo(const std::string & fileNameInZip,const unz_file_info & rawFileInfo)36 ZipReader::EntryInfo::EntryInfo(const std::string &fileNameInZip, const unz_file_info &rawFileInfo)
37     : filePath_(FilePath::FromUTF8Unsafe(fileNameInZip)), isDirectory_(false), isUnsafe_(false), isEncrypted_(false)
38 {
39     originalSize_ = rawFileInfo.uncompressed_size;
40 
41     // Directory entries in zip files end with "/".
42     isDirectory_ = EndsWith(fileNameInZip, "/");
43 
44     // Check the file name here for directory traversal issues.
45     isUnsafe_ = filePath_.ReferencesParent();
46 
47     // We also consider that the file name is unsafe, if it's absolute.
48     // On Windows, IsAbsolute() returns false for paths starting with "/".
49     if (filePath_.IsAbsolute() || StartsWith(fileNameInZip, "/")) {
50         isUnsafe_ = false;
51     }
52 
53     // Whether the file is encrypted is bit 0 of the flag.
54     isEncrypted_ = rawFileInfo.flag & 1;
55 
56     // Construct the last modified time. The timezone info is not present in
57     // zip files, so we construct the time as local time.
58     if (GetCurrentSystemTime() != nullptr) {
59         lastModified_ = *GetCurrentSystemTime();
60     }
61 }
62 
ZipReader()63 ZipReader::ZipReader()
64 {
65     Reset();
66 }
67 
~ZipReader()68 ZipReader::~ZipReader()
69 {
70     Close();
71 }
72 
Open(FilePath & zipFilePath)73 bool ZipReader::Open(FilePath &zipFilePath)
74 {
75     if (zipFile_ != nullptr) {
76         return false;
77     }
78 
79     // Use of "Unsafe" function does not look good, but there is no way to do
80     // this safely on Linux. See file_util.h for details.
81     std::string zipfile = zipFilePath.Value();
82     zipFile_ = OpenForUnzipping(zipfile);
83     if (zipFile_ == nullptr) {
84         return false;
85     }
86 
87     return OpenInternal();
88 }
89 
OpenFromPlatformFile(PlatformFile zipFd)90 bool ZipReader::OpenFromPlatformFile(PlatformFile zipFd)
91 {
92     if (zipFile_ != nullptr) {
93         return false;
94     }
95     zipFile_ = OpenFdForUnzipping(zipFd);
96     if (!zipFile_) {
97         return false;
98     }
99 
100     return OpenInternal();
101 }
102 
OpenFromString(const std::string & data)103 bool ZipReader::OpenFromString(const std::string &data)
104 {
105     zipFile_ = PrepareMemoryForUnzipping(data);
106     if (!zipFile_) {
107         return false;
108     }
109 
110     return OpenInternal();
111 }
112 
Close()113 void ZipReader::Close()
114 {
115     if (zipFile_) {
116         unzClose(zipFile_);
117     }
118     Reset();
119 }
120 
HasMore()121 bool ZipReader::HasMore()
122 {
123     return !reachedEnd_;
124 }
125 
AdvanceToNextEntry()126 bool ZipReader::AdvanceToNextEntry()
127 {
128     if (zipFile_ == nullptr) {
129         return false;
130     }
131     // Should not go further if we already reached the end.
132     if (reachedEnd_) {
133         return false;
134     }
135     unz_file_pos position = {};
136     if (unzGetFilePos(zipFile_, &position) != UNZ_OK) {
137         return false;
138     }
139 
140     const int currentEntryIndex = position.num_of_file;
141     // If we are currently at the last entry, then the next position is the
142     // end of the zip file, so mark that we reached the end.
143     if (currentEntryIndex + 1 == numEntries_) {
144         reachedEnd_ = true;
145     } else {
146         if (unzGoToNextFile(zipFile_) != UNZ_OK) {
147             return false;
148         }
149     }
150     currentEntryInfo_.reset();
151     return true;
152 }
153 
OpenCurrentEntryInZip()154 bool ZipReader::OpenCurrentEntryInZip()
155 {
156     if (zipFile_ == nullptr) {
157         return false;
158     }
159 
160     unz_file_info raw_file_info = {};
161     char raw_file_name_in_zip[kZipMaxPath] = {};
162 
163     const int result = unzGetCurrentFileInfo(zipFile_,
164         &raw_file_info,
165         raw_file_name_in_zip,
166         sizeof(raw_file_name_in_zip) - 1,
167         NULL,  // extraField.
168         0,     // extraFieldBufferSize.
169         NULL,  // szComment.
170         0);    // commentBufferSize.
171 
172     if (result != UNZ_OK) {
173         return false;
174     }
175     if (raw_file_name_in_zip[0] == '\0') {
176         return false;
177     }
178     currentEntryInfo_.reset(new EntryInfo(std::string(raw_file_name_in_zip), raw_file_info));
179     return true;
180 }
181 
ExtractCurrentEntry(WriterDelegate * delegate,uint64_t numBytesToExtract) const182 bool ZipReader::ExtractCurrentEntry(WriterDelegate *delegate, uint64_t numBytesToExtract) const
183 {
184     if (zipFile_ == nullptr) {
185         return false;
186     }
187     const int openResult = unzOpenCurrentFile(zipFile_);
188     if (openResult != UNZ_OK) {
189         return false;
190     }
191     if (!delegate->PrepareOutput()) {
192         return false;
193     }
194     std::unique_ptr<char[]> buf(new char[kZipBufSize]);
195     uint64_t remainingCapacity = numBytesToExtract;
196     bool entirefileextracted = false;
197 
198     while (remainingCapacity > 0) {
199         const int numBytesRead = unzReadCurrentFile(zipFile_, buf.get(), kZipBufSize);
200         if (numBytesRead == 0) {
201             entirefileextracted = true;
202             break;
203         } else if (numBytesRead < 0) {
204             // If numBytesRead < 0, then it's a specific UNZ_* error code.
205             break;
206         } else if (numBytesRead > 0) {
207             uint64_t numBytesToWrite = std::min<uint64_t>(remainingCapacity, checked_cast<uint64_t>(numBytesRead));
208             if (!delegate->WriteBytes(buf.get(), numBytesToWrite)) {
209                 break;
210             }
211             if (remainingCapacity == checked_cast<uint64_t>(numBytesRead)) {
212                 // Ensures function returns true if the entire file has been read.
213                 entirefileextracted = (unzReadCurrentFile(zipFile_, buf.get(), 1) == 0);
214             }
215             if (remainingCapacity >= numBytesToWrite) {
216                 remainingCapacity -= numBytesToWrite;
217             }
218         }
219     }
220 
221     unzCloseCurrentFile(zipFile_);
222     // closeFile
223     delegate->SetTimeModified(GetCurrentSystemTime());
224 
225     return entirefileextracted;
226 }
227 
OpenInternal()228 bool ZipReader::OpenInternal()
229 {
230     if (zipFile_ == nullptr) {
231         return false;
232     }
233 
234     unz_global_info zipInfo = {};  // Zero-clear.
235     if (unzGetGlobalInfo(zipFile_, &zipInfo) != UNZ_OK) {
236         return false;
237     }
238     numEntries_ = zipInfo.number_entry;
239     if (numEntries_ < 0) {
240         return false;
241     }
242 
243     // We are already at the end if the zip file is empty.
244     reachedEnd_ = (numEntries_ == 0);
245     return true;
246 }
247 
Reset()248 void ZipReader::Reset()
249 {
250     zipFile_ = NULL;
251     numEntries_ = 0;
252     reachedEnd_ = false;
253     currentEntryInfo_.reset();
254 }
255 
256 // FilePathWriterDelegate
FilePathWriterDelegate(const FilePath & outputFilePath)257 FilePathWriterDelegate::FilePathWriterDelegate(const FilePath &outputFilePath) : outputFilePath_(outputFilePath)
258 {}
259 
~FilePathWriterDelegate()260 FilePathWriterDelegate::~FilePathWriterDelegate()
261 {}
262 
PrepareOutput()263 bool FilePathWriterDelegate::PrepareOutput()
264 {
265     if (!FilePathCheckValid(outputFilePath_.Value())) {
266         HILOG_ERROR("%{public}s called, outputFilePath_ invalid !!!.", __func__);
267         return false;
268     }
269     // We can't rely on parent directory entries being specified in the
270     // zip, so we make sure they are created.
271     if (!FilePath::CreateDirectory(outputFilePath_.DirName())) {
272         return false;
273     }
274 
275     file_ = fopen(outputFilePath_.Value().c_str(), "wb");
276     return FilePath::PathIsValid(outputFilePath_);
277 }
278 
WriteBytes(const char * data,int numBytes)279 bool FilePathWriterDelegate::WriteBytes(const char *data, int numBytes)
280 {
281     if (file_ == nullptr || numBytes <= 0) {
282         return false;
283     }
284     int writebytes = fwrite(data, 1, numBytes, file_);
285     return numBytes == writebytes;
286 }
287 
SetTimeModified(const struct tm * time)288 void FilePathWriterDelegate::SetTimeModified(const struct tm *time)
289 {
290     fclose(file_);
291     file_ = nullptr;
292 }
293 
294 }  // namespace LIBZIP
295 }  // namespace AAFwk
296 }  // namespace OHOS