From a9fac4155f645b59d92028d56af573999051ab70 Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Thu, 12 Jan 2012 14:01:43 -0800 Subject: [PATCH 1/4] Fix hd command so it doesn't error out on EOF hd would error out on files that were not a multiple of its read buffer size (4096). For example: Read error on init.rc, offset 17040 len 4096, No such file or directory The fix is to stop reading on EOF instead of treating it as an error. Change-Id: Ie7f4216e08835150f2f5784e77bb9e2190b0def4 Signed-off-by: Scott Anderson --- toolbox/hd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/toolbox/hd.c b/toolbox/hd.c index da312452b..0d2f96af5 100644 --- a/toolbox/hd.c +++ b/toolbox/hd.c @@ -68,6 +68,8 @@ int hd_main(int argc, char *argv[]) if(count > 0 && base + count - filepos < read_len) read_len = base + count - filepos; res = read(fd, &buf, read_len); + if(res == 0) + break; for(i = 0; i < res; i++) { if((i & 15) == 0) { printf("%08x: ", filepos + i); @@ -80,7 +82,7 @@ int hd_main(int argc, char *argv[]) lsum = 0; } } - if(res <= 0) { + if(res < 0) { printf("Read error on %s, offset %d len %d, %s\n", argv[optind], filepos, read_len, strerror(errno)); return 1; } From a9d6a534ca0d6eef631833ff891e015ad29ee9ce Mon Sep 17 00:00:00 2001 From: Scott Anderson Date: Wed, 11 Jan 2012 18:13:26 -0800 Subject: [PATCH 2/4] Add md5 command to toolbox. This command outputs the MD5 for specified files. The output is in the same form as the md5sum command on Linux. Signed-off-by: Scott Anderson (cherry picked from commit d0455c952d61d36e662f4a95d5e03689ecedca8f) Change-Id: I3be7751b6e301eb2d12844f88f1f5b9ed1225f6b --- toolbox/Android.mk | 5 ++- toolbox/md5.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 1 deletion(-) create mode 100644 toolbox/md5.c diff --git a/toolbox/Android.mk b/toolbox/Android.mk index d7a675a70..7e20448b1 100644 --- a/toolbox/Android.mk +++ b/toolbox/Android.mk @@ -55,7 +55,8 @@ TOOLS := \ nandread \ ionice \ touch \ - lsof + lsof \ + md5 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) TOOLS += r @@ -68,6 +69,8 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := libcutils libc libusbhost +LOCAL_C_INCLUDES := bionic/libc/bionic + LOCAL_MODULE:= toolbox # Including this will define $(intermediates). diff --git a/toolbox/md5.c b/toolbox/md5.c new file mode 100644 index 000000000..2fb8b053a --- /dev/null +++ b/toolbox/md5.c @@ -0,0 +1,77 @@ +#include +#include +#include +#include +#include +#include +#include + +/* When this was written, bionic's md5.h did not define this. */ +#ifndef MD5_DIGEST_LENGTH +#define MD5_DIGEST_LENGTH 16 +#endif + +static int usage() +{ + fprintf(stderr,"md5 file ...\n"); + return -1; +} + +static int do_md5(const char *path) +{ + unsigned int i; + int fd; + MD5_CTX md5_ctx; + unsigned char md5[MD5_DIGEST_LENGTH]; + + fd = open(path, O_RDONLY); + if (fd < 0) { + fprintf(stderr,"could not open %s, %s\n", path, strerror(errno)); + return -1; + } + + /* Note that bionic's MD5_* functions return void. */ + MD5_Init(&md5_ctx); + + while (1) { + char buf[4096]; + ssize_t rlen; + rlen = read(fd, buf, sizeof(buf)); + if (rlen == 0) + break; + else if (rlen < 0) { + (void)close(fd); + fprintf(stderr,"could not read %s, %s\n", path, strerror(errno)); + return -1; + } + MD5_Update(&md5_ctx, buf, rlen); + } + if (close(fd)) { + fprintf(stderr,"could not close %s, %s\n", path, strerror(errno)); + return -1; + } + + MD5_Final(md5, &md5_ctx); + + for (i = 0; i < (int)sizeof(md5); i++) + printf("%02x", md5[i]); + printf(" %s\n", path); + + return 0; +} + +int md5_main(int argc, char *argv[]) +{ + int i, ret = 0; + + if (argc < 2) + return usage(); + + /* loop over the file args */ + for (i = 1; i < argc; i++) { + if (do_md5(argv[i])) + ret = 1; + } + + return ret; +} From 65cf84f3ff959eec0c97229489d58279feaf3bcb Mon Sep 17 00:00:00 2001 From: Anatol Pomazau Date: Thu, 15 Dec 2011 17:50:18 -0800 Subject: [PATCH 3/4] Implement 'fastboot format' command Some filesystems (e.g. ext4) require flushing an initial fs image, right after erasing it the partition is unusable. Doing erase,flush emptyfs is a little bit scaring so we have a separate command that performs it as atomic step: - get size of partition - create an empty filesystem image - erase the partition - flush empty fs to the partition This command applicable only for ext4 filesystem and checks the partition type before formatting it. Change-Id: I8529bc1dc64698f1f0d91312f7c0ab1a6e5d8b44 --- fastboot/Android.mk | 7 ++- fastboot/engine.c | 149 +++++++++++++++++++++++++++++++++++++++++++- fastboot/fastboot.c | 5 ++ fastboot/fastboot.h | 1 + 4 files changed, 157 insertions(+), 5 deletions(-) diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 3c5538f4a..8c130ffde 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -16,8 +16,9 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg -LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \ + $(LOCAL_PATH)/../../extras/ext4_utils +LOCAL_SRC_FILES := protocol.c engine.c bootimg.c fastboot.c LOCAL_MODULE := fastboot ifeq ($(HOST_OS),linux) @@ -47,7 +48,7 @@ ifeq ($(HOST_OS),windows) LOCAL_C_INCLUDES += development/host/windows/usb/api endif -LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz +LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz libext4_utils libz include $(BUILD_HOST_EXECUTABLE) $(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE)) diff --git a/fastboot/engine.c b/fastboot/engine.c index 6d9403551..e40db2bad 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -26,13 +26,24 @@ * SUCH DAMAGE. */ +#include "fastboot.h" +#include "make_ext4fs.h" +#include "ext4_utils.h" + #include #include #include +#include #include +#include +#include #include +#include +#include -#include "fastboot.h" +extern struct fs_info info; + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) double now() { @@ -60,15 +71,18 @@ char *mkmsg(const char *fmt, ...) #define OP_COMMAND 2 #define OP_QUERY 3 #define OP_NOTICE 4 +#define OP_FORMAT 5 typedef struct Action Action; +#define CMD_SIZE 64 + struct Action { unsigned op; Action *next; - char cmd[64]; + char cmd[CMD_SIZE]; const char *prod; void *data; unsigned size; @@ -82,6 +96,35 @@ struct Action static Action *action_list = 0; static Action *action_last = 0; + +struct image_data { + long long partition_size; + long long image_size; // real size of image file + void *buffer; +}; + +void generate_ext4_image(struct image_data *image); +void munmap_image(struct image_data *image); + +struct generator { + char *fs_type; + + /* generate image and return it as image->buffer. + * size of the buffer returned as image->image_size. + * + * image->partition_size specifies what is the size of the + * file partition we generate image for. + */ + void (*generate)(struct image_data *image); + + /* it cleans the buffer allocated during image creation. + * this function probably does free() or munmap(). + */ + void (*cleanup)(struct image_data *image); +} generators[] = { + { "ext4", generate_ext4_image, munmap_image } +}; + static int cb_default(Action *a, int status, char *resp) { if (status) { @@ -133,6 +176,104 @@ void fb_queue_erase(const char *ptn) a->msg = mkmsg("erasing '%s'", ptn); } +void munmap_image(struct image_data *image) +{ + munmap(image->buffer, image->image_size); +} + +void generate_ext4_image(struct image_data *image) +{ + int fd; + struct stat st; + + fd = fileno(tmpfile()); + info.len = image->partition_size; + make_ext4fs_internal(fd, NULL, NULL, 0, 0, 1, 0, 0, 0); + + fstat(fd, &st); + image->image_size = st.st_size; + + image->buffer = mmap(NULL, image->image_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (image->buffer == MAP_FAILED) { + perror("mmap"); + image->buffer = NULL; + } + + close(fd); +} + +int fb_format(Action *a, usb_handle *usb) +{ + const char *partition = a->cmd; + char query[256]; + char response[FB_RESPONSE_SZ+1]; + int status = 0; + struct image_data image; + struct generator *generator = NULL; + int fd; + unsigned i; + char cmd[CMD_SIZE]; + + response[FB_RESPONSE_SZ] = '\0'; + snprintf(query, sizeof(query), "getvar:partition-type:%s", partition); + status = fb_command_response(usb, query, response); + if (status) { + fprintf(stderr,"FAILED (%s)\n", fb_get_error()); + return status; + } + + for (i = 0; i < ARRAY_SIZE(generators); i++) { + if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) { + generator = &generators[i]; + break; + } + } + if (!generator) { + fprintf(stderr,"Formatting is not supported for filesystem with type '%s'.\n", + response); + return -1; + } + + response[FB_RESPONSE_SZ] = '\0'; + snprintf(query, sizeof(query), "getvar:partition-size:%s", partition); + status = fb_command_response(usb, query, response); + if (status) { + fprintf(stderr,"FAILED (%s)\n", fb_get_error()); + return status; + } + image.partition_size = strtoll(response, (char **)NULL, 16); + + generator->generate(&image); + if (!image.buffer) { + fprintf(stderr,"Cannot generate image.\n"); + return -1; + } + + // Following piece of code is similar to fb_queue_flash() but executes + // actions directly without queuing + fprintf(stderr, "sending '%s' (%lli KB)...\n", partition, image.image_size/1024); + status = fb_download_data(usb, image.buffer, image.image_size); + if (status) goto cleanup; + + fprintf(stderr, "writing '%s'...\n", partition); + snprintf(cmd, CMD_SIZE, "flash:%s", partition); + status = fb_command(usb, cmd); + if (status) goto cleanup; + +cleanup: + generator->cleanup(&image); + + return status; +} + +void fb_queue_format(const char *partition) +{ + Action *a; + + a = queue_action(OP_FORMAT, partition); + a->msg = mkmsg("formatting '%s' partition", partition); +} + void fb_queue_flash(const char *ptn, void *data, unsigned sz) { Action *a; @@ -340,6 +481,10 @@ int fb_execute_queue(usb_handle *usb) if (status) break; } else if (a->op == OP_NOTICE) { fprintf(stderr,"%s\n",(char*)a->data); + } else if (a->op == OP_FORMAT) { + status = fb_format(a, usb); + status = a->func(a, status, status ? fb_get_error() : ""); + if (status) break; } else { die("bogus action"); } diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index a069d339d..fad70d48e 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -222,6 +222,7 @@ void usage(void) " flashall flash boot + recovery + system\n" " flash [ ] write a file to a flash partition\n" " erase erase a flash partition\n" + " format format a flash partition \n" " getvar display a bootloader variable\n" " boot [ ] download and boot kernel\n" " flash:raw boot [ ] create bootimage and flash it\n" @@ -633,6 +634,10 @@ int main(int argc, char **argv) require(2); fb_queue_erase(argv[1]); skip(2); + } else if(!strcmp(*argv, "format")) { + require(2); + fb_queue_format(argv[1]); + skip(2); } else if(!strcmp(*argv, "signature")) { require(2); data = load_file(argv[1], &sz); diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index 9e043fe6f..c2f97a210 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -43,6 +43,7 @@ char *fb_get_error(void); /* engine.c - high level command queue engine */ void fb_queue_flash(const char *ptn, void *data, unsigned sz);; void fb_queue_erase(const char *ptn); +void fb_queue_format(const char *ptn); void fb_queue_require(const char *prod, const char *var, int invert, unsigned nvalues, const char **value); void fb_queue_display(const char *var, const char *prettyname); From 714052ba4d86a2d411a3944f5034c5a5833ffbb6 Mon Sep 17 00:00:00 2001 From: "Mike J. Chen" Date: Sat, 4 Feb 2012 16:22:22 -0800 Subject: [PATCH 4/4] DO NOT MERGE fastboot: Change -w to format after the erase of userdata & cache If the bootloader doesn't support formatting of those partitions (either because it doesn't support the getvar commands needed or the partition type is not supported), the errors are printed but doesn't halt processing of subsequent commands. Change-Id: I816ac2e5e7593846fcb4fd39c793a8dbdd996f6f Signed-off-by: Mike J. Chen --- fastboot/engine.c | 29 ++++++++++++++++++++++++++--- fastboot/fastboot.c | 4 +++- fastboot/fastboot.h | 2 +- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/fastboot/engine.c b/fastboot/engine.c index e40db2bad..345577d38 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -187,6 +187,8 @@ void generate_ext4_image(struct image_data *image) struct stat st; fd = fileno(tmpfile()); + /* reset ext4fs info so we can be called multiple times */ + reset_ext4fs_info(); info.len = image->partition_size; make_ext4fs_internal(fd, NULL, NULL, 0, 0, 1, 0, 0, 0); @@ -202,7 +204,7 @@ void generate_ext4_image(struct image_data *image) close(fd); } -int fb_format(Action *a, usb_handle *usb) +int fb_format(Action *a, usb_handle *usb, int skip_if_not_supported) { const char *partition = a->cmd; char query[256]; @@ -218,6 +220,13 @@ int fb_format(Action *a, usb_handle *usb) snprintf(query, sizeof(query), "getvar:partition-type:%s", partition); status = fb_command_response(usb, query, response); if (status) { + if (skip_if_not_supported) { + fprintf(stderr, + "Erase successful, but not automatically formatting.\n"); + fprintf(stderr, + "Can't determine partition type.\n"); + return 0; + } fprintf(stderr,"FAILED (%s)\n", fb_get_error()); return status; } @@ -229,6 +238,13 @@ int fb_format(Action *a, usb_handle *usb) } } if (!generator) { + if (skip_if_not_supported) { + fprintf(stderr, + "Erase successful, but not automatically formatting.\n"); + fprintf(stderr, + "File system type %s not supported.\n", response); + return 0; + } fprintf(stderr,"Formatting is not supported for filesystem with type '%s'.\n", response); return -1; @@ -238,6 +254,12 @@ int fb_format(Action *a, usb_handle *usb) snprintf(query, sizeof(query), "getvar:partition-size:%s", partition); status = fb_command_response(usb, query, response); if (status) { + if (skip_if_not_supported) { + fprintf(stderr, + "Erase successful, but not automatically formatting.\n"); + fprintf(stderr, "Unable to get partition size\n."); + return 0; + } fprintf(stderr,"FAILED (%s)\n", fb_get_error()); return status; } @@ -266,11 +288,12 @@ cleanup: return status; } -void fb_queue_format(const char *partition) +void fb_queue_format(const char *partition, int skip_if_not_supported) { Action *a; a = queue_action(OP_FORMAT, partition); + a->data = (void*)skip_if_not_supported; a->msg = mkmsg("formatting '%s' partition", partition); } @@ -482,7 +505,7 @@ int fb_execute_queue(usb_handle *usb) } else if (a->op == OP_NOTICE) { fprintf(stderr,"%s\n",(char*)a->data); } else if (a->op == OP_FORMAT) { - status = fb_format(a, usb); + status = fb_format(a, usb, (int)a->data); status = a->func(a, status, status ? fb_get_error() : ""); if (status) break; } else { diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index fad70d48e..805014fd6 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -636,7 +636,7 @@ int main(int argc, char **argv) skip(2); } else if(!strcmp(*argv, "format")) { require(2); - fb_queue_format(argv[1]); + fb_queue_format(argv[1], 0); skip(2); } else if(!strcmp(*argv, "signature")) { require(2); @@ -723,7 +723,9 @@ int main(int argc, char **argv) if (wants_wipe) { fb_queue_erase("userdata"); + fb_queue_format("userdata", 1); fb_queue_erase("cache"); + fb_queue_format("cache", 1); } if (wants_reboot) { fb_queue_reboot(); diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index c2f97a210..b77cb19bf 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -43,7 +43,7 @@ char *fb_get_error(void); /* engine.c - high level command queue engine */ void fb_queue_flash(const char *ptn, void *data, unsigned sz);; void fb_queue_erase(const char *ptn); -void fb_queue_format(const char *ptn); +void fb_queue_format(const char *ptn, int skip_if_not_supported); void fb_queue_require(const char *prod, const char *var, int invert, unsigned nvalues, const char **value); void fb_queue_display(const char *var, const char *prettyname);