Home | History | Annotate | Download | only in updater
      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 
     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 
     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 
     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       // Sanity check the block count.
    132       if (result.blocks() != src_blocks) {
    133         *err =
    134             android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
    135                                         src_ranges.ToString().c_str(), src_blocks);
    136         return false;
    137       }
    138 
    139       *source = result;
    140       return true;
    141     }
    142 
    143     src_ranges_location = RangeSet::Parse(tokens[pos++]);
    144     if (!src_ranges_location) {
    145       *err = "invalid source ranges location";
    146       return false;
    147     }
    148   }
    149 
    150   // <[stash_id:stash_location]>
    151   std::vector<StashInfo> stashes;
    152   while (pos < tokens.size()) {
    153     // Each word is a an index into the stash table, a colon, and then a RangeSet describing where
    154     // in the source block that stashed data should go.
    155     std::vector<std::string> pairs = android::base::Split(tokens[pos++], ":");
    156     if (pairs.size() != 2) {
    157       *err = "invalid stash info";
    158       return false;
    159     }
    160     RangeSet stash_location = RangeSet::Parse(pairs[1]);
    161     if (!stash_location) {
    162       *err = "invalid stash location";
    163       return false;
    164     }
    165     stashes.emplace_back(pairs[0], stash_location);
    166   }
    167 
    168   SourceInfo result(src_hash, src_ranges, src_ranges_location, stashes);
    169   if (src_blocks != result.blocks()) {
    170     *err = android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(),
    171                                        src_ranges.ToString().c_str(), src_blocks);
    172     return false;
    173   }
    174 
    175   *source = result;
    176   return true;
    177 }
    178 
    179 Command Command::Parse(const std::string& line, size_t index, std::string* err) {
    180   std::vector<std::string> tokens = android::base::Split(line, " ");
    181   size_t pos = 0;
    182   // tokens.size() will be 1 at least.
    183   Type op = ParseType(tokens[pos++]);
    184   if (op == Type::LAST) {
    185     *err = "invalid type";
    186     return {};
    187   }
    188 
    189   PatchInfo patch_info;
    190   TargetInfo target_info;
    191   SourceInfo source_info;
    192   StashInfo stash_info;
    193 
    194   if (op == Type::ZERO || op == Type::NEW || op == Type::ERASE) {
    195     // zero/new/erase <rangeset>
    196     if (pos + 1 != tokens.size()) {
    197       *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
    198                                          tokens.size() - pos);
    199       return {};
    200     }
    201     RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]);
    202     if (!tgt_ranges) {
    203       return {};
    204     }
    205     static const std::string kUnknownHash{ "unknown-hash" };
    206     target_info = TargetInfo(kUnknownHash, tgt_ranges);
    207   } else if (op == Type::STASH) {
    208     // stash <stash_id> <src_ranges>
    209     if (pos + 2 != tokens.size()) {
    210       *err = android::base::StringPrintf("invalid number of args: %zu (expected 2)",
    211                                          tokens.size() - pos);
    212       return {};
    213     }
    214     const std::string& id = tokens[pos++];
    215     RangeSet src_ranges = RangeSet::Parse(tokens[pos++]);
    216     if (!src_ranges) {
    217       *err = "invalid token";
    218       return {};
    219     }
    220     stash_info = StashInfo(id, src_ranges);
    221   } else if (op == Type::FREE) {
    222     // free <stash_id>
    223     if (pos + 1 != tokens.size()) {
    224       *err = android::base::StringPrintf("invalid number of args: %zu (expected 1)",
    225                                          tokens.size() - pos);
    226       return {};
    227     }
    228     stash_info = StashInfo(tokens[pos++], {});
    229   } else if (op == Type::MOVE) {
    230     // <hash>
    231     if (pos + 1 > tokens.size()) {
    232       *err = "missing hash";
    233       return {};
    234     }
    235     std::string hash = tokens[pos++];
    236     if (!ParseTargetInfoAndSourceInfo(
    237             std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), hash, &target_info,
    238             hash, &source_info, err)) {
    239       return {};
    240     }
    241   } else if (op == Type::BSDIFF || op == Type::IMGDIFF) {
    242     // <offset> <length> <srchash> <dsthash>
    243     if (pos + 4 > tokens.size()) {
    244       *err = android::base::StringPrintf("invalid number of args: %zu (expected 4+)",
    245                                          tokens.size() - pos);
    246       return {};
    247     }
    248     size_t offset;
    249     size_t length;
    250     if (!android::base::ParseUint(tokens[pos++], &offset) ||
    251         !android::base::ParseUint(tokens[pos++], &length)) {
    252       *err = "invalid patch offset/length";
    253       return {};
    254     }
    255     patch_info = PatchInfo(offset, length);
    256 
    257     std::string src_hash = tokens[pos++];
    258     std::string dst_hash = tokens[pos++];
    259     if (!ParseTargetInfoAndSourceInfo(
    260             std::vector<std::string>(tokens.cbegin() + pos, tokens.cend()), dst_hash, &target_info,
    261             src_hash, &source_info, err)) {
    262       return {};
    263     }
    264   } else if (op == Type::ABORT) {
    265     // No-op, other than sanity checking the input args.
    266     if (pos != tokens.size()) {
    267       *err = android::base::StringPrintf("invalid number of args: %zu (expected 0)",
    268                                          tokens.size() - pos);
    269       return {};
    270     }
    271   } else if (op == Type::COMPUTE_HASH_TREE) {
    272     // <hash_tree_ranges> <source_ranges> <hash_algorithm> <salt_hex> <root_hash>
    273     if (pos + 5 != tokens.size()) {
    274       *err = android::base::StringPrintf("invalid number of args: %zu (expected 5)",
    275                                          tokens.size() - pos);
    276       return {};
    277     }
    278 
    279     // Expects the hash_tree data to be contiguous.
    280     RangeSet hash_tree_ranges = RangeSet::Parse(tokens[pos++]);
    281     if (!hash_tree_ranges || hash_tree_ranges.size() != 1) {
    282       *err = "invalid hash tree ranges in: " + line;
    283       return {};
    284     }
    285 
    286     RangeSet source_ranges = RangeSet::Parse(tokens[pos++]);
    287     if (!source_ranges) {
    288       *err = "invalid source ranges in: " + line;
    289       return {};
    290     }
    291 
    292     std::string hash_algorithm = tokens[pos++];
    293     std::string salt_hex = tokens[pos++];
    294     std::string root_hash = tokens[pos++];
    295     if (hash_algorithm.empty() || salt_hex.empty() || root_hash.empty()) {
    296       *err = "invalid hash tree arguments in " + line;
    297       return {};
    298     }
    299 
    300     HashTreeInfo hash_tree_info(std::move(hash_tree_ranges), std::move(source_ranges),
    301                                 std::move(hash_algorithm), std::move(salt_hex),
    302                                 std::move(root_hash));
    303     return Command(op, index, line, std::move(hash_tree_info));
    304   } else {
    305     *err = "invalid op";
    306     return {};
    307   }
    308 
    309   return Command(op, index, line, patch_info, target_info, source_info, stash_info);
    310 }
    311 
    312 bool SourceInfo::Overlaps(const TargetInfo& target) const {
    313   return ranges_.Overlaps(target.ranges());
    314 }
    315 
    316 // Moves blocks in the 'source' vector to the specified locations (as in 'locs') in the 'dest'
    317 // vector. Note that source and dest may be the same buffer.
    318 static void MoveRange(std::vector<uint8_t>* dest, const RangeSet& locs,
    319                       const std::vector<uint8_t>& source, size_t block_size) {
    320   const uint8_t* from = source.data();
    321   uint8_t* to = dest->data();
    322   size_t start = locs.blocks();
    323   // Must do the movement backward.
    324   for (auto it = locs.crbegin(); it != locs.crend(); it++) {
    325     size_t blocks = it->second - it->first;
    326     start -= blocks;
    327     memmove(to + (it->first * block_size), from + (start * block_size), blocks * block_size);
    328   }
    329 }
    330 
    331 bool SourceInfo::ReadAll(
    332     std::vector<uint8_t>* buffer, size_t block_size,
    333     const std::function<int(const RangeSet&, std::vector<uint8_t>*)>& block_reader,
    334     const std::function<int(const std::string&, std::vector<uint8_t>*)>& stash_reader) const {
    335   if (buffer->size() < blocks() * block_size) {
    336     return false;
    337   }
    338 
    339   // Read in the source ranges.
    340   if (ranges_) {
    341     if (block_reader(ranges_, buffer) != 0) {
    342       return false;
    343     }
    344     if (location_) {
    345       MoveRange(buffer, location_, *buffer, block_size);
    346     }
    347   }
    348 
    349   // Read in the stashes.
    350   for (const StashInfo& stash : stashes_) {
    351     std::vector<uint8_t> stash_buffer(stash.blocks() * block_size);
    352     if (stash_reader(stash.id(), &stash_buffer) != 0) {
    353       return false;
    354     }
    355     MoveRange(buffer, stash.ranges(), stash_buffer, block_size);
    356   }
    357   return true;
    358 }
    359 
    360 void SourceInfo::DumpBuffer(const std::vector<uint8_t>& buffer, size_t block_size) const {
    361   LOG(INFO) << "Dumping hashes in hex for " << ranges_.blocks() << " source blocks";
    362 
    363   const RangeSet& location = location_ ? location_ : RangeSet({ Range{ 0, ranges_.blocks() } });
    364   for (size_t i = 0; i < ranges_.blocks(); i++) {
    365     size_t block_num = ranges_.GetBlockNumber(i);
    366     size_t buffer_index = location.GetBlockNumber(i);
    367     CHECK_LE((buffer_index + 1) * block_size, buffer.size());
    368 
    369     uint8_t digest[SHA_DIGEST_LENGTH];
    370     SHA1(buffer.data() + buffer_index * block_size, block_size, digest);
    371     std::string hexdigest = print_sha1(digest);
    372     LOG(INFO) << "  block number: " << block_num << ", SHA-1: " << hexdigest;
    373   }
    374 }
    375 
    376 std::ostream& operator<<(std::ostream& os, const Command& command) {
    377   os << command.index() << ": " << command.cmdline();
    378   return os;
    379 }
    380 
    381 std::ostream& operator<<(std::ostream& os, const TargetInfo& target) {
    382   os << target.blocks() << " blocks (" << target.hash_ << "): " << target.ranges_.ToString();
    383   return os;
    384 }
    385 
    386 std::ostream& operator<<(std::ostream& os, const StashInfo& stash) {
    387   os << stash.blocks() << " blocks (" << stash.id_ << "): " << stash.ranges_.ToString();
    388   return os;
    389 }
    390 
    391 std::ostream& operator<<(std::ostream& os, const SourceInfo& source) {
    392   os << source.blocks_ << " blocks (" << source.hash_ << "): ";
    393   if (source.ranges_) {
    394     os << source.ranges_.ToString();
    395     if (source.location_) {
    396       os << " (location: " << source.location_.ToString() << ")";
    397     }
    398   }
    399   if (!source.stashes_.empty()) {
    400     os << " " << source.stashes_.size() << " stash(es)";
    401   }
    402   return os;
    403 }
    404 
    405 TransferList TransferList::Parse(const std::string& transfer_list_str, std::string* err) {
    406   TransferList result{};
    407 
    408   std::vector<std::string> lines = android::base::Split(transfer_list_str, "\n");
    409   if (lines.size() < kTransferListHeaderLines) {
    410     *err = android::base::StringPrintf("too few lines in the transfer list [%zu]", lines.size());
    411     return TransferList{};
    412   }
    413 
    414   // First line in transfer list is the version number.
    415   if (!android::base::ParseInt(lines[0], &result.version_, 3, 4)) {
    416     *err = "unexpected transfer list version ["s + lines[0] + "]";
    417     return TransferList{};
    418   }
    419 
    420   // Second line in transfer list is the total number of blocks we expect to write.
    421   if (!android::base::ParseUint(lines[1], &result.total_blocks_)) {
    422     *err = "unexpected block count ["s + lines[1] + "]";
    423     return TransferList{};
    424   }
    425 
    426   // Third line is how many stash entries are needed simultaneously.
    427   if (!android::base::ParseUint(lines[2], &result.stash_max_entries_)) {
    428     return TransferList{};
    429   }
    430 
    431   // Fourth line is the maximum number of blocks that will be stashed simultaneously.
    432   if (!android::base::ParseUint(lines[3], &result.stash_max_blocks_)) {
    433     *err = "unexpected maximum stash blocks ["s + lines[3] + "]";
    434     return TransferList{};
    435   }
    436 
    437   // Subsequent lines are all individual transfer commands.
    438   for (size_t i = kTransferListHeaderLines; i < lines.size(); i++) {
    439     const std::string& line = lines[i];
    440     if (line.empty()) continue;
    441 
    442     size_t cmdindex = i - kTransferListHeaderLines;
    443     std::string parsing_error;
    444     Command command = Command::Parse(line, cmdindex, &parsing_error);
    445     if (!command) {
    446       *err = android::base::StringPrintf("Failed to parse command %zu [%s]: %s", cmdindex,
    447                                          line.c_str(), parsing_error.c_str());
    448       return TransferList{};
    449     }
    450     result.commands_.push_back(command);
    451   }
    452 
    453   return result;
    454 }
    455