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 "update_diff.h"
17 #include <cstdlib>
18 #include <memory>
19 #include "image_diff.h"
20 #include "pkg_manager.h"
21 
22 using namespace Hpackage;
23 
24 namespace UpdatePatch {
25 #ifdef __WIN32
26 #undef ERROR
27 #endif
28 
~ImageParser()29 ImageParser::~ImageParser()
30 {
31     Hpackage::PkgManager::ReleasePackageInstance(pkgManager_);
32     pkgManager_ = nullptr;
33 }
34 
GetPkgBuffer(BlockBuffer & buffer) const35 int32_t ImageParser::GetPkgBuffer(BlockBuffer &buffer) const
36 {
37     int32_t ret = -1;
38     Hpackage::PkgBuffer pkgBuffer {};
39     if (stream_ != nullptr) {
40         ret = stream_->GetBuffer(pkgBuffer);
41         buffer.buffer = pkgBuffer.buffer;
42         buffer.length = pkgBuffer.length;
43     }
44     return ret;
45 }
46 
GetFileInfo(const std::string & fileName) const47 const Hpackage::FileInfo *ImageParser::GetFileInfo(const std::string &fileName) const
48 {
49     if (pkgManager_ != nullptr) {
50         return pkgManager_->GetFileInfo(fileName);
51     }
52     return nullptr;
53 }
54 
Parse(const std::string & packageName)55 int32_t ImageParser::Parse(const std::string &packageName)
56 {
57     pkgManager_ = Hpackage::PkgManager::CreatePackageInstance();
58     if (pkgManager_ == nullptr) {
59         PATCH_LOGE("Failed to get pkg manager");
60         return PATCH_INVALID_PARAM;
61     }
62     int32_t ret = PatchMapFile(packageName, memMap_);
63     if (ret != 0) {
64         PATCH_LOGE("Failed to read file");
65         return -1;
66     }
67 
68     PkgBuffer buffer {memMap_.memory, memMap_.length};
69     ret = pkgManager_->CreatePkgStream(stream_, packageName, buffer);
70     if (ret != 0) {
71         PATCH_LOGE("Failed to create pkg stream");
72         return -1;
73     }
74 
75     // parse img and get image type
76     type_ = PKG_PACK_TYPE_ZIP;
77     ret = pkgManager_->ParsePackage(stream_, fileIds_, type_);
78     if (ret == 0) {
79         return 0;
80     }
81     type_ = PKG_PACK_TYPE_LZ4;
82     ret = pkgManager_->ParsePackage(stream_, fileIds_, type_);
83     if (ret == 0) {
84         return 0;
85     }
86     type_ = PKG_PACK_TYPE_GZIP;
87     ret = pkgManager_->ParsePackage(stream_, fileIds_, type_);
88     if (ret == 0) {
89         return 0;
90     }
91     type_ = PKG_PACK_TYPE_NONE;
92     return 0;
93 }
94 
Extract(const std::string & fileName,std::vector<uint8_t> & buffer)95 int32_t ImageParser::Extract(const std::string &fileName, std::vector<uint8_t> &buffer)
96 {
97     PATCH_DEBUG("ImageParser::Extract %s", fileName.c_str());
98     if (pkgManager_ == nullptr) {
99         PATCH_LOGE("Failed to get pkg manager");
100         return PATCH_INVALID_PARAM;
101     }
102     size_t bufferSize = 0;
103     Hpackage::PkgManager::StreamPtr outStream = nullptr;
104     int32_t ret = pkgManager_->CreatePkgStream(outStream, fileName,
105         [&buffer, &bufferSize](const PkgBuffer &data, size_t size,
106             size_t start, bool isFinish, const void *context) ->int {
107             if (isFinish) {
108                 return 0;
109             }
110             bufferSize += size;
111             if ((start + bufferSize) > buffer.size()) {
112                 buffer.resize(IGMDIFF_LIMIT_UNIT * ((start + bufferSize) / IGMDIFF_LIMIT_UNIT + 1));
113             }
114             return memcpy_s(buffer.data() + start, buffer.size(), data.buffer, size);
115         }, nullptr);
116     if (ret != 0) {
117         PATCH_LOGE("Failed to extract data");
118         return -1;
119     }
120 
121     ret = pkgManager_->ExtractFile(fileName, outStream);
122     pkgManager_->ClosePkgStream(outStream);
123 
124     const FileInfo *fileInfo = pkgManager_->GetFileInfo(fileName);
125     if (fileInfo == nullptr) {
126         PATCH_LOGE("Failed to get file info");
127         return -1;
128     }
129     if (fileInfo->unpackedSize != bufferSize) {
130         PATCH_LOGE("Failed to check uncompress data size %zu %zu", fileInfo->unpackedSize, bufferSize);
131         return -1;
132     }
133     return ret;
134 }
135 
MakePatch(const std::string & oldFileName,const std::string & newFileName,const std::string & patchFileName)136 int32_t UpdateDiff::MakePatch(const std::string &oldFileName,
137     const std::string &newFileName, const std::string &patchFileName)
138 {
139     if (blockDiff_) {
140         return BlocksDiff::MakePatch(oldFileName, newFileName, patchFileName);
141     }
142 
143     newParser_.reset(new ImageParser());
144     oldParser_.reset(new ImageParser());
145     if (newParser_ == nullptr || oldParser_ == nullptr) {
146         PATCH_LOGE("Failed to create parser");
147         return -1;
148     }
149 
150     if (newParser_->Parse(newFileName) != 0 || oldParser_->Parse(oldFileName) != 0) {
151         PATCH_LOGE("Failed to parse image");
152         return -1;
153     }
154     std::unique_ptr<ImageDiff> imageDiff = nullptr;
155     PATCH_DEBUG("UpdateDiff::MakePatch type: %d %d", newParser_->GetType(), oldParser_->GetType());
156     if (newParser_->GetType() != oldParser_->GetType()) {
157         imageDiff.reset(new ImageDiff(limit_, newParser_.get(), oldParser_.get()));
158         if (imageDiff == nullptr) {
159             PATCH_LOGE("Failed to diff file");
160             return -1;
161         }
162         return imageDiff->MakePatch(patchFileName);
163     }
164 
165     switch (newParser_->GetType()) {
166         case PKG_PACK_TYPE_ZIP:
167             imageDiff.reset(new ZipImageDiff(limit_, newParser_.get(), oldParser_.get()));
168             break;
169         case PKG_PACK_TYPE_LZ4:
170             imageDiff.reset(new Lz4ImageDiff(limit_, newParser_.get(), oldParser_.get()));
171             break;
172         case PKG_PACK_TYPE_GZIP:
173             imageDiff.reset(new GZipImageDiff(limit_, newParser_.get(), oldParser_.get()));
174             break;
175         default:
176             imageDiff.reset(new ImageDiff(limit_, newParser_.get(), oldParser_.get()));
177             break;
178     }
179     if (imageDiff == nullptr) {
180         PATCH_LOGE("Failed to diff file");
181         return -1;
182     }
183     return imageDiff->MakePatch(patchFileName);
184 }
185 
DiffImage(size_t limit,const std::string & oldFileName,const std::string & newFileName,const std::string & patchFileName)186 int32_t UpdateDiff::DiffImage(size_t limit, const std::string &oldFileName,
187     const std::string &newFileName, const std::string &patchFileName)
188 {
189     auto updateDiff = std::make_unique<UpdateDiff>(limit, false);
190     if (updateDiff == nullptr) {
191         PATCH_LOGE("Failed to create update diff");
192         return -1;
193     }
194     return updateDiff->MakePatch(oldFileName, newFileName, patchFileName);
195 }
196 
DiffBlock(const std::string & oldFileName,const std::string & newFileName,const std::string & patchFileName)197 int32_t UpdateDiff::DiffBlock(const std::string &oldFileName,
198     const std::string &newFileName, const std::string &patchFileName)
199 {
200     auto updateDiff = std::make_unique<UpdateDiff>(0, true);
201     if (updateDiff == nullptr) {
202         PATCH_LOGE("Failed to create update diff");
203         return -1;
204     }
205     return updateDiff->MakePatch(oldFileName, newFileName, patchFileName);
206 }
207 } // namespace UpdatePatch
208