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