6866041ff0
This CL fixes the design problem of the previous mechanism for providing the bootstrap bionic and the runtime bionic to the same path. Previously, bootstrap bionic was self-bind-mounted; i.e. /system/bin/libc.so is bind-mounted to itself. And the runtime bionic was bind-mounted on top of the bootstrap bionic. This has not only caused problems like `adb sync` not working(b/122737045), but also is quite difficult to understand due to the double-and-self mounting. This is the new design: Most importantly, these four are all distinct: 1) bootstrap bionic (/system/lib/bootstrap/libc.so) 2) runtime bionic (/apex/com.android.runtime/lib/bionic/libc.so) 3) mount point for 1) and 2) (/bionic/lib/libc.so) 4) symlink for 3) (/system/lib/libc.so -> /bionic/lib/libc.so) Inside the mount namespace of the pre-apexd processes, 1) is bind-mounted to 3). Likewise, inside the mount namespace of the post-apexd processes, 2) is bind-mounted to 3). In other words, there is no self-mount, and no double-mount. Another change is that mount points are under /bionic and the legacy paths become symlinks to the mount points. This is to make sure that there is no bind mounts under /system, which is breaking some apps. Finally, code for creating mount namespaces, mounting bionic, etc are refactored to mount_namespace.cpp Bug: 120266448 Bug: 123275379 Test: m, device boots, adb sync/push/pull works, especially with following paths: /bionic/lib64/libc.so /bionic/bin/linker64 /system/lib64/bootstrap/libc.so /system/bin/bootstrap/linker64 Change-Id: Icdfbdcc1efca540ac854d4df79e07ee61fca559f
261 lines
9.6 KiB
C++
261 lines
9.6 KiB
C++
/*
|
|
* 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
|