• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
now()51 double now()
52 {
53     struct timeval tv;
54     gettimeofday(&tv, NULL);
55     return (double)tv.tv_sec + (double)tv.tv_usec / 1000000;
56 }
57 
mkmsg(const char * fmt,...)58 char *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 
80 typedef struct Action Action;
81 
82 #define CMD_SIZE 64
83 
84 struct 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 
100 static Action *action_list = 0;
101 static Action *action_last = 0;
102 
103 
104 struct image_data {
105     long long partition_size;
106     long long image_size; // real size of image file
107     void *buffer;
108 };
109 
110 void generate_ext4_image(struct image_data *image);
111 void cleanup_image(struct image_data *image);
112 
fb_getvar(struct usb_handle * usb,char * response,const char * fmt,...)113 int 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 
127 struct 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  */
fb_format_supported(usb_handle * usb,const char * partition)153 int 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 
cb_default(Action * a,int status,char * resp)179 static 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 
queue_action(unsigned op,const char * fmt,...)191 static 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 
fb_queue_erase(const char * ptn)223 void 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. */
load_buffer(int fd,off_t size)231 static 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 
cleanup_image(struct image_data * image)275 void 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 
generate_ext4_image(struct image_data * image)284 void 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 
fb_format(Action * a,usb_handle * usb,int skip_if_not_supported)313 int 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 
386 cleanup:
387     generator->cleanup(&image);
388 
389     return status;
390 }
391 
fb_queue_format(const char * partition,int skip_if_not_supported)392 void 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 
fb_queue_flash(const char * ptn,void * data,unsigned sz)401 void 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 
fb_queue_flash_sparse(const char * ptn,struct sparse_file * s,unsigned sz)414 void 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 
match(char * str,const char ** value,unsigned count)427 static 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 
cb_check(Action * a,int status,char * resp,int invert)453 static 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 
cb_require(Action * a,int status,char * resp)496 static int cb_require(Action *a, int status, char *resp)
497 {
498     return cb_check(a, status, resp, 0);
499 }
500 
cb_reject(Action * a,int status,char * resp)501 static int cb_reject(Action *a, int status, char *resp)
502 {
503     return cb_check(a, status, resp, 1);
504 }
505 
fb_queue_require(const char * prod,const char * var,int invert,unsigned nvalues,const char ** value)506 void 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 
cb_display(Action * a,int status,char * resp)519 static 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 
fb_queue_display(const char * var,const char * prettyname)529 void 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 
cb_save(Action * a,int status,char * resp)538 static 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 
fb_queue_query_save(const char * var,char * dest,unsigned dest_size)548 void 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 
cb_do_nothing(Action * a,int status,char * resp)557 static int cb_do_nothing(Action *a, int status, char *resp)
558 {
559     fprintf(stderr,"\n");
560     return 0;
561 }
562 
fb_queue_reboot(void)563 void 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 
fb_queue_command(const char * cmd,const char * msg)570 void fb_queue_command(const char *cmd, const char *msg)
571 {
572     Action *a = queue_action(OP_COMMAND, cmd);
573     a->msg = msg;
574 }
575 
fb_queue_download(const char * name,void * data,unsigned size)576 void 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 
fb_queue_notice(const char * notice)584 void fb_queue_notice(const char *notice)
585 {
586     Action *a = queue_action(OP_NOTICE, "");
587     a->data = (void*) notice;
588 }
589 
fb_execute_queue(usb_handle * usb)590 int 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 
fb_queue_is_empty(void)640 int fb_queue_is_empty(void)
641 {
642     return (action_list == NULL);
643 }
644