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