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