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