engine.c revision 88e8f61a9ef5cda24932f669d3b224e0216deba9
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
32#include <errno.h>
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#define ARRAY_SIZE(x)           (sizeof(x)/sizeof(x[0]))
50
51double now()
52{
53    struct timeval tv;
54    gettimeofday(&tv, NULL);
55    return (double)tv.tv_sec + (double)tv.tv_usec / 1000000;
56}
57
58char *mkmsg(const char *fmt, ...)
59{
60    char buf[256];
61    char *s;
62    va_list ap;
63
64    va_start(ap, fmt);
65    vsprintf(buf, fmt, ap);
66    va_end(ap);
67
68    s = strdup(buf);
69    if (s == 0) die("out of memory");
70    return s;
71}
72
73#define OP_DOWNLOAD   1
74#define OP_COMMAND    2
75#define OP_QUERY      3
76#define OP_NOTICE     4
77#define OP_FORMAT     5
78#define OP_DOWNLOAD_SPARSE 6
79
80typedef struct Action Action;
81
82#define CMD_SIZE 64
83
84struct Action
85{
86    unsigned op;
87    Action *next;
88
89    char cmd[CMD_SIZE];
90    const char *prod;
91    void *data;
92    unsigned size;
93
94    const char *msg;
95    int (*func)(Action *a, int status, char *resp);
96
97    double start;
98};
99
100static Action *action_list = 0;
101static Action *action_last = 0;
102
103
104struct image_data {
105    long long partition_size;
106    long long image_size; // real size of image file
107    void *buffer;
108};
109
110void generate_ext4_image(struct image_data *image);
111void cleanup_image(struct image_data *image);
112
113int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...)
114{
115    char cmd[CMD_SIZE] = "getvar:";
116    int getvar_len = strlen(cmd);
117    va_list args;
118
119    response[FB_RESPONSE_SZ] = '\0';
120    va_start(args, fmt);
121    vsnprintf(cmd + getvar_len, sizeof(cmd) - getvar_len, fmt, args);
122    va_end(args);
123    cmd[CMD_SIZE - 1] = '\0';
124    return fb_command_response(usb, cmd, response);
125}
126
127struct generator {
128    char *fs_type;
129
130    /* generate image and return it as image->buffer.
131     * size of the buffer returned as image->image_size.
132     *
133     * image->partition_size specifies what is the size of the
134     * file partition we generate image for.
135     */
136    void (*generate)(struct image_data *image);
137
138    /* it cleans the buffer allocated during image creation.
139     * this function probably does free() or munmap().
140     */
141    void (*cleanup)(struct image_data *image);
142} generators[] = {
143    { "ext4", generate_ext4_image, cleanup_image }
144};
145
146/* Return true if this partition is supported by the fastboot format command.
147 * It is also used to determine if we should first erase a partition before
148 * flashing it with an ext4 filesystem.  See needs_erase()
149 *
150 * Not all devices report the filesystem type, so don't report any errors,
151 * just return false.
152 */
153int fb_format_supported(usb_handle *usb, const char *partition)
154{
155    char response[FB_RESPONSE_SZ+1];
156    struct generator *generator = NULL;
157    int status;
158    unsigned int i;
159
160    status = fb_getvar(usb, response, "partition-type:%s", partition);
161    if (status) {
162        return 0;
163    }
164
165    for (i = 0; i < ARRAY_SIZE(generators); i++) {
166        if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) {
167            generator = &generators[i];
168            break;
169        }
170    }
171
172    if (generator) {
173        return 1;
174    }
175
176    return 0;
177}
178
179static int cb_default(Action *a, int status, char *resp)
180{
181    if (status) {
182        fprintf(stderr,"FAILED (%s)\n", resp);
183    } else {
184        double split = now();
185        fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
186        a->start = split;
187    }
188    return status;
189}
190
191static Action *queue_action(unsigned op, const char *fmt, ...)
192{
193    Action *a;
194    va_list ap;
195    size_t cmdsize;
196
197    a = calloc(1, sizeof(Action));
198    if (a == 0) die("out of memory");
199
200    va_start(ap, fmt);
201    cmdsize = vsnprintf(a->cmd, sizeof(a->cmd), fmt, ap);
202    va_end(ap);
203
204    if (cmdsize >= sizeof(a->cmd)) {
205        free(a);
206        die("Command length (%d) exceeds maximum size (%d)", cmdsize, sizeof(a->cmd));
207    }
208
209    if (action_last) {
210        action_last->next = a;
211    } else {
212        action_list = a;
213    }
214    action_last = a;
215    a->op = op;
216    a->func = cb_default;
217
218    a->start = -1;
219
220    return a;
221}
222
223void fb_queue_erase(const char *ptn)
224{
225    Action *a;
226    a = queue_action(OP_COMMAND, "erase:%s", ptn);
227    a->msg = mkmsg("erasing '%s'", ptn);
228}
229
230/* Loads file content into buffer. Returns NULL on error. */
231static void *load_buffer(int fd, off_t size)
232{
233    void *buffer;
234
235#ifdef USE_MINGW
236    ssize_t count = 0;
237
238    // mmap is more efficient but mingw does not support it.
239    // In this case we read whole image into memory buffer.
240    buffer = malloc(size);
241    if (!buffer) {
242        perror("malloc");
243        return NULL;
244    }
245
246    lseek(fd, 0, SEEK_SET);
247    while(count < size) {
248        ssize_t actually_read = read(fd, (char*)buffer+count, size-count);
249
250        if (actually_read == 0) {
251            break;
252        }
253        if (actually_read < 0) {
254            if (errno == EINTR) {
255                continue;
256            }
257            perror("read");
258            free(buffer);
259            return NULL;
260        }
261
262        count += actually_read;
263    }
264#else
265    buffer = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
266    if (buffer == MAP_FAILED) {
267        perror("mmap");
268        return NULL;
269    }
270#endif
271
272    return buffer;
273}
274
275void cleanup_image(struct image_data *image)
276{
277#ifdef USE_MINGW
278    free(image->buffer);
279#else
280    munmap(image->buffer, image->image_size);
281#endif
282}
283
284void generate_ext4_image(struct image_data *image)
285{
286    int fd;
287    struct stat st;
288
289#ifdef USE_MINGW
290    /* Ideally we should use tmpfile() here, the same as with unix version.
291     * But unfortunately it is not portable as it is not clear whether this
292     * function opens file in TEXT or BINARY mode.
293     *
294     * There are also some reports it is buggy:
295     *    http://pdplab.it.uom.gr/teaching/gcc_manuals/gnulib.html#tmpfile
296     *    http://www.mega-nerd.com/erikd/Blog/Windiots/tmpfile.html
297     */
298    char *filename = tempnam(getenv("TEMP"), "fastboot-format.img");
299    fd = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0644);
300    unlink(filename);
301#else
302    fd = fileno(tmpfile());
303#endif
304    make_ext4fs_sparse_fd(fd, image->partition_size, NULL, NULL);
305
306    fstat(fd, &st);
307    image->image_size = st.st_size;
308    image->buffer = load_buffer(fd, st.st_size);
309
310    close(fd);
311}
312
313int fb_format(Action *a, usb_handle *usb, int skip_if_not_supported)
314{
315    const char *partition = a->cmd;
316    char response[FB_RESPONSE_SZ+1];
317    int status = 0;
318    struct image_data image;
319    struct generator *generator = NULL;
320    int fd;
321    unsigned i;
322    char cmd[CMD_SIZE];
323
324    status = fb_getvar(usb, response, "partition-type:%s", partition);
325    if (status) {
326        if (skip_if_not_supported) {
327            fprintf(stderr,
328                    "Erase successful, but not automatically formatting.\n");
329            fprintf(stderr,
330                    "Can't determine partition type.\n");
331            return 0;
332        }
333        fprintf(stderr,"FAILED (%s)\n", fb_get_error());
334        return status;
335    }
336
337    for (i = 0; i < ARRAY_SIZE(generators); i++) {
338        if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) {
339            generator = &generators[i];
340            break;
341        }
342    }
343    if (!generator) {
344        if (skip_if_not_supported) {
345            fprintf(stderr,
346                    "Erase successful, but not automatically formatting.\n");
347            fprintf(stderr,
348                    "File system type %s not supported.\n", response);
349            return 0;
350        }
351        fprintf(stderr,"Formatting is not supported for filesystem with type '%s'.\n",
352                response);
353        return -1;
354    }
355
356    status = fb_getvar(usb, response, "partition-size:%s", partition);
357    if (status) {
358        if (skip_if_not_supported) {
359            fprintf(stderr,
360                    "Erase successful, but not automatically formatting.\n");
361            fprintf(stderr, "Unable to get partition size\n.");
362            return 0;
363        }
364        fprintf(stderr,"FAILED (%s)\n", fb_get_error());
365        return status;
366    }
367    image.partition_size = strtoll(response, (char **)NULL, 16);
368
369    generator->generate(&image);
370    if (!image.buffer) {
371        fprintf(stderr,"Cannot generate image.\n");
372        return -1;
373    }
374
375    // Following piece of code is similar to fb_queue_flash() but executes
376    // actions directly without queuing
377    fprintf(stderr, "sending '%s' (%lli KB)...\n", partition, image.image_size/1024);
378    status = fb_download_data(usb, image.buffer, image.image_size);
379    if (status) goto cleanup;
380
381    fprintf(stderr, "writing '%s'...\n", partition);
382    snprintf(cmd, sizeof(cmd), "flash:%s", partition);
383    status = fb_command(usb, cmd);
384    if (status) goto cleanup;
385
386cleanup:
387    generator->cleanup(&image);
388
389    return status;
390}
391
392void fb_queue_format(const char *partition, int skip_if_not_supported)
393{
394    Action *a;
395
396    a = queue_action(OP_FORMAT, partition);
397    a->data = (void*)skip_if_not_supported;
398    a->msg = mkmsg("formatting '%s' partition", partition);
399}
400
401void fb_queue_flash(const char *ptn, void *data, unsigned sz)
402{
403    Action *a;
404
405    a = queue_action(OP_DOWNLOAD, "");
406    a->data = data;
407    a->size = sz;
408    a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
409
410    a = queue_action(OP_COMMAND, "flash:%s", ptn);
411    a->msg = mkmsg("writing '%s'", ptn);
412}
413
414void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz)
415{
416    Action *a;
417
418    a = queue_action(OP_DOWNLOAD_SPARSE, "");
419    a->data = s;
420    a->size = 0;
421    a->msg = mkmsg("sending sparse '%s' (%d KB)", ptn, sz / 1024);
422
423    a = queue_action(OP_COMMAND, "flash:%s", ptn);
424    a->msg = mkmsg("writing '%s'", ptn);
425}
426
427static int match(char *str, const char **value, unsigned count)
428{
429    const char *val;
430    unsigned n;
431    int len;
432
433    for (n = 0; n < count; n++) {
434        const char *val = value[n];
435        int len = strlen(val);
436        int match;
437
438        if ((len > 1) && (val[len-1] == '*')) {
439            len--;
440            match = !strncmp(val, str, len);
441        } else {
442            match = !strcmp(val, str);
443        }
444
445        if (match) return 1;
446    }
447
448    return 0;
449}
450
451
452
453static int cb_check(Action *a, int status, char *resp, int invert)
454{
455    const char **value = a->data;
456    unsigned count = a->size;
457    unsigned n;
458    int yes;
459
460    if (status) {
461        fprintf(stderr,"FAILED (%s)\n", resp);
462        return status;
463    }
464
465    if (a->prod) {
466        if (strcmp(a->prod, cur_product) != 0) {
467            double split = now();
468            fprintf(stderr,"IGNORE, product is %s required only for %s [%7.3fs]\n",
469                    cur_product, a->prod, (split - a->start));
470            a->start = split;
471            return 0;
472        }
473    }
474
475    yes = match(resp, value, count);
476    if (invert) yes = !yes;
477
478    if (yes) {
479        double split = now();
480        fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
481        a->start = split;
482        return 0;
483    }
484
485    fprintf(stderr,"FAILED\n\n");
486    fprintf(stderr,"Device %s is '%s'.\n", a->cmd + 7, resp);
487    fprintf(stderr,"Update %s '%s'",
488            invert ? "rejects" : "requires", value[0]);
489    for (n = 1; n < count; n++) {
490        fprintf(stderr," or '%s'", value[n]);
491    }
492    fprintf(stderr,".\n\n");
493    return -1;
494}
495
496static int cb_require(Action *a, int status, char *resp)
497{
498    return cb_check(a, status, resp, 0);
499}
500
501static int cb_reject(Action *a, int status, char *resp)
502{
503    return cb_check(a, status, resp, 1);
504}
505
506void fb_queue_require(const char *prod, const char *var,
507		int invert, unsigned nvalues, const char **value)
508{
509    Action *a;
510    a = queue_action(OP_QUERY, "getvar:%s", var);
511    a->prod = prod;
512    a->data = value;
513    a->size = nvalues;
514    a->msg = mkmsg("checking %s", var);
515    a->func = invert ? cb_reject : cb_require;
516    if (a->data == 0) die("out of memory");
517}
518
519static int cb_display(Action *a, int status, char *resp)
520{
521    if (status) {
522        fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
523        return status;
524    }
525    fprintf(stderr, "%s: %s\n", (char*) a->data, resp);
526    return 0;
527}
528
529void fb_queue_display(const char *var, const char *prettyname)
530{
531    Action *a;
532    a = queue_action(OP_QUERY, "getvar:%s", var);
533    a->data = strdup(prettyname);
534    if (a->data == 0) die("out of memory");
535    a->func = cb_display;
536}
537
538static int cb_save(Action *a, int status, char *resp)
539{
540    if (status) {
541        fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
542        return status;
543    }
544    strncpy(a->data, resp, a->size);
545    return 0;
546}
547
548void fb_queue_query_save(const char *var, char *dest, unsigned dest_size)
549{
550    Action *a;
551    a = queue_action(OP_QUERY, "getvar:%s", var);
552    a->data = (void *)dest;
553    a->size = dest_size;
554    a->func = cb_save;
555}
556
557static int cb_do_nothing(Action *a, int status, char *resp)
558{
559    fprintf(stderr,"\n");
560    return 0;
561}
562
563void fb_queue_reboot(void)
564{
565    Action *a = queue_action(OP_COMMAND, "reboot");
566    a->func = cb_do_nothing;
567    a->msg = "rebooting";
568}
569
570void fb_queue_command(const char *cmd, const char *msg)
571{
572    Action *a = queue_action(OP_COMMAND, cmd);
573    a->msg = msg;
574}
575
576void fb_queue_download(const char *name, void *data, unsigned size)
577{
578    Action *a = queue_action(OP_DOWNLOAD, "");
579    a->data = data;
580    a->size = size;
581    a->msg = mkmsg("downloading '%s'", name);
582}
583
584void fb_queue_notice(const char *notice)
585{
586    Action *a = queue_action(OP_NOTICE, "");
587    a->data = (void*) notice;
588}
589
590int fb_execute_queue(usb_handle *usb)
591{
592    Action *a;
593    char resp[FB_RESPONSE_SZ+1];
594    int status = 0;
595
596    a = action_list;
597    if (!a)
598        return status;
599    resp[FB_RESPONSE_SZ] = 0;
600
601    double start = -1;
602    for (a = action_list; a; a = a->next) {
603        a->start = now();
604        if (start < 0) start = a->start;
605        if (a->msg) {
606            // fprintf(stderr,"%30s... ",a->msg);
607            fprintf(stderr,"%s...\n",a->msg);
608        }
609        if (a->op == OP_DOWNLOAD) {
610            status = fb_download_data(usb, a->data, a->size);
611            status = a->func(a, status, status ? fb_get_error() : "");
612            if (status) break;
613        } else if (a->op == OP_COMMAND) {
614            status = fb_command(usb, a->cmd);
615            status = a->func(a, status, status ? fb_get_error() : "");
616            if (status) break;
617        } else if (a->op == OP_QUERY) {
618            status = fb_command_response(usb, a->cmd, resp);
619            status = a->func(a, status, status ? fb_get_error() : resp);
620            if (status) break;
621        } else if (a->op == OP_NOTICE) {
622            fprintf(stderr,"%s\n",(char*)a->data);
623        } else if (a->op == OP_FORMAT) {
624            status = fb_format(a, usb, (int)a->data);
625            status = a->func(a, status, status ? fb_get_error() : "");
626            if (status) break;
627        } else if (a->op == OP_DOWNLOAD_SPARSE) {
628            status = fb_download_data_sparse(usb, a->data);
629            status = a->func(a, status, status ? fb_get_error() : "");
630            if (status) break;
631        } else {
632            die("bogus action");
633        }
634    }
635
636    fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
637    return status;
638}
639
640int fb_queue_is_empty(void)
641{
642    return (action_list == NULL);
643}
644