• 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 #include "pkg_gzipfile.h"
16 
17 using namespace std;
18 
19 namespace Hpackage {
20 /* gzip flag byte */
21 constexpr uint16_t HEADER_CRC = 0x02; /* bit 1 set: CRC16 for the gzip header */
22 constexpr uint16_t EXTRA_FIELD = 0x04; /* bit 2 set: extra field present */
23 constexpr uint16_t ORIG_NAME = 0x08; /* bit 3 set: original file name present */
24 constexpr uint16_t COMMENT = 0x10; /* bit 4 set: file comment present */
25 constexpr uint16_t ENCRYPTED = 0x20; /* bit 5 set: file is encrypted */
26 constexpr int32_t DEF_MEM_LEVEL = 8;
27 constexpr uint16_t GZIP_MAGIC = 0x8b1f;
28 constexpr int32_t BUFFER_SIZE = 1024;
29 #ifdef SUPPORT_EXTRA_FIELD
30 constexpr int32_t EXTRA_FIELD_LEN = 20;
31 #endif
32 constexpr int32_t BLOCK_SIZE = 8;
33 
34 /*
35     Each member has the following structure:
36      +---+---+---+---+---+---+---+---+---+---+
37      |ID1|ID2|CM |FLG|     MTIME     |XFL|OS | (more-->)
38      +---+---+---+---+---+---+---+---+---+---+
39 
40     (if FLG.FEXTRA set)
41 
42      +---+---+=================================+
43      | XLEN  |...XLEN bytes of "extra field"...| (more-->)
44      +---+---+=================================+
45 
46     (if FLG.FNAME set)
47 
48      +=========================================+
49      |...original file name, zero-terminated...| (more-->)
50      +=========================================+
51 
52     (if FLG.FCOMMENT set)
53 
54      +===================================+
55      |...file comment, zero-terminated...| (more-->)
56      +===================================+
57 
58     (if FLG.FHCRC set)
59 
60      +---+---+
61      | CRC16 |
62      +---+---+
63  */
EncodeHeader(PkgStreamPtr inStream,size_t startOffset,size_t & encodeLen)64 int32_t GZipFileEntry::EncodeHeader(PkgStreamPtr inStream, size_t startOffset, size_t &encodeLen)
65 {
66     PkgStreamPtr outStream = pkgFile_->GetPkgStream();
67     PKG_CHECK(outStream != nullptr, return PKG_INVALID_PARAM,
68         "Check outstream fail %s", fileInfo_.fileInfo.identity.c_str());
69     size_t offset = 0;
70     PkgBuffer buffer(BUFFER_SIZE);
71     GZipHeader *header = reinterpret_cast<GZipHeader *>(buffer.buffer);
72     header->magic = GZIP_MAGIC;
73     header->method = Z_DEFLATED;
74     header->flags = 0;
75     header->mtime = fileInfo_.fileInfo.modifiedTime;
76     offset += sizeof(GZipHeader);
77 #ifdef SUPPORT_EXTRA_FIELD
78     header->flags |= EXTRA_FIELD;
79     {
80         WriteLE16(buffer.buffer + offset, EXTRA_FIELD_LEN);
81         offset += sizeof(uint16_t) + EXTRA_FIELD_LEN;
82     }
83 #endif
84     header->flags |= ORIG_NAME;
85     {
86         size_t fileNameLen = 0;
87         PkgFile::ConvertStringToBuffer(
88             fileInfo_.fileInfo.identity, {buffer.buffer + offset, buffer.length - offset}, fileNameLen);
89         offset += fileNameLen;
90         buffer.buffer[offset] = 0;
91         offset += 1;
92     }
93 #ifdef SUPPORT_EXTRA_FIELD
94     header->flags |= COMMENT;
95     {
96         size_t fileNameLen = 0;
97         PkgFile::ConvertStringToBuffer(
98             fileInfo_.fileInfo.identity, {buffer.buffer + offset, buffer.length - offset}, fileNameLen);
99         offset += fileNameLen;
100         buffer.buffer[offset] = 0;
101         offset += 1;
102     }
103 #endif
104     fileInfo_.fileInfo.headerOffset = startOffset;
105     fileInfo_.fileInfo.dataOffset = startOffset + offset;
106     int32_t ret = outStream->Write(buffer, offset, startOffset);
107     PKG_CHECK(ret == PKG_SUCCESS, return ret, "Fail write header for %s", fileInfo_.fileInfo.identity.c_str());
108     encodeLen = offset;
109     return PKG_SUCCESS;
110 }
111 
Pack(PkgStreamPtr inStream,size_t startOffset,size_t & encodeLen)112 int32_t GZipFileEntry::Pack(PkgStreamPtr inStream, size_t startOffset, size_t &encodeLen)
113 {
114     PkgAlgorithm::PkgAlgorithmPtr algorithm = PkgAlgorithmFactory::GetAlgorithm(&fileInfo_.fileInfo);
115     PkgStreamPtr outStream = pkgFile_->GetPkgStream();
116     if (fileInfo_.fileInfo.dataOffset != startOffset) {
117         PKG_LOGE("start offset error for %s", fileInfo_.fileInfo.identity.c_str());
118         return PKG_INVALID_PARAM;
119     }
120     if (algorithm == nullptr || outStream == nullptr || inStream == nullptr) {
121         PKG_LOGE("outStream or inStream null for %s", fileInfo_.fileInfo.identity.c_str());
122         return PKG_INVALID_PARAM;
123     }
124     fileInfo_.fileInfo.dataOffset = startOffset;
125     PkgAlgorithmContext context = {
126         {0, startOffset},
127         {fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize},
128         0, fileInfo_.fileInfo.digestMethod
129     };
130     int32_t ret = algorithm->Pack(inStream, outStream, context);
131     if (ret != PKG_SUCCESS) {
132         PKG_LOGE("Fail Compress for %s", fileInfo_.fileInfo.identity.c_str());
133         return ret;
134     }
135     fileInfo_.fileInfo.packedSize = context.packedSize;
136 
137     /*
138     0   1   2   3   4   5   6   7
139     +---+---+---+---+---+---+---+---+
140     |     CRC32     |     ISIZE     |
141     +---+---+---+---+---+---+---+---+
142     */
143     PkgBuffer buffer(BLOCK_SIZE);
144     WriteLE32(buffer.buffer, context.crc);
145     WriteLE32(buffer.buffer + sizeof(uint32_t), fileInfo_.fileInfo.unpackedSize);
146     ret = outStream->Write(buffer, BLOCK_SIZE, fileInfo_.fileInfo.dataOffset + fileInfo_.fileInfo.packedSize);
147     if (ret != PKG_SUCCESS) {
148         PKG_LOGE("Fail write header for %s", fileInfo_.fileInfo.identity.c_str());
149         return ret;
150     }
151     encodeLen = fileInfo_.fileInfo.packedSize + BLOCK_SIZE;
152     PKG_LOGI("Pack packedSize:%zu unpackedSize: %zu offset: %zu %zu", fileInfo_.fileInfo.packedSize,
153         fileInfo_.fileInfo.unpackedSize, fileInfo_.fileInfo.headerOffset, fileInfo_.fileInfo.dataOffset);
154     return PKG_SUCCESS;
155 }
156 
Unpack(PkgStreamPtr outStream)157 int32_t GZipFileEntry::Unpack(PkgStreamPtr outStream)
158 {
159     PkgAlgorithm::PkgAlgorithmPtr algorithm = PkgAlgorithmFactory::GetAlgorithm(&fileInfo_.fileInfo);
160     PKG_CHECK(algorithm != nullptr, return PKG_INVALID_PARAM, "can not algorithm for %s",
161         fileInfo_.fileInfo.identity.c_str());
162 
163     PKG_LOGI("packedSize: %zu unpackedSize: %zu  offset header: %zu data: %zu", fileInfo_.fileInfo.packedSize,
164         fileInfo_.fileInfo.unpackedSize, fileInfo_.fileInfo.headerOffset, fileInfo_.fileInfo.dataOffset);
165 
166     PkgStreamPtr inStream = pkgFile_->GetPkgStream();
167     PKG_CHECK(outStream != nullptr && inStream != nullptr, return PKG_INVALID_PARAM,
168         "outStream or inStream null for %s", fileInfo_.fileInfo.identity.c_str());
169     PkgAlgorithmContext context = {
170         {fileInfo_.fileInfo.dataOffset, 0},
171         {fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize},
172         0, fileInfo_.fileInfo.digestMethod
173     };
174     int32_t ret = algorithm->Unpack(inStream, outStream, context);
175     PKG_CHECK(ret == PKG_SUCCESS, return ret, "Fail Decompress for %s", fileInfo_.fileInfo.identity.c_str());
176 
177     // Get uncompressed size
178     size_t readLen = 0;
179     fileInfo_.fileInfo.packedSize = context.packedSize;
180     PkgBuffer buffer(BLOCK_SIZE); // Read last 8 bytes at the end of package
181     ret = inStream->Read(buffer, context.packedSize + fileInfo_.fileInfo.dataOffset, BLOCK_SIZE, readLen);
182     PKG_CHECK(ret == PKG_SUCCESS, return ret, "Fail to read file %s", inStream->GetFileName().c_str());
183     crc32_ = ReadLE32(buffer.buffer);
184     fileInfo_.fileInfo.unpackedSize = ReadLE32(buffer.buffer + sizeof(uint32_t));
185     PKG_CHECK(crc32_ == context.crc, return ret, "Crc error %u %u", crc32_, context.crc);
186     PKG_CHECK(fileInfo_.fileInfo.unpackedSize == context.unpackedSize,
187         return ret, "Crc error %u %u", crc32_, context.crc);
188 
189     PKG_LOGI("packedSize: %zu unpackedSize: %zu  offset header: %zu data: %zu", fileInfo_.fileInfo.packedSize,
190         fileInfo_.fileInfo.unpackedSize, fileInfo_.fileInfo.headerOffset, fileInfo_.fileInfo.dataOffset);
191     outStream->Flush(fileInfo_.fileInfo.unpackedSize);
192     algorithm->UpdateFileInfo(&fileInfo_.fileInfo);
193     return PKG_SUCCESS;
194 }
195 
DecodeHeaderCalOffset(uint8_t flags,const PkgBuffer & buffer,size_t & offset,std::string & fileName) const196 void GZipFileEntry::DecodeHeaderCalOffset(uint8_t flags, const PkgBuffer &buffer, size_t &offset,
197     std::string &fileName) const
198 {
199     if (flags & EXTRA_FIELD) {
200         uint16_t extLen = ReadLE16(buffer.buffer + offset);
201         offset += sizeof(uint16_t) + extLen;
202     }
203     if (flags & ORIG_NAME) {
204         PkgFile::ConvertBufferToString(fileName, {buffer.buffer + offset, buffer.length - offset});
205         offset += fileName.size() + 1;
206     }
207     if (flags & COMMENT) {
208         std::string comment;
209         PkgFile::ConvertBufferToString(comment, {buffer.buffer + offset, buffer.length - offset});
210         offset += comment.size() + 1;
211     }
212     if (flags & HEADER_CRC) { // Skip CRC
213         offset += sizeof(uint16_t);
214     }
215     return;
216 }
217 
DecodeHeader(const PkgBuffer & buffer,size_t headerOffset,size_t dataOffset,size_t & decodeLen)218 int32_t GZipFileEntry::DecodeHeader(const PkgBuffer &buffer, size_t headerOffset, size_t dataOffset,
219     size_t &decodeLen)
220 {
221     PkgStreamPtr inStream = pkgFile_->GetPkgStream();
222     if (inStream == nullptr || buffer.buffer == nullptr) {
223         PKG_LOGE("outStream or inStream null for %s", fileInfo_.fileInfo.identity.c_str());
224         return PKG_INVALID_PARAM;
225     }
226     size_t offset = sizeof(GZipHeader);
227 
228     uint8_t flags = *(buffer.buffer + offsetof(GZipHeader, flags));
229 
230     DecodeHeaderCalOffset(flags, buffer, offset, fileName_);
231     if (fileName_.empty()) {
232         fileInfo_.fileInfo.identity = "gzip_";
233         fileInfo_.fileInfo.identity.append(std::to_string(nodeId_));
234         fileName_ = fileInfo_.fileInfo.identity;
235     } else {
236         fileInfo_.fileInfo.identity = fileName_;
237     }
238     fileInfo_.fileInfo.digestMethod = PKG_DIGEST_TYPE_CRC;
239     fileInfo_.fileInfo.packMethod = PKG_COMPRESS_METHOD_GZIP;
240     fileInfo_.level = Z_BEST_COMPRESSION;
241     fileInfo_.method = Z_DEFLATED;
242     fileInfo_.windowBits = -MAX_WBITS;
243     fileInfo_.memLevel = DEF_MEM_LEVEL;
244     fileInfo_.strategy = Z_DEFAULT_STRATEGY;
245 
246     fileInfo_.fileInfo.headerOffset = headerOffset;
247     fileInfo_.fileInfo.dataOffset = headerOffset + offset;
248     // Read data length here.
249     // The length read here maybe incorrect, so should adjust it
250     // when unpack.
251     size_t readLen = 0;
252     size_t blockOffset = inStream->GetFileLength() - BLOCK_SIZE;
253     int32_t ret = inStream->Read(buffer, blockOffset, buffer.length, readLen);
254     if (ret != PKG_SUCCESS) {
255         PKG_LOGE("Fail to read file %s", inStream->GetFileName().c_str());
256         return ret;
257     }
258     fileInfo_.fileInfo.unpackedSize = ReadLE32(buffer.buffer + sizeof(uint32_t));
259     fileInfo_.fileInfo.packedSize = blockOffset - fileInfo_.fileInfo.dataOffset;
260     PKG_LOGI("GZipFileEntry::DecodeHeader dataOffset %zu, packedSize: %zu %zu", fileInfo_.fileInfo.dataOffset,
261         fileInfo_.fileInfo.packedSize, fileInfo_.fileInfo.unpackedSize);
262     decodeLen = offset;
263     return PKG_SUCCESS;
264 }
265 
AddEntry(const PkgManager::FileInfoPtr file,const PkgStreamPtr inStream)266 int32_t GZipPkgFile::AddEntry(const PkgManager::FileInfoPtr file, const PkgStreamPtr inStream)
267 {
268     if (file == nullptr || inStream == nullptr) {
269         PKG_LOGE("Fail to check input param");
270         return PKG_INVALID_PARAM;
271     }
272     if (!CheckState({PKG_FILE_STATE_IDLE, PKG_FILE_STATE_WORKING}, PKG_FILE_STATE_CLOSE)) {
273         PKG_LOGE("error state curr %d ", state_);
274         return PKG_INVALID_STATE;
275     }
276     PKG_LOGI("Add file %s to package", file->identity.c_str());
277 
278     GZipFileEntry *entry = static_cast<GZipFileEntry *>(AddPkgEntry(file->identity));
279     if (entry == nullptr) {
280         PKG_LOGE("Fail create pkg node for %s", file->identity.c_str());
281         return PKG_NONE_MEMORY;
282     }
283     int32_t ret = entry->Init(file, inStream);
284     if (ret != PKG_SUCCESS) {
285         PKG_LOGE("Fail init entry for %s", file->identity.c_str());
286         return ret;
287     }
288 
289     size_t encodeLen = 0;
290     ret = entry->EncodeHeader(inStream, currentOffset_, encodeLen);
291     if (ret != PKG_SUCCESS) {
292         PKG_LOGE("Fail encode header for %s", file->identity.c_str());
293         return ret;
294     }
295     currentOffset_ += encodeLen;
296     ret = entry->Pack(inStream, currentOffset_, encodeLen);
297     if (ret != PKG_SUCCESS) {
298         PKG_LOGE("Fail Pack for %s", file->identity.c_str());
299         return ret;
300     }
301     currentOffset_ += encodeLen;
302     pkgStream_->Flush(currentOffset_);
303     return PKG_SUCCESS;
304 }
305 
SavePackage(size_t & offset)306 int32_t GZipPkgFile::SavePackage(size_t &offset)
307 {
308     AddSignData(pkgInfo_.digestMethod, currentOffset_, offset);
309     return PKG_SUCCESS;
310 }
311 
LoadPackage(std::vector<std::string> & fileNames,VerifyFunction verifier)312 int32_t GZipPkgFile::LoadPackage(std::vector<std::string> &fileNames, VerifyFunction verifier)
313 {
314     UNUSED(verifier);
315     if (!CheckState({ PKG_FILE_STATE_IDLE }, PKG_FILE_STATE_WORKING)) {
316         PKG_LOGE("error state curr %d ", state_);
317         return PKG_INVALID_STATE;
318     }
319     PKG_LOGI("LoadPackage %s ", pkgStream_->GetFileName().c_str());
320     size_t srcOffset = 0;
321     size_t readLen = 0;
322     PkgBuffer buffer(BUFFER_SIZE);
323     int32_t ret = pkgStream_->Read(buffer, srcOffset, buffer.length, readLen);
324     if (ret != PKG_SUCCESS) {
325         PKG_LOGE("Fail to read file %s", pkgStream_->GetFileName().c_str());
326         return ret;
327     }
328 
329     GZipHeader *header = reinterpret_cast<GZipHeader *>(buffer.buffer);
330     // Check magic number
331     if (header->magic != GZIP_MAGIC) {
332         PKG_LOGE("Invalid gzip file %s", pkgStream_->GetFileName().c_str());
333         return PKG_INVALID_FILE;
334     }
335     // Does not support encryption
336     if ((header->flags & ENCRYPTED) != 0) {
337         PKG_LOGE("Not support encrypted ");
338         return PKG_INVALID_FILE;
339     }
340 
341     GZipFileEntry *entry = new GZipFileEntry(this, nodeId_++);
342     if (entry == nullptr) {
343         PKG_LOGE("Fail create gzip node for %s", pkgStream_->GetFileName().c_str());
344         return PKG_LZ4_FINISH;
345     }
346     ret = entry->DecodeHeader(buffer, srcOffset, srcOffset, readLen);
347     srcOffset += readLen;
348 
349     // Save entry
350     pkgEntryMapId_.insert(std::pair<uint32_t, PkgEntryPtr>(entry->GetNodeId(), entry));
351     pkgEntryMapFileName_.insert(std::pair<std::string, PkgEntryPtr>(entry->GetFileName(), entry));
352     fileNames.push_back(entry->GetFileName());
353     return ret;
354 }
355 } // namespace Hpackage
356