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

Commit f694ba5f authored by Colin Cross's avatar Colin Cross Committed by android code review
Browse files

Merge changes...

Merge changes I6c59381c,I0da088fc,I883572f5,Idc2cfe20,I1a156d10,I177abdb9,I18707bd9,I26744c74,I266f70e1,I75f10db2,I95aa2317,I2f21355b,I5d4ab07f,Ic138ad58,I012d8a42,If664e4fc

* changes:
  fastboot: add support for auto-resparsing large files
  fastboot: add fb_getvar
  fastboot: use getopt_long
  libsparse: rename symbols that conflict with libext4_utils
  libsparse: add sparse_file_len
  libsparse: add function to resparse a file and a utility to use it
  libsparse: add callback output file type
  libsparse: pseudo-subclass output_file for normal and gz files
  libsparse: add sparse_file read and convert tools to use it
  libsparse: fix windows image writing
  libsparse: merge adjacent blocks of the same type
  libsparse: add error reporting functions
  libsparse: add support for including fds
  libsparse: cleanups
  libsparse: remove static variables
  system/core: move libsparse into system/core
parents b3a67938 f838788e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ ifeq ($(HOST_OS),windows)
  LOCAL_C_INCLUDES += development/host/windows/usb/api
endif

LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz libext4_utils libz
LOCAL_STATIC_LIBRARIES := $(EXTRA_STATIC_LIBS) libzipfile libunz libext4_utils libsparse libz

ifneq ($(HOST_OS),windows)
ifeq ($(HAVE_SELINUX), true)
+34 −7
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@

#include "fastboot.h"
#include "make_ext4fs.h"
#include "ext4_utils.h"

#include <stdio.h>
#include <stdlib.h>
@@ -77,6 +76,7 @@ char *mkmsg(const char *fmt, ...)
#define OP_QUERY      3
#define OP_NOTICE     4
#define OP_FORMAT     5
#define OP_DOWNLOAD_SPARSE 6

typedef struct Action Action;

@@ -111,6 +111,20 @@ struct image_data {
void generate_ext4_image(struct image_data *image);
void cleanup_image(struct image_data *image);

int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...)
{
    char cmd[CMD_SIZE] = "getvar:";
    int getvar_len = strlen(cmd);
    va_list args;

    response[FB_RESPONSE_SZ] = '\0';
    va_start(args, fmt);
    vsnprintf(cmd + getvar_len, sizeof(cmd) - getvar_len, fmt, args);
    va_end(args);
    cmd[CMD_SIZE - 1] = '\0';
    return fb_command_response(usb, cmd, response);
}

