Merge "Use /bootstrap-apex for bootstrap APEXes" into main am: 58ba0b44c2 am: 6b0c2c3cc6 am: 3249f9ff35 am: b47809dd10

Original change: https://android-review.googlesource.com/c/platform/system/core/+/2666915

Change-Id: I83b59cb169d2c83a7f3e7dbce29d3c11345e1f29
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Jooyung Han 2023-08-01 00:32:33 +00:00 committed by Automerger Merge Worker
commit dd90936be1
6 changed files with 91 additions and 11 deletions

View file

@ -1262,6 +1262,51 @@ static Result<void> MountLinkerConfigForDefaultNamespace() {
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 @@ static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
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();
}

View file

@ -832,6 +832,12 @@ static void MountExtraFilesystems() {
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"));

View file

@ -66,15 +66,6 @@ static std::string GetMountNamespaceId() {
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 @@ static std::string default_ns_id;
} // 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 @@ bool SetupMountNamespaces() {
// 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

View file

@ -24,9 +24,12 @@ namespace init {
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

View file

@ -766,7 +766,7 @@ void SelinuxRestoreContext() {
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

View file

@ -91,7 +91,7 @@ endif
#
# 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; \