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 ¢ralEntry)
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