struct generator {
    char *fs_type;

@@ -278,9 +292,7 @@ int fb_format(Action *a, usb_handle *usb, int skip_if_not_supported)
    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);
    status = fb_getvar(usb, response, "partition-type:%s", partition);
    if (status) {
        if (skip_if_not_supported) {
            fprintf(stderr,
@@ -312,9 +324,7 @@ int fb_format(Action *a, usb_handle *usb, int skip_if_not_supported)
        return -1;
    }

    response[FB_RESPONSE_SZ] = '\0';
    snprintf(cmd, sizeof(cmd), "getvar:partition-size:%s", partition);
    status = fb_command_response(usb, cmd, response);
    status = fb_getvar(usb, response, "partition-size:%s", partition);
    if (status) {
        if (skip_if_not_supported) {
            fprintf(stderr,
@@ -372,6 +382,19 @@ void fb_queue_flash(const char *ptn, void *data, unsigned sz)
    a->msg = mkmsg("writing '%s'", ptn);
}

void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz)
{
    Action *a;

    a = queue_action(OP_DOWNLOAD_SPARSE, "");
    a->data = s;
    a->size = 0;
    a->msg = mkmsg("sending sparse '%s' (%d KB)", ptn, sz / 1024);

    a = queue_action(OP_COMMAND, "flash:%s", ptn);
    a->msg = mkmsg("writing '%s'", ptn);
}

static int match(char *str, const char **value, unsigned count)
{
    const char *val;
@@ -570,6 +593,10 @@ int fb_execute_queue(usb_handle *usb)
            status = fb_format(a, usb, (int)a->data);
            status = a->func(a, status, status ? fb_get_error() : "");
            if (status) break;
        } else if (a->op == OP_DOWNLOAD_SPARSE) {
            status = fb_download_data_sparse(usb, a->data);
            status = a->func(a, status, status ? fb_get_error() : "");
            if (status) break;
        } else {
            die("bogus action");
        }
+252 −49
Original line number Diff line number Diff line
@@ -26,22 +26,36 @@
 * SUCH DAMAGE.
 */

#define _LARGEFILE64_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <ctype.h>
#include <getopt.h>

#include <sys/time.h>
#include <sys/types.h>

#include <bootimg.h>
#include <sparse/sparse.h>
#include <zipfile/zipfile.h>

#include "fastboot.h"

#ifndef O_BINARY
#define O_BINARY 0
#endif

#define DEFAULT_SPARSE_LIMIT (256 * 1024 * 1024)

char cur_product[FB_RESPONSE_SZ + 1];

void bootimg_set_cmdline(boot_img_hdr *h, const char *cmdline);
@@ -58,6 +72,8 @@ static const char *product = 0;
static const char *cmdline = 0;
static int wipe_data = 0;
static unsigned short vendor_id = 0;
static int64_t sparse_limit = -1;
static int64_t target_sparse_limit = -1;

static unsigned base_addr = 0x10000000;

@@ -116,7 +132,27 @@ char *find_item(const char *item, const char *product)

#ifdef _WIN32
void *load_file(const char *fn, unsigned *_sz);
int64_t file_size(const char *fn);
#else
#if defined(__APPLE__) && defined(__MACH__)
#define lseek64 lseek
#define off64_t off_t
#endif

int64_t file_size(const char *fn)
{
    off64_t off;
    int fd;

    fd = open(fn, O_RDONLY);
    if (fd < 0) return -1;

    off = lseek64(fd, 0, SEEK_END);
    close(fd);

    return off;
}

void *load_file(const char *fn, unsigned *_sz)
{
    char *data;
@@ -244,6 +280,8 @@ void usage(void)
            "  -i <vendor id>                           specify a custom USB vendor id\n"
            "  -b <base_addr>                           specify a custom kernel base address\n"
            "  -n <page size>                           specify the nand page size. default: 2048\n"
            "  -S <size>[K|M|G]                         automatically sparse files greater than\n"
            "                                           size. default: 256M, 0 to disable\n"
        );
}

@@ -429,6 +467,110 @@ void queue_info_dump(void)
    fb_queue_notice("--------------------------------------------");
}


struct sparse_file **load_sparse_files(const char *fname, int max_size)
{
    int fd;
    struct sparse_file *s;
    int files;
    struct sparse_file **out_s;

    fd = open(fname, O_RDONLY | O_BINARY);
    if (fd < 0) {
        die("cannot open '%s'\n", fname);
    }

    s = sparse_file_import_auto(fd, false);
    if (!s) {
        die("cannot sparse read file '%s'\n", fname);
    }

    files = sparse_file_resparse(s, max_size, NULL, 0);
    if (files < 0) {
        die("Failed to resparse '%s'\n", fname);
    }

    out_s = calloc(sizeof(struct sparse_file *), files + 1);
    if (!out_s) {
        die("Failed to allocate sparse file array\n");
    }

    files = sparse_file_resparse(s, max_size, out_s, files);
    if (files < 0) {
        die("Failed to resparse '%s'\n", fname);
    }

    return out_s;
}

static int64_t get_target_sparse_limit(struct usb_handle *usb)
{
    int64_t limit = 0;
    char response[FB_RESPONSE_SZ + 1];
    int status = fb_getvar(usb, response, "max-download-size");

    if (!status) {
        limit = strtoul(response, NULL, 0);
        if (limit > 0) {
            fprintf(stderr, "target reported max download size of %lld bytes\n",
                    limit);
        }
    }

    return limit;
}

static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size)
{
    int64_t limit;

    if (sparse_limit == 0) {
        return 0;
    } else if (sparse_limit > 0) {
        limit = sparse_limit;
    } else {
        if (target_sparse_limit == -1) {
            target_sparse_limit = get_target_sparse_limit(usb);
        }
        if (target_sparse_limit > 0) {
            limit = target_sparse_limit;
        } else {
            limit = DEFAULT_SPARSE_LIMIT;
        }
    }

    if (size > limit) {
        return limit;
    }

    return 0;
}

