fastboot driver: add fetch command in driver
The `fastboot fetch` command is a wrapper around the fetch protocol. It: - getvar max-fetch-size - get the size of the partition - read the data by chunks, chunk size = max-fetch-size. The name of the partition is passed directly to the device (with the usual has-slot magic for flashing etc.) If we support fetching partitions other than vendor_boot in the future, no change in the driver is needed. Bug: 173654501 Test: manual Change-Id: Ie576eea668234df236b096a372e65c5e91c1e48c
This commit is contained in:
parent
17d469bd57
commit
bcd2770207
4 changed files with 111 additions and 24 deletions
|
|
@ -712,13 +712,20 @@ class PartitionFetcher {
|
|||
// If successfully opened, ret_ is left untouched. Otherwise, ret_ is set to the value
|
||||
// that FetchHandler should return.
|
||||
bool Open() {
|
||||
partition_name_ = args_->at(1);
|
||||
if (partition_name_ != "vendor_boot") {
|
||||
ret_ = device_->WriteFail("Fetch is only allowed on vendor_boot");
|
||||
if (args_->size() < 2) {
|
||||
ret_ = device_->WriteFail("Missing partition arg");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!OpenPartition(device_, partition_name_, &handle_)) {
|
||||
partition_name_ = args_->at(1);
|
||||
if (std::find(kAllowedPartitions.begin(), kAllowedPartitions.end(), partition_name_) ==
|
||||
kAllowedPartitions.end()) {
|
||||
ret_ = device_->WriteFail("Fetch is only allowed on [" +
|
||||
android::base::Join(kAllowedPartitions, ", ") + "]");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!OpenPartition(device_, partition_name_, &handle_, true /* read */)) {
|
||||
ret_ = device_->WriteFail(
|
||||
android::base::StringPrintf("Cannot open %s", partition_name_.c_str()));
|
||||
return false;
|
||||
|
|
@ -826,6 +833,12 @@ class PartitionFetcher {
|
|||
start_offset_, total_size_to_read_));
|
||||
}
|
||||
|
||||
static constexpr std::array<const char*, 3> kAllowedPartitions{
|
||||
"vendor_boot",
|
||||
"vendor_boot_a",
|
||||
"vendor_boot_b",
|
||||
};
|
||||
|
||||
FastbootDevice* device_;
|
||||
const std::vector<std::string>* args_ = nullptr;
|
||||
std::string partition_name_;
|
||||
|
|
|
|||
|
|
@ -77,11 +77,13 @@
|
|||
#include "usb.h"
|
||||
#include "util.h"
|
||||
|
||||
using android::base::borrowed_fd;
|
||||
using android::base::ReadFully;
|
||||
using android::base::Split;
|
||||
using android::base::Trim;
|
||||
using android::base::unique_fd;
|
||||
using namespace std::string_literals;
|
||||
using namespace std::placeholders;
|
||||
|
||||
static const char* serial = nullptr;
|
||||
|
||||
|
|
@ -414,6 +416,7 @@ static int show_help() {
|
|||
" snapshot-update merge On devices that support snapshot-based updates, finish\n"
|
||||
" an in-progress update if it is in the \"merging\"\n"
|
||||
" phase.\n"
|
||||
" fetch PARTITION Fetch a partition image from the device."
|
||||
"\n"
|
||||
"boot image:\n"
|
||||
" boot KERNEL [RAMDISK [SECOND]]\n"
|
||||
|
|
@ -466,7 +469,7 @@ static int show_help() {
|
|||
" --version Display version.\n"
|
||||
" --help, -h Show this message.\n"
|
||||
);
|
||||
// clang-format off
|
||||
// clang-format on
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -851,24 +854,23 @@ static struct sparse_file** load_sparse_files(int fd, int64_t max_size) {
|
|||
return out_s;
|
||||
}
|
||||
|
||||
static int64_t get_target_sparse_limit() {
|
||||
std::string max_download_size;
|
||||
if (fb->GetVar("max-download-size", &max_download_size) != fastboot::SUCCESS ||
|
||||
max_download_size.empty()) {
|
||||
verbose("target didn't report max-download-size");
|
||||
static uint64_t get_uint_var(const char* var_name) {
|
||||
std::string value_str;
|
||||
if (fb->GetVar(var_name, &value_str) != fastboot::SUCCESS || value_str.empty()) {
|
||||
verbose("target didn't report %s", var_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Some bootloaders (angler, for example) send spurious whitespace too.
|
||||
max_download_size = android::base::Trim(max_download_size);
|
||||
value_str = android::base::Trim(value_str);
|
||||
|
||||
uint64_t limit;
|
||||
if (!android::base::ParseUint(max_download_size, &limit)) {
|
||||
fprintf(stderr, "couldn't parse max-download-size '%s'\n", max_download_size.c_str());
|
||||
uint64_t value;
|
||||
if (!android::base::ParseUint(value_str, &value)) {
|
||||
fprintf(stderr, "couldn't parse %s '%s'\n", var_name, value_str.c_str());
|
||||
return 0;
|
||||
}
|
||||
if (limit > 0) verbose("target reported max download size of %" PRId64 " bytes", limit);
|
||||
return limit;
|
||||
if (value > 0) verbose("target reported %s of %" PRId64 " bytes", var_name, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
static int64_t get_sparse_limit(int64_t size) {
|
||||
|
|
@ -877,7 +879,7 @@ static int64_t get_sparse_limit(int64_t size) {
|
|||
// Unlimited, so see what the target device's limit is.
|
||||
// TODO: shouldn't we apply this limit even if you've used -S?
|
||||
if (target_sparse_limit == -1) {
|
||||
target_sparse_limit = get_target_sparse_limit();
|
||||
target_sparse_limit = static_cast<int64_t>(get_uint_var("max-download-size"));
|
||||
}
|
||||
if (target_sparse_limit > 0) {
|
||||
limit = target_sparse_limit;
|
||||
|
|
@ -1011,21 +1013,28 @@ static std::string fb_fix_numeric_var(std::string var) {
|
|||
return var;
|
||||
}
|
||||
|
||||
static void copy_boot_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
|
||||
if (buf->sz < AVB_FOOTER_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
static uint64_t get_partition_size(const std::string& partition) {
|
||||
std::string partition_size_str;
|
||||
if (fb->GetVar("partition-size:" + partition, &partition_size_str) != fastboot::SUCCESS) {
|
||||
die("cannot get partition size for %s", partition.c_str());
|
||||
}
|
||||
|
||||
partition_size_str = fb_fix_numeric_var(partition_size_str);
|
||||
int64_t partition_size;
|
||||
if (!android::base::ParseInt(partition_size_str, &partition_size)) {
|
||||
uint64_t partition_size;
|
||||
if (!android::base::ParseUint(partition_size_str, &partition_size)) {
|
||||
die("Couldn't parse partition size '%s'.", partition_size_str.c_str());
|
||||
}
|
||||
return partition_size;
|
||||
}
|
||||
|
||||
static void copy_boot_avb_footer(const std::string& partition, struct fastboot_buffer* buf) {
|
||||
if (buf->sz < AVB_FOOTER_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If overflows and negative, it should be < buf->sz.
|
||||
int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));
|
||||
|
||||
if (partition_size == buf->sz) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1245,6 +1254,38 @@ static bool is_retrofit_device() {
|
|||
return android::base::StartsWith(value, "system_");
|
||||
}
|
||||
|
||||
// Fetch a partition from the device to a given fd. This is a wrapper over FetchToFd to fetch
|
||||
// the full image.
|
||||
static uint64_t fetch_partition(const std::string& partition, borrowed_fd fd) {
|
||||
uint64_t fetch_size = get_uint_var(FB_VAR_MAX_FETCH_SIZE);
|
||||
if (fetch_size == 0) {
|
||||
die("Unable to get %s. Device does not support fetch command.", FB_VAR_MAX_FETCH_SIZE);
|
||||
}
|
||||
uint64_t partition_size = get_partition_size(partition);
|
||||
if (partition_size <= 0) {
|
||||
die("Invalid partition size for partition %s: %" PRId64, partition.c_str(), partition_size);
|
||||
}
|
||||
|
||||
uint64_t offset = 0;
|
||||
while (offset < partition_size) {
|
||||
uint64_t chunk_size = std::min(fetch_size, partition_size - offset);
|
||||
if (fb->FetchToFd(partition, fd, offset, chunk_size) != fastboot::RetCode::SUCCESS) {
|
||||
die("Unable to fetch %s (offset=%" PRIx64 ", size=%" PRIx64 ")", partition.c_str(),
|
||||
offset, chunk_size);
|
||||
}
|
||||
offset += chunk_size;
|
||||
}
|
||||
return partition_size;
|
||||
}
|
||||
|
||||
static void do_fetch(const std::string& partition, const std::string& slot_override,
|
||||
const std::string& outfile) {
|
||||
unique_fd fd(TEMP_FAILURE_RETRY(
|
||||
open(outfile.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY, 0644)));
|
||||
auto fetch = std::bind(fetch_partition, _1, borrowed_fd(fd));
|
||||
do_for_partitions(partition, slot_override, fetch, false /* force slot */);
|
||||
}
|
||||
|
||||
static void do_flash(const char* pname, const char* fname) {
|
||||
struct fastboot_buffer buf;
|
||||
|
||||
|
|
@ -2167,6 +2208,10 @@ int FastBootTool::Main(int argc, char* argv[]) {
|
|||
syntax_error("expected: snapshot-update [cancel|merge]");
|
||||
}
|
||||
fb->SnapshotUpdateCommand(arg);
|
||||
} else if (command == FB_CMD_FETCH) {
|
||||
std::string partition = next_arg(&args);
|
||||
std::string outfile = next_arg(&args);
|
||||
do_fetch(partition, slot_override, outfile);
|
||||
} else {
|
||||
syntax_error("unknown command %s", command.c_str());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/mapped_file.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
|
@ -357,6 +359,29 @@ RetCode FastBootDriver::UploadInner(const std::string& outfile, std::string* res
|
|||
return ret;
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::FetchToFd(const std::string& partition, android::base::borrowed_fd fd,
|
||||
int64_t offset, int64_t size, std::string* response,
|
||||
std::vector<std::string>* info) {
|
||||
prolog_(android::base::StringPrintf("Fetching %s (offset=%" PRIx64 ", size=%" PRIx64 ")",
|
||||
partition.c_str(), offset, size));
|
||||
std::string cmd = FB_CMD_FETCH ":" + partition;
|
||||
if (offset >= 0) {
|
||||
cmd += android::base::StringPrintf(":0x%08" PRIx64, offset);
|
||||
if (size >= 0) {
|
||||
cmd += android::base::StringPrintf(":0x%08" PRIx64, size);
|
||||
}
|
||||
}
|
||||
RetCode ret = RunAndReadBuffer(cmd, response, info, [&](const char* data, uint64_t size) {
|
||||
if (!android::base::WriteFully(fd, data, size)) {
|
||||
error_ = android::base::StringPrintf("Cannot write: %s", strerror(errno));
|
||||
return IO_ERROR;
|
||||
}
|
||||
return SUCCESS;
|
||||
});
|
||||
epilog_(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
void FastBootDriver::SetInfoCallback(std::function<void(const std::string&)> info) {
|
||||
info_ = info;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <bootimg.h>
|
||||
#include <inttypes.h>
|
||||
#include <sparse/sparse.h>
|
||||
|
|
@ -106,6 +107,9 @@ class FastBootDriver {
|
|||
std::vector<std::string>* info = nullptr);
|
||||
RetCode SnapshotUpdateCommand(const std::string& command, std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr);
|
||||
RetCode FetchToFd(const std::string& partition, android::base::borrowed_fd fd,
|
||||
int64_t offset = -1, int64_t size = -1, std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr);
|
||||
|
||||
/* HIGHER LEVEL COMMANDS -- Composed of the commands above */
|
||||
RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue