From 6a7e4af7c640cd8b056cdc703e84b9d122ce3703 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 14 Jun 2018 21:57:43 -0700 Subject: updater: Add Command parsing codes. The added codes are not used in the updater yet. The switch will happen in subsequent CLs. Test: Run recovery_unit_test and recovery_component_test on marlin. Change-Id: I1ae8a233280f02c2171b43ef028bdccdacb39c59 --- updater/commands.cpp | 247 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 236 insertions(+), 11 deletions(-) (limited to 'updater/commands.cpp') diff --git a/updater/commands.cpp b/updater/commands.cpp index f798c6a73..6d4b5310b 100644 --- a/updater/commands.cpp +++ b/updater/commands.cpp @@ -16,28 +16,253 @@ #include "private/commands.h" +#include #include +#include #include +#include +#include +#include + +#include "otautil/rangeset.h" + +using namespace std::string_literals; Command::Type Command::ParseType(const std::string& type_str) { - if (type_str == "zero") { - return Type::ZERO; - } else if (type_str == "new") { - return Type::NEW; + if (type_str == "bsdiff") { + return Type::BSDIFF; } else if (type_str == "erase") { return Type::ERASE; - } else if (type_str == "move") { - return Type::MOVE; - } else if (type_str == "bsdiff") { - return Type::BSDIFF; + } else if (type_str == "free") { + return Type::FREE; } else if (type_str == "imgdiff") { return Type::IMGDIFF; + } else if (type_str == "move") { + return Type::MOVE; + } else if (type_str == "new") { + return Type::NEW; } else if (type_str == "stash") { return Type::STASH; - } else if (type_str == "free") { - return Type::FREE; + } else if (type_str == "zero") { + return Type::ZERO; } - LOG(ERROR) << "Invalid type: " << type_str; return Type::LAST; }; + +bool Command::ParseTargetInfoAndSourceInfo(const std::vector& tokens, + const std::string& tgt_hash, TargetInfo* target, + const std::string& src_hash, SourceInfo* source, + std::string* err) { + // We expect the given tokens parameter in one of the following formats. + // + // - <[stash_id:location] ...> + // (loads data from stashes only) + // + // + // (loads data from source image only) + // + // <[stash_id:location] ...> + // (loads data from both of source image and stashes) + + // At least it needs to provide three parameters: , and + // "-"/. + if (tokens.size() < 3) { + *err = "invalid number of parameters"; + return false; + } + + size_t pos = 0; + RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]); + if (!tgt_ranges) { + *err = "invalid target ranges"; + return false; + } + *target = TargetInfo(tgt_hash, tgt_ranges); + + // + const std::string& token = tokens[pos++]; + size_t src_blocks; + if (!android::base::ParseUint(token, &src_blocks)) { + *err = "invalid src_block_count \""s + token + "\""; + return false; + } + + RangeSet src_ranges; + RangeSet src_ranges_location; + // "-" or [] + if (tokens[pos] == "-") { + // no source ranges, only stashes + pos++; + } else { + src_ranges = RangeSet::Parse(tokens[pos++]); + if (!src_ranges) { + *err = "invalid source ranges"; + return false; + } + + if (pos >= tokens.size()) { + // No stashes, only source ranges. + SourceInfo result(src_hash, src_ranges, {}, {}); + + // Sanity check the block count. + if (result.blocks() != src_blocks) { + *err = + android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(), + src_ranges.ToString().c_str(), src_blocks); + return false; + } + + *source = result; + return true; + } + + src_ranges_location = RangeSet::Parse(tokens[pos++]); + if (!src_ranges_location) { + *err = "invalid source ranges location"; + return false; + } + } + + // <[stash_id:stash_location]> + std::vector stashes; + while (pos < tokens.size()) { + // Each word is a an index into the stash table, a colon, and then a RangeSet describing where + // in the source block that stashed data should go. + std::vector pairs = android::base::Split(tokens[pos++], ":"); + if (pairs.size() != 2) { + *err = "invalid stash info"; + return false; + } + RangeSet stash_location = RangeSet::Parse(pairs[1]); + if (!stash_location) { + *err = "invalid stash location"; + return false; + } + stashes.emplace_back(pairs[0], stash_location); + } + + SourceInfo result(src_hash, src_ranges, src_ranges_location, stashes); + if (src_blocks != result.blocks()) { + *err = android::base::StringPrintf("mismatching block count: %zu (%s) vs %zu", result.blocks(), + src_ranges.ToString().c_str(), src_blocks); + return false; + } + + *source = result; + return true; +} + +Command Command::Parse(const std::string& line, size_t index, std::string* err) { + std::vector tokens = android::base::Split(line, " "); + size_t pos = 0; + // tokens.size() will be 1 at least. + Type op = ParseType(tokens[pos++]); + if (op == Type::LAST) { + *err = "invalid type"; + return {}; + } + + PatchInfo patch_info; + TargetInfo target_info; + SourceInfo source_info; + StashInfo stash_info; + + if (op == Type::ZERO || op == Type::NEW || op == Type::ERASE) { + // zero/new/erase + RangeSet tgt_ranges = RangeSet::Parse(tokens[pos++]); + if (!tgt_ranges) { + return {}; + } + static const std::string kUnknownHash{ "unknown-hash" }; + target_info = TargetInfo(kUnknownHash, tgt_ranges); + } else if (op == Type::STASH) { + // stash + if (pos + 2 > tokens.size()) { + *err = "missing stash id and/or source ranges"; + return {}; + } + const std::string& id = tokens[pos++]; + RangeSet src_ranges = RangeSet::Parse(tokens[pos++]); + if (!src_ranges) { + *err = "invalid token"; + return {}; + } + stash_info = StashInfo(id, src_ranges); + } else if (op == Type::FREE) { + // free + if (pos + 1 > tokens.size()) { + *err = "missing stash id in free command"; + return {}; + } + stash_info = StashInfo(tokens[pos++], {}); + } else if (op == Type::MOVE) { + // + if (pos + 1 > tokens.size()) { + *err = "missing hash"; + return {}; + } + std::string hash = tokens[pos++]; + if (!ParseTargetInfoAndSourceInfo( + std::vector(tokens.cbegin() + pos, tokens.cend()), hash, &target_info, + hash, &source_info, err)) { + return {}; + } + } else if (op == Type::BSDIFF || op == Type::IMGDIFF) { + // + if (pos + 4 > tokens.size()) { + *err = "invalid number of tokens"; + return {}; + } + size_t offset; + size_t length; + if (!android::base::ParseUint(tokens[pos++], &offset) || + !android::base::ParseUint(tokens[pos++], &length)) { + *err = "invalid patch offset/length"; + return {}; + } + patch_info = PatchInfo(offset, length); + + std::string src_hash = tokens[pos++]; + std::string dst_hash = tokens[pos++]; + if (!ParseTargetInfoAndSourceInfo( + std::vector(tokens.cbegin() + pos, tokens.cend()), dst_hash, &target_info, + src_hash, &source_info, err)) { + return {}; + } + } else { + *err = "invalid op"; + return {}; + } + + return Command(op, index, line, patch_info, target_info, source_info, stash_info); +} + +std::ostream& operator<<(std::ostream& os, const Command& command) { + os << command.index() << ": " << command.cmdline(); + return os; +} + +std::ostream& operator<<(std::ostream& os, const TargetInfo& target) { + os << target.blocks() << " blocks (" << target.hash_ << "): " << target.ranges_.ToString(); + return os; +} + +std::ostream& operator<<(std::ostream& os, const StashInfo& stash) { + os << stash.blocks() << " blocks (" << stash.id_ << "): " << stash.ranges_.ToString(); + return os; +} + +std::ostream& operator<<(std::ostream& os, const SourceInfo& source) { + os << source.blocks_ << " blocks (" << source.hash_ << "): "; + if (source.ranges_) { + os << source.ranges_.ToString(); + if (source.location_) { + os << " (location: " << source.location_.ToString() << ")"; + } + } + if (!source.stashes_.empty()) { + os << " " << source.stashes_.size() << " stash(es)"; + } + return os; +} -- cgit v1.2.3