| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "mount_namespace.h" |
| |
| #include <sys/mount.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include <android-base/file.h> |
| #include <android-base/logging.h> |
| #include <android-base/properties.h> |
| #include <android-base/unique_fd.h> |
| |
| #include "util.h" |
| |
| namespace android { |
| namespace init { |
| namespace { |
| |
| static constexpr const char* kLinkerMountPoint = "/bionic/bin/linker"; |
| static constexpr const char* kBootstrapLinkerPath = "/system/bin/bootstrap/linker"; |
| static constexpr const char* kRuntimeLinkerPath = "/apex/com.android.runtime/bin/linker"; |
| |
| static constexpr const char* kBionicLibsMountPointDir = "/bionic/lib/"; |
| static constexpr const char* kBootstrapBionicLibsDir = "/system/lib/bootstrap/"; |
| static constexpr const char* kRuntimeBionicLibsDir = "/apex/com.android.runtime/lib/bionic/"; |
| |
| static constexpr const char* kLinkerMountPoint64 = "/bionic/bin/linker64"; |
| static constexpr const char* kBootstrapLinkerPath64 = "/system/bin/bootstrap/linker64"; |
| static constexpr const char* kRuntimeLinkerPath64 = "/apex/com.android.runtime/bin/linker64"; |
| |
| static constexpr const char* kBionicLibsMountPointDir64 = "/bionic/lib64/"; |
| static constexpr const char* kBootstrapBionicLibsDir64 = "/system/lib64/bootstrap/"; |
| static constexpr const char* kRuntimeBionicLibsDir64 = "/apex/com.android.runtime/lib64/bionic/"; |
| |
| static const std::vector<std::string> kBionicLibFileNames = {"libc.so", "libm.so", "libdl.so"}; |
| |
| static bool BindMount(const std::string& source, const std::string& mount_point, |
| bool recursive = false) { |
| unsigned long mountflags = MS_BIND; |
| if (recursive) { |
| mountflags |= MS_REC; |
| } |
| if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) { |
| PLOG(ERROR) << "Could not bind-mount " << source << " to " << mount_point; |
| return false; |
| } |
| return true; |
| } |
| |
| static bool MakeShared(const std::string& mount_point, bool recursive = false) { |
| unsigned long mountflags = MS_SHARED; |
| if (recursive) { |
| mountflags |= MS_REC; |
| } |
| if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) { |
| PLOG(ERROR) << "Failed to change propagation type to shared"; |
| return false; |
| } |
| return true; |
| } |
| |
| static bool MakePrivate(const std::string& mount_point, bool recursive = false) { |
| unsigned long mountflags = MS_PRIVATE; |
| if (recursive) { |
| mountflags |= MS_REC; |
| } |
| if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) { |
| PLOG(ERROR) << "Failed to change propagation type to private"; |
| return false; |
| } |
| return true; |
| } |
| |
| static int OpenMountNamespace() { |
| int fd = open("/proc/self/ns/mnt", O_RDONLY | O_CLOEXEC); |
| if (fd < 0) { |
| PLOG(ERROR) << "Cannot open fd for current mount namespace"; |
| } |
| return fd; |
| } |
| |
| static std::string GetMountNamespaceId() { |
| std::string ret; |
| if (!android::base::Readlink("/proc/self/ns/mnt", &ret)) { |
| PLOG(ERROR) << "Failed to read namespace ID"; |
| return ""; |
| } |
| return ret; |
| } |
| |
| static bool BindMountBionic(const std::string& linker_source, const std::string& lib_dir_source, |
| const std::string& linker_mount_point, |
| const std::string& lib_mount_dir) { |
| if (access(linker_source.c_str(), F_OK) != 0) { |
| PLOG(INFO) << linker_source << " does not exist. skipping mounting bionic there."; |
| // This can happen for 64-bit bionic in 32-bit only device. |
| // It is okay to skip mounting the 64-bit bionic. |
| return true; |
| } |
| if (!BindMount(linker_source, linker_mount_point)) { |
| return false; |
| } |
| if (!MakePrivate(linker_mount_point)) { |
| return false; |
| } |
| for (const auto& libname : kBionicLibFileNames) { |
| std::string mount_point = lib_mount_dir + libname; |
| std::string source = lib_dir_source + libname; |
| if (!BindMount(source, mount_point)) { |
| return false; |
| } |
| if (!MakePrivate(mount_point)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| static bool IsBionicUpdatable() { |
| static bool result = android::base::GetBoolProperty("ro.apex.IsBionicUpdatable", false); |
| return result; |
| } |
| |
| static android::base::unique_fd bootstrap_ns_fd; |
| static android::base::unique_fd default_ns_fd; |
| |
| static std::string bootstrap_ns_id; |
| static std::string default_ns_id; |
| |
| } // namespace |
| |
| 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 |
| // needed for /foo/bar, then we will make /foo/bar as a mount point (by |
| // bind-mounting by to itself) and set the propagation type of the mount |
| // point to private. |
| if (!MakeShared("/", true /*recursive*/)) return false; |
| |
| // Since different files (bootstrap or runtime APEX) should be mounted to |
| // the same mount point paths (e.g. /bionic/bin/linker, /bionic/lib/libc.so, |
| // etc.) across the two mount namespaces, we create a private mount point at |
| // /bionic so that a mount event for the bootstrap bionic in the mount |
| // namespace for pre-apexd processes is not propagated to the other mount |
| // namespace for post-apexd process, and vice versa. |
| // |
| // Other mount points other than /bionic, however, are all still shared. |
| if (!BindMount("/bionic", "/bionic", true /*recursive*/)) return false; |
| if (!MakePrivate("/bionic")) return false; |
| |
| // Bind-mount bootstrap bionic. |
| if (!BindMountBionic(kBootstrapLinkerPath, kBootstrapBionicLibsDir, kLinkerMountPoint, |
| kBionicLibsMountPointDir)) |
| return false; |
| if (!BindMountBionic(kBootstrapLinkerPath64, kBootstrapBionicLibsDir64, kLinkerMountPoint64, |
| kBionicLibsMountPointDir64)) |
| return false; |
| |
| bootstrap_ns_fd.reset(OpenMountNamespace()); |
| bootstrap_ns_id = GetMountNamespaceId(); |
| |
| // When bionic is updatable via the runtime APEX, we create separate mount |
| // namespaces for processes that are started before and after the APEX is |
| // activated by apexd. In the namespace for pre-apexd processes, the bionic |
| // from the /system partition (that we call bootstrap bionic) is |
| // bind-mounted. In the namespace for post-apexd processes, the bionic from |
| // the runtime APEX is bind-mounted. |
| bool success = true; |
| if (IsBionicUpdatable() && !IsRecoveryMode()) { |
| // Creating a new namespace by cloning, saving, and switching back to |
| // the original namespace. |
| if (unshare(CLONE_NEWNS) == -1) { |
| PLOG(ERROR) << "Cannot create mount namespace"; |
| return false; |
| } |
| default_ns_fd.reset(OpenMountNamespace()); |
| default_ns_id = GetMountNamespaceId(); |
| |
| // By this unmount, the bootstrap bionic are not mounted in the default |
| // mount namespace. |
| if (umount2("/bionic", MNT_DETACH) == -1) { |
| PLOG(ERROR) << "Cannot unmount /bionic"; |
| // Don't return here. We have to switch back to the bootstrap |
| // namespace. |
| success = false; |
| } |
| |
| if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) { |
| PLOG(ERROR) << "Cannot switch back to bootstrap mount namespace"; |
| return false; |
| } |
| } else { |
| // Otherwise, default == bootstrap |
| default_ns_fd.reset(OpenMountNamespace()); |
| default_ns_id = GetMountNamespaceId(); |
| } |
| |
| LOG(INFO) << "SetupMountNamespaces done"; |
| return success; |
| } |
| |
| bool SwitchToDefaultMountNamespace() { |
| if (IsRecoveryMode()) { |
| // we don't have multiple namespaces in recovery mode |
| return true; |
| } |
| 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; |
| } |
| } |
| |
| // Bind-mount bionic from the runtime APEX since it is now available. Note |
| // that in case of IsBionicUpdatable() == false, these mounts are over the |
| // existing existing bind mounts for the bootstrap bionic, which effectively |
| // becomes hidden. |
| if (!BindMountBionic(kRuntimeLinkerPath, kRuntimeBionicLibsDir, kLinkerMountPoint, |
| kBionicLibsMountPointDir)) |
| return false; |
| if (!BindMountBionic(kRuntimeLinkerPath64, kRuntimeBionicLibsDir64, kLinkerMountPoint64, |
| kBionicLibsMountPointDir64)) |
| return false; |
| |
| 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 && |
| IsBionicUpdatable()) { |
| if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) { |
| PLOG(ERROR) << "Failed to switch to bootstrap mount namespace."; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| } // namespace init |
| } // namespace android |