diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp index 271b792df..60b71247e 100644 --- a/fastboot/engine.cpp +++ b/fastboot/engine.cpp @@ -27,7 +27,6 @@ */ #include "fastboot.h" -#include "fs.h" #include #include @@ -38,150 +37,117 @@ #include #include -#define OP_DOWNLOAD 1 -#define OP_COMMAND 2 -#define OP_QUERY 3 -#define OP_NOTICE 4 -#define OP_DOWNLOAD_SPARSE 5 -#define OP_WAIT_FOR_DISCONNECT 6 -#define OP_DOWNLOAD_FD 7 -#define OP_UPLOAD 8 +#include +#include -typedef struct Action Action; +#include -#define CMD_SIZE 64 - -struct Action { - unsigned op; - Action* next; - - char cmd[CMD_SIZE]; - const char* prod; - void* data; - int fd; - - // The protocol only supports 32-bit sizes, so you'll have to break - // anything larger into chunks. - uint32_t size; - - const char *msg; - int (*func)(Action* a, int status, const char* resp); - - double start; +enum Op { + OP_DOWNLOAD, + OP_COMMAND, + OP_QUERY, + OP_NOTICE, + OP_DOWNLOAD_SPARSE, + OP_WAIT_FOR_DISCONNECT, + OP_DOWNLOAD_FD, + OP_UPLOAD, }; -static Action *action_list = 0; -static Action *action_last = 0; +struct Action { + Action(Op op, const std::string& cmd) : op(op), cmd(cmd) {} + Op op; + std::string cmd; + std::string msg; + std::string product; + void* data = nullptr; + // The protocol only supports 32-bit sizes, so you'll have to break + // anything larger into multiple chunks. + uint32_t size = 0; + + int fd = -1; + + int (*func)(Action& a, int status, const char* resp) = nullptr; + + double start = -1; +}; + +static std::vector> action_list; bool fb_getvar(Transport* transport, const std::string& key, std::string* value) { - std::string cmd = "getvar:"; - cmd += key; + std::string cmd = "getvar:" + key; char buf[FB_RESPONSE_SZ + 1]; memset(buf, 0, sizeof(buf)); - if (fb_command_response(transport, cmd.c_str(), buf)) { - return false; + if (fb_command_response(transport, cmd, buf)) { + return false; } *value = buf; return true; } -static int cb_default(Action* a, int status, const char* resp) { +static int cb_default(Action& a, int status, const char* resp) { if (status) { fprintf(stderr,"FAILED (%s)\n", resp); } else { double split = now(); - fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start)); - a->start = split; + fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start)); + a.start = split; } return status; } -static Action *queue_action(unsigned op, const char *fmt, ...) -{ - va_list ap; - size_t cmdsize; - - Action* a = reinterpret_cast(calloc(1, sizeof(Action))); - if (a == nullptr) die("out of memory"); - - va_start(ap, fmt); - cmdsize = vsnprintf(a->cmd, sizeof(a->cmd), fmt, ap); - va_end(ap); - - if (cmdsize >= sizeof(a->cmd)) { - free(a); - die("Command length (%zu) exceeds maximum size (%zu)", cmdsize, sizeof(a->cmd)); - } - - if (action_last) { - action_last->next = a; - } else { - action_list = a; - } - action_last = a; - a->op = op; +static Action& queue_action(Op op, const std::string& cmd) { + std::unique_ptr a{new Action(op, cmd)}; a->func = cb_default; - a->start = -1; - - return a; + action_list.push_back(std::move(a)); + return *action_list.back(); } -void fb_set_active(const char *slot) -{ - Action *a; - a = queue_action(OP_COMMAND, "set_active:%s", slot); - a->msg = mkmsg("Setting current slot to '%s'", slot); +void fb_set_active(const std::string& slot) { + Action& a = queue_action(OP_COMMAND, "set_active:" + slot); + a.msg = "Setting current slot to '" + slot + "'..."; } -void fb_queue_erase(const char *ptn) -{ - Action *a; - a = queue_action(OP_COMMAND, "erase:%s", ptn); - a->msg = mkmsg("erasing '%s'", ptn); +void fb_queue_erase(const std::string& partition) { + Action& a = queue_action(OP_COMMAND, "erase:" + partition); + a.msg = "Erasing '" + partition + "'..."; } -void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz) -{ - Action *a; +void fb_queue_flash_fd(const std::string& partition, int fd, uint32_t sz) { + Action& a = queue_action(OP_DOWNLOAD_FD, ""); + a.fd = fd; + a.size = sz; + a.msg = android::base::StringPrintf("Sending '%s' (%d KB)...", partition.c_str(), sz / 1024); - a = queue_action(OP_DOWNLOAD_FD, ""); - a->fd = fd; - a->size = sz; - a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024); - - a = queue_action(OP_COMMAND, "flash:%s", ptn); - a->msg = mkmsg("writing '%s'", ptn); + Action& b = queue_action(OP_COMMAND, "flash:" + partition); + b.msg = "Writing '" + partition + "'..."; } -void fb_queue_flash(const char *ptn, void *data, uint32_t sz) -{ - Action *a; +void fb_queue_flash(const std::string& partition, void* data, uint32_t sz) { + Action& a = queue_action(OP_DOWNLOAD, ""); + a.data = data; + a.size = sz; + a.msg = android::base::StringPrintf("Sending '%s' (%d KB)...", partition.c_str(), sz / 1024); - a = queue_action(OP_DOWNLOAD, ""); - a->data = data; - a->size = sz; - a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024); - - a = queue_action(OP_COMMAND, "flash:%s", ptn); - a->msg = mkmsg("writing '%s'", ptn); + Action& b = queue_action(OP_COMMAND, "flash:" + partition); + b.msg = "Writing '" + partition + "'..."; } -void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current, - size_t total) { - Action *a; +void fb_queue_flash_sparse(const std::string& partition, struct sparse_file* s, uint32_t sz, + size_t current, size_t total) { + Action& a = queue_action(OP_DOWNLOAD_SPARSE, ""); + a.data = s; + a.size = 0; + a.msg = android::base::StringPrintf("Sending sparse '%s' %zu/%zu (%d KB)...", partition.c_str(), + current, total, sz / 1024); - a = queue_action(OP_DOWNLOAD_SPARSE, ""); - a->data = s; - a->size = 0; - a->msg = mkmsg("sending sparse '%s' %zu/%zu (%d KB)", ptn, current, total, sz / 1024); - - a = queue_action(OP_COMMAND, "flash:%s", ptn); - a->msg = mkmsg("writing '%s' %zu/%zu", ptn, current, total); + Action& b = queue_action(OP_COMMAND, "flash:" + partition); + b.msg = + android::base::StringPrintf("Writing '%s' %zu/%zu...", partition.c_str(), current, total); } static int match(const char* str, const char** value, unsigned count) { @@ -205,212 +171,181 @@ static int match(const char* str, const char** value, unsigned count) { return 0; } - - -static int cb_check(Action* a, int status, const char* resp, int invert) -{ - const char** value = reinterpret_cast(a->data); - unsigned count = a->size; +static int cb_check(Action& a, int status, const char* resp, int invert) { + const char** value = reinterpret_cast(a.data); + unsigned count = a.size; unsigned n; - int yes; if (status) { fprintf(stderr,"FAILED (%s)\n", resp); return status; } - if (a->prod) { - if (strcmp(a->prod, cur_product) != 0) { + if (!a.product.empty()) { + if (a.product != cur_product) { double split = now(); - fprintf(stderr,"IGNORE, product is %s required only for %s [%7.3fs]\n", - cur_product, a->prod, (split - a->start)); - a->start = split; + fprintf(stderr, "IGNORE, product is %s required only for %s [%7.3fs]\n", cur_product, + a.product.c_str(), (split - a.start)); + a.start = split; return 0; } } - yes = match(resp, value, count); + int yes = match(resp, value, count); if (invert) yes = !yes; if (yes) { double split = now(); - fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start)); - a->start = split; + fprintf(stderr, "OKAY [%7.3fs]\n", (split - a.start)); + a.start = split; return 0; } - fprintf(stderr,"FAILED\n\n"); - fprintf(stderr,"Device %s is '%s'.\n", a->cmd + 7, resp); - fprintf(stderr,"Update %s '%s'", - invert ? "rejects" : "requires", value[0]); + fprintf(stderr, "FAILED\n\n"); + fprintf(stderr, "Device %s is '%s'.\n", a.cmd.c_str() + 7, resp); + fprintf(stderr, "Update %s '%s'", invert ? "rejects" : "requires", value[0]); for (n = 1; n < count; n++) { - fprintf(stderr," or '%s'", value[n]); + fprintf(stderr, " or '%s'", value[n]); } - fprintf(stderr,".\n\n"); + fprintf(stderr, ".\n\n"); return -1; } -static int cb_require(Action*a, int status, const char* resp) { +static int cb_require(Action& a, int status, const char* resp) { return cb_check(a, status, resp, 0); } -static int cb_reject(Action* a, int status, const char* resp) { +static int cb_reject(Action& a, int status, const char* resp) { return cb_check(a, status, resp, 1); } -static char* xstrdup(const char* s) { - char* result = strdup(s); - if (!result) die("out of memory"); - return result; +void fb_queue_require(const std::string& product, const std::string& var, bool invert, + size_t nvalues, const char** values) { + Action& a = queue_action(OP_QUERY, "getvar:" + var); + a.product = product; + a.data = values; + a.size = nvalues; + a.msg = "Checking " + var; + a.func = invert ? cb_reject : cb_require; + if (a.data == nullptr) die("out of memory"); } -void fb_queue_require(const char *prod, const char *var, - bool invert, size_t nvalues, const char **value) -{ - Action *a; - a = queue_action(OP_QUERY, "getvar:%s", var); - a->prod = prod; - a->data = value; - a->size = nvalues; - a->msg = mkmsg("checking %s", var); - a->func = invert ? cb_reject : cb_require; - if (a->data == nullptr) die("out of memory"); -} - -static int cb_display(Action* a, int status, const char* resp) { +static int cb_display(Action& a, int status, const char* resp) { if (status) { - fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp); + fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp); return status; } - fprintf(stderr, "%s: %s\n", static_cast(a->data), resp); - free(static_cast(a->data)); + fprintf(stderr, "%s: %s\n", static_cast(a.data), resp); + free(static_cast(a.data)); return 0; } -void fb_queue_display(const char* var, const char* prettyname) { - Action* a = queue_action(OP_QUERY, "getvar:%s", var); - a->data = xstrdup(prettyname); - a->func = cb_display; +void fb_queue_display(const std::string& label, const std::string& var) { + Action& a = queue_action(OP_QUERY, "getvar:" + var); + a.data = xstrdup(label.c_str()); + a.func = cb_display; } -static int cb_save(Action* a, int status, const char* resp) { +static int cb_save(Action& a, int status, const char* resp) { if (status) { - fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp); + fprintf(stderr, "%s FAILED (%s)\n", a.cmd.c_str(), resp); return status; } - strncpy(reinterpret_cast(a->data), resp, a->size); + strncpy(reinterpret_cast(a.data), resp, a.size); return 0; } -void fb_queue_query_save(const char* var, char* dest, uint32_t dest_size) { - Action* a = queue_action(OP_QUERY, "getvar:%s", var); - a->data = dest; - a->size = dest_size; - a->func = cb_save; +void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size) { + Action& a = queue_action(OP_QUERY, "getvar:" + var); + a.data = dest; + a.size = dest_size; + a.func = cb_save; } -static int cb_do_nothing(Action*, int , const char*) { - fprintf(stderr,"\n"); +static int cb_do_nothing(Action&, int, const char*) { + fprintf(stderr, "\n"); return 0; } -void fb_queue_reboot(void) -{ - Action *a = queue_action(OP_COMMAND, "reboot"); - a->func = cb_do_nothing; - a->msg = "rebooting"; +void fb_queue_reboot() { + Action& a = queue_action(OP_COMMAND, "reboot"); + a.func = cb_do_nothing; + a.msg = "Rebooting..."; } -void fb_queue_command(const char *cmd, const char *msg) -{ - Action *a = queue_action(OP_COMMAND, cmd); - a->msg = msg; +void fb_queue_command(const std::string& cmd, const std::string& msg) { + Action& a = queue_action(OP_COMMAND, cmd); + a.msg = msg; } -void fb_queue_download(const char *name, void *data, uint32_t size) -{ - Action *a = queue_action(OP_DOWNLOAD, ""); - a->data = data; - a->size = size; - a->msg = mkmsg("downloading '%s'", name); +void fb_queue_download(const std::string& name, void* data, uint32_t size) { + Action& a = queue_action(OP_DOWNLOAD, ""); + a.data = data; + a.size = size; + a.msg = "Downloading '" + name + "'"; } -void fb_queue_download_fd(const char *name, int fd, uint32_t sz) -{ - Action *a; - a = queue_action(OP_DOWNLOAD_FD, ""); - a->fd = fd; - a->size = sz; - a->msg = mkmsg("sending '%s' (%d KB)", name, sz / 1024); +void fb_queue_download_fd(const std::string& name, int fd, uint32_t sz) { + Action& a = queue_action(OP_DOWNLOAD_FD, ""); + a.fd = fd; + a.size = sz; + a.msg = android::base::StringPrintf("Sending '%s' (%d KB)", name.c_str(), sz / 1024); } -void fb_queue_upload(const char* outfile) { - Action* a = queue_action(OP_UPLOAD, ""); - a->data = xstrdup(outfile); - a->msg = mkmsg("uploading '%s'", outfile); +void fb_queue_upload(const std::string& outfile) { + Action& a = queue_action(OP_UPLOAD, ""); + a.data = xstrdup(outfile.c_str()); + a.msg = "Uploading '" + outfile + "'"; } -void fb_queue_notice(const char* notice) { - Action *a = queue_action(OP_NOTICE, ""); - a->data = (void*) notice; +void fb_queue_notice(const std::string& notice) { + Action& a = queue_action(OP_NOTICE, ""); + a.msg = notice; } -void fb_queue_wait_for_disconnect(void) -{ +void fb_queue_wait_for_disconnect() { queue_action(OP_WAIT_FOR_DISCONNECT, ""); } -int64_t fb_execute_queue(Transport* transport) -{ - Action *a; - char resp[FB_RESPONSE_SZ+1]; +int64_t fb_execute_queue(Transport* transport) { int64_t status = 0; - - a = action_list; - if (!a) - return status; - resp[FB_RESPONSE_SZ] = 0; - - double start = -1; - for (a = action_list; a; a = a->next) { + for (auto& a : action_list) { a->start = now(); - if (start < 0) start = a->start; - if (a->msg) { - // fprintf(stderr,"%30s... ",a->msg); - fprintf(stderr,"%s...\n",a->msg); + if (!a->msg.empty()) { + fprintf(stderr, "%s\n", a->msg.c_str()); } if (a->op == OP_DOWNLOAD) { status = fb_download_data(transport, a->data, a->size); - status = a->func(a, status, status ? fb_get_error().c_str() : ""); + 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 = a->func(a, status, status ? fb_get_error().c_str() : ""); + 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 = a->func(a, status, status ? fb_get_error().c_str() : ""); + 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); + status = a->func(*a, status, status ? fb_get_error().c_str() : resp); if (status) break; } else if (a->op == OP_NOTICE) { - fprintf(stderr,"%s\n",(char*)a->data); + // We already showed the notice because it's in `Action::msg`. } else if (a->op == OP_DOWNLOAD_SPARSE) { status = fb_download_data_sparse(transport, reinterpret_cast(a->data)); - status = a->func(a, status, status ? fb_get_error().c_str() : ""); + status = a->func(*a, status, status ? fb_get_error().c_str() : ""); if (status) break; } else if (a->op == OP_WAIT_FOR_DISCONNECT) { transport->WaitForDisconnect(); } else if (a->op == OP_UPLOAD) { status = fb_upload_data(transport, reinterpret_cast(a->data)); - status = a->func(a, status, status ? fb_get_error().c_str() : ""); + status = a->func(*a, status, status ? fb_get_error().c_str() : ""); } else { - die("bogus action"); + die("unknown action: %d", a->op); } } - - fprintf(stderr,"finished. total time: %.3fs\n", (now() - start)); + action_list.clear(); return status; } diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index 7155b7ebd..237f08170 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -635,27 +635,31 @@ static int unzip_to_file(ZipArchiveHandle zip, const char* entry_name) { return fd.release(); } -static char *strip(char *s) -{ - int n; - while(*s && isspace(*s)) s++; - n = strlen(s); - while(n-- > 0) { - if(!isspace(s[n])) break; +static char* strip(char* s) { + while (*s && isspace(*s)) s++; + + int n = strlen(s); + while (n-- > 0) { + if (!isspace(s[n])) break; s[n] = 0; } return s; } #define MAX_OPTIONS 32 -static int setup_requirement_line(char *name) -{ +static void check_requirement(Transport* transport, char* line) { char *val[MAX_OPTIONS]; - char *prod = nullptr; - unsigned n, count; + unsigned count; char *x; int invert = 0; + // "require product=alpha|beta|gamma" + // "require version-bootloader=1234" + // "require-for-product:gamma version-bootloader=istanbul|constantinople" + // "require partition-exists=vendor" + + char* name = line; + const char* product = ""; if (!strncmp(name, "reject ", 7)) { name += 7; invert = 1; @@ -664,19 +668,46 @@ static int setup_requirement_line(char *name) invert = 0; } else if (!strncmp(name, "require-for-product:", 20)) { // Get the product and point name past it - prod = name + 20; + product = name + 20; name = strchr(name, ' '); - if (!name) return -1; + if (!name) die("android-info.txt syntax error: %s", line); *name = 0; name += 1; invert = 0; } x = strchr(name, '='); - if (x == 0) return 0; + if (x == 0) return; *x = 0; val[0] = x + 1; + name = strip(name); + + // "require partition-exists=x" is a special case, added because of the trouble we had when + // Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them, + // missing out new partitions. A device with new partitions can use "partition-exists" to + // override the `is_optional` field in the `images` array. + 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) || + (has_slot != "yes" && has_slot != "no")) { + die("device doesn't have required partition %s!", partition_name); + } + bool known_partition = false; + for (size_t i = 0; i < arraysize(images); ++i) { + if (images[i].nickname && !strcmp(images[i].nickname, partition_name)) { + images[i].is_optional = false; + known_partition = true; + } + } + if (!known_partition) { + die("device requires partition %s which is not known to this version of fastboot", + partition_name); + } + return; + } + for(count = 1; count < MAX_OPTIONS; count++) { x = strchr(val[count - 1],'|'); if (x == 0) break; @@ -684,54 +715,39 @@ static int setup_requirement_line(char *name) val[count] = x + 1; } - name = strip(name); - for(n = 0; n < count; n++) val[n] = strip(val[n]); - - name = strip(name); - if (name == 0) return -1; - - const char* var = name; // Work around an unfortunate name mismatch. - if (!strcmp(name,"board")) var = "product"; + const char* var = name; + if (!strcmp(name, "board")) var = "product"; const char** out = reinterpret_cast(malloc(sizeof(char*) * count)); - if (out == 0) return -1; + if (out == nullptr) die("out of memory"); - for(n = 0; n < count; n++) { - out[n] = strdup(strip(val[n])); - if (out[n] == 0) { - for(size_t i = 0; i < n; ++i) { - free((char*) out[i]); - } - free(out); - return -1; - } + for (size_t i = 0; i < count; ++i) { + out[i] = xstrdup(strip(val[i])); } - fb_queue_require(prod, var, invert, n, out); - return 0; + fb_queue_require(product, var, invert, count, out); } -static void setup_requirements(char* data, int64_t sz) { +static void check_requirements(Transport* transport, char* data, int64_t sz) { char* s = data; while (sz-- > 0) { if (*s == '\n') { *s++ = 0; - if (setup_requirement_line(data)) { - die("out of memory"); - } + check_requirement(transport, data); data = s; } else { s++; } } + if (fb_execute_queue(transport)) die("requirements not met!"); } static void queue_info_dump() { fb_queue_notice("--------------------------------------------"); - fb_queue_display("version-bootloader", "Bootloader Version..."); - fb_queue_display("version-baseband", "Baseband Version....."); - fb_queue_display("serialno", "Serial Number........"); + fb_queue_display("Bootloader Version...", "version-bootloader"); + fb_queue_display("Baseband Version.....", "version-baseband"); + fb_queue_display("Serial Number........", "serialno"); fb_queue_notice("--------------------------------------------"); } @@ -891,14 +907,13 @@ static void rewrite_vbmeta_buffer(struct fastboot_buffer* buf) { lseek(fd, 0, SEEK_SET); } -static void flash_buf(const char *pname, struct fastboot_buffer *buf) +static void flash_buf(const std::string& partition, struct fastboot_buffer *buf) { sparse_file** s; // Rewrite vbmeta if that's what we're flashing and modification has been requested. if ((g_disable_verity || g_disable_verification) && - (strcmp(pname, "vbmeta") == 0 || strcmp(pname, "vbmeta_a") == 0 || - strcmp(pname, "vbmeta_b") == 0)) { + (partition == "vbmeta" || partition == "vbmeta_a" || partition == "vbmeta_b")) { rewrite_vbmeta_buffer(buf); } @@ -914,12 +929,12 @@ static void flash_buf(const char *pname, struct fastboot_buffer *buf) for (size_t i = 0; i < sparse_files.size(); ++i) { const auto& pair = sparse_files[i]; - fb_queue_flash_sparse(pname, pair.first, pair.second, i + 1, sparse_files.size()); + fb_queue_flash_sparse(partition, pair.first, pair.second, i + 1, sparse_files.size()); } break; } case FB_BUFFER_FD: - fb_queue_flash_fd(pname, buf->fd, buf->sz); + fb_queue_flash_fd(partition, buf->fd, buf->sz); break; default: die("unknown buffer type: %d", buf->type); @@ -1117,11 +1132,11 @@ static void set_active(Transport* transport, const std::string& slot_override) { } } if (slot_override != "") { - fb_set_active((separator + slot_override).c_str()); + fb_set_active(separator + slot_override); } else { std::string current_slot = get_current_slot(transport); if (current_slot != "") { - fb_set_active((separator + current_slot).c_str()); + fb_set_active(separator + current_slot); } } } @@ -1143,7 +1158,7 @@ static void do_update(Transport* transport, const char* filename, const std::str die("update package '%s' has no android-info.txt", filename); } - setup_requirements(reinterpret_cast(data), sz); + check_requirements(transport, reinterpret_cast(data), sz); std::string secondary; if (!skip_secondary) { @@ -1185,7 +1200,7 @@ static void do_update(Transport* transport, const char* filename, const std::str auto update = [&](const std::string& partition) { do_update_signature(zip, images[i].sig_name); if (erase_first && needs_erase(transport, partition.c_str())) { - fb_queue_erase(partition.c_str()); + fb_queue_erase(partition); } flash_buf(partition.c_str(), &buf); /* not closing the fd here since the sparse code keeps the fd around @@ -1232,7 +1247,7 @@ 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)); - setup_requirements(reinterpret_cast(data), sz); + check_requirements(transport, reinterpret_cast(data), sz); std::string secondary; if (!skip_secondary) { @@ -1267,7 +1282,7 @@ static void do_flashall(Transport* transport, const std::string& slot_override, auto flashall = [&](const std::string &partition) { do_send_signature(fname.c_str()); if (erase_first && needs_erase(transport, partition.c_str())) { - fb_queue_erase(partition.c_str()); + fb_queue_erase(partition); } flash_buf(partition.c_str(), &buf); }; @@ -1307,7 +1322,7 @@ static void do_oem_command(const std::string& cmd, std::vector* arg while (!args->empty()) { command += " " + next_arg(args); } - fb_queue_command(command.c_str(), ""); + fb_queue_command(command, ""); } static int64_t parse_num(const char *arg) @@ -1362,8 +1377,8 @@ static std::string fb_fix_numeric_var(std::string var) { static unsigned fb_get_flash_block_size(Transport* transport, std::string name) { std::string sizeString; - if (!fb_getvar(transport, name.c_str(), &sizeString) || sizeString.empty()) { - /* This device does not report flash block sizes, so return 0 */ + if (!fb_getvar(transport, name, &sizeString) || sizeString.empty()) { + // This device does not report flash block sizes, so return 0. return 0; } sizeString = fb_fix_numeric_var(sizeString); @@ -1381,7 +1396,7 @@ static unsigned fb_get_flash_block_size(Transport* transport, std::string name) } static void fb_perform_format(Transport* transport, - const char* partition, int skip_if_not_supported, + const std::string& partition, int skip_if_not_supported, const std::string& type_override, const std::string& size_override, const std::string& initial_dir) { std::string partition_type, partition_size; @@ -1400,26 +1415,26 @@ static void fb_perform_format(Transport* transport, limit = sparse_limit; } - if (!fb_getvar(transport, std::string("partition-type:") + partition, &partition_type)) { + if (!fb_getvar(transport, "partition-type:" + partition, &partition_type)) { errMsg = "Can't determine partition type.\n"; goto failed; } if (!type_override.empty()) { if (partition_type != type_override) { fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n", - partition, partition_type.c_str(), type_override.c_str()); + partition.c_str(), partition_type.c_str(), type_override.c_str()); } partition_type = type_override; } - if (!fb_getvar(transport, std::string("partition-size:") + partition, &partition_size)) { + if (!fb_getvar(transport, "partition-size:" + partition, &partition_size)) { errMsg = "Unable to get partition size\n"; goto failed; } if (!size_override.empty()) { if (partition_size != size_override) { fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n", - partition, partition_size.c_str(), size_override.c_str()); + partition.c_str(), partition_size.c_str(), size_override.c_str()); } partition_size = size_override; } @@ -1449,7 +1464,7 @@ static void fb_perform_format(Transport* transport, if (fs_generator_generate(gen, output.path, size, initial_dir, eraseBlkSize, logicalBlkSize)) { - die("Cannot generate image for %s", partition); + die("Cannot generate image for %s", partition.c_str()); return; } @@ -1632,6 +1647,8 @@ int main(int argc, char **argv) return 1; } + const double start = now(); + if (!supports_AB(transport) && supports_AB_obsolete(transport)) { fprintf(stderr, "Warning: Device A/B support is outdated. Bootloader update required.\n"); } @@ -1659,7 +1676,7 @@ int main(int argc, char **argv) if (command == "getvar") { std::string variable = next_arg(&args); - fb_queue_display(variable.c_str(), variable.c_str()); + fb_queue_display(variable, variable); } else if (command == "erase") { std::string partition = next_arg(&args); auto erase = [&](const std::string& partition) { @@ -1671,7 +1688,7 @@ int main(int argc, char **argv) partition_type.c_str()); } - fb_queue_erase(partition.c_str()); + fb_queue_erase(partition); }; do_for_partitions(transport, partition, slot_override, erase, true); } else if (android::base::StartsWith(command, "format")) { @@ -1692,10 +1709,9 @@ int main(int argc, char **argv) auto format = [&](const std::string& partition) { if (erase_first && needs_erase(transport, partition.c_str())) { - fb_queue_erase(partition.c_str()); + fb_queue_erase(partition); } - fb_perform_format(transport, partition.c_str(), 0, type_override, size_override, - ""); + fb_perform_format(transport, partition, 0, type_override, size_override, ""); }; do_for_partitions(transport, partition.c_str(), slot_override, format, true); } else if (command == "signature") { @@ -1749,7 +1765,7 @@ int main(int argc, char **argv) auto flash = [&](const std::string &partition) { if (erase_first && needs_erase(transport, partition.c_str())) { - fb_queue_erase(partition.c_str()); + fb_queue_erase(partition); } do_flash(transport, partition.c_str(), fname.c_str()); }; @@ -1764,7 +1780,7 @@ int main(int argc, char **argv) data = load_bootable_image(kernel, ramdisk, second_stage, &sz, cmdline); auto flashraw = [&](const std::string& partition) { - fb_queue_flash(partition.c_str(), data, sz); + fb_queue_flash(partition, data, sz); }; do_for_partitions(transport, partition, slot_override, flashraw, true); } else if (command == "flashall") { @@ -1798,7 +1814,7 @@ int main(int argc, char **argv) fb_getvar(transport, "slot-suffixes", &var)) { slot = "_" + slot; } - fb_set_active(slot.c_str()); + fb_set_active(slot); } else if (command == "stage") { std::string filename = next_arg(&args); @@ -1806,10 +1822,10 @@ int main(int argc, char **argv) if (!load_buf(transport, filename.c_str(), &buf) || buf.type != FB_BUFFER_FD) { die("cannot load '%s'", filename.c_str()); } - fb_queue_download_fd(filename.c_str(), buf.fd, buf.sz); + fb_queue_download_fd(filename, buf.fd, buf.sz); } else if (command == "get_staged") { std::string filename = next_arg(&args); - fb_queue_upload(filename.c_str()); + fb_queue_upload(filename); } else if (command == "oem") { do_oem_command("oem", &args); } else if (command == "flashing") { @@ -1855,7 +1871,7 @@ int main(int argc, char **argv) } } if (wants_set_active) { - fb_set_active(next_active.c_str()); + fb_set_active(next_active); } if (wants_reboot && !skip_reboot) { fb_queue_reboot(); @@ -1868,5 +1884,7 @@ int main(int argc, char **argv) fb_queue_wait_for_disconnect(); } - return fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS; + int status = fb_execute_queue(transport) ? EXIT_FAILURE : EXIT_SUCCESS; + fprintf(stderr, "Finished. Total time: %.3fs\n", (now() - start)); + return status; } diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index f4faa213c..a31057a23 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -39,8 +39,8 @@ struct sparse_file; /* protocol.c - fastboot protocol */ -int fb_command(Transport* transport, const char* cmd); -int fb_command_response(Transport* transport, const char* cmd, char* response); +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); @@ -52,29 +52,29 @@ const std::string fb_get_error(); /* engine.c - high level command queue engine */ bool fb_getvar(Transport* transport, const std::string& key, std::string* value); -void fb_queue_flash(const char *ptn, void *data, uint32_t sz); -void fb_queue_flash_fd(const char *ptn, int fd, uint32_t sz); -void fb_queue_flash_sparse(const char* ptn, struct sparse_file* s, uint32_t sz, size_t current, - size_t total); -void fb_queue_erase(const char *ptn); -void fb_queue_format(const char *ptn, int skip_if_not_supported, int32_t max_chunk_sz); -void fb_queue_require(const char *prod, const char *var, bool invert, - size_t nvalues, const char **value); -void fb_queue_display(const char *var, const char *prettyname); -void fb_queue_query_save(const char *var, char *dest, uint32_t dest_size); +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, + size_t current, size_t total); +void fb_queue_erase(const std::string& partition); +void fb_queue_format(const std::string& partition, int skip_if_not_supported, int32_t max_chunk_sz); +void fb_queue_require(const std::string& prod, const std::string& var, bool invert, size_t nvalues, + const char** values); +void fb_queue_display(const std::string& label, const std::string& var); +void fb_queue_query_save(const std::string& var, char* dest, uint32_t dest_size); void fb_queue_reboot(void); -void fb_queue_command(const char *cmd, const char *msg); -void fb_queue_download(const char *name, void *data, uint32_t size); -void fb_queue_download_fd(const char *name, int fd, uint32_t sz); -void fb_queue_upload(const char* outfile); -void fb_queue_notice(const char *notice); +void fb_queue_command(const std::string& cmd, const std::string& msg); +void fb_queue_download(const std::string& name, void* data, uint32_t size); +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); -void fb_set_active(const char *slot); +void fb_set_active(const std::string& slot); /* util stuff */ double now(); -char *mkmsg(const char *fmt, ...); +char* xstrdup(const char*); // These printf-like functions are implemented in terms of vsnprintf, so they // use the same attribute for compile-time format string checking. On Windows, diff --git a/fastboot/protocol.cpp b/fastboot/protocol.cpp index dcdf8f0c1..c23986131 100644 --- a/fastboot/protocol.cpp +++ b/fastboot/protocol.cpp @@ -113,10 +113,10 @@ static int64_t check_response(Transport* transport, uint32_t size, char* respons return -1; } -static int64_t _command_start(Transport* transport, const char* cmd, uint32_t size, char* response) { - size_t cmdsize = strlen(cmd); - if (cmdsize > 64) { - g_error = android::base::StringPrintf("command too large (%zu)", cmdsize); +static int64_t _command_start(Transport* transport, const std::string& cmd, uint32_t size, + char* response) { + if (cmd.size() > 64) { + g_error = android::base::StringPrintf("command too large (%zu)", cmd.size()); return -1; } @@ -124,7 +124,7 @@ static int64_t _command_start(Transport* transport, const char* cmd, uint32_t si response[0] = 0; } - if (transport->Write(cmd, cmdsize) != static_cast(cmdsize)) { + if (transport->Write(cmd.c_str(), cmd.size()) != static_cast(cmd.size())) { g_error = android::base::StringPrintf("command write failed (%s)", strerror(errno)); transport->Close(); return -1; @@ -167,8 +167,8 @@ static int64_t _command_end(Transport* transport) { return check_response(transport, 0, 0) < 0 ? -1 : 0; } -static int64_t _command_send(Transport* transport, const char* cmd, const void* data, uint32_t size, - char* response) { +static int64_t _command_send(Transport* transport, const std::string& cmd, const void* data, + uint32_t size, char* response) { if (size == 0) { return -1; } @@ -190,7 +190,7 @@ static int64_t _command_send(Transport* transport, const char* cmd, const void* return size; } -static int64_t _command_send_fd(Transport* transport, const char* cmd, int fd, uint32_t 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; @@ -223,15 +223,15 @@ static int64_t _command_send_fd(Transport* transport, const char* cmd, int fd, u return size; } -static int _command_send_no_data(Transport* transport, const char* cmd, char* response) { +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 char* cmd) { +int fb_command(Transport* transport, const std::string& cmd) { return _command_send_no_data(transport, cmd, 0); } -int fb_command_response(Transport* transport, const char* cmd, char* response) { +int fb_command_response(Transport* transport, const std::string& cmd, char* response) { return _command_send_no_data(transport, cmd, response); } @@ -339,7 +339,7 @@ int fb_download_data_sparse(Transport* transport, struct sparse_file* s) { } std::string cmd(android::base::StringPrintf("download:%08x", size)); - int r = _command_start(transport, cmd.c_str(), size, 0); + int r = _command_start(transport, cmd, size, 0); if (r < 0) { return -1; } diff --git a/fastboot/util.cpp b/fastboot/util.cpp index f2bbd3472..fb085e757 100644 --- a/fastboot/util.cpp +++ b/fastboot/util.cpp @@ -35,35 +35,24 @@ #include "fastboot.h" -double now() -{ +double now() { struct timeval tv; gettimeofday(&tv, NULL); return (double)tv.tv_sec + (double)tv.tv_usec / 1000000; } -char *mkmsg(const char *fmt, ...) -{ - char buf[256]; - char *s; - va_list ap; - - va_start(ap, fmt); - vsprintf(buf, fmt, ap); - va_end(ap); - - s = strdup(buf); - if (s == 0) die("out of memory"); - return s; -} - -void die(const char *fmt, ...) -{ +void die(const char* fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr,"error: "); vfprintf(stderr, fmt, ap); fprintf(stderr,"\n"); va_end(ap); - exit(1); + exit(EXIT_FAILURE); +} + +char* xstrdup(const char* s) { + char* result = strdup(s); + if (!result) die("out of memory"); + return result; }