Merge "Use /bootstrap-apex for bootstrap APEXes" into main am: 58ba0b44c2
Original change: https://android-review.googlesource.com/c/platform/system/core/+/2666915
Change-Id: Ifccb5b15c8fc1b7e9a2c9140127fc884090d35c4
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/init/builtins.cpp b/init/builtins.cpp
index fa5e36d..cf784ac 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1262,6 +1262,51 @@
return {};
}
+
+static Result<void> MountApexRootForDefaultNamespace() {
+ auto mount_namespace_id = GetCurrentMountNamespace();
+ if (!mount_namespace_id.ok()) {
+ return mount_namespace_id.error();
+ }
+ // There's nothing to do if it's still in the bootstrap mount namespace.
+ // This happens when we don't need to update APEXes (e.g. Microdroid)
+ // where bootstrap mount namespace == default mount namespace.
+ if (mount_namespace_id.value() == NS_BOOTSTRAP) {
+ return {};
+ }
+
+ // Now, we're in the "default" mount namespace and need a fresh /apex for
+ // the default mount namespace.
+ //
+ // At this point, there are two mounts at the same mount point: /apex
+ // - to tmpfs (private)
+ // - to /bootstrap-apex (shared)
+ //
+ // We need unmount the second mount so that /apex in the default mount
+ // namespace becomes RW/empty and "private" (we don't want mount events to
+ // propagate to the bootstrap mount namespace).
+ //
+ // Likewise, we don't want the unmount event itself to propagate to the
+ // bootstrap mount namespace. Otherwise, /apex in the bootstrap mount
+ // namespace would become empty due to the unmount.
+ //
+ // Hence, before unmounting, we make /apex (the second one) "private" first.
+ // so that the unmouting below doesn't affect to the bootstrap mount namespace.
+ if (mount(nullptr, "/apex", nullptr, MS_PRIVATE | MS_REC, nullptr) == -1) {
+ return ErrnoError() << "Failed to remount /apex as private";
+ }
+
+ // Now we can unmount /apex (bind-mount to /bootstrap-apex). This only affects
+ // in the default mount namespace and /apex is now seen as tmpfs mount.
+ // Note that /apex in the bootstrap mount namespace is still a bind-mount to
+ // /bootstrap-apex and holds the APEX mounts.
+ if (umount2("/apex", MNT_DETACH) == -1) {
+ return ErrnoError() << "Failed to umount /apex";
+ }
+
+ return {};
+}
+
static Result<void> do_update_linker_config(const BuiltinArguments&) {
return GenerateLinkerConfiguration();
}
@@ -1315,6 +1360,11 @@
if (auto result = SwitchToMountNamespaceIfNeeded(NS_DEFAULT); !result.ok()) {
return result.error();
}
+
+ if (auto result = MountApexRootForDefaultNamespace(); !result.ok()) {
+ return result.error();
+ }
+
if (auto result = MountLinkerConfigForDefaultNamespace(); !result.ok()) {
return result.error();
}
diff --git a/init/init.cpp b/init/init.cpp
index da63fdc..4bb8eec 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -832,6 +832,12 @@
CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
+ if (NeedsTwoMountNamespaces()) {
+ // /bootstrap-apex is used to mount "bootstrap" APEXes.
+ CHECKCALL(mount("tmpfs", "/bootstrap-apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
+ "mode=0755,uid=0,gid=0"));
+ }
+
// /linkerconfig is used to keep generated linker configuration
CHECKCALL(mount("tmpfs", "/linkerconfig", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV,
"mode=0755,uid=0,gid=0"));
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index 5b53d50..e069a5d 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -66,15 +66,6 @@
return ret;
}
-// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
-// namespaces.
-static bool NeedsTwoMountNamespaces() {
- if (IsRecoveryMode()) return false;
- // In microdroid, there's only one set of APEXes in built-in directories include block devices.
- if (IsMicrodroid()) return false;
- return true;
-}
-
static android::base::unique_fd bootstrap_ns_fd;
static android::base::unique_fd default_ns_fd;
@@ -83,6 +74,15 @@
} // namespace
+// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount
+// namespaces.
+bool NeedsTwoMountNamespaces() {
+ if (IsRecoveryMode()) return false;
+ // In microdroid, there's only one set of APEXes in built-in directories include block devices.
+ if (IsMicrodroid()) return false;
+ return true;
+}
+
bool SetupMountNamespaces() {
// Set the propagation type of / as shared so that any mounting event (e.g.
// /data) is by default visible to all processes. When private mounting is
@@ -96,6 +96,27 @@
// the bootstrap namespace get APEXes from the read-only partition.
if (!(ChangeMount("/apex", MS_PRIVATE))) return false;
+ // However, some components (e.g. servicemanager) need to access bootstrap
+ // APEXes from the default mount namespace. To achieve that, we bind-mount
+ // /apex with /bootstrap-apex (not private) in the bootstrap mount namespace.
+ // Bootstrap APEXes are mounted in /apex and also visible in /bootstrap-apex.
+ // In the default mount namespace, we detach /bootstrap-apex from /apex and
+ // bootstrap APEXes are still be visible in /bootstrap-apex.
+ //
+ // The end result will look like:
+ // in the bootstrap mount namespace:
+ // /apex (== /bootstrap-apex)
+ // {bootstrap APEXes from the read-only partition}
+ //
+ // in the default mount namespace:
+ // /bootstrap-apex
+ // {bootstrap APEXes from the read-only partition}
+ // /apex
+ // {APEXes, can be from /data partition}
+ if (NeedsTwoMountNamespaces()) {
+ if (!(BindMount("/bootstrap-apex", "/apex"))) return false;
+ }
+
// /linkerconfig is a private mountpoint to give a different linker configuration
// based on the mount namespace. Subdirectory will be bind-mounted based on current mount
// namespace
diff --git a/init/mount_namespace.h b/init/mount_namespace.h
index 5e3dab2..43c5476 100644
--- a/init/mount_namespace.h
+++ b/init/mount_namespace.h
@@ -24,9 +24,12 @@
enum MountNamespace { NS_BOOTSTRAP, NS_DEFAULT };
bool SetupMountNamespaces();
+
base::Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace);
base::Result<MountNamespace> GetCurrentMountNamespace();
+bool NeedsTwoMountNamespaces();
+
} // namespace init
} // namespace android
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 51093d8..8532c44 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -766,7 +766,7 @@
selinux_android_restorecon("/dev/device-mapper", 0);
selinux_android_restorecon("/apex", 0);
-
+ selinux_android_restorecon("/bootstrap-apex", 0);
selinux_android_restorecon("/linkerconfig", 0);
// adb remount, snapshot-based updates, and DSUs all create files during
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 3362872..5218753 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -91,7 +91,7 @@
#
# create some directories (some are mount points) and symlinks
LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
- dev proc sys system data data_mirror odm oem acct config storage mnt apex debug_ramdisk \
+ dev proc sys system data data_mirror odm oem acct config storage mnt apex bootstrap-apex debug_ramdisk \
linkerconfig second_stage_resources postinstall $(BOARD_ROOT_EXTRA_FOLDERS)); \
ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \