init: start ueventd in the default mount namespace

Init starts ueventd in the default mount namespace to support loading
firmware from APEXes.

Bug: 155023652
Test: devices boots
      adb$ nsenter -t (pid of ueventd) -m ls /apex
      => shows all APEXes
Change-Id: Ibb8b33a07eb014752275e3bca4541b8b694dc64b
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 0ac66f2..0b456e7 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1221,6 +1221,20 @@
     return {};
 }
 
+static Result<void> MountLinkerConfigForDefaultNamespace() {
+    // No need to mount linkerconfig for default mount namespace if the path does not exist (which
+    // would mean it is already mounted)
+    if (access("/linkerconfig/default", 0) != 0) {
+        return {};
+    }
+
+    if (mount("/linkerconfig/default", "/linkerconfig", nullptr, MS_BIND | MS_REC, nullptr) != 0) {
+        return ErrnoError() << "Failed to mount linker configuration for default mount namespace.";
+    }
+
+    return {};
+}
+
 static bool IsApexUpdatable() {
     static bool updatable = android::sysprop::ApexProperties::updatable().value_or(false);
     return updatable;
@@ -1319,11 +1333,14 @@
 }
 
 static Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {
-    if (SwitchToDefaultMountNamespace()) {
-        return {};
-    } else {
-        return Error() << "Failed to enter into default mount namespace";
+    if (auto result = SwitchToMountNamespaceIfNeeded(NS_DEFAULT); !result.ok()) {
+        return result.error();
     }
+    if (auto result = MountLinkerConfigForDefaultNamespace(); !result.ok()) {
+        return result.error();
+    }
+    LOG(INFO) << "Switched to default mount namespace";
+    return {};
 }
 
 // Builtin-function-map start
diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp
index f3b584c..b9d5d67 100644
--- a/init/mount_namespace.cpp
+++ b/init/mount_namespace.cpp
@@ -176,20 +176,6 @@
     return true;
 }
 
-static Result<void> MountLinkerConfigForDefaultNamespace() {
-    // No need to mount linkerconfig for default mount namespace if the path does not exist (which
-    // would mean it is already mounted)
-    if (access("/linkerconfig/default", 0) != 0) {
-        return {};
-    }
-
-    if (mount("/linkerconfig/default", "/linkerconfig", nullptr, MS_BIND | MS_REC, nullptr) != 0) {
-        return ErrnoError() << "Failed to mount linker configuration for default mount namespace.";
-    }
-
-    return {};
-}
-
 static android::base::unique_fd bootstrap_ns_fd;
 static android::base::unique_fd default_ns_fd;
 
@@ -290,40 +276,20 @@
     return success;
 }
 
-bool SwitchToDefaultMountNamespace() {
-    if (IsRecoveryMode()) {
-        // we don't have multiple namespaces in recovery mode
-        return true;
+Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace) {
+    if (IsRecoveryMode() || !IsApexUpdatable()) {
+        // we don't have multiple namespaces in recovery mode or if apex is not updatable
+        return {};
     }
-    if (default_ns_id != GetMountNamespaceId()) {
-        if (setns(default_ns_fd.get(), CLONE_NEWNS) == -1) {
-            PLOG(ERROR) << "Failed to switch back to the default mount namespace.";
-            return false;
-        }
-
-        if (auto result = MountLinkerConfigForDefaultNamespace(); !result.ok()) {
-            LOG(ERROR) << result.error();
-            return false;
+    const auto& ns_id = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_id : default_ns_id;
+    const auto& ns_fd = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_fd : default_ns_fd;
+    const auto& ns_name = target_mount_namespace == NS_BOOTSTRAP ? "bootstrap" : "default";
+    if (ns_id != GetMountNamespaceId() && ns_fd.get() != -1) {
+        if (setns(ns_fd.get(), CLONE_NEWNS) == -1) {
+            return ErrnoError() << "Failed to switch to " << ns_name << " mount namespace.";
         }
     }
-
-    LOG(INFO) << "Switched to default mount namespace";
-    return true;
-}
-
-bool SwitchToBootstrapMountNamespaceIfNeeded() {
-    if (IsRecoveryMode()) {
-        // we don't have multiple namespaces in recovery mode
-        return true;
-    }
-    if (bootstrap_ns_id != GetMountNamespaceId() && bootstrap_ns_fd.get() != -1 &&
-        IsApexUpdatable()) {
-        if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {
-            PLOG(ERROR) << "Failed to switch to bootstrap mount namespace.";
-            return false;
-        }
-    }
-    return true;
+    return {};
 }
 
 }  // namespace init
diff --git a/init/mount_namespace.h b/init/mount_namespace.h
index c41a449..d4d6f82 100644
--- a/init/mount_namespace.h
+++ b/init/mount_namespace.h
@@ -16,12 +16,15 @@
 
 #pragma once
 
+#include <android-base/result.h>
+
 namespace android {
 namespace init {
 
+enum MountNamespace { NS_BOOTSTRAP, NS_DEFAULT };
+
 bool SetupMountNamespaces();
-bool SwitchToDefaultMountNamespace();
-bool SwitchToBootstrapMountNamespaceIfNeeded();
+base::Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace);
 
 }  // namespace init
 }  // namespace android
diff --git a/init/reboot.cpp b/init/reboot.cpp
index ffd58a3..ec92361 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -831,7 +831,7 @@
         sub_reason = "apex";
         return result;
     }
