fs_mgr_overlayfs: Support remounting submounts

The move mount motions logic are built on top of heuristics and allow
non-fatal failures.
If the overlay was setup and mounted without reboot (this could happen
if AVB is disabled), the mount state (especially the MS_SHARED and
MS_PRIVATE flags) can still get skewed somehow, due to unforeseen errors
or resource race.
It is always advised to reboot after initial overlay setup, as the
overlay mount logic is less likely to require moving submounts when
executed by `init`, this provides the greatest chance of success.

Below is an example of the expected outcome of remounting submounts:
[precondition]
* AVB is already disabled, so overlay is setup and mounted w/o reboot
* No existing overlay; is initial overlay setup

0. /proc/self/mountinfo would contain something like:
<id> <parent id> <mountpoint> <shared/private> <filesystem>
2    1           /product     shared:2         erofs
3    2           /product/app shared:3         erofs

1. adb remount /product/app
(note how the mount flag of <3> changes to private so <30> can be
MS_MOVE later)
2    1           /product     shared:2         erofs
3    2           /product/app private          erofs
30   3           /product/app shared:30        overlay

2. adb remount /product
(note how the parent of <30> changes to <40> as the result of MS_MOVE)
(note that <3> is _not_ moved)
2    1           /product     private          erofs
3    2           /product/app private          erofs
30   40          /product/app shared:30        overlay
40   2           /product     shared:40        overlay

Bug: 306124139
Test: adb-remount-test
Test: Verified with a remount submount scenario by editing the fstab.
Test: remount /system/bin then /system and verify the submount overlay
  (/system/bin) is moved under the parent mount overlay (/system).
Change-Id: I6cdbe8c52d826a6f03fd363c909ebb0005446b96
diff --git a/fs_mgr/fs_mgr_overlayfs_mount.cpp b/fs_mgr/fs_mgr_overlayfs_mount.cpp
index 66faecd..f149a31 100644
--- a/fs_mgr/fs_mgr_overlayfs_mount.cpp
+++ b/fs_mgr/fs_mgr_overlayfs_mount.cpp
@@ -381,32 +381,64 @@
 
     auto retval = true;
 
-    struct move_entry {
+    struct MoveEntry {
         std::string mount_point;
         std::string dir;
         bool shared_flag;
     };
-    std::vector<move_entry> move;
+
+    std::vector<MoveEntry> moved_mounts;
     auto parent_private = false;
     auto parent_made_private = false;
     auto dev_private = false;
     auto dev_made_private = false;
-    for (auto& entry : ReadMountinfoFromFile("/proc/self/mountinfo")) {
+
+    // There could be multiple mount entries with the same mountpoint.
+    // Group these entries together with stable_sort, and keep only the last entry of a group.
+    // Only move mount the last entry in an over mount group, because the other entries are
+    // overshadowed and only the filesystem mounted with the last entry participates in file
+    // pathname resolution.
+    auto mountinfo = ReadMountinfoFromFile("/proc/self/mountinfo");
+    std::stable_sort(mountinfo.begin(), mountinfo.end(), [](const auto& lhs, const auto& rhs) {
+        return lhs.mount_point < rhs.mount_point;
+    });
+    std::reverse(mountinfo.begin(), mountinfo.end());
+    auto erase_from = std::unique(
+            mountinfo.begin(), mountinfo.end(),
+            [](const auto& lhs, const auto& rhs) { return lhs.mount_point == rhs.mount_point; });
+    mountinfo.erase(erase_from, mountinfo.end());
+    std::reverse(mountinfo.begin(), mountinfo.end());
+    // mountinfo is reversed twice, so still is in lexical sorted order.
+
+    for (const auto& entry : mountinfo) {
         if ((entry.mount_point == mount_point) && !entry.shared_flag) {
             parent_private = true;
         }
         if ((entry.mount_point == "/dev") && !entry.shared_flag) {
             dev_private = true;
         }
+    }
 
+    // Need to make the original mountpoint MS_PRIVATE, so that the overlayfs can be MS_MOVE.
+    // This could happen if its parent mount is remounted later.
+    if (!parent_private) {
+        parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
+    }
+
+    for (const auto& entry : mountinfo) {
+        // Find all immediate submounts.
         if (!android::base::StartsWith(entry.mount_point, mount_point + "/")) {
             continue;
         }
-        if (std::find_if(move.begin(), move.end(), [&entry](const auto& it) {
-                return android::base::StartsWith(entry.mount_point, it.mount_point + "/");
-            }) != move.end()) {
+        // Exclude duplicated or more specific entries.
+        if (std::find_if(moved_mounts.begin(), moved_mounts.end(), [&entry](const auto& it) {
+                return it.mount_point == entry.mount_point ||
+                       android::base::StartsWith(entry.mount_point, it.mount_point + "/");
+            }) != moved_mounts.end()) {
             continue;
         }
+        // mountinfo is in lexical order, so no need to worry about |entry| being a parent mount of
+        // entries of |moved_mounts|.
 
         // use as the bound directory in /dev.
         AutoSetFsCreateCon createcon;
@@ -414,8 +446,7 @@
         if (new_context.empty() || !createcon.Set(new_context)) {
             continue;
         }
-        move_entry new_entry = {std::move(entry.mount_point), "/dev/TemporaryDir-XXXXXX",
-                                entry.shared_flag};
+        MoveEntry new_entry{entry.mount_point, "/dev/TemporaryDir-XXXXXX", entry.shared_flag};
         const auto target = mkdtemp(new_entry.dir.data());
         if (!createcon.Restore()) {
             return false;
@@ -426,9 +457,6 @@
             continue;
         }
 
-        if (!parent_private && !parent_made_private) {
-            parent_made_private = fs_mgr_overlayfs_set_shared_mount(mount_point, false);
-        }
         if (new_entry.shared_flag) {
             new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false);
         }
@@ -439,7 +467,7 @@
             }
             continue;
         }
-        move.emplace_back(std::move(new_entry));
+        moved_mounts.push_back(std::move(new_entry));
     }
 
     // hijack __mount() report format to help triage
