Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c8ba5366 authored by Anatol Pomazau's avatar Anatol Pomazau
Browse files

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: I8529bc3485739487f0d91312f7c0ab1a6e5d8b44
parent fc656103
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -16,7 +16,8 @@ LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg
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

@@ -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))
+210 −2
Original line number Diff line number Diff line
@@ -26,13 +26,29 @@
 * 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>

#include "fastboot.h"
#ifdef USE_MINGW
#include <fcntl.h>
#else
#include <sys/mman.h>
#endif

extern struct fs_info info;

#define ARRAY_SIZE(x)           (sizeof(x)/sizeof(x[0]))

double now()
{
@@ -60,15 +76,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 +101,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 +181,162 @@ 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;
    }

    lseek(fd, 0, SEEK_SET);
    while(count < size) {
        ssize_t actually_read = read(fd, (char*)buffer+count, size-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;

#ifdef USE_MINGW
    /* Ideally we should use tmpfile() here, the same as with unix version.
     * But unfortunately it is not portable as it is not clear whether this
     * function opens file in TEXT or BINARY mode.
     *
     * There are also some reports it is buggy:
     *    http://pdplab.it.uom.gr/teaching/gcc_manuals/gnulib.html#tmpfile
     *    http://www.mega-nerd.com/erikd/Blog/Windiots/tmpfile.html
     */
    char *filename = tempnam(getenv("TEMP"), "fastboot-format.img");
    fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644);
    unlink(filename);
#else
    fd = fileno(tmpfile());
#endif
    reset_ext4fs_info();
    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 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(cmd, sizeof(cmd), "getvar:partition-type:%s", partition);
    status = fb_command_response(usb, cmd, 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(cmd, sizeof(cmd), "getvar:partition-size:%s", partition);
    status = fb_command_response(usb, cmd, 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, sizeof(cmd), "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 +544,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");
        }
+5 −0
Original line number Diff line number Diff line
@@ -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);
+1 −0
Original line number Diff line number Diff line
@@ -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);