summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--device.cpp8
-rw-r--r--device.h12
-rw-r--r--recovery.cpp37
-rw-r--r--roots.cpp87
-rw-r--r--roots.h2
5 files changed, 132 insertions, 14 deletions
diff --git a/device.cpp b/device.cpp
index 46ee880..1f6bac7 100644
--- a/device.cpp
+++ b/device.cpp
@@ -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;
}
}
diff --git a/device.h b/device.h
index d761d2a..c11f52e 100644
--- a/device.h
+++ b/device.h
@@ -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
diff --git a/roots.cpp b/roots.cpp
index b4860d7..fda8f61 100644
--- a/roots.cpp
+++ b/roots.cpp
@@ -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) {
diff --git a/roots.h b/roots.h
index e38707e..9b369c0 100644
--- a/roots.h
+++ b/roots.h
@@ -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);