Now for real unmount bind-mounts on top of /data

Approach in
https://android-review.googlesource.com/c/platform/system/core/+/1191457
depended on ordering of entries. Instead fs_mgr now tries to unmount
everything with the same block device as /data until timeout is reached.
This is very similar to what is done in UmountPartitions in reboot.cpp,
but I'm a little bit reluctant to merge them together, since the later
unmount all read-write partitions, while here we are only interested in
/data.

Test: on both coral and blueline:
Test: adb shell /system/bin/vdc checkpoint startCheckpoint 1
Test: adb shell setprop sys.init.userdata_remount.force_umount_f2fs 1
Test: adb reboot userspace
Test: verified that userdata was remounted
Bug: 135984674
Change-Id: I929a058fa2f9639f0a2b73e9ea269cf1e07db889
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index 5c0401e..48ce4cd 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -34,6 +34,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <functional>
 #include <map>
 #include <memory>
@@ -42,6 +43,7 @@
 #include <utility>
 #include <vector>
 
+#include <android-base/chrono_utils.h>
 #include <android-base/file.h>
 #include <android-base/properties.h>
 #include <android-base/stringprintf.h>
@@ -94,6 +96,7 @@
 using android::base::GetBoolProperty;
 using android::base::Realpath;
 using android::base::StartsWith;
+using android::base::Timer;
 using android::base::unique_fd;
 using android::dm::DeviceMapper;
 using android::dm::DmDeviceState;
@@ -1358,6 +1361,36 @@
     return ret;
 }
 
+static bool fs_mgr_unmount_all_data_mounts(const std::string& block_device) {
+    Timer t;
+    // TODO(b/135984674): should be configured via a read-only property.
+    std::chrono::milliseconds timeout = 5s;
+    while (true) {
+        bool umount_done = true;
+        Fstab proc_mounts;
+        if (!ReadFstabFromFile("/proc/mounts", &proc_mounts)) {
+            LERROR << "Can't read /proc/mounts";
+            return -1;
+        }
+        // Now proceed with other bind mounts on top of /data.
+        for (const auto& entry : proc_mounts) {
+            if (entry.blk_device == block_device) {
+                if (umount2(entry.mount_point.c_str(), 0) != 0) {
+                    umount_done = false;
+                }
+            }
+        }
+        if (umount_done) {
+            LINFO << "Unmounting /data took " << t;
+            return true;
+        }
+        if (t.duration() > timeout) {
+            return false;
+        }
+        std::this_thread::sleep_for(50ms);
+    }
+}
+
 // TODO(b/143970043): return different error codes based on which step failed.
 int fs_mgr_remount_userdata_into_checkpointing(Fstab* fstab) {
     Fstab proc_mounts;
@@ -1401,17 +1434,8 @@
         }
     } else {
         LINFO << "Unmounting /data before remounting into checkpointing mode";
-        // First make sure that all the bind-mounts on top of /data are unmounted.
-        for (const auto& entry : proc_mounts) {
-            if (entry.blk_device == block_device && entry.mount_point != "/data") {
-                LINFO << "Unmounting bind-mount " << entry.mount_point;
-                if (umount2(entry.mount_point.c_str(), UMOUNT_NOFOLLOW) != 0) {
-                    PWARNING << "Failed to unmount " << entry.mount_point;
-                }
-            }
-        }
-        if (umount2("/data", UMOUNT_NOFOLLOW) != 0) {
-            PERROR << "Failed to umount /data";
+        if (!fs_mgr_unmount_all_data_mounts(block_device)) {
+            LERROR << "Failed to umount /data";
             return -1;
         }
         DeviceMapper& dm = DeviceMapper::Instance();