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