From 2f6c1807495319f1f174baf8b42b2867d1d1ccf2 Mon Sep 17 00:00:00 2001 From: Mark Salyzyn Date: Fri, 15 Feb 2019 07:45:26 -0800 Subject: [PATCH] adb: use /system/bin/remount command Replace direct logic in support for 'adb remount' with an exec out to /system/bin/remount to do the heavy lifting. Remount success and failure strings are reported by the adb remount service in response to the various reported errors, freeing up the remount command itself from the legacy of script expectations. Test: adb-remount-test.sh Bug: 122602260 Change-Id: I686fa465f463b881bbb38f709d780a95e463be80 --- adb/Android.bp | 8 + adb/daemon/remount_service.cpp | 366 +++--------------- adb/daemon/remount_service.h | 1 - .../set_verity_enable_state_service.cpp | 14 +- 4 files changed, 75 insertions(+), 314 deletions(-) diff --git a/adb/Android.bp b/adb/Android.bp index 38135781d..1e085a7b3 100644 --- a/adb/Android.bp +++ b/adb/Android.bp @@ -402,6 +402,14 @@ cc_library { "liblog", ], + product_variables: { + debuggable: { + required: [ + "remount", + ], + }, + }, + target: { android: { srcs: [ diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp index 5e6d416cc..7999ddcca 100644 --- a/adb/daemon/remount_service.cpp +++ b/adb/daemon/remount_service.cpp @@ -14,339 +14,81 @@ * limitations under the License. */ -#define TRACE_TAG ADB - -#include "sysdeps.h" - #include #include -#include -#include -#include -#include #include -#include -#include -#include +#include +#include #include -#include -#include #include -#include - -#include -#include -#include -#include -#include -#include #include "adb.h" #include "adb_io.h" #include "adb_unique_fd.h" -#include "adb_utils.h" -#include "set_verity_enable_state_service.h" - -using android::base::Realpath; -using android::fs_mgr::Fstab; -using android::fs_mgr::ReadDefaultFstab; - -// Returns the last device used to mount a directory in /proc/mounts. -// This will find overlayfs entry where upperdir=lowerdir, to make sure -// remount is associated with the correct directory. -static std::string find_proc_mount(const char* dir) { - std::unique_ptr fp(setmntent("/proc/mounts", "r"), endmntent); - std::string mnt_fsname; - if (!fp) return mnt_fsname; - - // dir might be a symlink, e.g., /product -> /system/product in GSI. - std::string canonical_path; - if (!Realpath(dir, &canonical_path)) { - PLOG(ERROR) << "Realpath failed: " << dir; - } - - mntent* e; - while ((e = getmntent(fp.get())) != nullptr) { - if (canonical_path == e->mnt_dir) { - mnt_fsname = e->mnt_fsname; - } - } - return mnt_fsname; -} - -// Returns the device used to mount a directory in the fstab. -static std::string find_fstab_mount(const char* dir) { - Fstab fstab; - if (!ReadDefaultFstab(&fstab)) { - return ""; - } - - auto entry = std::find_if(fstab.begin(), fstab.end(), - [&dir](const auto& entry) { return entry.mount_point == dir; }); - if (entry == fstab.end()) { - return ""; - } - if (entry->fs_mgr_flags.logical) { - fs_mgr_update_logical_partition(&(*entry)); - } - return entry->blk_device; -} - -// The proc entry for / is full of lies, so check fstab instead. -// /proc/mounts lists rootfs and /dev/root, neither of which is what we want. -static std::string find_mount(const char* dir, bool is_root) { - if (is_root) { - return find_fstab_mount(dir); - } else { - return find_proc_mount(dir); - } -} - -bool dev_is_overlayfs(const std::string& dev) { - return (dev == "overlay") || (dev == "overlayfs"); -} - -bool make_block_device_writable(const std::string& dev) { - if (dev_is_overlayfs(dev)) return true; - int fd = unix_open(dev, O_RDONLY | O_CLOEXEC); - if (fd == -1) { - return false; - } - - int OFF = 0; - bool result = (ioctl(fd, BLKROSET, &OFF) != -1); - unix_close(fd); - return result; -} - -static bool can_unshare_blocks(int fd, const char* dev) { - const char* E2FSCK_BIN = "/system/bin/e2fsck"; - if (access(E2FSCK_BIN, X_OK)) { - WriteFdFmt(fd, "e2fsck is not available, cannot undo deduplication on %s\n", dev); - return false; - } - - pid_t child; - char* env[] = {nullptr}; - const char* argv[] = {E2FSCK_BIN, "-n", "-E", "unshare_blocks", dev, nullptr}; - if (posix_spawn(&child, E2FSCK_BIN, nullptr, nullptr, const_cast(argv), env)) { - WriteFdFmt(fd, "failed to e2fsck to check deduplication: %s\n", strerror(errno)); - return false; - } - int status = 0; - int ret = TEMP_FAILURE_RETRY(waitpid(child, &status, 0)); - if (ret < 0) { - WriteFdFmt(fd, "failed to get e2fsck status: %s\n", strerror(errno)); - return false; - } - if (!WIFEXITED(status)) { - WriteFdFmt(fd, "e2fsck exited abnormally with status %d\n", status); - return false; - } - int rc = WEXITSTATUS(status); - if (rc != 0) { - WriteFdFmt(fd, - "%s is deduplicated, and an e2fsck check failed. It might not " - "have enough free-space to be remounted as writable.\n", - dev); - return false; - } - return true; -} - -static unsigned long get_mount_flags(int fd, const char* dir) { - struct statvfs st_vfs; - if (statvfs(dir, &st_vfs) == -1) { - // Even though we could not get the original mount flags, assume that - // the mount was originally read-only. - WriteFdFmt(fd, "statvfs of the %s mount failed: %s.\n", dir, strerror(errno)); - return MS_RDONLY; - } - return st_vfs.f_flag; -} - -static bool remount_partition(int fd, const char* dir) { - if (!directory_exists(dir)) { - return true; - } - bool is_root = strcmp(dir, "/") == 0; - if (is_root && dev_is_overlayfs(find_mount("/system", false))) { - dir = "/system"; - is_root = false; - } - std::string dev = find_mount(dir, is_root); - if (is_root && dev.empty()) { - // The fstab entry will be /system if the device switched roots during - // first-stage init. - dev = find_mount("/system", true); - } - // Even if the device for the root is not found, we still try to remount it - // as rw. This typically only happens when running Android in a container: - // the root will almost always be in a loop device, which is dynamic, so - // it's not convenient to put in the fstab. - if (dev.empty() && !is_root) { - return true; - } - if (!dev.empty() && !make_block_device_writable(dev)) { - WriteFdFmt(fd, "remount of %s failed; couldn't make block device %s writable: %s\n", - dir, dev.c_str(), strerror(errno)); - return false; - } - - unsigned long remount_flags = get_mount_flags(fd, dir); - remount_flags &= ~MS_RDONLY; - remount_flags |= MS_REMOUNT; - - if (mount(dev.c_str(), dir, "none", remount_flags | MS_BIND, nullptr) == -1) { - // This is useful for cases where the superblock is already marked as - // read-write, but the mount itself is read-only, such as containers - // where the remount with just MS_REMOUNT is forbidden by the kernel. - WriteFdFmt(fd, "remount of the %s mount failed: %s.\n", dir, strerror(errno)); - return false; - } - if (mount(dev.c_str(), dir, "none", MS_REMOUNT, nullptr) == -1) { - 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()); -} - -static void try_unmount_bionic(int fd) { - static constexpr const char* kBionic = "/bionic"; - struct statfs buf; - if (statfs(kBionic, &buf) == -1) { - WriteFdFmt(fd, "statfs of the %s mount failed: %s.\n", kBionic, strerror(errno)); - return; - } - if (buf.f_flags & ST_RDONLY) { - // /bionic is on a read-only partition; can happen for - // non-system-as-root-devices. Don' try to unmount. - return; - } - // Success/Fail of the actual remount will be reported by the function. - remount_partition(fd, kBionic); - return; -} void remount_service(unique_fd fd, const std::string& cmd) { - bool user_requested_reboot = cmd == "-R"; + static constexpr char remount_cmd[] = "/system/bin/remount"; + static constexpr char remount_failed[] = "remount failed\n"; if (getuid() != 0) { WriteFdExactly(fd.get(), "Not running as root. Try \"adb root\" first.\n"); + WriteFdExactly(fd.get(), remount_failed); return; } - bool system_verified = !(android::base::GetProperty("partition.system.verified", "").empty()); - bool vendor_verified = !(android::base::GetProperty("partition.vendor.verified", "").empty()); - - std::vector partitions{"/", "/odm", "/oem", "/product_services", - "/product", "/vendor"}; - - if (system_verified || vendor_verified) { - // Disable verity automatically (reboot will be required). - set_verity_enabled_state_service(unique_fd(dup(fd.get())), false); - - // If overlayfs is not supported, we try and remount or set up - // un-deduplication. If it is supported, we can go ahead and wait for - // a reboot. - if (fs_mgr_overlayfs_valid() != OverlayfsValidResult::kNotSupported) { - if (user_requested_reboot) { - if (android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot")) { - WriteFdExactly(fd.get(), "rebooting device\n"); - } else { - WriteFdExactly(fd.get(), "reboot failed\n"); - } - } - return; - } - } else if (fs_mgr_overlayfs_setup()) { - // If we can use overlayfs, lets get it in place first before we - // struggle with determining deduplication operations. - Fstab fstab; - if (ReadDefaultFstab(&fstab) && fs_mgr_overlayfs_mount_all(&fstab)) { - WriteFdExactly(fd.get(), "overlayfs mounted\n"); - } - } - - // If overlayfs is supported, we don't bother trying to un-deduplicate - // partitions. - std::set dedup; - if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) { - // Find partitions that are deduplicated, and can be un-deduplicated. - for (const auto& part : partitions) { - auto partition = part; - if ((part == "/") && !find_mount("/system", false).empty()) partition = "/system"; - std::string dev = find_mount(partition.c_str(), partition == "/"); - if (dev.empty() || !fs_mgr_has_shared_blocks(partition, dev)) { - continue; - } - if (can_unshare_blocks(fd.get(), dev.c_str())) { - dedup.emplace(partition); - } - } - - // Reboot now if the user requested it (and an operation needs a reboot). - if (user_requested_reboot) { - if (!dedup.empty()) { - reboot_for_remount(fd.get(), !dedup.empty()); - return; - } - WriteFdExactly(fd.get(), "No reboot needed, skipping -R.\n"); - } - } - - bool success = true; - 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.get(), partition.c_str()); - } - - if (!dedup.empty()) { - WriteFdExactly(fd.get(), - "The following partitions are deduplicated and cannot " - "yet be remounted:\n"); - for (const std::string& name : dedup) { - WriteFdFmt(fd.get(), " %s\n", name.c_str()); - } - - WriteFdExactly(fd.get(), - "To reboot and un-deduplicate the listed partitions, " - "please retry with adb remount -R.\n"); - if (system_verified || vendor_verified) { - WriteFdExactly(fd.get(), "Note: verity will be automatically disabled after reboot.\n"); - } + auto pid = vfork(); + if (pid < 0) { + WriteFdFmt(fd.get(), "Failed to fork to %s: %s\n", remount_cmd, strerror(errno)); + WriteFdExactly(fd.get(), remount_failed); return; } - try_unmount_bionic(fd.get()); + if (pid == 0) { + // child side of the fork + fcntl(fd.get(), F_SETFD, 0); + dup2(fd.get(), STDIN_FILENO); + dup2(fd.get(), STDOUT_FILENO); + dup2(fd.get(), STDERR_FILENO); - if (!success) { - WriteFdExactly(fd.get(), "remount failed\n"); - } else { - WriteFdExactly(fd.get(), "remount succeeded\n"); + execl(remount_cmd, remount_cmd, cmd.empty() ? nullptr : cmd.c_str(), nullptr); + _exit(-errno ?: 42); } + + int wstatus = 0; + auto ret = waitpid(pid, &wstatus, 0); + + if (ret == -1) { + WriteFdFmt(fd.get(), "Failed to wait for %s: %s\n", remount_cmd, strerror(errno)); + goto err; + } + + if (ret != pid) { + WriteFdFmt(fd.get(), "pid %d and waitpid return %d do not match for %s\n", + static_cast(pid), static_cast(ret), remount_cmd); + goto err; + } + + if (WIFSIGNALED(wstatus)) { + WriteFdFmt(fd.get(), "%s terminated with signal %s\n", remount_cmd, + strsignal(WTERMSIG(wstatus))); + goto err; + } + + if (!WIFEXITED(wstatus)) { + WriteFdFmt(fd.get(), "%s stopped with status 0x%x\n", remount_cmd, wstatus); + goto err; + } + + if (WEXITSTATUS(wstatus)) { + WriteFdFmt(fd.get(), "%s exited with status %d\n", remount_cmd, + static_cast(WEXITSTATUS(wstatus))); + goto err; + } + + WriteFdExactly(fd.get(), "remount succeeded\n"); + return; + +err: + WriteFdExactly(fd.get(), remount_failed); } diff --git a/adb/daemon/remount_service.h b/adb/daemon/remount_service.h index c84740315..522a5da26 100644 --- a/adb/daemon/remount_service.h +++ b/adb/daemon/remount_service.h @@ -21,6 +21,5 @@ #include "adb_unique_fd.h" #if defined(__ANDROID__) -bool make_block_device_writable(const std::string&); void remount_service(unique_fd, const std::string&); #endif diff --git a/adb/daemon/set_verity_enable_state_service.cpp b/adb/daemon/set_verity_enable_state_service.cpp index 92851c016..658261ec8 100644 --- a/adb/daemon/set_verity_enable_state_service.cpp +++ b/adb/daemon/set_verity_enable_state_service.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -37,7 +38,6 @@ #include "adb.h" #include "adb_io.h" #include "adb_unique_fd.h" -#include "remount_service.h" #include "fec/io.h" @@ -51,6 +51,18 @@ void suggest_run_adb_root(int fd) { if (getuid() != 0) WriteFdExactly(fd, "Maybe run adb root?\n"); } +static bool make_block_device_writable(const std::string& dev) { + int fd = unix_open(dev, O_RDONLY | O_CLOEXEC); + if (fd == -1) { + return false; + } + + int OFF = 0; + bool result = (ioctl(fd, BLKROSET, &OFF) != -1); + unix_close(fd); + return result; +} + /* Turn verity on/off */ static bool set_verity_enabled_state(int fd, const char* block_device, const char* mount_point, bool enable) {