summaryrefslogtreecommitdiffstats
path: root/fastboot/engine.c
diff options
context:
space:
mode:
authorAnatol Pomazau <anatol@google.com>2011-12-15 17:50:18 -0800
committerAnatol Pomazau <anatol@google.com>2012-02-03 16:07:18 -0800
commit230d160a718333651f7ca1557404f96682795b37 (patch)
tree6b5a97bfc436c49f1e99a5cf64be85bd78fcfe50 /fastboot/engine.c
parent7806e5648557532fcfb971bd2fd88f4033f32cb8 (diff)
downloadsystem_core-230d160a718333651f7ca1557404f96682795b37.zip
system_core-230d160a718333651f7ca1557404f96682795b37.tar.gz
system_core-230d160a718333651f7ca1557404f96682795b37.tar.bz2
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/engine.c')
-rw-r--r--fastboot/engine.c196
1 files changed, 194 insertions, 2 deletions
diff --git a/fastboot/engine.c b/fastboot/engine.c
index 6d94035..34bf521 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");
}