diff options
author | Anatol Pomazau <anatol@google.com> | 2011-12-15 17:50:18 -0800 |
---|---|---|
committer | Anatol Pomazau <anatol@google.com> | 2012-02-03 16:07:18 -0800 |
commit | 230d160a718333651f7ca1557404f96682795b37 (patch) | |
tree | 6b5a97bfc436c49f1e99a5cf64be85bd78fcfe50 /fastboot | |
parent | 7806e5648557532fcfb971bd2fd88f4033f32cb8 (diff) | |
download | core-230d160a718333651f7ca1557404f96682795b37.tar.gz core-230d160a718333651f7ca1557404f96682795b37.tar.bz2 core-230d160a718333651f7ca1557404f96682795b37.zip |
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: I8529bc1dc64237f1f0d91312f7c0ab1a6e5d8b44
Diffstat (limited to 'fastboot')
-rw-r--r-- | fastboot/Android.mk | 7 | ||||
-rw-r--r-- | fastboot/engine.c | 196 | ||||
-rw-r--r-- | fastboot/fastboot.c | 5 | ||||
-rw-r--r-- | fastboot/fastboot.h | 1 |
4 files changed, 204 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..34bf521dd 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -26,13 +26,28 @@ * SUCH DAMAGE. */ +#include "fastboot.h" +#include "make_ext4fs.h" +#include "ext4_utils.h" + #include <stdio.h> #include <stdlib.h> #include <stdarg.h> +#include <stdbool.h> #include <string.h> +#include <sys/stat.h> #include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#ifndef USE_MINGW +#include <sys/mman.h> +#endif -#include "fastboot.h" + +extern struct fs_info info; + +#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) double now() { @@ -60,15 +75,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 +100,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 cleanup_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, cleanup_image } +}; + static int cb_default(Action *a, int status, char *resp) { if (status) { @@ -133,6 +180,147 @@ void fb_queue_erase(const char *ptn) a->msg = mkmsg("erasing '%s'", ptn); } +/* Loads file content into buffer. Returns NULL on error. */ +static void *load_buffer(int fd, off_t size) +{ + void *buffer; + +#ifdef USE_MINGW + ssize_t count = 0; + + // mmap is more efficient but mingw does not support it. + // In this case we read whole image into memory buffer. + buffer = malloc(size); + if (!buffer) { + perror("malloc"); + return NULL; + } + + while(count < size) { + ssize_t actually_read = pread(fd, (char*)buffer+count, size-count, count); + + if (actually_read == 0) { + break; + } + if (actually_read < 0) { + if (errno == EINTR) { + continue; + } + perror("read"); + free(buffer); + return NULL; + } + + count += actually_read; + } +#else + buffer = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (buffer == MAP_FAILED) { + perror("mmap"); + return NULL; + } +#endif + + return buffer; +} + +void cleanup_image(struct image_data *image) +{ +#ifdef USE_MINGW + free(image->buffer); +#else + munmap(image->buffer, image->image_size); +#endif +} + +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 = load_buffer(fd, st.st_size); + + 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 +528,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 95be0acf2..2d28d59a1 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -225,6 +225,7 @@ void usage(void) " flashall flash boot + recovery + system\n" " flash <partition> [ <filename> ] write a file to a flash partition\n" " erase <partition> erase a flash partition\n" + " format <partition> format a flash partition \n" " getvar <variable> display a bootloader variable\n" " boot <kernel> [ <ramdisk> ] download and boot kernel\n" " flash:raw boot <kernel> [ <ramdisk> ] create bootimage and flash it\n" @@ -636,6 +637,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); |