void do_flash(usb_handle *usb, const char *pname, const char *fname)
{
    int64_t sz64;
    void *data;
    int64_t limit;

    sz64 = file_size(fname);
    limit = get_sparse_limit(usb, sz64);
    if (limit) {
        struct sparse_file **s = load_sparse_files(fname, limit);
        if (s == NULL) {
            die("cannot sparse load '%s'\n", fname);
        }
        while (*s) {
            sz64 = sparse_file_len(*s, true, false);
            fb_queue_flash_sparse(pname, *s++, sz64);
        }
    } else {
        unsigned int sz;
        data = load_file(fname, &sz);
        if (data == 0) die("cannot load '%s'\n", fname);
        fb_queue_flash(pname, data, sz);
    }
}

void do_update_signature(zipfile_t zip, char *fn)
{
    void *data;
@@ -566,6 +708,47 @@ int do_oem_command(int argc, char **argv)
    return 0;
}

static int64_t parse_num(const char *arg)
{
    char *endptr;
    unsigned long long num;

    num = strtoull(arg, &endptr, 0);
    if (endptr == arg) {
        return -1;
    }

    if (*endptr == 'k' || *endptr == 'K') {
        if (num >= (-1ULL) / 1024) {
            return -1;
        }
        num *= 1024LL;
        endptr++;
    } else if (*endptr == 'm' || *endptr == 'M') {
        if (num >= (-1ULL) / (1024 * 1024)) {
            return -1;
        }
        num *= 1024LL * 1024LL;
        endptr++;
    } else if (*endptr == 'g' || *endptr == 'G') {
        if (num >= (-1ULL) / (1024 * 1024 * 1024)) {
            return -1;
        }
        num *= 1024LL * 1024LL * 1024LL;
        endptr++;
    }

    if (*endptr != '\0') {
        return -1;
    }

    if (num > INT64_MAX) {
        return -1;
    }

    return num;
}

