From 07445f4a71a3d0139cf0e7017e2b039a8e21c824 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 17 May 2018 12:12:54 -0700 Subject: [PATCH] adb: add remount -R for deduplicated ext4 When using "adb remount" on a deduplicated filesystem, the current response is a warning that the remount will not work. This patch allows the user to specify an -R option. This option will reboot to recovery, run e2fsck to undo deduplication, and then reboot the device where "adb remount" will then succeed. In addition, if verity needs to be disabled to remount, it will be disabled in the same reboot cycle to minimize reboots. Bug: 64109868 Test: adb remount -R on a deduplicated filesystem Change-Id: I812407499b2df6f4d2509e8d51878117108a6849 --- adb/daemon/remount_service.cpp | 120 +++++++++++++++++++++++++-------- adb/services.cpp | 4 +- 2 files changed, 94 insertions(+), 30 deletions(-) diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp index eb4690377..830b35d63 100644 --- a/adb/daemon/remount_service.cpp +++ b/adb/daemon/remount_service.cpp @@ -29,10 +29,13 @@ #include #include +#include #include #include #include +#include +#include #include #include "adb.h" @@ -142,7 +145,7 @@ static bool can_unshare_blocks(int fd, const char* dev) { return true; } -static bool remount_partition(int fd, const char* dir, std::vector& dedup) { +static bool remount_partition(int fd, const char* dir) { if (!directory_exists(dir)) { return true; } @@ -168,32 +171,83 @@ static bool remount_partition(int fd, const char* dir, std::vector& return false; } if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) { - if (errno == EROFS && fs_has_shared_blocks(dev.c_str())) { - if (!can_unshare_blocks(fd, dev.c_str())) { - return false; - } - // We return true so remount_service() can detect that the only - // failure was deduplicated filesystems. - dedup.push_back(dev); - return true; - } WriteFdFmt(fd, "remount of the %s superblock failed: %s\n", dir, strerror(errno)); return false; } return true; } +static void reboot_for_remount(int fd, bool need_fsck) { + std::string reboot_cmd = "reboot"; + if (need_fsck) { + const std::vector options = {"--fsck_unshare_blocks"}; + std::string err; + if (!write_bootloader_message(options, &err)) { + WriteFdFmt(fd, "Failed to set bootloader message: %s\n", err.c_str()); + return; + } + + WriteFdExactly(fd, + "The device will now reboot to recovery and attempt " + "un-deduplication.\n"); + reboot_cmd = "reboot,recovery"; + } + + sync(); + android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd.c_str()); +} + void remount_service(int fd, void* cookie) { + unique_fd close_fd(fd); + + const char* cmd = reinterpret_cast(cookie); + bool user_requested_reboot = cmd && !strcmp(cmd, "-R"); + if (getuid() != 0) { WriteFdExactly(fd, "Not running as root. Try \"adb root\" first.\n"); - adb_close(fd); return; } bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty()); bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty()); - if (system_verified || vendor_verified) { + std::vector partitions = {"/odm", "/oem", "/product", "/vendor"}; + if (android::base::GetBoolProperty("ro.build.system_root_image", false)) { + partitions.push_back("/"); + } else { + partitions.push_back("/system"); + } + + // Find partitions that are deduplicated, and can be un-deduplicated. + std::set dedup; + for (const auto& partition : partitions) { + std::string dev = find_mount(partition.c_str(), partition == "/"); + if (dev.empty() || !fs_has_shared_blocks(dev.c_str())) { + continue; + } + if (can_unshare_blocks(fd, dev.c_str())) { + dedup.emplace(partition); + } + } + + bool verity_enabled = (system_verified || vendor_verified); + + // Reboot now if the user requested it (and an operation needs a reboot). + if (user_requested_reboot) { + if (!dedup.empty() || verity_enabled) { + if (verity_enabled) { + set_verity_enabled_state_service(fd, nullptr); + } + reboot_for_remount(fd, !dedup.empty()); + return; + } + WriteFdExactly(fd, "No reboot needed, skipping -R.\n"); + } + + // If we need to disable-verity, but we also need to perform a recovery + // fsck for deduplicated partitions, hold off on warning about verity. We + // can handle both verity and the recovery fsck in the same reboot cycle. + if (verity_enabled && dedup.empty()) { // Allow remount but warn of likely bad effects bool both = system_verified && vendor_verified; WriteFdFmt(fd, @@ -206,32 +260,40 @@ void remount_service(int fd, void* cookie) { "Use \"adb disable-verity\" to disable verity.\n" "If you do not, remount may succeed, however, you will still " "not be able to write to these volumes.\n"); + WriteFdExactly(fd, + "Alternately, use \"adb remount -R\" to disable verity " + "and automatically reboot.\n"); } bool success = true; - std::vector dedup; - if (android::base::GetBoolProperty("ro.build.system_root_image", false)) { - success &= remount_partition(fd, "/", dedup); - } else { - success &= remount_partition(fd, "/system", dedup); + for (const auto& partition : partitions) { + // Don't try to remount partitions that need an fsck in recovery. + if (dedup.count(partition)) { + continue; + } + success &= remount_partition(fd, partition.c_str()); } - success &= remount_partition(fd, "/odm", dedup); - success &= remount_partition(fd, "/oem", dedup); - success &= remount_partition(fd, "/product", dedup); - success &= remount_partition(fd, "/vendor", dedup); - if (!success) { - WriteFdExactly(fd, "remount failed\n"); - } else if (dedup.empty()) { - WriteFdExactly(fd, "remount succeeded\n"); - } else { + if (!dedup.empty()) { WriteFdExactly(fd, - "The following partitions are deduplicated and could " - "not be remounted:\n"); + "The following partitions are deduplicated and cannot " + "yet be remounted:\n"); for (const std::string& name : dedup) { WriteFdFmt(fd, " %s\n", name.c_str()); } + + WriteFdExactly(fd, + "To reboot and un-deduplicate the listed partitions, " + "please retry with adb remount -R.\n"); + if (system_verified || vendor_verified) { + WriteFdExactly(fd, "Note: verity will be automatically disabled after reboot.\n"); + } + return; } - adb_close(fd); + if (!success) { + WriteFdExactly(fd, "remount failed\n"); + } else { + WriteFdExactly(fd, "remount succeeded\n"); + } } diff --git a/adb/services.cpp b/adb/services.cpp index 0b0c1612c..a757d9066 100644 --- a/adb/services.cpp +++ b/adb/services.cpp @@ -291,7 +291,9 @@ int service_to_fd(const char* name, atransport* transport) { } else if(!strncmp(name, "sync:", 5)) { ret = create_service_thread("sync", file_sync_service, nullptr); } else if(!strncmp(name, "remount:", 8)) { - ret = create_service_thread("remount", remount_service, nullptr); + const char* options = name + strlen("remount:"); + void* cookie = const_cast(reinterpret_cast(options)); + ret = create_service_thread("remount", remount_service, cookie); } else if(!strncmp(name, "reboot:", 7)) { void* arg = strdup(name + 7); if (arg == NULL) return -1;