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