• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <ctype.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <getopt.h>
21 #include <limits.h>
22 #include <linux/input.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <time.h>
29 #include <unistd.h>
30 #include <dirent.h>
31 
32 #include "bootloader.h"
33 #include "common.h"
34 #include "cutils/properties.h"
35 #include "cutils/android_reboot.h"
36 #include "install.h"
37 #include "minui/minui.h"
38 #include "minzip/DirUtil.h"
39 #include "roots.h"
40 #include "recovery_ui.h"
41 
42 static const struct option OPTIONS[] = {
43   { "send_intent", required_argument, NULL, 's' },
44   { "update_package", required_argument, NULL, 'u' },
45   { "wipe_data", no_argument, NULL, 'w' },
46   { "wipe_cache", no_argument, NULL, 'c' },
47   { "show_text", no_argument, NULL, 't' },
48   { NULL, 0, NULL, 0 },
49 };
50 
51 static const char *COMMAND_FILE = "/cache/recovery/command";
52 static const char *INTENT_FILE = "/cache/recovery/intent";
53 static const char *LOG_FILE = "/cache/recovery/log";
54 static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
55 static const char *CACHE_ROOT = "/cache";
56 static const char *SDCARD_ROOT = "/sdcard";
57 static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
58 static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload";
59 
60 extern UIParameters ui_parameters;    // from ui.c
61 
62 /*
63  * The recovery tool communicates with the main system through /cache files.
64  *   /cache/recovery/command - INPUT - command line for tool, one arg per line
65  *   /cache/recovery/log - OUTPUT - combined log file from recovery run(s)
66  *   /cache/recovery/intent - OUTPUT - intent that was passed in
67  *
68  * The arguments which may be supplied in the recovery.command file:
69  *   --send_intent=anystring - write the text out to recovery.intent
70  *   --update_package=path - verify install an OTA package file
71  *   --wipe_data - erase user data (and cache), then reboot
72  *   --wipe_cache - wipe cache (but not user data), then reboot
73  *   --set_encrypted_filesystem=on|off - enables / diasables encrypted fs
74  *
75  * After completing, we remove /cache/recovery/command and reboot.
76  * Arguments may also be supplied in the bootloader control block (BCB).
77  * These important scenarios must be safely restartable at any point:
78  *
79  * FACTORY RESET
80  * 1. user selects "factory reset"
81  * 2. main system writes "--wipe_data" to /cache/recovery/command
82  * 3. main system reboots into recovery
83  * 4. get_args() writes BCB with "boot-recovery" and "--wipe_data"
84  *    -- after this, rebooting will restart the erase --
85  * 5. erase_volume() reformats /data
86  * 6. erase_volume() reformats /cache
87  * 7. finish_recovery() erases BCB
88  *    -- after this, rebooting will restart the main system --
89  * 8. main() calls reboot() to boot main system
90  *
91  * OTA INSTALL
92  * 1. main system downloads OTA package to /cache/some-filename.zip
93  * 2. main system writes "--update_package=/cache/some-filename.zip"
94  * 3. main system reboots into recovery
95  * 4. get_args() writes BCB with "boot-recovery" and "--update_package=..."
96  *    -- after this, rebooting will attempt to reinstall the update --
97  * 5. install_package() attempts to install the update
98  *    NOTE: the package install must itself be restartable from any point
99  * 6. finish_recovery() erases BCB
100  *    -- after this, rebooting will (try to) restart the main system --
101  * 7. ** if install failed **
102  *    7a. prompt_and_wait() shows an error icon and waits for the user
103  *    7b; the user reboots (pulling the battery, etc) into the main system
104  * 8. main() calls maybe_install_firmware_update()
105  *    ** if the update contained radio/hboot firmware **:
106  *    8a. m_i_f_u() writes BCB with "boot-recovery" and "--wipe_cache"
107  *        -- after this, rebooting will reformat cache & restart main system --
108  *    8b. m_i_f_u() writes firmware image into raw cache partition
109  *    8c. m_i_f_u() writes BCB with "update-radio/hboot" and "--wipe_cache"
110  *        -- after this, rebooting will attempt to reinstall firmware --
111  *    8d. bootloader tries to flash firmware
112  *    8e. bootloader writes BCB with "boot-recovery" (keeping "--wipe_cache")
113  *        -- after this, rebooting will reformat cache & restart main system --
114  *    8f. erase_volume() reformats /cache
115  *    8g. finish_recovery() erases BCB
116  *        -- after this, rebooting will (try to) restart the main system --
117  * 9. main() calls reboot() to boot main system
118  */
119 
120 static const int MAX_ARG_LENGTH = 4096;
121 static const int MAX_ARGS = 100;
122 
123 // open a given path, mounting partitions as necessary
124 FILE*
fopen_path(const char * path,const char * mode)125 fopen_path(const char *path, const char *mode) {
126     if (ensure_path_mounted(path) != 0) {
127         LOGE("Can't mount %s\n", path);
128         return NULL;
129     }
130 
131     // When writing, try to create the containing directory, if necessary.
132     // Use generous permissions, the system (init.rc) will reset them.
133     if (strchr("wa", mode[0])) dirCreateHierarchy(path, 0777, NULL, 1);
134 
135     FILE *fp = fopen(path, mode);
136     return fp;
137 }
138 
139 // close a file, log an error if the error indicator is set
140 static void
check_and_fclose(FILE * fp,const char * name)141 check_and_fclose(FILE *fp, const char *name) {
142     fflush(fp);
143     if (ferror(fp)) LOGE("Error in %s\n(%s)\n", name, strerror(errno));
144     fclose(fp);
145 }
146 
147 // command line args come from, in decreasing precedence:
148 //   - the actual command line
149 //   - the bootloader control block (one per line, after "recovery")
150 //   - the contents of COMMAND_FILE (one per line)
151 static void
get_args(int * argc,char *** argv)152 get_args(int *argc, char ***argv) {
153     struct bootloader_message boot;
154     memset(&boot, 0, sizeof(boot));
155     get_bootloader_message(&boot);  // this may fail, leaving a zeroed structure
156 
157     if (boot.command[0] != 0 && boot.command[0] != 255) {
158         LOGI("Boot command: %.*s\n", sizeof(boot.command), boot.command);
159     }
160 
161     if (boot.status[0] != 0 && boot.status[0] != 255) {
162         LOGI("Boot status: %.*s\n", sizeof(boot.status), boot.status);
163     }
164 
165     // --- if arguments weren't supplied, look in the bootloader control block
166     if (*argc <= 1) {
167         boot.recovery[sizeof(boot.recovery) - 1] = '\0';  // Ensure termination
168         const char *arg = strtok(boot.recovery, "\n");
169         if (arg != NULL && !strcmp(arg, "recovery")) {
170             *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
171             (*argv)[0] = strdup(arg);
172             for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
173                 if ((arg = strtok(NULL, "\n")) == NULL) break;
174                 (*argv)[*argc] = strdup(arg);
175             }
176             LOGI("Got arguments from boot message\n");
177         } else if (boot.recovery[0] != 0 && boot.recovery[0] != 255) {
178             LOGE("Bad boot message\n\"%.20s\"\n", boot.recovery);
179         }
180     }
181 
182     // --- if that doesn't work, try the command file
183     if (*argc <= 1) {
184         FILE *fp = fopen_path(COMMAND_FILE, "r");
185         if (fp != NULL) {
186             char *argv0 = (*argv)[0];
187             *argv = (char **) malloc(sizeof(char *) * MAX_ARGS);
188             (*argv)[0] = argv0;  // use the same program name
189 
190             char buf[MAX_ARG_LENGTH];
191             for (*argc = 1; *argc < MAX_ARGS; ++*argc) {
192                 if (!fgets(buf, sizeof(buf), fp)) break;
193                 (*argv)[*argc] = strdup(strtok(buf, "\r\n"));  // Strip newline.
194             }
195 
196             check_and_fclose(fp, COMMAND_FILE);
197             LOGI("Got arguments from %s\n", COMMAND_FILE);
198         }
199     }
200 
201     // --> write the arguments we have back into the bootloader control block
202     // always boot into recovery after this (until finish_recovery() is called)
203     strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
204     strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
205     int i;
206     for (i = 1; i < *argc; ++i) {
207         strlcat(boot.recovery, (*argv)[i], sizeof(boot.recovery));
208         strlcat(boot.recovery, "\n", sizeof(boot.recovery));
209     }
210     set_bootloader_message(&boot);
211 }
212 
213 static void
set_sdcard_update_bootloader_message()214 set_sdcard_update_bootloader_message() {
215     struct bootloader_message boot;
216     memset(&boot, 0, sizeof(boot));
217     strlcpy(boot.command, "boot-recovery", sizeof(boot.command));
218     strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery));
219     set_bootloader_message(&boot);
220 }
221 
222 // How much of the temp log we have copied to the copy in cache.
223 static long tmplog_offset = 0;
224 
225 static void
copy_log_file(const char * destination,int append)226 copy_log_file(const char* destination, int append) {
227     FILE *log = fopen_path(destination, append ? "a" : "w");
228     if (log == NULL) {
229         LOGE("Can't open %s\n", destination);
230     } else {
231         FILE *tmplog = fopen(TEMPORARY_LOG_FILE, "r");
232         if (tmplog == NULL) {
233             LOGE("Can't open %s\n", TEMPORARY_LOG_FILE);
234         } else {
235             if (append) {
236                 fseek(tmplog, tmplog_offset, SEEK_SET);  // Since last write
237             }
238             char buf[4096];
239             while (fgets(buf, sizeof(buf), tmplog)) fputs(buf, log);
240             if (append) {
241                 tmplog_offset = ftell(tmplog);
242             }
243             check_and_fclose(tmplog, TEMPORARY_LOG_FILE);
244         }
245         check_and_fclose(log, destination);
246     }
247 }
248 
249 
250 // clear the recovery command and prepare to boot a (hopefully working) system,
251 // copy our log file to cache as well (for the system to read), and
252 // record any intent we were asked to communicate back to the system.
253 // this function is idempotent: call it as many times as you like.
254 static void
finish_recovery(const char * send_intent)255 finish_recovery(const char *send_intent) {
256     // By this point, we're ready to return to the main system...
257     if (send_intent != NULL) {
258         FILE *fp = fopen_path(INTENT_FILE, "w");
259         if (fp == NULL) {
260             LOGE("Can't open %s\n", INTENT_FILE);
261         } else {
262             fputs(send_intent, fp);
263             check_and_fclose(fp, INTENT_FILE);
264         }
265     }
266 
267     // Copy logs to cache so the system can find out what happened.
268     copy_log_file(LOG_FILE, true);
269     copy_log_file(LAST_LOG_FILE, false);
270     chmod(LAST_LOG_FILE, 0640);
271 
272     // Reset to mormal system boot so recovery won't cycle indefinitely.
273     struct bootloader_message boot;
274     memset(&boot, 0, sizeof(boot));
275     set_bootloader_message(&boot);
276 
277     // Remove the command file, so recovery won't repeat indefinitely.
278     if (ensure_path_mounted(COMMAND_FILE) != 0 ||
279         (unlink(COMMAND_FILE) && errno != ENOENT)) {
280         LOGW("Can't unlink %s\n", COMMAND_FILE);
281     }
282 
283     sync();  // For good measure.
284 }
285 
286 static int
erase_volume(const char * volume)287 erase_volume(const char *volume) {
288     ui_set_background(BACKGROUND_ICON_INSTALLING);
289     ui_show_indeterminate_progress();
290     ui_print("Formatting %s...\n", volume);
291 
292     if (strcmp(volume, "/cache") == 0) {
293         // Any part of the log we'd copied to cache is now gone.
294         // Reset the pointer so we copy from the beginning of the temp
295         // log.
296         tmplog_offset = 0;
297     }
298 
299     return format_volume(volume);
300 }
301 
302 static char*
copy_sideloaded_package(const char * original_path)303 copy_sideloaded_package(const char* original_path) {
304   if (ensure_path_mounted(original_path) != 0) {
305     LOGE("Can't mount %s\n", original_path);
306     return NULL;
307   }
308 
309   if (ensure_path_mounted(SIDELOAD_TEMP_DIR) != 0) {
310     LOGE("Can't mount %s\n", SIDELOAD_TEMP_DIR);
311     return NULL;
312   }
313 
314   if (mkdir(SIDELOAD_TEMP_DIR, 0700) != 0) {
315     if (errno != EEXIST) {
316       LOGE("Can't mkdir %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno));
317       return NULL;
318     }
319   }
320 
321   // verify that SIDELOAD_TEMP_DIR is exactly what we expect: a
322   // directory, owned by root, readable and writable only by root.
323   struct stat st;
324   if (stat(SIDELOAD_TEMP_DIR, &st) != 0) {
325     LOGE("failed to stat %s (%s)\n", SIDELOAD_TEMP_DIR, strerror(errno));
326     return NULL;
327   }
328   if (!S_ISDIR(st.st_mode)) {
329     LOGE("%s isn't a directory\n", SIDELOAD_TEMP_DIR);
330     return NULL;
331   }
332   if ((st.st_mode & 0777) != 0700) {
333     LOGE("%s has perms %o\n", SIDELOAD_TEMP_DIR, st.st_mode);
334     return NULL;
335   }
336   if (st.st_uid != 0) {
337     LOGE("%s owned by %lu; not root\n", SIDELOAD_TEMP_DIR, st.st_uid);
338     return NULL;
339   }
340 
341   char copy_path[PATH_MAX];
342   strcpy(copy_path, SIDELOAD_TEMP_DIR);
343   strcat(copy_path, "/package.zip");
344 
345   char* buffer = malloc(BUFSIZ);
346   if (buffer == NULL) {
347     LOGE("Failed to allocate buffer\n");
348     return NULL;
349   }
350 
351   size_t read;
352   FILE* fin = fopen(original_path, "rb");
353   if (fin == NULL) {
354     LOGE("Failed to open %s (%s)\n", original_path, strerror(errno));
355     return NULL;
356   }
357   FILE* fout = fopen(copy_path, "wb");
358   if (fout == NULL) {
359     LOGE("Failed to open %s (%s)\n", copy_path, strerror(errno));
360     return NULL;
361   }
362 
363   while ((read = fread(buffer, 1, BUFSIZ, fin)) > 0) {
364     if (fwrite(buffer, 1, read, fout) != read) {
365       LOGE("Short write of %s (%s)\n", copy_path, strerror(errno));
366       return NULL;
367     }
368   }
369 
370   free(buffer);
371 
372   if (fclose(fout) != 0) {
373     LOGE("Failed to close %s (%s)\n", copy_path, strerror(errno));
374     return NULL;
375   }
376 
377   if (fclose(fin) != 0) {
378     LOGE("Failed to close %s (%s)\n", original_path, strerror(errno));
379     return NULL;
380   }
381 
382   // "adb push" is happy to overwrite read-only files when it's
383   // running as root, but we'll try anyway.
384   if (chmod(copy_path, 0400) != 0) {
385     LOGE("Failed to chmod %s (%s)\n", copy_path, strerror(errno));
386     return NULL;
387   }
388 
389   return strdup(copy_path);
390 }
391 
392 static char**
prepend_title(const char ** headers)393 prepend_title(const char** headers) {
394     char* title[] = { "Android system recovery <"
395                           EXPAND(RECOVERY_API_VERSION) "e>",
396                       "",
397                       NULL };
398 
399     // count the number of lines in our title, plus the
400     // caller-provided headers.
401     int count = 0;
402     char** p;
403     for (p = title; *p; ++p, ++count);
404     for (p = headers; *p; ++p, ++count);
405 
406     char** new_headers = malloc((count+1) * sizeof(char*));
407     char** h = new_headers;
408     for (p = title; *p; ++p, ++h) *h = *p;
409     for (p = headers; *p; ++p, ++h) *h = *p;
410     *h = NULL;
411 
412     return new_headers;
413 }
414 
415 static int
get_menu_selection(char ** headers,char ** items,int menu_only,int initial_selection)416 get_menu_selection(char** headers, char** items, int menu_only,
417                    int initial_selection) {
418     // throw away keys pressed previously, so user doesn't
419     // accidentally trigger menu items.
420     ui_clear_key_queue();
421 
422     ui_start_menu(headers, items, initial_selection);
423     int selected = initial_selection;
424     int chosen_item = -1;
425 
426     while (chosen_item < 0) {
427         int key = ui_wait_key();
428         int visible = ui_text_visible();
429 
430         if (key == -1) {   // ui_wait_key() timed out
431             if (ui_text_ever_visible()) {
432                 continue;
433             } else {
434                 LOGI("timed out waiting for key input; rebooting.\n");
435                 ui_end_menu();
436                 return ITEM_REBOOT;
437             }
438         }
439 
440         int action = device_handle_key(key, visible);
441 
442         if (action < 0) {
443             switch (action) {
444                 case HIGHLIGHT_UP:
445                     --selected;
446                     selected = ui_menu_select(selected);
447                     break;
448                 case HIGHLIGHT_DOWN:
449                     ++selected;
450                     selected = ui_menu_select(selected);
451                     break;
452                 case SELECT_ITEM:
453                     chosen_item = selected;
454                     break;
455                 case NO_ACTION:
456                     break;
457             }
458         } else if (!menu_only) {
459             chosen_item = action;
460         }
461     }
462 
463     ui_end_menu();
464     return chosen_item;
465 }
466 
compare_string(const void * a,const void * b)467 static int compare_string(const void* a, const void* b) {
468     return strcmp(*(const char**)a, *(const char**)b);
469 }
470 
471 static int
update_directory(const char * path,const char * unmount_when_done)472 update_directory(const char* path, const char* unmount_when_done) {
473     ensure_path_mounted(path);
474 
475     const char* MENU_HEADERS[] = { "Choose a package to install:",
476                                    path,
477                                    "",
478                                    NULL };
479     DIR* d;
480     struct dirent* de;
481     d = opendir(path);
482     if (d == NULL) {
483         LOGE("error opening %s: %s\n", path, strerror(errno));
484         if (unmount_when_done != NULL) {
485             ensure_path_unmounted(unmount_when_done);
486         }
487         return 0;
488     }
489 
490     char** headers = prepend_title(MENU_HEADERS);
491 
492     int d_size = 0;
493     int d_alloc = 10;
494     char** dirs = malloc(d_alloc * sizeof(char*));
495     int z_size = 1;
496     int z_alloc = 10;
497     char** zips = malloc(z_alloc * sizeof(char*));
498     zips[0] = strdup("../");
499 
500     while ((de = readdir(d)) != NULL) {
501         int name_len = strlen(de->d_name);
502 
503         if (de->d_type == DT_DIR) {
504             // skip "." and ".." entries
505             if (name_len == 1 && de->d_name[0] == '.') continue;
506             if (name_len == 2 && de->d_name[0] == '.' &&
507                 de->d_name[1] == '.') continue;
508 
509             if (d_size >= d_alloc) {
510                 d_alloc *= 2;
511                 dirs = realloc(dirs, d_alloc * sizeof(char*));
512             }
513             dirs[d_size] = malloc(name_len + 2);
514             strcpy(dirs[d_size], de->d_name);
515             dirs[d_size][name_len] = '/';
516             dirs[d_size][name_len+1] = '\0';
517             ++d_size;
518         } else if (de->d_type == DT_REG &&
519                    name_len >= 4 &&
520                    strncasecmp(de->d_name + (name_len-4), ".zip", 4) == 0) {
521             if (z_size >= z_alloc) {
522                 z_alloc *= 2;
523                 zips = realloc(zips, z_alloc * sizeof(char*));
524             }
525             zips[z_size++] = strdup(de->d_name);
526         }
527     }
528     closedir(d);
529 
530     qsort(dirs, d_size, sizeof(char*), compare_string);
531     qsort(zips, z_size, sizeof(char*), compare_string);
532 
533     // append dirs to the zips list
534     if (d_size + z_size + 1 > z_alloc) {
535         z_alloc = d_size + z_size + 1;
536         zips = realloc(zips, z_alloc * sizeof(char*));
537     }
538     memcpy(zips + z_size, dirs, d_size * sizeof(char*));
539     free(dirs);
540     z_size += d_size;
541     zips[z_size] = NULL;
542 
543     int result;
544     int chosen_item = 0;
545     do {
546         chosen_item = get_menu_selection(headers, zips, 1, chosen_item);
547 
548         char* item = zips[chosen_item];
549         int item_len = strlen(item);
550         if (chosen_item == 0) {          // item 0 is always "../"
551             // go up but continue browsing (if the caller is update_directory)
552             result = -1;
553             break;
554         } else if (item[item_len-1] == '/') {
555             // recurse down into a subdirectory
556             char new_path[PATH_MAX];
557             strlcpy(new_path, path, PATH_MAX);
558             strlcat(new_path, "/", PATH_MAX);
559             strlcat(new_path, item, PATH_MAX);
560             new_path[strlen(new_path)-1] = '\0';  // truncate the trailing '/'
561             result = update_directory(new_path, unmount_when_done);
562             if (result >= 0) break;
563         } else {
564             // selected a zip file:  attempt to install it, and return
565             // the status to the caller.
566             char new_path[PATH_MAX];
567             strlcpy(new_path, path, PATH_MAX);
568             strlcat(new_path, "/", PATH_MAX);
569             strlcat(new_path, item, PATH_MAX);
570 
571             ui_print("\n-- Install %s ...\n", path);
572             set_sdcard_update_bootloader_message();
573             char* copy = copy_sideloaded_package(new_path);
574             if (unmount_when_done != NULL) {
575                 ensure_path_unmounted(unmount_when_done);
576             }
577             if (copy) {
578                 result = install_package(copy);
579                 free(copy);
580             } else {
581                 result = INSTALL_ERROR;
582             }
583             break;
584         }
585     } while (true);
586 
587     int i;
588     for (i = 0; i < z_size; ++i) free(zips[i]);
589     free(zips);
590     free(headers);
591 
592     if (unmount_when_done != NULL) {
593         ensure_path_unmounted(unmount_when_done);
594     }
595     return result;
596 }
597 
598 static void
wipe_data(int confirm)599 wipe_data(int confirm) {
600     if (confirm) {
601         static char** title_headers = NULL;
602 
603         if (title_headers == NULL) {
604             char* headers[] = { "Confirm wipe of all user data?",
605                                 "  THIS CAN NOT BE UNDONE.",
606                                 "",
607                                 NULL };
608             title_headers = prepend_title((const char**)headers);
609         }
610 
611         char* items[] = { " No",
612                           " No",
613                           " No",
614                           " No",
615                           " No",
616                           " No",
617                           " No",
618                           " Yes -- delete all user data",   // [7]
619                           " No",
620                           " No",
621                           " No",
622                           NULL };
623 
624         int chosen_item = get_menu_selection(title_headers, items, 1, 0);
625         if (chosen_item != 7) {
626             return;
627         }
628     }
629 
630     ui_print("\n-- Wiping data...\n");
631     device_wipe_data();
632     erase_volume("/data");
633     erase_volume("/cache");
634     ui_print("Data wipe complete.\n");
635 }
636 
637 static void
prompt_and_wait()638 prompt_and_wait() {
639     char** headers = prepend_title((const char**)MENU_HEADERS);
640 
641     for (;;) {
642         finish_recovery(NULL);
643         ui_reset_progress();
644 
645         int chosen_item = get_menu_selection(headers, MENU_ITEMS, 0, 0);
646 
647         // device-specific code may take some action here.  It may
648         // return one of the core actions handled in the switch
649         // statement below.
650         chosen_item = device_perform_action(chosen_item);
651 
652         int status;
653         switch (chosen_item) {
654             case ITEM_REBOOT:
655                 return;
656 
657             case ITEM_WIPE_DATA:
658                 wipe_data(ui_text_visible());
659                 if (!ui_text_visible()) return;
660                 break;
661 
662             case ITEM_WIPE_CACHE:
663                 ui_print("\n-- Wiping cache...\n");
664                 erase_volume("/cache");
665                 ui_print("Cache wipe complete.\n");
666                 if (!ui_text_visible()) return;
667                 break;
668 
669             case ITEM_APPLY_SDCARD:
670                 status = update_directory(SDCARD_ROOT, SDCARD_ROOT);
671                 if (status >= 0) {
672                     if (status != INSTALL_SUCCESS) {
673                         ui_set_background(BACKGROUND_ICON_ERROR);
674                         ui_print("Installation aborted.\n");
675                     } else if (!ui_text_visible()) {
676                         return;  // reboot if logs aren't visible
677                     } else {
678                         ui_print("\nInstall from sdcard complete.\n");
679                     }
680                 }
681                 break;
682             case ITEM_APPLY_CACHE:
683                 // Don't unmount cache at the end of this.
684                 status = update_directory(CACHE_ROOT, NULL);
685                 if (status >= 0) {
686                     if (status != INSTALL_SUCCESS) {
687                         ui_set_background(BACKGROUND_ICON_ERROR);
688                         ui_print("Installation aborted.\n");
689                     } else if (!ui_text_visible()) {
690                         return;  // reboot if logs aren't visible
691                     } else {
692                         ui_print("\nInstall from cache complete.\n");
693                     }
694                 }
695                 break;
696 
697         }
698     }
699 }
700 
701 static void
print_property(const char * key,const char * name,void * cookie)702 print_property(const char *key, const char *name, void *cookie) {
703     printf("%s=%s\n", key, name);
704 }
705 
706 int
main(int argc,char ** argv)707 main(int argc, char **argv) {
708     time_t start = time(NULL);
709 
710     // If these fail, there's not really anywhere to complain...
711     freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);
712     freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
713     printf("Starting recovery on %s", ctime(&start));
714 
715     device_ui_init(&ui_parameters);
716     ui_init();
717     ui_set_background(BACKGROUND_ICON_INSTALLING);
718     load_volume_table();
719     get_args(&argc, &argv);
720 
721     int previous_runs = 0;
722     const char *send_intent = NULL;
723     const char *update_package = NULL;
724     int wipe_data = 0, wipe_cache = 0;
725 
726     int arg;
727     while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
728         switch (arg) {
729         case 'p': previous_runs = atoi(optarg); break;
730         case 's': send_intent = optarg; break;
731         case 'u': update_package = optarg; break;
732         case 'w': wipe_data = wipe_cache = 1; break;
733         case 'c': wipe_cache = 1; break;
734         case 't': ui_show_text(1); break;
735         case '?':
736             LOGE("Invalid command argument\n");
737             continue;
738         }
739     }
740 
741     device_recovery_start();
742 
743     printf("Command:");
744     for (arg = 0; arg < argc; arg++) {
745         printf(" \"%s\"", argv[arg]);
746     }
747     printf("\n");
748 
749     if (update_package) {
750         // For backwards compatibility on the cache partition only, if
751         // we're given an old 'root' path "CACHE:foo", change it to
752         // "/cache/foo".
753         if (strncmp(update_package, "CACHE:", 6) == 0) {
754             int len = strlen(update_package) + 10;
755             char* modified_path = malloc(len);
756             strlcpy(modified_path, "/cache/", len);
757             strlcat(modified_path, update_package+6, len);
758             printf("(replacing path \"%s\" with \"%s\")\n",
759                    update_package, modified_path);
760             update_package = modified_path;
761         }
762     }
763     printf("\n");
764 
765     property_list(print_property, NULL);
766     printf("\n");
767 
768     int status = INSTALL_SUCCESS;
769 
770     if (update_package != NULL) {
771         status = install_package(update_package);
772         if (status != INSTALL_SUCCESS) ui_print("Installation aborted.\n");
773     } else if (wipe_data) {
774         if (device_wipe_data()) status = INSTALL_ERROR;
775         if (erase_volume("/data")) status = INSTALL_ERROR;
776         if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
777         if (status != INSTALL_SUCCESS) ui_print("Data wipe failed.\n");
778     } else if (wipe_cache) {
779         if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
780         if (status != INSTALL_SUCCESS) ui_print("Cache wipe failed.\n");
781     } else {
782         status = INSTALL_ERROR;  // No command specified
783     }
784 
785     if (status != INSTALL_SUCCESS) ui_set_background(BACKGROUND_ICON_ERROR);
786     if (status != INSTALL_SUCCESS || ui_text_visible()) {
787         prompt_and_wait();
788     }
789 
790     // Otherwise, get ready to boot the main system...
791     finish_recovery(send_intent);
792     ui_print("Rebooting...\n");
793     android_reboot(ANDROID_RB_RESTART, 0, 0);
794     return EXIT_SUCCESS;
795 }
796