Merge changes from topic "move_bionic_to_apex3"
* changes: Handle adb sync with Bionic under /bionic Proper mount namespace configuration for bionic
This commit is contained in:
commit
a15f385821
16 changed files with 479 additions and 10 deletions
|
@ -25,6 +25,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
@ -209,6 +210,22 @@ done:
|
|||
return WriteFdExactly(s, &msg.dent, sizeof(msg.dent));
|
||||
}
|
||||
|
||||
static bool is_mountpoint(const std::string& path, pid_t tid) {
|
||||
const std::string mountinfo_path = "/proc/" + std::to_string(tid) + "/mountinfo";
|
||||
std::string mountinfo;
|
||||
if (!android::base::ReadFileToString(mountinfo_path, &mountinfo)) {
|
||||
PLOG(ERROR) << "Failed to open " << mountinfo_path;
|
||||
return false;
|
||||
}
|
||||
std::vector<std::string> lines = android::base::Split(mountinfo, "\n");
|
||||
return std::find_if(lines.begin(), lines.end(), [&path](const auto& line) {
|
||||
auto tokens = android::base::Split(line, " ");
|
||||
// line format is ...
|
||||
// mountid parentmountid major:minor sourcepath targetpath option ...
|
||||
return tokens.size() >= 4 && tokens[4] == path;
|
||||
}) != lines.end();
|
||||
}
|
||||
|
||||
// Make sure that SendFail from adb_io.cpp isn't accidentally used in this file.
|
||||
#pragma GCC poison SendFail
|
||||
|
||||
|
@ -415,6 +432,18 @@ static bool do_send(int s, const std::string& spec, std::vector<char>& buffer) {
|
|||
struct stat st;
|
||||
bool do_unlink = (lstat(path.c_str(), &st) == -1) || S_ISREG(st.st_mode) ||
|
||||
(S_ISLNK(st.st_mode) && !S_ISLNK(mode));
|
||||
|
||||
// If the path is a file that is a mount point, don't unlink it, but instead
|
||||
// truncate to zero. If unlinked, existing mounts on the path is all
|
||||
// unmounted
|
||||
if (S_ISREG(st.st_mode) && is_mountpoint(path, getpid())) {
|
||||
do_unlink = false;
|
||||
if (truncate(path.c_str(), 0) == -1) {
|
||||
SendSyncFail(s, "truncate to zero failed");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_unlink) {
|
||||
adb_unlink(path.c_str());
|
||||
}
|
||||
|
@ -546,7 +575,64 @@ static bool handle_sync_command(int fd, std::vector<char>& buffer) {
|
|||
return true;
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
class FileSyncPreparer {
|
||||
public:
|
||||
FileSyncPreparer() : saved_ns_fd_(-1), rooted_(getuid() == 0) {
|
||||
const std::string namespace_path = "/proc/" + std::to_string(gettid()) + "/ns/mnt";
|
||||
const int ns_fd = adb_open(namespace_path.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (ns_fd == -1) {
|
||||
if (rooted_) PLOG(ERROR) << "Failed to save mount namespace";
|
||||
return;
|
||||
}
|
||||
saved_ns_fd_.reset(ns_fd);
|
||||
|
||||
// Note: this is for the current thread only
|
||||
if (unshare(CLONE_NEWNS) != 0) {
|
||||
if (rooted_) PLOG(ERROR) << "Failed to clone mount namespace";
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the propagation type of / to private so that unmount below is
|
||||
// not propagated to other mount namespaces.
|
||||
if (mount(nullptr, "/", nullptr, MS_PRIVATE | MS_REC, nullptr) == -1) {
|
||||
if (rooted_) PLOG(ERROR) << "Could not change propagation type of / to MS_PRIVATE";
|
||||
return;
|
||||
}
|
||||
|
||||
// unmount /bionic which is bind-mount to itself by init. Under /bionic,
|
||||
// there are other bind mounts for the bionic files. By unmounting this,
|
||||
// we unmount them all thus revealing the raw file system that is the
|
||||
// same as the local file system seen by the adb client.
|
||||
if (umount2("/bionic", MNT_DETACH) == -1 && errno != ENOENT) {
|
||||
if (rooted_) PLOG(ERROR) << "Could not unmount /bionic to reveal raw filesystem";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
~FileSyncPreparer() {
|
||||
if (saved_ns_fd_.get() != -1) {
|
||||
// In fact, this is not strictly required because this thread for file
|
||||
// sync service will be destroyed after the current transfer is all
|
||||
// done. However, let's restore the ns in case the same thread is
|
||||
// reused by multiple transfers in the future refactoring.
|
||||
if (setns(saved_ns_fd_, CLONE_NEWNS) == -1) {
|
||||
PLOG(ERROR) << "Failed to restore saved mount namespace";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
unique_fd saved_ns_fd_;
|
||||
bool rooted_;
|
||||
};
|
||||
#endif
|
||||
|
||||
void file_sync_service(unique_fd fd) {
|
||||
#if defined(__ANDROID__)
|
||||
FileSyncPreparer preparer;
|
||||
#endif
|
||||
|
||||
std::vector<char> buffer(SYNC_DATA_MAX);
|
||||
|
||||
while (handle_sync_command(fd.get(), buffer)) {
|
||||
|
|
|
@ -230,6 +230,23 @@ static void reboot_for_remount(int fd, bool need_fsck) {
|
|||
android::base::SetProperty(ANDROID_RB_PROPERTY, reboot_cmd.c_str());
|
||||
}
|
||||
|
||||
static void try_unmount_bionic(int fd) {
|
||||
static constexpr const char* kBionic = "/bionic";
|
||||
struct statfs buf;
|
||||
if (statfs(kBionic, &buf) == -1) {
|
||||
WriteFdFmt(fd, "statfs of the %s mount failed: %s.\n", kBionic, strerror(errno));
|
||||
return;
|
||||
}
|
||||
if (buf.f_flags & ST_RDONLY) {
|
||||
// /bionic is on a read-only partition; can happen for
|
||||
// non-system-as-root-devices. Don' try to unmount.
|
||||
return;
|
||||
}
|
||||
// Success/Fail of the actual remount will be reported by the function.
|
||||
remount_partition(fd, kBionic);
|
||||
return;
|
||||
}
|
||||
|
||||
void remount_service(unique_fd fd, const std::string& cmd) {
|
||||
bool user_requested_reboot = cmd == "-R";
|
||||
|
||||
|
@ -323,6 +340,8 @@ void remount_service(unique_fd fd, const std::string& cmd) {
|
|||
return;
|
||||
}
|
||||
|
||||
try_unmount_bionic(fd.get());
|
||||
|
||||
if (!success) {
|
||||
WriteFdExactly(fd.get(), "remount failed\n");
|
||||
} else {
|
||||
|
|
|
@ -569,6 +569,18 @@ B="`adb_cat /vendor/hello`" ||
|
|||
die "vendor hello"
|
||||
check_eq "${A}" "${B}" /vendor before reboot
|
||||
|
||||
# download libc.so, append some gargage, push back, and check if the file is updated.
|
||||
tempdir="`mktemp -d`"
|
||||
cleanup() {
|
||||
rm -rf ${tempdir}
|
||||
}
|
||||
adb pull /system/lib/bootstrap/libc.so ${tempdir} || die "pull libc.so from device"
|
||||
garbage="`hexdump -n 16 -e '4/4 "%08X" 1 "\n"' /dev/random`"
|
||||
echo ${garbage} >> ${tempdir}/libc.so
|
||||
adb push ${tempdir}/libc.so /system/lib/bootstrap/libc.so || die "push libc.so to device"
|
||||
adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice || die "pull libc.so from device"
|
||||
diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null || die "libc.so differ"
|
||||
|
||||
echo "${GREEN}[ RUN ]${NORMAL} reboot to confirm content persistent" >&2
|
||||
|
||||
adb_reboot &&
|
||||
|
@ -607,6 +619,14 @@ adb_root &&
|
|||
check_eq "${A}" "${B}" vendor after reboot
|
||||
echo "${GREEN}[ OK ]${NORMAL} /vendor content remains after reboot" >&2
|
||||
|
||||
# check if the updated libc.so is persistent after reboot
|
||||
adb_root &&
|
||||
adb pull /system/lib/bootstrap/libc.so ${tempdir}/libc.so.fromdevice ||
|
||||
die "pull libc.so from device"
|
||||
diff ${tempdir}/libc.so ${tempdir}/libc.so.fromdevice > /dev/null || die "libc.so differ"
|
||||
rm -r ${tempdir}
|
||||
echo "${GREEN}[ OK ]${NORMAL} /system/lib/bootstrap/libc.so content remains after reboot" >&2
|
||||
|
||||
echo "${GREEN}[ RUN ]${NORMAL} flash vendor, confirm its content disappears" >&2
|
||||
|
||||
H=`adb_sh echo '${HOSTNAME}' </dev/null 2>/dev/null`
|
||||
|
|
|
@ -110,6 +110,7 @@ cc_library_static {
|
|||
"init.cpp",
|
||||
"keychords.cpp",
|
||||
"modalias_handler.cpp",
|
||||
"mount_namespace.cpp",
|
||||
"parser.cpp",
|
||||
"persistent_properties.cpp",
|
||||
"persistent_properties.proto",
|
||||
|
@ -166,6 +167,7 @@ cc_binary {
|
|||
exclude_shared_libs: ["libbinder", "libutils"],
|
||||
},
|
||||
},
|
||||
ldflags: ["-Wl,--rpath,/system/${LIB}/bootstrap"],
|
||||
}
|
||||
|
||||
// Tests
|
||||
|
|
|
@ -47,6 +47,7 @@ LOCAL_SRC_FILES := \
|
|||
first_stage_init.cpp \
|
||||
first_stage_main.cpp \
|
||||
first_stage_mount.cpp \
|
||||
mount_namespace.cpp \
|
||||
reboot_utils.cpp \
|
||||
selinux.cpp \
|
||||
switch_root.cpp \
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
#include "action_manager.h"
|
||||
#include "bootchart.h"
|
||||
#include "init.h"
|
||||
#include "mount_namespace.h"
|
||||
#include "parser.h"
|
||||
#include "property_service.h"
|
||||
#include "reboot.h"
|
||||
|
@ -1098,6 +1099,14 @@ static Result<Success> do_parse_apex_configs(const BuiltinArguments& args) {
|
|||
}
|
||||
}
|
||||
|
||||
static Result<Success> do_setup_runtime_bionic(const BuiltinArguments& args) {
|
||||
if (SwitchToDefaultMountNamespace()) {
|
||||
return Success();
|
||||
} else {
|
||||
return Error() << "Failed to setup runtime bionic";
|
||||
}
|
||||
}
|
||||
|
||||
// Builtin-function-map start
|
||||
const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
|
||||
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
|
||||
|
@ -1145,6 +1154,7 @@ const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
|
|||
{"rmdir", {1, 1, {true, do_rmdir}}},
|
||||
{"setprop", {2, 2, {true, do_setprop}}},
|
||||
{"setrlimit", {3, 3, {false, do_setrlimit}}},
|
||||
{"setup_runtime_bionic", {0, 0, {false, do_setup_runtime_bionic}}},
|
||||
{"start", {1, 1, {false, do_start}}},
|
||||
{"stop", {1, 1, {false, do_stop}}},
|
||||
{"swapon_all", {1, 1, {false, do_swapon_all}}},
|
||||
|
|
|
@ -136,10 +136,6 @@ static inline bool IsDtVbmetaCompatible(const Fstab& fstab) {
|
|||
return is_android_dt_value_expected("vbmeta/compatible", "android,vbmeta");
|
||||
}
|
||||
|
||||
static bool IsRecoveryMode() {
|
||||
return access("/system/bin/recovery", F_OK) == 0;
|
||||
}
|
||||
|
||||
static Fstab ReadFirstStageFstab() {
|
||||
Fstab fstab;
|
||||
if (!ReadFstabFromDt(&fstab)) {
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "first_stage_mount.h"
|
||||
#include "import_parser.h"
|
||||
#include "keychords.h"
|
||||
#include "mount_namespace.h"
|
||||
#include "property_service.h"
|
||||
#include "reboot.h"
|
||||
#include "reboot_utils.h"
|
||||
|
@ -666,6 +667,10 @@ int SecondStageMain(int argc, char** argv) {
|
|||
const BuiltinFunctionMap function_map;
|
||||
Action::set_function_map(&function_map);
|
||||
|
||||
if (!SetupMountNamespaces()) {
|
||||
PLOG(FATAL) << "SetupMountNamespaces failed";
|
||||
}
|
||||
|
||||
subcontexts = InitializeSubcontexts();
|
||||
|
||||
ActionManager& am = ActionManager::GetInstance();
|
||||
|
|
261
init/mount_namespace.cpp
Normal file
261
init/mount_namespace.cpp
Normal file
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* 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
|
27
init/mount_namespace.h
Normal file
27
init/mount_namespace.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace android {
|
||||
namespace init {
|
||||
|
||||
bool SetupMountNamespaces();
|
||||
bool SwitchToDefaultMountNamespace();
|
||||
bool SwitchToBootstrapMountNamespaceIfNeeded();
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
|
@ -50,6 +50,7 @@
|
|||
#include <sys/system_properties.h>
|
||||
|
||||
#include "init.h"
|
||||
#include "mount_namespace.h"
|
||||
#include "property_service.h"
|
||||
#include "selinux.h"
|
||||
#else
|
||||
|
@ -207,6 +208,11 @@ static bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigsto
|
|||
return execv(c_strings[0], c_strings.data()) == 0;
|
||||
}
|
||||
|
||||
static bool IsRuntimeApexReady() {
|
||||
struct stat buf;
|
||||
return stat("/apex/com.android.runtime/", &buf) == 0;
|
||||
}
|
||||
|
||||
unsigned long Service::next_start_order_ = 1;
|
||||
bool Service::is_exec_service_running_ = false;
|
||||
|
||||
|
@ -929,6 +935,14 @@ Result<Success> Service::Start() {
|
|||
scon = *result;
|
||||
}
|
||||
|
||||
if (!IsRuntimeApexReady() && !pre_apexd_) {
|
||||
// If this service is started before the runtime APEX gets available,
|
||||
// mark it as pre-apexd one. Note that this marking is permanent. So
|
||||
// for example, if the service is re-launched (e.g., due to crash),
|
||||
// it is still recognized as pre-apexd... for consistency.
|
||||
pre_apexd_ = true;
|
||||
}
|
||||
|
||||
LOG(INFO) << "starting service '" << name_ << "'...";
|
||||
|
||||
pid_t pid = -1;
|
||||
|
@ -945,6 +959,15 @@ Result<Success> Service::Start() {
|
|||
LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
if (pre_apexd_) {
|
||||
if (!SwitchToBootstrapMountNamespaceIfNeeded()) {
|
||||
LOG(FATAL) << "Service '" << name_ << "' could not enter "
|
||||
<< "into the bootstrap mount namespace";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (namespace_flags_ & CLONE_NEWNS) {
|
||||
if (auto result = SetUpMountNamespace(); !result) {
|
||||
LOG(FATAL) << "Service '" << name_
|
||||
|
|
|
@ -242,6 +242,8 @@ class Service {
|
|||
std::vector<std::string> args_;
|
||||
|
||||
std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
|
||||
|
||||
bool pre_apexd_ = false;
|
||||
};
|
||||
|
||||
class ServiceList {
|
||||
|
|
|
@ -441,5 +441,9 @@ void InitKernelLogging(char** argv, std::function<void(const char*)> abort_funct
|
|||
android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function));
|
||||
}
|
||||
|
||||
bool IsRecoveryMode() {
|
||||
return access("/system/bin/recovery", F_OK) == 0;
|
||||
}
|
||||
|
||||
} // namespace init
|
||||
} // namespace android
|
||||
|
|
|
@ -64,7 +64,7 @@ bool is_android_dt_value_expected(const std::string& sub_path, const std::string
|
|||
bool IsLegalPropertyName(const std::string& name);
|
||||
|
||||
void InitKernelLogging(char** argv, std::function<void(const char*)> abort_function);
|
||||
|
||||
bool IsRecoveryMode();
|
||||
} // namespace init
|
||||
} // namespace android
|
||||
|
||||
|
|
|
@ -73,6 +73,8 @@ namespace.default.permitted.paths += /%PRODUCT_SERVICES%/app
|
|||
namespace.default.permitted.paths += /%PRODUCT_SERVICES%/priv-app
|
||||
namespace.default.permitted.paths += /data
|
||||
namespace.default.permitted.paths += /mnt/expand
|
||||
namespace.default.permitted.paths += /bionic/${LIB}
|
||||
namespace.default.permitted.paths += /system/${LIB}/bootstrap
|
||||
|
||||
namespace.default.asan.search.paths = /data/asan/system/${LIB}
|
||||
namespace.default.asan.search.paths += /system/${LIB}
|
||||
|
@ -104,6 +106,8 @@ namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/framework
|
|||
namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/app
|
||||
namespace.default.asan.permitted.paths += /%PRODUCT_SERVICES%/priv-app
|
||||
namespace.default.asan.permitted.paths += /mnt/expand
|
||||
namespace.default.asan.permitted.paths += /bionic/${LIB}
|
||||
namespace.default.asan.permitted.paths += /system/${LIB}/bootstrap
|
||||
|
||||
# Keep in sync with ld.config.txt in the com.android.runtime APEX.
|
||||
# If a shared library or an executable requests a shared library that
|
||||
|
|
|
@ -12,6 +12,12 @@ import /init.usb.configfs.rc
|
|||
import /init.${ro.zygote}.rc
|
||||
|
||||
on early-init
|
||||
# Mount shared so changes propagate into child namespaces
|
||||
# Do this before other processes are started from init. Otherwise,
|
||||
# processes launched while the propagation type of / is 'private'
|
||||
# won't get mount events from others.
|
||||
mount rootfs rootfs / shared rec
|
||||
|
||||
# Set init and its forked children's oom_adj.
|
||||
write /proc/1/oom_score_adj -1000
|
||||
|
||||
|
@ -346,8 +352,6 @@ on post-fs
|
|||
# Once everything is setup, no need to modify /.
|
||||
# The bind+remount combination allows this to work in containers.
|
||||
mount rootfs rootfs / remount bind ro nodev
|
||||
# Mount shared so changes propagate into child namespaces
|
||||
mount rootfs rootfs / shared rec
|
||||
# Mount default storage into root namespace
|
||||
mount none /mnt/runtime/default /storage bind rec
|
||||
mount none none /storage slave rec
|
||||
|
@ -583,6 +587,14 @@ on post-fs-data
|
|||
# Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
|
||||
exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo
|
||||
|
||||
# Wait for apexd to finish activating APEXes before starting more processes.
|
||||
# This certainly reduces the parallelism but is required to make as many processes
|
||||
# as possible to use the bionic libs from the runtime APEX. This takes less than 50ms
|
||||
# so the impact on the booting time is not significant.
|
||||
wait_for_prop apexd.status ready
|
||||
setup_runtime_bionic
|
||||
parse_apex_configs
|
||||
|
||||
# If there is no post-fs-data action in the init.<device>.rc file, you
|
||||
# must uncomment this line, otherwise encrypted filesystems
|
||||
# won't work.
|
||||
|
@ -804,6 +816,3 @@ on property:ro.debuggable=1
|
|||
service flash_recovery /system/bin/install-recovery.sh
|
||||
class main
|
||||
oneshot
|
||||
|
||||
on property:apexd.status=ready
|
||||
parse_apex_configs
|
||||
|
|
Loading…
Reference in a new issue