• 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 constexpr uint8_t MAP_FILE_SUFFIX = 4;
41 const char FILE_SEPARATOR_CHAR = '/';
42 }  // namespace
43 
ZipEntry(const CentralDirEntry & centralEntry)44 ZipEntry::ZipEntry(const CentralDirEntry &centralEntry)
45 {
46     compressionMethod = centralEntry.compressionMethod;
47     uncompressedSize = centralEntry.uncompressedSize;
48     compressedSize = centralEntry.compressedSize;
49     localHeaderOffset = centralEntry.localHeaderOffset;
50     crc = centralEntry.crc;
51     flags = centralEntry.flags;
52     modifiedTime = centralEntry.modifiedTime;
53     modifiedDate = centralEntry.modifiedDate;
54 }
55 
ZipFile(const std::string & pathName)56 ZipFile::ZipFile(const std::string &pathName) : pathName_(pathName)
57 {
58     dirRoot_ = std::make_shared<DirTreeNode>();
59 }
60 
~ZipFile()61 ZipFile::~ZipFile()
62 {
63     Close();
64 }
65 
SetContentLocation(const ZipPos start,const size_t length)66 void ZipFile::SetContentLocation(const ZipPos start, const size_t length)
67 {
68     if (isOpen_) {
69         ABILITYBASE_LOGE("file has been opened already.");
70         return;
71     }
72     fileStartPos_ = start;
73     fileLength_ = length;
74 }
75 
CheckEndDir(const EndDir & endDir) const76 bool ZipFile::CheckEndDir(const EndDir &endDir) const
77 {
78     size_t lenEndDir = sizeof(EndDir);
79     if ((endDir.numDisk != 0) || (endDir.signature != EOCD_SIGNATURE) || (endDir.startDiskOfCentralDir != 0) ||
80         (endDir.offset >= fileLength_) || (endDir.totalEntriesInThisDisk != endDir.totalEntries) ||
81         (endDir.commentLen != 0) ||
82         // central dir can't overlap end of central dir
83         ((endDir.offset + endDir.sizeOfCentralDir + lenEndDir) > fileLength_)) {
84         ABILITYBASE_LOGE("end dir format error");
85         return false;
86     }
87     return true;
88 }
89 
ParseEndDirectory()90 bool ZipFile::ParseEndDirectory()
91 {
92     size_t endDirLen = sizeof(EndDir);
93     size_t endFilePos = fileStartPos_ + fileLength_;
94 
95     if (fileLength_ <= endDirLen) {
96         ABILITYBASE_LOGE("parse EOCD file length(%{public}llu) <= end dir length(%{public}llu)",
97             fileStartPos_, fileLength_);
98         return false;
99     }
100 
101     size_t eocdPos = endFilePos - endDirLen;
102     if (!zipFileReader_->ReadBuffer(reinterpret_cast<uint8_t*>(&endDir_), eocdPos, sizeof(EndDir))) {
103         ABILITYBASE_LOGE("read EOCD struct failed.");
104         return false;
105     }
106 
107     centralDirPos_ = endDir_.offset + fileStartPos_;
108 
109     return CheckEndDir(endDir_);
110 }
111 
ParseOneEntry(uint8_t * & entryPtr)112 bool ZipFile::ParseOneEntry(uint8_t* &entryPtr)
113 {
114     if (entryPtr == nullptr) {
115         ABILITYBASE_LOGE("Input entryPtr is nullptr.");
116         return false;
117     }
118 
119     CentralDirEntry directoryEntry;
120     if (memcpy_s(&directoryEntry, sizeof(CentralDirEntry), entryPtr, sizeof(CentralDirEntry)) != EOK) {
121         ABILITYBASE_LOGE("Mem copy directory entry failed.");
122         return false;
123     }
124 
125     if (directoryEntry.signature != CENTRAL_SIGNATURE) {
126         ABILITYBASE_LOGE("parse entry, check signature failed");
127         return false;
128     }
129 
130     entryPtr += sizeof(CentralDirEntry);
131     size_t fileLength = (directoryEntry.nameSize >= MAX_FILE_NAME) ? (MAX_FILE_NAME - 1) : directoryEntry.nameSize;
132     std::string fileName(fileLength, 0);
133     if (memcpy_s(&(fileName[0]), fileLength, entryPtr, fileLength) != EOK) {
134         ABILITYBASE_LOGE("Mem copy file name failed.");
135         return false;
136     }
137 
138     ZipEntry currentEntry(directoryEntry);
139     currentEntry.fileName = fileName;
140     entriesMap_[fileName] = currentEntry;
141     AddEntryToTree(fileName);
142     entryPtr += directoryEntry.nameSize + directoryEntry.extraSize + directoryEntry.commentSize;
143     return true;
144 }
145 
AddEntryToTree(const std::string & fileName)146 void ZipFile::AddEntryToTree(const std::string &fileName)
147 {
148     size_t cur = 0;
149     auto parent = dirRoot_;
150     do {
151         while (cur < fileName.size() && fileName[cur] == FILE_SEPARATOR_CHAR) {
152             cur++;
153         }
154         if (cur >= fileName.size()) {
155             break;
156         }
157         auto next = fileName.find_first_of(FILE_SEPARATOR_CHAR, cur);
158         auto nodeName = fileName.substr(cur, next - cur);
159         auto it = parent->children.find(nodeName);
160         if (it != parent->children.end()) {
161             parent = it->second;
162         } else {
163             auto node = std::make_shared<DirTreeNode>();
164             parent->children.emplace(nodeName, node);
165             parent = node;
166         }
167         cur = next;
168     } while (cur != std::string::npos);
169 }
170 
ParseAllEntries()171 bool ZipFile::ParseAllEntries()
172 {
173     auto centralData = zipFileReader_->ReadBuffer(static_cast<size_t>(centralDirPos_),
174         static_cast<size_t>(endDir_.sizeOfCentralDir));
175     if (centralData.empty()) {
176         ABILITYBASE_LOGE("read central data for [%{public}s] failed.", pathName_.c_str());
177         return false;
178     }
179 
180     bool ret = true;
181     uint8_t *entryPtr = reinterpret_cast<uint8_t *>(centralData.data());
182     for (uint16_t i = 0; i < endDir_.totalEntries; i++) {
183         if (!ParseOneEntry(entryPtr)) {
184             ABILITYBASE_LOGE("Parse entry[%{public}d] failed.", i);
185             ret = false;
186             break;
187         }
188     }
189 
190     return ret;
191 }
192 
Open()193 bool ZipFile::Open()
194 {
195     if (isOpen_) {
196         ABILITYBASE_LOGE("has already opened");
197         return true;
198     }
199 
200     if (pathName_.length() > PATH_MAX) {
201         ABILITYBASE_LOGE("path length(%{public}u) longer than max path length(%{public}d)",
202             static_cast<unsigned int>(pathName_.length()), PATH_MAX);
203         return false;
204     }
205     std::string realPath;
206     realPath.reserve(PATH_MAX);
207     realPath.resize(PATH_MAX - 1);
208     if (realpath(pathName_.c_str(), &(realPath[0])) == nullptr) {
209         ABILITYBASE_LOGE("transform real path error: %{public}d, pathName: %{public}s", errno, pathName_.c_str());
210         return false;
211     }
212 
213     zipFileReader_ = ZipFileReader::CreateZipFileReader(realPath);
214     if (!zipFileReader_) {
215         ABILITYBASE_LOGE("open file(%{public}s) failed", pathName_.c_str());
216         return false;
217     }
218 
219     if (fileLength_ == 0) {
220         auto fileLength = zipFileReader_->GetFileLen();
221         fileLength_ = static_cast<ZipPos>(fileLength);
222         if (fileStartPos_ >= fileLength_) {
223             ABILITYBASE_LOGE("open start pos > length failed");
224             zipFileReader_.reset();
225             return false;
226         }
227 
228         fileLength_ -= fileStartPos_;
229     }
230 
231     bool result = ParseEndDirectory();
232     if (result) {
233         result = ParseAllEntries();
234     }
235     // it means open file success.
236     isOpen_ = true;
237     return result;
238 }
239 
Close()240 void ZipFile::Close()
241 {
242     if (!isOpen_ || zipFileReader_ == nullptr) {
243         ABILITYBASE_LOGD("file is not opened");
244         return;
245     }
246 
247     isOpen_ = false;
248     entriesMap_.clear();
249     dirRoot_->children.clear();
250     pathName_ = "";
251 
252     zipFileReader_.reset();
253 }
254 
255 // Get all file zipEntry in this file
GetAllEntries() const256 const ZipEntryMap &ZipFile::GetAllEntries() const
257 {
258     return entriesMap_;
259 }
260 
HasEntry(const std::string & entryName) const261 bool ZipFile::HasEntry(const std::string &entryName) const
262 {
263     return entriesMap_.find(entryName) != entriesMap_.end();
264 }
265 
IsDirExist(const std::string & dir) const266 bool ZipFile::IsDirExist(const std::string &dir) const
267 {
268     if (dir.empty()) {
269         ABILITYBASE_LOGE("target dir is empty");
270         return false;
271     }
272 
273     size_t cur = 0;
274     auto parent = dirRoot_;
275     do {
276         while (cur < dir.size() && dir[cur] == FILE_SEPARATOR_CHAR) {
277             cur++;
278         }
279         if (cur >= dir.size()) {
280             break;
281         }
282         auto next = dir.find_first_of(FILE_SEPARATOR_CHAR, cur);
283         auto nodeName = dir.substr(cur, next - cur);
284         auto it = parent->children.find(nodeName);
285         if (it == parent->children.end()) {
286             ABILITYBASE_LOGD("target dir not found, dir : %{public}s", dir.c_str());
287             return false;
288         }
289         parent = it->second;
290         cur = next;
291     } while (cur != std::string::npos);
292 
293     return true;
294 }
295 namespace {
GetTreeFileList(const std::shared_ptr<DirTreeNode> & root,const std::string & rootPath,std::vector<std::string> & assetList)296 void GetTreeFileList(const std::shared_ptr<DirTreeNode> &root, const std::string &rootPath,
297     std::vector<std::string> &assetList)
298 {
299     if (root->children.empty()) {
300         assetList.push_back(rootPath);
301     } else {
302         for (const auto &child : root->children) {
303             GetTreeFileList(child.second, rootPath + "/" + child.first, assetList);
304         }
305     }
306 }
307 }
308 
GetAllFileList(const std::string & srcPath,std::vector<std::string> & assetList)309 void ZipFile::GetAllFileList(const std::string &srcPath, std::vector<std::string> &assetList)
310 {
311     if (srcPath.empty()) {
312         ABILITYBASE_LOGE("target dir is empty");
313         return;
314     }
315 
316     auto rootName = srcPath.back() == FILE_SEPARATOR_CHAR ?
317         srcPath.substr(0, srcPath.length() - 1) : srcPath;
318 
319     size_t cur = 0;
320     auto parent = dirRoot_;
321     do {
322         while (cur < rootName.size() && rootName[cur] == FILE_SEPARATOR_CHAR) {
323             cur++;
324         }
325         if (cur >= rootName.size()) {
326             break;
327         }
328         auto next = rootName.find_first_of(FILE_SEPARATOR_CHAR, cur);
329         auto nodeName = rootName.substr(cur, next - cur);
330         auto it = parent->children.find(nodeName);
331         if (it == parent->children.end()) {
332             ABILITYBASE_LOGI("target srcPath not found: %{public}s", rootName.c_str());
333             return;
334         }
335         parent = it->second;
336         cur = next;
337     } while (cur != std::string::npos);
338 
339     GetTreeFileList(parent, rootName, assetList);
340 }
341 
GetChildNames(const std::string & srcPath,std::set<std::string> & fileSet)342 void ZipFile::GetChildNames(const std::string &srcPath, std::set<std::string> &fileSet)
343 {
344     if (srcPath.empty()) {
345         ABILITYBASE_LOGE("target dir is empty");
346         return;
347     }
348 
349     size_t cur = 0;
350     auto parent = dirRoot_;
351     do {
352         while (cur < srcPath.size() && srcPath[cur] == FILE_SEPARATOR_CHAR) {
353             cur++;
354         }
355         if (cur >= srcPath.size()) {
356             break;
357         }
358         auto next = srcPath.find_first_of(FILE_SEPARATOR_CHAR, cur);
359         auto nodeName = srcPath.substr(cur, next - cur);
360         auto it = parent->children.find(nodeName);
361         if (it == parent->children.end()) {
362             ABILITYBASE_LOGI("target srcPath not found: %{public}s", srcPath.c_str());
363             return;
364         }
365         parent = it->second;
366         cur = next;
367     } while (cur != std::string::npos);
368 
369     for (const auto &child : parent->children) {
370         fileSet.insert(child.first);
371     }
372 }
373 
GetEntry(const std::string & entryName,ZipEntry & resultEntry) const374 bool ZipFile::GetEntry(const std::string &entryName, ZipEntry &resultEntry) const
375 {
376     auto iter = entriesMap_.find(entryName);
377     if (iter != entriesMap_.end()) {
378         resultEntry = iter->second;
379         return true;
380     }
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, fail: %{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 wrong!");
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     ABILITYBASE_LOGD("ExtractFile end");
669 }
670 
ExtractFileFromMMap(const std::string & file,void * mmapDataPtr,std::unique_ptr<uint8_t[]> & dataPtr,size_t & len) const671 bool ZipFile::ExtractFileFromMMap(const std::string &file, void *mmapDataPtr,
672     std::unique_ptr<uint8_t[]> &dataPtr, size_t &len) const
673 {
674     ABILITYBASE_LOGI("ExtractFileFromMMap %{public}s.", file.c_str());
675     ZipEntry zipEntry;
676     if (!GetEntry(file, zipEntry)) {
677         ABILITYBASE_LOGE("extract file: not find file");
678         return false;
679     }
680 
681     if (!zipEntry.compressionMethod) {
682         ABILITYBASE_LOGE("file[%{public}s] is not extracted.", file.c_str());
683         return false;
684     }
685 
686     uint16_t extraSize = 0;
687     if (!CheckCoherencyLocalHeader(zipEntry, extraSize)) {
688         ABILITYBASE_LOGE("check coherency local header failed");
689         return false;
690     }
691 
692     bool ret = false;
693     ret = UnzipWithInflatedFromMMap(zipEntry, extraSize, mmapDataPtr, dataPtr, len);
694 
695     return ret;
696 }
697 
UnzipWithInflatedFromMMap(const ZipEntry & zipEntry,const uint16_t extraSize,void * mmapDataPtr,std::unique_ptr<uint8_t[]> & dataPtr,size_t & len) const698 bool ZipFile::UnzipWithInflatedFromMMap(const ZipEntry &zipEntry, const uint16_t extraSize,
699     void *mmapDataPtr, std::unique_ptr<uint8_t[]> &dataPtr, size_t &len) const
700 {
701     z_stream zstream;
702     if (!InitZStream(zstream)) {
703         ABILITYBASE_LOGE("Init zstream failed.");
704         return false;
705     }
706 
707     BytePtr bufIn = zstream.next_in;
708     BytePtr bufOut = zstream.next_out;
709 
710     bool ret = true;
711     int32_t zlibErr = Z_OK;
712     uint32_t remainCompressedSize = zipEntry.compressedSize;
713     size_t inflateLen = 0;
714     uint8_t errorTimes = 0;
715 
716     len = zipEntry.uncompressedSize;
717     dataPtr = std::make_unique<uint8_t[]>(len);
718     uint8_t *dstDataPtr = static_cast<uint8_t *>(dataPtr.get());
719     void *mmapSrcDataPtr = mmapDataPtr;
720 
721     while ((remainCompressedSize > 0) || (zstream.avail_in > 0)) {
722         if (!ReadZStreamFromMMap(bufIn, mmapSrcDataPtr, zstream, remainCompressedSize)) {
723             ret = false;
724             break;
725         }
726 
727         zlibErr = inflate(&zstream, Z_SYNC_FLUSH);
728         if ((zlibErr >= Z_OK) && (zstream.msg != nullptr)) {
729             ABILITYBASE_LOGE("unzip inflated inflate, error: %{public}d, err msg: %{public}s.", zlibErr, zstream.msg);
730             ret = false;
731             break;
732         }
733 
734         inflateLen = UNZIP_BUF_OUT_LEN - zstream.avail_out;
735         if (!CopyInflateOut(zstream, inflateLen, &dstDataPtr, bufOut, errorTimes)) {
736             break;
737         }
738     }
739 
740     // free all dynamically allocated data structures except the next_in and next_out for this stream.
741     zlibErr = inflateEnd(&zstream);
742     if (zlibErr != Z_OK) {
743         ABILITYBASE_LOGE("unzip inflateEnd error, error: %{public}d", zlibErr);
744         ret = false;
745     }
746 
747     delete[] bufOut;
748     delete[] bufIn;
749     return ret;
750 }
751 
CopyInflateOut(z_stream & zstream,size_t inflateLen,uint8_t ** dstDataPtr,BytePtr bufOut,uint8_t & errorTimes) const752 bool ZipFile::CopyInflateOut(z_stream &zstream, size_t inflateLen, uint8_t** dstDataPtr,
753     BytePtr bufOut, uint8_t &errorTimes) const
754 {
755     if (inflateLen > 0) {
756         if (memcpy_s(*dstDataPtr, inflateLen, bufOut, inflateLen) != EOK) {
757             ABILITYBASE_LOGE("Mem copy failed!");
758             return false;
759         }
760 
761         *dstDataPtr += inflateLen;
762         zstream.next_out = bufOut;
763         zstream.avail_out = UNZIP_BUF_OUT_LEN;
764         errorTimes = 0;
765     } else {
766         errorTimes++;
767     }
768     if (errorTimes >= INFLATE_ERROR_TIMES) {
769         ABILITYBASE_LOGE("unzip inflated data is abnormal!");
770         return false;
771     }
772 
773     return true;
774 }
775 
ReadZStreamFromMMap(const BytePtr & buffer,void * & dataPtr,z_stream & zstream,uint32_t & remainCompressedSize) const776 bool ZipFile::ReadZStreamFromMMap(const BytePtr &buffer, void* &dataPtr,
777     z_stream &zstream, uint32_t &remainCompressedSize) const
778 {
779     if (!dataPtr) {
780         ABILITYBASE_LOGE("Input dataPtr is nullptr.");
781         return false;
782     }
783 
784     uint8_t *srcDataPtr = static_cast<uint8_t *>(dataPtr);
785     if (zstream.avail_in == 0) {
786         size_t remainBytes = (remainCompressedSize > UNZIP_BUF_IN_LEN) ? UNZIP_BUF_IN_LEN : remainCompressedSize;
787         size_t readBytes = sizeof(Byte) * remainBytes;
788         if (memcpy_s(buffer, readBytes, srcDataPtr, readBytes) != EOK) {
789             ABILITYBASE_LOGE("Mem copy failed.");
790             return false;
791         }
792         srcDataPtr += readBytes;
793         remainCompressedSize -= remainBytes;
794         zstream.avail_in = remainBytes;
795         zstream.next_in = buffer;
796     }
797     dataPtr = srcDataPtr;
798     return true;
799 }
800 
CreateFileMapper(const std::string & fileName,FileMapperType type) const801 std::unique_ptr<FileMapper> ZipFile::CreateFileMapper(const std::string &fileName, FileMapperType type) const
802 {
803     ZipEntry zipEntry;
804     if (!GetEntry(fileName, zipEntry)) {
805         ABILITYBASE_LOGE("GetEntry failed hapPath %{public}s.", fileName.c_str());
806         return nullptr;
807     }
808 
809     ZipPos offset = 0;
810     uint32_t length = 0;
811     if (!GetDataOffsetRelative(zipEntry, offset, length)) {
812         ABILITYBASE_LOGE("GetDataOffsetRelative failed hapPath %{public}s.", fileName.c_str());
813         return nullptr;
814     }
815     bool compress = zipEntry.compressionMethod > 0;
816     if (type == FileMapperType::SAFE_ABC && compress) {
817         ABILITYBASE_LOGW("Entry is compressed for safe: %{public}s.", fileName.c_str());
818     }
819     std::unique_ptr<FileMapper> fileMapper = std::make_unique<FileMapper>();
820     auto result = false;
821     if (type == FileMapperType::NORMAL_MEM) {
822         result = fileMapper->CreateFileMapper(zipFileReader_, fileName, offset, length, compress);
823     } else {
824         result = fileMapper->CreateFileMapper(fileName, compress, zipFileReader_->GetFd(), offset, length, type);
825         if (result && type == FileMapperType::SAFE_ABC) {
826             zipFileReader_->SetClosable(false);
827         }
828     }
829 
830     if (!result) {
831         return nullptr;
832     }
833     return fileMapper;
834 }
835 
ExtractToBufByName(const std::string & fileName,std::unique_ptr<uint8_t[]> & dataPtr,size_t & len) const836 bool ZipFile::ExtractToBufByName(const std::string &fileName, std::unique_ptr<uint8_t[]> &dataPtr,
837     size_t &len) const
838 {
839     ZipEntry zipEntry;
840     if (!GetEntry(fileName, zipEntry)) {
841         if (fileName.length() > MAP_FILE_SUFFIX && fileName.substr(fileName.length() - MAP_FILE_SUFFIX) != ".map") {
842             ABILITYBASE_LOGE("GetEntry failed hapPath %{public}s.", fileName.c_str());
843         }
844         return false;
845     }
846     uint16_t extraSize = 0;
847     if (!CheckCoherencyLocalHeader(zipEntry, extraSize)) {
848         ABILITYBASE_LOGE("check coherency local header failed");
849         return false;
850     }
851 
852     ZipPos offset = GetEntryDataOffset(zipEntry, extraSize);
853     uint32_t length = zipEntry.compressedSize;
854     auto dataTmp = std::make_unique<uint8_t[]>(length);
855     if (!zipFileReader_->ReadBuffer(dataTmp.get(), offset, length)) {
856         ABILITYBASE_LOGE("read file failed, len[%{public}zu]. fileName: %{public}s, offset: %{public}zu",
857             len, fileName.c_str(), (size_t)offset);
858         dataTmp.reset();
859         return false;
860     }
861 
862     if (zipEntry.compressionMethod > 0) {
863         return UnzipWithInflatedFromMMap(zipEntry, extraSize, dataTmp.get(), dataPtr, len);
864     }
865 
866     len = length;
867     dataPtr = std::move(dataTmp);
868 
869     return true;
870 }
871 }  // namespace AbilityBase
872 }  // namespace OHOS
873