blob: eba8e241791a598501a3a8d2e7a7c91a88644507 [file] [log] [blame]
// Copyright 2016 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "arc/setup/arc_setup.h"
#include <fcntl.h>
#include <linux/magic.h>
#include <sched.h>
#include <selinux/selinux.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <unistd.h>
#include <algorithm>
#include <array>
#include <limits>
#include <memory>
#include <string>
#include <vector>
#include <base/command_line.h>
#include <base/environment.h>
#include <base/files/file_enumerator.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/logging.h>
#include <base/macros.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
#include <base/strings/stringprintf.h>
#include <base/sys_info.h>
#include <base/threading/platform_thread.h>
#include <base/time/time.h>
#include <base/timer/elapsed_timer.h>
#include <chromeos-config/libcros_config/cros_config.h>
#include <metrics/metrics_library.h>
#include "arc/setup/arc_read_ahead.h"
#include "arc/setup/arc_setup_metrics.h"
#include "arc/setup/art_container.h"
#define EXIT_IF(f) \
do { \
LOG(INFO) << "Running " << (#f) << "..."; \
CHECK(!(f)); \
} while (false)
#define IGNORE_ERRORS(f) \
do { \
LOG(INFO) << "Running " << (#f) << "..."; \
LOG_IF(INFO, !(f)) << "Ignoring failures: " << (#f); \
} while (false)
// TODO(yusukes): use android_filesystem_config.h.
#define AID_ROOT 0 /* traditional unix root user */
#define AID_SYSTEM 1000 /* system server */
#define AID_LOG 1007 /* log devices */
#define AID_MEDIA_RW 1023 /* internal media storage write access */
#define AID_SHELL 2000 /* adb and debug shell user */
#define AID_CACHE 2001 /* cache access */
namespace arc {
namespace {
// Lexicographically sorted. Usually you don't have to use these constants
// directly. Prefer base::FilePath variables in ArcPaths instead.
constexpr char kAndroidCmdline[] = "/run/arc/cmdline.android";
constexpr char kAndroidGeneratedPropertiesDirectory[] = "/run/arc/properties";
constexpr char kAndroidKmsgFifo[] = "/run/arc/android.kmsg.fifo";
constexpr char kAndroidMutableSource[] =
"/opt/google/containers/android/rootfs/android-data";
constexpr char kAndroidRootfsDirectory[] =
"/opt/google/containers/android/rootfs/root";
constexpr char kOldApkCacheDir[] =
"/mnt/stateful_partition/unencrypted/cache/apk";
constexpr char kApkCacheDir[] = "/mnt/stateful_partition/unencrypted/apkcache";
constexpr char kArcBridgeSocketContext[] = "u:object_r:arc_bridge_socket:s0";
constexpr char kArcBridgeSocketPath[] = "/run/chrome/arc_bridge.sock";
constexpr char kBinFmtMiscDirectory[] = "/proc/sys/fs/binfmt_misc";
constexpr char kCameraProfileDir[] =
"/mnt/stateful_partition/encrypted/var/cache/camera";
constexpr char kCrasSocketDirectory[] = "/run/cras";
constexpr char kDebugfsDirectory[] = "/run/arc/debugfs";
constexpr char kFakeKptrRestrict[] = "/run/arc/fake_kptr_restrict";
constexpr char kFakeMmapRndBits[] = "/run/arc/fake_mmap_rnd_bits";
constexpr char kFakeMmapRndCompatBits[] = "/run/arc/fake_mmap_rnd_compat_bits";
constexpr char kHostSideDalvikCacheDirectoryInContainer[] =
"/var/run/arc/dalvik-cache";
constexpr char kMediaDestDirectory[] = "/run/arc/media/removable";
constexpr char kMediaMountDirectory[] = "/run/arc/media";
constexpr char kMediaProfileFile[] = "media_profiles.xml";
constexpr char kMediaRootfsDirectory[] =
"/opt/google/containers/arc-removable-media/mountpoints/container-root";
constexpr char kObbMountDirectory[] = "/run/arc/obb";
constexpr char kObbRootfsDirectory[] =
"/opt/google/containers/arc-obb-mounter/mountpoints/container-root";
constexpr char kObbRootfsImage[] =
"/opt/google/containers/arc-obb-mounter/rootfs.squashfs";
constexpr char kOemMountDirectory[] = "/run/arc/oem";
constexpr char kPassthroughImage[] =
"/usr/share/mount-passthrough/rootfs.squashfs";
constexpr char kPlatformXmlFileRelative[] = "etc/permissions/platform.xml";
constexpr char kRestoreconWhitelistSync[] = "/sys/kernel/debug/sync";
constexpr char kSdcardMountDirectory[] = "/run/arc/sdcard";
constexpr char kSdcardRootfsDirectory[] =
"/opt/google/containers/arc-sdcard/mountpoints/container-root";
constexpr char kSdcardRootfsImage[] =
"/opt/google/containers/arc-sdcard/rootfs.squashfs";
constexpr char kSharedMountDirectory[] = "/run/arc/shared_mounts";
constexpr char kSysfsCpu[] = "/sys/devices/system/cpu";
constexpr char kSysfsTracing[] = "/sys/kernel/debug/tracing";
constexpr char kSystemLibArmDirectoryRelative[] = "system/lib/arm";
constexpr char kSystemImage[] = "/opt/google/containers/android/system.raw.img";
constexpr char kUsbDevicesDirectory[] = "/dev/bus/usb";
// Names for possible binfmt_misc entries.
constexpr const char* kBinFmtMiscEntryNames[] = {"arm_dyn", "arm_exe",
"arm64_dyn", "arm64_exe"};
constexpr uid_t kHostRootUid = 0;
constexpr gid_t kHostRootGid = 0;
constexpr uid_t kShiftUid = 655360;
constexpr gid_t kShiftGid = 655360;
constexpr uid_t kRootUid = AID_ROOT + kShiftUid;
constexpr gid_t kRootGid = AID_ROOT + kShiftGid;
constexpr uid_t kSystemUid = AID_SYSTEM + kShiftUid;
constexpr gid_t kSystemGid = AID_SYSTEM + kShiftGid;
constexpr uid_t kMediaUid = AID_MEDIA_RW + kShiftUid;
constexpr gid_t kMediaGid = AID_MEDIA_RW + kShiftGid;
constexpr uid_t kShellUid = AID_SHELL + kShiftUid;
constexpr gid_t kCacheGid = AID_CACHE + kShiftGid;
constexpr gid_t kLogGid = AID_LOG + kShiftGid;
constexpr char kSwitchSetup[] = "setup";
constexpr char kSwitchSetupForLoginScreen[] = "setup-for-login-screen";
constexpr char kSwitchBootContinue[] = "boot-continue";
constexpr char kSwitchStop[] = "stop";
constexpr char kSwitchOnetimeSetup[] = "onetime-setup";
constexpr char kSwitchOnetimeStop[] = "onetime-stop";
constexpr char kSwitchPreChroot[] = "pre-chroot";
constexpr char kSwitchReadAhead[] = "read-ahead";
// The maximum time arc::EmulateArcUreadahead() can spend.
constexpr base::TimeDelta kReadAheadTimeout = base::TimeDelta::FromSeconds(7);
enum class Mode {
SETUP = 0,
SETUP_FOR_LOGIN_SCREEN,
BOOT_CONTINUE,
STOP,
ONETIME_SETUP,
ONETIME_STOP,
PRE_CHROOT,
READ_AHEAD,
UNKNOWN,
};
Mode GetMode() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(kSwitchSetup))
return Mode::SETUP;
if (command_line->HasSwitch(kSwitchSetupForLoginScreen))
return Mode::SETUP_FOR_LOGIN_SCREEN;
if (command_line->HasSwitch(kSwitchBootContinue))
return Mode::BOOT_CONTINUE;
if (command_line->HasSwitch(kSwitchStop))
return Mode::STOP;
if (command_line->HasSwitch(kSwitchOnetimeSetup))
return Mode::ONETIME_SETUP;
if (command_line->HasSwitch(kSwitchOnetimeStop))
return Mode::ONETIME_STOP;
if (command_line->HasSwitch(kSwitchPreChroot))
return Mode::PRE_CHROOT;
if (command_line->HasSwitch(kSwitchReadAhead))
return Mode::READ_AHEAD;
CHECK(false) << "Missing mode";
return Mode::UNKNOWN;
}
bool RegisterAllBinFmtMiscEntries(ArcMounter* mounter,
const base::FilePath& entry_directory,
const base::FilePath& binfmt_misc_directory) {
std::unique_ptr<ScopedMount> binfmt_misc_mount =
ScopedMount::CreateScopedMount(mounter, "binfmt_misc",
binfmt_misc_directory, "binfmt_misc",
MS_NOSUID | MS_NODEV | MS_NOEXEC, nullptr);
if (!binfmt_misc_mount)
return false;
const base::FilePath binfmt_misc_register_path =
binfmt_misc_directory.Append("register");
for (auto entry_name : kBinFmtMiscEntryNames) {
const base::FilePath entry_path = entry_directory.Append(entry_name);
// arm64_{dyn,exe} are only available on some boards/configurations. Only
// install them if they are present.
if (!base::PathExists(entry_path))
continue;
if (!base::CopyFile(entry_path, binfmt_misc_register_path)) {
PLOG(ERROR) << "Failed to register " << entry_path.value();
return false;
}
}
return true;
}
void UnregisterBinFmtMiscEntry(const base::FilePath& entry_path) {
// This function is for Mode::STOP. Ignore errors to make sure to run all
// clean up code.
base::File entry(entry_path, base::File::FLAG_OPEN | base::File::FLAG_WRITE);
if (!entry.IsValid()) {
PLOG(INFO) << "Ignoring failure: Failed to open " << entry_path.value();
return;
}
constexpr char kBinfmtMiscUnregister[] = "-1";
IGNORE_ERRORS(
entry.Write(0, kBinfmtMiscUnregister, sizeof(kBinfmtMiscUnregister) - 1));
}
// Prepends |path_to_prepend| to each element in [first, last), and returns the
// result as a vector.
template <typename It>
std::vector<base::FilePath> PrependPath(It first,
It last,
const base::FilePath& path_to_prepend) {
std::vector<base::FilePath> result;
std::transform(first, last, std::back_inserter(result),
[&path_to_prepend](const char* path) {
return path_to_prepend.Append(path);
});
return result;
}
} // namespace
struct ArcPaths {
std::unique_ptr<base::Environment> env{base::Environment::Create()};
const Mode mode{GetMode()};
// Lexicographically sorted.
const base::FilePath android_cmdline{kAndroidCmdline};
const base::FilePath android_generated_properties_directory{
kAndroidGeneratedPropertiesDirectory};
const base::FilePath android_kmsg_fifo{kAndroidKmsgFifo};
const base::FilePath android_mutable_source{kAndroidMutableSource};
const base::FilePath android_rootfs_directory{kAndroidRootfsDirectory};
const base::FilePath arc_bridge_socket_path{kArcBridgeSocketPath};
const base::FilePath old_apk_cache_dir{kOldApkCacheDir};
const base::FilePath apk_cache_dir{kApkCacheDir};
const base::FilePath art_container_data_directory{kArtContainerDataDirectory};
const base::FilePath binfmt_misc_directory{kBinFmtMiscDirectory};
const base::FilePath camera_profile_dir{kCameraProfileDir};
const base::FilePath cras_socket_directory{kCrasSocketDirectory};
const base::FilePath debugfs_directory{kDebugfsDirectory};
const base::FilePath fake_kptr_restrict{kFakeKptrRestrict};
const base::FilePath fake_mmap_rnd_bits{kFakeMmapRndBits};
const base::FilePath fake_mmap_rnd_compat_bits{kFakeMmapRndCompatBits};
const base::FilePath host_side_dalvik_cache_directory_in_container{
kHostSideDalvikCacheDirectoryInContainer};
const base::FilePath media_dest_directory{kMediaDestDirectory};
const base::FilePath media_mount_directory{kMediaMountDirectory};
const base::FilePath media_profile_file{kMediaProfileFile};
const base::FilePath media_rootfs_directory{kMediaRootfsDirectory};
const base::FilePath obb_mount_directory{kObbMountDirectory};
const base::FilePath obb_rootfs_directory{kObbRootfsDirectory};
const base::FilePath oem_mount_directory{kOemMountDirectory};
const base::FilePath platform_xml_file_relative{kPlatformXmlFileRelative};
const base::FilePath sdcard_mount_directory{kSdcardMountDirectory};
const base::FilePath sdcard_rootfs_directory{kSdcardRootfsDirectory};
const base::FilePath shared_mount_directory{kSharedMountDirectory};
const base::FilePath sysfs_cpu{kSysfsCpu};
const base::FilePath sysfs_tracing{kSysfsTracing};
const base::FilePath system_lib_arm_directory_relative{
kSystemLibArmDirectoryRelative};
const base::FilePath usb_devices_directory{kUsbDevicesDirectory};
const base::FilePath restorecon_whitelist_sync{kRestoreconWhitelistSync};
// session_manager must start arc-setup job with ANDROID_DATA_DIR parameter
// containing the path of the real android-data directory. They are passed
// only when the mode is either --setup or --boot-continue.
const bool kHasAndroidDataDir =
mode == Mode::SETUP || mode == Mode::BOOT_CONTINUE;
const base::FilePath android_data_directory =
kHasAndroidDataDir ? GetFilePathOrDie(env.get(), "ANDROID_DATA_DIR")
: base::FilePath();
const base::FilePath android_data_old_directory =
kHasAndroidDataDir ? GetFilePathOrDie(env.get(), "ANDROID_DATA_OLD_DIR")
: base::FilePath();
};
ArcSetup::ArcSetup()
: arc_mounter_(GetDefaultMounter()),
arc_paths_(std::make_unique<ArcPaths>()),
arc_setup_metrics_(std::make_unique<ArcSetupMetrics>()) {}
ArcSetup::~ArcSetup() = default;
void ArcSetup::DeleteExecutableFilesInData(
bool should_delete_data_dalvik_cache_directory,
bool should_delete_data_app_executables) {
if (!should_delete_data_dalvik_cache_directory &&
!should_delete_data_app_executables) {
return;
}
if (!base::PathExists(arc_paths_->android_data_old_directory)) {
EXIT_IF(!InstallDirectory(0700, kHostRootUid, kHostRootGid,
arc_paths_->android_data_old_directory));
}
base::FilePath old_executables_directory;
EXIT_IF(!base::CreateTemporaryDirInDir(arc_paths_->android_data_old_directory,
"old_executables_",
&old_executables_directory));
// Move data/dalvik-cache to old_executables_directory.
const base::FilePath dalvik_cache_directory =
arc_paths_->android_data_directory.Append(
base::FilePath("data/dalvik-cache"));
if (should_delete_data_dalvik_cache_directory &&
base::PathExists(dalvik_cache_directory)) {
LOG(INFO) << "Moving " << dalvik_cache_directory.value() << " to "
<< old_executables_directory.value();
if (!base::Move(dalvik_cache_directory, old_executables_directory))
PLOG(ERROR) << "Failed to move dalvik-cache";
}
// Move data/app/oat cache.
const base::FilePath app_directory =
arc_paths_->android_data_directory.Append(base::FilePath("data/app"));
if (should_delete_data_app_executables && base::PathExists(app_directory)) {
base::ElapsedTimer timer;
MoveDataAppOatDirectory(app_directory, old_executables_directory);
LOG(INFO) << "Moving data/app/<package_name>/oat took "
<< timer.Elapsed().InMillisecondsRoundedUp() << "ms";
}
}
void ArcSetup::WaitForRtLimitsJob() {
constexpr base::TimeDelta kWaitForRtLimitsJobTimeOut =
base::TimeDelta::FromSeconds(10);
constexpr base::TimeDelta kSleepInterval =
base::TimeDelta::FromMilliseconds(100);
constexpr char kCgroupFilePath[] =
"/sys/fs/cgroup/cpu/session_manager_containers/cpu.rt_runtime_us";
base::ElapsedTimer timer;
const base::FilePath cgroup_file(kCgroupFilePath);
while (true) {
if (base::PathExists(cgroup_file)) {
std::string rt_runtime_us_str;
EXIT_IF(!base::ReadFileToString(cgroup_file, &rt_runtime_us_str));
int rt_runtime_us = 0;
base::StringToInt(rt_runtime_us_str, &rt_runtime_us);
if (rt_runtime_us > 0) {
LOG(INFO) << cgroup_file.value() << " is set to " << rt_runtime_us;
break;
}
}
base::PlatformThread::Sleep(kSleepInterval);
CHECK_GE(kWaitForRtLimitsJobTimeOut, timer.Elapsed())
<< ": rt-limits job didn't start in " << kWaitForRtLimitsJobTimeOut;
}
LOG(INFO) << "rt-limits job is ready in "
<< timer.Elapsed().InMillisecondsRoundedUp() << " ms";
}
ArcBinaryTranslationType ArcSetup::IdentifyBinaryTranslationType() {
const bool is_houdini_available = kUseHoudini;
bool is_ndk_translation_available = kUseNdkTranslation;
if (!base::PathExists(arc_paths_->android_rootfs_directory.Append(
"system/lib/libndk_translation.so"))) {
// Allow developers to use custom android build
// without ndk-translation in it.
is_ndk_translation_available = false;
}
if (!is_houdini_available && !is_ndk_translation_available)
return ArcBinaryTranslationType::NONE;
const bool prefer_ndk_translation =
(!is_houdini_available ||
GetBooleanEnvOrDie(arc_paths_->env.get(), "NATIVE_BRIDGE_EXPERIMENT"));
if (is_ndk_translation_available && prefer_ndk_translation)
return ArcBinaryTranslationType::NDK_TRANSLATION;
return ArcBinaryTranslationType::HOUDINI;
}
void ArcSetup::SetUpBinFmtMisc(ArcBinaryTranslationType bin_type) {
const std::string system_arch = base::SysInfo::OperatingSystemArchitecture();
if (system_arch != "x86_64")
return;
base::FilePath root_directory;
switch (bin_type) {
case ArcBinaryTranslationType::NONE: {
// No binary translation at all, neither Houdini nor NDK translation.
return;
}
case ArcBinaryTranslationType::HOUDINI: {
root_directory = arc_paths_->android_rootfs_directory.Append("vendor");
break;
}
case ArcBinaryTranslationType::NDK_TRANSLATION: {
root_directory = arc_paths_->android_rootfs_directory.Append("system");
break;
}
}
EXIT_IF(!RegisterAllBinFmtMiscEntries(
arc_mounter_.get(), root_directory.Append("etc/binfmt_misc"),
arc_paths_->binfmt_misc_directory));
}
void ArcSetup::SetUpAndroidData() {
EXIT_IF(!InstallDirectory(0700, kHostRootUid, kHostRootGid,
arc_paths_->android_data_directory));
// To make our bind-mount business easier, we first bind-mount the real
// android-data directory ($ANDROID_DATA_DIR) to a fixed path
// ($ANDROID_MUTABLE_SOURCE).
// Then we do not need to pass around $ANDROID_DATA_DIR in every other places.
EXIT_IF(!arc_mounter_->BindMount(arc_paths_->android_data_directory,
arc_paths_->android_mutable_source));
// match android/system/core/rootdir/init.rc
EXIT_IF(!InstallDirectory(0771, kSystemUid, kSystemGid,
arc_paths_->android_mutable_source.Append("data")));
EXIT_IF(
!InstallDirectory(0770, kSystemUid, kCacheGid,
arc_paths_->android_mutable_source.Append("cache")));
SetUpPackagesCache();
if (kUseMasterContainer)
SetUpNetwork();
}
void ArcSetup::SetUpPackagesCache() {
if (GetBooleanEnvOrDie(arc_paths_->env.get(), "SKIP_PACKAGES_CACHE_SETUP")) {
LOG(INFO) << "Packages cache setup is disabled.";
return;
}
// When /data/system/packages.xml does not exist, copy pre-generated
// /system/etc/packages_cache.xml to /data/system/packages.xml
const base::FilePath packages_cache =
arc_paths_->android_mutable_source.Append("data/system/packages.xml");
if (base::PathExists(packages_cache))
return;
const base::FilePath source_cache =
arc_paths_->android_rootfs_directory.Append(
"system/etc/packages_cache.xml");
// Test if packages cache exists. Manually pushed images may not contain it.
if (!base::PathExists(source_cache)) {
LOG(INFO) << "Packages cache was not found "
<< "(this expected for manually-pushed images).";
return;
}
LOG(INFO) << "Installing packages cache to " << packages_cache.value() << ".";
EXIT_IF(!InstallDirectory(0775, kSystemUid, kSystemGid,
packages_cache.DirName()));
EXIT_IF(!base::CopyFile(source_cache, packages_cache));
EXIT_IF(!Chown(kSystemUid, kSystemGid, packages_cache));
EXIT_IF(!base::SetPosixFilePermissions(packages_cache, 0660));
}
void ArcSetup::SetUpDalvikCacheInternal(
const base::FilePath& dalvik_cache_directory,
const base::FilePath& isa_relative) {
const base::FilePath dest_directory =
arc_paths_->android_mutable_source.Append("data/dalvik-cache")
.Append(isa_relative);
LOG(INFO) << "Setting up " << dest_directory.value();
EXIT_IF(!base::PathExists(dest_directory));
base::FilePath src_directory = dalvik_cache_directory.Append(isa_relative);
EXIT_IF(!arc_mounter_->BindMount(src_directory, dest_directory));
}
void ArcSetup::SetUpDalvikCache() {
const base::FilePath dalvik_cache_directory =
arc_paths_->art_container_data_directory.Append("dalvik-cache");
if (!base::PathExists(dalvik_cache_directory)) {
LOG(INFO) << dalvik_cache_directory.value() << " does not exist.";
return;
}
base::FileEnumerator directories(dalvik_cache_directory,
false /* recursive */,
base::FileEnumerator::FileType::DIRECTORIES);
for (base::FilePath name = directories.Next(); !name.empty();
name = directories.Next()) {
SetUpDalvikCacheInternal(dalvik_cache_directory, name.BaseName());
}
}
void ArcSetup::CleanUpDalvikCache() {
const base::FilePath dalvik_cache_directory =
arc_paths_->android_mutable_source.Append("data/dalvik-cache");
IGNORE_ERRORS(
arc_mounter_->UmountLazily(dalvik_cache_directory.Append("arm")));
IGNORE_ERRORS(
arc_mounter_->UmountLazily(dalvik_cache_directory.Append("x86")));
IGNORE_ERRORS(
arc_mounter_->UmountLazily(dalvik_cache_directory.Append("x86_64")));
}
void ArcSetup::CreateContainerFilesAndDirectories() {
EXIT_IF(!InstallDirectory(0755, kHostRootUid, kHostRootGid,
base::FilePath("/run/arc")));
EXIT_IF(!InstallDirectory(0755, kShellUid, kLogGid,
base::FilePath("/run/arc/bugreport")));
// If the log file exists, change the UID/GID here. We used to use
// android-root for the file, but now we use just root. The Upstart
// job does not (and cannot efficiently) do it.
// TODO(yusukes): This is a temporary migration code. Remove it once
// we hit M68.
const base::FilePath android_kmsg("/var/log/android.kmsg");
if (base::PathExists(android_kmsg))
EXIT_IF(!Chown(kHostRootUid, kHostRootGid, android_kmsg));
// Create the FIFO file and start its reader job.
RemoveAndroidKmsgFifo();
EXIT_IF(mkfifo(arc_paths_->android_kmsg_fifo.value().c_str(), 0644) < 0);
{
base::ScopedFD fd =
OpenFifoSafely(arc_paths_->android_kmsg_fifo, O_RDONLY, 0);
EXIT_IF(!fd.is_valid());
EXIT_IF(fchown(fd.get(), kRootUid, kRootGid) < 0);
}
EXIT_IF(!LaunchAndWait(
{"/sbin/initctl", "start", "--no-wait", "arc-kmsg-logger"}));
// TODO(b/28988348)
EXIT_IF(!base::SetPosixFilePermissions(base::FilePath("/run/chrome"), 0755));
}
void ArcSetup::ApplyPerBoardConfigurations() {
EXIT_IF(!MkdirRecursively(arc_paths_->oem_mount_directory));
const base::FilePath hardware_features_xml("/etc/hardware_features.xml");
if (!base::PathExists(hardware_features_xml))
return;
EXIT_IF(!arc_mounter_->Mount("tmpfs", arc_paths_->oem_mount_directory,
"tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC,
"mode=0755"));
EXIT_IF(!MkdirRecursively(
arc_paths_->oem_mount_directory.Append("etc/permissions")));
// Detect camera device and generate camera profiles.
const base::FilePath generate_camera_profile(
"/usr/bin/generate_camera_profile");
if (base::PathExists(generate_camera_profile)) {
EXIT_IF(!LaunchAndWait({generate_camera_profile.value()}));
const base::FilePath generated_media_profile_xml =
base::FilePath(arc_paths_->camera_profile_dir)
.Append(arc_paths_->media_profile_file);
const base::FilePath new_media_profile_xml =
base::FilePath(arc_paths_->oem_mount_directory)
.Append("etc")
.Append(arc_paths_->media_profile_file);
if (base::PathExists(generated_media_profile_xml)) {
EXIT_IF(
!base::CopyFile(generated_media_profile_xml, new_media_profile_xml));
}
}
const base::FilePath platform_xml_file =
base::FilePath(arc_paths_->oem_mount_directory)
.Append(arc_paths_->platform_xml_file_relative);
EXIT_IF(!base::CopyFile(hardware_features_xml, platform_xml_file));
const base::FilePath board_hardware_features(
"/usr/sbin/board_hardware_features");
if (!base::PathExists(board_hardware_features))
return;
// The board_hardware_features is usually made by shell script and should
// receive platform XML file argument in absolute path to avoid unexpected
// environment issues.
EXIT_IF(!LaunchAndWait(
{board_hardware_features.value(), platform_xml_file.value()}));
}
void ArcSetup::CreateBuildProperties() {
EXIT_IF(
!MkdirRecursively(arc_paths_->android_generated_properties_directory));
// InitModel won't succeed on non-unibuild boards, but that doesn't matter
// because the property files won't contain any templates that need to be
// expanded. On unibuild boards, if it doesn't succeed then
// ExpandPropertyFile() will later fail when it can't look up the template
// expansions. Either way, errors here should be ignored.
auto config = std::make_unique<brillo::CrosConfig>();
IGNORE_ERRORS(config->InitModel());
constexpr const char* prop_files[] = {"default.prop", "system/build.prop"};
for (const auto& prop_file : prop_files) {
const base::FilePath in_prop =
arc_paths_->android_rootfs_directory.Append(prop_file);
const base::FilePath expanded_prop =
arc_paths_->android_generated_properties_directory.Append(
in_prop.BaseName());
ExpandPropertyFile(in_prop, expanded_prop, config.get());
}
}
void ArcSetup::ExpandPropertyFile(const base::FilePath& input,
const base::FilePath& output,
brillo::CrosConfigInterface* config) {
std::string content;
std::string expanded;
EXIT_IF(!base::ReadFileToString(input, &content));
EXIT_IF(!ExpandPropertyContents(content, config, &expanded));
EXIT_IF(!WriteToFile(output, 0600, expanded));
EXIT_IF(!Chown(kRootUid, kRootGid, output));
}
void ArcSetup::MaybeStartUreadaheadInTracingMode() {
const base::FilePath readahead_pack_file(
"/var/lib/ureadahead/opt.google.containers.android.rootfs.root.pack");
if (!base::PathExists(readahead_pack_file)) {
// We should continue to launch the container even if arc-ureadahead-trace
// fails to start (b/31680524).
IGNORE_ERRORS(
LaunchAndWait({"/sbin/initctl", "start", "arc-ureadahead-trace"}));
}
}
void ArcSetup::SetUpSharedTmpfsForExternalStorage() {
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->sdcard_mount_directory));
EXIT_IF(!MkdirRecursively(arc_paths_->sdcard_mount_directory));
EXIT_IF(!arc_mounter_->Mount("tmpfs", arc_paths_->sdcard_mount_directory,
"tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC,
"mode=0755"));
EXIT_IF(!arc_mounter_->SharedMount(arc_paths_->sdcard_mount_directory));
EXIT_IF(
!InstallDirectory(0755, kRootUid, kRootGid,
arc_paths_->sdcard_mount_directory.Append("default")));
EXIT_IF(!InstallDirectory(0755, kRootUid, kRootGid,
arc_paths_->sdcard_mount_directory.Append("read")));
EXIT_IF(
!InstallDirectory(0755, kRootUid, kRootGid,
arc_paths_->sdcard_mount_directory.Append("write")));
// Create the mount directories. In original Android, these are created in
// EmulatedVolume.cpp just before /system/bin/sdcard is fork()/exec()'ed.
// Following code just emulates it. The directories are owned by Android's
// root.
// Note that, these creation should be conceptually done in arc-sdcard
// container, but to keep it simpler, here create the directories instead.
EXIT_IF(!InstallDirectory(
0755, kRootUid, kRootGid,
arc_paths_->sdcard_mount_directory.Append("default/emulated")));
EXIT_IF(!InstallDirectory(
0755, kRootUid, kRootGid,
arc_paths_->sdcard_mount_directory.Append("read/emulated")));
EXIT_IF(!InstallDirectory(
0755, kRootUid, kRootGid,
arc_paths_->sdcard_mount_directory.Append("write/emulated")));
}
void ArcSetup::SetUpFilesystemForObbMounter() {
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->obb_mount_directory));
EXIT_IF(!MkdirRecursively(arc_paths_->obb_mount_directory));
EXIT_IF(!arc_mounter_->Mount("tmpfs", arc_paths_->obb_mount_directory,
"tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC,
"mode=0755"));
EXIT_IF(!arc_mounter_->SharedMount(arc_paths_->obb_mount_directory));
}
bool ArcSetup::GenerateHostSideCodeInternal(
const base::FilePath& host_dalvik_cache_directory) {
base::ElapsedTimer timer;
std::unique_ptr<ArtContainer> art_container =
ArtContainer::CreateContainer(arc_mounter_.get());
if (!art_container) {
LOG(ERROR) << "Failed to create art container";
return false;
}
const std::string salt = GetSalt();
if (salt.empty())
return false;
const uint64_t offset_seed =
GetArtCompilationOffsetSeed(GetSystemImageFingerprint(), salt);
if (!art_container->PatchImage(offset_seed)) {
LOG(ERROR) << "Failed to relocate boot images";
return false;
}
arc_setup_metrics_->SendCodeRelocationTime(timer.Elapsed());
return true;
}
bool ArcSetup::GenerateHostSideCode(
const base::FilePath& host_dalvik_cache_directory) {
bool result = true;
base::ElapsedTimer timer;
if (!GenerateHostSideCodeInternal(host_dalvik_cache_directory)) {
// If anything fails, delete code in cache.
LOG(INFO) << "Failed to generate host-side code. Deleting existing code in "
<< host_dalvik_cache_directory.value();
DeleteFilesInDir(host_dalvik_cache_directory);
result = false;
}
base::TimeDelta time_delta = timer.Elapsed();
LOG(INFO) << "GenerateHostSideCode took "
<< time_delta.InMillisecondsRoundedUp() << "ms";
arc_setup_metrics_->SendCodeRelocationResult(
result ? ArcCodeRelocationResult::SUCCESS
: ArcCodeRelocationResult::ERROR_UNABLE_TO_RELOCATE);
return result;
}
bool ArcSetup::InstallLinksToHostSideCodeInternal(
const base::FilePath& src_isa_directory,
const base::FilePath& dest_isa_directory,
const std::string& isa) {
constexpr char kDalvikCacheSELinuxContext[] =
"u:object_r:dalvikcache_data_file:s0";
bool src_file_exists = false;
LOG(INFO) << "Adding symlinks to " << dest_isa_directory.value();
// Do the same as maybeCreateDalvikCache() in
// framework/base/cmds/app_process/app_main.cpp.
EXIT_IF(!InstallDirectory(0711, kRootUid, kRootGid, dest_isa_directory));
EXIT_IF(!Chcon(kDalvikCacheSELinuxContext, dest_isa_directory));
base::FileEnumerator src_file_iter(
src_isa_directory, false /* recursive */,
base::FileEnumerator::FILES | base::FileEnumerator::SHOW_SYM_LINKS);
for (auto src_file = src_file_iter.Next(); !src_file.empty();
src_file = src_file_iter.Next()) {
const base::FilePath base_name = src_file.BaseName();
LOG(INFO) << "Processing " << base_name.value();
base::FilePath link_target;
if (S_ISLNK(src_file_iter.GetInfo().stat().st_mode)) {
// *boot*.oat files in |src_isa_directory| are links to /system. Create a
// link to /system.
EXIT_IF(!base::ReadSymbolicLink(src_file, &link_target));
} else {
// Create a link to a host-side *boot*.art file.
link_target =
arc_paths_->host_side_dalvik_cache_directory_in_container.Append(isa)
.Append(base_name);
}
const base::FilePath dest_file = dest_isa_directory.Append(base_name);
// Remove |dest_file| first when it exists. When |dest_file| is a symlink,
// this deletes the link itself.
IGNORE_ERRORS(base::DeleteFile(dest_file, false /* recursive */));
EXIT_IF(!base::CreateSymbolicLink(link_target, dest_file));
EXIT_IF(lchown(dest_file.value().c_str(), kRootUid, kRootGid) != 0);
EXIT_IF(!Chcon(kDalvikCacheSELinuxContext, dest_file));
LOG(INFO) << "Created a link to " << link_target.value();
src_file_exists = true;
}
return src_file_exists;
}
bool ArcSetup::InstallLinksToHostSideCode() {
bool result = true;
base::ElapsedTimer timer;
const base::FilePath src_directory =
arc_paths_->art_container_data_directory.Append("dalvik-cache");
const base::FilePath dest_directory =
arc_paths_->android_data_directory.Append("data/dalvik-cache");
EXIT_IF(!InstallDirectory(0771, kRootUid, kRootGid, dest_directory));
// Iterate through each isa sub directory. For example, dalvik-cache/x86 and
// dalvik-cache/x86_64
base::FileEnumerator src_directory_iter(src_directory, false,
base::FileEnumerator::DIRECTORIES);
for (auto src_isa_directory = src_directory_iter.Next();
!src_isa_directory.empty();
src_isa_directory = src_directory_iter.Next()) {
const std::string isa = src_isa_directory.BaseName().value();
if (!InstallLinksToHostSideCodeInternal(src_isa_directory,
dest_directory.Append(isa), isa)) {
result = false;
LOG(INFO) << "InstallLinksToHostSideCodeInternal() for " << isa
<< " failed. "
<< "Deleting container's /data/dalvik-cache...";
DeleteExecutableFilesInData(true, /* delete dalvik cache */
false /* delete data app executables */);
break;
}
}
LOG(INFO) << "InstallLinksToHostSideCode() took "
<< timer.Elapsed().InMillisecondsRoundedUp() << "ms";
return result;
}
void ArcSetup::CreateAndroidCmdlineFile(bool for_login_screen,
bool is_dev_mode,
bool is_inside_vm,
bool is_debuggable,
ArcBootType boot_type) {
const base::FilePath lsb_release_file_path("/etc/lsb-release");
LOG(INFO) << "Developer mode is " << is_dev_mode;
LOG(INFO) << "Inside VM is " << is_inside_vm;
LOG(INFO) << "Debuggable is " << is_debuggable;
const std::string chromeos_channel =
GetChromeOsChannelFromFile(lsb_release_file_path);
LOG(INFO) << "ChromeOS channel is \"" << chromeos_channel << "\"";
const std::string arc_lcd_density =
GetEnvOrDie(arc_paths_->env.get(), "ARC_LCD_DENSITY");
LOG(INFO) << "lcd_density is " << arc_lcd_density;
const std::string arc_ui_scale =
GetEnvOrDie(arc_paths_->env.get(), "ARC_UI_SCALE");
LOG(INFO) << "ui_scale is " << arc_ui_scale;
const std::string arc_container_ipv4_address =
GetEnvOrDie(arc_paths_->env.get(), "ARC_CONTAINER_IPV4_ADDRESS");
const std::string arc_gateway_ipv4_address =
GetEnvOrDie(arc_paths_->env.get(), "ARC_GATEWAY_IPV4_ADDRESS");
// When |for_login_screen| is true, don't set all properties. The serialno
// is unknown until the user's /home is mounted. |disable_boot_completed|,
// |vendor_privileged|, and |container_boot_type| are unnecessary until
// system_server starts.
std::string serialno;
std::string disable_boot_completed;
std::string vendor_privileged;
std::string container_boot_type;
std::string copy_packages_cache;
if (!for_login_screen) {
serialno = base::StringPrintf("androidboot.serialno=%s ",
GetSerialNumber().c_str());
disable_boot_completed = base::StringPrintf(
"androidboot.disable_boot_completed=%d ",
GetBooleanEnvOrDie(arc_paths_->env.get(),
"DISABLE_BOOT_COMPLETED_BROADCAST"));
vendor_privileged = base::StringPrintf(
"androidboot.vendor_privileged=%d ",
GetBooleanEnvOrDie(arc_paths_->env.get(), "ENABLE_VENDOR_PRIVILEGED"));
container_boot_type =
base::StringPrintf("androidboot.container_boot_type=%d ", boot_type);
copy_packages_cache = base::StringPrintf(
"androidboot.copy.packages.cache=%d ",
GetBooleanEnvOrDie(arc_paths_->env.get(), "COPY_PACKAGES_CACHE"));
}
std::string native_bridge;
switch (IdentifyBinaryTranslationType()) {
case ArcBinaryTranslationType::NONE:
native_bridge = "0";
break;
case ArcBinaryTranslationType::HOUDINI:
native_bridge = "libhoudini.so";
break;
case ArcBinaryTranslationType::NDK_TRANSLATION:
native_bridge = "libndk_translation.so";
break;
}
LOG(INFO) << "native_bridge is \"" << native_bridge << "\"";
std::string content;
// Note that we are intentionally not setting the ro.kernel.qemu property
// since that is tied to running the Android emulator, which has a few key
// differences:
// * It assumes that ADB is connected through the qemu pipe, which is not
// true in Chrome OS' case.
// * It controls whether the emulated GLES implementation should be used
// (but can be overriden by setting ro.kernel.qemu.gles to -1).
// * It disables a bunch of pixel formats and uses only RGB565.
// * It disables Bluetooth (which we might do regardless).
content = base::StringPrintf(
"androidboot.hardware=cheets "
"androidboot.container=1 "
"androidboot.dev_mode=%d "
"androidboot.vm=%d "
"androidboot.debuggable=%d "
"androidboot.lcd_density=%s "
"androidboot.ui_scale=%s "
"androidboot.container_ipv4_address=%s "
"androidboot.gateway_ipv4_address=%s "
"androidboot.partial_boot=%d "
"androidboot.native_bridge=%s "
"androidboot.chromeos_channel=%s "
"%s" // serialno
"%s" // disable_boot_completed
"%s" // vendor_privileged
"%s" // container_boot_type
"%s" // copy_packages_cache
"\n",
is_dev_mode, is_inside_vm, is_debuggable, arc_lcd_density.c_str(),
arc_ui_scale.c_str(), arc_container_ipv4_address.c_str(),
arc_gateway_ipv4_address.c_str(), for_login_screen, native_bridge.c_str(),
chromeos_channel.c_str(), serialno.c_str(),
disable_boot_completed.c_str(), vendor_privileged.c_str(),
container_boot_type.c_str(), copy_packages_cache.c_str());
EXIT_IF(!WriteToFile(arc_paths_->android_cmdline, 0644, content));
}
void ArcSetup::CreateFakeProcfsFiles() {
// Android attempts to modify these files in procfs during init
// Since these files on the host side require root permissions to modify (real
// root, not android-root), we need to present fake versions to Android.
constexpr char kProcSecurityContext[] = "u:object_r:proc_security:s0";
EXIT_IF(!WriteToFile(arc_paths_->fake_kptr_restrict, 0644, "2\n"));
EXIT_IF(!Chown(kRootUid, kRootGid, arc_paths_->fake_kptr_restrict));
EXIT_IF(!Chcon(kProcSecurityContext, arc_paths_->fake_kptr_restrict));
EXIT_IF(!WriteToFile(arc_paths_->fake_mmap_rnd_bits, 0644, "32\n"));
EXIT_IF(!Chown(kRootUid, kRootGid, arc_paths_->fake_mmap_rnd_bits));
EXIT_IF(!Chcon(kProcSecurityContext, arc_paths_->fake_mmap_rnd_bits));
EXIT_IF(!WriteToFile(arc_paths_->fake_mmap_rnd_compat_bits, 0644, "16\n"));
EXIT_IF(!Chown(kRootUid, kRootGid, arc_paths_->fake_mmap_rnd_compat_bits));
EXIT_IF(!Chcon(kProcSecurityContext, arc_paths_->fake_mmap_rnd_compat_bits));
}
void ArcSetup::SetUpMountPointForDebugFilesystem(bool is_dev_mode) {
const base::FilePath sync_mount_directory =
arc_paths_->debugfs_directory.Append("sync");
EXIT_IF(!InstallDirectory(0755, kHostRootUid, kHostRootGid,
arc_paths_->debugfs_directory));
// debug/sync does not exist on all kernels so ignore errors
IGNORE_ERRORS(arc_mounter_->UmountLazily(sync_mount_directory));
EXIT_IF(
!InstallDirectory(0755, kSystemUid, kSystemGid, sync_mount_directory));
const base::FilePath sync_directory("/sys/kernel/debug/sync");
if (base::DirectoryExists(sync_directory)) {
EXIT_IF(!Chown(kSystemUid, kSystemGid, sync_directory));
EXIT_IF(!Chown(kSystemUid, kSystemGid, sync_directory.Append("info")));
// Kernel change that introduces sw_sync is follows sync/info
if (base::PathExists(sync_directory.Append("sw_sync")))
EXIT_IF(!Chown(kSystemUid, kSystemGid, sync_directory.Append("sw_sync")));
EXIT_IF(!arc_mounter_->BindMount(sync_directory, sync_mount_directory));
}
const base::FilePath tracing_mount_directory =
arc_paths_->debugfs_directory.Append("tracing");
IGNORE_ERRORS(arc_mounter_->UmountLazily(tracing_mount_directory));
EXIT_IF(!InstallDirectory(0755, kHostRootUid, kHostRootGid,
tracing_mount_directory));
if (!is_dev_mode)
return;
const base::FilePath tracing_directory("/sys/kernel/debug/tracing");
EXIT_IF(!arc_mounter_->BindMount(tracing_directory, tracing_mount_directory));
}
void ArcSetup::SetUpMountPointForRemovableMedia() {
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->media_mount_directory));
EXIT_IF(!InstallDirectory(0755, kRootUid, kSystemGid,
arc_paths_->media_mount_directory));
const std::string media_mount_options =
base::StringPrintf("mode=0755,uid=%u,gid=%u", kRootUid, kSystemGid);
EXIT_IF(!arc_mounter_->Mount("tmpfs", arc_paths_->media_mount_directory,
"tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC,
media_mount_options.c_str()));
EXIT_IF(!arc_mounter_->SharedMount(arc_paths_->media_mount_directory));
EXIT_IF(
!InstallDirectory(0755, kMediaUid, kMediaGid,
arc_paths_->media_mount_directory.Append("removable")));
}
void ArcSetup::CleanUpStaleMountPoints() {
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->media_dest_directory));
}
void ArcSetup::SetUpSharedMountPoints(bool for_login_screen) {
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->shared_mount_directory));
EXIT_IF(!InstallDirectory(0755, kRootUid, kRootGid,
arc_paths_->shared_mount_directory));
if (!for_login_screen)
return;
// Use 0755 to make sure only the real root user can write to the shared
// mount point.
EXIT_IF(!arc_mounter_->Mount("tmpfs", arc_paths_->shared_mount_directory,
"tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC,
"mode=0755"));
// The shared mount point is needed only when the container is started for
// login screen.
EXIT_IF(!arc_mounter_->SharedMount(arc_paths_->shared_mount_directory));
}
void ArcSetup::RestoreContext() {
std::vector<base::FilePath> directories = {
// Restore the label for the file now since this is the only place to do
// so.
// The file is bind-mounted in the container as /proc/cmdline, but unlike
// /run/arc and /run/camera, the file cannot have the "mount_outside"
// option
// because /proc for the container is always mounted inside the container,
// and cmdline file has to be mounted on top of that.
arc_paths_->android_cmdline,
arc_paths_->debugfs_directory, arc_paths_->obb_mount_directory,
arc_paths_->sdcard_mount_directory, arc_paths_->sysfs_cpu,
arc_paths_->sysfs_tracing,
};
if (base::DirectoryExists(arc_paths_->restorecon_whitelist_sync))
directories.push_back(arc_paths_->restorecon_whitelist_sync);
// usbfs does not exist on test VMs without any USB emulation, skip it there.
if (base::DirectoryExists(arc_paths_->usb_devices_directory))
directories.push_back(arc_paths_->usb_devices_directory);
EXIT_IF(!RestoreconRecursively(directories));
}
void ArcSetup::SetUpGraphicsSysfsContext() {
const base::FilePath sysfs_drm_path("/sys/class/drm");
const std::string sysfs_drm_context("u:object_r:gpu_device:s0");
const std::string render_node_pattern("renderD*");
const std::vector<std::string> attrs{"uevent", "config",
"vendor", "device",
"subsystem_vendor", "subsystem_device"};
base::FileEnumerator drm_directory(
sysfs_drm_path, false,
base::FileEnumerator::FileType::FILES |
base::FileEnumerator::FileType::DIRECTORIES |
base::FileEnumerator::FileType::SHOW_SYM_LINKS,
render_node_pattern);
for (auto dev = drm_directory.Next(); !dev.empty();
dev = drm_directory.Next()) {
auto device = dev.Append("device");
for (const auto& attr : attrs) {
auto attr_path = device.Append(attr);
if (base::PathExists(attr_path))
EXIT_IF(!Chcon(sysfs_drm_context, Realpath(attr_path)));
}
}
}
void ArcSetup::SetUpNetwork() {
constexpr char kSelinuxContext[] = "u:object_r:system_data_file:s0";
constexpr gid_t kMiscGid = 9998 + kShiftGid;
const base::FilePath data_dir =
arc_paths_->android_mutable_source.Append("data");
const base::FilePath misc_dir = data_dir.Append("misc");
const base::FilePath eth_dir = misc_dir.Append("ethernet");
const base::FilePath ipconfig_dest = eth_dir.Append("ipconfig.txt");
std::string ip_addr =
GetEnvOrDie(arc_paths_->env.get(), "ARC_CONTAINER_IPV4_ADDRESS");
std::string gateway =
GetEnvOrDie(arc_paths_->env.get(), "ARC_GATEWAY_IPV4_ADDRESS");
// Get rid of prefix length from ip address.
const size_t mask_position = ip_addr.find('/');
if (mask_position != std::string::npos)
ip_addr.resize(mask_position);
// Ensure strings aren't too long.
EXIT_IF(ip_addr.length() > std::numeric_limits<char>::max());
EXIT_IF(gateway.length() > std::numeric_limits<char>::max());
const char ip_addr_len = ip_addr.length();
const char gateway_len = gateway.length();
EXIT_IF(!InstallDirectory(01771, kSystemUid, kMiscGid, misc_dir));
EXIT_IF(!Chcon(kSelinuxContext, misc_dir));
EXIT_IF(!InstallDirectory(0770, kSystemUid, kSystemGid, eth_dir));
EXIT_IF(!Chcon(kSelinuxContext, eth_dir));
base::File ipconfig(ipconfig_dest,
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
EXIT_IF(!ipconfig.IsValid());
// The ipconfig.txt file is in network byte order. Since we can reasonably
// expect the length of the ip address to be less than the maximum value of a
// signed byte (char), we only use one byte for the length, and put it after a
// null byte to make a 16 bit interget in network byte order. These null bytes
// are at the end of the "first_section" and "second_section" to reduce the
// number of write calls.
// The file format was reverse engineered from the java class
// com.android.server.net.IpConfigStore.
// clang-format off
constexpr char kFirstSection[] =
"\0\0\0\x02\0\x02" "id" "\0\0\0\0"
"\0\x0c" "ipAssignment" "\0\x06" "STATIC"
"\0\x0b" "linkAddress" "\0";
ipconfig.WriteAtCurrentPos(kFirstSection, sizeof(kFirstSection) - 1);
ipconfig.WriteAtCurrentPos(&ip_addr_len, sizeof(ip_addr_len));
ipconfig.WriteAtCurrentPos(ip_addr.c_str(), ip_addr_len);
constexpr char kSecondSection[] =
"\0\0\0" "\x1e"
"\0\x07" "gateway" "\0\0\0\0\0\0\0\x01" "\0";
ipconfig.WriteAtCurrentPos(kSecondSection, sizeof(kSecondSection) - 1);
ipconfig.WriteAtCurrentPos(&gateway_len, sizeof(gateway_len));
ipconfig.WriteAtCurrentPos(gateway.c_str(), gateway_len);
constexpr char kThirdSection[] =
"\0\x03" "dns" "\0\x07" "8.8.8.8"
"\0\x03" "dns" "\0\x07" "8.8.4.4"
"\0\x03" "eos";
ipconfig.WriteAtCurrentPos(kThirdSection, sizeof(kThirdSection) - 1);
// clang-format on
EXIT_IF(!Chcon(kSelinuxContext, ipconfig_dest));
EXIT_IF(!Chown(kSystemUid, kSystemGid, ipconfig_dest));
}
void ArcSetup::MakeMountPointsReadOnly() {
// NOLINTNEXTLINE(runtime/int)
constexpr unsigned long remount_flags =
MS_RDONLY | MS_NOSUID | MS_NODEV | MS_NOEXEC;
constexpr char kMountOptions[] = "seclabel,mode=0755";
const std::string media_mount_options =
base::StringPrintf("mode=0755,uid=%u,gid=%u", kRootUid, kSystemGid);
// Make these mount points readonly so that Android container cannot modify
// files/directories inside these filesystem. Android container cannot remove
// the readonly flag because it is run in user namespace.
// These directories are also bind-mounted as read-write into either the
// SDCARD or arc-obb-mounter container, which runs outside of the user
// namespace so that such a daemon can modify files/directories inside the
// filesystem (See also arc-sdcard.conf and arc-obb-mounter.conf).
EXIT_IF(!arc_mounter_->Remount(arc_paths_->sdcard_mount_directory,
remount_flags, kMountOptions));
EXIT_IF(!arc_mounter_->Remount(arc_paths_->obb_mount_directory, remount_flags,
kMountOptions));
EXIT_IF(!arc_mounter_->Remount(arc_paths_->media_mount_directory,
remount_flags, media_mount_options.c_str()));
}
void ArcSetup::SetUpCameraProperty() {
// Camera HAL V3 needs two properties from build.prop for picture taking.
// Copy the information to /var/cache.
const base::FilePath camera_prop_directory("/var/cache/camera");
const base::FilePath camera_prop_file =
base::FilePath(camera_prop_directory).Append("camera.prop");
if (base::PathExists(camera_prop_file))
return;
if (!MkdirRecursively(camera_prop_directory))
return;
const base::FilePath build_prop =
arc_paths_->android_rootfs_directory.Append("system/build.prop");
std::string content;
EXIT_IF(!base::ReadFileToString(build_prop, &content));
std::vector<std::string> properties = base::SplitString(
content, "\n", base::WhitespaceHandling::TRIM_WHITESPACE,
base::SplitResult::SPLIT_WANT_ALL);
const std::string kManufacturer = "ro.product.manufacturer";
const std::string kModel = "ro.product.model";
std::string camera_properties;
for (const auto& property : properties) {
if (!property.compare(0, kManufacturer.length(), kManufacturer) ||
!property.compare(0, kModel.length(), kModel)) {
camera_properties += property + "\n";
}
}
EXIT_IF(!WriteToFile(camera_prop_file, 0644, camera_properties));
}
void ArcSetup::SetUpSharedApkDirectory() {
// TODO(yusuks): Remove the migration code in M68.
if (base::PathExists(arc_paths_->old_apk_cache_dir)) {
// The old directory is found. Move it to the new location. Still call
// InstallDirectory() to make sure permissions, uid, and gid are all
// correct.
EXIT_IF(
!base::Move(arc_paths_->old_apk_cache_dir, arc_paths_->apk_cache_dir));
}
EXIT_IF(!InstallDirectory(0700, kSystemUid, kSystemGid,
arc_paths_->apk_cache_dir));
}
void ArcSetup::CleanUpBinFmtMiscSetUp() {
const std::string system_arch = base::SysInfo::OperatingSystemArchitecture();
if (system_arch != "x86_64")
return;
std::unique_ptr<ScopedMount> binfmt_misc_mount =
ScopedMount::CreateScopedMount(
arc_mounter_.get(), "binfmt_misc", arc_paths_->binfmt_misc_directory,
"binfmt_misc", MS_NOSUID | MS_NODEV | MS_NOEXEC, nullptr);
// This function is for Mode::STOP. Ignore errors to make sure to run all
// clean up code.
if (!binfmt_misc_mount) {
PLOG(INFO) << "Ignoring failure: Failed to mount binfmt_misc";
return;
}
for (auto entry_name : kBinFmtMiscEntryNames) {
UnregisterBinFmtMiscEntry(
arc_paths_->binfmt_misc_directory.Append(entry_name));
}
}
void ArcSetup::UnmountOnStop() {
// This function is for Mode::STOP. Use IGNORE_ERRORS to make sure to run all
// clean up code.
IGNORE_ERRORS(arc_mounter_->UmountLazily(
arc_paths_->shared_mount_directory.Append("cache")));
IGNORE_ERRORS(arc_mounter_->UmountLazily(
arc_paths_->shared_mount_directory.Append("data")));
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->media_dest_directory));
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->media_mount_directory));
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->sdcard_mount_directory));
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->shared_mount_directory));
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->obb_mount_directory));
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->oem_mount_directory));
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->android_mutable_source));
IGNORE_ERRORS(
arc_mounter_->UmountLazily(arc_paths_->debugfs_directory.Append("sync")));
IGNORE_ERRORS(arc_mounter_->UmountLazily(
arc_paths_->debugfs_directory.Append("tracing")));
// Clean up in case this was not unmounted.
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->binfmt_misc_directory));
IGNORE_ERRORS(
arc_mounter_->UmountLazily(arc_paths_->android_rootfs_directory.Append(
arc_paths_->system_lib_arm_directory_relative)));
}
void ArcSetup::RemoveBugreportPipe() {
// This function is for Mode::STOP. Use IGNORE_ERRORS to make sure to run all
// clean up code.
IGNORE_ERRORS(base::DeleteFile(base::FilePath("/run/arc/bugreport/pipe"),
false /* recursive */));
}
void ArcSetup::RemoveAndroidKmsgFifo() {
// This function is for Mode::STOP. Use IGNORE_ERRORS to make sure to run all
// clean up code.
IGNORE_ERRORS(
base::DeleteFile(arc_paths_->android_kmsg_fifo, false /* recursive */));
}
std::string ArcSetup::GetSystemImageFingerprint() {
constexpr char kFingerprintProp[] = "ro.build.fingerprint";
const base::FilePath build_prop =
arc_paths_->android_rootfs_directory.Append("system/build.prop");
std::string system_fingerprint;
EXIT_IF(
!GetPropertyFromFile(build_prop, kFingerprintProp, &system_fingerprint));
EXIT_IF(system_fingerprint.empty());
return system_fingerprint;
}
ArcBootType ArcSetup::GetBootType() {
const std::string system_fingerprint = GetSystemImageFingerprint();
// Note: The XML file name has to match com.android.server.pm.Settings's
// mSettingsFilename. This will be very unlikely to change, but we still need
// to be careful.
const base::FilePath packages_xml =
arc_paths_->android_data_directory.Append("data/system/packages.xml");
if (!base::PathExists(packages_xml)) {
// This path is taken when /data is empty, which is not an error.
LOG(INFO) << packages_xml.value()
<< " does not exist. This is the very first boot aka opt-in.";
return ArcBootType::FIRST_BOOT;
}
// Get a fingerprint from /data/system/packages.xml.
std::string data_fingerprint;
if (!GetFingerprintFromPackagesXml(packages_xml, &data_fingerprint)) {
LOG(ERROR) << "Failed to get a fingerprint from " << packages_xml.value();
// Return kFirstBootAfterUpdate so the caller invalidates art/oat files
// which is safer than returning kRegularBoot.
return ArcBootType::FIRST_BOOT_AFTER_UPDATE;
}
// If two fingerprints don't match, this is the first boot after OTA.
// Android's PackageManagerService.isUpgrade(), at least on M, N, and
// O releases, does exactly the same to detect OTA.
// TODO(yusukes): Check Android P's behavior when it is ready.
const bool ota_detected = system_fingerprint != data_fingerprint;
if (!ota_detected) {
LOG(INFO) << "This is regular boot.";
} else {
LOG(INFO) << "This is the first boot after OTA: system="
<< system_fingerprint << ", data=" << data_fingerprint;
}
return ota_detected ? ArcBootType::FIRST_BOOT_AFTER_UPDATE
: ArcBootType::REGULAR_BOOT;
}
void ArcSetup::ShouldDeleteDataExecutables(
ArcBootType boot_type,
bool* out_should_delete_data_dalvik_cache_directory,
bool* out_should_delete_data_app_executables) {
if (boot_type == ArcBootType::FIRST_BOOT_AFTER_UPDATE &&
GetBooleanEnvOrDie(arc_paths_->env.get(),
"DELETE_DATA_EXECUTABLES_AFTER_OTA")) {
*out_should_delete_data_dalvik_cache_directory = true;
*out_should_delete_data_app_executables = true;
return;
}
*out_should_delete_data_dalvik_cache_directory =
GetBooleanEnvOrDie(arc_paths_->env.get(), "DELETE_DATA_DALVIK_CACHE_DIR");
*out_should_delete_data_app_executables =
GetBooleanEnvOrDie(arc_paths_->env.get(), "DELETE_DATA_APP_EXECUTABLES");
}
std::string ArcSetup::GetSalt() {
constexpr char kSaltFile[] = "/home/.shadow/salt";
constexpr size_t kSaltFileSize = 16;
std::string per_machine_salt;
if (!base::ReadFileToString(base::FilePath(kSaltFile), &per_machine_salt) ||
per_machine_salt.size() < kSaltFileSize) {
LOG(WARNING) << kSaltFile << " is not available yet. OOBE boot?";
return std::string();
}
if (per_machine_salt.size() != kSaltFileSize) {
LOG(WARNING) << "Unexpected " << kSaltFile
<< " size: " << per_machine_salt.size();
}
return per_machine_salt;
}
std::string ArcSetup::GetSerialNumber() {
const std::string chromeos_user =
GetEnvOrDie(arc_paths_->env.get(), "CHROMEOS_USER");
const std::string salt = GetSalt();
EXIT_IF(salt.empty()); // at this point, the salt file should always exist.
return arc::GenerateFakeSerialNumber(chromeos_user, salt);
}
void ArcSetup::MountSharedAndroidDirectories() {
const base::FilePath cache_directory =
arc_paths_->android_data_directory.Append("cache");
const base::FilePath data_directory =
arc_paths_->android_data_directory.Append("data");
const base::FilePath data_cache_directory = data_directory.Append("cache");
const base::FilePath shared_cache_directory =
arc_paths_->shared_mount_directory.Append("cache");
const base::FilePath shared_data_directory =
arc_paths_->shared_mount_directory.Append("data");
if (!base::PathExists(shared_data_directory)) {
EXIT_IF(!InstallDirectory(0700, kHostRootUid, kHostRootGid,
shared_data_directory));
}
if (kUseMasterContainer) {
// TODO(yusukes): Double-check if we really want to migrate N's /cache as
// /data/cache in P. If we don't, we can remove all the code for master
// as well as the MS_REC flag in this function.
if (!base::PathExists(data_cache_directory)) {
EXIT_IF(!InstallDirectory(0700, kHostRootUid, kHostRootGid,
data_cache_directory));
}
} else {
if (!base::PathExists(shared_cache_directory)) {
EXIT_IF(!InstallDirectory(0700, kHostRootUid, kHostRootGid,
shared_cache_directory));
}
}
// First, make the original data directory a mount point and also make it
// executable. This has to be done *before* passing the directory into
// the shared mount point because the new flags won't be propagated if the
// mount point has already been shared with the slave.
EXIT_IF(!arc_mounter_->BindMount(data_directory, data_directory));
EXIT_IF(
!arc_mounter_->Remount(data_directory, MS_NOSUID | MS_NODEV, "seclabel"));
// Then, bind-mount /cache to the shared mount point. On master, pass
// |cache_directory| as part of |data_directory| instead.
if (kUseMasterContainer)
EXIT_IF(!arc_mounter_->BindMount(cache_directory, data_cache_directory));
else
EXIT_IF(!arc_mounter_->BindMount(cache_directory, shared_cache_directory));
// Finally, bind-mount /data to the shared mount point. Note that MS_REC is
// not needed for non-master builds, but it doesn't hurt either.
EXIT_IF(!arc_mounter_->Mount(data_directory.value(), shared_data_directory,
nullptr, MS_BIND | MS_REC, nullptr));
}
void ArcSetup::UnmountSharedAndroidDirectories() {
const base::FilePath data_directory =
arc_paths_->android_data_directory.Append("data");
const base::FilePath shared_cache_directory =
arc_paths_->shared_mount_directory.Append("cache");
const base::FilePath shared_data_directory =
arc_paths_->shared_mount_directory.Append("data");
IGNORE_ERRORS(arc_mounter_->UmountLazily(data_directory));
IGNORE_ERRORS(arc_mounter_->UmountLazily(shared_cache_directory));
IGNORE_ERRORS(arc_mounter_->UmountLazily(shared_data_directory));
IGNORE_ERRORS(arc_mounter_->UmountLazily(arc_paths_->shared_mount_directory));
}
void ArcSetup::ContinueContainerBoot(ArcBootType boot_type) {
constexpr char kCommand[] = "/system/bin/arcbootcontinue";
// Run |kCommand| on the container side. The binary does the following:
// * Bind-mount the actual cache and data in /var/arc/shared_mounts to /cache
// and /data.
// * Set ro.boot.serialno and others.
// * Then, set ro.data_mounted=1 to ask /init to start the processes in the
// "main" class.
// We don't use -S (set UID), -G (set GID), and /system/bin/runcon here and
// instead run the command with UID 0 (host's root) because our goal is to
// remove or reduce [u]mount operations from the container, especially from
// its /init, and then to enforce it with SELinux.
const std::vector<std::string> command_line = {
"/usr/bin/nsenter", "-t",
GetEnvOrDie(arc_paths_->env.get(), "CONTAINER_PID"),
"-m", // enter mount namespace
"-U", // enter user namespace
"-i", // enter System V IPC namespace
"-n", // enter network namespace
"-p", // enter pid namespace
"-r", // set the root directory
"-w", // set the working directory
"--", kCommand, "--serialno", GetSerialNumber(),
"--disable-boot-completed",
GetEnvOrDie(arc_paths_->env.get(), "DISABLE_BOOT_COMPLETED_BROADCAST"),
"--vendor-privileged",
GetEnvOrDie(arc_paths_->env.get(), "ENABLE_VENDOR_PRIVILEGED"),
"--container-boot-type", std::to_string(static_cast<int>(boot_type)),
// When this COPY_PACKAGES_CACHE is set to "1", SystemServer copies
// /data/system/pacakges.xml to /data/system/pacakges_copy.xml after the
// initialization stage of PackageManagerService.
"--copy-packages-cache",
GetEnvOrDie(arc_paths_->env.get(), "COPY_PACKAGES_CACHE"),
};
base::ElapsedTimer timer;
EXIT_IF(!LaunchAndWait(command_line));
LOG(INFO) << "Running " << kCommand << " took "
<< timer.Elapsed().InMillisecondsRoundedUp() << "ms";
}
// TODO(yusukes): The mini instance for login screen does not really require
// any /run directories. Only the fully featured instance does. If we use a
// different runtime.json file for login screen, we can remove this function.
void ArcSetup::EnsureContainerDirectories() {
// uid/gid will be modified by cras.conf later.
// FIXME(b/64553266): Work around push_to_device/deploy_vendor_image running
// arc_setup after cras.conf by skipping the setup if the directory exists.
if (!base::DirectoryExists(arc_paths_->cras_socket_directory))
EXIT_IF(!InstallDirectory(01770, kHostRootUid, kHostRootGid,
arc_paths_->cras_socket_directory));
}
void ArcSetup::MountOnOnetimeSetup() {
const bool is_writable =
GetBooleanEnvOrDie(arc_paths_->env.get(), "WRITABLE_MOUNT");
const unsigned long writable_flag = // NOLINT(runtime/int)
is_writable ? 0 : MS_RDONLY;
if (is_writable)
EXIT_IF(!arc_mounter_->Remount(base::FilePath("/"), 0 /* rw */, nullptr));
// Try to drop as many privileges as possible. If we end up starting ARC,
// we'll bind-mount the rootfs directory in the container-side with the
// appropriate flags.
EXIT_IF(!arc_mounter_->LoopMount(
kSystemImage, arc_paths_->android_rootfs_directory,
MS_NOEXEC | MS_NOSUID | MS_NODEV | writable_flag));
unsigned long kBaseFlags = // NOLINT(runtime/int)
writable_flag | MS_NOEXEC | MS_NOSUID;
// Though we can technically mount these in mount namespace with minijail,
// we do not bother to handle loopback mounts by ourselves but just mount it
// in host namespace. Unlike system.raw.img, these images are always squashfs.
// Unlike system.raw.img, we don't remount them as exec either. The images do
// not contain any executables.
EXIT_IF(!arc_mounter_->LoopMount(
kPassthroughImage, arc_paths_->media_rootfs_directory, kBaseFlags));
EXIT_IF(!arc_mounter_->LoopMount(
kSdcardRootfsImage, arc_paths_->sdcard_rootfs_directory, kBaseFlags));
EXIT_IF(!arc_mounter_->LoopMount(
kObbRootfsImage, arc_paths_->obb_rootfs_directory, kBaseFlags));
}
void ArcSetup::UnmountOnOnetimeStop() {
IGNORE_ERRORS(arc_mounter_->LoopUmount(arc_paths_->obb_rootfs_directory));
IGNORE_ERRORS(arc_mounter_->LoopUmount(arc_paths_->sdcard_rootfs_directory));
IGNORE_ERRORS(arc_mounter_->LoopUmount(arc_paths_->media_rootfs_directory));
IGNORE_ERRORS(arc_mounter_->LoopUmount(arc_paths_->android_rootfs_directory));
}
void ArcSetup::BindMountInContainerNamespaceOnPreChroot(
const base::FilePath& rootfs,
const ArcBinaryTranslationType binary_translation_type) {
if (binary_translation_type == ArcBinaryTranslationType::HOUDINI) {
// system_lib_arm either is empty or contains ndk-translation's libraries.
// Since houdini is selected bind-mount its libraries instead.
EXIT_IF(!arc_mounter_->BindMount(
rootfs.Append("vendor/lib/arm"),
rootfs.Append(arc_paths_->system_lib_arm_directory_relative)));
}
}
void ArcSetup::RestoreContextOnPreChroot(const base::FilePath& rootfs) {
{
// The list of container directories that need to be recursively re-labeled.
// Note that "var/run" (the parent directory) is not in the list because
// some of entries in the directory are on a read-only filesystem.
// Note: The array is for directories. Do no add files to the array. Add
// them to |kPaths| below instead.
constexpr std::array<const char*, 8> kDirectories{
"dev",
"oem",
"var/run/arc/apkcache",
"var/run/arc/bugreport",
"var/run/arc/dalvik-cache",
"var/run/camera",
"var/run/chrome",
"var/run/cras"};
// Transform |kDirectories| because the mount points are visible only in
// |rootfs|. Note that Chrome OS' file_contexts does recognize paths with
// the |rootfs| prefix.
EXIT_IF(!RestoreconRecursively(
PrependPath(kDirectories.cbegin(), kDirectories.cend(), rootfs)));
}
{
// Do the same as above for files and directories but in a non-recursive
// way.
constexpr std::array<const char*, 5> kPaths{
"default.prop", "sys/kernel/debug", "system/build.prop", "var/run/arc",
"var/run/inputbridge"};
EXIT_IF(!Restorecon(PrependPath(kPaths.cbegin(), kPaths.cend(), rootfs)));
}
}
void ArcSetup::CreateDevColdbootDoneOnPreChroot(const base::FilePath& rootfs) {
const base::FilePath coldboot_done = rootfs.Append("dev/.coldboot_done");
// TODO(yusukes): Once N finishes migrating to run_oci, add an auto test for
// checking this file to cheets_LoginScreen.
EXIT_IF(!CreateOrTruncate(coldboot_done, 0755));
EXIT_IF(!Chown(kRootUid, kRootGid, coldboot_done));
}
void ArcSetup::OnSetup(bool for_login_screen) {
const bool is_dev_mode =
GetBooleanEnvOrDie(arc_paths_->env.get(), "CHROMEOS_DEV_MODE");
const bool is_inside_vm =
GetBooleanEnvOrDie(arc_paths_->env.get(), "CHROMEOS_INSIDE_VM");
const bool is_debuggable =
GetBooleanEnvOrDie(arc_paths_->env.get(), "ANDROID_DEBUGGABLE");
// ArcBootType is unknown until the user's /data is mounted.
const ArcBootType boot_type =
for_login_screen ? ArcBootType::UNKNOWN : GetBootType();
if (!for_login_screen) {
// This is not necessary when |for_login_screen| is true because in that
// case /data is always empty.
bool should_delete_data_dalvik_cache_directory;
bool should_delete_data_app_executables;
ShouldDeleteDataExecutables(boot_type,
&should_delete_data_dalvik_cache_directory,
&should_delete_data_app_executables);
DeleteExecutableFilesInData(should_delete_data_dalvik_cache_directory,
should_delete_data_app_executables);
}
// The host-side dalvik-cache directory is mounted into the container
// via the json file. Create it regardless of whether the code integrity
// feature is enabled.
ArtContainer::CreateArtContainerDataDirectory();
// For login screen, mount host-compiled and host-verified .art and .oat
// files. The container will see these files, but other than that, the
// /data and /cache directories are empty and read-only which is the best
// for security.
if (for_login_screen) {
// Unconditionally generate host-side code here.
const base::FilePath art_dalvik_cache_directory =
arc_paths_->art_container_data_directory.Append("dalvik-cache");
base::ElapsedTimer timer;
GenerateHostSideCode(art_dalvik_cache_directory);
// For now, integrity checking time is the time needed to relocate
// boot*.art files because of b/67912719. Once TPM is enabled, this will
// report the total time spend on code verification + [relocation + sign]
arc_setup_metrics_->SendCodeIntegrityCheckingTotalTime(timer.Elapsed());
SetUpDalvikCache();
} else {
SetUpAndroidData();
InstallLinksToHostSideCode();
}
SetUpSharedMountPoints(for_login_screen);
CreateContainerFilesAndDirectories();
ApplyPerBoardConfigurations();
if (!for_login_screen) {
// We don't trace the container startup when |for_login_screen| is true
// because the mini container itself is started for preloading files. We
// don't run ureadahead before starting the mini container.
MaybeStartUreadaheadInTracingMode();
}
SetUpSharedTmpfsForExternalStorage();
SetUpFilesystemForObbMounter();
CreateAndroidCmdlineFile(for_login_screen, is_dev_mode, is_inside_vm,
is_debuggable, boot_type);
CreateFakeProcfsFiles();
SetUpMountPointForDebugFilesystem(is_dev_mode);
SetUpMountPointForRemovableMedia();
CleanUpStaleMountPoints();
RestoreContext();
SetUpGraphicsSysfsContext();
MakeMountPointsReadOnly();
SetUpCameraProperty();
SetUpSharedApkDirectory();
// These should be the last thing OnSetup() does because the job and
// directories are not needed for arc-setup. Only the container's startup code
// (in session_manager side) requires the job and directories.
WaitForRtLimitsJob();
}
void ArcSetup::OnBootContinue() {
const ArcBootType boot_type = GetBootType();
bool should_delete_data_dalvik_cache_directory;
bool should_delete_data_app_executables;
ShouldDeleteDataExecutables(boot_type,
&should_delete_data_dalvik_cache_directory,
&should_delete_data_app_executables);
DeleteExecutableFilesInData(should_delete_data_dalvik_cache_directory,
should_delete_data_app_executables);
CleanUpDalvikCache();
// The socket isn't created when the mini-container is started, so the
// arc-setup --pre-chroot call won't label it. Label it here instead.
EXIT_IF(!Chcon(kArcBridgeSocketContext, arc_paths_->arc_bridge_socket_path));
// Set up |android_mutable_source|. Although the container does not use
// the directory directly, we should still set up the directory so that
// session_manager can delete (to be more precise, move) the directory
// on opt-out. Since this creates cache and data directories when they
// don't exist, this has to be done before calling ShareAndroidData().
SetUpAndroidData();
if (!InstallLinksToHostSideCode()) {
arc_setup_metrics_->SendBootContinueCodeInstallationResult(
ArcBootContinueCodeInstallationResult::ERROR_CANNOT_INSTALL_HOST_CODE);
} else {
arc_setup_metrics_->SendBootContinueCodeInstallationResult(
ArcBootContinueCodeInstallationResult::SUCCESS);
}
// Set up /run/arc/shared_mounts/{cache,data} to expose the user's data to
// the container.
MountSharedAndroidDirectories();
// Asks the container to continue boot.
MaybeStartUreadaheadInTracingMode();
ContinueContainerBoot(boot_type);
// Unmount /run/arc/shared_mounts and its children. They are unnecessary at
// this point.
UnmountSharedAndroidDirectories();
}
void ArcSetup::OnStop() {
CleanUpBinFmtMiscSetUp();
CleanUpDalvikCache();
UnmountOnStop();
RemoveBugreportPipe();
RemoveAndroidKmsgFifo();
}
void ArcSetup::OnOnetimeSetup() {
EnsureContainerDirectories();
MountOnOnetimeSetup();
// Build properties are needed to finish booting the container, so we need
// to set them up here instead of in the per-board setup.
CreateBuildProperties();
}
void ArcSetup::OnOnetimeStop() {
UnmountOnOnetimeStop();
}
void ArcSetup::OnPreChroot() {
// binfmt_misc setup has to be done before entering container
// namespace below (namely before CreateScopedMountNamespaceForPid).
ArcBinaryTranslationType binary_translation_type =
IdentifyBinaryTranslationType();
SetUpBinFmtMisc(binary_translation_type);
int container_pid;
base::FilePath rootfs;
EXIT_IF(!GetOciContainerState(base::FilePath("/dev/stdin"), &container_pid,
&rootfs));
// Enter the container namespace since the paths we want to re-label here
// are easier to access from inside of it.
std::unique_ptr<ScopedMountNamespace> container_mount_ns =
ScopedMountNamespace::CreateScopedMountNamespaceForPid(container_pid);
PLOG_IF(FATAL, !container_mount_ns)
<< "Failed to enter the container mount namespace";
BindMountInContainerNamespaceOnPreChroot(rootfs, binary_translation_type);
RestoreContextOnPreChroot(rootfs);
CreateDevColdbootDoneOnPreChroot(rootfs);
}
void ArcSetup::OnReadAhead() {
EmulateArcUreadahead(arc_paths_->android_rootfs_directory, kReadAheadTimeout);
}
void ArcSetup::Run() {
switch (arc_paths_->mode) {
case Mode::SETUP:
case Mode::SETUP_FOR_LOGIN_SCREEN:
OnSetup(arc_paths_->mode == Mode::SETUP_FOR_LOGIN_SCREEN);
break;
case Mode::STOP:
OnStop();
break;
case Mode::BOOT_CONTINUE:
OnBootContinue();
break;
case Mode::ONETIME_SETUP:
OnOnetimeSetup();
break;
case Mode::ONETIME_STOP:
OnOnetimeStop();
break;
case Mode::PRE_CHROOT:
OnPreChroot();
break;
case Mode::READ_AHEAD:
OnReadAhead();
break;
case Mode::UNKNOWN:
NOTREACHED();
break;
}
}
void ArcSetup::MountOnOnetimeSetupForTesting() {
MountOnOnetimeSetup();
}
void ArcSetup::UnmountOnOnetimeStopForTesting() {
UnmountOnOnetimeStop();
}
} // namespace arc