diff options
-rw-r--r-- | device.cpp | 8 | ||||
-rw-r--r-- | device.h | 12 | ||||
-rw-r--r-- | recovery.cpp | 37 | ||||
-rw-r--r-- | roots.cpp | 87 | ||||
-rw-r--r-- | roots.h | 2 |
5 files changed, 132 insertions, 14 deletions
@@ -22,6 +22,7 @@ static const char* MENU_ITEMS[] = { "Apply update", "Wipe data/factory reset", "Wipe cache partition", + "Wipe media", "Mount /system", "View recovery logs", "Power off", @@ -41,9 +42,10 @@ Device::BuiltinAction Device::InvokeMenuItem(int menu_position) { case 2: return APPLY_UPDATE; case 3: return WIPE_DATA; case 4: return WIPE_CACHE; - case 5: return MOUNT_SYSTEM; - case 6: return VIEW_RECOVERY_LOGS; - case 7: return SHUTDOWN; + case 5: return WIPE_MEDIA; + case 6: return MOUNT_SYSTEM; + case 7: return VIEW_RECOVERY_LOGS; + case 8: return SHUTDOWN; default: return NO_ACTION; } } @@ -64,10 +64,11 @@ class Device : public VoldWatcher { // APPLY_ADB_SIDELOAD was 4. WIPE_DATA = 5, WIPE_CACHE = 6, - REBOOT_BOOTLOADER = 7, - SHUTDOWN = 8, - VIEW_RECOVERY_LOGS = 9, - MOUNT_SYSTEM = 10, + WIPE_MEDIA = 7, + REBOOT_BOOTLOADER = 8, + SHUTDOWN = 9, + VIEW_RECOVERY_LOGS = 10, + MOUNT_SYSTEM = 11, }; // Return the list of menu items (an array of strings, @@ -104,6 +105,9 @@ class Device : public VoldWatcher { virtual bool PreWipeData() { return true; } virtual bool PostWipeData() { return true; } + virtual bool PreWipeMedia() { return true; } + virtual bool PostWipeMedia() { return true; } + // Called before reboot virtual char const* GetRebootReason() { return ""; } diff --git a/recovery.cpp b/recovery.cpp index 5c482e0..d3dc398 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -66,6 +66,7 @@ static const struct option OPTIONS[] = { { "update_package", required_argument, NULL, 'u' }, { "wipe_data", no_argument, NULL, 'w' }, { "wipe_cache", no_argument, NULL, 'c' }, + { "wipe_media", no_argument, NULL, 'm' }, { "show_text", no_argument, NULL, 't' }, { "sideload", no_argument, NULL, 's' }, { "sideload_auto_reboot", no_argument, NULL, 'a' }, @@ -564,7 +565,9 @@ static bool erase_volume(const char* volume) { ui->Print("Formatting %s...\n", volume); - ensure_path_unmounted(volume); + if (volume[0] == '/') { + ensure_path_unmounted(volume); + } int result = format_volume(volume); if (is_cache) { @@ -765,7 +768,7 @@ static bool yes_no(Device* device, const char* question1, const char* question2) } // Return true on success. -static bool wipe_data(int should_confirm, Device* device) { +static bool wipe_data(int should_confirm, Device* device, bool force = false) { if (should_confirm && !yes_no(device, "Wipe all user data?", " THIS CAN NOT BE UNDONE!")) { return false; } @@ -775,13 +778,29 @@ static bool wipe_data(int should_confirm, Device* device) { ui->Print("\n-- Wiping data...\n"); bool success = device->PreWipeData() && - erase_volume("/data") && + erase_volume("/data", force) && erase_volume("/cache") && device->PostWipeData(); ui->Print("Data wipe %s.\n", success ? "complete" : "failed"); return success; } +static bool wipe_media(int should_confirm, Device* device) { + if (should_confirm && !yes_no(device, "Wipe all user media?", " THIS CAN NOT BE UNDONE!")) { + return false; + } + + modified_flash = true; + + ui->Print("\n-- Wiping media...\n"); + bool success = + device->PreWipeMedia() && + erase_volume("media") && + device->PostWipeMedia(); + ui->Print("Media wipe %s.\n", success ? "complete" : "failed"); + return success; +} + // Return true on success. static bool wipe_cache(bool should_confirm, Device* device) { if (should_confirm && !yes_no(device, "Wipe cache?", " THIS CAN NOT BE UNDONE!")) { @@ -992,6 +1011,11 @@ prompt_and_wait(Device* device, int status) { if (!ui->IsTextVisible()) return Device::NO_ACTION; break; + case Device::WIPE_MEDIA: + wipe_media(ui->IsTextVisible(), device); + if (!ui->IsTextVisible()) return Device::NO_ACTION; + break; + case Device::APPLY_UPDATE: { status = show_apply_update_menu(device); @@ -1211,6 +1235,7 @@ main(int argc, char **argv) { const char *update_package = NULL; bool should_wipe_data = false; bool should_wipe_cache = false; + bool should_wipe_media = false; bool show_text = false; bool sideload = false; bool sideload_auto_reboot = false; @@ -1334,13 +1359,17 @@ main(int argc, char **argv) { } } } else if (should_wipe_data) { - if (!wipe_data(false, device)) { + if (!wipe_data(false, device, should_wipe_media)) { status = INSTALL_ERROR; } } else if (should_wipe_cache) { if (!wipe_cache(false, device)) { status = INSTALL_ERROR; } + } else if (should_wipe_media) { + if (!wipe_media(false, device)) { + status = INSTALL_ERROR; + } } else if (sideload) { // 'adb reboot sideload' acts the same as user presses key combinations // to enter the sideload mode. When 'sideload-auto-reboot' is used, text @@ -309,7 +309,60 @@ static int exec_cmd(const char* path, char* const argv[]) { return WEXITSTATUS(status); } +static int rmtree_except(const char* path, const char* except) +{ + char pathbuf[PATH_MAX]; + int rc = 0; + DIR* dp = opendir(path); + if (dp == NULL) { + return -1; + } + struct dirent* de; + while ((de = readdir(dp)) != NULL) { + if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) + continue; + if (except && !strcmp(de->d_name, except)) + continue; + struct stat st; + snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path, de->d_name); + rc = lstat(pathbuf, &st); + if (rc != 0) { + LOGE("Failed to stat %s\n", pathbuf); + break; + } + if (S_ISDIR(st.st_mode)) { + rc = rmtree_except(pathbuf, NULL); + if (rc != 0) + break; + rc = rmdir(pathbuf); + } + else { + rc = unlink(pathbuf); + } + if (rc != 0) { + LOGI("Failed to remove %s: %s\n", pathbuf, strerror(errno)); + break; + } + } + closedir(dp); + return rc; +} + int format_volume(const char* volume) { + if (strcmp(volume, "media") == 0) { + if (!vdc->isEmulatedStorage()) { + return 0; + } + if (ensure_path_mounted("/data") != 0) { + LOGE("format_volume failed to mount /data\n"); + return -1; + } + int rc = 0; + rc = rmtree_except("/data/media", NULL); + ensure_path_unmounted("/data"); + return rc; + } + Volume* v = volume_for_path(volume); if (v == NULL) { LOGE("unknown volume \"%s\"\n", volume); @@ -325,6 +378,38 @@ int format_volume(const char* volume) { return -1; } + if (strcmp(volume, "/data") == 0 && vdc->isEmulatedStorage()) { + if (ensure_path_mounted("/data") == 0) { + // Preserve .layout_version to avoid "nesting bug" + LOGI("Preserving layout version\n"); + unsigned char layout_buf[256]; + ssize_t layout_buflen = -1; + int fd; + fd = open("/data/.layout_version", O_RDONLY); + if (fd != -1) { + layout_buflen = read(fd, layout_buf, sizeof(layout_buf)); + close(fd); + } + + int rc = rmtree_except("/data", "media"); + + // Restore .layout_version + if (layout_buflen > 0) { + LOGI("Restoring layout version\n"); + fd = open("/data/.layout_version", O_WRONLY | O_CREAT | O_EXCL, 0600); + if (fd != -1) { + write(fd, layout_buf, layout_buflen); + close(fd); + } + } + + ensure_path_unmounted(volume); + + return rc; + } + LOGE("format_volume failed to mount /data, formatting instead\n"); + } + if (ensure_path_unmounted(volume) != 0) { LOGE("format_volume failed to unmount \"%s\"\n", v->mount_point); return -1; @@ -431,7 +516,7 @@ int setup_install_mounts() { // datamedia and anything managed by vold must be unmounted // with the detach flag to ensure that FUSE works. bool detach = false; - if (is_data_media() && strcmp(v->mount_point, "/data") == 0) { + if (vdc->isEmulatedStorage() && strcmp(v->mount_point, "/data") == 0) { detach = true; } if (ensure_volume_unmounted(v, detach) != 0) { @@ -50,8 +50,6 @@ int setup_install_mounts(); int get_num_volumes(); -int is_data_media(); - bool volume_is_mountable(Volume *v); bool volume_is_readonly(Volume *v); bool volume_is_verity(Volume *v); |