Home | History | Annotate | Download | only in fastboot
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *  * Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  *  * Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in
     12  *    the documentation and/or other materials provided with the
     13  *    distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
     18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
     19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
     22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include "fastboot.h"
     30 #include "make_ext4fs.h"
     31 #include "ext4_utils.h"
     32 
     33 #include <stdio.h>
     34 #include <stdlib.h>
     35 #include <stdarg.h>
     36 #include <stdbool.h>
     37 #include <string.h>
     38 #include <sys/stat.h>
     39 #include <sys/time.h>
     40 #include <sys/types.h>
     41 #include <unistd.h>
     42 
     43 #ifdef USE_MINGW
     44 #include <fcntl.h>
     45 #else
     46 #include <sys/mman.h>
     47 #endif
     48 
     49 extern struct fs_info info;
     50 
     51 #define ARRAY_SIZE(x)           (sizeof(x)/sizeof(x[0]))
     52 
     53 double now()
     54 {
     55     struct timeval tv;
     56     gettimeofday(&tv, NULL);
     57     return (double)tv.tv_sec + (double)tv.tv_usec / 1000000;
     58 }
     59 
     60 char *mkmsg(const char *fmt, ...)
     61 {
     62     char buf[256];
     63     char *s;
     64     va_list ap;
     65 
     66     va_start(ap, fmt);
     67     vsprintf(buf, fmt, ap);
     68     va_end(ap);
     69 
     70     s = strdup(buf);
     71     if (s == 0) die("out of memory");
     72     return s;
     73 }
     74 
     75 #define OP_DOWNLOAD   1
     76 #define OP_COMMAND    2
     77 #define OP_QUERY      3
     78 #define OP_NOTICE     4
     79 #define OP_FORMAT     5
     80 
     81 typedef struct Action Action;
     82 
     83 #define CMD_SIZE 64
     84 
     85 struct Action
     86 {
     87     unsigned op;
     88     Action *next;
     89 
     90     char cmd[CMD_SIZE];
     91     const char *prod;
     92     void *data;
     93     unsigned size;
     94 
     95     const char *msg;
     96     int (*func)(Action *a, int status, char *resp);
     97 
     98     double start;
     99 };
    100 
    101 static Action *action_list = 0;
    102 static Action *action_last = 0;
    103 
    104 
    105 struct image_data {
    106     long long partition_size;
    107     long long image_size; // real size of image file
    108     void *buffer;
    109 };
    110 
    111 void generate_ext4_image(struct image_data *image);
    112 void cleanup_image(struct image_data *image);
    113 
    114 struct generator {
    115     char *fs_type;
    116 
    117     /* generate image and return it as image->buffer.
    118      * size of the buffer returned as image->image_size.
    119      *
    120      * image->partition_size specifies what is the size of the
    121      * file partition we generate image for.
    122      */
    123     void (*generate)(struct image_data *image);
    124 
    125     /* it cleans the buffer allocated during image creation.
    126      * this function probably does free() or munmap().
    127      */
    128     void (*cleanup)(struct image_data *image);
    129 } generators[] = {
    130     { "ext4", generate_ext4_image, cleanup_image }
    131 };
    132 
    133 static int cb_default(Action *a, int status, char *resp)
    134 {
    135     if (status) {
    136         fprintf(stderr,"FAILED (%s)\n", resp);
    137     } else {
    138         double split = now();
    139         fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
    140         a->start = split;
    141     }
    142     return status;
    143 }
    144 
    145 static Action *queue_action(unsigned op, const char *fmt, ...)
    146 {
    147     Action *a;
    148     va_list ap;
    149     size_t cmdsize;
    150 
    151     a = calloc(1, sizeof(Action));
    152     if (a == 0) die("out of memory");
    153 
    154     va_start(ap, fmt);
    155     cmdsize = vsnprintf(a->cmd, sizeof(a->cmd), fmt, ap);
    156     va_end(ap);
    157 
    158     if (cmdsize >= sizeof(a->cmd)) {
    159         free(a);
    160         die("Command length (%d) exceeds maximum size (%d)", cmdsize, sizeof(a->cmd));
    161     }
    162 
    163     if (action_last) {
    164         action_last->next = a;
    165     } else {
    166         action_list = a;
    167     }
    168     action_last = a;
    169     a->op = op;
    170     a->func = cb_default;
    171 
    172     a->start = -1;
    173 
    174     return a;
    175 }
    176 
    177 void fb_queue_erase(const char *ptn)
    178 {
    179     Action *a;
    180     a = queue_action(OP_COMMAND, "erase:%s", ptn);
    181     a->msg = mkmsg("erasing '%s'", ptn);
    182 }
    183 
    184 /* Loads file content into buffer. Returns NULL on error. */
    185 static void *load_buffer(int fd, off_t size)
    186 {
    187     void *buffer;
    188 
    189 #ifdef USE_MINGW
    190     ssize_t count = 0;
    191 
    192     // mmap is more efficient but mingw does not support it.
    193     // In this case we read whole image into memory buffer.
    194     buffer = malloc(size);
    195     if (!buffer) {
    196         perror("malloc");
    197         return NULL;
    198     }
    199 
    200     lseek(fd, 0, SEEK_SET);
    201     while(count < size) {
    202         ssize_t actually_read = read(fd, (char*)buffer+count, size-count);
    203 
    204         if (actually_read == 0) {
    205             break;
    206         }
    207         if (actually_read < 0) {
    208             if (errno == EINTR) {
    209                 continue;
    210             }
    211             perror("read");
    212             free(buffer);
    213             return NULL;
    214         }
    215 
    216         count += actually_read;
    217     }
    218 #else
    219     buffer = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
    220     if (buffer == MAP_FAILED) {
    221         perror("mmap");
    222         return NULL;
    223     }
    224 #endif
    225 
    226     return buffer;
    227 }
    228 
    229 void cleanup_image(struct image_data *image)
    230 {
    231 #ifdef USE_MINGW
    232     free(image->buffer);
    233 #else
    234     munmap(image->buffer, image->image_size);
    235 #endif
    236 }
    237 
    238 void generate_ext4_image(struct image_data *image)
    239 {
    240     int fd;
    241     struct stat st;
    242 
    243 #ifdef USE_MINGW
    244     /* Ideally we should use tmpfile() here, the same as with unix version.
    245      * But unfortunately it is not portable as it is not clear whether this
    246      * function opens file in TEXT or BINARY mode.
    247      *
    248      * There are also some reports it is buggy:
    249      *    http://pdplab.it.uom.gr/teaching/gcc_manuals/gnulib.html#tmpfile
    250      *    http://www.mega-nerd.com/erikd/Blog/Windiots/tmpfile.html
    251      */
    252     char *filename = tempnam(getenv("TEMP"), "fastboot-format.img");
    253     fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644);
    254     unlink(filename);
    255 #else
    256     fd = fileno(tmpfile());
    257 #endif
    258     /* reset ext4fs info so we can be called multiple times */
    259     reset_ext4fs_info();
    260     info.len = image->partition_size;
    261     make_ext4fs_internal(fd, NULL, NULL, NULL, 0, 1, 0, 0, 0, NULL);
    262 
    263     fstat(fd, &st);
    264     image->image_size = st.st_size;
    265     image->buffer = load_buffer(fd, st.st_size);
    266 
    267     close(fd);
    268 }
    269 
    270 int fb_format(Action *a, usb_handle *usb, int skip_if_not_supported)
    271 {
    272     const char *partition = a->cmd;
    273     char response[FB_RESPONSE_SZ+1];
    274     int status = 0;
    275     struct image_data image;
    276     struct generator *generator = NULL;
    277     int fd;
    278     unsigned i;
    279     char cmd[CMD_SIZE];
    280 
    281     response[FB_RESPONSE_SZ] = '\0';
    282     snprintf(cmd, sizeof(cmd), "getvar:partition-type:%s", partition);
    283     status = fb_command_response(usb, cmd, response);
    284     if (status) {
    285         if (skip_if_not_supported) {
    286             fprintf(stderr,
    287                     "Erase successful, but not automatically formatting.\n");
    288             fprintf(stderr,
    289                     "Can't determine partition type.\n");
    290             return 0;
    291         }
    292         fprintf(stderr,"FAILED (%s)\n", fb_get_error());
    293         return status;
    294     }
    295 
    296     for (i = 0; i < ARRAY_SIZE(generators); i++) {
    297         if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) {
    298             generator = &generators[i];
    299             break;
    300         }
    301     }
    302     if (!generator) {
    303         if (skip_if_not_supported) {
    304             fprintf(stderr,
    305                     "Erase successful, but not automatically formatting.\n");
    306             fprintf(stderr,
    307                     "File system type %s not supported.\n", response);
    308             return 0;
    309         }
    310         fprintf(stderr,"Formatting is not supported for filesystem with type '%s'.\n",
    311                 response);
    312         return -1;
    313     }
    314 
    315     response[FB_RESPONSE_SZ] = '\0';
    316     snprintf(cmd, sizeof(cmd), "getvar:partition-size:%s", partition);
    317     status = fb_command_response(usb, cmd, response);
    318     if (status) {
    319         if (skip_if_not_supported) {
    320             fprintf(stderr,
    321                     "Erase successful, but not automatically formatting.\n");
    322             fprintf(stderr, "Unable to get partition size\n.");
    323             return 0;
    324         }
    325         fprintf(stderr,"FAILED (%s)\n", fb_get_error());
    326         return status;
    327     }
    328     image.partition_size = strtoll(response, (char **)NULL, 16);
    329 
    330     generator->generate(&image);
    331     if (!image.buffer) {
    332         fprintf(stderr,"Cannot generate image.\n");
    333         return -1;
    334     }
    335 
    336     // Following piece of code is similar to fb_queue_flash() but executes
    337     // actions directly without queuing
    338     fprintf(stderr, "sending '%s' (%lli KB)...\n", partition, image.image_size/1024);
    339     status = fb_download_data(usb, image.buffer, image.image_size);
    340     if (status) goto cleanup;
    341 
    342     fprintf(stderr, "writing '%s'...\n", partition);
    343     snprintf(cmd, sizeof(cmd), "flash:%s", partition);
    344     status = fb_command(usb, cmd);
    345     if (status) goto cleanup;
    346 
    347 cleanup:
    348     generator->cleanup(&image);
    349 
    350     return status;
    351 }
    352 
    353 void fb_queue_format(const char *partition, int skip_if_not_supported)
    354 {
    355     Action *a;
    356 
    357     a = queue_action(OP_FORMAT, partition);
    358     a->data = (void*)skip_if_not_supported;
    359     a->msg = mkmsg("formatting '%s' partition", partition);
    360 }
    361 
    362 void fb_queue_flash(const char *ptn, void *data, unsigned sz)
    363 {
    364     Action *a;
    365 
    366     a = queue_action(OP_DOWNLOAD, "");
    367     a->data = data;
    368     a->size = sz;
    369     a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
    370 
    371     a = queue_action(OP_COMMAND, "flash:%s", ptn);
    372     a->msg = mkmsg("writing '%s'", ptn);
    373 }
    374 
    375 static int match(char *str, const char **value, unsigned count)
    376 {
    377     const char *val;
    378     unsigned n;
    379     int len;
    380 
    381     for (n = 0; n < count; n++) {
    382         const char *val = value[n];
    383         int len = strlen(val);
    384         int match;
    385 
    386         if ((len > 1) && (val[len-1] == '*')) {
    387             len--;
    388             match = !strncmp(val, str, len);
    389         } else {
    390             match = !strcmp(val, str);
    391         }
    392 
    393         if (match) return 1;
    394     }
    395 
    396     return 0;
    397 }
    398 
    399 
    400 
    401 static int cb_check(Action *a, int status, char *resp, int invert)
    402 {
    403     const char **value = a->data;
    404     unsigned count = a->size;
    405     unsigned n;
    406     int yes;
    407 
    408     if (status) {
    409         fprintf(stderr,"FAILED (%s)\n", resp);
    410         return status;
    411     }
    412 
    413     if (a->prod) {
    414         if (strcmp(a->prod, cur_product) != 0) {
    415             double split = now();
    416             fprintf(stderr,"IGNORE, product is %s required only for %s [%7.3fs]\n",
    417                     cur_product, a->prod, (split - a->start));
    418             a->start = split;
    419             return 0;
    420         }
    421     }
    422 
    423     yes = match(resp, value, count);
    424     if (invert) yes = !yes;
    425 
    426     if (yes) {
    427         double split = now();
    428         fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
    429         a->start = split;
    430         return 0;
    431     }
    432 
    433     fprintf(stderr,"FAILED\n\n");
    434     fprintf(stderr,"Device %s is '%s'.\n", a->cmd + 7, resp);
    435     fprintf(stderr,"Update %s '%s'",
    436             invert ? "rejects" : "requires", value[0]);
    437     for (n = 1; n < count; n++) {
    438         fprintf(stderr," or '%s'", value[n]);
    439     }
    440     fprintf(stderr,".\n\n");
    441     return -1;
    442 }
    443 
    444 static int cb_require(Action *a, int status, char *resp)
    445 {
    446     return cb_check(a, status, resp, 0);
    447 }
    448 
    449 static int cb_reject(Action *a, int status, char *resp)
    450 {
    451     return cb_check(a, status, resp, 1);
    452 }
    453 
    454 void fb_queue_require(const char *prod, const char *var,
    455 		int invert, unsigned nvalues, const char **value)
    456 {
    457     Action *a;
    458     a = queue_action(OP_QUERY, "getvar:%s", var);
    459     a->prod = prod;
    460     a->data = value;
    461     a->size = nvalues;
    462     a->msg = mkmsg("checking %s", var);
    463     a->func = invert ? cb_reject : cb_require;
    464     if (a->data == 0) die("out of memory");
    465 }
    466 
    467 static int cb_display(Action *a, int status, char *resp)
    468 {
    469     if (status) {
    470         fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
    471         return status;
    472     }
    473     fprintf(stderr, "%s: %s\n", (char*) a->data, resp);
    474     return 0;
    475 }
    476 
    477 void fb_queue_display(const char *var, const char *prettyname)
    478 {
    479     Action *a;
    480     a = queue_action(OP_QUERY, "getvar:%s", var);
    481     a->data = strdup(prettyname);
    482     if (a->data == 0) die("out of memory");
    483     a->func = cb_display;
    484 }
    485 
    486 static int cb_save(Action *a, int status, char *resp)
    487 {
    488     if (status) {
    489         fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
    490         return status;
    491     }
    492     strncpy(a->data, resp, a->size);
    493     return 0;
    494 }
    495 
    496 void fb_queue_query_save(const char *var, char *dest, unsigned dest_size)
    497 {
    498     Action *a;
    499     a = queue_action(OP_QUERY, "getvar:%s", var);
    500     a->data = (void *)dest;
    501     a->size = dest_size;
    502     a->func = cb_save;
    503 }
    504 
    505 static int cb_do_nothing(Action *a, int status, char *resp)
    506 {
    507     fprintf(stderr,"\n");
    508     return 0;
    509 }
    510 
    511 void fb_queue_reboot(void)
    512 {
    513     Action *a = queue_action(OP_COMMAND, "reboot");
    514     a->func = cb_do_nothing;
    515     a->msg = "rebooting";
    516 }
    517 
    518 void fb_queue_command(const char *cmd, const char *msg)
    519 {
    520     Action *a = queue_action(OP_COMMAND, cmd);
    521     a->msg = msg;
    522 }
    523 
    524 void fb_queue_download(const char *name, void *data, unsigned size)
    525 {
    526     Action *a = queue_action(OP_DOWNLOAD, "");
    527     a->data = data;
    528     a->size = size;
    529     a->msg = mkmsg("downloading '%s'", name);
    530 }
    531 
    532 void fb_queue_notice(const char *notice)
    533 {
    534     Action *a = queue_action(OP_NOTICE, "");
    535     a->data = (void*) notice;
    536 }
    537 
    538 int fb_execute_queue(usb_handle *usb)
    539 {
    540     Action *a;
    541     char resp[FB_RESPONSE_SZ+1];
    542     int status = 0;
    543 
    544     a = action_list;
    545     resp[FB_RESPONSE_SZ] = 0;
    546 
    547     double start = -1;
    548     for (a = action_list; a; a = a->next) {
    549         a->start = now();
    550         if (start < 0) start = a->start;
    551         if (a->msg) {
    552             // fprintf(stderr,"%30s... ",a->msg);
    553             fprintf(stderr,"%s...\n",a->msg);
    554         }
    555         if (a->op == OP_DOWNLOAD) {
    556             status = fb_download_data(usb, a->data, a->size);
    557             status = a->func(a, status, status ? fb_get_error() : "");
    558             if (status) break;
    559         } else if (a->op == OP_COMMAND) {
    560             status = fb_command(usb, a->cmd);
    561             status = a->func(a, status, status ? fb_get_error() : "");
    562             if (status) break;
    563         } else if (a->op == OP_QUERY) {
    564             status = fb_command_response(usb, a->cmd, resp);
    565             status = a->func(a, status, status ? fb_get_error() : resp);
    566             if (status) break;
    567         } else if (a->op == OP_NOTICE) {
    568             fprintf(stderr,"%s\n",(char*)a->data);
    569         } else if (a->op == OP_FORMAT) {
    570             status = fb_format(a, usb, (int)a->data);
    571             status = a->func(a, status, status ? fb_get_error() : "");
    572             if (status) break;
    573         } else {
    574             die("bogus action");
    575         }
    576     }
    577 
    578     fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
    579     return status;
    580 }
    581