Merge changes from topic "move_bionic_to_apex3" am: a15f385821 am: 90b7095cf5
am: 7ce93d2d0d

Change-Id: I670493883a18e604bb3cbc80979c76d0a725c0ef
diff --git a/adb/daemon/file_sync_service.cpp b/adb/daemon/file_sync_service.cpp
index 56b5cd8..9e1760d 100644
--- a/adb/daemon/file_sync_service.cpp
+++ b/adb/daemon/file_sync_service.cpp
@@ -25,6 +25,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -209,6 +210,22 @@
     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<std::string> 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 @@
     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 @@
     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<char> 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 b26c691..c36f1b6 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -230,6 +230,23 @@
     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 @@
         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 ede0122..4d9bc61 100755
--- a/fs_mgr/tests/adb-remount-test.sh
+++ b/fs_mgr/tests/adb-remount-test.sh
@@ -569,6 +569,18 @@
   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 @@
 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 2>/dev/null`