• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 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 <cassert>
19 #include <cstring>
20 #include <ostream>
21 
22 #include "securec.h"
23 #include "zlib.h"
24 #include "app_log_wrapper.h"
25 
26 namespace OHOS {
27 namespace AppExecFwk {
28 namespace {
29 
30 constexpr uint32_t MAX_FILE_NAME = 256;
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 size_t FILE_READ_COUNT = 1;
40 
41 }  // namespace
42 
ZipEntry(const CentralDirEntry & centralEntry)43 ZipEntry::ZipEntry(const CentralDirEntry &centralEntry)
44 {
45     compressionMethod = centralEntry.compressionMethod;
46     uncompressedSize = centralEntry.uncompressedSize;
47     compressedSize = centralEntry.compressedSize;
48     localHeaderOffset = centralEntry.localHeaderOffset;
49     crc = centralEntry.crc;
50     flags = centralEntry.flags;
51 }
52 
ZipFile(const std::string & pathName)53 ZipFile::ZipFile(const std::string &pathName) : pathName_(pathName)
54 {
55     APP_LOGI("create instance from %{private}s", pathName_.c_str());
56 }
57 
~ZipFile()58 ZipFile::~ZipFile()
59 {
60     Close();
61 }
62 
SetContentLocation(const ZipPos start,const size_t length)63 void ZipFile::SetContentLocation(const ZipPos start, const size_t length)
64 {
65     APP_LOGD("set content location start position(%{public}llu), length(%{public}zu)", start, length);
66     fileStartPos_ = start;
67     fileLength_ = length;
68 }
69 
CheckEndDir(const EndDir & endDir) const70 bool ZipFile::CheckEndDir(const EndDir &endDir) const
71 {
72     size_t lenEndDir = sizeof(EndDir);
73     if ((endDir.numDisk != 0) || (endDir.signature != EOCD_SIGNATURE) || (endDir.startDiskOfCentralDir != 0) ||
74         (endDir.offset >= fileLength_) || (endDir.totalEntriesInThisDisk != endDir.totalEntries) ||
75         (endDir.commentLen != 0) ||
76         // central dir can't overlap end of central dir
77         ((endDir.offset + endDir.sizeOfCentralDir + lenEndDir) > fileLength_)) {
78         APP_LOGE("end dir format error");
79         return false;
80     }
81     return true;
82 }
83 
ParseEndDirectory()84 bool ZipFile::ParseEndDirectory()
85 {
86     size_t endDirLen = sizeof(EndDir);
87     size_t endFilePos = fileStartPos_ + fileLength_;
88 
89     if (fileLength_ <= endDirLen) {
90         APP_LOGE("parse EOCD file length(%{public}llu) <= end dir length(%{public}llu)", fileStartPos_, fileLength_);
91         return false;
92     }
93 
94     size_t eocdPos = endFilePos - endDirLen;
95     if (fseek(file_, eocdPos, SEEK_SET) != 0) {
96         APP_LOGE("locate EOCD seek failed, error: %{public}s", strerror(errno));
97         return false;
98     }
99 
100     if (fread(&endDir_, sizeof(EndDir), FILE_READ_COUNT, file_) != FILE_READ_COUNT) {
101         APP_LOGE("read EOCD struct failed, error: %{public}s", strerror(errno));
102         return false;
103     }
104 
105     centralDirPos_ = endDir_.offset + fileStartPos_;
106     APP_LOGD("parse EOCD offset(0x%{public}08x) file start position(0x%{public}08llx)", endDir_.offset, fileStartPos_);
107 
108     return CheckEndDir(endDir_);
109 }
110 
ParseAllEntries()111 bool ZipFile::ParseAllEntries()
112 {
113     bool ret = true;
114     ZipPos currentPos = centralDirPos_;
115     CentralDirEntry directoryEntry = {0};
116     size_t fileLength = 0;
117 
118     for (int32_t i = 0; i < endDir_.totalEntries; i++) {
119         std::string fileName;
120         fileName.reserve(MAX_FILE_NAME);
121         fileName.resize(MAX_FILE_NAME - 1);
122 
123         if (fseek(file_, currentPos, SEEK_SET) != 0) {
124             APP_LOGE("parse entry(%{public}d) seek zipEntry failed, error: %{public}s", i, strerror(errno));
125             ret = false;
126             break;
127         }
128 
129         if (fread(&directoryEntry, sizeof(CentralDirEntry), FILE_READ_COUNT, file_) != FILE_READ_COUNT) {
130             APP_LOGE("parse entry(%{public}d) read ZipEntry failed, error: %{public}s", i, strerror(errno));
131             ret = false;
132             break;
133         }
134 
135         if (directoryEntry.signature != CENTRAL_SIGNATURE) {
136             APP_LOGE("parse entry(%{public}d) check signature(0x%08x) at pos(0x%08llx) failed",
137                 i,
138                 directoryEntry.signature,
139                 currentPos);
140             ret = false;
141             break;
142         }
143 
144         fileLength = (directoryEntry.nameSize >= MAX_FILE_NAME) ? (MAX_FILE_NAME - 1) : directoryEntry.nameSize;
145         if (fread(&(fileName[0]), fileLength, FILE_READ_COUNT, file_) != FILE_READ_COUNT) {
146             APP_LOGE("parse entry(%{public}d) read file name failed, error: %{public}s", i, strerror(errno));
147             ret = false;
148             break;
149         }
150         fileName.resize(fileLength);
151 
152         ZipEntry currentEntry(directoryEntry);
153         currentEntry.fileName = fileName;
154         entriesMap_[fileName] = currentEntry;
155 
156         currentPos += sizeof(directoryEntry);
157         currentPos += directoryEntry.nameSize + directoryEntry.extraSize + directoryEntry.commentSize;
158     }
159 
160     APP_LOGD("parse %{public}d central entries from %{private}s", endDir_.totalEntries, pathName_.c_str());
161     return ret;
162 }
163 
Open()164 bool ZipFile::Open()
165 {
166     APP_LOGD("open: %{private}s", pathName_.c_str());
167 
168     if (isOpen_) {
169         APP_LOGE("has already opened");
170         return true;
171     }
172 
173     if (pathName_.length() > PATH_MAX) {
174         APP_LOGE("path length(%{public}u) longer than max path length(%{public}d)",
175             static_cast<unsigned int>(pathName_.length()),
176             PATH_MAX);
177         return false;
178     }
179     std::string realPath;
180     realPath.reserve(PATH_MAX);
181     realPath.resize(PATH_MAX - 1);
182     if (realpath(pathName_.c_str(), &(realPath[0])) == nullptr) {
183         APP_LOGE("transform real path error: %{public}s", strerror(errno));
184         return false;
185     }
186 
187     FILE *tmpFile = fopen(&(realPath[0]), "rb");
188     if (tmpFile == nullptr) {
189         APP_LOGE("open file(%{private}s) failed, error: %{public}s", pathName_.c_str(), strerror(errno));
190         return false;
191     }
192 
193     if (fileLength_ == 0) {
194         if (fseek(tmpFile, 0, SEEK_END) != 0) {
195             APP_LOGE("file seek failed, error: %{public}s", strerror(errno));
196             fclose(tmpFile);
197             return false;
198         }
199 
200         fileLength_ = ftell(tmpFile);
201         if (fileStartPos_ >= fileLength_) {
202             APP_LOGE("open start pos > length failed");
203             fclose(tmpFile);
204             return false;
205         }
206 
207         fileLength_ -= fileStartPos_;
208     }
209 
210     file_ = tmpFile;
211     bool result = ParseEndDirectory();
212     if (result) {
213         result = ParseAllEntries();
214     }
215     // it means open file success.
216     isOpen_ = true;
217     return result;
218 }
219 
Close()220 void ZipFile::Close()
221 {
222     APP_LOGD("close: %{private}s", pathName_.c_str());
223 
224     if (!isOpen_ || file_ == nullptr) {
225         APP_LOGW("file is not opened");
226         return;
227     }
228 
229     entriesMap_.clear();
230     pathName_ = "";
231     isOpen_ = false;
232 
233     if (fclose(file_) != 0) {
234         APP_LOGW("close failed, error: %{public}s", strerror(errno));
235     }
236     file_ = nullptr;
237 }
238 
239 // Get all file zipEntry in this file
GetAllEntries() const240 const ZipEntryMap &ZipFile::GetAllEntries() const
241 {
242     return entriesMap_;
243 }
244 
GetEntry(const std::string & entryName,ZipEntry & resultEntry) const245 bool ZipFile::GetEntry(const std::string &entryName, ZipEntry &resultEntry) const
246 {
247     APP_LOGD("get entry by name: %{public}s", entryName.c_str());
248     auto iter = entriesMap_.find(entryName);
249     if (iter != entriesMap_.end()) {
250         resultEntry = iter->second;
251         APP_LOGD("get entry succeed");
252         return true;
253     }
254     APP_LOGE("get entry failed");
255     return false;
256 }
257 
GetLocalHeaderSize(const uint16_t nameSize,const uint16_t extraSize) const258 size_t ZipFile::GetLocalHeaderSize(const uint16_t nameSize, const uint16_t extraSize) const
259 {
260     return sizeof(LocalHeader) + nameSize + extraSize;
261 }
262 
CheckDataDesc(const ZipEntry & zipEntry,const LocalHeader & localHeader) const263 bool ZipFile::CheckDataDesc(const ZipEntry &zipEntry, const LocalHeader &localHeader) const
264 {
265     uint32_t crcLocal = 0;
266     uint32_t compressedLocal = 0;
267     uint32_t uncompressedLocal = 0;
268 
269     if (localHeader.flags & FLAG_DATA_DESC) {  // use data desc
270         DataDesc dataDesc;
271         ZipPos descPos = zipEntry.localHeaderOffset + GetLocalHeaderSize(localHeader.nameSize, localHeader.extraSize);
272         descPos += fileStartPos_ + zipEntry.compressedSize;
273 
274         if (fseek(file_, descPos, SEEK_SET) != 0) {
275             APP_LOGE("check local header seek datadesc failed, error: %{public}s", strerror(errno));
276             return false;
277         }
278 
279         if (fread(&dataDesc, sizeof(DataDesc), FILE_READ_COUNT, file_) != FILE_READ_COUNT) {
280             APP_LOGE("check local header read datadesc failed, error: %{public}s", strerror(errno));
281             return false;
282         }
283 
284         if (dataDesc.signature != DATA_DESC_SIGNATURE) {
285             APP_LOGE("check local header check datadesc signature failed");
286             return false;
287         }
288 
289         crcLocal = dataDesc.crc;
290         compressedLocal = dataDesc.compressedSize;
291         uncompressedLocal = dataDesc.uncompressedSize;
292     } else {
293         crcLocal = localHeader.crc;
294         compressedLocal = localHeader.compressedSize;
295         uncompressedLocal = localHeader.uncompressedSize;
296     }
297 
298     if ((zipEntry.crc != crcLocal) || (zipEntry.compressedSize != compressedLocal) ||
299         (zipEntry.uncompressedSize != uncompressedLocal)) {
300         APP_LOGE("check local header compressed size corrupted");
301         return false;
302     }
303 
304     return true;
305 }
306 
CheckCoherencyLocalHeader(const ZipEntry & zipEntry,uint16_t & extraSize) const307 bool ZipFile::CheckCoherencyLocalHeader(const ZipEntry &zipEntry, uint16_t &extraSize) const
308 {
309     LocalHeader localHeader = {0};
310 
311     if (zipEntry.localHeaderOffset >= fileLength_) {
312         APP_LOGE("check local file header offset is overflow %{public}d", zipEntry.localHeaderOffset);
313         return false;
314     }
315 
316     if (fseek(file_, fileStartPos_ + zipEntry.localHeaderOffset, SEEK_SET) != 0) {
317         APP_LOGE("check local header seek failed, error: %{public}s", strerror(errno));
318         return false;
319     }
320 
321     if (fread(&localHeader, sizeof(LocalHeader), FILE_READ_COUNT, file_) != FILE_READ_COUNT) {
322         APP_LOGE("check local header read localheader failed, error: %{public}s", strerror(errno));
323         return false;
324     }
325 
326     if ((localHeader.signature != LOCAL_HEADER_SIGNATURE) ||
327         (zipEntry.compressionMethod != localHeader.compressionMethod)) {
328         APP_LOGE("check local header signature or compressionMethod failed");
329         return false;
330     }
331 
332     // current only support store and Z_DEFLATED method
333     if ((zipEntry.compressionMethod != Z_DEFLATED) && (zipEntry.compressionMethod != 0)) {
334         APP_LOGE("check local header compressionMethod(%{public}d) not support", zipEntry.compressionMethod);
335         return false;
336     }
337 
338     std::string fileName;
339     fileName.reserve(MAX_FILE_NAME);
340     fileName.resize(MAX_FILE_NAME - 1);
341     size_t fileLength = (localHeader.nameSize >= MAX_FILE_NAME) ? (MAX_FILE_NAME - 1) : localHeader.nameSize;
342     if (fileLength != zipEntry.fileName.length()) {
343         APP_LOGE("check local header file name size failed");
344         return false;
345     }
346     if (fread(&(fileName[0]), fileLength, FILE_READ_COUNT, file_) != FILE_READ_COUNT) {
347         APP_LOGE("check local header read file name failed, error: %{public}s", strerror(errno));
348         return false;
349     }
350     fileName.resize(fileLength);
351     if (zipEntry.fileName != fileName) {
352         APP_LOGE("check local header file name corrupted");
353         return false;
354     }
355 
356     if (!CheckDataDesc(zipEntry, localHeader)) {
357         APP_LOGE("check data desc failed");
358         return false;
359     }
360 
361     extraSize = localHeader.extraSize;
362     return true;
363 }
364 
SeekToEntryStart(const ZipEntry & zipEntry,const uint16_t extraSize) const365 bool ZipFile::SeekToEntryStart(const ZipEntry &zipEntry, const uint16_t extraSize) const
366 {
367     ZipPos startOffset = zipEntry.localHeaderOffset;
368     // get data offset, add signature+localheader+namesize+extrasize
369     startOffset += GetLocalHeaderSize(zipEntry.fileName.length(), extraSize);
370     if (startOffset + zipEntry.compressedSize > fileLength_) {
371         APP_LOGE("startOffset(%{public}lld)+entryCompressedSize(%{public}ud) > fileLength(%{public}llu)",
372             startOffset,
373             zipEntry.compressedSize,
374             fileLength_);
375         return false;
376     }
377     startOffset += fileStartPos_;  // add file start relative to file stream
378 
379     APP_LOGD("seek to entry start 0x%{public}08llx", startOffset);
380     if (fseek(file_, startOffset, SEEK_SET) != 0) {
381         APP_LOGE("seek failed, error: %{public}s", strerror(errno));
382         return false;
383     }
384     return true;
385 }
386 
UnzipWithStore(const ZipEntry & zipEntry,const uint16_t extraSize,std::ostream & dest) const387 bool ZipFile::UnzipWithStore(const ZipEntry &zipEntry, const uint16_t extraSize, std::ostream &dest) const
388 {
389     APP_LOGD("unzip with store");
390 
391     if (!SeekToEntryStart(zipEntry, extraSize)) {
392         APP_LOGE("seek to entry start failed");
393         return false;
394     }
395 
396     uint32_t remainSize = zipEntry.compressedSize;
397     std::string readBuffer;
398     readBuffer.reserve(UNZIP_BUF_OUT_LEN);
399     readBuffer.resize(UNZIP_BUF_OUT_LEN - 1);
400     while (remainSize > 0) {
401         size_t readBytes;
402         size_t readLen = (remainSize > UNZIP_BUF_OUT_LEN) ? UNZIP_BUF_OUT_LEN : remainSize;
403         readBytes = fread(&(readBuffer[0]), sizeof(Byte), readLen, file_);
404         if (readBytes == 0) {
405             APP_LOGE("unzip store read failed, error: %{public}s", strerror(errno));
406             return false;
407         }
408         remainSize -= readBytes;
409         dest.write(&(readBuffer[0]), readBytes);
410     }
411 
412     return true;
413 }
414 
InitZStream(z_stream & zstream) const415 bool ZipFile::InitZStream(z_stream &zstream) const
416 {
417     // init zlib stream
418     if (memset_s(&zstream, sizeof(z_stream), 0, sizeof(z_stream))) {
419         APP_LOGE("unzip stream buffer init failed");
420         return false;
421     }
422     int32_t zlibErr = inflateInit2(&zstream, -MAX_WBITS);
423     if (zlibErr != Z_OK) {
424         APP_LOGE("unzip inflated init failed");
425         return false;
426     }
427 
428     BytePtr bufOut = new (std::nothrow) Byte[UNZIP_BUF_OUT_LEN];
429     if (bufOut == nullptr) {
430         APP_LOGE("unzip inflated new out buffer failed");
431         return false;
432     }
433 
434     BytePtr bufIn = new (std::nothrow) Byte[UNZIP_BUF_IN_LEN];
435     if (bufIn == nullptr) {
436         APP_LOGE("unzip inflated new in buffer failed");
437         delete[] bufOut;
438         return false;
439     }
440     zstream.next_out = bufOut;
441     zstream.next_in = bufIn;
442     zstream.avail_out = UNZIP_BUF_OUT_LEN;
443     return true;
444 }
445 
ReadZStream(const BytePtr & buffer,z_stream & zstream,uint32_t & remainCompressedSize) const446 bool ZipFile::ReadZStream(const BytePtr &buffer, z_stream &zstream, uint32_t &remainCompressedSize) const
447 {
448     if (zstream.avail_in == 0) {
449         size_t readBytes;
450         size_t remainBytes = (remainCompressedSize > UNZIP_BUF_IN_LEN) ? UNZIP_BUF_IN_LEN : remainCompressedSize;
451         readBytes = fread(buffer, sizeof(Byte), remainBytes, file_);
452         if (readBytes == 0) {
453             APP_LOGE("unzip inflated read failed, error: %{public}s", strerror(errno));
454             return false;
455         }
456 
457         remainCompressedSize -= readBytes;
458         zstream.avail_in = readBytes;
459         zstream.next_in = buffer;
460     }
461     return true;
462 }
463 
UnzipWithInflated(const ZipEntry & zipEntry,const uint16_t extraSize,std::ostream & dest) const464 bool ZipFile::UnzipWithInflated(const ZipEntry &zipEntry, const uint16_t extraSize, std::ostream &dest) const
465 {
466     APP_LOGD("unzip with inflated");
467 
468     z_stream zstream;
469     if (!SeekToEntryStart(zipEntry, extraSize)) {
470         APP_LOGE("seek to entry start failed");
471         return false;
472     }
473     if (!InitZStream(zstream)) {
474         APP_LOGE("init zstream failed");
475         return false;
476     }
477     BytePtr bufIn = zstream.next_in;
478     BytePtr bufOut = zstream.next_out;
479 
480     bool ret = true;
481     int32_t zlibErr = Z_OK;
482     uint32_t remainCompressedSize = zipEntry.compressedSize;
483     size_t inflateLen = 0;
484     uint32_t crc = 0;
485     while ((remainCompressedSize > 0) || (zstream.avail_in > 0)) {
486         if (!ReadZStream(bufIn, zstream, remainCompressedSize)) {
487             ret = false;
488             break;
489         }
490 
491         zlibErr = inflate(&zstream, Z_SYNC_FLUSH);
492         if ((zlibErr >= Z_OK) && (zstream.msg != nullptr)) {
493             APP_LOGE("unzip inflated inflate, error: %{public}d, err msg: %{public}s", zlibErr, zstream.msg);
494             ret = false;
495             break;
496         }
497 
498         inflateLen = UNZIP_BUF_OUT_LEN - zstream.avail_out;
499         if (inflateLen > 0) {
500             dest.write((const char *)bufOut, inflateLen);
501             crc = crc32(crc, bufOut, inflateLen);
502             zstream.next_out = bufOut;
503             zstream.avail_out = UNZIP_BUF_OUT_LEN;
504         }
505     }
506 
507     if (crc != zipEntry.crc) {
508         APP_LOGE("unzip inflate crc check");
509         ret = false;
510     }
511 
512     // free all dynamically allocated data structures except the next_in and next_out for this stream.
513     zlibErr = inflateEnd(&zstream);
514     if (zlibErr != Z_OK) {
515         APP_LOGE("unzip inflateEnd error, error: %{public}d", zlibErr);
516         ret = false;
517     }
518 
519     delete[] bufOut;
520     delete[] bufIn;
521     return ret;
522 }
523 
GetEntryDataOffset(const ZipEntry & zipEntry,const uint16_t extraSize) const524 ZipPos ZipFile::GetEntryDataOffset(const ZipEntry &zipEntry, const uint16_t extraSize) const
525 {
526     // get entry data offset relative file
527     ZipPos offset = zipEntry.localHeaderOffset;
528 
529     offset += GetLocalHeaderSize(zipEntry.fileName.length(), extraSize);
530     offset += fileStartPos_;
531 
532     return offset;
533 }
534 
GetDataOffsetRelative(const std::string & file,ZipPos & offset,uint32_t & length) const535 bool ZipFile::GetDataOffsetRelative(const std::string &file, ZipPos &offset, uint32_t &length) const
536 {
537     APP_LOGD("get data relative offset for file %{private}s", file.c_str());
538 
539     ZipEntry zipEntry;
540     if (!GetEntry(file, zipEntry)) {
541         APP_LOGE("extract file: not find file");
542         return false;
543     }
544 
545     uint16_t extraSize = 0;
546     if (!CheckCoherencyLocalHeader(zipEntry, extraSize)) {
547         APP_LOGE("check coherency local header failed");
548         return false;
549     }
550 
551     offset = GetEntryDataOffset(zipEntry, extraSize);
552     length = zipEntry.compressedSize;
553     return true;
554 }
555 
ExtractFile(const std::string & file,std::ostream & dest) const556 bool ZipFile::ExtractFile(const std::string &file, std::ostream &dest) const
557 {
558     APP_LOGD("extract file %{private}s", file.c_str());
559 
560     ZipEntry zipEntry;
561     if (!GetEntry(file, zipEntry)) {
562         APP_LOGE("extract file: not find file");
563         return false;
564     }
565 
566     uint16_t extraSize = 0;
567     if (!CheckCoherencyLocalHeader(zipEntry, extraSize)) {
568         APP_LOGE("check coherency local header failed");
569         return false;
570     }
571 
572     bool ret = true;
573     if (zipEntry.compressionMethod == 0) {
574         ret = UnzipWithStore(zipEntry, extraSize, dest);
575     } else {
576         ret = UnzipWithInflated(zipEntry, extraSize, dest);
577     }
578 
579     return ret;
580 }
581 
582 }  // namespace AppExecFwk
583 }  // namespace OHOS
584