• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "private/commands.h"
18 
19 #include <stdint.h>
20 #include <string.h>
21 
22 #include <functional>
23 #include <ostream>
24 #include <string>
25 #include <vector>
26 
27 #include <android-base/logging.h>
28 #include <android-base/parseint.h>
29 #include <android-base/stringprintf.h>
30 #include <android-base/strings.h>
31 #include <openssl/sha.h>
32 
33 #include "otautil/print_sha1.h"
34 #include "otautil/rangeset.h"
35 
36 using namespace std::string_literals;
37 
38 bool Command::abort_allowed_ = false;
39 
Command(Type type,size_t index,std::string cmdline,HashTreeInfo hash_tree_info)40 Command::Command(Type type, size_t index, std::string cmdline, HashTreeInfo hash_tree_info)
41     : type_(type),
42       index_(index),
43       cmdline_(std::move(cmdline)),
44       hash_tree_info_(std::move(hash_tree_info)) {
45   CHECK(type == Type::COMPUTE_HASH_TREE);
46 }
47 
ParseType(const std::string & type_str)48 Command::Type Command::ParseType(const std::string& type_str) {
49   if (type_str == "abort") {
50     if (!abort_allowed_) {
51       LOG(ERROR) << "ABORT disallowed";
52       return Type::LAST;
53     }
54     return Type::ABORT;
55   } else if (type_str == "bsdiff") {
56     return Type::BSDIFF;
57   } else if (type_str == "compute_hash_tree") {
58     return Type::COMPUTE_HASH_TREE;
59   } else if (type_str == "erase") {
60     return Type::ERASE;
61   } else if (type_str == "free") {
62     return Type::FREE;
63   } else if (type_str == "imgdiff") {
64     return Type::IMGDIFF;
65   } else if (type_str == "move") {
66     return Type::MOVE;
67   } else if (type_str == "new") {
68     return Type::NEW;
69   } else if (type_str == "stash") {
70     return Type::STASH;
71   } else if (type_str == "zero") {
72     return Type::ZERO;
73   }
74   return Type::LAST;
75 };
76 
ParseTargetInfoAndSourceInfo(const std::vector<std::string> & tokens,const std::string & tgt_hash,TargetInfo * target,const std::string & src_hash,SourceInfo * source,std::string * err)77 bool Command::ParseTargetInfoAndSourceInfo(const std::vector<std::string>& tokens,
78                                            const std::string& tgt_hash, TargetInfo* target,
79                                            const std::string& src_hash, SourceInfo* source,
80                                            std::string* err) {
81   // We expect the given args (in 'tokens' vector) in one of the following formats.
82   //
83   //    <tgt_ranges> <src_block_count> - <[stash_id:location] ...>
84   //        (loads data from stashes only)
85   //
86   //    <tgt_ranges> <src_block_count> <src_ranges>
87   //        (loads data from source image only)
88   //
89   //    <tgt_ranges> <src_block_count> <src_ranges> <src_ranges_location> <[stash_id:location] ...>
90   //        (loads data from both of source image and stashes)
91 
92   // At least it needs to provide three args: <tgt_ranges>, <src_block_count> and "-"/<src_ranges>.
93   if (tokens.size() < 3) {
94     *err = "invalid number of args";
95     return false;
96   }
97 
98   size_t pos = 0;
99   RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
100   if (!tgt_ranges) {
101     *err = "invalid target ranges";
102     return false;
103   }
104   *target = TargetInfo(tgt_hash, tgt_ranges);
105 
106   // <src_block_count>
107   const std::string& token = tokens[pos++];
108   size_t src_blocks;
109   if (!android::base::ParseUint(token, &src_blocks)) {
110     *err = "invalid src_block_count \""s + token + "\"";
111     return false;
112   }
113 
114   RangeSet src_ranges;
115   RangeSet src_ranges_location;
116   // "-" or <src_ranges> [<src_ranges_location>]
117   if (tokens[pos] == "-") {
118     // no source ranges, only stashes
119     pos++;
120   } else {
121     src_ranges = RangeSet::Parse(tokens[pos++]);
122     if (!src_ranges) {
123       *err = "invalid source ranges";
124       return false;
125     }
126 
127     if (pos >= tokens.size()) {
128       // No stashes, only source ranges.
129       SourceInfo result(src_hash, src_ranges, {}, {});
130 
131       if (result.blocks() != src_blocks) {
132         *err =
133             android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
134                                         src_ranges.ToString().c_str(), src_blocks);
135         return false;
136       }
137 
138       *source = result;
139       return true;
140     }
141 
142     src_ranges_location = RangeSet::Parse(tokens[pos++]);
143     if (!src_ranges_location) {
144       *err = "invalid source ranges location";
145       return false;
146     }
147   }
148 
149   // <[stash_id:stash_location]>
150   std::vector<StashInfo> stashes;
151   while (pos < tokens.size()) {
152     // Each word is a an index into the stash table, a colon, and then a RangeSet describing where
153     // in the source block that stashed data should go.
154     std::vector<std::string> pairs = android::base::Split(tokens[pos++], ":");
155     if (pairs.size() != 2) {
156       *err = "invalid stash info";
157       return false;
158     }
159     RangeSet stash_location = RangeSet::Parse(pairs[1]);
160     if (!stash_location) {
161       *err = "invalid stash location";
162       return false;
163     }
164     stashes.emplace_back(pairs[0], stash_location);
165   }
166 
167   SourceInfo result(src_hash, src_ranges, src_ranges_location, stashes);
168   if (src_blocks != result.blocks()) {
169     *err = android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
170                                        src_ranges.ToString().c_str(), src_blocks);
171     return false;
172   }
173 
174   *source = result;
175   return true;
176 }
177 
Parse(const std::string & line,size_t index,std::string * err)178 Command Command::Parse(const std::string& line, size_t index, std::string* err) {
179   std::vector<std::string> tokens = android::base::Split(line, " ");
180   size_t pos = 0;
181   // tokens.size() will be 1 at least.
182   Type op = ParseType(tokens[pos++]);
183   if (op == Type::LAST) {
184     *err = "invalid type";
185     return {};
186   }
187 
188   PatchInfo patch_info;
189   TargetInfo target_info;
190   SourceInfo source_info;
191   StashInfo stash_info;
192 
193   if (op == Type::ZERO || op == Type::NEW || op == Type::ERASE) {
194     // zero/new/erase <rangeset>
195     if (pos + 1 != tokens.size()) {
196       *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
197                                          tokens.size() - pos);
198       return {};
199     }
200     RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
201     if (!tgt_ranges) {
202       return {};
203     }
204     static const std::string kUnknownHash{ "unknown-hash" };
205     target_info = TargetInfo(kUnknownHash, tgt_ranges);
206   } else if (op == Type::STASH) {
207     // stash <stash_id> <src_ranges>
208     if (pos + 2 != tokens.size()) {
209       *err = android::base::StringPrintf("invalid number of args: %zu (expected 2)",
210                                          tokens.size() - pos);
211       return {};
212     }
213     const std::string& id = tokens[pos++];
214     RangeSet src_ranges = RangeSet::Parse(tokens[pos++]);
215     if (!src_ranges) {
216       *err = "invalid token";
217       return {};
218     }
219     stash_info = StashInfo(id, src_ranges);
220   } else if (op == Type::FREE) {
221     // free <stash_id>
222     if (pos + 1 != tokens.size()) {
223       *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
224                                          tokens.size() - pos);
225       return {};
226     }
227     stash_info = StashInfo(tokens[pos++], {});
228   } else if (op == Type::MOVE) {
229     // <hash>
230     if (pos + 1 > tokens.size()) {
231       *err = "missing hash";
232       return {};
233     }
234     std::string hash = tokens[pos++];
235     if (!ParseTargetInfoAndSourceInfo(
236             std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), hash, &target_info,
237             hash, &source_info, err)) {
238       return {};
239     }
240   } else if (op == Type::BSDIFF || op == Type::IMGDIFF) {
241     // <offset> <length> <srchash> <dsthash>
242     if (pos + 4 > tokens.size()) {
243       *err = android::base::StringPrintf("invalid number of args: %zu (expected 4+)",
244                                          tokens.size() - pos);
245       return {};
246     }
247     size_t offset;
248     size_t length;
249     if (!android::base::ParseUint(tokens[pos++], &offset) ||
250         !android::base::ParseUint(tokens[pos++], &length)) {
251       *err = "invalid patch offset/length";
252       return {};
253     }
254     patch_info = PatchInfo(offset, length);
255 
256     std::string src_hash = tokens[pos++];
257     std::string dst_hash = tokens[pos++];
258     if (!ParseTargetInfoAndSourceInfo(
259             std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), dst_hash, &target_info,
260             src_hash, &source_info, err)) {
261       return {};
262     }
263   } else if (op == Type::ABORT) {
264     // Abort takes no arguments, so there's nothing else to check.
265     if (pos != tokens.size()) {
266       *err = android::base::StringPrintf("invalid number of args: %zu (expected 0)",
267                                          tokens.size() - pos);
268       return {};
269     }
270   } else if (op == Type::COMPUTE_HASH_TREE) {
271     // <hash_tree_ranges> <source_ranges> <hash_algorithm> <salt_hex> <root_hash>
272     if (pos + 5 != tokens.size()) {
273       *err = android::base::StringPrintf("invalid number of args: %zu (expected 5)",
274                                          tokens.size() - pos);
275       return {};
276     }
277 
278     // Expects the hash_tree data to be contiguous.
279     RangeSet hash_tree_ranges = RangeSet::Parse(tokens[pos++]);
280     if (!hash_tree_ranges || hash_tree_ranges.size() != 1) {
281       *err = "invalid hash tree ranges in: " + line;
282       return {};
283     }
284 
285     RangeSet source_ranges = RangeSet::Parse(tokens[pos++]);
286     if (!source_ranges) {
287       *err = "invalid source ranges in: " + line;
288       return {};
289     }
290 
291     std::string hash_algorithm = tokens[pos++];
292     std::string salt_hex = tokens[pos++];
293     std::string root_hash = tokens[pos++];
294     if (hash_algorithm.empty() || salt_hex.empty() || root_hash.empty()) {
295       *err = "invalid hash tree arguments in " + line;
296       return {};
297     }
298 
299     HashTreeInfo hash_tree_info(std::move(hash_tree_ranges), std::move(source_ranges),
300                                 std::move(hash_algorithm), std::move(salt_hex),
301                                 std::move(root_hash));
302     return Command(op, index, line, std::move(hash_tree_info));
303   } else {
304     *err = "invalid op";
305     return {};
306   }
307 
308   return Command(op, index, line, patch_info, target_info, source_info, stash_info);
309 }
310 
Overlaps(const TargetInfo & target) const311 bool SourceInfo::Overlaps(const TargetInfo& target) const {
312   return ranges_.Overlaps(target.ranges());
313 }
314 
315 // Moves blocks in the 'source' vector to the specified locations (as in 'locs') in the 'dest'
316 // vector. Note that source and dest may be the same buffer.
MoveRange(std::vector<uint8_t> * dest,const RangeSet & locs,const std::vector<uint8_t> & source,size_t block_size)317 static void MoveRange(std::vector<uint8_t>* dest, const RangeSet& locs,
318                       const std::vector<uint8_t>& source, size_t block_size) {
319   const uint8_t* from = source.data();
320   uint8_t* to = dest->data();
321   size_t start = locs.blocks();
322   // Must do the movement backward.
323   for (auto it = locs.crbegin(); it != locs.crend(); it++) {
324     size_t blocks = it->second - it->first;
325     start -= blocks;
326     memmove(to + (it->first * block_size), from + (start * block_size), blocks * block_size);
327   }
328 }
329 
ReadAll(std::vector<uint8_t> * buffer,size_t block_size,const std::function<int (const RangeSet &,std::vector<uint8_t> *)> & block_reader,const std::function<int (const std::string &,std::vector<uint8_t> *)> & stash_reader) const330 bool SourceInfo::ReadAll(
331     std::vector<uint8_t>* buffer, size_t block_size,
332     const std::function<int(const RangeSet&, std::vector<uint8_t>*)>& block_reader,
333     const std::function<int(const std::string&, std::vector<uint8_t>*)>& stash_reader) const {
334   if (buffer->size() < blocks() * block_size) {
335     return false;
336   }
337 
338   // Read in the source ranges.
339   if (ranges_) {
340     if (block_reader(ranges_, buffer) != 0) {
341       return false;
342     }
343     if (location_) {
344       MoveRange(buffer, location_, *buffer, block_size);
345     }
346   }
347 
348   // Read in the stashes.
349   for (const StashInfo& stash : stashes_) {
350     std::vector<uint8_t> stash_buffer(stash.blocks() * block_size);
351     if (stash_reader(stash.id(), &stash_buffer) != 0) {
352       return false;
353     }
354     MoveRange(buffer, stash.ranges(), stash_buffer, block_size);
355   }
356   return true;
357 }
358 
DumpBuffer(const std::vector<uint8_t> & buffer,size_t block_size) const359 void SourceInfo::DumpBuffer(const std::vector<uint8_t>& buffer, size_t block_size) const {
360   LOG(INFO) << "Dumping hashes in hex for " << ranges_.blocks() << " source blocks";
361 
362   const RangeSet& location = location_ ? location_ : RangeSet({ Range{ 0, ranges_.blocks() } });
363   for (size_t i = 0; i < ranges_.blocks(); i++) {
364     size_t block_num = ranges_.GetBlockNumber(i);
365     size_t buffer_index = location.GetBlockNumber(i);
366     CHECK_LE((buffer_index + 1) * block_size, buffer.size());
367 
368     uint8_t digest[SHA_DIGEST_LENGTH];
369     SHA1(buffer.data() + buffer_index * block_size, block_size, digest);
370     std::string hexdigest = print_sha1(digest);
371     LOG(INFO) << "  block number: " << block_num << ", SHA-1: " << hexdigest;
372   }
373 }
374 
operator <<(std::ostream & os,const Command & command)375 std::ostream& operator<<(std::ostream& os, const Command& command) {
376   os << command.index() << ": " << command.cmdline();
377   return os;
378 }
379 
operator <<(std::ostream & os,const TargetInfo & target)380 std::ostream& operator<<(std::ostream& os, const TargetInfo& target) {
381   os << target.blocks() << " blocks (" << target.hash_ << "): " << target.ranges_.ToString();
382   return os;
383 }
384 
operator <<(std::ostream & os,const StashInfo & stash)385 std::ostream& operator<<(std::ostream& os, const StashInfo& stash) {
386   os << stash.blocks() << " blocks (" << stash.id_ << "): " << stash.ranges_.ToString();
387   return os;
388 }
389 
operator <<(std::ostream & os,const SourceInfo & source)390 std::ostream& operator<<(std::ostream& os, const SourceInfo& source) {
391   os << source.blocks_ << " blocks (" << source.hash_ << "): ";
392   if (source.ranges_) {
393     os << source.ranges_.ToString();
394     if (source.location_) {
395       os << " (location: " << source.location_.ToString() << ")";
396     }
397   }
398   if (!source.stashes_.empty()) {
399     os << " " << source.stashes_.size() << " stash(es)";
400   }
401   return os;
402 }
403 
Parse(const std::string & transfer_list_str,std::string * err)404 TransferList TransferList::Parse(const std::string& transfer_list_str, std::string* err) {
405   TransferList result{};
406 
407   std::vector<std::string> lines = android::base::Split(transfer_list_str, "\n");
408   if (lines.size() < kTransferListHeaderLines) {
409     *err = android::base::StringPrintf("too few lines in the transfer list [%zu]", lines.size());
410     return TransferList{};
411   }
412 
413   // First line in transfer list is the version number.
414   if (!android::base::ParseInt(lines[0], &result.version_, 3, 4)) {
415     *err = "unexpected transfer list version ["s + lines[0] + "]";
416     return TransferList{};
417   }
418 
419   // Second line in transfer list is the total number of blocks we expect to write.
420   if (!android::base::ParseUint(lines[1], &result.total_blocks_)) {
421     *err = "unexpected block count ["s + lines[1] + "]";
422     return TransferList{};
423   }
424 
425   // Third line is how many stash entries are needed simultaneously.
426   if (!android::base::ParseUint(lines[2], &result.stash_max_entries_)) {
427     return TransferList{};
428   }
429 
430   // Fourth line is the maximum number of blocks that will be stashed simultaneously.
431   if (!android::base::ParseUint(lines[3], &result.stash_max_blocks_)) {
432     *err = "unexpected maximum stash blocks ["s + lines[3] + "]";
433     return TransferList{};
434   }
435 
436   // Subsequent lines are all individual transfer commands.
437   for (size_t i = kTransferListHeaderLines; i < lines.size(); i++) {
438     const std::string& line = lines[i];
439     if (line.empty()) continue;
440 
441     size_t cmdindex = i - kTransferListHeaderLines;
442     std::string parsing_error;
443     Command command = Command::Parse(line, cmdindex, &parsing_error);
444     if (!command) {
445       *err = android::base::StringPrintf("Failed to parse command %zu [%s]: %s", cmdindex,
446                                          line.c_str(), parsing_error.c_str());
447       return TransferList{};
448     }
449     result.commands_.push_back(command);
450   }
451 
452   return result;
453 }
454