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
This commit is contained in:
parent
b28db2a923
commit
c78df87fad
|
@ -381,32 +381,64 @@ static bool fs_mgr_overlayfs_mount(const FstabEntry& entry) {
|
|||
|
||||
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 @@ static bool fs_mgr_overlayfs_mount(const FstabEntry& entry) {
|
|||
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 @@ static bool fs_mgr_overlayfs_mount(const FstabEntry& entry) {
|
|||
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 @@ static bool fs_mgr_overlayfs_mount(const FstabEntry& entry) {
|
|||
}
|
||||
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 @@ static bool fs_mgr_overlayfs_mount(const FstabEntry& entry) {
|
|||
}
|
||||
|
||||
// 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 @@ Fstab fs_mgr_overlayfs_candidate_list(const Fstab& fstab) {
|
|||
!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;
|
||||
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 (android::base::StartsWith(it_mount_point, new_mount_point + "/")) {
|
||||
it = candidates.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
if (!duplicate_or_more_specific) candidates.emplace_back(std::move(new_entry));
|
||||
candidates.push_back(std::move(new_entry));
|
||||
}
|
||||
return candidates;
|
||||
}
|
||||
|
|
|
@ -88,17 +88,6 @@ const std::string system_mount_point(const android::fs_mgr::FstabEntry& entry) {
|
|||
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 @@ static bool IsRemountable(Fstab& candidates, const FstabEntry& entry) {
|
|||
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 @@ bool GetRemountList(const Fstab& fstab, const std::vector<std::string>& argv, Fs
|
|||
}
|
||||
|
||||
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.
|
||||
|
|
Loading…
Reference in New Issue