Merge "Refactor libfastboot"
am: 767506fc5a
Change-Id: I1e20f30872969dd847fe67a23ebc5975dcb743e7
This commit is contained in:
commit
40cd9e03c9
15 changed files with 905 additions and 519 deletions
56
fastboot/Android.bp
Normal file
56
fastboot/Android.bp
Normal file
|
@ -0,0 +1,56 @@
|
|||
cc_library_host_static {
|
||||
name: "libfastboot2",
|
||||
|
||||
//host_supported: true,
|
||||
|
||||
compile_multilib: "first",
|
||||
srcs: [
|
||||
"bootimg_utils.cpp",
|
||||
"fs.cpp",
|
||||
"socket.cpp",
|
||||
"tcp.cpp",
|
||||
"udp.cpp",
|
||||
"util.cpp",
|
||||
"fastboot_driver.cpp",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libziparchive",
|
||||
"libsparse",
|
||||
"libutils",
|
||||
"liblog",
|
||||
"libz",
|
||||
"libdiagnose_usb",
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libgtest",
|
||||
"libgtest_main",
|
||||
"libbase",
|
||||
"libadb_host"
|
||||
],
|
||||
|
||||
header_libs: [
|
||||
"bootimg_headers"
|
||||
],
|
||||
|
||||
export_header_lib_headers: [
|
||||
"bootimg_headers"
|
||||
],
|
||||
|
||||
|
||||
target: {
|
||||
linux: {
|
||||
srcs: ["usb_linux.cpp"],
|
||||
},
|
||||
},
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-Wunreachable-code",
|
||||
],
|
||||
|
||||
export_include_dirs: ["."],
|
||||
|
||||
}
|
|
@ -50,12 +50,12 @@ LOCAL_SRC_FILES := \
|
|||
bootimg_utils.cpp \
|
||||
engine.cpp \
|
||||
fastboot.cpp \
|
||||
fs.cpp\
|
||||
protocol.cpp \
|
||||
fs.cpp \
|
||||
socket.cpp \
|
||||
tcp.cpp \
|
||||
udp.cpp \
|
||||
util.cpp \
|
||||
fastboot_driver.cpp \
|
||||
|
||||
LOCAL_SRC_FILES_darwin := usb_osx.cpp
|
||||
LOCAL_SRC_FILES_linux := usb_linux.cpp
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
#include "bootimg_utils.h"
|
||||
|
||||
#include "fastboot.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fastboot.h"
|
||||
#include "engine.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
|
@ -78,17 +77,20 @@ struct Action {
|
|||
};
|
||||
|
||||
static std::vector<std::unique_ptr<Action>> action_list;
|
||||
static fastboot::FastBootDriver* fb = nullptr;
|
||||
|
||||
bool fb_getvar(Transport* transport, const std::string& key, std::string* value) {
|
||||
std::string cmd = FB_CMD_GETVAR ":" + key;
|
||||
void fb_init(fastboot::FastBootDriver& fbi) {
|
||||
fb = &fbi;
|
||||
auto cb = [](std::string& info) { fprintf(stderr, "(bootloader) %s\n", info.c_str()); };
|
||||
fb->SetInfoCallback(cb);
|
||||
}
|
||||
|
||||
char buf[FB_RESPONSE_SZ + 1];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (fb_command_response(transport, cmd, buf)) {
|
||||
return false;
|
||||
}
|
||||
*value = buf;
|
||||
return true;
|
||||
const std::string fb_get_error() {
|
||||
return fb->Error();
|
||||
}
|
||||
|
||||
bool fb_getvar(const std::string& key, std::string* value) {
|
||||
return !fb->GetVar(key, value);
|
||||
}
|
||||
|
||||
static int cb_default(Action& a, int status, const char* resp) {
|
||||
|
@ -310,7 +312,7 @@ void fb_queue_wait_for_disconnect() {
|
|||
queue_action(OP_WAIT_FOR_DISCONNECT, "");
|
||||
}
|
||||
|
||||
int64_t fb_execute_queue(Transport* transport) {
|
||||
int64_t fb_execute_queue() {
|
||||
int64_t status = 0;
|
||||
for (auto& a : action_list) {
|
||||
a->start = now();
|
||||
|
@ -319,33 +321,34 @@ int64_t fb_execute_queue(Transport* transport) {
|
|||
verbose("\n");
|
||||
}
|
||||
if (a->op == OP_DOWNLOAD) {
|
||||
status = fb_download_data(transport, a->data, a->size);
|
||||
char* cbuf = static_cast<char*>(a->data);
|
||||
status = fb->Download(cbuf, a->size);
|
||||
status = a->func(*a, status, status ? fb_get_error().c_str() : "");
|
||||
if (status) break;
|
||||
} else if (a->op == OP_DOWNLOAD_FD) {
|
||||
status = fb_download_data_fd(transport, a->fd, a->size);
|
||||
status = fb->Download(a->fd, a->size);
|
||||
status = a->func(*a, status, status ? fb_get_error().c_str() : "");
|
||||
if (status) break;
|
||||
} else if (a->op == OP_COMMAND) {
|
||||
status = fb_command(transport, a->cmd);
|
||||
status = fb->RawCommand(a->cmd);
|
||||
status = a->func(*a, status, status ? fb_get_error().c_str() : "");
|
||||
if (status) break;
|
||||
} else if (a->op == OP_QUERY) {
|
||||
char resp[FB_RESPONSE_SZ + 1] = {};
|
||||
status = fb_command_response(transport, a->cmd, resp);
|
||||
status = a->func(*a, status, status ? fb_get_error().c_str() : resp);
|
||||
std::string resp;
|
||||
status = fb->RawCommand(a->cmd, &resp);
|
||||
status = a->func(*a, status, status ? fb_get_error().c_str() : resp.c_str());
|
||||
if (status) break;
|
||||
} else if (a->op == OP_NOTICE) {
|
||||
// We already showed the notice because it's in `Action::msg`.
|
||||
fprintf(stderr, "\n");
|
||||
} else if (a->op == OP_DOWNLOAD_SPARSE) {
|
||||
status = fb_download_data_sparse(transport, reinterpret_cast<sparse_file*>(a->data));
|
||||
status = fb->Download(reinterpret_cast<sparse_file*>(a->data));
|
||||
status = a->func(*a, status, status ? fb_get_error().c_str() : "");
|
||||
if (status) break;
|
||||
} else if (a->op == OP_WAIT_FOR_DISCONNECT) {
|
||||
transport->WaitForDisconnect();
|
||||
fb->WaitForDisconnect();
|
||||
} else if (a->op == OP_UPLOAD) {
|
||||
status = fb_upload_data(transport, reinterpret_cast<char*>(a->data));
|
||||
status = fb->Upload(reinterpret_cast<const char*>(a->data));
|
||||
status = a->func(*a, status, status ? fb_get_error().c_str() : "");
|
||||
} else {
|
||||
die("unknown action: %d", a->op);
|
||||
|
|
|
@ -34,23 +34,24 @@
|
|||
#include <string>
|
||||
|
||||
#include <bootimg.h>
|
||||
#include "fastboot_driver.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "constants.h"
|
||||
|
||||
class Transport;
|
||||
struct sparse_file;
|
||||
|
||||
/* protocol.c - fastboot protocol */
|
||||
int fb_command(Transport* transport, const std::string& cmd);
|
||||
int fb_command_response(Transport* transport, const std::string& cmd, char* response);
|
||||
int64_t fb_download_data(Transport* transport, const void* data, uint32_t size);
|
||||
int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size);
|
||||
int fb_download_data_sparse(Transport* transport, struct sparse_file* s);
|
||||
int64_t fb_upload_data(Transport* transport, const char* outfile);
|
||||
const std::string fb_get_error();
|
||||
|
||||
//#define FB_COMMAND_SZ (fastboot::FB_COMMAND_SZ)
|
||||
//#define FB_RESPONSE_SZ (fastboot::FB_RESPONSE_SZ)
|
||||
|
||||
/* engine.c - high level command queue engine */
|
||||
bool fb_getvar(Transport* transport, const std::string& key, std::string* value);
|
||||
|
||||
void fb_init(fastboot::FastBootDriver& fbi);
|
||||
|
||||
bool fb_getvar(const std::string& key, std::string* value);
|
||||
void fb_queue_flash(const std::string& partition, void* data, uint32_t sz);
|
||||
void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz);
|
||||
void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz,
|
||||
|
@ -68,24 +69,13 @@ void fb_queue_download_fd(const std::string& name, int fd, uint32_t sz);
|
|||
void fb_queue_upload(const std::string& outfile);
|
||||
void fb_queue_notice(const std::string& notice);
|
||||
void fb_queue_wait_for_disconnect(void);
|
||||
int64_t fb_execute_queue(Transport* transport);
|
||||
int64_t fb_execute_queue();
|
||||
void fb_set_active(const std::string& slot);
|
||||
|
||||
/* util stuff */
|
||||
double now();
|
||||
char* xstrdup(const char*);
|
||||
void set_verbose();
|
||||
|
||||
// These printf-like functions are implemented in terms of vsnprintf, so they
|
||||
// use the same attribute for compile-time format string checking.
|
||||
void die(const char* fmt, ...) __attribute__((__noreturn__))
|
||||
__attribute__((__format__(__printf__, 1, 2)));
|
||||
void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
|
||||
|
||||
/* Current product */
|
||||
extern char cur_product[FB_RESPONSE_SZ + 1];
|
||||
|
||||
class FastBoot {
|
||||
class FastBootTool {
|
||||
public:
|
||||
int Main(int argc, char* argv[]);
|
||||
|
|
@ -60,7 +60,7 @@
|
|||
|
||||
#include "bootimg_utils.h"
|
||||
#include "diagnose_usb.h"
|
||||
#include "fastboot.h"
|
||||
#include "engine.h"
|
||||
#include "fs.h"
|
||||
#include "tcp.h"
|
||||
#include "transport.h"
|
||||
|
@ -592,7 +592,7 @@ static char* strip(char* s) {
|
|||
}
|
||||
|
||||
#define MAX_OPTIONS 32
|
||||
static void check_requirement(Transport* transport, char* line) {
|
||||
static void check_requirement(char* line) {
|
||||
char *val[MAX_OPTIONS];
|
||||
unsigned count;
|
||||
char *x;
|
||||
|
@ -635,7 +635,7 @@ static void check_requirement(Transport* transport, char* line) {
|
|||
if (!strcmp(name, "partition-exists")) {
|
||||
const char* partition_name = val[0];
|
||||
std::string has_slot;
|
||||
if (!fb_getvar(transport, std::string("has-slot:") + partition_name, &has_slot) ||
|
||||
if (!fb_getvar(std::string("has-slot:") + partition_name, &has_slot) ||
|
||||
(has_slot != "yes" && has_slot != "no")) {
|
||||
die("device doesn't have required partition %s!", partition_name);
|
||||
}
|
||||
|
@ -674,18 +674,18 @@ static void check_requirement(Transport* transport, char* line) {
|
|||
fb_queue_require(product, var, invert, count, out);
|
||||
}
|
||||
|
||||
static void check_requirements(Transport* transport, char* data, int64_t sz) {
|
||||
static void check_requirements(char* data, int64_t sz) {
|
||||
char* s = data;
|
||||
while (sz-- > 0) {
|
||||
if (*s == '\n') {
|
||||
*s++ = 0;
|
||||
check_requirement(transport, data);
|
||||
check_requirement(data);
|
||||
data = s;
|
||||
} else {
|
||||
s++;
|
||||
}
|
||||
}
|
||||
if (fb_execute_queue(transport)) die("requirements not met!");
|
||||
if (fb_execute_queue()) die("requirements not met!");
|
||||
}
|
||||
|
||||
static void queue_info_dump() {
|
||||
|
@ -716,9 +716,9 @@ static struct sparse_file** load_sparse_files(int fd, int64_t max_size) {
|
|||
return out_s;
|
||||
}
|
||||
|
||||
static int64_t get_target_sparse_limit(Transport* transport) {
|
||||
static int64_t get_target_sparse_limit() {
|
||||
std::string max_download_size;
|
||||
if (!fb_getvar(transport, "max-download-size", &max_download_size) ||
|
||||
if (!fb_getvar("max-download-size", &max_download_size) ||
|
||||
max_download_size.empty()) {
|
||||
verbose("target didn't report max-download-size");
|
||||
return 0;
|
||||
|
@ -736,13 +736,13 @@ static int64_t get_target_sparse_limit(Transport* transport) {
|
|||
return limit;
|
||||
}
|
||||
|
||||
static int64_t get_sparse_limit(Transport* transport, int64_t size) {
|
||||
static int64_t get_sparse_limit(int64_t size) {
|
||||
int64_t limit = sparse_limit;
|
||||
if (limit == 0) {
|
||||
// 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(transport);
|
||||
target_sparse_limit = get_target_sparse_limit();
|
||||
}
|
||||
if (target_sparse_limit > 0) {
|
||||
limit = target_sparse_limit;
|
||||
|
@ -758,14 +758,14 @@ static int64_t get_sparse_limit(Transport* transport, int64_t size) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool load_buf_fd(Transport* transport, int fd, struct fastboot_buffer* buf) {
|
||||
static bool load_buf_fd(int fd, struct fastboot_buffer* buf) {
|
||||
int64_t sz = get_file_size(fd);
|
||||
if (sz == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lseek64(fd, 0, SEEK_SET);
|
||||
int64_t limit = get_sparse_limit(transport, sz);
|
||||
int64_t limit = get_sparse_limit(sz);
|
||||
if (limit) {
|
||||
sparse_file** s = load_sparse_files(fd, limit);
|
||||
if (s == nullptr) {
|
||||
|
@ -783,7 +783,7 @@ static bool load_buf_fd(Transport* transport, int fd, struct fastboot_buffer* bu
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool load_buf(Transport* transport, const char* fname, struct fastboot_buffer* buf) {
|
||||
static bool load_buf(const char* fname, struct fastboot_buffer* buf) {
|
||||
unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));
|
||||
|
||||
if (fd == -1) {
|
||||
|
@ -799,7 +799,7 @@ static bool load_buf(Transport* transport, const char* fname, struct fastboot_bu
|
|||
return false;
|
||||
}
|
||||
|
||||
return load_buf_fd(transport, fd.release(), buf);
|
||||
return load_buf_fd(fd.release(), buf);
|
||||
}
|
||||
|
||||
static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf) {
|
||||
|
@ -871,23 +871,23 @@ static void flash_buf(const std::string& partition, struct fastboot_buffer *buf)
|
|||
}
|
||||
}
|
||||
|
||||
static std::string get_current_slot(Transport* transport) {
|
||||
static std::string get_current_slot() {
|
||||
std::string current_slot;
|
||||
if (!fb_getvar(transport, "current-slot", ¤t_slot)) return "";
|
||||
if (!fb_getvar("current-slot", ¤t_slot)) return "";
|
||||
return current_slot;
|
||||
}
|
||||
|
||||
static int get_slot_count(Transport* transport) {
|
||||
static int get_slot_count() {
|
||||
std::string var;
|
||||
int count = 0;
|
||||
if (!fb_getvar(transport, "slot-count", &var) || !android::base::ParseInt(var, &count)) {
|
||||
if (!fb_getvar("slot-count", &var) || !android::base::ParseInt(var, &count)) {
|
||||
return 0;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static bool supports_AB(Transport* transport) {
|
||||
return get_slot_count(transport) >= 2;
|
||||
static bool supports_AB() {
|
||||
return get_slot_count() >= 2;
|
||||
}
|
||||
|
||||
// Given a current slot, this returns what the 'other' slot is.
|
||||
|
@ -898,25 +898,25 @@ static std::string get_other_slot(const std::string& current_slot, int count) {
|
|||
return std::string(1, next);
|
||||
}
|
||||
|
||||
static std::string get_other_slot(Transport* transport, const std::string& current_slot) {
|
||||
return get_other_slot(current_slot, get_slot_count(transport));
|
||||
static std::string get_other_slot(const std::string& current_slot) {
|
||||
return get_other_slot(current_slot, get_slot_count());
|
||||
}
|
||||
|
||||
static std::string get_other_slot(Transport* transport, int count) {
|
||||
return get_other_slot(get_current_slot(transport), count);
|
||||
static std::string get_other_slot(int count) {
|
||||
return get_other_slot(get_current_slot(), count);
|
||||
}
|
||||
|
||||
static std::string get_other_slot(Transport* transport) {
|
||||
return get_other_slot(get_current_slot(transport), get_slot_count(transport));
|
||||
static std::string get_other_slot() {
|
||||
return get_other_slot(get_current_slot(), get_slot_count());
|
||||
}
|
||||
|
||||
static std::string verify_slot(Transport* transport, const std::string& slot_name, bool allow_all) {
|
||||
static std::string verify_slot(const std::string& slot_name, bool allow_all) {
|
||||
std::string slot = slot_name;
|
||||
if (slot == "all") {
|
||||
if (allow_all) {
|
||||
return "all";
|
||||
} else {
|
||||
int count = get_slot_count(transport);
|
||||
int count = get_slot_count();
|
||||
if (count > 0) {
|
||||
return "a";
|
||||
} else {
|
||||
|
@ -925,11 +925,11 @@ static std::string verify_slot(Transport* transport, const std::string& slot_nam
|
|||
}
|
||||
}
|
||||
|
||||
int count = get_slot_count(transport);
|
||||
int count = get_slot_count();
|
||||
if (count == 0) die("Device does not support slots");
|
||||
|
||||
if (slot == "other") {
|
||||
std::string other = get_other_slot(transport, count);
|
||||
std::string other = get_other_slot( count);
|
||||
if (other == "") {
|
||||
die("No known slots");
|
||||
}
|
||||
|
@ -946,22 +946,22 @@ static std::string verify_slot(Transport* transport, const std::string& slot_nam
|
|||
exit(1);
|
||||
}
|
||||
|
||||
static std::string verify_slot(Transport* transport, const std::string& slot) {
|
||||
return verify_slot(transport, slot, true);
|
||||
static std::string verify_slot(const std::string& slot) {
|
||||
return verify_slot(slot, true);
|
||||
}
|
||||
|
||||
static void do_for_partition(Transport* transport, const std::string& part, const std::string& slot,
|
||||
static void do_for_partition(const std::string& part, const std::string& slot,
|
||||
const std::function<void(const std::string&)>& func, bool force_slot) {
|
||||
std::string has_slot;
|
||||
std::string current_slot;
|
||||
|
||||
if (!fb_getvar(transport, "has-slot:" + part, &has_slot)) {
|
||||
if (!fb_getvar("has-slot:" + part, &has_slot)) {
|
||||
/* If has-slot is not supported, the answer is no. */
|
||||
has_slot = "no";
|
||||
}
|
||||
if (has_slot == "yes") {
|
||||
if (slot == "") {
|
||||
current_slot = get_current_slot(transport);
|
||||
current_slot = get_current_slot();
|
||||
if (current_slot == "") {
|
||||
die("Failed to identify current slot");
|
||||
}
|
||||
|
@ -983,30 +983,30 @@ static void do_for_partition(Transport* transport, const std::string& part, cons
|
|||
* partition names. If force_slot is true, it will fail if a slot is specified, and the given
|
||||
* partition does not support slots.
|
||||
*/
|
||||
static void do_for_partitions(Transport* transport, const std::string& part, const std::string& slot,
|
||||
static void do_for_partitions(const std::string& part, const std::string& slot,
|
||||
const std::function<void(const std::string&)>& func, bool force_slot) {
|
||||
std::string has_slot;
|
||||
|
||||
if (slot == "all") {
|
||||
if (!fb_getvar(transport, "has-slot:" + part, &has_slot)) {
|
||||
if (!fb_getvar("has-slot:" + part, &has_slot)) {
|
||||
die("Could not check if partition %s has slot %s", part.c_str(), slot.c_str());
|
||||
}
|
||||
if (has_slot == "yes") {
|
||||
for (int i=0; i < get_slot_count(transport); i++) {
|
||||
do_for_partition(transport, part, std::string(1, (char)(i + 'a')), func, force_slot);
|
||||
for (int i=0; i < get_slot_count(); i++) {
|
||||
do_for_partition(part, std::string(1, (char)(i + 'a')), func, force_slot);
|
||||
}
|
||||
} else {
|
||||
do_for_partition(transport, part, "", func, force_slot);
|
||||
do_for_partition(part, "", func, force_slot);
|
||||
}
|
||||
} else {
|
||||
do_for_partition(transport, part, slot, func, force_slot);
|
||||
do_for_partition(part, slot, func, force_slot);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_flash(Transport* transport, const char* pname, const char* fname) {
|
||||
static void do_flash(const char* pname, const char* fname) {
|
||||
struct fastboot_buffer buf;
|
||||
|
||||
if (!load_buf(transport, fname, &buf)) {
|
||||
if (!load_buf(fname, &buf)) {
|
||||
die("cannot load '%s': %s", fname, strerror(errno));
|
||||
}
|
||||
flash_buf(pname, &buf);
|
||||
|
@ -1022,20 +1022,20 @@ static void do_update_signature(ZipArchiveHandle zip, const char* filename) {
|
|||
|
||||
// Sets slot_override as the active slot. If slot_override is blank,
|
||||
// set current slot as active instead. This clears slot-unbootable.
|
||||
static void set_active(Transport* transport, const std::string& slot_override) {
|
||||
if (!supports_AB(transport)) return;
|
||||
static void set_active(const std::string& slot_override) {
|
||||
if (!supports_AB()) return;
|
||||
|
||||
if (slot_override != "") {
|
||||
fb_set_active(slot_override);
|
||||
} else {
|
||||
std::string current_slot = get_current_slot(transport);
|
||||
std::string current_slot = get_current_slot();
|
||||
if (current_slot != "") {
|
||||
fb_set_active(current_slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void do_update(Transport* transport, const char* filename, const std::string& slot_override, bool skip_secondary) {
|
||||
static void do_update(const char* filename, const std::string& slot_override, bool skip_secondary) {
|
||||
queue_info_dump();
|
||||
|
||||
fb_queue_query_save("product", cur_product, sizeof(cur_product));
|
||||
|
@ -1052,17 +1052,17 @@ static void do_update(Transport* transport, const char* filename, const std::str
|
|||
die("update package '%s' has no android-info.txt", filename);
|
||||
}
|
||||
|
||||
check_requirements(transport, reinterpret_cast<char*>(data), sz);
|
||||
check_requirements(reinterpret_cast<char*>(data), sz);
|
||||
|
||||
std::string secondary;
|
||||
if (!skip_secondary) {
|
||||
if (slot_override != "") {
|
||||
secondary = get_other_slot(transport, slot_override);
|
||||
secondary = get_other_slot(slot_override);
|
||||
} else {
|
||||
secondary = get_other_slot(transport);
|
||||
secondary = get_other_slot();
|
||||
}
|
||||
if (secondary == "") {
|
||||
if (supports_AB(transport)) {
|
||||
if (supports_AB()) {
|
||||
fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
|
||||
}
|
||||
skip_secondary = true;
|
||||
|
@ -1087,7 +1087,7 @@ static void do_update(Transport* transport, const char* filename, const std::str
|
|||
}
|
||||
|
||||
fastboot_buffer buf;
|
||||
if (!load_buf_fd(transport, fd, &buf)) {
|
||||
if (!load_buf_fd(fd, &buf)) {
|
||||
die("cannot load %s from flash: %s", images[i].img_name, strerror(errno));
|
||||
}
|
||||
|
||||
|
@ -1099,13 +1099,13 @@ static void do_update(Transport* transport, const char* filename, const std::str
|
|||
* program exits.
|
||||
*/
|
||||
};
|
||||
do_for_partitions(transport, images[i].part_name, slot, update, false);
|
||||
do_for_partitions(images[i].part_name, slot, update, false);
|
||||
}
|
||||
|
||||
if (slot_override == "all") {
|
||||
set_active(transport, "a");
|
||||
set_active("a");
|
||||
} else {
|
||||
set_active(transport, slot_override);
|
||||
set_active(slot_override);
|
||||
}
|
||||
|
||||
CloseArchive(zip);
|
||||
|
@ -1125,7 +1125,7 @@ static void do_send_signature(const std::string& fn) {
|
|||
fb_queue_command("signature", "installing signature");
|
||||
}
|
||||
|
||||
static void do_flashall(Transport* transport, const std::string& slot_override, bool skip_secondary) {
|
||||
static void do_flashall(const std::string& slot_override, bool skip_secondary) {
|
||||
std::string fname;
|
||||
queue_info_dump();
|
||||
|
||||
|
@ -1138,17 +1138,17 @@ static void do_flashall(Transport* transport, const std::string& slot_override,
|
|||
void* data = load_file(fname.c_str(), &sz);
|
||||
if (data == nullptr) die("could not load android-info.txt: %s", strerror(errno));
|
||||
|
||||
check_requirements(transport, reinterpret_cast<char*>(data), sz);
|
||||
check_requirements(reinterpret_cast<char*>(data), sz);
|
||||
|
||||
std::string secondary;
|
||||
if (!skip_secondary) {
|
||||
if (slot_override != "") {
|
||||
secondary = get_other_slot(transport, slot_override);
|
||||
secondary = get_other_slot(slot_override);
|
||||
} else {
|
||||
secondary = get_other_slot(transport);
|
||||
secondary = get_other_slot();
|
||||
}
|
||||
if (secondary == "") {
|
||||
if (supports_AB(transport)) {
|
||||
if (supports_AB()) {
|
||||
fprintf(stderr, "Warning: Could not determine slot for secondary images. Ignoring.\n");
|
||||
}
|
||||
skip_secondary = true;
|
||||
|
@ -1165,7 +1165,7 @@ static void do_flashall(Transport* transport, const std::string& slot_override,
|
|||
if (!slot) continue;
|
||||
fname = find_item_given_name(images[i].img_name);
|
||||
fastboot_buffer buf;
|
||||
if (!load_buf(transport, fname.c_str(), &buf)) {
|
||||
if (!load_buf(fname.c_str(), &buf)) {
|
||||
if (images[i].is_optional) continue;
|
||||
die("could not load '%s': %s", images[i].img_name, strerror(errno));
|
||||
}
|
||||
|
@ -1174,13 +1174,13 @@ static void do_flashall(Transport* transport, const std::string& slot_override,
|
|||
do_send_signature(fname.c_str());
|
||||
flash_buf(partition.c_str(), &buf);
|
||||
};
|
||||
do_for_partitions(transport, images[i].part_name, slot, flashall, false);
|
||||
do_for_partitions(images[i].part_name, slot, flashall, false);
|
||||
}
|
||||
|
||||
if (slot_override == "all") {
|
||||
set_active(transport, "a");
|
||||
set_active("a");
|
||||
} else {
|
||||
set_active(transport, slot_override);
|
||||
set_active(slot_override);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1210,9 +1210,9 @@ static std::string fb_fix_numeric_var(std::string var) {
|
|||
return var;
|
||||
}
|
||||
|
||||
static unsigned fb_get_flash_block_size(Transport* transport, std::string name) {
|
||||
static unsigned fb_get_flash_block_size(std::string name) {
|
||||
std::string sizeString;
|
||||
if (!fb_getvar(transport, name, &sizeString) || sizeString.empty()) {
|
||||
if (!fb_getvar(name, &sizeString) || sizeString.empty()) {
|
||||
// This device does not report flash block sizes, so return 0.
|
||||
return 0;
|
||||
}
|
||||
|
@ -1230,7 +1230,7 @@ static unsigned fb_get_flash_block_size(Transport* transport, std::string name)
|
|||
return size;
|
||||
}
|
||||
|
||||
static void fb_perform_format(Transport* transport,
|
||||
static void fb_perform_format(
|
||||
const std::string& partition, int skip_if_not_supported,
|
||||
const std::string& type_override, const std::string& size_override,
|
||||
const std::string& initial_dir) {
|
||||
|
@ -1250,7 +1250,7 @@ static void fb_perform_format(Transport* transport,
|
|||
limit = sparse_limit;
|
||||
}
|
||||
|
||||
if (!fb_getvar(transport, "partition-type:" + partition, &partition_type)) {
|
||||
if (!fb_getvar("partition-type:" + partition, &partition_type)) {
|
||||
errMsg = "Can't determine partition type.\n";
|
||||
goto failed;
|
||||
}
|
||||
|
@ -1262,7 +1262,7 @@ static void fb_perform_format(Transport* transport,
|
|||
partition_type = type_override;
|
||||
}
|
||||
|
||||
if (!fb_getvar(transport, "partition-size:" + partition, &partition_size)) {
|
||||
if (!fb_getvar("partition-size:" + partition, &partition_size)) {
|
||||
errMsg = "Unable to get partition size\n";
|
||||
goto failed;
|
||||
}
|
||||
|
@ -1294,8 +1294,8 @@ static void fb_perform_format(Transport* transport,
|
|||
}
|
||||
|
||||
unsigned eraseBlkSize, logicalBlkSize;
|
||||
eraseBlkSize = fb_get_flash_block_size(transport, "erase-block-size");
|
||||
logicalBlkSize = fb_get_flash_block_size(transport, "logical-block-size");
|
||||
eraseBlkSize = fb_get_flash_block_size("erase-block-size");
|
||||
logicalBlkSize = fb_get_flash_block_size("logical-block-size");
|
||||
|
||||
if (fs_generator_generate(gen, output.path, size, initial_dir,
|
||||
eraseBlkSize, logicalBlkSize)) {
|
||||
|
@ -1308,7 +1308,7 @@ static void fb_perform_format(Transport* transport,
|
|||
fprintf(stderr, "Cannot open generated image: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
if (!load_buf_fd(transport, fd.release(), &buf)) {
|
||||
if (!load_buf_fd(fd.release(), &buf)) {
|
||||
fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
@ -1323,7 +1323,7 @@ failed:
|
|||
fprintf(stderr, "FAILED (%s)\n", fb_get_error().c_str());
|
||||
}
|
||||
|
||||
int FastBoot::Main(int argc, char* argv[]) {
|
||||
int FastBootTool::Main(int argc, char* argv[]) {
|
||||
bool wants_wipe = false;
|
||||
bool wants_reboot = false;
|
||||
bool wants_reboot_bootloader = false;
|
||||
|
@ -1470,23 +1470,25 @@ int FastBoot::Main(int argc, char* argv[]) {
|
|||
if (transport == nullptr) {
|
||||
return 1;
|
||||
}
|
||||
fastboot::FastBootDriver fb(transport);
|
||||
fb_init(fb);
|
||||
|
||||
const double start = now();
|
||||
|
||||
if (slot_override != "") slot_override = verify_slot(transport, slot_override);
|
||||
if (next_active != "") next_active = verify_slot(transport, next_active, false);
|
||||
if (slot_override != "") slot_override = verify_slot(slot_override);
|
||||
if (next_active != "") next_active = verify_slot(next_active, false);
|
||||
|
||||
if (wants_set_active) {
|
||||
if (next_active == "") {
|
||||
if (slot_override == "") {
|
||||
std::string current_slot;
|
||||
if (fb_getvar(transport, "current-slot", ¤t_slot)) {
|
||||
next_active = verify_slot(transport, current_slot, false);
|
||||
if (fb_getvar("current-slot", ¤t_slot)) {
|
||||
next_active = verify_slot(current_slot, false);
|
||||
} else {
|
||||
wants_set_active = false;
|
||||
}
|
||||
} else {
|
||||
next_active = verify_slot(transport, slot_override, false);
|
||||
next_active = verify_slot(slot_override, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1502,7 +1504,7 @@ int FastBoot::Main(int argc, char* argv[]) {
|
|||
std::string partition = next_arg(&args);
|
||||
auto erase = [&](const std::string& partition) {
|
||||
std::string partition_type;
|
||||
if (fb_getvar(transport, std::string("partition-type:") + partition,
|
||||
if (fb_getvar(std::string("partition-type:") + partition,
|
||||
&partition_type) &&
|
||||
fs_get_generator(partition_type) != nullptr) {
|
||||
fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
|
||||
|
@ -1511,7 +1513,7 @@ int FastBoot::Main(int argc, char* argv[]) {
|
|||
|
||||
fb_queue_erase(partition);
|
||||
};
|
||||
do_for_partitions(transport, partition, slot_override, erase, true);
|
||||
do_for_partitions(partition, slot_override, erase, true);
|
||||
} else if (android::base::StartsWith(command, "format")) {
|
||||
// Parsing for: "format[:[type][:[size]]]"
|
||||
// Some valid things:
|
||||
|
@ -1529,9 +1531,9 @@ int FastBoot::Main(int argc, char* argv[]) {
|
|||
std::string partition = next_arg(&args);
|
||||
|
||||
auto format = [&](const std::string& partition) {
|
||||
fb_perform_format(transport, partition, 0, type_override, size_override, "");
|
||||
fb_perform_format(partition, 0, type_override, size_override, "");
|
||||
};
|
||||
do_for_partitions(transport, partition.c_str(), slot_override, format, true);
|
||||
do_for_partitions(partition.c_str(), slot_override, format, true);
|
||||
} else if (command == "signature") {
|
||||
std::string filename = next_arg(&args);
|
||||
data = load_file(filename.c_str(), &sz);
|
||||
|
@ -1579,9 +1581,9 @@ int FastBoot::Main(int argc, char* argv[]) {
|
|||
if (fname.empty()) die("cannot determine image filename for '%s'", pname.c_str());
|
||||
|
||||
auto flash = [&](const std::string &partition) {
|
||||
do_flash(transport, partition.c_str(), fname.c_str());
|
||||
do_flash(partition.c_str(), fname.c_str());
|
||||
};
|
||||
do_for_partitions(transport, pname.c_str(), slot_override, flash, true);
|
||||
do_for_partitions(pname.c_str(), slot_override, flash, true);
|
||||
} else if (command == "flash:raw") {
|
||||
std::string partition = next_arg(&args);
|
||||
std::string kernel = next_arg(&args);
|
||||
|
@ -1594,13 +1596,13 @@ int FastBoot::Main(int argc, char* argv[]) {
|
|||
auto flashraw = [&](const std::string& partition) {
|
||||
fb_queue_flash(partition, data, sz);
|
||||
};
|
||||
do_for_partitions(transport, partition, slot_override, flashraw, true);
|
||||
do_for_partitions(partition, slot_override, flashraw, true);
|
||||
} else if (command == "flashall") {
|
||||
if (slot_override == "all") {
|
||||
fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
|
||||
do_flashall(transport, slot_override, true);
|
||||
do_flashall(slot_override, true);
|
||||
} else {
|
||||
do_flashall(transport, slot_override, skip_secondary);
|
||||
do_flashall(slot_override, skip_secondary);
|
||||
}
|
||||
wants_reboot = true;
|
||||
} else if (command == "update") {
|
||||
|
@ -1612,16 +1614,16 @@ int FastBoot::Main(int argc, char* argv[]) {
|
|||
if (!args.empty()) {
|
||||
filename = next_arg(&args);
|
||||
}
|
||||
do_update(transport, filename.c_str(), slot_override, skip_secondary || slot_all);
|
||||
do_update(filename.c_str(), slot_override, skip_secondary || slot_all);
|
||||
wants_reboot = true;
|
||||
} else if (command == "set_active") {
|
||||
std::string slot = verify_slot(transport, next_arg(&args), false);
|
||||
std::string slot = verify_slot(next_arg(&args), false);
|
||||
fb_set_active(slot);
|
||||
} else if (command == "stage") {
|
||||
std::string filename = next_arg(&args);
|
||||
|
||||
struct fastboot_buffer buf;
|
||||
if (!load_buf(transport, filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
|
||||
if (!load_buf(filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) {
|
||||
die("cannot load '%s'", filename.c_str());
|
||||
}
|
||||
fb_queue_download_fd(filename, buf.fd, buf.sz);
|
||||
|
@ -1650,16 +1652,16 @@ int FastBoot::Main(int argc, char* argv[]) {
|
|||
std::vector<std::string> partitions = { "userdata", "cache", "metadata" };
|
||||
for (const auto& partition : partitions) {
|
||||
std::string partition_type;
|
||||
if (!fb_getvar(transport, std::string{"partition-type:"} + partition, &partition_type)) continue;
|
||||
if (!fb_getvar(std::string{"partition-type:"} + partition, &partition_type)) continue;
|
||||
if (partition_type.empty()) continue;
|
||||
fb_queue_erase(partition);
|
||||
if (partition == "userdata" && set_fbe_marker) {
|
||||
fprintf(stderr, "setting FBE marker on initial userdata...\n");
|
||||
std::string initial_userdata_dir = create_fbemarker_tmpdir();
|
||||
fb_perform_format(transport, partition, 1, "", "", initial_userdata_dir);
|
||||
fb_perform_format(partition, 1, "", "", initial_userdata_dir);
|
||||
delete_fbemarker_tmpdir(initial_userdata_dir);
|
||||
} else {
|
||||
fb_perform_format(transport, partition, 1, "", "", "");
|
||||
fb_perform_format(partition, 1, "", "", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1674,12 +1676,12 @@ int FastBoot::Main(int argc, char* argv[]) {
|
|||
fb_queue_wait_for_disconnect();
|
||||
}
|
||||
|
||||
int status = fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
int status = fb_execute_queue() ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start));
|
||||
return status;
|
||||
}
|
||||
|
||||
void FastBoot::ParseOsPatchLevel(boot_img_hdr_v1* hdr, const char* arg) {
|
||||
void FastBootTool::ParseOsPatchLevel(boot_img_hdr_v1* hdr, const char* arg) {
|
||||
unsigned year, month, day;
|
||||
if (sscanf(arg, "%u-%u-%u", &year, &month, &day) != 3) {
|
||||
syntax_error("OS patch level should be YYYY-MM-DD: %s", arg);
|
||||
|
@ -1689,7 +1691,7 @@ void FastBoot::ParseOsPatchLevel(boot_img_hdr_v1* hdr, const char* arg) {
|
|||
hdr->SetOsPatchLevel(year, month);
|
||||
}
|
||||
|
||||
void FastBoot::ParseOsVersion(boot_img_hdr_v1* hdr, const char* arg) {
|
||||
void FastBootTool::ParseOsVersion(boot_img_hdr_v1* hdr, const char* arg) {
|
||||
unsigned major = 0, minor = 0, patch = 0;
|
||||
std::vector<std::string> versions = android::base::Split(arg, ".");
|
||||
if (versions.size() < 1 || versions.size() > 3 ||
|
||||
|
|
533
fastboot/fastboot_driver.cpp
Normal file
533
fastboot/fastboot_driver.cpp
Normal file
|
@ -0,0 +1,533 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
#include "fastboot_driver.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <utils/FileMap.h>
|
||||
#include "fastboot_driver.h"
|
||||
#include "transport.h"
|
||||
|
||||
namespace fastboot {
|
||||
|
||||
/*************************** PUBLIC *******************************/
|
||||
FastBootDriver::FastBootDriver(Transport* transport, std::function<void(std::string&)> info,
|
||||
bool no_checks)
|
||||
: transport(transport) {
|
||||
info_cb_ = info;
|
||||
disable_checks_ = no_checks;
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {
|
||||
return RawCommand(Commands::BOOT, response, info);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Continue(std::string* response, std::vector<std::string>* info) {
|
||||
return RawCommand(Commands::CONTINUE, response, info);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Erase(const std::string& part, std::string* response,
|
||||
std::vector<std::string>* info) {
|
||||
return RawCommand(Commands::ERASE + part, response, info);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Flash(const std::string& part, std::string* response,
|
||||
std::vector<std::string>* info) {
|
||||
return RawCommand(Commands::FLASH + part, response, info);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::GetVar(const std::string& key, std::string* val,
|
||||
std::vector<std::string>* info) {
|
||||
return RawCommand(Commands::GET_VAR + key, val, info);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::GetVarAll(std::vector<std::string>* response) {
|
||||
std::string tmp;
|
||||
return GetVar("all", &tmp, response);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Powerdown(std::string* response, std::vector<std::string>* info) {
|
||||
return RawCommand(Commands::POWERDOWN, response, info);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Reboot(std::string* response, std::vector<std::string>* info) {
|
||||
return RawCommand(Commands::REBOOT, response, info);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::SetActive(const std::string& part, std::string* response,
|
||||
std::vector<std::string>* info) {
|
||||
return RawCommand(Commands::SET_ACTIVE + part, response, info);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Verify(uint32_t num, std::string* response, std::vector<std::string>* info) {
|
||||
std::string cmd = android::base::StringPrintf("%s%08" PRIx32, Commands::VERIFY.c_str(), num);
|
||||
return RawCommand(cmd, response, info);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::FlashPartition(const std::string& part, const std::vector<char>& data) {
|
||||
RetCode ret;
|
||||
if ((ret = Download(data))) {
|
||||
return ret;
|
||||
}
|
||||
return RawCommand(Commands::FLASH + part);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::FlashPartition(const std::string& part, int fd, uint32_t sz) {
|
||||
RetCode ret;
|
||||
if ((ret = Download(fd, sz))) {
|
||||
return ret;
|
||||
}
|
||||
return RawCommand(Commands::FLASH + part);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::FlashPartition(const std::string& part, sparse_file* s) {
|
||||
RetCode ret;
|
||||
if ((ret = Download(s))) {
|
||||
return ret;
|
||||
}
|
||||
return RawCommand(Commands::FLASH + part);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint32_t>>* parts) {
|
||||
std::vector<std::string> all;
|
||||
RetCode ret;
|
||||
if ((ret = GetVarAll(&all))) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::regex reg("partition-size[[:s:]]*:[[:s:]]*([[:w:]]+)[[:s:]]*:[[:s:]]*0x([[:d:]]+)");
|
||||
std::smatch sm;
|
||||
|
||||
for (auto& s : all) {
|
||||
if (std::regex_match(s, sm, reg)) {
|
||||
std::string m1(sm[1]);
|
||||
std::string m2(sm[2]);
|
||||
uint32_t tmp = strtol(m2.c_str(), 0, 16);
|
||||
parts->push_back(std::make_tuple(m1, tmp));
|
||||
}
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Require(const std::string& var, const std::vector<std::string>& allowed,
|
||||
bool* reqmet, bool invert) {
|
||||
*reqmet = invert;
|
||||
RetCode ret;
|
||||
std::string response;
|
||||
if ((ret = GetVar(var, &response))) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Now check if we have a match
|
||||
for (const auto s : allowed) {
|
||||
// If it ends in *, and starting substring match
|
||||
if (response == s || (s.length() && s.back() == '*' &&
|
||||
!response.compare(0, s.length() - 1, s, 0, s.length() - 1))) {
|
||||
*reqmet = !invert;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Download(int fd, size_t size, std::string* response,
|
||||
std::vector<std::string>* info) {
|
||||
RetCode ret;
|
||||
|
||||
if ((size <= 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
|
||||
error_ = "File is too large to download";
|
||||
return BAD_ARG;
|
||||
}
|
||||
|
||||
uint32_t u32size = static_cast<uint32_t>(size);
|
||||
if ((ret = DownloadCommand(u32size, response, info))) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Write the buffer
|
||||
if ((ret = SendBuffer(fd, size))) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Wait for response
|
||||
return HandleResponse(response, info);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,
|
||||
std::vector<std::string>* info) {
|
||||
return Download(buf.data(), buf.size(), response, info);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Download(const char* buf, uint32_t size, std::string* response,
|
||||
std::vector<std::string>* info) {
|
||||
RetCode ret;
|
||||
error_ = "";
|
||||
if ((size == 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {
|
||||
error_ = "Buffer is too large or 0 bytes";
|
||||
return BAD_ARG;
|
||||
}
|
||||
|
||||
if ((ret = DownloadCommand(size, response, info))) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Write the buffer
|
||||
if ((ret = SendBuffer(buf, size))) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Wait for response
|
||||
return HandleResponse(response, info);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Download(sparse_file* s, std::string* response,
|
||||
std::vector<std::string>* info) {
|
||||
error_ = "";
|
||||
int64_t size = sparse_file_len(s, true, false);
|
||||
if (size <= 0 || size > MAX_DOWNLOAD_SIZE) {
|
||||
error_ = "Sparse file is too large or invalid";
|
||||
return BAD_ARG;
|
||||
}
|
||||
|
||||
RetCode ret;
|
||||
uint32_t u32size = static_cast<uint32_t>(size);
|
||||
if ((ret = DownloadCommand(u32size, response, info))) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct SparseCBPrivate {
|
||||
FastBootDriver* self;
|
||||
std::vector<char> tpbuf;
|
||||
} cb_priv;
|
||||
cb_priv.self = this;
|
||||
|
||||
auto cb = [](void* priv, const void* buf, size_t len) -> int {
|
||||
SparseCBPrivate* data = static_cast<SparseCBPrivate*>(priv);
|
||||
const char* cbuf = static_cast<const char*>(buf);
|
||||
return data->self->SparseWriteCallback(data->tpbuf, cbuf, len);
|
||||
};
|
||||
|
||||
if (sparse_file_callback(s, true, false, cb, &cb_priv) < 0) {
|
||||
error_ = "Error reading sparse file";
|
||||
return IO_ERROR;
|
||||
}
|
||||
|
||||
// Now flush
|
||||
if (cb_priv.tpbuf.size() && (ret = SendBuffer(cb_priv.tpbuf))) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return HandleResponse(response, info);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::Upload(const std::string& outfile, std::string* response,
|
||||
std::vector<std::string>* info) {
|
||||
RetCode ret;
|
||||
int dsize;
|
||||
if ((ret = RawCommand(Commands::UPLOAD, response, info, &dsize)) || dsize == 0) {
|
||||
error_ = "Upload request failed";
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<char> data;
|
||||
data.resize(dsize);
|
||||
|
||||
if ((ret = ReadBuffer(data))) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::ofstream ofs;
|
||||
ofs.open(outfile, std::ofstream::out | std::ofstream::binary);
|
||||
if (ofs.fail()) {
|
||||
error_ = android::base::StringPrintf("Failed to open '%s'", outfile.c_str());
|
||||
return IO_ERROR;
|
||||
}
|
||||
ofs.write(data.data(), data.size());
|
||||
if (ofs.fail() || ofs.bad()) {
|
||||
error_ = android::base::StringPrintf("Writing to '%s' failed", outfile.c_str());
|
||||
return IO_ERROR;
|
||||
}
|
||||
ofs.close();
|
||||
|
||||
return HandleResponse(response, info);
|
||||
}
|
||||
|
||||
// Helpers
|
||||
void FastBootDriver::SetInfoCallback(std::function<void(std::string&)> info) {
|
||||
info_cb_ = info;
|
||||
}
|
||||
|
||||
const std::string FastBootDriver::RCString(RetCode rc) {
|
||||
switch (rc) {
|
||||
case SUCCESS:
|
||||
return std::string("Success");
|
||||
|
||||
case BAD_ARG:
|
||||
return std::string("Invalid Argument");
|
||||
|
||||
case IO_ERROR:
|
||||
return std::string("I/O Error");
|
||||
|
||||
case BAD_DEV_RESP:
|
||||
return std::string("Invalid Device Response");
|
||||
|
||||
case DEVICE_FAIL:
|
||||
return std::string("Device Error");
|
||||
|
||||
case TIMEOUT:
|
||||
return std::string("Timeout");
|
||||
|
||||
default:
|
||||
return std::string("Unknown Error");
|
||||
}
|
||||
}
|
||||
|
||||
std::string FastBootDriver::Error() {
|
||||
return error_;
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::WaitForDisconnect() {
|
||||
return transport->WaitForDisconnect() ? IO_ERROR : SUCCESS;
|
||||
}
|
||||
|
||||
/****************************** PROTECTED *************************************/
|
||||
RetCode FastBootDriver::RawCommand(const std::string& cmd, std::string* response,
|
||||
std::vector<std::string>* info, int* dsize) {
|
||||
error_ = ""; // Clear any pending error
|
||||
if (cmd.size() > FB_COMMAND_SZ && !disable_checks_) {
|
||||
error_ = "Command length to RawCommand() is too long";
|
||||
return BAD_ARG;
|
||||
}
|
||||
|
||||
if (transport->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
|
||||
error_ = ErrnoStr("Write to device failed");
|
||||
return IO_ERROR;
|
||||
}
|
||||
|
||||
// Read the response
|
||||
return HandleResponse(response, info, dsize);
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::DownloadCommand(uint32_t size, std::string* response,
|
||||
std::vector<std::string>* info) {
|
||||
std::string cmd(android::base::StringPrintf("%s%08" PRIx32, Commands::DOWNLOAD.c_str(), size));
|
||||
RetCode ret;
|
||||
if ((ret = RawCommand(cmd, response, info))) {
|
||||
return ret;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::HandleResponse(std::string* response, std::vector<std::string>* info,
|
||||
int* dsize) {
|
||||
char status[FB_RESPONSE_SZ + 1];
|
||||
auto start = std::chrono::system_clock::now();
|
||||
|
||||
auto set_response = [response](std::string s) {
|
||||
if (response) *response = std::move(s);
|
||||
};
|
||||
auto add_info = [info](std::string s) {
|
||||
if (info) info->push_back(std::move(s));
|
||||
};
|
||||
|
||||
// erase response
|
||||
set_response("");
|
||||
while ((std::chrono::system_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
|
||||
int r = transport->Read(status, FB_RESPONSE_SZ);
|
||||
if (r < 0) {
|
||||
error_ = ErrnoStr("Status read failed");
|
||||
return IO_ERROR;
|
||||
}
|
||||
|
||||
status[r] = '\0'; // Need the null terminator
|
||||
std::string input(status);
|
||||
if (android::base::StartsWith(input, "INFO")) {
|
||||
std::string tmp = input.substr(strlen("INFO"));
|
||||
info_cb_(tmp);
|
||||
add_info(std::move(tmp));
|
||||
} else if (android::base::StartsWith(input, "OKAY")) {
|
||||
set_response(input.substr(strlen("OKAY")));
|
||||
return SUCCESS;
|
||||
} else if (android::base::StartsWith(input, "FAIL")) {
|
||||
error_ = android::base::StringPrintf("remote: '%s'", status + strlen("FAIL"));
|
||||
set_response(input.substr(strlen("FAIL")));
|
||||
return DEVICE_FAIL;
|
||||
} else if (android::base::StartsWith(input, "DATA")) {
|
||||
std::string tmp = input.substr(strlen("DATA"));
|
||||
uint32_t num = strtol(tmp.c_str(), 0, 16);
|
||||
if (num > MAX_DOWNLOAD_SIZE) {
|
||||
error_ = android::base::StringPrintf("Data size too large (%d)", num);
|
||||
return BAD_DEV_RESP;
|
||||
}
|
||||
if (dsize) *dsize = num;
|
||||
set_response(std::move(tmp));
|
||||
return SUCCESS;
|
||||
} else {
|
||||
error_ = android::base::StringPrintf("Device sent unknown status code: %s", status);
|
||||
return BAD_DEV_RESP;
|
||||
}
|
||||
|
||||
} // End of while loop
|
||||
|
||||
return TIMEOUT;
|
||||
}
|
||||
|
||||
std::string FastBootDriver::ErrnoStr(const std::string& msg) {
|
||||
return android::base::StringPrintf("%s (%s)", msg.c_str(), strerror(errno));
|
||||
}
|
||||
|
||||
const std::string FastBootDriver::Commands::BOOT = "boot";
|
||||
const std::string FastBootDriver::Commands::CONTINUE = "continue";
|
||||
const std::string FastBootDriver::Commands::DOWNLOAD = "download:";
|
||||
const std::string FastBootDriver::Commands::ERASE = "erase:";
|
||||
const std::string FastBootDriver::Commands::FLASH = "flash:";
|
||||
const std::string FastBootDriver::Commands::GET_VAR = "getvar:";
|
||||
const std::string FastBootDriver::Commands::POWERDOWN = "powerdown";
|
||||
const std::string FastBootDriver::Commands::REBOOT = "reboot";
|
||||
const std::string FastBootDriver::Commands::SET_ACTIVE = "set_active:";
|
||||
const std::string FastBootDriver::Commands::UPLOAD = "upload";
|
||||
const std::string FastBootDriver::Commands::VERIFY = "verify:";
|
||||
|
||||
/******************************* PRIVATE **************************************/
|
||||
RetCode FastBootDriver::SendBuffer(int fd, size_t size) {
|
||||
static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
|
||||
off64_t offset = 0;
|
||||
uint32_t remaining = size;
|
||||
RetCode ret;
|
||||
|
||||
while (remaining) {
|
||||
// Memory map the file
|
||||
android::FileMap filemap;
|
||||
size_t len = std::min(remaining, MAX_MAP_SIZE);
|
||||
|
||||
if (!filemap.create(NULL, fd, offset, len, true)) {
|
||||
error_ = "Creating filemap failed";
|
||||
return IO_ERROR;
|
||||
}
|
||||
|
||||
if ((ret = SendBuffer(filemap.getDataPtr(), len))) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
remaining -= len;
|
||||
offset += len;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::SendBuffer(const std::vector<char>& buf) {
|
||||
// Write the buffer
|
||||
return SendBuffer(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::SendBuffer(const void* buf, size_t size) {
|
||||
// Write the buffer
|
||||
ssize_t tmp = transport->Write(buf, size);
|
||||
|
||||
if (tmp < 0) {
|
||||
error_ = ErrnoStr("Write to device failed in SendBuffer()");
|
||||
return IO_ERROR;
|
||||
} else if (static_cast<size_t>(tmp) != size) {
|
||||
error_ = android::base::StringPrintf("Failed to write all %zu bytes", size);
|
||||
|
||||
return IO_ERROR;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::ReadBuffer(std::vector<char>& buf) {
|
||||
// Read the buffer
|
||||
return ReadBuffer(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
RetCode FastBootDriver::ReadBuffer(void* buf, size_t size) {
|
||||
// Read the buffer
|
||||
ssize_t tmp = transport->Read(buf, size);
|
||||
|
||||
if (tmp < 0) {
|
||||
error_ = ErrnoStr("Read from device failed in ReadBuffer()");
|
||||
return IO_ERROR;
|
||||
} else if (static_cast<size_t>(tmp) != size) {
|
||||
error_ = android::base::StringPrintf("Failed to read all %zu bytes", size);
|
||||
return IO_ERROR;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int FastBootDriver::SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len) {
|
||||
size_t total = 0;
|
||||
size_t to_write = std::min(TRANSPORT_CHUNK_SIZE - tpbuf.size(), len);
|
||||
|
||||
// Handle the residual
|
||||
tpbuf.insert(tpbuf.end(), data, data + to_write);
|
||||
if (tpbuf.size() < TRANSPORT_CHUNK_SIZE) { // Nothing enough to send rn
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (SendBuffer(tpbuf)) {
|
||||
error_ = ErrnoStr("Send failed in SparseWriteCallback()");
|
||||
return -1;
|
||||
}
|
||||
tpbuf.clear();
|
||||
total += to_write;
|
||||
|
||||
// Now we need to send a multiple of chunk size
|
||||
size_t nchunks = (len - total) / TRANSPORT_CHUNK_SIZE;
|
||||
size_t nbytes = TRANSPORT_CHUNK_SIZE * nchunks;
|
||||
if (SendBuffer(data + total, nbytes)) {
|
||||
error_ = ErrnoStr("Send failed in SparseWriteCallback()");
|
||||
return -1;
|
||||
}
|
||||
total += nbytes;
|
||||
|
||||
if (len - total > 0) { // We have residual data to save for next time
|
||||
tpbuf.assign(data + total, data + len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // End namespace fastboot
|
154
fastboot/fastboot_driver.h
Normal file
154
fastboot/fastboot_driver.h
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
#pragma once
|
||||
#include <cstdlib>
|
||||
#include <deque>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <bootimg.h>
|
||||
#include <inttypes.h>
|
||||
#include <sparse/sparse.h>
|
||||
#include "transport.h"
|
||||
|
||||
class Transport;
|
||||
|
||||
namespace fastboot {
|
||||
|
||||
static constexpr int FB_COMMAND_SZ = 64;
|
||||
static constexpr int FB_RESPONSE_SZ = 64;
|
||||
|
||||
enum RetCode : int {
|
||||
SUCCESS = 0,
|
||||
BAD_ARG,
|
||||
IO_ERROR,
|
||||
BAD_DEV_RESP,
|
||||
DEVICE_FAIL,
|
||||
TIMEOUT,
|
||||
};
|
||||
|
||||
class FastBootDriver {
|
||||
public:
|
||||
static constexpr int RESP_TIMEOUT = 10; // 10 seconds
|
||||
static constexpr uint32_t MAX_DOWNLOAD_SIZE = std::numeric_limits<uint32_t>::max();
|
||||
static constexpr size_t TRANSPORT_CHUNK_SIZE = 1024;
|
||||
|
||||
FastBootDriver(Transport* transport,
|
||||
std::function<void(std::string&)> info = [](std::string&) {},
|
||||
bool no_checks = false);
|
||||
|
||||
RetCode Boot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
|
||||
RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
|
||||
RetCode Download(int fd, size_t size, std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr);
|
||||
RetCode Download(const std::vector<char>& buf, std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr);
|
||||
// This will be removed after fastboot is modified to use a vector
|
||||
RetCode Download(const char* buf, uint32_t size, std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr);
|
||||
RetCode Download(sparse_file* s, std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr);
|
||||
RetCode Erase(const std::string& part, std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr);
|
||||
RetCode Flash(const std::string& part, std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr);
|
||||
RetCode GetVar(const std::string& key, std::string* val,
|
||||
std::vector<std::string>* info = nullptr);
|
||||
RetCode GetVarAll(std::vector<std::string>* response);
|
||||
RetCode Powerdown(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
|
||||
RetCode Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
|
||||
RetCode SetActive(const std::string& part, std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr);
|
||||
RetCode Upload(const std::string& outfile, std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr);
|
||||
RetCode Verify(uint32_t num, std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr);
|
||||
|
||||
/* HIGHER LEVEL COMMANDS -- Composed of the commands above */
|
||||
RetCode FlashPartition(const std::string& part, const std::vector<char>& data);
|
||||
RetCode FlashPartition(const std::string& part, int fd, uint32_t sz);
|
||||
RetCode FlashPartition(const std::string& part, sparse_file* s);
|
||||
|
||||
RetCode Partitions(std::vector<std::tuple<std::string, uint32_t>>* parts);
|
||||
RetCode Require(const std::string& var, const std::vector<std::string>& allowed, bool* reqmet,
|
||||
bool invert = false);
|
||||
|
||||
/* HELPERS */
|
||||
void SetInfoCallback(std::function<void(std::string&)> info);
|
||||
const std::string RCString(RetCode rc);
|
||||
std::string Error();
|
||||
RetCode WaitForDisconnect();
|
||||
|
||||
// This is temporarily public for engine.cpp
|
||||
RetCode RawCommand(const std::string& cmd, std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr, int* dsize = nullptr);
|
||||
|
||||
protected:
|
||||
RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr);
|
||||
RetCode HandleResponse(std::string* response = nullptr,
|
||||
std::vector<std::string>* info = nullptr, int* dsize = nullptr);
|
||||
|
||||
std::string ErrnoStr(const std::string& msg);
|
||||
|
||||
// More like a namespace...
|
||||
struct Commands {
|
||||
static const std::string BOOT;
|
||||
static const std::string CONTINUE;
|
||||
static const std::string DOWNLOAD;
|
||||
static const std::string ERASE;
|
||||
static const std::string FLASH;
|
||||
static const std::string GET_VAR;
|
||||
static const std::string POWERDOWN;
|
||||
static const std::string REBOOT;
|
||||
static const std::string SET_ACTIVE;
|
||||
static const std::string UPLOAD;
|
||||
static const std::string VERIFY;
|
||||
};
|
||||
|
||||
Transport* const transport;
|
||||
|
||||
private:
|
||||
RetCode SendBuffer(int fd, size_t size);
|
||||
RetCode SendBuffer(const std::vector<char>& buf);
|
||||
RetCode SendBuffer(const void* buf, size_t size);
|
||||
|
||||
RetCode ReadBuffer(std::vector<char>& buf);
|
||||
RetCode ReadBuffer(void* buf, size_t size);
|
||||
|
||||
int SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len);
|
||||
|
||||
std::string error_;
|
||||
std::function<void(std::string&)> info_cb_;
|
||||
bool disable_checks_;
|
||||
};
|
||||
|
||||
} // namespace fastboot
|
|
@ -14,12 +14,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "fastboot.h"
|
||||
#include "engine.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(FastBoot, ParseOsPatchLevel) {
|
||||
FastBoot fb;
|
||||
FastBootTool fb;
|
||||
boot_img_hdr_v1 hdr;
|
||||
|
||||
hdr = {};
|
||||
|
@ -34,7 +34,7 @@ TEST(FastBoot, ParseOsPatchLevel) {
|
|||
}
|
||||
|
||||
TEST(FastBoot, ParseOsVersion) {
|
||||
FastBoot fb;
|
||||
FastBootTool fb;
|
||||
boot_img_hdr_v1 hdr;
|
||||
|
||||
hdr = {};
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "fs.h"
|
||||
|
||||
#include "fastboot.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
|
|
@ -26,9 +26,9 @@
|
|||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "fastboot.h"
|
||||
#include "engine.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
FastBoot fb;
|
||||
FastBootTool fb;
|
||||
return fb.Main(argc, argv);
|
||||
}
|
||||
|
|
|
@ -1,370 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define round_down(a, b) \
|
||||
({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <sparse/sparse.h>
|
||||
#include <utils/FileMap.h>
|
||||
|
||||
#include "constants.h"
|
||||
#include "fastboot.h"
|
||||
#include "transport.h"
|
||||
|
||||
static std::string g_error;
|
||||
|
||||
using android::base::unique_fd;
|
||||
using android::base::WriteStringToFile;
|
||||
|
||||
const std::string fb_get_error() {
|
||||
return g_error;
|
||||
}
|
||||
|
||||
static int64_t check_response(Transport* transport, uint32_t size, char* response) {
|
||||
char status[FB_RESPONSE_SZ + 1];
|
||||
|
||||
while (true) {
|
||||
int r = transport->Read(status, FB_RESPONSE_SZ);
|
||||
if (r < 0) {
|
||||
g_error = android::base::StringPrintf("status read failed (%s)", strerror(errno));
|
||||
transport->Close();
|
||||
return -1;
|
||||
}
|
||||
status[r] = 0;
|
||||
|
||||
if (static_cast<size_t>(r) < strlen(RESPONSE_OKAY)) {
|
||||
g_error = android::base::StringPrintf("status malformed (%d bytes)", r);
|
||||
transport->Close();
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!memcmp(status, RESPONSE_INFO, strlen(RESPONSE_INFO))) {
|
||||
verbose("received INFO \"%s\"", status + strlen(RESPONSE_INFO));
|
||||
fprintf(stderr, "(bootloader) %s\n", status + strlen(RESPONSE_INFO));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!memcmp(status, RESPONSE_OKAY, strlen(RESPONSE_OKAY))) {
|
||||
verbose("received OKAY \"%s\"", status + strlen(RESPONSE_OKAY));
|
||||
if (response) {
|
||||
strcpy(response, status + strlen(RESPONSE_OKAY));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!memcmp(status, RESPONSE_FAIL, strlen(RESPONSE_FAIL))) {
|
||||
verbose("received FAIL \"%s\"", status + strlen(RESPONSE_FAIL));
|
||||
if (static_cast<size_t>(r) > strlen(RESPONSE_FAIL)) {
|
||||
g_error = android::base::StringPrintf("remote: %s", status + strlen(RESPONSE_FAIL));
|
||||
} else {
|
||||
g_error = "remote failure";
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!memcmp(status, RESPONSE_DATA, strlen(RESPONSE_DATA)) && size > 0){
|
||||
verbose("received DATA %s", status + strlen(RESPONSE_DATA));
|
||||
uint32_t dsize = strtol(status + strlen(RESPONSE_DATA), 0, 16);
|
||||
if (dsize > size) {
|
||||
g_error = android::base::StringPrintf("data size too large (%d)", dsize);
|
||||
transport->Close();
|
||||
return -1;
|
||||
}
|
||||
return dsize;
|
||||
}
|
||||
|
||||
verbose("received unknown status code \"%4.4s\"", status);
|
||||
g_error = "unknown status code";
|
||||
transport->Close();
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int64_t _command_start(Transport* transport, const std::string& cmd, uint32_t size,
|
||||
char* response) {
|
||||
if (cmd.size() > FB_COMMAND_SZ) {
|
||||
g_error = android::base::StringPrintf("command too large (%zu)", cmd.size());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (response) {
|
||||
response[0] = 0;
|
||||
}
|
||||
|
||||
verbose("sending command \"%s\"", cmd.c_str());
|
||||
|
||||
if (transport->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
|
||||
g_error = android::base::StringPrintf("command write failed (%s)", strerror(errno));
|
||||
transport->Close();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return check_response(transport, size, response);
|
||||
}
|
||||
|
||||
static int64_t _command_write_data(Transport* transport, const void* data, uint32_t size) {
|
||||
verbose("sending data (%" PRIu32 " bytes)", size);
|
||||
|
||||
int64_t r = transport->Write(data, size);
|
||||
if (r < 0) {
|
||||
g_error = android::base::StringPrintf("data write failure (%s)", strerror(errno));
|
||||
transport->Close();
|
||||
return -1;
|
||||
}
|
||||
if (r != static_cast<int64_t>(size)) {
|
||||
g_error = "data write failure (short transfer)";
|
||||
transport->Close();
|
||||
return -1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int64_t _command_read_data(Transport* transport, void* data, uint32_t size) {
|
||||
verbose("reading data (%" PRIu32 " bytes)", size);
|
||||
|
||||
int64_t r = transport->Read(data, size);
|
||||
if (r < 0) {
|
||||
g_error = android::base::StringPrintf("data read failure (%s)", strerror(errno));
|
||||
transport->Close();
|
||||
return -1;
|
||||
}
|
||||
if (r != (static_cast<int64_t>(size))) {
|
||||
g_error = "data read failure (short transfer)";
|
||||
transport->Close();
|
||||
return -1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
static int64_t _command_end(Transport* transport) {
|
||||
return check_response(transport, 0, 0) < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
static int64_t _command_send(Transport* transport, const std::string& cmd, const void* data,
|
||||
uint32_t size, char* response) {
|
||||
if (size == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t r = _command_start(transport, cmd, size, response);
|
||||
if (r < 0) {
|
||||
return -1;
|
||||
}
|
||||
r = _command_write_data(transport, data, size);
|
||||
if (r < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = _command_end(transport);
|
||||
if (r < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int64_t _command_send_fd(Transport* transport, const std::string& cmd, int fd, uint32_t size,
|
||||
char* response) {
|
||||
static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;
|
||||
off64_t offset = 0;
|
||||
uint32_t remaining = size;
|
||||
|
||||
if (_command_start(transport, cmd, size, response) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (remaining) {
|
||||
android::FileMap filemap;
|
||||
size_t len = std::min(remaining, MAX_MAP_SIZE);
|
||||
|
||||
if (!filemap.create(NULL, fd, offset, len, true)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (_command_write_data(transport, filemap.getDataPtr(), len) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
remaining -= len;
|
||||
offset += len;
|
||||
}
|
||||
|
||||
if (_command_end(transport) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int _command_send_no_data(Transport* transport, const std::string& cmd, char* response) {
|
||||
return _command_start(transport, cmd, 0, response);
|
||||
}
|
||||
|
||||
int fb_command(Transport* transport, const std::string& cmd) {
|
||||
return _command_send_no_data(transport, cmd, 0);
|
||||
}
|
||||
|
||||
int fb_command_response(Transport* transport, const std::string& cmd, char* response) {
|
||||
return _command_send_no_data(transport, cmd, response);
|
||||
}
|
||||
|
||||
int64_t fb_download_data(Transport* transport, const void* data, uint32_t size) {
|
||||
std::string cmd(android::base::StringPrintf(
|
||||
FB_CMD_DOWNLOAD ":" "%08x", size));
|
||||
return _command_send(transport, cmd.c_str(), data, size, 0) < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
int64_t fb_download_data_fd(Transport* transport, int fd, uint32_t size) {
|
||||
std::string cmd(android::base::StringPrintf(
|
||||
FB_CMD_DOWNLOAD ":" "%08x", size));
|
||||
return _command_send_fd(transport, cmd.c_str(), fd, size, 0) < 0 ? -1 : 0;
|
||||
}
|
||||
|
||||
int64_t fb_upload_data(Transport* transport, const char* outfile) {
|
||||
// positive return value is the upload size sent by the device
|
||||
int64_t r = _command_start(transport, FB_CMD_UPLOAD,
|
||||
std::numeric_limits<int32_t>::max(), nullptr);
|
||||
if (r <= 0) {
|
||||
g_error = android::base::StringPrintf("command start failed (%s)", strerror(errno));
|
||||
return r;
|
||||
}
|
||||
|
||||
std::string data;
|
||||
data.resize(r);
|
||||
if ((r = _command_read_data(transport, &data[0], data.size())) == -1) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!WriteStringToFile(data, outfile, true)) {
|
||||
g_error = android::base::StringPrintf("write to '%s' failed", outfile);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _command_end(transport);
|
||||
}
|
||||
|
||||
static constexpr size_t TRANSPORT_BUF_SIZE = 1024;
|
||||
static char transport_buf[TRANSPORT_BUF_SIZE];
|
||||
static size_t transport_buf_len;
|
||||
|
||||
static int fb_download_data_sparse_write(void* priv, const void* data, size_t len) {
|
||||
const char* ptr = static_cast<const char*>(data);
|
||||
if (transport_buf_len) {
|
||||
size_t to_write = std::min(TRANSPORT_BUF_SIZE - transport_buf_len, len);
|
||||
|
||||
memcpy(transport_buf + transport_buf_len, ptr, to_write);
|
||||
transport_buf_len += to_write;
|
||||
ptr += to_write;
|
||||
len -= to_write;
|
||||
}
|
||||
|
||||
Transport* transport = static_cast<Transport*>(priv);
|
||||
if (transport_buf_len == TRANSPORT_BUF_SIZE) {
|
||||
int64_t r = _command_write_data(transport, transport_buf, TRANSPORT_BUF_SIZE);
|
||||
if (r != static_cast<int64_t>(TRANSPORT_BUF_SIZE)) {
|
||||
return -1;
|
||||
}
|
||||
transport_buf_len = 0;
|
||||
}
|
||||
|
||||
if (len > TRANSPORT_BUF_SIZE) {
|
||||
if (transport_buf_len > 0) {
|
||||
g_error = "internal error: transport_buf not empty";
|
||||
return -1;
|
||||
}
|
||||
size_t to_write = round_down(len, TRANSPORT_BUF_SIZE);
|
||||
int64_t r = _command_write_data(transport, ptr, to_write);
|
||||
if (r != static_cast<int64_t>(to_write)) {
|
||||
return -1;
|
||||
}
|
||||
ptr += to_write;
|
||||
len -= to_write;
|
||||
}
|
||||
|
||||
if (len > 0) {
|
||||
if (len > TRANSPORT_BUF_SIZE) {
|
||||
g_error = "internal error: too much left for transport_buf";
|
||||
return -1;
|
||||
}
|
||||
memcpy(transport_buf, ptr, len);
|
||||
transport_buf_len = len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fb_download_data_sparse_flush(Transport* transport) {
|
||||
if (transport_buf_len > 0) {
|
||||
int64_t r = _command_write_data(transport, transport_buf, transport_buf_len);
|
||||
if (r != static_cast<int64_t>(transport_buf_len)) {
|
||||
return -1;
|
||||
}
|
||||
transport_buf_len = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fb_download_data_sparse(Transport* transport, struct sparse_file* s) {
|
||||
int64_t size = sparse_file_len(s, true, false);
|
||||
if (size <= 0 || size > std::numeric_limits<uint32_t>::max()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string cmd(android::base::StringPrintf(
|
||||
FB_CMD_DOWNLOAD ":" "%08" PRIx64, size));
|
||||
int r = _command_start(transport, cmd, size, 0);
|
||||
if (r < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, transport);
|
||||
if (r < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
r = fb_download_data_sparse_flush(transport);
|
||||
if (r < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return _command_end(transport);
|
||||
}
|
|
@ -47,8 +47,8 @@
|
|||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "fastboot.h"
|
||||
#include "usb.h"
|
||||
#include "util.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "fastboot.h"
|
||||
#include "util.h"
|
||||
|
||||
static bool g_verbose = false;
|
||||
|
||||
|
|
19
fastboot/util.h
Normal file
19
fastboot/util.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <bootimg.h>
|
||||
|
||||
/* util stuff */
|
||||
double now();
|
||||
char* xstrdup(const char*);
|
||||
void set_verbose();
|
||||
|
||||
// These printf-like functions are implemented in terms of vsnprintf, so they
|
||||
// use the same attribute for compile-time format string checking.
|
||||
void die(const char* fmt, ...) __attribute__((__noreturn__))
|
||||
__attribute__((__format__(__printf__, 1, 2)));
|
||||
void verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));
|
Loading…
Reference in a new issue