-    if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
+    if (!SwitchToMountNamespaceIfNeeded(NS_BOOTSTRAP)) {
         sub_reason = "ns_switch";
         return Error() << "Failed to switch to bootstrap namespace";
     }
diff --git a/init/service.cpp b/init/service.cpp
index 165b848..68365b3 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -465,6 +465,16 @@
         pre_apexd_ = true;
     }
 
+    // For pre-apexd services, override mount namespace as "bootstrap" one before starting.
+    // Note: "ueventd" is supposed to be run in "default" mount namespace even if it's pre-apexd
+    // to support loading firmwares from APEXes.
+    std::optional<MountNamespace> override_mount_namespace;
+    if (name_ == "ueventd") {
+        override_mount_namespace = NS_DEFAULT;
+    } else if (pre_apexd_) {
+        override_mount_namespace = NS_BOOTSTRAP;
+    }
+
     post_data_ = ServiceList::GetInstance().IsPostData();
 
     LOG(INFO) << "starting service '" << name_ << "'...";
@@ -496,7 +506,8 @@
     if (pid == 0) {
         umask(077);
 
-        if (auto result = EnterNamespaces(namespaces_, name_, pre_apexd_); !result.ok()) {
+        if (auto result = EnterNamespaces(namespaces_, name_, override_mount_namespace);
+            !result.ok()) {
             LOG(FATAL) << "Service '" << name_
                        << "' failed to set up namespaces: " << result.error();
         }
diff --git a/init/service_utils.cpp b/init/service_utils.cpp
index 484c2c8..05e632b 100644
--- a/init/service_utils.cpp
+++ b/init/service_utils.cpp
@@ -194,7 +194,8 @@
     return Descriptor(ANDROID_FILE_ENV_PREFIX + name, std::move(fd));
 }
 
-Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd) {
+Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name,
+                             std::optional<MountNamespace> override_mount_namespace) {
     for (const auto& [nstype, path] : info.namespaces_to_enter) {
         if (auto result = EnterNamespace(nstype, path.c_str()); !result.ok()) {
             return result;
@@ -202,9 +203,10 @@
     }
 
 #if defined(__ANDROID__)
-    if (pre_apexd) {
-        if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
-            return Error() << "could not enter into the bootstrap mount namespace";
+    if (override_mount_namespace.has_value()) {
+        if (auto result = SwitchToMountNamespaceIfNeeded(override_mount_namespace.value());
+            !result.ok()) {
+            return result;
         }
     }
 #endif
diff --git a/init/service_utils.h b/init/service_utils.h
index 3f1071e..e74f8c1 100644
--- a/init/service_utils.h
+++ b/init/service_utils.h
@@ -19,12 +19,14 @@
 #include <sys/resource.h>
 #include <sys/types.h>
 
+#include <optional>
 #include <string>
 #include <vector>
 
 #include <android-base/unique_fd.h>
 #include <cutils/iosched_policy.h>
 
+#include "mount_namespace.h"
 #include "result.h"
 
 namespace android {
@@ -66,7 +68,8 @@
     // Pair of namespace type, path to name.
     std::vector<std::pair<int, std::string>> namespaces_to_enter;
 };
-Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name, bool pre_apexd);
+Result<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name,
+                             std::optional<MountNamespace> override_mount_namespace);
 
 struct ProcessAttributes {
     std::string console;