int main(int argc, char **argv)
{
    int wants_wipe = 0;
@@ -575,62 +758,83 @@ int main(int argc, char **argv)
    unsigned sz;
    unsigned page_size = 2048;
    int status;
    int c;
    int r;

    skip(1);
    if (argc == 0) {
        usage();
        return 1;
    }
    const struct option longopts = { 0, 0, 0, 0 };

    if (!strcmp(*argv, "devices")) {
        list_devices();
        return 0;
    }
    serial = getenv("ANDROID_SERIAL");

    if (!strcmp(*argv, "help")) {
        usage();
        return 0;
    while (1) {
        c = getopt_long(argc, argv, "wb:n:s:S:p:c:i:m:h", &longopts, NULL);
        if (c < 0) {
            break;
        }


    serial = getenv("ANDROID_SERIAL");

    while (argc > 0) {
        if(!strcmp(*argv, "-w")) {
        switch (c) {
        case 'w':
            wants_wipe = 1;
            skip(1);
        } else if(!strcmp(*argv, "-b")) {
            require(2);
            base_addr = strtoul(argv[1], 0, 16);
            skip(2);
        } else if(!strcmp(*argv, "-n")) {
            require(2);
            page_size = (unsigned)strtoul(argv[1], NULL, 0);
            break;
        case 'b':
            base_addr = strtoul(optarg, 0, 16);
            break;
        case 'n':
            page_size = (unsigned)strtoul(optarg, NULL, 0);
            if (!page_size) die("invalid page size");
            skip(2);
        } else if(!strcmp(*argv, "-s")) {
            require(2);
            serial = argv[1];
            skip(2);
        } else if(!strcmp(*argv, "-p")) {
            require(2);
            product = argv[1];
            skip(2);
        } else if(!strcmp(*argv, "-c")) {
            require(2);
            cmdline = argv[1];
            skip(2);
        } else if(!strcmp(*argv, "-i")) {
            break;
        case 's':
            serial = optarg;
            break;
        case 'S':
            sparse_limit = parse_num(optarg);
            if (sparse_limit < 0) {
                    die("invalid sparse limit");
            }
            break;
        case 'p':
            product = optarg;
            break;
        case 'c':
            cmdline = optarg;
            break;
        case 'i': {
                char *endptr = NULL;
                unsigned long val;

            require(2);
            val = strtoul(argv[1], &endptr, 0);
                val = strtoul(optarg, &endptr, 0);
                if (!endptr || *endptr != '\0' || (val & ~0xffff))
                die("invalid vendor id '%s'", argv[1]);
                    die("invalid vendor id '%s'", optarg);
                vendor_id = (unsigned short)val;
            skip(2);
        } else if(!strcmp(*argv, "getvar")) {
                break;
            }
        case 'h':
            usage();
            return 1;
        case '?':
            return 1;
        default:
            abort();
        }
    }

    argc -= optind;
    argv += optind;

    if (argc == 0 && !wants_wipe) {
        usage();
        return 1;
    }

    if (!strcmp(*argv, "devices")) {
        skip(1);
        list_devices();
        return 0;
    }

    usb = open_device();

    while (argc > 0) {
        if(!strcmp(*argv, "getvar")) {
            require(2);
            fb_queue_display(argv[1], argv[1]);
            skip(2);
@@ -687,9 +891,7 @@ int main(int argc, char **argv)
                skip(2);
            }
            if (fname == 0) die("cannot determine image filename for '%s'", pname);
            data = load_file(fname, &sz);
            if (data == 0) die("cannot load '%s'\n", fname);
            fb_queue_flash(pname, data, sz);
            do_flash(usb, pname, fname);
        } else if(!strcmp(*argv, "flash:raw")) {
            char *pname = argv[1];
            char *kname = argv[2];
@@ -719,6 +921,9 @@ int main(int argc, char **argv)
            wants_reboot = 1;
        } else if(!strcmp(*argv, "oem")) {
            argc = do_oem_command(argc, argv);
        } else if (!strcmp(*argv, "help")) {
            usage();
            return 0;
        } else {
            usage();
            return 1;
@@ -737,8 +942,6 @@ int main(int argc, char **argv)
        fb_queue_command("reboot-bootloader", "rebooting into bootloader");
    }

    usb = open_device();

    status = fb_execute_queue(usb);
    return (status) ? 1 : 0;
}
+5 −0
Original line number Diff line number Diff line
@@ -31,17 +31,22 @@

#include "usb.h"

struct sparse_file;

/* protocol.c - fastboot protocol */
int fb_command(usb_handle *usb, const char *cmd);
int fb_command_response(usb_handle *usb, const char *cmd, char *response);
int fb_download_data(usb_handle *usb, const void *data, unsigned size);
int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s);
char *fb_get_error(void);

#define FB_COMMAND_SZ 64
#define FB_RESPONSE_SZ 64

/* engine.c - high level command queue engine */
int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...);
void fb_queue_flash(const char *ptn, void *data, unsigned sz);;
void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz);
void fb_queue_erase(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,
+160 −27
Original line number Diff line number Diff line
@@ -26,11 +26,18 @@
 * SUCH DAMAGE.
 */

#define min(a, b) \
    ({ typeof(a) _a = (a); typeof(b) _b = (b); (_a < _b) ? _a : _b; })
#define round_down(a, b) \
    ({ typeof(a) _a = (a); typeof(b) _b = (b); _a - (_a % _b); })

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <sparse/sparse.h>

#include "fastboot.h"

static char ERROR[128];
@@ -40,8 +47,7 @@ char *fb_get_error(void)
    return ERROR;
}

static int check_response(usb_handle *usb, unsigned size,
                          unsigned data_okay, char *response)
