Home | History | Annotate | Download | only in recovery
      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   { "locale", required_argument, NULL, 'l' },
     58   { NULL, 0, NULL, 0 },
     59 };
     60 
     61 static const char *COMMAND_FILE = "/cache/recovery/command";
     62 static const char *INTENT_FILE = "/cache/recovery/intent";
     63 static const char *LOG_FILE = "/cache/recovery/log";
     64 static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
     65 static const char *LAST_INSTALL_FILE = "/cache/recovery/last_install";
     66 static const char *LOCALE_FILE = "/cache/recovery/last_locale";
     67 static const char *CACHE_ROOT = "/cache";
     68 static const char *SDCARD_ROOT = "/sdcard";
     69 static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log";
     70 static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
     71 static const char *SIDELOAD_TEMP_DIR = "/tmp/sideload";
     72 
     73 RecoveryUI* ui = NULL;
     74 char* 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 
    135 static const int MAX_ARG_LENGTH = 4096;
    136 static const int MAX_ARGS = 100;
    137 
    138 // open a given path, mounting partitions as necessary
    139 FILE*
    140 fopen_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
    155 static void
    156 check_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)
    166 static void
    167 get_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 
    228 static void
    229 set_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.
    238 static long tmplog_offset = 0;
    239 
    240 static void
    241 copy_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.
    267 static void
    268 finish_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 
    316 static int
    317 erase_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 
    334 static char*
    335 copy_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 
    424 static const char**
    425 prepend_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 
    447 static int
    448 get_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 
    499 static int compare_string(const void* a, const void* b) {
    500     return strcmp(*(const char**)a, *(const char**)b);
    501 }
    502 
    503 static int
    504 update_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 
    631 static void
    632 wipe_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 
    670 static void
    671 prompt_and_wait(Device* device, int status) {
    672     const char* const* headers = prepend_title(device->GetMenuHeaders());
    673 
    674     for (;;) {
    675         finish_recovery(NULL);
    676         switch (status) {
    677             case INSTALL_SUCCESS:
    678             case INSTALL_NONE:
    679                 ui->SetBackground(RecoveryUI::NO_COMMAND);
    680                 break;
    681 
    682             case INSTALL_ERROR:
    683             case INSTALL_CORRUPT:
    684                 ui->SetBackground(RecoveryUI::ERROR);
    685                 break;
    686         }
    687         ui->SetProgressType(RecoveryUI::EMPTY);
    688 
    689         int chosen_item = get_menu_selection(headers, device->GetMenuItems(), 0, 0, device);
    690 
    691         // device-specific code may take some action here.  It may
    692         // return one of the core actions handled in the switch
    693         // statement below.
    694         chosen_item = device->InvokeMenuItem(chosen_item);
    695 
    696         int wipe_cache;
    697         switch (chosen_item) {
    698             case Device::REBOOT:
    699                 return;
    700 
    701             case Device::WIPE_DATA:
    702                 wipe_data(ui->IsTextVisible(), device);
    703                 if (!ui->IsTextVisible()) return;
    704                 break;
    705 
    706             case Device::WIPE_CACHE:
    707                 ui->ShowText(false);
    708                 ui->Print("\n-- Wiping cache...\n");
    709                 erase_volume("/cache");
    710                 ui->Print("Cache wipe complete.\n");
    711                 if (!ui->IsTextVisible()) return;
    712                 break;
    713 
    714             case Device::APPLY_EXT:
    715                 // Some packages expect /cache to be mounted (eg,
    716                 // standard incremental packages expect to use /cache
    717                 // as scratch space).
    718                 ensure_path_mounted(CACHE_ROOT);
    719                 status = update_directory(SDCARD_ROOT, SDCARD_ROOT, &wipe_cache, device);
    720                 if (status == INSTALL_SUCCESS && wipe_cache) {
    721                     ui->Print("\n-- Wiping cache (at package request)...\n");
    722                     if (erase_volume("/cache")) {
    723                         ui->Print("Cache wipe failed.\n");
    724                     } else {
    725                         ui->Print("Cache wipe complete.\n");
    726                     }
    727                 }
    728                 if (status >= 0) {
    729                     if (status != INSTALL_SUCCESS) {
    730                         ui->SetBackground(RecoveryUI::ERROR);
    731                         ui->Print("Installation aborted.\n");
    732                     } else if (!ui->IsTextVisible()) {
    733                         return;  // reboot if logs aren't visible
    734                     } else {
    735                         ui->Print("\nInstall from sdcard complete.\n");
    736                     }
    737                 }
    738                 break;
    739 
    740             case Device::APPLY_CACHE:
    741                 // Don't unmount cache at the end of this.
    742                 status = update_directory(CACHE_ROOT, NULL, &wipe_cache, device);
    743                 if (status == INSTALL_SUCCESS && wipe_cache) {
    744                     ui->Print("\n-- Wiping cache (at package request)...\n");
    745                     if (erase_volume("/cache")) {
    746                         ui->Print("Cache wipe failed.\n");
    747                     } else {
    748                         ui->Print("Cache wipe complete.\n");
    749                     }
    750                 }
    751                 if (status >= 0) {
    752                     if (status != INSTALL_SUCCESS) {
    753                         ui->SetBackground(RecoveryUI::ERROR);
    754                         ui->Print("Installation aborted.\n");
    755                     } else if (!ui->IsTextVisible()) {
    756                         return;  // reboot if logs aren't visible
    757                     } else {
    758                         ui->Print("\nInstall from cache complete.\n");
    759                     }
    760                 }
    761                 break;
    762 
    763             case Device::APPLY_ADB_SIDELOAD:
    764                 ensure_path_mounted(CACHE_ROOT);
    765                 status = apply_from_adb(ui, &wipe_cache, TEMPORARY_INSTALL_FILE);
    766                 if (status >= 0) {
    767                     if (status != INSTALL_SUCCESS) {
    768                         ui->SetBackground(RecoveryUI::ERROR);
    769                         ui->Print("Installation aborted.\n");
    770                     } else if (!ui->IsTextVisible()) {
    771                         return;  // reboot if logs aren't visible
    772                     } else {
    773                         ui->Print("\nInstall from ADB complete.\n");
    774                     }
    775                 }
    776                 break;
    777         }
    778     }
    779 }
    780 
    781 static void
    782 print_property(const char *key, const char *name, void *cookie) {
    783     printf("%s=%s\n", key, name);
    784 }
    785 
    786 static void
    787 load_locale_from_cache() {
    788     FILE* fp = fopen_path(LOCALE_FILE, "r");
    789     char buffer[80];
    790     if (fp != NULL) {
    791         fgets(buffer, sizeof(buffer), fp);
    792         int j = 0;
    793         unsigned int i;
    794         for (i = 0; i < sizeof(buffer) && buffer[i]; ++i) {
    795             if (!isspace(buffer[i])) {
    796                 buffer[j++] = buffer[i];
    797             }
    798         }
    799         buffer[j] = 0;
    800         locale = strdup(buffer);
    801         check_and_fclose(fp, LOCALE_FILE);
    802     }
    803 }
    804 
    805 int
    806 main(int argc, char **argv) {
    807     time_t start = time(NULL);
    808 
    809     // If these fail, there's not really anywhere to complain...
    810     freopen(TEMPORARY_LOG_FILE, "a", stdout); setbuf(stdout, NULL);
    811     freopen(TEMPORARY_LOG_FILE, "a", stderr); setbuf(stderr, NULL);
    812 
    813     // If this binary is started with the single argument "--adbd",
    814     // instead of being the normal recovery binary, it turns into kind
    815     // of a stripped-down version of adbd that only supports the
    816     // 'sideload' command.  Note this must be a real argument, not
    817     // anything in the command file or bootloader control block; the
    818     // only way recovery should be run with this argument is when it
    819     // starts a copy of itself from the apply_from_adb() function.
    820     if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
    821         adb_main();
    822         return 0;
    823     }
    824 
    825     printf("Starting recovery on %s", ctime(&start));
    826 
    827     load_volume_table();
    828     get_args(&argc, &argv);
    829 
    830     int previous_runs = 0;
    831     const char *send_intent = NULL;
    832     const char *update_package = NULL;
    833     int wipe_data = 0, wipe_cache = 0, show_text = 0;
    834     bool just_exit = false;
    835 
    836     int arg;
    837     while ((arg = getopt_long(argc, argv, "", OPTIONS, NULL)) != -1) {
    838         switch (arg) {
    839         case 'p': previous_runs = atoi(optarg); break;
    840         case 's': send_intent = optarg; break;
    841         case 'u': update_package = optarg; break;
    842         case 'w': wipe_data = wipe_cache = 1; break;
    843         case 'c': wipe_cache = 1; break;
    844         case 't': show_text = 1; break;
    845         case 'x': just_exit = true; break;
    846         case 'l': locale = optarg; break;
    847         case '?':
    848             LOGE("Invalid command argument\n");
    849             continue;
    850         }
    851     }
    852 
    853     if (locale == NULL) {
    854         load_locale_from_cache();
    855     }
    856     printf("locale is [%s]\n", locale);
    857 
    858     Device* device = make_device();
    859     ui = device->GetUI();
    860 
    861     ui->Init();
    862     ui->SetLocale(locale);
    863     ui->SetBackground(RecoveryUI::NONE);
    864     if (show_text) ui->ShowText(true);
    865 
    866 #ifdef HAVE_SELINUX
    867     struct selinux_opt seopts[] = {
    868       { SELABEL_OPT_PATH, "/file_contexts" }
    869     };
    870 
    871     sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);
    872 
    873     if (!sehandle) {
    874         fprintf(stderr, "Warning: No file_contexts\n");
    875         ui->Print("Warning:  No file_contexts\n");
    876     }
    877 #endif
    878 
    879     device->StartRecovery();
    880 
    881     printf("Command:");
    882     for (arg = 0; arg < argc; arg++) {
    883         printf(" \"%s\"", argv[arg]);
    884     }
    885     printf("\n");
    886 
    887     if (update_package) {
    888         // For backwards compatibility on the cache partition only, if
    889         // we're given an old 'root' path "CACHE:foo", change it to
    890         // "/cache/foo".
    891         if (strncmp(update_package, "CACHE:", 6) == 0) {
    892             int len = strlen(update_package) + 10;
    893             char* modified_path = (char*)malloc(len);
    894             strlcpy(modified_path, "/cache/", len);
    895             strlcat(modified_path, update_package+6, len);
    896             printf("(replacing path \"%s\" with \"%s\")\n",
    897                    update_package, modified_path);
    898             update_package = modified_path;
    899         }
    900     }
    901     printf("\n");
    902 
    903     property_list(print_property, NULL);
    904     printf("\n");
    905 
    906     int status = INSTALL_SUCCESS;
    907 
    908     if (update_package != NULL) {
    909         status = install_package(update_package, &wipe_cache, TEMPORARY_INSTALL_FILE);
    910         if (status == INSTALL_SUCCESS && wipe_cache) {
    911             if (erase_volume("/cache")) {
    912                 LOGE("Cache wipe (requested by package) failed.");
    913             }
    914         }
    915         if (status != INSTALL_SUCCESS) ui->Print("Installation aborted.\n");
    916     } else if (wipe_data) {
    917         if (device->WipeData()) status = INSTALL_ERROR;
    918         if (erase_volume("/data")) status = INSTALL_ERROR;
    919         if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
    920         if (status != INSTALL_SUCCESS) ui->Print("Data wipe failed.\n");
    921     } else if (wipe_cache) {
    922         if (wipe_cache && erase_volume("/cache")) status = INSTALL_ERROR;
    923         if (status != INSTALL_SUCCESS) ui->Print("Cache wipe failed.\n");
    924     } else if (!just_exit) {
    925         status = INSTALL_NONE;  // No command specified
    926         ui->SetBackground(RecoveryUI::NO_COMMAND);
    927     }
    928 
    929     if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) {
    930         ui->SetBackground(RecoveryUI::ERROR);
    931     }
    932     if (status != INSTALL_SUCCESS || ui->IsTextVisible()) {
    933         prompt_and_wait(device, status);
    934     }
    935 
    936     // Otherwise, get ready to boot the main system...
    937     finish_recovery(send_intent);
    938     ui->Print("Rebooting...\n");
    939     android_reboot(ANDROID_RB_RESTART, 0, 0);
    940     return EXIT_SUCCESS;
    941 }
    942