engine.c revision 230d160a718333651f7ca1557404f96682795b37
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#ifndef USE_MINGW
44#include <sys/mman.h>
45#endif
46
47
48extern struct fs_info info;
49
50#define ARRAY_SIZE(x)           (sizeof(x)/sizeof(x[0]))
51
52double now()
53{
54    struct timeval tv;
55    gettimeofday(&tv, NULL);
56    return (double)tv.tv_sec + (double)tv.tv_usec / 1000000;
57}
58
59char *mkmsg(const char *fmt, ...)
60{
61    char buf[256];
62    char *s;
63    va_list ap;
64
65    va_start(ap, fmt);
66    vsprintf(buf, fmt, ap);
67    va_end(ap);
68
69    s = strdup(buf);
70    if (s == 0) die("out of memory");
71    return s;
72}
73
74#define OP_DOWNLOAD   1
75#define OP_COMMAND    2
76#define OP_QUERY      3
77#define OP_NOTICE     4
78#define OP_FORMAT     5
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
113struct generator {
114    char *fs_type;
115
116    /* generate image and return it as image->buffer.
117     * size of the buffer returned as image->image_size.
118     *
119     * image->partition_size specifies what is the size of the
120     * file partition we generate image for.
121     */
122    void (*generate)(struct image_data *image);
123
124    /* it cleans the buffer allocated during image creation.
125     * this function probably does free() or munmap().
126     */
127    void (*cleanup)(struct image_data *image);
128} generators[] = {
129    { "ext4", generate_ext4_image, cleanup_image }
130};
131
132static int cb_default(Action *a, int status, char *resp)
133{
134    if (status) {
135        fprintf(stderr,"FAILED (%s)\n", resp);
136    } else {
137        double split = now();
138        fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
139        a->start = split;
140    }
141    return status;
142}
143
144static Action *queue_action(unsigned op, const char *fmt, ...)
145{
146    Action *a;
147    va_list ap;
148    size_t cmdsize;
149
150    a = calloc(1, sizeof(Action));
151    if (a == 0) die("out of memory");
152
153    va_start(ap, fmt);
154    cmdsize = vsnprintf(a->cmd, sizeof(a->cmd), fmt, ap);
155    va_end(ap);
156
157    if (cmdsize >= sizeof(a->cmd)) {
158        free(a);
159        die("Command length (%d) exceeds maximum size (%d)", cmdsize, sizeof(a->cmd));
160    }
161
162    if (action_last) {
163        action_last->next = a;
164    } else {
165        action_list = a;
166    }
167    action_last = a;
168    a->op = op;
169    a->func = cb_default;
170
171    a->start = -1;
172
173    return a;
174}
175
176void fb_queue_erase(const char *ptn)
177{
178    Action *a;
179    a = queue_action(OP_COMMAND, "erase:%s", ptn);
180    a->msg = mkmsg("erasing '%s'", ptn);
181}
182
183/* Loads file content into buffer. Returns NULL on error. */
184static void *load_buffer(int fd, off_t size)
185{
186    void *buffer;
187
188#ifdef USE_MINGW
189    ssize_t count = 0;
190
191    // mmap is more efficient but mingw does not support it.
192    // In this case we read whole image into memory buffer.
193    buffer = malloc(size);
194    if (!buffer) {
195        perror("malloc");
196        return NULL;
197    }
198
199    while(count < size) {
200        ssize_t actually_read = pread(fd, (char*)buffer+count, size-count, count);
201
202        if (actually_read == 0) {
203            break;
204        }
205        if (actually_read < 0) {
206            if (errno == EINTR) {
207                continue;
208            }
209            perror("read");
210            free(buffer);
211            return NULL;
212        }
213
214        count += actually_read;
215    }
216#else
217    buffer = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
218    if (buffer == MAP_FAILED) {
219        perror("mmap");
220        return NULL;
221    }
222#endif
223
224    return buffer;
225}
226
227void cleanup_image(struct image_data *image)
228{
229#ifdef USE_MINGW
230    free(image->buffer);
231#else
232    munmap(image->buffer, image->image_size);
233#endif
234}
235
236void generate_ext4_image(struct image_data *image)
237{
238    int fd;
239    struct stat st;
240
241    fd = fileno(tmpfile());
242    info.len = image->partition_size;
243    make_ext4fs_internal(fd, NULL, NULL, 0, 0, 1, 0, 0, 0);
244
245    fstat(fd, &st);
246    image->image_size = st.st_size;
247    image->buffer = load_buffer(fd, st.st_size);
248
249    close(fd);
250}
251
252int fb_format(Action *a, usb_handle *usb)
253{
254    const char *partition = a->cmd;
255    char query[256];
256    char response[FB_RESPONSE_SZ+1];
257    int status = 0;
258    struct image_data image;
259    struct generator *generator = NULL;
260    int fd;
261    unsigned i;
262    char cmd[CMD_SIZE];
263
264    response[FB_RESPONSE_SZ] = '\0';
265    snprintf(query, sizeof(query), "getvar:partition-type:%s", partition);
266    status = fb_command_response(usb, query, response);
267    if (status) {
268        fprintf(stderr,"FAILED (%s)\n", fb_get_error());
269        return status;
270    }
271
272    for (i = 0; i < ARRAY_SIZE(generators); i++) {
273        if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) {
274            generator = &generators[i];
275            break;
276        }
277    }
278    if (!generator) {
279        fprintf(stderr,"Formatting is not supported for filesystem with type '%s'.\n",
280                response);
281        return -1;
282    }
283
284    response[FB_RESPONSE_SZ] = '\0';
285    snprintf(query, sizeof(query), "getvar:partition-size:%s", partition);
286    status = fb_command_response(usb, query, response);
287    if (status) {
288        fprintf(stderr,"FAILED (%s)\n", fb_get_error());
289        return status;
290    }
291    image.partition_size = strtoll(response, (char **)NULL, 16);
292
293    generator->generate(&image);
294    if (!image.buffer) {
295        fprintf(stderr,"Cannot generate image.\n");
296        return -1;
297    }
298
299    // Following piece of code is similar to fb_queue_flash() but executes
300    // actions directly without queuing
301    fprintf(stderr, "sending '%s' (%lli KB)...\n", partition, image.image_size/1024);
302    status = fb_download_data(usb, image.buffer, image.image_size);
303    if (status) goto cleanup;
304
305    fprintf(stderr, "writing '%s'...\n", partition);
306    snprintf(cmd, CMD_SIZE, "flash:%s", partition);
307    status = fb_command(usb, cmd);
308    if (status) goto cleanup;
309
310cleanup:
311    generator->cleanup(&image);
312
313    return status;
314}
315
316void fb_queue_format(const char *partition)
317{
318    Action *a;
319
320    a = queue_action(OP_FORMAT, partition);
321    a->msg = mkmsg("formatting '%s' partition", partition);
322}
323
324void fb_queue_flash(const char *ptn, void *data, unsigned sz)
325{
326    Action *a;
327
328    a = queue_action(OP_DOWNLOAD, "");
329    a->data = data;
330    a->size = sz;
331    a->msg = mkmsg("sending '%s' (%d KB)", ptn, sz / 1024);
332
333    a = queue_action(OP_COMMAND, "flash:%s", ptn);
334    a->msg = mkmsg("writing '%s'", ptn);
335}
336
337static int match(char *str, const char **value, unsigned count)
338{
339    const char *val;
340    unsigned n;
341    int len;
342
343    for (n = 0; n < count; n++) {
344        const char *val = value[n];
345        int len = strlen(val);
346        int match;
347
348        if ((len > 1) && (val[len-1] == '*')) {
349            len--;
350            match = !strncmp(val, str, len);
351        } else {
352            match = !strcmp(val, str);
353        }
354
355        if (match) return 1;
356    }
357
358    return 0;
359}
360
361
362
363static int cb_check(Action *a, int status, char *resp, int invert)
364{
365    const char **value = a->data;
366    unsigned count = a->size;
367    unsigned n;
368    int yes;
369
370    if (status) {
371        fprintf(stderr,"FAILED (%s)\n", resp);
372        return status;
373    }
374
375    if (a->prod) {
376        if (strcmp(a->prod, cur_product) != 0) {
377            double split = now();
378            fprintf(stderr,"IGNORE, product is %s required only for %s [%7.3fs]\n",
379                    cur_product, a->prod, (split - a->start));
380            a->start = split;
381            return 0;
382        }
383    }
384
385    yes = match(resp, value, count);
386    if (invert) yes = !yes;
387
388    if (yes) {
389        double split = now();
390        fprintf(stderr,"OKAY [%7.3fs]\n", (split - a->start));
391        a->start = split;
392        return 0;
393    }
394
395    fprintf(stderr,"FAILED\n\n");
396    fprintf(stderr,"Device %s is '%s'.\n", a->cmd + 7, resp);
397    fprintf(stderr,"Update %s '%s'",
398            invert ? "rejects" : "requires", value[0]);
399    for (n = 1; n < count; n++) {
400        fprintf(stderr," or '%s'", value[n]);
401    }
402    fprintf(stderr,".\n\n");
403    return -1;
404}
405
406static int cb_require(Action *a, int status, char *resp)
407{
408    return cb_check(a, status, resp, 0);
409}
410
411static int cb_reject(Action *a, int status, char *resp)
412{
413    return cb_check(a, status, resp, 1);
414}
415
416void fb_queue_require(const char *prod, const char *var,
417		int invert, unsigned nvalues, const char **value)
418{
419    Action *a;
420    a = queue_action(OP_QUERY, "getvar:%s", var);
421    a->prod = prod;
422    a->data = value;
423    a->size = nvalues;
424    a->msg = mkmsg("checking %s", var);
425    a->func = invert ? cb_reject : cb_require;
426    if (a->data == 0) die("out of memory");
427}
428
429static int cb_display(Action *a, int status, char *resp)
430{
431    if (status) {
432        fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
433        return status;
434    }
435    fprintf(stderr, "%s: %s\n", (char*) a->data, resp);
436    return 0;
437}
438
439void fb_queue_display(const char *var, const char *prettyname)
440{
441    Action *a;
442    a = queue_action(OP_QUERY, "getvar:%s", var);
443    a->data = strdup(prettyname);
444    if (a->data == 0) die("out of memory");
445    a->func = cb_display;
446}
447
448static int cb_save(Action *a, int status, char *resp)
449{
450    if (status) {
451        fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
452        return status;
453    }
454    strncpy(a->data, resp, a->size);
455    return 0;
456}
457
458void fb_queue_query_save(const char *var, char *dest, unsigned dest_size)
459{
460    Action *a;
461    a = queue_action(OP_QUERY, "getvar:%s", var);
462    a->data = (void *)dest;
463    a->size = dest_size;
464    a->func = cb_save;
465}
466
467static int cb_do_nothing(Action *a, int status, char *resp)
468{
469    fprintf(stderr,"\n");
470    return 0;
471}
472
473void fb_queue_reboot(void)
474{
475    Action *a = queue_action(OP_COMMAND, "reboot");
476    a->func = cb_do_nothing;
477    a->msg = "rebooting";
478}
479
480void fb_queue_command(const char *cmd, const char *msg)
481{
482    Action *a = queue_action(OP_COMMAND, cmd);
483    a->msg = msg;
484}
485
486void fb_queue_download(const char *name, void *data, unsigned size)
487{
488    Action *a = queue_action(OP_DOWNLOAD, "");
489    a->data = data;
490    a->size = size;
491    a->msg = mkmsg("downloading '%s'", name);
492}
493
494void fb_queue_notice(const char *notice)
495{
496    Action *a = queue_action(OP_NOTICE, "");
497    a->data = (void*) notice;
498}
499
500int fb_execute_queue(usb_handle *usb)
501{
502    Action *a;
503    char resp[FB_RESPONSE_SZ+1];
504    int status = 0;
505
506    a = action_list;
507    resp[FB_RESPONSE_SZ] = 0;
508
509    double start = -1;
510    for (a = action_list; a; a = a->next) {
511        a->start = now();
512        if (start < 0) start = a->start;
513        if (a->msg) {
514            // fprintf(stderr,"%30s... ",a->msg);
515            fprintf(stderr,"%s...\n",a->msg);
516        }
517        if (a->op == OP_DOWNLOAD) {
518            status = fb_download_data(usb, a->data, a->size);
519            status = a->func(a, status, status ? fb_get_error() : "");
520            if (status) break;
521        } else if (a->op == OP_COMMAND) {
522            status = fb_command(usb, a->cmd);
523            status = a->func(a, status, status ? fb_get_error() : "");
524            if (status) break;
525        } else if (a->op == OP_QUERY) {
526            status = fb_command_response(usb, a->cmd, resp);
527            status = a->func(a, status, status ? fb_get_error() : resp);
528            if (status) break;
529        } else if (a->op == OP_NOTICE) {
530            fprintf(stderr,"%s\n",(char*)a->data);
531        } else if (a->op == OP_FORMAT) {
532            status = fb_format(a, usb);
533            status = a->func(a, status, status ? fb_get_error() : "");
534            if (status) break;
535        } else {
536            die("bogus action");
537        }
538    }
539
540    fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
541    return status;
542}
543