diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp index 56b5cd8d9..9e1760db9 100644 --- a/adb/daemon/file_sync_service.cpp +++ b/adb/daemon/file_sync_service.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -209,6 +210,22 @@ done: return WriteFdExactly(s, &msg.dent, sizeof(msg.dent)); } +static bool is_mountpoint(const std::string& path, pid_t tid) { + const std::string mountinfo_path = "/proc/" + std::to_string(tid) + "/mountinfo"; + std::string mountinfo; + if (!android::base::ReadFileToString(mountinfo_path, &mountinfo)) { + PLOG(ERROR) << "Failed to open " << mountinfo_path; + return false; + } + std::vector lines = android::base::Split(mountinfo, "\n"); + return std::find_if(lines.begin(), lines.end(), [&path](const auto& line) { + auto tokens = android::base::Split(line, " "); + // line format is ... + // mountid parentmountid major:minor sourcepath targetpath option ... + return tokens.size() >= 4 && tokens[4] == path; + }) != lines.end(); +} + // Make sure that SendFail from adb_io.cpp isn't accidentally used in this file. #pragma GCC poison SendFail @@ -415,6 +432,18 @@ static bool do_send(int s, const std::string& spec, std::vector& buffer) { struct stat st; bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) || (S_ISLNK(st.st_mode) && !S_ISLNK(mode)); + + // If the path is a file that is a mount point, don't unlink it, but instead + // truncate to zero. If unlinked, existing mounts on the path is all + // unmounted + if (S_ISREG(st.st_mode) && is_mountpoint(path, getpid())) { + do_unlink = false; + if (truncate(path.c_str(), 0) == -1) { + SendSyncFail(s, "truncate to zero failed"); + return false; + } + } + if (do_unlink) { adb_unlink(path.c_str()); } @@ -546,7 +575,64 @@ static bool handle_sync_command(int fd, std::vector& buffer) { return true; } +#if defined(__ANDROID__) +class FileSyncPreparer { + public: + FileSyncPreparer() : saved_ns_fd_(-1), rooted_(getuid() == 0) { + const std::string namespace_path = "/proc/" + std::to_string(gettid()) + "/ns/mnt"; + const int ns_fd = adb_open(namespace_path.c_str(), O_RDONLY | O_CLOEXEC); + if (ns_fd == -1) { + if (rooted_) PLOG(ERROR) << "Failed to save mount namespace"; + return; + } + saved_ns_fd_.reset(ns_fd); + + // Note: this is for the current thread only + if (unshare(CLONE_NEWNS) != 0) { + if (rooted_) PLOG(ERROR) << "Failed to clone mount namespace"; + return; + } + + // Set the propagation type of / to private so that unmount below is + // not propagated to other mount namespaces. + if (mount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr) == -1) { + if (rooted_) PLOG(ERROR) << "Could not change propagation type of / to MS_PRIVATE"; + return; + } + + // unmount /bionic which is bind-mount to itself by init. Under /bionic, + // there are other bind mounts for the bionic files. By unmounting this, + // we unmount them all thus revealing the raw file system that is the + // same as the local file system seen by the adb client. + if (umount2("/bionic", MNT_DETACH) == -1 && errno != ENOENT) { + if (rooted_) PLOG(ERROR) << "Could not unmount /bionic to reveal raw filesystem"; + return; + } + } + + ~FileSyncPreparer() { + if (saved_ns_fd_.get() != -1) { + // In fact, this is not strictly required because this thread for file + // sync service will be destroyed after the current transfer is all + // done. However, let's restore the ns in case the same thread is + // reused by multiple transfers in the future refactoring. + if (setns(saved_ns_fd_, CLONE_NEWNS) == -1) { + PLOG(ERROR) << "Failed to restore saved mount namespace"; + } + } + } + + private: + unique_fd saved_ns_fd_; + bool rooted_; +}; +#endif + void file_sync_service(unique_fd fd) { +#if defined(__ANDROID__) + FileSyncPreparer preparer; +#endif + std::vector buffer(SYNC_DATA_MAX); while (handle_sync_command(fd.get(), buffer)) { diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp index b26c691ca..c36f1b6f9 100644 --- a/adb/daemon/remount_service.cpp +++ b/adb/daemon/remount_service.cpp @@ -230,6 +230,23 @@ static void reboot_for_remount(int fd, bool need_fsck) { 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"; @@ -323,6 +340,8 @@ void remount_service(unique_fd fd, const std::string& cmd) { return; } + try_unmount_bionic(fd.get()); + if (!success) { WriteFdExactly(fd.get(), "remount failed\n"); } else { diff --git a/fs_mgr/tests/adb-remount-test.sh b/fs_mgr/tests/adb-remount-test.sh index ede0122e0..4d9bc610f 100755 --- a/fs_mgr/tests/adb-remount-test.sh +++ b/fs_mgr/tests/adb-remount-test.sh @@ -569,6 +569,18 @@ B="`adb_cat /vendor/hello`" || die "vendor hello" check_eq "${A}" "${B}" /vendor before reboot +# download libc.so, append some gargage, push back, and check if the file is updated. +tempdir="`mktemp -d`" +cleanup() { + rm -rf ${tempdir} +} +adb pull /system/lib/bootstrap/libc.so ${tempdir} || die "pull libc.so from device" +garbage="`hexdump -n 16 -e '4/4 "%08X" 1 "\n"' /dev/random`" +echo ${garbage} >> ${tempdir}/libc.so +adb push ${tempdir}/libc.so /system/lib/bootstrap/libc.so || die "push libc.so to device" +adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice || die "pull libc.so from device" +diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null || die "libc.so differ" + echo "${GREEN}[ RUN ]${NORMAL} reboot to confirm content persistent" >&2 adb_reboot && @@ -607,6 +619,14 @@ adb_root && check_eq "${A}" "${B}" vendor after reboot echo "${GREEN}[ OK ]${NORMAL} /vendor content remains after reboot" >&2 +# check if the updated libc.so is persistent after reboot +adb_root && + adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice || + die "pull libc.so from device" +diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null || die "libc.so differ" +rm -r ${tempdir} +echo "${GREEN}[ OK ]${NORMAL} /system/lib/bootstrap/libc.so content remains after reboot" >&2 + echo "${GREEN}[ RUN ]${NORMAL} flash vendor, confirm its content disappears" >&2 H=`adb_sh echo '${HOSTNAME}' /dev/null`