• 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 "applypatch/block_set.h"
17 #include <linux/fs.h>
18 #include <sys/ioctl.h>
19 #include <openssl/sha.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 #include "applypatch/command.h"
24 #include "applypatch/store.h"
25 #include "applypatch/transfer_manager.h"
26 #include "log/dump.h"
27 #include "log/log.h"
28 #include "patch/update_patch.h"
29 #include "securec.h"
30 #include "utils.h"
31 
32 using namespace Updater;
33 using namespace Updater::Utils;
34 
35 namespace Updater {
BlockSet(std::vector<BlockPair> && pairs)36 BlockSet::BlockSet(std::vector<BlockPair> &&pairs)
37 {
38     blockSize_ = 0;
39     if (pairs.empty()) {
40         LOG(ERROR) << "Invalid block.";
41         return;
42     }
43 
44     for (const auto &pair : pairs) {
45         if (!CheckReliablePair(pair)) {
46             return;
47         }
48         PushBack(pair);
49     }
50 }
51 
CheckReliablePair(BlockPair pair)52 bool BlockSet::CheckReliablePair(BlockPair pair)
53 {
54     if (pair.first >= pair.second) {
55         LOG(ERROR) << "Invalid number of block size";
56         return false;
57     }
58     size_t size = pair.second - pair.first;
59     if (blockSize_ >= (SIZE_MAX - size)) {
60         LOG(ERROR) << "Block size overflow";
61         return false;
62     }
63     return true;
64 }
65 
PushBack(BlockPair blockPair)66 void BlockSet::PushBack(BlockPair blockPair)
67 {
68     blocks_.push_back(std::move(blockPair));
69     blockSize_ += (blockPair.second - blockPair.first);
70 }
71 
ClearBlocks()72 void BlockSet::ClearBlocks()
73 {
74     blockSize_ = 0;
75     blocks_.clear();
76 }
77 
ParserAndInsert(const std::string & blockStr)78 bool BlockSet::ParserAndInsert(const std::string &blockStr)
79 {
80     if (blockStr == "") {
81         LOG(ERROR) << "Invalid argument, this argument is empty";
82         return false;
83     }
84     std::vector<std::string> pairs = SplitString(blockStr, ",");
85     return ParserAndInsert(pairs);
86 }
87 
ParserAndInsert(const std::vector<std::string> & blockToken)88 bool BlockSet::ParserAndInsert(const std::vector<std::string> &blockToken)
89 {
90     ClearBlocks();
91     if (blockToken.empty()) {
92         LOG(ERROR) << "Invalid block token argument";
93         return false;
94     }
95     if (blockToken.size() < 3) { // 3:blockToken.size() < 3 means too small blocks_ in argument
96         LOG(ERROR) << "Too small blocks_ in argument";
97         return false;
98     }
99     // Get number of blockToken
100     unsigned long long int blockPairSize;
101     std::vector<std::string> bt = blockToken;
102     std::vector<std::string>::iterator bp = bt.begin();
103     blockPairSize = String2Int<unsigned long long int>(*bp, N_DEC);
104     if (blockPairSize == 0 || blockPairSize % 2 != 0 || // 2:Check whether blockPairSize is valid.
105         blockPairSize != bt.size() - 1) {
106         LOG(ERROR) << "Invalid number in block token";
107         return false;
108     }
109 
110     while (++bp != bt.end()) {
111         size_t first = String2Int<size_t>(*bp++, N_DEC);
112         size_t second = String2Int<size_t>(*bp, N_DEC);
113         blocks_.push_back(BlockPair {
114             first, second
115         });
116         blockSize_ += (second - first);
117     }
118     return true;
119 }
120 
Begin()121 std::vector<BlockPair>::iterator BlockSet::Begin()
122 {
123     return blocks_.begin();
124 }
125 
End()126 std::vector<BlockPair>::iterator BlockSet::End()
127 {
128     return blocks_.end();
129 }
130 
CBegin() const131 std::vector<BlockPair>::const_iterator BlockSet::CBegin() const
132 {
133     return blocks_.cbegin();
134 }
135 
CEnd() const136 std::vector<BlockPair>::const_iterator BlockSet::CEnd() const
137 {
138     return blocks_.cend();
139 }
140 
CrBegin() const141 std::vector<BlockPair>::const_reverse_iterator BlockSet::CrBegin() const
142 {
143     return blocks_.crbegin();
144 }
145 
CrEnd() const146 std::vector<BlockPair>::const_reverse_iterator BlockSet::CrEnd() const
147 {
148     return blocks_.crend();
149 }
150 
ReadDataFromBlock(int fd,std::vector<uint8_t> & buffer)151 size_t BlockSet::ReadDataFromBlock(int fd, std::vector<uint8_t> &buffer)
152 {
153     size_t pos = 0;
154     std::vector<BlockPair>::iterator it = blocks_.begin();
155     int ret;
156     for (; it != blocks_.end(); ++it) {
157         ret = lseek64(fd, static_cast<off64_t>(it->first * H_BLOCK_SIZE), SEEK_SET);
158         if (ret == -1) {
159             LOG(ERROR) << "Fail to seek";
160             return 0;
161         }
162         size_t size = (it->second - it->first) * H_BLOCK_SIZE;
163         if (!Utils::ReadFully(fd, buffer.data() + pos, size)) {
164             LOG(ERROR) << "Fail to read";
165             return 0;
166         }
167         pos += size;
168     }
169     return pos;
170 }
171 
WriteDataToBlock(int fd,std::vector<uint8_t> & buffer)172 size_t BlockSet::WriteDataToBlock(int fd, std::vector<uint8_t> &buffer)
173 {
174     size_t pos = 0;
175     std::vector<BlockPair>::iterator it = blocks_.begin();
176     int ret = 0;
177     for (; it != blocks_.end(); ++it) {
178         off64_t offset = static_cast<off64_t>(it->first * H_BLOCK_SIZE);
179         size_t writeSize = (it->second - it->first) * H_BLOCK_SIZE;
180 
181         ret = lseek64(fd, offset, SEEK_SET);
182         if (ret == -1) {
183             LOG(ERROR) << "BlockSet::WriteDataToBlock Fail to seek";
184             return 0;
185         }
186         if (Utils::WriteFully(fd, buffer.data() + pos, writeSize) == false) {
187             LOG(ERROR) << "Write data to block error, errno : " << errno;
188             return 0;
189         }
190         pos += writeSize;
191     }
192     if (fsync(fd) == -1) {
193         LOG(ERROR) << "Failed to fsync" << strerror(errno);
194         return 0;
195     }
196     return pos;
197 }
198 
CountOfRanges() const199 size_t BlockSet::CountOfRanges() const
200 {
201     return blocks_.size();
202 }
203 
TotalBlockSize() const204 size_t BlockSet::TotalBlockSize() const
205 {
206     return blockSize_;
207 }
208 
VerifySha256(const std::vector<uint8_t> & buffer,const size_t size,const std::string & expected)209 int32_t BlockSet::VerifySha256(const std::vector<uint8_t> &buffer, const size_t size, const std::string &expected)
210 {
211     UPDATER_INIT_RECORD;
212     uint8_t digest[SHA256_DIGEST_LENGTH];
213     SHA256(buffer.data(), size * H_BLOCK_SIZE, digest);
214     std::string hexdigest = Utils::ConvertSha256Hex(digest, SHA256_DIGEST_LENGTH);
215     if (hexdigest == expected) {
216         return 0;
217     }
218     return -1;
219 }
220 
IsTwoBlocksOverlap(const BlockSet & source,BlockSet & target)221 bool BlockSet::IsTwoBlocksOverlap(const BlockSet &source, BlockSet &target)
222 {
223     auto firstIter = source.CBegin();
224     for (; firstIter != source.CEnd(); ++firstIter) {
225         std::vector<BlockPair>::iterator secondIter = target.Begin();
226         for (; secondIter != target.End(); ++secondIter) {
227             if (!(secondIter->first >= firstIter->second ||
228                 firstIter->first >= secondIter->second)) {
229                 return true;
230             }
231         }
232     }
233     return false;
234 }
235 
MoveBlock(std::vector<uint8_t> & target,const BlockSet & locations,const std::vector<uint8_t> & source)236 void BlockSet::MoveBlock(std::vector<uint8_t> &target, const BlockSet& locations,
237     const std::vector<uint8_t>& source)
238 {
239     const uint8_t *sd = source.data();
240     uint8_t *td = target.data();
241     size_t start = locations.TotalBlockSize();
242     for (auto it = locations.CrBegin(); it != locations.CrEnd(); it++) {
243         size_t blocks = it->second - it->first;
244         start -= blocks;
245         if (memmove_s(td + (it->first * H_BLOCK_SIZE), blocks * H_BLOCK_SIZE, sd + (start *
246             H_BLOCK_SIZE), blocks * H_BLOCK_SIZE) != EOK) {
247                 LOG(ERROR) << "MoveBlock memmove_s failed!";
248                 return;
249             }
250     }
251 }
252 
LoadSourceBuffer(const Command & cmd,size_t & pos,std::vector<uint8_t> & sourceBuffer,bool & isOverlap,size_t & srcBlockSize)253 int32_t BlockSet::LoadSourceBuffer(const Command &cmd, size_t &pos, std::vector<uint8_t> &sourceBuffer,
254     bool &isOverlap, size_t &srcBlockSize)
255 {
256     std::string targetCmd = cmd.GetArgumentByPos(pos++);
257     std::string storeBase = cmd.GetTransferParams()->storeBase;
258     if (targetCmd != "-") {
259         BlockSet srcBlk;
260         srcBlk.ParserAndInsert(targetCmd);
261         if (!cmd.IsStreamCmd()) {
262             isOverlap = IsTwoBlocksOverlap(srcBlk, *this);
263         }
264         // read source data
265         if (srcBlk.ReadDataFromBlock(cmd.GetSrcFileDescriptor(), sourceBuffer) == 0) {
266             LOG(ERROR) << "ReadDataFromBlock failed";
267             return -1;
268         }
269         std::string nextArgv = cmd.GetArgumentByPos(pos++);
270         if (nextArgv == "") {
271             return 1;
272         }
273         BlockSet locations;
274         locations.ParserAndInsert(nextArgv);
275         MoveBlock(sourceBuffer, locations, sourceBuffer);
276     }
277 
278     std::string lastArg = cmd.GetArgumentByPos(pos++);
279     while (lastArg != "") {
280         std::vector<std::string> tokens = SplitString(lastArg, ":");
281         if (tokens.size() != H_CMD_ARGS_LIMIT) {
282             LOG(ERROR) << "invalid parameter";
283             return -1;
284         }
285         std::vector<uint8_t> stash;
286         auto ret = Store::LoadDataFromStore(storeBase, tokens[H_ZERO_NUMBER], stash);
287         if (ret == -1) {
288             LOG(ERROR) << "Failed to load tokens";
289             return -1;
290         }
291         BlockSet locations;
292         locations.ParserAndInsert(tokens[1]);
293         MoveBlock(sourceBuffer, locations, stash);
294 
295         lastArg = cmd.GetArgumentByPos(pos++);
296     }
297     return 1;
298 }
299 
BlockVerify(const Command & cmd,std::vector<uint8_t> & buffer,const size_t size,const std::string srcHash,size_t & pos)300 __attribute__((weak)) int32_t BlockVerify(const Command &cmd, std::vector<uint8_t> &buffer,
301     const size_t size, const std::string srcHash, size_t &pos)
302 {
303     return -1;
304 }
305 
306 
LoadTargetBuffer(const Command & cmd,std::vector<uint8_t> & buffer,size_t & blockSize,size_t pos,std::string & srcHash)307 int32_t BlockSet::LoadTargetBuffer(const Command &cmd, std::vector<uint8_t> &buffer, size_t &blockSize,
308     size_t pos, std::string &srcHash)
309 {
310     bool isOverlap = false;
311     auto ret = LoadSourceBuffer(cmd, pos, buffer, isOverlap, blockSize);
312     if (ret != 1) {
313         LOG(ERROR) << "LoadSourceBuffer failed";
314         return ret;
315     }
316     if (cmd.IsStreamCmd()) {
317         return 0;
318     }
319 
320     std::string storeBase = cmd.GetTransferParams()->storeBase;
321     std::string storePath = storeBase + "/" + srcHash;
322     struct stat storeStat {};
323     int res = stat(storePath.c_str(), &storeStat);
324     int32_t verifyRes = VerifySha256(buffer, blockSize, srcHash);
325     if (verifyRes != 0 && !cmd.GetTransferParams()->canWrite) {
326         return BlockVerify(cmd, buffer, blockSize, srcHash, pos);
327     }
328     if (verifyRes == 0) {
329         if (isOverlap && res != 0) {
330             cmd.GetTransferParams()->freeStash = srcHash;
331             ret = Store::WriteDataToStore(storeBase, srcHash, buffer, blockSize * H_BLOCK_SIZE);
332             if (ret != 0) {
333                 LOG(ERROR) << "failed to stash overlapping source blocks";
334                 return -1;
335             }
336         }
337         return 0;
338     }
339     if (Store::LoadDataFromStore(storeBase, srcHash, buffer) == 0) {
340         return 0;
341     }
342     return -1;
343 }
344 
WriteZeroToBlock(int fd,bool isErase)345 int32_t BlockSet::WriteZeroToBlock(int fd, bool isErase)
346 {
347     std::vector<uint8_t> buffer;
348     buffer.resize(H_BLOCK_SIZE);
349     if (memset_s(buffer.data(), H_BLOCK_SIZE, 0, H_BLOCK_SIZE) != EOK) {
350         LOG(ERROR) << "memset_s failed";
351         return -1;
352     }
353 
354     auto iter = blocks_.begin();
355     while (iter != blocks_.end()) {
356         off64_t offset = static_cast<off64_t>(iter->first * H_BLOCK_SIZE);
357         int ret = 0;
358 
359         if (isErase && Utils::IsUpdaterMode()) {
360 #ifndef UPDATER_UT
361             size_t writeSize = (iter->second - iter->first) * H_BLOCK_SIZE;
362             uint64_t arguments[2] = {static_cast<uint64_t>(offset), writeSize};
363             ret = ioctl(fd, BLKDISCARD, &arguments);
364             if (ret == -1 && errno != EOPNOTSUPP) {
365                 LOG(ERROR) << "Error to write block set to memory";
366                 return -1;
367             }
368 #endif
369             iter++;
370             continue;
371         }
372         ret = lseek64(fd, offset, SEEK_SET);
373         if (ret == -1) {
374             LOG(ERROR) << "BlockSet::WriteZeroToBlock Fail to seek";
375             return -1;
376         }
377         for (size_t pos = iter->first; pos < iter->second; pos++) {
378             if (Utils::WriteFully(fd, buffer.data(), H_BLOCK_SIZE)) {
379                 continue;
380             }
381             if (errno == EIO) {
382                 return 1;
383             }
384             LOG(ERROR) << "BlockSet::WriteZeroToBlock Write 0 to block error";
385             return -1;
386         }
387         iter++;
388     }
389     return 0;
390 }
391 
WriteDiffToBlock(const Command & cmd,std::vector<uint8_t> & sourceBuffer,uint8_t * patchBuffer,size_t patchLength,bool isImgDiff)392 int32_t BlockSet::WriteDiffToBlock(const Command &cmd, std::vector<uint8_t> &sourceBuffer, uint8_t *patchBuffer,
393                                    size_t patchLength, bool isImgDiff)
394 {
395     size_t srcBuffSize =  sourceBuffer.size();
396     if (isImgDiff) {
397         std::vector<uint8_t> empty;
398         UpdatePatch::PatchParam patchParam = {sourceBuffer.data(), srcBuffSize, patchBuffer, patchLength};
399         std::unique_ptr<BlockWriter> writer = std::make_unique<BlockWriter>(cmd.GetTargetFileDescriptor(), *this);
400         if (writer.get() == nullptr) {
401             LOG(ERROR) << "Cannot create block writer, pkgdiff patch abort!";
402             return -1;
403         }
404         int32_t ret = UpdatePatch::UpdateApplyPatch::ApplyImagePatch(patchParam, empty,
405             [&](size_t start, const UpdatePatch::BlockBuffer &data, size_t size) -> int {
406                 return (writer->Write(data.buffer, size, nullptr)) ? 0 : -1;
407             }, cmd.GetArgumentByPos(H_DIFF_CMD_ARGS_START + 1));
408         writer.reset();
409         if (ret != 0) {
410             LOG(ERROR) << "Fail to ApplyImagePatch";
411             return -1;
412         }
413     } else {
414         LOG(DEBUG) << "Run bsdiff patch.";
415         UpdatePatch::PatchBuffer patchInfo = {patchBuffer, 0, patchLength};
416         std::unique_ptr<BlockWriter> writer = std::make_unique<BlockWriter>(cmd.GetTargetFileDescriptor(), *this);
417         if (writer.get() == nullptr) {
418             LOG(ERROR) << "Cannot create block writer, pkgdiff patch abort!";
419             return -1;
420         }
421         auto ret = UpdatePatch::UpdateApplyPatch::ApplyBlockPatch(patchInfo, {sourceBuffer.data(), srcBuffSize},
422             [&](size_t start, const UpdatePatch::BlockBuffer &data, size_t size) -> int {
423                 return (writer->Write(data.buffer, size, nullptr)) ? 0 : -1;
424             }, cmd.GetArgumentByPos(H_DIFF_CMD_ARGS_START + 1));
425         writer.reset();
426         if (ret != 0) {
427             LOG(ERROR) << "Fail to ApplyBlockPatch";
428             return -1;
429         }
430     }
431     if (fsync(cmd.GetTargetFileDescriptor()) == -1) {
432         LOG(ERROR) << "Failed to sync restored data";
433         return -1;
434     }
435     return 0;
436 }
437 } // namespace Updater
438