static int check_response(usb_handle *usb, unsigned int size, char *response)
{
    unsigned char status[65];
    int r;
@@ -82,7 +88,7 @@ static int check_response(usb_handle *usb, unsigned size,
            return -1;
        }

        if(!memcmp(status, "DATA", 4) && data_okay){
        if(!memcmp(status, "DATA", 4) && size > 0){
            unsigned dsize = strtoul((char*) status + 4, 0, 16);
            if(dsize > size) {
                strcpy(ERROR, "data size too large");
@@ -100,8 +106,7 @@ static int check_response(usb_handle *usb, unsigned size,
    return -1;
}

static int _command_send(usb_handle *usb, const char *cmd,
                         const void *data, unsigned size,
static int _command_start(usb_handle *usb, const char *cmd, unsigned size,
                          char *response)
{
    int cmdsize = strlen(cmd);
@@ -122,17 +127,13 @@ static int _command_send(usb_handle *usb, const char *cmd,
        return -1;
    }

    if(data == 0) {
        return check_response(usb, size, 0, response);
    return check_response(usb, size, response);
}

    r = check_response(usb, size, 1, 0);
    if(r < 0) {
        return -1;
    }
    size = r;
static int _command_data(usb_handle *usb, const void *data, unsigned size)
{
    int r;

    if(size) {
    r = usb_write(usb, data, size);
    if(r < 0) {
        sprintf(ERROR, "data transfer failure (%s)", strerror(errno));
@@ -144,24 +145,63 @@ static int _command_send(usb_handle *usb, const char *cmd,
        usb_close(usb);
        return -1;
    }

    return r;
}

    r = check_response(usb, 0, 0, 0);
static int _command_end(usb_handle *usb)
{
    int r;
    r = check_response(usb, 0, 0);
    if(r < 0) {
        return -1;
    } else {
    }
    return 0;
}

static int _command_send(usb_handle *usb, const char *cmd,
                         const void *data, unsigned size,
                         char *response)
{
    int r;
    if (size == 0) {
        return -1;
    }

    r = _command_start(usb, cmd, size, response);
    if (r < 0) {
        return -1;
    }

    r = _command_data(usb, data, size);
    if (r < 0) {
        return -1;
    }

    r = _command_end(usb);
    if(r < 0) {
        return -1;
    }

    return size;
}

static int _command_send_no_data(usb_handle *usb, const char *cmd,
                                 char *response)
{
    int r;

    return _command_start(usb, cmd, 0, response);
}

int fb_command(usb_handle *usb, const char *cmd)
{
    return _command_send(usb, cmd, 0, 0, 0);
    return _command_send_no_data(usb, cmd, 0);
}

int fb_command_response(usb_handle *usb, const char *cmd, char *response)
{
    return _command_send(usb, cmd, 0, 0, response);
    return _command_send_no_data(usb, cmd, response);
}

int fb_download_data(usb_handle *usb, const void *data, unsigned size)
@@ -179,3 +219,96 @@ int fb_download_data(usb_handle *usb, const void *data, unsigned size)
    }
}

#define USB_BUF_SIZE 512
static char usb_buf[USB_BUF_SIZE];
static int usb_buf_len;

static int fb_download_data_sparse_write(void *priv, const void *data, int len)
{
    int r;
    usb_handle *usb = priv;
    int to_write;
    const char *ptr = data;

    if (usb_buf_len) {
        to_write = min(USB_BUF_SIZE - usb_buf_len, len);

        memcpy(usb_buf + usb_buf_len, ptr, to_write);
        usb_buf_len += to_write;
        ptr += to_write;
        len -= to_write;
    }

    if (usb_buf_len == USB_BUF_SIZE) {
        r = _command_data(usb, usb_buf, USB_BUF_SIZE);
        if (r != USB_BUF_SIZE) {
            return -1;
        }
        usb_buf_len = 0;
    }

    if (len > USB_BUF_SIZE) {
        if (usb_buf_len > 0) {
            sprintf(ERROR, "internal error: usb_buf not empty\n");
            return -1;
        }
        to_write = round_down(len, USB_BUF_SIZE);
        r = _command_data(usb, ptr, to_write);
        if (r != to_write) {
            return -1;
        }
        ptr += to_write;
        len -= to_write;
    }

    if (len > 0) {
        if (len > USB_BUF_SIZE) {
            sprintf(ERROR, "internal error: too much left for usb_buf\n");
            return -1;
        }
        memcpy(usb_buf, ptr, len);
        usb_buf_len = len;
    }

    return 0;
}

static int fb_download_data_sparse_flush(usb_handle *usb)
{
    int r;

    if (usb_buf_len > 0) {
        r = _command_data(usb, usb_buf, usb_buf_len);
        if (r != usb_buf_len) {
            return -1;
        }
        usb_buf_len = 0;
    }

    return 0;
}

int fb_download_data_sparse(usb_handle *usb, struct sparse_file *s)
{
    char cmd[64];
    int r;
    int size = sparse_file_len(s, true, false);
    if (size <= 0) {
        return -1;
    }

    sprintf(cmd, "download:%08x", size);
    r = _command_start(usb, cmd, size, 0);
    if (r < 0) {
        return -1;
    }

    r = sparse_file_callback(s, true, false, fb_download_data_sparse_write, usb);
    if (r < 0) {
        return -1;
    }

    fb_download_data_sparse_flush(usb);

    return _command_end(usb);
}
Loading