| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #pragma once |
| |
| #include <stdint.h> |
| |
| #include <functional> |
| #include <ostream> |
| #include <string> |
| #include <vector> |
| |
| #include <gtest/gtest_prod.h> // FRIEND_TEST |
| |
| #include "otautil/rangeset.h" |
| |
| // Represents the target info used in a Command. TargetInfo contains the ranges of the blocks and |
| // the expected hash. |
| class TargetInfo { |
| public: |
| TargetInfo() = default; |
| |
| TargetInfo(std::string hash, RangeSet ranges) |
| : hash_(std::move(hash)), ranges_(std::move(ranges)) {} |
| |
| const std::string& hash() const { |
| return hash_; |
| } |
| |
| const RangeSet& ranges() const { |
| return ranges_; |
| } |
| |
| size_t blocks() const { |
| return ranges_.blocks(); |
| } |
| |
| bool operator==(const TargetInfo& other) const { |
| return hash_ == other.hash_ && ranges_ == other.ranges_; |
| } |
| |
| private: |
| friend std::ostream& operator<<(std::ostream& os, const TargetInfo& source); |
| |
| // The hash of the data represented by the object. |
| std::string hash_; |
| // The block ranges that the data should be written to. |
| RangeSet ranges_; |
| }; |
| |
| std::ostream& operator<<(std::ostream& os, const TargetInfo& source); |
| |
| // Represents the stash info used in a Command. |
| class StashInfo { |
| public: |
| StashInfo() = default; |
| |
| StashInfo(std::string id, RangeSet ranges) : id_(std::move(id)), ranges_(std::move(ranges)) {} |
| |
| size_t blocks() const { |
| return ranges_.blocks(); |
| } |
| |
| const std::string& id() const { |
| return id_; |
| } |
| |
| const RangeSet& ranges() const { |
| return ranges_; |
| } |
| |
| bool operator==(const StashInfo& other) const { |
| return id_ == other.id_ && ranges_ == other.ranges_; |
| } |
| |
| private: |
| friend std::ostream& operator<<(std::ostream& os, const StashInfo& stash); |
| |
| // The id (i.e. hash) of the stash. |
| std::string id_; |
| // The matching location of the stash. |
| RangeSet ranges_; |
| }; |
| |
| std::ostream& operator<<(std::ostream& os, const StashInfo& stash); |
| |
| // Represents the source info in a Command, whose data could come from source image, stashed blocks, |
| // or both. |
| class SourceInfo { |
| public: |
| SourceInfo() = default; |
| |
| SourceInfo(std::string hash, RangeSet ranges, RangeSet location, std::vector<StashInfo> stashes) |
| : hash_(std::move(hash)), |
| ranges_(std::move(ranges)), |
| location_(std::move(location)), |
| stashes_(std::move(stashes)) { |
| blocks_ = ranges_.blocks(); |
| for (const auto& stash : stashes_) { |
| blocks_ += stash.ranges().blocks(); |
| } |
| } |
| |
| // Reads all the data specified by this SourceInfo object into the given 'buffer', by calling the |
| // given readers. Caller needs to specify the block size for the represented blocks. The given |
| // buffer needs to be sufficiently large. Otherwise it returns false. 'block_reader' and |
| // 'stash_reader' read the specified data into the given buffer (guaranteed to be large enough) |
| // respectively. The readers should return 0 on success, or -1 on error. |
| bool 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) const; |
| |
| // Whether this SourceInfo overlaps with the given TargetInfo object. |
| bool Overlaps(const TargetInfo& target) const; |
| |
| // Dumps the hashes in hex for the given buffer that's loaded from this SourceInfo object |
| // (excluding the stashed blocks which are handled separately). |
| void DumpBuffer(const std::vector<uint8_t>& buffer, size_t block_size) const; |
| |
| const std::string& hash() const { |
| return hash_; |
| } |
| |
| size_t blocks() const { |
| return blocks_; |
| } |
| |
| bool operator==(const SourceInfo& other) const { |
| return hash_ == other.hash_ && ranges_ == other.ranges_ && location_ == other.location_ && |
| stashes_ == other.stashes_; |
| } |
| |
| private: |
| friend std::ostream& operator<<(std::ostream& os, const SourceInfo& source); |
| |
| // The hash of the data represented by the object. |
| std::string hash_; |
| // The block ranges from the source image to read data from. This could be a subset of all the |
| // blocks represented by the object, or empty if all the data should be loaded from stash. |
| RangeSet ranges_; |
| // The location in the buffer to load ranges_ into. Empty if ranges_ alone covers all the blocks |
| // (i.e. nothing needs to be loaded from stash). |
| RangeSet location_; |
| // The info for the stashed blocks that are part of the source. Empty if there's none. |
| std::vector<StashInfo> stashes_; |
| // Total number of blocks represented by the object. |
| size_t blocks_{ 0 }; |
| }; |
| |
| std::ostream& operator<<(std::ostream& os, const SourceInfo& source); |
| |
| class PatchInfo { |
| public: |
| PatchInfo() = default; |
| |
| PatchInfo(size_t offset, size_t length) : offset_(offset), length_(length) {} |
| |
| size_t offset() const { |
| return offset_; |
| } |
| |
| size_t length() const { |
| return length_; |
| } |
| |
| bool operator==(const PatchInfo& other) const { |
| return offset_ == other.offset_ && length_ == other.length_; |
| } |
| |
| private: |
| size_t offset_{ 0 }; |
| size_t length_{ 0 }; |
| }; |
| |
| // The arguments to build a hash tree from blocks on the block device. |
| class HashTreeInfo { |
| public: |
| HashTreeInfo() = default; |
| |
| HashTreeInfo(RangeSet hash_tree_ranges, RangeSet source_ranges, std::string hash_algorithm, |
| std::string salt_hex, std::string root_hash) |
| : hash_tree_ranges_(std::move(hash_tree_ranges)), |
| source_ranges_(std::move(source_ranges)), |
| hash_algorithm_(std::move(hash_algorithm)), |
| salt_hex_(std::move(salt_hex)), |
| root_hash_(std::move(root_hash)) {} |
| |
| const RangeSet& hash_tree_ranges() const { |
| return hash_tree_ranges_; |
| } |
| const RangeSet& source_ranges() const { |
| return source_ranges_; |
| } |
| |
| const std::string& hash_algorithm() const { |
| return hash_algorithm_; |
| } |
| const std::string& salt_hex() const { |
| return salt_hex_; |
| } |
| const std::string& root_hash() const { |
| return root_hash_; |
| } |
| |
| bool operator==(const HashTreeInfo& other) const { |
| return hash_tree_ranges_ == other.hash_tree_ranges_ && source_ranges_ == other.source_ranges_ && |
| hash_algorithm_ == other.hash_algorithm_ && salt_hex_ == other.salt_hex_ && |
| root_hash_ == other.root_hash_; |
| } |
| |
| private: |
| RangeSet hash_tree_ranges_; |
| RangeSet source_ranges_; |
| std::string hash_algorithm_; |
| std::string salt_hex_; |
| std::string root_hash_; |
| }; |
| |
| // Command class holds the info for an update command that performs block-based OTA (BBOTA). Each |
| // command consists of one or several args, namely TargetInfo, SourceInfo, StashInfo and PatchInfo. |
| // The currently used BBOTA version is v4. |
| // |
| // zero <tgt_ranges> |
| // - Fill the indicated blocks with zeros. |
| // - Meaningful args: TargetInfo |
| // |
| // new <tgt_ranges> |
| // - Fill the blocks with data read from the new_data file. |
| // - Meaningful args: TargetInfo |
| // |
| // erase <tgt_ranges> |
| // - Mark the given blocks as empty. |
| // - Meaningful args: TargetInfo |
| // |
| // move <hash> <...> |
| // - Read the source blocks, write result to target blocks. |
| // - Meaningful args: TargetInfo, SourceInfo |
| // |
| // See the note below for <...>. |
| // |
| // bsdiff <patchstart> <patchlen> <srchash> <dsthash> <...> |
| // imgdiff <patchstart> <patchlen> <srchash> <dsthash> <...> |
| // - Read the source blocks, apply a patch, and write result to target blocks. |
| // - Meaningful args: PatchInfo, TargetInfo, SourceInfo |
| // |
| // It expects <...> in one of the following formats: |
| // |
| // <tgt_ranges> <src_block_count> - <[stash_id:stash_location] ...> |
| // (loads data from stashes only) |
| // |
| // <tgt_ranges> <src_block_count> <src_ranges> |
| // (loads data from source image only) |
| // |
| // <tgt_ranges> <src_block_count> <src_ranges> <src_ranges_location> |
| // <[stash_id:stash_location] ...> |
| // (loads data from both of source image and stashes) |
| // |
| // stash <stash_id> <src_ranges> |
| // - Load the given source blocks and stash the data in the given slot of the stash table. |
| // - Meaningful args: StashInfo |
| // |
| // free <stash_id> |
| // - Free the given stash data. |
| // - Meaningful args: StashInfo |
| // |
| // compute_hash_tree <hash_tree_ranges> <source_ranges> <hash_algorithm> <salt_hex> <root_hash> |
| // - Computes the hash_tree bytes and writes the result to the specified range on the |
| // block_device. |
| // |
| // abort |
| // - Abort the current update. Allowed for testing code only. |
| // |
| class Command { |
| public: |
| enum class Type { |
| ABORT, |
| BSDIFF, |
| COMPUTE_HASH_TREE, |
| ERASE, |
| FREE, |
| IMGDIFF, |
| MOVE, |
| NEW, |
| STASH, |
| ZERO, |
| LAST, // Not a valid type. |
| }; |
| |
| Command() = default; |
| |
| Command(Type type, size_t index, std::string cmdline, PatchInfo patch, TargetInfo target, |
| SourceInfo source, StashInfo stash) |
| : type_(type), |
| index_(index), |
| cmdline_(std::move(cmdline)), |
| patch_(patch), |
| target_(std::move(target)), |
| source_(std::move(source)), |
| stash_(std::move(stash)) {} |
| |
| Command(Type type, size_t index, std::string cmdline, HashTreeInfo hash_tree_info); |
| |
| // Parses the given command 'line' into a Command object and returns it. The 'index' is specified |
| // by the caller to index the object. On parsing error, it returns an empty Command object that |
| // evaluates to false, and the specific error message will be set in 'err'. |
| static Command Parse(const std::string& line, size_t index, std::string* err); |
| |
| // Parses the command type from the given string. |
| static Type ParseType(const std::string& type_str); |
| |
| Type type() const { |
| return type_; |
| } |
| |
| size_t index() const { |
| return index_; |
| } |
| |
| const std::string& cmdline() const { |
| return cmdline_; |
| } |
| |
| const PatchInfo& patch() const { |
| return patch_; |
| } |
| |
| const TargetInfo& target() const { |
| return target_; |
| } |
| |
| const SourceInfo& source() const { |
| return source_; |
| } |
| |
| const StashInfo& stash() const { |
| return stash_; |
| } |
| |
| const HashTreeInfo& hash_tree_info() const { |
| return hash_tree_info_; |
| } |
| |
| size_t block_size() const { |
| return block_size_; |
| } |
| |
| constexpr explicit operator bool() const { |
| return type_ != Type::LAST; |
| } |
| |
| private: |
| friend class ResumableUpdaterTest; |
| friend class UpdaterTest; |
| |
| FRIEND_TEST(CommandsTest, Parse_ABORT_Allowed); |
| FRIEND_TEST(CommandsTest, Parse_InvalidNumberOfArgs); |
| FRIEND_TEST(CommandsTest, ParseTargetInfoAndSourceInfo_InvalidInput); |
| FRIEND_TEST(CommandsTest, ParseTargetInfoAndSourceInfo_StashesOnly); |
| FRIEND_TEST(CommandsTest, ParseTargetInfoAndSourceInfo_SourceBlocksAndStashes); |
| FRIEND_TEST(CommandsTest, ParseTargetInfoAndSourceInfo_SourceBlocksOnly); |
| |
| // Parses the target and source info from the given 'tokens' vector. Saves the parsed info into |
| // 'target' and 'source' objects. Returns the parsing result. Error message will be set in 'err' |
| // on parsing error, and the contents in 'target' and 'source' will be undefined. |
| static bool ParseTargetInfoAndSourceInfo(const std::vector<std::string>& tokens, |
| const std::string& tgt_hash, TargetInfo* target, |
| const std::string& src_hash, SourceInfo* source, |
| std::string* err); |
| |
| // Allows parsing ABORT command, which should be used for testing purpose only. |
| static bool abort_allowed_; |
| |
| // The type of the command. |
| Type type_{ Type::LAST }; |
| // The index of the Command object, which is specified by the caller. |
| size_t index_{ 0 }; |
| // The input string that the Command object is parsed from. |
| std::string cmdline_; |
| // The patch info. Only meaningful for BSDIFF and IMGDIFF commands. |
| PatchInfo patch_; |
| // The target info, where the command should be written to. |
| TargetInfo target_; |
| // The source info to load the source blocks for the command. |
| SourceInfo source_; |
| // The stash info. Only meaningful for STASH and FREE commands. Note that although SourceInfo may |
| // also load data from stash, such info will be owned and managed by SourceInfo (i.e. in source_). |
| StashInfo stash_; |
| // The hash_tree info. Only meaningful for COMPUTE_HASH_TREE. |
| HashTreeInfo hash_tree_info_; |
| // The unit size of each block to be used in this command. |
| size_t block_size_{ 4096 }; |
| }; |
| |
| std::ostream& operator<<(std::ostream& os, const Command& command); |
| |
| // TransferList represents the info for a transfer list, which is parsed from input text lines |
| // containing commands to transfer data from one place to another on the target partition. |
| // |
| // The creator of the transfer list will guarantee that no block is read (i.e., used as the source |
| // for a patch or move) after it has been written. |
| // |
| // The creator will guarantee that a given stash is loaded (with a stash command) before it's used |
| // in a move/bsdiff/imgdiff command. |
| // |
| // Within one command the source and target ranges may overlap so in general we need to read the |
| // entire source into memory before writing anything to the target blocks. |
| // |
| // All the patch data is concatenated into one patch_data file in the update package. It must be |
| // stored uncompressed because we memory-map it in directly from the archive. (Since patches are |
| // already compressed, we lose very little by not compressing their concatenation.) |
| // |
| // Commands that read data from the partition (i.e. move/bsdiff/imgdiff/stash) have one or more |
| // additional hashes before the range parameters, which are used to check if the command has |
| // already been completed and verify the integrity of the source data. |
| class TransferList { |
| public: |
| // Number of header lines. |
| static constexpr size_t kTransferListHeaderLines = 4; |
| |
| TransferList() = default; |
| |
| // Parses the given input string and returns a TransferList object. Sets error message if any. |
| static TransferList Parse(const std::string& transfer_list_str, std::string* err); |
| |
| int version() const { |
| return version_; |
| } |
| |
| size_t total_blocks() const { |
| return total_blocks_; |
| } |
| |
| size_t stash_max_entries() const { |
| return stash_max_entries_; |
| } |
| |
| size_t stash_max_blocks() const { |
| return stash_max_blocks_; |
| } |
| |
| const std::vector<Command>& commands() const { |
| return commands_; |
| } |
| |
| // Returns whether the TransferList is valid. |
| constexpr explicit operator bool() const { |
| return version_ != 0; |
| } |
| |
| private: |
| // BBOTA version. |
| int version_{ 0 }; |
| // Total number of blocks to be written in this transfer. |
| size_t total_blocks_; |
| // Maximum number of stashes that exist at the same time. |
| size_t stash_max_entries_; |
| // Maximum number of blocks to be stashed. |
| size_t stash_max_blocks_; |
| // Commands in this transfer. |
| std::vector<Command> commands_; |
| }; |