You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
476 lines
15 KiB
476 lines
15 KiB
/*
|
|
* 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_;
|
|
};
|