@@ -463,7 +491,7 @@
     }
 
     // Move submounts back.
-    for (const auto& entry : move) {
+    for (const auto& entry : moved_mounts) {
         if (!dev_private && !dev_made_private) {
             dev_made_private = fs_mgr_overlayfs_set_shared_mount("/dev", false);
         }
@@ -658,22 +686,13 @@
             !fs_mgr_wants_overlayfs(&new_entry)) {
             continue;
         }
-        auto new_mount_point = fs_mgr_mount_point(entry.mount_point);
-        auto duplicate_or_more_specific = false;
-        for (auto it = candidates.begin(); it != candidates.end();) {
-            auto it_mount_point = fs_mgr_mount_point(it->mount_point);
-            if ((it_mount_point == new_mount_point) ||
-                (android::base::StartsWith(new_mount_point, it_mount_point + "/"))) {
-                duplicate_or_more_specific = true;
-                break;
-            }
-            if (android::base::StartsWith(it_mount_point, new_mount_point + "/")) {
-                it = candidates.erase(it);
-            } else {
-                ++it;
-            }
+        const auto new_mount_point = fs_mgr_mount_point(new_entry.mount_point);
+        if (std::find_if(candidates.begin(), candidates.end(), [&](const auto& it) {
+                return fs_mgr_mount_point(it.mount_point) == new_mount_point;
+            }) != candidates.end()) {
+            continue;
         }
-        if (!duplicate_or_more_specific) candidates.emplace_back(std::move(new_entry));
+        candidates.push_back(std::move(new_entry));
     }
     return candidates;
 }
diff --git a/fs_mgr/fs_mgr_remount.cpp b/fs_mgr/fs_mgr_remount.cpp
index 4b3a5d3..18892f9 100644
--- a/fs_mgr/fs_mgr_remount.cpp
+++ b/fs_mgr/fs_mgr_remount.cpp
@@ -88,17 +88,6 @@
     return entry.mount_point;
 }
 
-const FstabEntry* GetWrappedEntry(const Fstab& overlayfs_candidates, const FstabEntry& entry) {
-    auto mount_point = system_mount_point(entry);
-    auto it = std::find_if(overlayfs_candidates.begin(), overlayfs_candidates.end(),
-                           [&mount_point](const auto& entry) {
-                               return android::base::StartsWith(mount_point,
-                                                                system_mount_point(entry) + "/");
-                           });
-    if (it == overlayfs_candidates.end()) return nullptr;
-    return &(*it);
-}
-
 class MyLogger {
   public:
     explicit MyLogger(bool verbose) : verbose_(verbose) {}
@@ -196,9 +185,6 @@
     if (auto candidate_entry = GetEntryForMountPoint(&candidates, entry.mount_point)) {
         return candidate_entry->fs_type == entry.fs_type;
     }
-    if (GetWrappedEntry(candidates, entry)) {
-        return false;
-    }
     return true;
 }
 
@@ -252,11 +238,6 @@
         }
 
         const FstabEntry* entry = &*it;
-        if (auto wrap = GetWrappedEntry(candidates, *entry); wrap != nullptr) {
-            LOG(INFO) << "partition " << arg << " covered by overlayfs for " << wrap->mount_point
-                      << ", switching";
-            entry = wrap;
-        }
 
         // If it's already remounted, include it so it gets gracefully skipped
         // later on.