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