• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 
18 #include <stdio.h>
19 #include <time.h>
20 #include <unistd.h>
21 #include <utility>
22 
23 #include "app_log_wrapper.h"
24 #include "checked_cast.h"
25 #include "contrib/minizip/unzip.h"
26 #include "string_ex.h"
27 #include "zip_internal.h"
28 #include "zip_utils.h"
29 
30 using namespace OHOS::AppExecFwk;
31 
32 namespace OHOS {
33 namespace AppExecFwk {
34 namespace LIBZIP {
35 
36 // The implementation assumes that file names in zip files
37 // are encoded in UTF-8. This is true for zip files created by Zip()
38 // function in zip.h, but not true for user-supplied random zip files.
EntryInfo(const std::string & fileNameInZip,const unz_file_info & rawFileInfo)39 ZipReader::EntryInfo::EntryInfo(const std::string &fileNameInZip, const unz_file_info &rawFileInfo)
40     : filePath_(FilePath::FromUTF8Unsafe(fileNameInZip)), isDirectory_(false), isUnsafe_(false), isEncrypted_(false)
41 {
42     originalSize_ = rawFileInfo.uncompressed_size;
43 
44     // Directory entries in zip files end with "/".
45     isDirectory_ = EndsWith(fileNameInZip, "/");
46 
47     // Check the file name here for directory traversal issues.
48     isUnsafe_ = filePath_.ReferencesParent();
49 
50     // We also consider that the file name is unsafe, if it's absolute.
51     // On Windows, IsAbsolute() returns false for paths starting with "/".
52     if (filePath_.IsAbsolute() || StartsWith(fileNameInZip, "/")) {
53         isUnsafe_ = false;
54     }
55 
56     // Whether the file is encrypted is bit 0 of the flag.
57     isEncrypted_ = rawFileInfo.flag & 1;
58 
59     // Construct the last modified time. The timezone info is not present in
60     // zip files, so we construct the time as local time.
61     if (GetCurrentSystemTime() != nullptr) {
62         lastModified_ = *GetCurrentSystemTime();
63     }
64 }
65 
ZipReader()66 ZipReader::ZipReader()
67 {
68     Reset();
69 }
70 
~ZipReader()71 ZipReader::~ZipReader()
72 {
73     Close();
74 }
75 
Open(FilePath & zipFilePath)76 bool ZipReader::Open(FilePath &zipFilePath)
77 {
78     if (zipFile_ != nullptr) {
79         return false;
80     }
81 
82     // Use of "Unsafe" function does not look good, but there is no way to do
83     // this safely on Linux. See file_util.h for details.
84     std::string zipfile = zipFilePath.Value();
85     zipFile_ = OpenForUnzipping(zipfile);
86     if (zipFile_ == nullptr) {
87         return false;
88     }
89 
90     return OpenInternal();
91 }
92 
OpenFromPlatformFile(PlatformFile zipFd)93 bool ZipReader::OpenFromPlatformFile(PlatformFile zipFd)
94 {
95     if (zipFile_ != nullptr) {
96         return false;
97     }
98     zipFile_ = OpenFdForUnzipping(zipFd);
99     if (!zipFile_) {
100         return false;
101     }
102 
103     return OpenInternal();
104 }
105 
OpenFromString(const std::string & data)106 bool ZipReader::OpenFromString(const std::string &data)
107 {
108     zipFile_ = PrepareMemoryForUnzipping(data);
109     if (!zipFile_) {
110         return false;
111     }
112 
113     return OpenInternal();
114 }
115 
Close()116 void ZipReader::Close()
117 {
118     if (zipFile_) {
119         unzClose(zipFile_);
120     }
121     Reset();
122 }
123 
HasMore()124 bool ZipReader::HasMore()
125 {
126     return !reachedEnd_;
127 }
128 
AdvanceToNextEntry()129 bool ZipReader::AdvanceToNextEntry()
130 {
131     if (zipFile_ == nullptr) {
132         return false;
133     }
134     // Should not go further if we already reached the end.
135     if (reachedEnd_) {
136         return false;
137     }
138     unz_file_pos position = {};
139     if (unzGetFilePos(zipFile_, &position) != UNZ_OK) {
140         return false;
141     }
142 
143     const int currentEntryIndex = position.num_of_file;
144     // If we are currently at the last entry, then the next position is the
145     // end of the zip file, so mark that we reached the end.
146     if (currentEntryIndex + 1 == numEntries_) {
147         reachedEnd_ = true;
148     } else {
149         if (unzGoToNextFile(zipFile_) != UNZ_OK) {
150             return false;
151         }
152     }
153     currentEntryInfo_.reset();
154     return true;
155 }
156 
GetCurrentEntryPos(unz_file_pos & filePos)157 bool ZipReader::GetCurrentEntryPos(unz_file_pos &filePos)
158 {
159     if (unzGetFilePos(zipFile_, &filePos) != UNZ_OK) {
160         return false;
161     }
162     return true;
163 }
164 
OpenCurrentEntryInZip()165 bool ZipReader::OpenCurrentEntryInZip()
166 {
167     if (zipFile_ == nullptr) {
168         return false;
169     }
170 
171     unz_file_info raw_file_info = {};
172     char raw_file_name_in_zip[kZipMaxPath] = {};
173     const int result = unzGetCurrentFileInfo(zipFile_,
174         &raw_file_info,
175         raw_file_name_in_zip,
176         sizeof(raw_file_name_in_zip) - 1,
177         NULL,  // extraField.
178         0,     // extraFieldBufferSize.
179         NULL,  // szComment.
180         0);    // commentBufferSize.
181     if (result != UNZ_OK) {
182         APP_LOGE("unzGetCurrentFileInfo %{public}d", result);
183         return false;
184     }
185     if (raw_file_name_in_zip[0] == '\0') {
186         return false;
187     }
188     EntryInfo *entryInfo = new (std::nothrow) EntryInfo(std::string(raw_file_name_in_zip), raw_file_info);
189     if (entryInfo == nullptr) {
190         return false;
191     }
192     currentEntryInfo_.reset(entryInfo);
193     return true;
194 }
195 
ExtractCurrentEntry(WriterDelegate * delegate,uint64_t numBytesToExtract) const196 bool ZipReader::ExtractCurrentEntry(WriterDelegate *delegate, uint64_t numBytesToExtract) const
197 {
198     return ExtractEntry(delegate, zipFile_, numBytesToExtract);
199 }
200 
ExtractEntry(WriterDelegate * delegate,const unzFile & zipFile,uint64_t numBytesToExtract) const201 bool ZipReader::ExtractEntry(WriterDelegate *delegate, const unzFile &zipFile, uint64_t numBytesToExtract) const
202 {
203     if ((zipFile == nullptr) || (delegate == nullptr)) {
204         return false;
205     }
206     const int openResult = unzOpenCurrentFile(zipFile);
207     if (openResult != UNZ_OK) {
208         APP_LOGE("unzOpen err %{public}d", openResult);
209         return false;
210     }
211     auto buf = std::make_unique<char[]>(kZipBufSize);
212     uint64_t remainingCapacity = numBytesToExtract;
213     bool entirefileextracted = false;
214 
215     while (remainingCapacity > 0) {
216         const int numBytesRead = unzReadCurrentFile(zipFile, buf.get(), kZipBufSize);
217         if (numBytesRead == 0) {
218             entirefileextracted = true;
219             APP_LOGD("extract entry");
220             break;
221         } else if (numBytesRead < 0) {
222             // If numBytesRead < 0, then it's a specific UNZ_* error code.
223             APP_LOGE("unzReadCurrentFile < 0 %{public}d", numBytesRead);
224             break;
225         } else {
226             uint64_t numBytesToWrite = std::min<uint64_t>(remainingCapacity, checked_cast<uint64_t>(numBytesRead));
227             if (!delegate->WriteBytes(buf.get(), numBytesToWrite)) {
228                 APP_LOGE("WriteBytes %{public}lu", (unsigned long) numBytesToWrite);
229                 break;
230             }
231             if (remainingCapacity == checked_cast<uint64_t>(numBytesRead)) {
232                 // Ensures function returns true if the entire file has been read.
233                 entirefileextracted = (unzReadCurrentFile(zipFile, buf.get(), 1) == 0);
234                 APP_LOGI("extract entry %{public}d", entirefileextracted);
235             }
236             if (remainingCapacity >= numBytesToWrite) {
237                 remainingCapacity -= numBytesToWrite;
238             }
239         }
240     }
241 
242     unzCloseCurrentFile(zipFile);
243     // closeFile
244     delegate->SetTimeModified(GetCurrentSystemTime());
245 
246     return entirefileextracted;
247 }
248 
OpenInternal()249 bool ZipReader::OpenInternal()
250 {
251     if (zipFile_ == nullptr) {
252         return false;
253     }
254 
255     unz_global_info zipInfo = {};  // Zero-clear.
256     if (unzGetGlobalInfo(zipFile_, &zipInfo) != UNZ_OK) {
257         return false;
258     }
259     numEntries_ = zipInfo.number_entry;
260     if (numEntries_ < 0) {
261         return false;
262     }
263 
264     // We are already at the end if the zip file is empty.
265     reachedEnd_ = (numEntries_ == 0);
266     return true;
267 }
268 
Reset()269 void ZipReader::Reset()
270 {
271     zipFile_ = nullptr;
272     numEntries_ = 0;
273     reachedEnd_ = false;
274     currentEntryInfo_.reset();
275 }
276 
277 // ZipParallelReader
~ZipParallelReader()278 ZipParallelReader::~ZipParallelReader()
279 {
280     Close();
281 }
282 
Open(FilePath & zipFilePath)283 bool ZipParallelReader::Open(FilePath &zipFilePath)
284 {
285     if (!zipFiles_.empty()) {
286         return false;
287     }
288     if (!ZipReader::Open(zipFilePath)) {
289         return false;
290     }
291 
292     unzFile zipFile;
293     std::string zipFilePathValue = zipFilePath.Value();
294     for (int32_t i = 0; i < concurrency_; i++) {
295         zipFile = OpenForUnzipping(zipFilePathValue);
296         if (!zipFile) {
297             return false;
298         }
299         zipFiles_.push_back(zipFile);
300     }
301     return true;
302 }
303 
GotoEntry(unzFile & zipFile,unz_file_pos filePos)304 bool ZipParallelReader::GotoEntry(unzFile &zipFile, unz_file_pos filePos)
305 {
306     if (zipFile == nullptr) {
307         return false;
308     }
309     if (static_cast<int>(filePos.num_of_file) >= ZipReader::num_entries()) {
310         return false;
311     }
312 
313     if (unzGoToFilePos(zipFile, &filePos) != UNZ_OK) {
314         return false;
315     }
316     return true;
317 }
318 
GetZipHandler(int & resourceId)319 unzFile ZipParallelReader::GetZipHandler(int &resourceId)
320 {
321     resourceId %= concurrency_;
322     while (!mtxes_[resourceId].try_lock()) {
323         resourceId = (resourceId + 1) % concurrency_;
324     }
325     return zipFiles_[resourceId];
326 }
327 
ReleaseZipHandler(const int & resourceId)328 void ZipParallelReader::ReleaseZipHandler(const int &resourceId)
329 {
330     mtxes_[resourceId].unlock();
331 }
332 
Close()333 void ZipParallelReader::Close()
334 {
335     ZipReader::Close();
336     for (unzFile zipFile : zipFiles_) {
337         unzClose(zipFile);
338     }
339 }
340 
341 // FilePathWriterDelegate
FilePathWriterDelegate(const FilePath & outputFilePath)342 FilePathWriterDelegate::FilePathWriterDelegate(const FilePath &outputFilePath) : outputFilePath_(outputFilePath)
343 {}
344 
~FilePathWriterDelegate()345 FilePathWriterDelegate::~FilePathWriterDelegate()
346 {}
347 
PrepareOutput()348 bool FilePathWriterDelegate::PrepareOutput()
349 {
350     if (!FilePathCheckValid(outputFilePath_.Value())) {
351         APP_LOGE("outputFilePath_ invalid");
352         return false;
353     }
354     // We can't rely on parent directory entries being specified in the
355     // zip, so we make sure they are created.
356     if (!FilePath::CreateDirectory(outputFilePath_.DirName())) {
357         return false;
358     }
359 
360     file_ = fopen(outputFilePath_.Value().c_str(), "wb");
361     if (file_ == nullptr) {
362         APP_LOGE("fopen %{private}s err: %{public}d %{public}s",
363             outputFilePath_.Value().c_str(), errno, strerror(errno));
364         return false;
365     }
366     return FilePath::PathIsValid(outputFilePath_);
367 }
368 
WriteBytes(const char * data,int numBytes)369 bool FilePathWriterDelegate::WriteBytes(const char *data, int numBytes)
370 {
371     if ((file_ == nullptr) || (numBytes <= 0) || (data == nullptr)) {
372         return false;
373     }
374     int writebytes = fwrite(data, 1, numBytes, file_);
375     bool ret = (numBytes == writebytes);
376     if (!ret) {
377         APP_LOGE("fwrite %{public}d %{public}d", numBytes, writebytes);
378     }
379     return ret;
380 }
381 
SetTimeModified(const struct tm * time)382 void FilePathWriterDelegate::SetTimeModified(const struct tm *time)
383 {
384     if (file_ != nullptr) {
385         fclose(file_);
386         file_ = nullptr;
387     }
388 }
389 
390 }  // namespace LIBZIP
391 }  // namespace AppExecFwk
392 }  // namespace OHOS