• 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_file.h"
17 
18 #include <ostream>
19 
20 #include "ability_base_log_wrapper.h"
21 #include "file_mapper.h"
22 #include "file_path_utils.h"
23 #include "securec.h"
24 #include "zip_file_reader.h"
25 #include "zlib.h"
26 
27 namespace OHOS {
28 namespace AbilityBase {
29 namespace {
30 constexpr uint32_t MAX_FILE_NAME = 4096;
31 constexpr uint32_t UNZIP_BUFFER_SIZE = 1024;
32 constexpr uint32_t UNZIP_BUF_IN_LEN = 160 * UNZIP_BUFFER_SIZE;   // in  buffer length: 160KB
33 constexpr uint32_t UNZIP_BUF_OUT_LEN = 320 * UNZIP_BUFFER_SIZE;  // out buffer length: 320KB
34 constexpr uint32_t LOCAL_HEADER_SIGNATURE = 0x04034b50;
35 constexpr uint32_t CENTRAL_SIGNATURE = 0x02014b50;
36 constexpr uint32_t EOCD_SIGNATURE = 0x06054b50;
37 constexpr uint32_t DATA_DESC_SIGNATURE = 0x08074b50;
38 constexpr uint32_t FLAG_DATA_DESC = 0x8;
39 constexpr uint8_t INFLATE_ERROR_TIMES = 5;
40 const char FILE_SEPARATOR_CHAR = '/';
41 }  // namespace
42 
ZipEntry(const CentralDirEntry & centralEntry)43 ZipEntry::ZipEntry(const CentralDirEntry &centralEntry)
44 {
45     compressionMethod = centralEntry.compressionMethod;
46     uncompressedSize = centralEntry.uncompressedSize;
47     compressedSize = centralEntry.compressedSize;
48     localHeaderOffset = centralEntry.localHeaderOffset;
49     crc = centralEntry.crc;
50     flags = centralEntry.flags;
51     modifiedTime = centralEntry.modifiedTime;
52     modifiedDate = centralEntry.modifiedDate;
53 }
54 
ZipFile(const std::string & pathName)55 ZipFile::ZipFile(const std::string &pathName) : pathName_(pathName)
56 {
57     dirRoot_ = std::make_shared<DirTreeNode>();
58 }
59 
~ZipFile()60 ZipFile::~ZipFile()
61 {
62     Close();
63 }
64 
SetContentLocation(const ZipPos start,const size_t length)65 void ZipFile::SetContentLocation(const ZipPos start, const size_t length)
66 {
67     if (isOpen_) {
68         ABILITYBASE_LOGE("file has been opened already.");
69         return;
70     }
71     fileStartPos_ = start;
72     fileLength_ = length;
73 }
74 
CheckEndDir(const EndDir & endDir) const75 bool ZipFile::CheckEndDir(const EndDir &endDir) const
76 {
77     size_t lenEndDir = sizeof(EndDir);
78     if ((endDir.numDisk != 0) || (endDir.signature != EOCD_SIGNATURE) || (endDir.startDiskOfCentralDir != 0) ||
79         (endDir.offset >= fileLength_) || (endDir.totalEntriesInThisDisk != endDir.totalEntries) ||
80         (endDir.commentLen != 0) ||
81         // central dir can't overlap end of central dir
82         ((endDir.offset + endDir.sizeOfCentralDir + lenEndDir) > fileLength_)) {
83         ABILITYBASE_LOGE("end dir format error");
84         return false;
85     }
86     return true;
87 }
88 
ParseEndDirectory()89 bool ZipFile::ParseEndDirectory()
90 {
91     size_t endDirLen = sizeof(EndDir);
92     size_t endFilePos = fileStartPos_ + fileLength_;
93 
94     if (fileLength_ <= endDirLen) {
95         ABILITYBASE_LOGE("parse EOCD file length(%{public}llu) <= end dir length(%{public}llu)",
96             fileStartPos_, fileLength_);
97         return false;
98     }
99 
100     size_t eocdPos = endFilePos - endDirLen;
101     if (!zipFileReader_->ReadBuffer(reinterpret_cast<uint8_t*>(&endDir_), eocdPos, sizeof(EndDir))) {
102         ABILITYBASE_LOGE("read EOCD struct failed.");
103         return false;
104     }
105 
106     centralDirPos_ = endDir_.offset + fileStartPos_;
107 
108     return CheckEndDir(endDir_);
109 }
110 
ParseOneEntry(uint8_t * & entryPtr)111 bool ZipFile::ParseOneEntry(uint8_t* &entryPtr)
112 {
113     if (entryPtr == nullptr) {
114         ABILITYBASE_LOGE("Input entryPtr is nullptr.");
115         return false;
116     }
117 
118     CentralDirEntry directoryEntry;
119     if (memcpy_s(&directoryEntry, sizeof(CentralDirEntry), entryPtr, sizeof(CentralDirEntry)) != EOK) {
120         ABILITYBASE_LOGE("Mem copy directory entry failed.");
121         return false;
122     }
123 
124     if (directoryEntry.signature != CENTRAL_SIGNATURE) {
125         ABILITYBASE_LOGE("parse entry, check signature failed");
126         return false;
127     }
128 
129     entryPtr += sizeof(CentralDirEntry);
130     size_t fileLength = (directoryEntry.nameSize >= MAX_FILE_NAME) ? (MAX_FILE_NAME - 1) : directoryEntry.nameSize;
131     std::string fileName(fileLength, 0);
132     if (memcpy_s(&(fileName[0]), fileLength, entryPtr, fileLength) != EOK) {
133         ABILITYBASE_LOGE("Mem copy file name failed.");
134         return false;
135     }
136 
137     ZipEntry currentEntry(directoryEntry);
138     currentEntry.fileName = fileName;
139     entriesMap_[fileName] = currentEntry;
140     AddEntryToTree(fileName);
141     entryPtr += directoryEntry.nameSize + directoryEntry.extraSize + directoryEntry.commentSize;
142     return true;
143 }
144 
AddEntryToTree(const std::string & fileName)145 void ZipFile::AddEntryToTree(const std::string &fileName)
146 {
147     size_t cur = 0;
148     auto parent = dirRoot_;
149     do {
150         while (cur < fileName.size() && fileName[cur] == FILE_SEPARATOR_CHAR) {
151             cur++;
152         }
153         if (cur >= fileName.size()) {
154             break;
155         }
156         auto next = fileName.find_first_of(FILE_SEPARATOR_CHAR, cur);
157         auto nodeName = fileName.substr(cur, next - cur);
158         auto it = parent->children.find(nodeName);
159         if (it != parent->children.end()) {
160             parent = it->second;
161         } else {
162             auto node = std::make_shared<DirTreeNode>();
163             parent->children.emplace(nodeName, node);
164             parent = node;
165         }
166         cur = next;
167     } while (cur != std::string::npos);
168 }
169 
ParseAllEntries()170 bool ZipFile::ParseAllEntries()
171 {
172     auto centralData = zipFileReader_->ReadBuffer(static_cast<size_t>(centralDirPos_),
173         static_cast<size_t>(endDir_.sizeOfCentralDir));
174     if (centralData.empty()) {
175         ABILITYBASE_LOGE("read central data for [%{public}s] failed.", pathName_.c_str());
176         return false;
177     }
178 
179     bool ret = true;
180     uint8_t *entryPtr = reinterpret_cast<uint8_t *>(centralData.data());
181     for (uint16_t i = 0; i < endDir_.totalEntries; i++) {
182         if (!ParseOneEntry(entryPtr)) {
183             ABILITYBASE_LOGE("Parse entry[%{public}d] failed.", i);
184             ret = false;
185             break;
186         }
187     }
188 
189     return ret;
190 }
191 
Open()192 bool ZipFile::Open()
193 {
194     if (isOpen_) {
195         ABILITYBASE_LOGE("has already opened");
196         return true;
197     }
198 
199     if (pathName_.length() > PATH_MAX) {
200         ABILITYBASE_LOGE("path length(%{public}u) longer than max path length(%{public}d)",
201             static_cast<unsigned int>(pathName_.length()), PATH_MAX);
202         return false;
203     }
204     std::string realPath;
205     realPath.reserve(PATH_MAX);
206     realPath.resize(PATH_MAX - 1);
207     if (realpath(pathName_.c_str(), &(realPath[0])) == nullptr) {
208         ABILITYBASE_LOGE("transform real path error: %{public}d, pathName: %{public}s", errno, pathName_.c_str());
209         return false;
210     }
211 
212     zipFileReader_ = ZipFileReader::CreateZipFileReader(realPath);
213     if (!zipFileReader_) {
214         ABILITYBASE_LOGE("open file(%{public}s) failed", pathName_.c_str());
215         return false;
216     }
217 
218     if (fileLength_ == 0) {
219         auto fileLength = zipFileReader_->GetFileLen();
220         fileLength_ = static_cast<ZipPos>(fileLength);
221         if (fileStartPos_ >= fileLength_) {
222             ABILITYBASE_LOGE("open start pos > length failed");
223             zipFileReader_.reset();
224             return false;
225         }
226 
227         fileLength_ -= fileStartPos_;
228     }
229 
230     bool result = ParseEndDirectory();
231     if (result) {
232         result = ParseAllEntries();
233     }
234     // it means open file success.
235     isOpen_ = true;
236     return result;
237 }
238 
Close()239 void ZipFile::Close()
240 {
241     if (!isOpen_ || zipFileReader_ == nullptr) {
242         ABILITYBASE_LOGW("file is not opened");
243         return;
244     }
245 
246     isOpen_ = false;
247     entriesMap_.clear();
248     dirRoot_->children.clear();
249     pathName_ = "";
250 
251     zipFileReader_.reset();
252 }
253 
254 // Get all file zipEntry in this file
GetAllEntries() const255 const ZipEntryMap &ZipFile::GetAllEntries() const
256 {
257     return entriesMap_;
258 }
259 
HasEntry(const std::string & entryName) const260 bool ZipFile::HasEntry(const std::string &entryName) const
261 {
262     return entriesMap_.find(entryName) != entriesMap_.end();
263 }
264 
IsDirExist(const std::string & dir) const265 bool ZipFile::IsDirExist(const std::string &dir) const
266 {
267     if (dir.empty()) {
268         ABILITYBASE_LOGE("target dir is empty");
269         return false;
270     }
271 
272     size_t cur = 0;
273     auto parent = dirRoot_;
274     do {
275         while (cur < dir.size() && dir[cur] == FILE_SEPARATOR_CHAR) {
276             cur++;
277         }
278         if (cur >= dir.size()) {
279             break;
280         }
281         auto next = dir.find_first_of(FILE_SEPARATOR_CHAR, cur);
282         auto nodeName = dir.substr(cur, next - cur);
283         auto it = parent->children.find(nodeName);
284         if (it == parent->children.end()) {
285             ABILITYBASE_LOGI("target dir not found, dir : %{public}s", dir.c_str());
286             return false;
287         }
288         parent = it->second;
289         cur = next;
290     } while (cur != std::string::npos);
291 
292     return true;
293 }
294 namespace {
GetTreeFileList(const std::shared_ptr<DirTreeNode> & root,const std::string & rootPath,std::vector<std::string> & assetList)295 void GetTreeFileList(const std::shared_ptr<DirTreeNode> &root, const std::string &rootPath,
296     std::vector<std::string> &assetList)
297 {
298     if (root->children.empty()) {
299         assetList.push_back(rootPath);
300     } else {
301         for (const auto &child : root->children) {
302             GetTreeFileList(child.second, rootPath + "/" + child.first, assetList);
303         }
304     }
305 }
306 }
307 
GetAllFileList(const std::string & srcPath,std::vector<std::string> & assetList)308 void ZipFile::GetAllFileList(const std::string &srcPath, std::vector<std::string> &assetList)
309 {
310     if (srcPath.empty()) {
311         ABILITYBASE_LOGE("target dir is empty");
312         return;
313     }
314 
315     auto rootName = srcPath.back() == FILE_SEPARATOR_CHAR ?
316         srcPath.substr(0, srcPath.length() - 1) : srcPath;
317 
318     size_t cur = 0;
319     auto parent = dirRoot_;
320     do {
321         while (cur < rootName.size() && rootName[cur] == FILE_SEPARATOR_CHAR) {
322             cur++;
323         }
324         if (cur >= rootName.size()) {
325             break;
326         }
327         auto next = rootName.find_first_of(FILE_SEPARATOR_CHAR, cur);
328         auto nodeName = rootName.substr(cur, next - cur);
329         auto it = parent->children.find(nodeName);
330         if (it == parent->children.end()) {
331             ABILITYBASE_LOGI("target srcPath not found: %{public}s", rootName.c_str());
332             return;
333         }
334         parent = it->second;
335         cur = next;
336     } while (cur != std::string::npos);
337 
338     GetTreeFileList(parent, rootName, assetList);
339 }
340 
GetChildNames(const std::string & srcPath,std::set<std::string> & fileSet)341 void ZipFile::GetChildNames(const std::string &srcPath, std::set<std::string> &fileSet)
342 {
343     if (srcPath.empty()) {
344         ABILITYBASE_LOGE("target dir is empty");
345         return;
346     }
347 
348     size_t cur = 0;
349     auto parent = dirRoot_;
350     do {
351         while (cur < srcPath.size() && srcPath[cur] == FILE_SEPARATOR_CHAR) {
352             cur++;
353         }
354         if (cur >= srcPath.size()) {
355             break;
356         }
357         auto next = srcPath.find_first_of(FILE_SEPARATOR_CHAR, cur);
358         auto nodeName = srcPath.substr(cur, next - cur);
359         auto it = parent->children.find(nodeName);
360         if (it == parent->children.end()) {
361             ABILITYBASE_LOGI("target srcPath not found: %{public}s", srcPath.c_str());
362             return;
363         }
364         parent = it->second;
365         cur = next;
366     } while (cur != std::string::npos);
367 
368     for (const auto &child : parent->children) {
369         fileSet.insert(child.first);
370     }
371 }
372 
GetEntry(const std::string & entryName,ZipEntry & resultEntry) const373 bool ZipFile::GetEntry(const std::string &entryName, ZipEntry &resultEntry) const
374 {
375     auto iter = entriesMap_.find(entryName);
376     if (iter != entriesMap_.end()) {
377         resultEntry = iter->second;
378         return true;
379     }
380     ABILITYBASE_LOGW("get entry %{public}s failed", entryName.c_str());
381     return false;
382 }
383 
GetLocalHeaderSize(const uint16_t nameSize,const uint16_t extraSize) const384 size_t ZipFile::GetLocalHeaderSize(const uint16_t nameSize, const uint16_t extraSize) const
385 {
386     return sizeof(LocalHeader) + nameSize + extraSize;
387 }
388 
CheckDataDesc(const ZipEntry & zipEntry,const LocalHeader & localHeader) const389 bool ZipFile::CheckDataDesc(const ZipEntry &zipEntry, const LocalHeader &localHeader) const
390 {
391     uint32_t crcLocal = 0;
392     uint32_t compressedLocal = 0;
393     uint32_t uncompressedLocal = 0;
394 
395     if (localHeader.flags & FLAG_DATA_DESC) {  // use data desc
396         DataDesc dataDesc;
397         auto descPos = zipEntry.localHeaderOffset + GetLocalHeaderSize(localHeader.nameSize, localHeader.extraSize);
398         descPos += fileStartPos_ + zipEntry.compressedSize;
399 
400         if (!zipFileReader_->ReadBuffer(reinterpret_cast<uint8_t*>(&dataDesc), descPos, sizeof(DataDesc))) {
401             ABILITYBASE_LOGE("check local header read datadesc failed");
402             return false;
403         }
404 
405         if (dataDesc.signature != DATA_DESC_SIGNATURE) {
406             ABILITYBASE_LOGE("check local header check datadesc signature failed");
407             return false;
408         }
409 
410         crcLocal = dataDesc.crc;
411         compressedLocal = dataDesc.compressedSize;
412         uncompressedLocal = dataDesc.uncompressedSize;
413     } else {
414         crcLocal = localHeader.crc;
415         compressedLocal = localHeader.compressedSize;
416         uncompressedLocal = localHeader.uncompressedSize;
417     }
418 
419     if ((zipEntry.crc != crcLocal) || (zipEntry.compressedSize != compressedLocal) ||
420         (zipEntry.uncompressedSize != uncompressedLocal)) {
421         ABILITYBASE_LOGE("check local header compressed size corrupted");
422         return false;
423     }
424 
425     return true;
426 }
427 
CheckCoherencyLocalHeader(const ZipEntry & zipEntry,uint16_t & extraSize) const428 bool ZipFile::CheckCoherencyLocalHeader(const ZipEntry &zipEntry, uint16_t &extraSize) const
429 {
430     // current only support store and Z_DEFLATED method
431     if ((zipEntry.compressionMethod != Z_DEFLATED) && (zipEntry.compressionMethod != 0)) {
432         ABILITYBASE_LOGE("check local header compressionMethod(%{public}d) not support", zipEntry.compressionMethod);
433         return false;
434     }
435 
436     auto nameSize = zipEntry.fileName.length();
437     auto startPos = fileStartPos_ + zipEntry.localHeaderOffset;
438     size_t buffSize = sizeof(LocalHeader) + nameSize;
439     auto buff = zipFileReader_->ReadBuffer(startPos, buffSize);
440     if (buff.size() < buffSize) {
441         ABILITYBASE_LOGE("read header failed");
442         return false;
443     }
444 
445     LocalHeader localHeader = {0};
446     if (memcpy_s(&localHeader, sizeof(LocalHeader), buff.data(), sizeof(LocalHeader)) != EOK) {
447         ABILITYBASE_LOGE("memcpy localheader failed");
448         return false;
449     }
450     if ((localHeader.signature != LOCAL_HEADER_SIGNATURE) ||
451         (zipEntry.compressionMethod != localHeader.compressionMethod)) {
452         ABILITYBASE_LOGE("check local header signature or compressionMethod failed");
453         return false;
454     }
455 
456     if (localHeader.nameSize != nameSize && nameSize < MAX_FILE_NAME - 1) {
457         ABILITYBASE_LOGE("check local header file name corrupted");
458         return false;
459     }
460     std::string fileName = buff.substr(sizeof(LocalHeader));
461     if (zipEntry.fileName != fileName) {
462         ABILITYBASE_LOGE("check local header file name corrupted");
463         return false;
464     }
465 
466     if (!CheckDataDesc(zipEntry, localHeader)) {
467         ABILITYBASE_LOGE("check data desc failed");
468         return false;
469     }
470 
471     extraSize = localHeader.extraSize;
472     return true;
473 }
474 
GetEntryStart(const ZipEntry & zipEntry,const uint16_t extraSize) const475 size_t ZipFile::GetEntryStart(const ZipEntry &zipEntry, const uint16_t extraSize) const
476 {
477     ZipPos startOffset = zipEntry.localHeaderOffset;
478     // get data offset, add signature+localheader+namesize+extrasize
479     startOffset += GetLocalHeaderSize(zipEntry.fileName.length(), extraSize);
480     startOffset += fileStartPos_;  // add file start relative to file stream
481 
482     return startOffset;
483 }
484 
UnzipWithStore(const ZipEntry & zipEntry,const uint16_t extraSize,std::ostream & dest) const485 bool ZipFile::UnzipWithStore(const ZipEntry &zipEntry, const uint16_t extraSize, std::ostream &dest) const
486 {
487     auto startPos = GetEntryStart(zipEntry, extraSize);
488     uint32_t remainSize = zipEntry.compressedSize;
489     while (remainSize > 0) {
490         size_t readLen = (remainSize > UNZIP_BUF_OUT_LEN) ? UNZIP_BUF_OUT_LEN : remainSize;
491         std::string readBuffer = zipFileReader_->ReadBuffer(startPos, readLen);
492         if (readBuffer.empty()) {
493             ABILITYBASE_LOGE("unzip store read failed, error");
494             return false;
495         }
496         remainSize -= readLen;
497         startPos += readLen;
498         dest.write(readBuffer.data(), readBuffer.length());
499     }
500 
501     return true;
502 }
503 
InitZStream(z_stream & zstream) const504 bool ZipFile::InitZStream(z_stream &zstream) const
505 {
506     // init zlib stream
507     if (memset_s(&zstream, sizeof(z_stream), 0, sizeof(z_stream))) {
508         ABILITYBASE_LOGE("unzip stream buffer init failed");
509         return false;
510     }
511     int32_t zlibErr = inflateInit2(&zstream, -MAX_WBITS);
512     if (zlibErr != Z_OK) {
513         ABILITYBASE_LOGE("unzip inflated init failed");
514         return false;
515     }
516 
517     BytePtr bufOut = new (std::nothrow) Byte[UNZIP_BUF_OUT_LEN];
518     if (bufOut == nullptr) {
519         ABILITYBASE_LOGE("unzip inflated new out buffer failed");
520         return false;
521     }
522 
523     BytePtr bufIn = new (std::nothrow) Byte[UNZIP_BUF_IN_LEN];
524     if (bufIn == nullptr) {
525         ABILITYBASE_LOGE("unzip inflated new in buffer failed");
526         delete[] bufOut;
527         return false;
528     }
529     zstream.next_out = bufOut;
530     zstream.next_in = bufIn;
531     zstream.avail_out = UNZIP_BUF_OUT_LEN;
532     return true;
533 }
534 
ReadZStream(const BytePtr & buffer,z_stream & zstream,uint32_t & remainCompressedSize,size_t & startPos) const535 bool ZipFile::ReadZStream(const BytePtr &buffer, z_stream &zstream, uint32_t &remainCompressedSize,
536     size_t &startPos) const
537 {
538     if (zstream.avail_in == 0) {
539         size_t remainBytes = (remainCompressedSize > UNZIP_BUF_IN_LEN) ? UNZIP_BUF_IN_LEN : remainCompressedSize;
540         if (!zipFileReader_->ReadBuffer(buffer, startPos, remainBytes)) {
541             ABILITYBASE_LOGE("unzip inflated read failed, error");
542             return false;
543         }
544 
545         remainCompressedSize -= remainBytes;
546         startPos += remainBytes;
547         zstream.avail_in = remainBytes;
548         zstream.next_in = buffer;
549     }
550     return true;
551 }
552 
UnzipWithInflated(const ZipEntry & zipEntry,const uint16_t extraSize,std::ostream & dest) const553 bool ZipFile::UnzipWithInflated(const ZipEntry &zipEntry, const uint16_t extraSize, std::ostream &dest) const
554 {
555     z_stream zstream;
556     if (!InitZStream(zstream)) {
557         return false;
558     }
559 
560     auto startPos = GetEntryStart(zipEntry, extraSize);
561 
562     BytePtr bufIn = zstream.next_in;
563     BytePtr bufOut = zstream.next_out;
564 
565     bool ret = true;
566     int32_t zlibErr = Z_OK;
567     uint32_t remainCompressedSize = zipEntry.compressedSize;
568     size_t inflateLen = 0;
569     uint8_t errorTimes = 0;
570     while ((remainCompressedSize > 0) || (zstream.avail_in > 0)) {
571         if (!ReadZStream(bufIn, zstream, remainCompressedSize, startPos)) {
572             ret = false;
573             break;
574         }
575 
576         zlibErr = inflate(&zstream, Z_SYNC_FLUSH);
577         if ((zlibErr >= Z_OK) && (zstream.msg != nullptr)) {
578             ABILITYBASE_LOGE("unzip inflated inflate, error: %{public}d, err msg: %{public}s", zlibErr, zstream.msg);
579             ret = false;
580             break;
581         }
582 
583         inflateLen = UNZIP_BUF_OUT_LEN - zstream.avail_out;
584         if (inflateLen > 0) {
585             dest.write((const char *)bufOut, inflateLen);
586             zstream.next_out = bufOut;
587             zstream.avail_out = UNZIP_BUF_OUT_LEN;
588             errorTimes = 0;
589         } else {
590             errorTimes++;
591         }
592         if (errorTimes >= INFLATE_ERROR_TIMES) {
593             ABILITYBASE_LOGE("unzip inflated data is abnormal!");
594             ret = false;
595             break;
596         }
597     }
598 
599     // free all dynamically allocated data structures except the next_in and next_out for this stream.
600     zlibErr = inflateEnd(&zstream);
601     if (zlibErr != Z_OK) {
602         ABILITYBASE_LOGE("unzip inflateEnd error, error: %{public}d", zlibErr);
603         ret = false;
604     }
605 
606     delete[] bufOut;
607     delete[] bufIn;
608     return ret;
609 }
610 
GetEntryDataOffset(const ZipEntry & zipEntry,const uint16_t extraSize) const611 ZipPos ZipFile::GetEntryDataOffset(const ZipEntry &zipEntry, const uint16_t extraSize) const
612 {
613     // get entry data offset relative file
614     ZipPos offset = zipEntry.localHeaderOffset;
615 
616     offset += GetLocalHeaderSize(zipEntry.fileName.length(), extraSize);
617     offset += fileStartPos_;
618 
619     return offset;
620 }
621 
GetDataOffsetRelative(const std::string & file,ZipPos & offset,uint32_t & length) const622 bool ZipFile::GetDataOffsetRelative(const std::string &file, ZipPos &offset, uint32_t &length) const
623 {
624     ZipEntry zipEntry;
625     if (!GetEntry(file, zipEntry)) {
626         ABILITYBASE_LOGE("extract file: not find file");
627         return false;
628     }
629 
630     return GetDataOffsetRelative(zipEntry, offset, length);
631 }
632 
GetDataOffsetRelative(const ZipEntry & zipEntry,ZipPos & offset,uint32_t & length) const633 bool ZipFile::GetDataOffsetRelative(const ZipEntry &zipEntry, ZipPos &offset, uint32_t &length) const
634 {
635     uint16_t extraSize = 0;
636     if (!CheckCoherencyLocalHeader(zipEntry, extraSize)) {
637         ABILITYBASE_LOGE("check coherency local header failed");
638         return false;
639     }
640 
641     offset = GetEntryDataOffset(zipEntry, extraSize);
642     length = zipEntry.compressedSize;
643     return true;
644 }
645 
ExtractFile(const std::string & file,std::ostream & dest) const646 bool ZipFile::ExtractFile(const std::string &file, std::ostream &dest) const
647 {
648     ZipEntry zipEntry;
649     if (!GetEntry(file, zipEntry)) {
650         ABILITYBASE_LOGE("extract file: not find file");
651         return false;
652     }
653 
654     uint16_t extraSize = 0;
655     if (!CheckCoherencyLocalHeader(zipEntry, extraSize)) {
656         ABILITYBASE_LOGE("check coherency local header failed");
657         return false;
658     }
659 
660     bool ret = true;
661     if (zipEntry.compressionMethod == 0) {
662         ret = UnzipWithStore(zipEntry, extraSize, dest);
663     } else {
664         ret = UnzipWithInflated(zipEntry, extraSize, dest);
665     }
666 
667     return ret;
668 }
669 
ExtractFileFromMMap(const std::string & file,void * mmapDataPtr,std::unique_ptr<uint8_t[]> & dataPtr,size_t & len) const670 bool ZipFile::ExtractFileFromMMap(const std::string &file, void *mmapDataPtr,
671     std::unique_ptr<uint8_t[]> &dataPtr, size_t &len) const
672 {
673     ABILITYBASE_LOGI("ExtractFileFromMMap %{public}s.", file.c_str());
674     ZipEntry zipEntry;
675     if (!GetEntry(file, zipEntry)) {
676         ABILITYBASE_LOGE("extract file: not find file");
677         return false;
678     }
679 
680     if (!zipEntry.compressionMethod) {
681         ABILITYBASE_LOGE("file[%{public}s] is not extracted.", file.c_str());
682         return false;
683     }
684 
685     uint16_t extraSize = 0;
686     if (!CheckCoherencyLocalHeader(zipEntry, extraSize)) {
687         ABILITYBASE_LOGE("check coherency local header failed");
688         return false;
689     }
690 
691     bool ret = false;
692     ret = UnzipWithInflatedFromMMap(zipEntry, extraSize, mmapDataPtr, dataPtr, len);
693 
694     return ret;
695 }
696 
UnzipWithInflatedFromMMap(const ZipEntry & zipEntry,const uint16_t extraSize,void * mmapDataPtr,std::unique_ptr<uint8_t[]> & dataPtr,size_t & len) const697 bool ZipFile::UnzipWithInflatedFromMMap(const ZipEntry &zipEntry, const uint16_t extraSize,
698     void *mmapDataPtr, std::unique_ptr<uint8_t[]> &dataPtr, size_t &len) const
699 {
700     z_stream zstream;
701     if (!InitZStream(zstream)) {
702         ABILITYBASE_LOGE("Init zstream failed.");
703         return false;
704     }
705 
706     BytePtr bufIn = zstream.next_in;
707     BytePtr bufOut = zstream.next_out;
708 
709     bool ret = true;
710     int32_t zlibErr = Z_OK;
711     uint32_t remainCompressedSize = zipEntry.compressedSize;
712     size_t inflateLen = 0;
713     uint8_t errorTimes = 0;
714 
715     len = zipEntry.uncompressedSize;
716     dataPtr = std::make_unique<uint8_t[]>(len);
717     if (!dataPtr) {
718         ABILITYBASE_LOGE("Make unique ptr failed.");
719         return false;
720     }
721     uint8_t *dstDataPtr = static_cast<uint8_t *>(dataPtr.get());
722     void *mmapSrcDataPtr = mmapDataPtr;
723 
724     while ((remainCompressedSize > 0) || (zstream.avail_in > 0)) {
725         if (!ReadZStreamFromMMap(bufIn, mmapSrcDataPtr, zstream, remainCompressedSize)) {
726             ret = false;
727             break;
728         }
729 
730         zlibErr = inflate(&zstream, Z_SYNC_FLUSH);
731         if ((zlibErr >= Z_OK) && (zstream.msg != nullptr)) {
732             ABILITYBASE_LOGE("unzip inflated inflate, error: %{public}d, err msg: %{public}s", zlibErr, zstream.msg);
733             ret = false;
734             break;
735         }
736 
737         inflateLen = UNZIP_BUF_OUT_LEN - zstream.avail_out;
738         if (inflateLen > 0) {
739             if (memcpy_s(dstDataPtr, inflateLen, bufOut, inflateLen) != EOK) {
740                 ret = false;
741                 ABILITYBASE_LOGE("Mem copy failed!");
742                 break;
743             }
744 
745             dstDataPtr += inflateLen;
746             zstream.next_out = bufOut;
747             zstream.avail_out = UNZIP_BUF_OUT_LEN;
748             errorTimes = 0;
749         } else {
750             errorTimes++;
751         }
752         if (errorTimes >= INFLATE_ERROR_TIMES) {
753             ABILITYBASE_LOGE("unzip inflated data is abnormal!");
754             ret = false;
755             break;
756         }
757     }
758 
759     // free all dynamically allocated data structures except the next_in and next_out for this stream.
760     zlibErr = inflateEnd(&zstream);
761     if (zlibErr != Z_OK) {
762         ABILITYBASE_LOGE("unzip inflateEnd error, error: %{public}d", zlibErr);
763         ret = false;
764     }
765 
766     delete[] bufOut;
767     delete[] bufIn;
768     return ret;
769 }
770 
ReadZStreamFromMMap(const BytePtr & buffer,void * & dataPtr,z_stream & zstream,uint32_t & remainCompressedSize) const771 bool ZipFile::ReadZStreamFromMMap(const BytePtr &buffer, void* &dataPtr,
772     z_stream &zstream, uint32_t &remainCompressedSize) const
773 {
774     if (!dataPtr) {
775         ABILITYBASE_LOGE("Input dataPtr is nullptr.");
776         return false;
777     }
778 
779     uint8_t *srcDataPtr = static_cast<uint8_t *>(dataPtr);
780     if (zstream.avail_in == 0) {
781         size_t remainBytes = (remainCompressedSize > UNZIP_BUF_IN_LEN) ? UNZIP_BUF_IN_LEN : remainCompressedSize;
782         size_t readBytes = sizeof(Byte) * remainBytes;
783         if (memcpy_s(buffer, readBytes, srcDataPtr, readBytes) != EOK) {
784             ABILITYBASE_LOGE("Mem copy failed.");
785             return false;
786         }
787         srcDataPtr += readBytes;
788         remainCompressedSize -= remainBytes;
789         zstream.avail_in = remainBytes;
790         zstream.next_in = buffer;
791     }
792     dataPtr = srcDataPtr;
793     return true;
794 }
795 
CreateFileMapper(const std::string & fileName,FileMapperType type) const796 std::unique_ptr<FileMapper> ZipFile::CreateFileMapper(const std::string &fileName, FileMapperType type) const
797 {
798     ZipEntry zipEntry;
799     if (!GetEntry(fileName, zipEntry)) {
800         ABILITYBASE_LOGE("GetEntry failed hapPath %{public}s.", fileName.c_str());
801         return nullptr;
802     }
803 
804     ZipPos offset = 0;
805     uint32_t length = 0;
806     if (!GetDataOffsetRelative(zipEntry, offset, length)) {
807         ABILITYBASE_LOGE("GetDataOffsetRelative failed hapPath %{public}s.", fileName.c_str());
808         return nullptr;
809     }
810     bool compress = zipEntry.compressionMethod > 0;
811     if (type == FileMapperType::SAFE_ABC && compress) {
812         ABILITYBASE_LOGW("Entry is compressed for safe: %{public}s.", fileName.c_str());
813     }
814     std::unique_ptr<FileMapper> fileMapper = std::make_unique<FileMapper>();
815     auto result = false;
816     if (type == FileMapperType::NORMAL_MEM) {
817         result = fileMapper->CreateFileMapper(zipFileReader_, fileName, offset, length, compress);
818     } else {
819         result = fileMapper->CreateFileMapper(fileName, compress, zipFileReader_->GetFd(), offset, length, type);
820         if (result && type == FileMapperType::SAFE_ABC) {
821             zipFileReader_->SetClosable(false);
822         }
823     }
824 
825     if (!result) {
826         return nullptr;
827     }
828     return fileMapper;
829 }
830 
ExtractToBufByName(const std::string & fileName,std::unique_ptr<uint8_t[]> & dataPtr,size_t & len) const831 bool ZipFile::ExtractToBufByName(const std::string &fileName, std::unique_ptr<uint8_t[]> &dataPtr,
832     size_t &len) const
833 {
834     ZipEntry zipEntry;
835     if (!GetEntry(fileName, zipEntry)) {
836         ABILITYBASE_LOGE("GetEntry failed hapPath %{public}s.", fileName.c_str());
837         return false;
838     }
839     uint16_t extraSize = 0;
840     if (!CheckCoherencyLocalHeader(zipEntry, extraSize)) {
841         ABILITYBASE_LOGE("check coherency local header failed");
842         return false;
843     }
844 
845     ZipPos offset = GetEntryDataOffset(zipEntry, extraSize);
846     uint32_t length = zipEntry.compressedSize;
847     auto dataTmp = std::make_unique<uint8_t[]>(length);
848     if (!zipFileReader_->ReadBuffer(dataTmp.get(), offset, length)) {
849         ABILITYBASE_LOGE("read file failed, len[%{public}zu]. fileName: %{public}s, offset: %{public}zu",
850             len, fileName.c_str(), (size_t)offset);
851         dataTmp.reset();
852         return false;
853     }
854 
855     if (zipEntry.compressionMethod > 0) {
856         return UnzipWithInflatedFromMMap(zipEntry, extraSize, dataTmp.get(), dataPtr, len);
857     }
858 
859     len = length;
860     dataPtr = std::move(dataTmp);
861 
862     return true;
863 }
864 }  // namespace AbilityBase
865 }  // namespace OHOS
866