blob: c7f7b28263cf7867cfc2994087bece75e721e46d [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 <inttypes.h>
#include <linux/magic.h>
#include <sched.h>
#include <selinux/selinux.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/vfs.h>
#include <time.h>
#include <unistd.h>
#include <algorithm>
#include <array>
#include <limits>
#include <memory>
#include <vector>
#include <base/bind.h>
#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/memory/ptr_util.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
#include <base/strings/stringprintf.h>
#include <base/system/sys_info.h>
#include <base/threading/platform_thread.h>
#include <base/time/time.h>
#include <base/timer/elapsed_timer.h>
#include <brillo/cryptohome.h>
#include <brillo/file_utils.h>
#include <brillo/files/safe_fd.h>
#include <brillo/scoped_mount_namespace.h>
#include <chromeos-config/libcros_config/cros_config.h>
#include <chromeos/patchpanel/dbus/client.h>
#include <crypto/random.h>
#include <metrics/bootstat.h>
#include <metrics/metrics_library.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_SDCARD_RW 1015 /* external storage write access */
#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 */
#define AID_EVERYBODY 9997 /* shared between all apps in the same profile */
namespace arc {
namespace {
// Lexicographically sorted. Usually you don't have to use these constants
// directly. Prefer base::FilePath variables in ArcPaths instead.
constexpr char kAdbdMountDirectory[] = "/run/arc/adbd";
constexpr char kAdbdUnixSocketMountDirectory[] = "/run/arc/adb";
constexpr char kAndroidCmdline[] = "/run/arc/cmdline.android";
constexpr char kAndroidGeneratedPropertiesDirectory[] =
"/run/arc/host_generated";
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 kArcVmPerBoardConfigPath[] = "/run/arcvm/host_generated/oem";
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 kBuildPropFile[] = "/usr/share/arc/properties/build.prop";
constexpr char kBuildPropFileVm[] = "/usr/share/arcvm/properties/build.prop";
constexpr char kCameraProfileDir[] =
"/mnt/stateful_partition/encrypted/var/cache/camera";
constexpr char kCrasSocketDirectory[] = "/run/cras";
constexpr char kCombinedPropFileVm[] =
"/run/arcvm/host_generated/combined.prop";
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 kHostDownloadsDirectory[] = "/home/chronos/user/Downloads";
constexpr char kMediaMountDirectory[] = "/run/arc/media";
constexpr char kMediaMyFilesDirectory[] = "/run/arc/media/MyFiles";
constexpr char kMediaMyFilesDefaultDirectory[] =
"/run/arc/media/MyFiles-default";
constexpr char kMediaMyFilesReadDirectory[] = "/run/arc/media/MyFiles-read";
constexpr char kMediaMyFilesWriteDirectory[] = "/run/arc/media/MyFiles-write";
constexpr char kMediaProfileFile[] = "media_profiles.xml";
constexpr char kMediaRemovableDirectory[] = "/run/arc/media/removable";
constexpr char kMediaRemovableDefaultDirectory[] =
"/run/arc/media/removable-default";
constexpr char kMediaRemovableReadDirectory[] = "/run/arc/media/removable-read";
constexpr char kMediaRemovableWriteDirectory[] =
"/run/arc/media/removable-write";
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 kPlatformXmlFileRelative[] = "etc/permissions/platform.xml";
constexpr char kRestoreconAllowlistSync[] = "/sys/kernel/debug/sync";
constexpr char kSdcardConfigfsDirectory[] = "/sys/kernel/config/sdcardfs";
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";
// TODO(niwa): Remove kSystemBinArm* when we remove container Q+ support
// from arc-setup.
constexpr char kSystemBinArmDirectoryRelative[] = "system/bin/arm";
constexpr char kSystemBinArm64DirectoryRelative[] = "system/bin/arm64";
constexpr char kSystemLibArmDirectoryRelative[] = "system/lib/arm";
constexpr char kSystemLibArm64DirectoryRelative[] = "system/lib64/arm64";
constexpr char kSystemImage[] = "/opt/google/containers/android/system.raw.img";
constexpr char kUsbDevicesDirectory[] = "/dev/bus/usb";
constexpr char kZygotePreloadDoneFile[] = ".preload_done";
// Names for possible binfmt_misc entries.
constexpr const char* kBinFmtMiscEntryNames[] = {"arm_dyn", "arm_exe",
"arm64_dyn", "arm64_exe"};
// These are board-specific configuration settings, which are managed through
// the chromeos-config architecture.
// For details, see:
// https://chromium.googlesource.com/chromiumos/platform2/+/HEAD/chromeos-config/#arc
//
// Board-specific config files are automatically managed/generated via project
// config repos. For details, see:
// https://chromium.googlesource.com/chromiumos/config/
// For an example, see:
// https://chromium.googlesource.com/chromiumos/config/+/HEAD/test/project/fake/fake/sw_build_config/platform/chromeos-config/generated/arc/
constexpr char kHardwareFeaturesSetting[] = "/arc/hardware-features";
constexpr char kMediaProfilesSetting[] = "/arc/media-profiles";
constexpr char kSystemPath[] = "system-path";
constexpr uid_t kHostRootUid = 0;
constexpr gid_t kHostRootGid = 0;
constexpr uid_t kHostChronosUid = 1000;
constexpr gid_t kHostChronosGid = 1000;
constexpr uid_t kHostArcCameraUid = 603;
constexpr gid_t kHostArcCameraGid = 603;
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 uid_t kShellGid = AID_SHELL + kShiftGid;
constexpr gid_t kSdcardRwGid = AID_SDCARD_RW + kShiftGid;
constexpr gid_t kEverybodyGid = AID_EVERYBODY + kShiftGid;
// The maximum time to wait for /data/media setup.
constexpr base::TimeDelta kInstalldTimeout = base::TimeDelta::FromSeconds(60);
// Property name for fingerprint.
constexpr char kFingerprintProp[] = "ro.build.fingerprint";
// System salt and arc salt file size.
constexpr size_t kSaltFileSize = 16;
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;
const base::FilePath format_path = binfmt_misc_directory.Append(entry_name);
if (base::PathExists(format_path)) {
// If we had already registered this format earlier and failed
// unregistering it for some reason, the next operation will fail.
LOG(WARNING) << "Skipping re-registration of " << entry_path.value();
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;
}
// Returns SDK version upgrade type to be sent to UMA.
ArcSdkVersionUpgradeType GetUpgradeType(AndroidSdkVersion system_sdk_version,
AndroidSdkVersion data_sdk_version) {
if (data_sdk_version == AndroidSdkVersion::UNKNOWN || // First boot
data_sdk_version == system_sdk_version) {
return ArcSdkVersionUpgradeType::NO_UPGRADE;
}
if (data_sdk_version == AndroidSdkVersion::ANDROID_M) {
if (system_sdk_version == AndroidSdkVersion::ANDROID_P)
return ArcSdkVersionUpgradeType::M_TO_P;
}
if (data_sdk_version == AndroidSdkVersion::ANDROID_N_MR1 &&
system_sdk_version == AndroidSdkVersion::ANDROID_P) {
return ArcSdkVersionUpgradeType::N_TO_P;
}
if (data_sdk_version < system_sdk_version) {
LOG(ERROR) << "Unexpected Upgrade: data_sdk_version="
<< static_cast<int>(data_sdk_version) << " system_sdk_version="
<< static_cast<int>(system_sdk_version);
return ArcSdkVersionUpgradeType::UNKNOWN_UPGRADE;
}
LOG(ERROR) << "Unexpected Downgrade: data_sdk_version="
<< static_cast<int>(data_sdk_version)
<< " system_sdk_version=" << static_cast<int>(system_sdk_version);
return ArcSdkVersionUpgradeType::UNKNOWN_DOWNGRADE;
}
void CheckProcessIsAliveOrExit(const std::string& pid_str) {
pid_t pid;
EXIT_IF(!base::StringToInt(pid_str, &pid));
if (!IsProcessAlive(pid)) {
LOG(ERROR) << "Process " << pid << " is NOT alive";
exit(EXIT_FAILURE);
}
LOG(INFO) << "Process " << pid << " is still alive, at least as a zombie";
// TODO(yusukes): Check if the PID is a zombie or not, and log accordingly.
}
void CheckNamespacesAvailableOrExit(const std::string& pid_str) {
const base::FilePath proc("/proc");
const base::FilePath ns = proc.Append(pid_str).Append("ns");
EXIT_IF(!base::PathExists(ns));
for (const char* entry :
{"cgroup", "ipc", "mnt", "net", "pid", "user", "uts"}) {
// Use the same syscall, open, as nsenter. Other syscalls like lstat may
// succeed when open doesn't.
const base::FilePath path_to_check = ns.Append(entry);
base::ScopedFD fd(open(path_to_check.value().c_str(), O_RDONLY));
if (!fd.is_valid()) {
PLOG(ERROR) << "Failed to open " << path_to_check.value();
exit(EXIT_FAILURE);
}
}
LOG(INFO) << "Process " << pid_str << " still has all namespace entries";
}
void CheckOtherProcEntriesOrExit(const std::string& pid_str) {
const base::FilePath proc("/proc");
const base::FilePath proc_pid = proc.Append(pid_str);
for (const char* entry : {"cwd", "root"}) {
// Use open for the same reason as CheckNamespacesAvailableOrExit().
const base::FilePath path_to_check = proc_pid.Append(entry);
base::ScopedFD fd(open(path_to_check.value().c_str(), O_RDONLY));
if (!fd.is_valid()) {
PLOG(ERROR) << "Failed to open " << path_to_check.value();
exit(EXIT_FAILURE);
}
}
LOG(INFO) << "Process " << pid_str << " still has other proc entries";
}
// Creates subdirectories under dalvik-cache directory if not exists.
bool CreateArtContainerDataDirectory(
const base::FilePath& art_dalvik_cache_directory) {
for (const std::string& isa : ArtContainer::GetIsas()) {
base::FilePath isa_directory = art_dalvik_cache_directory.Append(isa);
// Use the same permissions as the ones used in maybeCreateDalvikCache() in
// framework/base/cmds/app_process/app_main.cpp
if (!InstallDirectory(0711, kRootUid, kRootGid, isa_directory)) {
PLOG(ERROR) << "Failed to create art container data dir: "
<< isa_directory.value();
return false;
}
}
return true;
}
// Stores relative path, mode_t for sdcard mounts.
// mode is an octal mask for file persmissions here.
struct EsdfsMount {
const char* relative_path;
mode_t mode;
gid_t gid;
};
const std::vector<EsdfsMount> GetEsdfsMounts(AndroidSdkVersion version) {
std::vector<EsdfsMount> mounts{
{"default/emulated", 0006, kSdcardRwGid},
{"read/emulated", 0027, kEverybodyGid},
{"write/emulated", 0007, kEverybodyGid},
};
return mounts;
}
// Esdfs mount options:
// --------------------
// fsuid, fsgid : Lower filesystem's uid/gid.
//
// derive_gid : Changes uid/gid values on the lower filesystem for tracking
// storage user by apps and various categories.
//
// default_normal: Does not treat the default mount (using gid AID_SDCARD_RW)
// differently. Without this, the gid presented by the upper
// filesystem does not include the user, and would allow shell
// users to access all user’s data.
//
// mask : Masks away permissions.
//
// gid : Upper filesystem's group id.
//
// ns_fd : Namespace file descriptor used to set the base namespace for
// the esdfs mount, similar to the argument to setns(2).
//
// dl_uid, dl_gid: Downloads integration uid/gid.
//
// dl_loc : The Android download directory acts as an overlay on dl_loc.
std::string CreateEsdfsMountOpts(uid_t fsuid,
gid_t fsgid,
mode_t mask,
uid_t userid,
gid_t gid,
int container_userns_fd) {
std::string opts = base::StringPrintf(
"fsuid=%d,fsgid=%d,derive_gid,default_normal,mask=%d,multiuser,"
"gid=%d,dl_loc=%s,dl_uid=%d,dl_gid=%d,ns_fd=%d",
fsuid, fsgid, mask, gid, kHostDownloadsDirectory, kHostChronosUid,
kHostChronosGid, container_userns_fd);
LOG(INFO) << "Esdfs mount options: " << opts;
return opts;
}
// Return path of layout_version based on |sdk_version|.
std::string GetInstalldLayoutRelativePath(AndroidSdkVersion sdk_version) {
return "data/.layout_version";
}
// Wait upto kInstalldTimeout for the sdcard source directory to be setup.
// On failure, exit.
bool WaitForSdcardSource(const base::FilePath& android_root,
AndroidSdkVersion sdk_version) {
bool ret;
base::TimeDelta elapsed;
// <android_root>/data path to synchronize with installd
const base::FilePath fs_version =
android_root.Append(GetInstalldLayoutRelativePath(sdk_version));
LOG(INFO) << "Waiting upto " << kInstalldTimeout
<< " for installd to complete setting up /data.";
ret = WaitForPaths({fs_version}, kInstalldTimeout, &elapsed);
LOG(INFO) << "Waiting for installd took " << elapsed.InSeconds() << "s";
if (!ret)
LOG(ERROR) << "Timed out waiting for /data setup.";
return ret;
}
// Don't use this, use GetOrCreateArcSalt instead. Reads the 16-byte per-machine
// random salt. The salt is created once when the machine is first used, and
// wiped/regenerated on powerwash/recovery. When it's not available yet (which
// could happen only on OOBE boot), returns an empty string.
// TODO(yusukes): Remove this function after M73.
std::string GetSystemSalt() {
constexpr char kSaltFile[] = "/home/.shadow/salt";
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;
}
// Reads a random number for the container from /var/lib/misc/arc_salt. If
// the file does not exist, generates a new one. This file will be cleared
// and regenerated after powerwash.
std::string GetOrCreateArcSalt() {
constexpr char kArcSaltFile[] = "/var/lib/misc/arc_salt";
constexpr mode_t kArcSaltFilePermissions = 0400;
std::string arc_salt;
const base::FilePath arc_salt_file(kArcSaltFile);
if (!base::ReadFileToString(arc_salt_file, &arc_salt) ||
arc_salt.size() != kSaltFileSize) {
// If system salt value is available, reuse the system salt to avoid
// clearing existing relocated boot*.art code.
arc_salt = GetSystemSalt();
if (arc_salt.size() != kSaltFileSize) {
char rand_value[kSaltFileSize];
crypto::RandBytes(rand_value, kSaltFileSize);
arc_salt = std::string(rand_value, kSaltFileSize);
}
if (!brillo::WriteToFileAtomic(arc_salt_file, arc_salt.data(),
arc_salt.size(), kArcSaltFilePermissions)) {
LOG(ERROR) << "Failed to write arc salt file.";
return std::string();
}
}
return arc_salt;
}
bool IsChromeOSUserAvailable(Mode mode) {
switch (mode) {
case Mode::BOOT_CONTINUE:
case Mode::CREATE_DATA:
case Mode::REMOVE_DATA:
case Mode::REMOVE_STALE_DATA:
case Mode::HANDLE_UPGRADE:
return true;
case Mode::APPLY_PER_BOARD_CONFIG:
case Mode::SETUP:
case Mode::STOP:
case Mode::ONETIME_SETUP:
case Mode::ONETIME_STOP:
case Mode::PRE_CHROOT:
case Mode::MOUNT_SDCARD:
case Mode::UNMOUNT_SDCARD:
case Mode::UPDATE_RESTORECON_LAST:
case Mode::UNKNOWN:
return false;
}
}
// Converts Dalvik memory profile to androidboot property if applicable.
std::string GetDalvikMemoryProfileParam(
const std::string& dalvik_memory_profile) {
if (dalvik_memory_profile.empty())
return std::string();
return base::StringPrintf("androidboot.arc_dalvik_memory_profile=%s ",
dalvik_memory_profile.c_str());
}
} // namespace
// A struct that holds all the FilePaths ArcSetup uses.
struct ArcPaths {
static std::unique_ptr<ArcPaths> Create(Mode mode, const Config& config) {
base::FilePath android_data;
base::FilePath android_data_old;
if (IsChromeOSUserAvailable(mode)) {
std::string chromeos_user = config.GetStringOrDie("CHROMEOS_USER");
const base::FilePath root_path =
brillo::cryptohome::home::GetRootPath(chromeos_user);
// Ensure the user directory exists.
EXIT_IF(root_path.empty() || !base::DirectoryExists(root_path));
android_data = root_path.Append("android-data");
android_data_old = root_path.Append("android-data-old");
}
return base::WrapUnique(new ArcPaths(android_data, android_data_old));
}
// Lexicographically sorted.
const base::FilePath adbd_mount_directory{kAdbdMountDirectory};
const base::FilePath adbd_unix_socket_mount_directory{
kAdbdUnixSocketMountDirectory};
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_dalvik_cache_directory{kArtDalvikCacheDirectory};
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_mount_directory{kMediaMountDirectory};
const base::FilePath media_myfiles_directory{kMediaMyFilesDirectory};
const base::FilePath media_myfiles_default_directory{
kMediaMyFilesDefaultDirectory};
const base::FilePath media_myfiles_read_directory{kMediaMyFilesReadDirectory};
const base::FilePath media_myfiles_write_directory{
kMediaMyFilesWriteDirectory};
const base::FilePath media_profile_file{kMediaProfileFile};
const base::FilePath media_removable_directory{kMediaRemovableDirectory};
const base::FilePath media_removable_default_directory{
kMediaRemovableDefaultDirectory};
const base::FilePath media_removable_read_directory{
kMediaRemovableReadDirectory};
const base::FilePath media_removable_write_directory{
kMediaRemovableWriteDirectory};
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_configfs_directory{kSdcardConfigfsDirectory};
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_bin_arm_directory_relative{
kSystemBinArmDirectoryRelative};
const base::FilePath system_bin_arm64_directory_relative{
kSystemBinArm64DirectoryRelative};
const base::FilePath system_lib_arm_directory_relative{
kSystemLibArmDirectoryRelative};
const base::FilePath system_lib64_arm64_directory_relative{
kSystemLibArm64DirectoryRelative};
const base::FilePath usb_devices_directory{kUsbDevicesDirectory};
const base::FilePath restorecon_allowlist_sync{kRestoreconAllowlistSync};
const base::FilePath android_data_directory;
const base::FilePath android_data_old_directory;
private:
ArcPaths(const base::FilePath& android_data_directory,
const base::FilePath& android_data_old_directory)
: android_data_directory(android_data_directory),
android_data_old_directory(android_data_old_directory) {}
ArcPaths(const ArcPaths&) = delete;
ArcPaths& operator=(const ArcPaths&) = delete;
};
ArcSetup::ArcSetup(Mode mode, const base::FilePath& config_json)
: mode_(mode),
config_(config_json, base::Environment::Create()),
arc_mounter_(GetDefaultMounter()),
arc_paths_(ArcPaths::Create(mode_, config_)),
arc_setup_metrics_(std::make_unique<ArcSetupMetrics>()) {
CHECK(mode == Mode::APPLY_PER_BOARD_CONFIG || mode == Mode::CREATE_DATA ||
mode == Mode::REMOVE_DATA || mode == Mode::REMOVE_STALE_DATA ||
mode == Mode::HANDLE_UPGRADE || !config_json.empty());
}
ArcSetup::~ArcSetup() = default;
// static
std::string ArcSetup::GetPlayStoreAutoUpdateParam(
PlayStoreAutoUpdate play_store_auto_update) {
switch (play_store_auto_update) {
case PlayStoreAutoUpdate::kDefault:
return std::string();
case PlayStoreAutoUpdate::kOn:
case PlayStoreAutoUpdate::kOff:
return base::StringPrintf(
"androidboot.play_store_auto_update=%d ",
play_store_auto_update == PlayStoreAutoUpdate::kOn);
}
}
// Note: This function has to be in sync with Android's arc-boot-type-detector.
// arc-boot-type-detector's DeleteExecutableFilesInData() function is very
// similar to this.
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;
}
// Move data/dalvik-cache.
if (should_delete_data_dalvik_cache_directory) {
MoveDirIntoDataOldDir(arc_paths_->android_data_directory.Append(
base::FilePath("data/dalvik-cache")),
arc_paths_->android_data_old_directory);
}
// 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;
base::FileEnumerator dir_enum(app_directory, false /* recursive */,
base::FileEnumerator::DIRECTORIES);
for (base::FilePath pkg_directory_name = dir_enum.Next();
!pkg_directory_name.empty(); pkg_directory_name = dir_enum.Next()) {
MoveDirIntoDataOldDir(pkg_directory_name.Append("oat"),
arc_paths_->android_data_old_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() {
bool is_houdini_available = kUseHoudini || kUseHoudini64;
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 ||
config_.GetBoolOrDie("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(const base::FilePath& bind_target) {
mode_t android_data_mode = 0700;
gid_t android_data_gid = kRootGid;
if (USE_ARCVM) {
// When ARCVM is enabled on the board, allow vm_concierge to access the
// directory. Note that vm_concierge runs as ugid crosvm in minijail.
uid_t dummy_uid;
EXIT_IF(!GetUserId("crosvm", &dummy_uid, &android_data_gid));
android_data_mode = 0750;
}
EXIT_IF(!InstallDirectory(android_data_mode, kRootUid, android_data_gid,
arc_paths_->android_data_directory));
// match android/system/core/rootdir/init.rc
EXIT_IF(!InstallDirectory(0771, kSystemUid, kSystemGid,
arc_paths_->android_data_directory.Append("data")));
if (USE_ARCVM) {
// For ARCVM, create /data/media too since crosvm exports the directory via
// virtio-fs.
EXIT_IF(!InstallDirectory(
0770, kMediaUid, kMediaGid,
arc_paths_->android_data_directory.Append("data").Append("media")));
}
// To make our bind-mount business easier, we first bind-mount the real
// android-data directory to bind_target (usually $ANDROID_MUTABLE_SOURCE).
// Then we do not need to pass the android-data path to other processes.
EXIT_IF(!arc_mounter_->BindMount(arc_paths_->android_data_directory,
bind_target));
}
void ArcSetup::UnmountSdcard() {
// We unmount here in both the ESDFS and the FUSE cases in order to
// clean up after Android's /system/bin/sdcard. However, the paths
// must be the same in both cases.
for (const auto& mount : GetEsdfsMounts(GetSdkVersion())) {
base::FilePath kDestDirectory =
arc_paths_->sdcard_mount_directory.Append(mount.relative_path);
IGNORE_ERRORS(arc_mounter_->Umount(kDestDirectory));
}
LOG(INFO) << "Unmount sdcard complete.";
}
void ArcSetup::CreateContainerFilesAndDirectories() {
// 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 =
brillo::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"}));
}
void ArcSetup::ApplyPerBoardConfigurations() {
EXIT_IF(!brillo::MkdirRecursively(
arc_paths_->oem_mount_directory.Append("etc"), 0755)
.is_valid());
EXIT_IF(!arc_mounter_->Mount("tmpfs", arc_paths_->oem_mount_directory,
"tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC,
"mode=0755"));
EXIT_IF(!brillo::MkdirRecursively(
arc_paths_->oem_mount_directory.Append("etc/permissions"), 0755)
.is_valid());
ApplyPerBoardConfigurationsInternal(arc_paths_->oem_mount_directory);
}
void ArcSetup::ApplyPerBoardConfigurationsInternal(
const base::FilePath& oem_mount_directory) {
auto config = std::make_unique<brillo::CrosConfig>();
config->Init();
base::FilePath media_profile_xml =
base::FilePath(arc_paths_->camera_profile_dir)
.Append(arc_paths_->media_profile_file);
std::string media_profile_setting;
if (config->GetString(kMediaProfilesSetting, kSystemPath,
&media_profile_setting)) {
media_profile_xml = base::FilePath(media_profile_setting);
} else {
// TODO(chromium:1083652) Remove dynamic shell scripts once all overlays
// are migrated to static XML config.
const base::FilePath generate_camera_profile(
"/usr/bin/generate_camera_profile");
if (base::PathExists(generate_camera_profile))
EXIT_IF(!LaunchAndWait({generate_camera_profile.value()}));
}
if (base::PathExists(media_profile_xml)) {
const base::FilePath new_media_profile_xml =
base::FilePath(oem_mount_directory)
.Append("etc")
.Append(arc_paths_->media_profile_file);
brillo::SafeFD dest_parent(
brillo::SafeFD::Root()
.first.OpenExistingDir(new_media_profile_xml.DirName())
.first);
(void)dest_parent.Unlink(new_media_profile_xml.BaseName().value());
EXIT_IF(!SafeCopyFile(
media_profile_xml, brillo::SafeFD::Root().first /*src_parent*/,
new_media_profile_xml.BaseName(), std::move(dest_parent),
0644 /*permissions*/, kHostArcCameraUid, kHostArcCameraGid));
}
base::FilePath hardware_features_xml("/etc/hardware_features.xml");
std::string hw_feature_setting;
if (config->GetString(kHardwareFeaturesSetting, kSystemPath,
&hw_feature_setting)) {
hardware_features_xml = base::FilePath(hw_feature_setting);
}
if (!base::PathExists(hardware_features_xml))
return;
const base::FilePath platform_xml_file =
base::FilePath(oem_mount_directory)
.Append(arc_paths_->platform_xml_file_relative);
brillo::SafeFD dest_parent(
brillo::SafeFD::Root()
.first.OpenExistingDir(platform_xml_file.DirName())
.first);
(void)dest_parent.Unlink(platform_xml_file.BaseName().value());
EXIT_IF(!SafeCopyFile(hardware_features_xml,
brillo::SafeFD::Root().first /*src_parent*/,
platform_xml_file.BaseName(), std::move(dest_parent),
0644 /*permissions*/));
// TODO(chromium:1083652) Remove dynamic shell scripts once all overlays
// are migrated to static XML config.
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::SetUpSdcard() {
constexpr unsigned int mount_flags =
MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME;
const base::FilePath source_directory =
arc_paths_->android_mutable_source.Append("data/media");
const bool is_esdfs_supported = config_.GetBoolOrDie("USE_ESDFS");
// Get the container's user namespace file descriptor.
const int container_pid = config_.GetIntOrDie("CONTAINER_PID");
base::ScopedFD container_userns_fd(HANDLE_EINTR(
open(base::StringPrintf("/proc/%d/ns/user", container_pid).c_str(),
O_RDONLY)));
// SetUpSdcard can only be called from arc-sdcard is USE_ESDFS is enabled.
CHECK(is_esdfs_supported);
// Installd setups up the user data directory skeleton on first-time boot.
// Wait for setup
EXIT_IF(!WaitForSdcardSource(arc_paths_->android_mutable_source,
GetSdkVersion()));
for (const auto& mount : GetEsdfsMounts(GetSdkVersion())) {
base::FilePath dest_directory =
arc_paths_->sdcard_mount_directory.Append(mount.relative_path);
EXIT_IF(!arc_mounter_->Mount(
source_directory.value(), dest_directory, "esdfs", mount_flags,
CreateEsdfsMountOpts(kMediaUid, kMediaGid, mount.mode, kRootUid,
mount.gid, container_userns_fd.get())
.c_str()));
}
LOG(INFO) << "Esdfs setup complete.";
}
void ArcSetup::SetUpSharedTmpfsForExternalStorage() {
EXIT_IF(!arc_mounter_->UmountIfExists(arc_paths_->sdcard_mount_directory));
EXIT_IF(!brillo::MkdirRecursively(arc_paths_->sdcard_mount_directory, 0755)
.is_valid());
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() {
EXIT_IF(!arc_mounter_->UmountIfExists(arc_paths_->obb_mount_directory));
EXIT_IF(!brillo::MkdirRecursively(arc_paths_->obb_mount_directory, 0755)
.is_valid());
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,
ArcCodeRelocationResult* result) {
*result = ArcCodeRelocationResult::ERROR_UNABLE_TO_RELOCATE;
base::ElapsedTimer timer;
std::unique_ptr<ArtContainer> art_container =
ArtContainer::CreateContainer(arc_mounter_.get(), GetSdkVersion());
if (!art_container) {
LOG(ERROR) << "Failed to create art container";
return false;
}
const std::string salt = GetOrCreateArcSalt();
if (salt.empty()) {
*result = ArcCodeRelocationResult::SALT_EMPTY;
return false;
}
const uint64_t offset_seed = GetArtCompilationOffsetSeed(
GetSystemBuildPropertyOrDie(kFingerprintProp), salt);
if (!art_container->PatchImage(offset_seed)) {
LOG(ERROR) << "Failed to relocate boot images";
return false;
}
*result = ArcCodeRelocationResult::SUCCESS;
arc_setup_metrics_->SendCodeRelocationTime(timer.Elapsed());
return true;
}
bool ArcSetup::GenerateHostSideCode(
const base::FilePath& host_dalvik_cache_directory) {
ArcCodeRelocationResult result;
base::ElapsedTimer timer;
if (!GenerateHostSideCodeInternal(host_dalvik_cache_directory, &result)) {
// 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);
}
base::TimeDelta time_delta = timer.Elapsed();
LOG(INFO) << "GenerateHostSideCode took "
<< time_delta.InMillisecondsRoundedUp() << "ms";
arc_setup_metrics_->SendCodeRelocationResult(result);
return result == ArcCodeRelocationResult::SUCCESS;
}
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));
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_dalvik_cache_directory;
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()) {
if (IsDirectoryEmpty(src_isa_directory))
continue;
const std::string isa = src_isa_directory.BaseName().value();
if (!InstallLinksToHostSideCodeInternal(src_isa_directory,
dest_directory.Append(isa), isa)) {
result = false;
LOG(ERROR) << "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 is_dev_mode,
bool is_inside_vm,
bool is_debuggable,
PlayStoreAutoUpdate play_store_auto_update,
const std::string& dalvik_memory_profile,
bool disable_system_default_app) {
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 int arc_lcd_density = config_.GetIntOrDie("ARC_LCD_DENSITY");
LOG(INFO) << "lcd_density is " << arc_lcd_density;
const int arc_file_picker = config_.GetIntOrDie("ARC_FILE_PICKER_EXPERIMENT");
LOG(INFO) << "arc_file_picker is " << arc_file_picker;
const int arc_custom_tabs = config_.GetIntOrDie("ARC_CUSTOM_TABS_EXPERIMENT");
LOG(INFO) << "arc_custom_tabs is " << arc_custom_tabs;
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 << "\"";
LOG(INFO) << "dalvik memory profile is \""
<< (dalvik_memory_profile.empty() ? "default"
: dalvik_memory_profile)
<< "\"";
// Get the CLOCK_BOOTTIME offset and send it to the container as the at which
// the container "booted". Given that there is no way to namespace time in
// Linux, we need to communicate this in a userspace-only way.
//
// For the time being, the only component that uses this is bootstat. It uses
// it to timeshift all readings from CLOCK_BOOTTIME and be able to more
// accurately report the time against "Android boot".
struct timespec ts;
EXIT_IF(clock_gettime(CLOCK_BOOTTIME, &ts) != 0);
// 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).
const std::string content = base::StringPrintf(
"androidboot.hardware=cheets "
"androidboot.container=1 "
"androidboot.dev_mode=%d "
"androidboot.disable_runas=%d "
"androidboot.host_is_in_vm=%d "
"androidboot.debuggable=%d "
"androidboot.lcd_density=%d "
"androidboot.native_bridge=%s "
"androidboot.arc_file_picker=%d "
"androidboot.arc_custom_tabs=%d "
"androidboot.chromeos_channel=%s "
"%s" /* Play Store auto-update mode */
"%s" /* Dalvik memory profile */
"androidboot.disable_system_default_app=%d "
"androidboot.boottime_offset=%" PRId64 "\n" /* in nanoseconds */,
is_dev_mode, !is_dev_mode, is_inside_vm, is_debuggable, arc_lcd_density,
native_bridge.c_str(), arc_file_picker, arc_custom_tabs,
chromeos_channel.c_str(),
GetPlayStoreAutoUpdateParam(play_store_auto_update).c_str(),
GetDalvikMemoryProfileParam(dalvik_memory_profile).c_str(),
disable_system_default_app,
ts.tv_sec * base::Time::kNanosecondsPerSecond + ts.tv_nsec);
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
EXIT_IF(!arc_mounter_->UmountIfExists(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");
EXIT_IF(!arc_mounter_->UmountIfExists(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::MountDemoApps(const base::FilePath& demo_apps_image,
const base::FilePath& demo_apps_mount_directory) {
// Verify that the demo apps image is under an imageloader mount point.
EXIT_IF(demo_apps_image.ReferencesParent());
EXIT_IF(!base::FilePath("/run/imageloader").IsParent(demo_apps_image));
// Create the target mount point directory.
EXIT_IF(!InstallDirectory(0700, kHostRootUid, kHostRootGid,
demo_apps_mount_directory));
// imageloader securely verifies images before mounting them, so we can trust
// the provided image and can mount it without MS_NOEXEC.
EXIT_IF(!arc_mounter_->LoopMount(demo_apps_image.value(),
demo_apps_mount_directory,
MS_RDONLY | MS_NODEV));
}
void ArcSetup::SetUpMountPointsForMedia() {
EXIT_IF(!arc_mounter_->UmountIfExists(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));
for (auto* directory :
{"removable", "removable-default", "removable-read", "removable-write",
"MyFiles", "MyFiles-default", "MyFiles-read", "MyFiles-write"}) {
EXIT_IF(
!InstallDirectory(0755, kMediaUid, kMediaGid,
arc_paths_->media_mount_directory.Append(directory)));
}
}
void ArcSetup::SetUpMountPointForAdbd() {
EXIT_IF(!arc_mounter_->UmountIfExists(arc_paths_->adbd_mount_directory));
EXIT_IF(!InstallDirectory(0770, kShellUid, kShellGid,
arc_paths_->adbd_mount_directory));
const std::string adbd_mount_options =
base::StringPrintf("mode=0770,uid=%u,gid=%u", kShellUid, kShellGid);
EXIT_IF(!arc_mounter_->Mount("tmpfs", arc_paths_->adbd_mount_directory,
"tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC,
adbd_mount_options.c_str()));
EXIT_IF(!arc_mounter_->SharedMount(arc_paths_->adbd_mount_directory));
}
// Setup mount point for ADB over Unix sockets. This is used to enforce
// permission of the Unix sockets through SELinux. The mount is needed for
// ARC++ container whenever ADB debugging is enabled.
void ArcSetup::SetUpMountPointForAdbdUnixSocket() {
EXIT_IF(!arc_mounter_->UmountIfExists(
arc_paths_->adbd_unix_socket_mount_directory));
EXIT_IF(!InstallDirectory(0775, kShellUid, kShellGid,
arc_paths_->adbd_unix_socket_mount_directory));
const std::string adbd_unix_socket_mount_options =
base::StringPrintf("mode=0775,uid=%u,gid=%u", kShellUid, kShellGid);
EXIT_IF(!arc_mounter_->Mount("tmpfs",
arc_paths_->adbd_unix_socket_mount_directory,
"tmpfs", MS_NOSUID | MS_NODEV | MS_NOEXEC,
adbd_unix_socket_mount_options.c_str()));
EXIT_IF(
!arc_mounter_->SharedMount(arc_paths_->adbd_unix_socket_mount_directory));
}
void ArcSetup::CleanUpStaleMountPoints() {
EXIT_IF(!arc_mounter_->UmountIfExists(arc_paths_->media_myfiles_directory));
EXIT_IF(!arc_mounter_->UmountIfExists(
arc_paths_->media_myfiles_default_directory));
EXIT_IF(
!arc_mounter_->UmountIfExists(arc_paths_->media_myfiles_read_directory));
EXIT_IF(
!arc_mounter_->UmountIfExists(arc_paths_->media_myfiles_write_directory));
EXIT_IF(!arc_mounter_->UmountIfExists(arc_paths_->media_removable_directory));
EXIT_IF(!arc_mounter_->UmountIfExists(
arc_paths_->media_removable_default_directory));
EXIT_IF(!arc_mounter_->UmountIfExists(
arc_paths_->media_removable_read_directory));
EXIT_IF(!arc_mounter_->UmountIfExists(
arc_paths_->media_removable_write_directory));
// If the android_mutable_source path cannot be unmounted below continue
// anyway. This allows the mini-container to start and allows tests to
// exercise the mini-container (b/148185982).
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->android_mutable_source));
}
void ArcSetup::SetUpSharedMountPoints() {
EXIT_IF(!arc_mounter_->UmountIfExists(arc_paths_->shared_mount_directory));
EXIT_IF(!InstallDirectory(0755, kRootUid, kRootGid,
arc_paths_->shared_mount_directory));
// 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"));
EXIT_IF(!arc_mounter_->SharedMount(arc_paths_->shared_mount_directory));
}
void ArcSetup::SetUpOwnershipForSdcardConfigfs() {
// Make sure <configfs>/sdcardfs/ and <configfs>/sdcardfs/extensions} are
// owned by android-root.
const base::FilePath extensions_dir =
arc_paths_->sdcard_configfs_directory.Append("extensions");
if (base::PathExists(extensions_dir)) {
EXIT_IF(!Chown(kRootUid, kRootGid, arc_paths_->sdcard_configfs_directory));
EXIT_IF(!Chown(kRootUid, kRootGid, extensions_dir));
}
}
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_allowlist_sync))
directories.push_back(arc_paths_->restorecon_allowlist_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", "drm"};
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 = Realpath(dev.Append("device"));
// If it's virtio gpu, actually the PCI device
// directory should be the parent directory.
if (device.BaseName().value().find("virtio") == 0)
device = device.DirName();
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::SetUpPowerSysfsContext() {
const base::FilePath sysfs_power_supply_path("/sys/class/power_supply");
const std::string sysfs_batteryinfo_context(
"u:object_r:sysfs_batteryinfo:s0");
base::FileEnumerator power_supply_dir(
sysfs_power_supply_path, false /* recursive */,
base::FileEnumerator::FileType::DIRECTORIES);
for (auto power_supply = power_supply_dir.Next(); !power_supply.empty();
power_supply = power_supply_dir.Next()) {
base::FileEnumerator power_supply_attrs(
power_supply, false /* recursive */,
base::FileEnumerator::FileType::FILES);
for (auto attr = power_supply_attrs.Next(); !attr.empty();
attr = power_supply_attrs.Next()) {
EXIT_IF(!Chcon(sysfs_batteryinfo_context, Realpath(attr)));
}
}
}
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(const base::FilePath& build_prop) {
// 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");
EXIT_IF(!brillo::MkdirRecursively(camera_prop_directory, 0755).is_valid());
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 kSystemManufacturer = "ro.product.system.manufacturer=";
const std::string kManufacturer = "ro.product.manufacturer=";
const std::string kSystemModel = "ro.product.system.model=";
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)) {
// For Android P.
camera_properties += property + "\n";
} else if (!property.compare(0, kSystemManufacturer.length(),
kSystemManufacturer)) {
// Android Q+ only has |kSystemManufacturer| in /system/build.prop, and
// |kSystemManufacturer| is copied to |kManufacturer| at boot time. Do
// the same here.
camera_properties +=
kManufacturer + property.substr(kSystemManufacturer.length()) + "\n";
} else if (!property.compare(0, kSystemModel.length(), kSystemModel)) {
// Do the same for |kSystemModel| for Android Q+.
camera_properties +=
kModel + property.substr(kSystemModel.length()) + "\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));
}
}
AndroidSdkVersion ArcSetup::SdkVersionFromString(
const std::string& version_str) {
const std::string version_codename_str =
GetSystemBuildPropertyOrDie("ro.build.version.codename");
if (version_codename_str != "REL") {
LOG(INFO) << "Not a release version; classifying as Android Unknown.";
return AndroidSdkVersion::UNKNOWN;
}
int version;
if (base::StringToInt(version_str, &version)) {
switch (version) {
case 23:
return AndroidSdkVersion::ANDROID_M;
case 25:
return AndroidSdkVersion::ANDROID_N_MR1;
case 28:
return AndroidSdkVersion::ANDROID_P;
case 30:
return AndroidSdkVersion::ANDROID_R;
}
}
LOG(ERROR) << "Unknown SDK version : " << version_str;
return AndroidSdkVersion::UNKNOWN;
}
AndroidSdkVersion ArcSetup::GetSdkVersion() {
const std::string version_str =
GetSystemBuildPropertyOrDie("ro.build.version.sdk");
LOG(INFO) << "SDK version string: " << version_str;
const AndroidSdkVersion version = SdkVersionFromString(version_str);
if (version == AndroidSdkVersion::UNKNOWN)
LOG(FATAL) << "Unknown SDK version: " << version_str;
return version;
}
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_->UmountIfExists(
arc_paths_->shared_mount_directory.Append("cache")));
IGNORE_ERRORS(arc_mounter_->UmountIfExists(
arc_paths_->shared_mount_directory.Append("data")));
IGNORE_ERRORS(arc_mounter_->LoopUmountIfExists(
arc_paths_->shared_mount_directory.Append("demo_apps")));
IGNORE_ERRORS(arc_mounter_->UmountIfExists(arc_paths_->adbd_mount_directory));
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->media_myfiles_directory));
IGNORE_ERRORS(arc_mounter_->UmountIfExists(
arc_paths_->media_myfiles_default_directory));
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->media_myfiles_read_directory));
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->media_myfiles_write_directory));
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->media_removable_directory));
IGNORE_ERRORS(arc_mounter_->UmountIfExists(
arc_paths_->media_removable_default_directory));
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->media_removable_read_directory));
IGNORE_ERRORS(arc_mounter_->UmountIfExists(
arc_paths_->media_removable_write_directory));
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->media_mount_directory));
IGNORE_ERRORS(arc_mounter_->Umount(arc_paths_->sdcard_mount_directory));
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->shared_mount_directory));
IGNORE_ERRORS(arc_mounter_->Umount(arc_paths_->obb_mount_directory));
IGNORE_ERRORS(arc_mounter_->Umount(arc_paths_->oem_mount_directory));
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->android_mutable_source));
IGNORE_ERRORS(arc_mounter_->UmountIfExists(
arc_paths_->debugfs_directory.Append("sync")));
IGNORE_ERRORS(arc_mounter_->UmountIfExists(
arc_paths_->debugfs_directory.Append("tracing")));
// Clean up in case this was not unmounted.
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->binfmt_misc_directory));
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->android_rootfs_directory.Append(
arc_paths_->system_bin_arm_directory_relative)));
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->android_rootfs_directory.Append(
arc_paths_->system_bin_arm64_directory_relative)));
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->android_rootfs_directory.Append(
arc_paths_->system_lib_arm_directory_relative)));
IGNORE_ERRORS(
arc_mounter_->UmountIfExists(arc_paths_->android_rootfs_directory.Append(
arc_paths_->system_lib64_arm64_directory_relative)));
}
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));
}
// Note: This function has to be in sync with Android's arc-boot-type-detector.
// arc-boot-type-detector's main() function is very similar to this.
void ArcSetup::GetBootTypeAndDataSdkVersion(
ArcBootType* out_boot_type, AndroidSdkVersion* out_data_sdk_version) {
const std::string system_fingerprint =
GetSystemBuildPropertyOrDie(kFingerprintProp);
// 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.";
*out_boot_type = ArcBootType::FIRST_BOOT;
*out_data_sdk_version = AndroidSdkVersion::UNKNOWN;
return;
}
// Get a fingerprint from /data/system/packages.xml.
std::string data_fingerprint;
std::string data_sdk_version;
if (!GetFingerprintAndSdkVersionFromPackagesXml(
packages_xml, &data_fingerprint, &data_sdk_version)) {
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.
*out_boot_type = ArcBootType::FIRST_BOOT_AFTER_UPDATE;
*out_data_sdk_version = AndroidSdkVersion::UNKNOWN;
return;
}
// 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.
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;
}
LOG(INFO) << "Data SDK version: " << data_sdk_version;
LOG(INFO) << "System SDK version: " << static_cast<int>(GetSdkVersion());
*out_boot_type = ota_detected ? ArcBootType::FIRST_BOOT_AFTER_UPDATE
: ArcBootType::REGULAR_BOOT;
*out_data_sdk_version = SdkVersionFromString(data_sdk_version);
}
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) {
// Delete /data/dalvik-cache and /data/app/<package_name>/oat before the
// container starts since this is the first boot after OTA.
*out_should_delete_data_dalvik_cache_directory = true;
*out_should_delete_data_app_executables = true;
return;
}
// Otherwise, clear neither /data/dalvik-cache nor /data/app/*/oat.
*out_should_delete_data_dalvik_cache_directory = false;
*out_should_delete_data_app_executables = false;
}
std::string ArcSetup::GetSerialNumber() {
const std::string chromeos_user = config_.GetStringOrDie("CHROMEOS_USER");
const std::string salt = GetOrCreateArcSalt();
EXIT_IF(salt.empty()); // at this point, the salt file should always exist.
return arc::GenerateFakeSerialNumber(chromeos_user, salt);
}
// TODO(yusukes): Delete this function in M85.
void ArcSetup::DeleteUnusedCacheDirectory() {
// /home/.../android-data/cache is bind-mounted to /cache on N in
// MountSharedAndroidDirectories, but it is no longer bind-mounted on P.
EXIT_IF(
!MoveDirIntoDataOldDir(arc_paths_->android_data_directory.Append("cache"),
arc_paths_->android_data_old_directory));
}
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 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));
}
// 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 MS_SLAVE one.
EXIT_IF(!arc_mounter_->BindMount(data_directory, data_directory));
EXIT_IF(
!arc_mounter_->Remount(data_directory, MS_NOSUID | MS_NODEV, "seclabel"));
// Finally, bind-mount /data to the shared mount point.
EXIT_IF(!arc_mounter_->Mount(data_directory.value(), shared_data_directory,
nullptr, MS_BIND, nullptr));
const std::string demo_session_apps =
config_.GetStringOrDie("DEMO_SESSION_APPS_PATH");
if (!demo_session_apps.empty()) {
MountDemoApps(base::FilePath(demo_session_apps),
arc_paths_->shared_mount_directory.Append("demo_apps"));
}
}
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");
const base::FilePath shared_demo_apps_directory =
arc_paths_->shared_mount_directory.Append("demo_apps");
IGNORE_ERRORS(arc_mounter_->Umount(data_directory));
IGNORE_ERRORS(arc_mounter_->UmountIfExists(shared_cache_directory));
IGNORE_ERRORS(arc_mounter_->Umount(shared_data_directory));
IGNORE_ERRORS(arc_mounter_->LoopUmountIfExists(shared_demo_apps_directory));
IGNORE_ERRORS(arc_mounter_->Umount(arc_paths_->shared_mount_directory));
}
void ArcSetup::MaybeStartAdbdProxy(bool is_dev_mode,
bool is_inside_vm,
const std::string& serialnumber) {
if (!is_dev_mode || is_inside_vm)
return;
const base::FilePath adbd_config_path("/etc/arc/adbd.json");
if (!base::PathExists(adbd_config_path))
return;
// Poll the firmware to determine whether UDC is enabled or not. We're only
// stopping the process if it's explicitly disabled because some systems (like
// ARM) do not have this signal wired in and just rely on the presence of
// adbd.json.
if (LaunchAndWait({"/usr/bin/crossystem", "dev_enable_udc?0"}))
return;
// Now that we have identified that the system is capable of continuing, touch
// the path where the FIFO will be located.
const base::FilePath control_endpoint_path("/run/arc/adbd/ep0");
EXIT_IF(!CreateOrTruncate(control_endpoint_path, 0600));
EXIT_IF(!Chown(kShellUid, kShellGid, control_endpoint_path));
EXIT_IF(!LaunchAndWait(
{"/sbin/initctl", "start", "--no-wait", "arc-adbd",
base::StringPrintf("SERIALNUMBER=%s", serialnumber.c_str())}));
}
void ArcSetup::ContinueContainerBoot(ArcBootType boot_type,
const std::string& serialnumber) {
constexpr char kCommand[] = "/system/bin/arcbootcontinue";
const bool mount_demo_apps =
!config_.GetStringOrDie("DEMO_SESSION_APPS_PATH").empty();
std::string copy_packages_cache;
if (config_.GetBoolOrDie("SKIP_PACKAGES_CACHE_SETUP")) {
copy_packages_cache = "2";
} else if (config_.GetBoolOrDie("COPY_PACKAGES_CACHE")) {
copy_packages_cache = "1";
} else {
copy_packages_cache = "0";
}
// 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::string pid_str = config_.GetStringOrDie("CONTAINER_PID");
const std::vector<std::string> command_line = {
"/usr/bin/nsenter", "-t", pid_str,
"-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", serialnumber, "--disable-boot-completed",
config_.GetStringOrDie("DISABLE_BOOT_COMPLETED_BROADCAST"),
"--container-boot-type", std::to_string(static_cast<int>(boot_type)),
// When copy_packages_cache is set to "0" or "1", arccachesetup copies
// /system/etc/packages_cache.xml to /data/system/packages.xml. If it is
// set to "2", arccachesetup skips copying. When copy_packages_cache is
// "1" or "2", SystemServer copies /data/system/packages.xml
// to /data/system/packages_copy.xml after the initialization stage of
// PackageManagerService.
"--copy-packages-cache", copy_packages_cache,
"--skip-gms-core-cache-setup",
config_.GetStringOrDie("SKIP_GMS_CORE_CACHE_SETUP"), "--mount-demo-apps",
mount_demo_apps ? "1" : "0", "--is-demo-session",
config_.GetStringOrDie("IS_DEMO_SESSION"), "--locale",
config_.GetStringOrDie("LOCALE"), "--preferred-languages",
config_.GetStringOrDie("PREFERRED_LANGUAGES"),
// Whether ARC should transition the supervision setup
// "0": No transition necessary.
// "1": Child -> regular transition, should disable supervision.
// "2": Regular -> child transition, should enable supervision.
"--supervision-transition",
config_.GetStringOrDie("SUPERVISION_TRANSITION"),
"--enable-adb-sideloading",
config_.GetStringOrDie("ENABLE_ADB_SIDELOAD")};
base::ElapsedTimer timer;
if (!LaunchAndWait(command_line)) {
auto elapsed = timer.Elapsed().InMillisecondsRoundedUp();
// ContinueContainerBoot() failed. Try to find out why it failed and log
// messages accordingly. If one of these functions calls exit(), it means
// that '/usr/bin/nsenter' is very likely the command that failed (rather
// than '/system/bin/arcbootcontinue'.)
CheckProcessIsAliveOrExit(pid_str);
CheckNamespacesAvailableOrExit(pid_str);
CheckOtherProcEntriesOrExit(pid_str);
// Either nsenter or arcbootcontinue failed, but we don't know which. For
// example, arcbootcontinue may fail if it tries to set a property while
// init is being shut down or crashing.
LOG(ERROR) << kCommand << " failed for unknown reason after " << elapsed
<< "ms";
exit(EXIT_FAILURE);
}
LOG(INFO) << "Running " << kCommand << " took "
<< timer.Elapsed().InMillisecondsRoundedUp() << "ms";
StartNetworking();
}
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));
// Chrome writes to /run/arc/host_generated even before starting the mini
// container.
EXIT_IF(!InstallDirectory(0755, kHostRootUid, kHostRootGid,
base::FilePath("/run/arc")));
EXIT_IF(!InstallDirectory(0775, kHostRootUid, kHostChronosGid,
base::FilePath("/run/arc/host_generated")));
}
void ArcSetup::StartNetworking() {
if (!patchpanel::Client::New()->NotifyArcStartup(
config_.GetIntOrDie("CONTAINER_PID"))) {
LOG(ERROR) << "Failed to notify network service";
}
}
void ArcSetup::StopNetworking() {
// The container pid isn't available at this point.
if (!patchpanel::Client::New()->NotifyArcShutdown())
LOG(ERROR) << "Failed to notify network service";
}
void ArcSetup::MountOnOnetimeSetup() {
const bool is_writable = config_.GetBoolOrDie("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(
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_->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)));
if (kUseHoudini64) {
// Bind mount arm64 directory for houdini64.
EXIT_IF(!arc_mounter_->BindMount(
rootfs.Append("vendor/lib64/arm64"),
rootfs.Append(arc_paths_->system_lib64_arm64_directory_relative)));
}
}
const base::FilePath proc_rnd_compat =
rootfs.Append("proc/sys/vm/mmap_rnd_compat_bits");
if (base::PathExists(proc_rnd_compat)) {
EXIT_IF(!arc_mounter_->BindMount(arc_paths_->fake_mmap_rnd_compat_bits,
proc_rnd_compat));
}
}
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.
std::vector<const char*> directories{"dev",
"oem/etc",
"var/run/arc/adb",
"var/run/arc/apkcache",
"var/run/arc/dalvik-cache",
"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(directories.cbegin(), directories.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",
"vendor/build.prop"};
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");
EXIT_IF(!CreateOrTruncate(coldboot_done, 0755));
EXIT_IF(!Chown(kRootUid, kRootGid, coldboot_done));
}
void ArcSetup::SendUpgradeMetrics(AndroidSdkVersion data_sdk_version) {
LOG(INFO) << "Sending upgrade metrics";
arc_setup_metrics_->SendSdkVersionUpgradeType(
GetUpgradeType(GetSdkVersion(), data_sdk_version));
}
void ArcSetup::DeleteAndroidDataOnUpgrade(AndroidSdkVersion data_sdk_version) {
if (!ShouldDeleteAndroidData(GetSdkVersion(), data_sdk_version))
return;
LOG(INFO) << "Deleting old Android data";
EXIT_IF(!MoveDirIntoDataOldDir(arc_paths_->android_data_directory,
arc_paths_->android_data_old_directory));
}
void ArcSetup::OnSetup() {
const bool is_dev_mode = config_.GetBoolOrDie("CHROMEOS_DEV_MODE");
const bool is_inside_vm = config_.GetBoolOrDie("CHROMEOS_INSIDE_VM");
const bool is_debuggable = config_.GetBoolOrDie("ANDROID_DEBUGGABLE");
const bool disable_system_default_app =
config_.GetBoolOrDie("DISABLE_SYSTEM_DEFAULT_APP");
// 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.
EXIT_IF(
!CreateArtContainerDataDirectory(arc_paths_->art_dalvik_cache_directory));
// 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.
// Unconditionally generate host-side code here.
{
base::ElapsedTimer timer;
EXIT_IF(!GenerateHostSideCode(arc_paths_->art_dalvik_cache_directory));
EXIT_IF(!Chown(kRootUid, kRootGid, arc_paths_->art_dalvik_cache_directory));
// Remove the file zygote may have created.
IGNORE_ERRORS(base::DeleteFile(
arc_paths_->art_dalvik_cache_directory.Append(kZygotePreloadDoneFile)));
// 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());
}
// Make sure directories for all ISA are there just to make config.json happy.
for (const auto* isa : {"arm", "arm64", "x86", "x86_64"}) {
EXIT_IF(!brillo::MkdirRecursively(
arc_paths_->art_dalvik_cache_directory.Append(isa), 0755)
.is_valid());
}
PlayStoreAutoUpdate play_store_auto_update;
std::string dalvik_memory_profile;
bool play_store_auto_update_on;
// PLAY_AUTO_UPDATE forces Play Store auto-update feature to on or off. If not
// set, its state is left unchanged.
if (config_.GetBool("PLAY_STORE_AUTO_UPDATE", &play_store_auto_update_on)) {
play_store_auto_update = play_store_auto_update_on
? PlayStoreAutoUpdate::kOn
: PlayStoreAutoUpdate::kOff;
} else {
play_store_auto_update = PlayStoreAutoUpdate::kDefault;
}
config_.GetString("DALVIK_MEMORY_PROFILE", &dalvik_memory_profile);
SetUpSharedMountPoints();
CreateContainerFilesAndDirectories();
ApplyPerBoardConfigurations();
SetUpSharedTmpfsForExternalStorage();
SetUpFilesystemForObbMounter();
CreateAndroidCmdlineFile(is_dev_mode, is_inside_vm, is_debuggable,
play_store_auto_update, dalvik_memory_profile,
disable_system_default_app);
CreateFakeProcfsFiles();
SetUpMountPointForDebugFilesystem(is_dev_mode);
SetUpMountPointsForMedia();
SetUpMountPointForAdbd();
SetUpMountPointForAdbdUnixSocket();
CleanUpStaleMountPoints();
RestoreContext();
SetUpGraphicsSysfsContext();
SetUpPowerSysfsContext();
MakeMountPointsReadOnly();
SetUpCameraProperty(base::FilePath(kBuildPropFile));
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 bool is_dev_mode = config_.GetBoolOrDie("CHROMEOS_DEV_MODE");
const bool is_inside_vm = config_.GetBoolOrDie("CHROMEOS_INSIDE_VM");
const std::string serialnumber = GetSerialNumber();
ArcBootType boot_type;
AndroidSdkVersion data_sdk_version;
GetBootTypeAndDataSdkVersion(&boot_type, &data_sdk_version);
SendUpgradeMetrics(data_sdk_version);
DeleteAndroidDataOnUpgrade(data_sdk_version);
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 socket isn't created when the mini-container is started, so the
// arc-setup --mode=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(arc_paths_->android_mutable_source);
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,demo_apps} to expose the user's
// data to the container. Demo apps are setup only for demo sessions.
MountSharedAndroidDirectories();
MaybeStartAdbdProxy(is_dev_mode, is_inside_vm, serialnumber);
// Asks the container to continue boot.
ContinueContainerBoot(boot_type, serialnumber);
// Unmount /run/arc/shared_mounts and its children. They are unnecessary at
// this point.
UnmountSharedAndroidDirectories();
DeleteUnusedCacheDirectory();
const std::string env_to_pass = base::StringPrintf(
"CONTAINER_PID=%d", config_.GetIntOrDie("CONTAINER_PID"));
EXIT_IF(!LaunchAndWait(
{"/sbin/initctl", "start", "--no-wait", "arc-sdcard", env_to_pass}));
}
void ArcSetup::OnStop() {
StopNetworking();
CleanUpBinFmtMiscSetUp();
UnmountOnStop();
RemoveAndroidKmsgFifo();
}
void ArcSetup::OnOnetimeSetup() {
EnsureContainerDirectories();
MountOnOnetimeSetup();
// Setup ownership for <configfs>/sdcard, if the directory exists.
SetUpOwnershipForSdcardConfigfs();
}
void ArcSetup::OnOnetimeStop() {
UnmountOnOnetimeStop();
}
void ArcSetup::OnPreChroot() {
// Note: Do not try to create a directory in tmpfs here. Recent (4.8+)
// kernel doesn't allow us to do so and returns EOVERFLOW. b/78262683
// 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<brillo::ScopedMountNamespace> container_mount_ns =
brillo::ScopedMountNamespace::CreateForPid(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::OnRemoveData() {
EXIT_IF(!MoveDirIntoDataOldDir(arc_paths_->android_data_directory,
arc_paths_->android_data_old_directory));
}
void ArcSetup::OnRemoveStaleData() {
// To protect itself, base::SafeFD::RmDir() uses a default maximum
// recursion depth of 256. In this case, we are deleting the user's
// arbitrary filesystem and want to be more generous. However, RmDir()
// uses one fd per path level when recursing so we will have the max
// number of fds per process as an upper bound (default 1024). Leave a
// 25% buffer below this default 1024 limit to give lots of room for
// incidental usage elsewhere in the process. Use this everywhere here
// for consistency.
constexpr int kRmdirMaxDepth = 768;
brillo::SafeFD root = brillo::SafeFD::Root().first;
if (USE_ARCVM) {
// On ARCVM, stale *.odex files are kept in /data/vendor/arc.
base::FilePath arcvm_stale_odex = arc_paths_->android_data_directory.Append(
"data/vendor/arc/old_arc_executables_pre_ota");
brillo::SafeFD parent_dir =
root.OpenExistingDir(arcvm_stale_odex.DirName()).first;
brillo::SafeFD::Error err = parent_dir.Rmdir(
arcvm_stale_odex.BaseName().value(), true /*recursive*/, kRmdirMaxDepth,
true /*keep_going*/);
if (brillo::SafeFD::IsError(err) &&
err != brillo::SafeFD::Error::kDoesNotExist) {
LOG(ERROR) << "Errors while cleaning old data from "
<< arcvm_stale_odex.value();
}
}
// Moving data to android_data_old no longer has race conditions so it is safe
// to delete the entire directory.
brillo::SafeFD parent_dir =
root.OpenExistingDir(arc_paths_->android_data_old_directory.DirName())
.first;
brillo::SafeFD::Error err = parent_dir.Rmdir(
arc_paths_->android_data_old_directory.BaseName().value(),
true /*recursive*/, kRmdirMaxDepth, true /*keep_going*/);
if (brillo::SafeFD::IsError(err) &&
err != brillo::SafeFD::Error::kDoesNotExist) {
LOG(ERROR) << "Errors while cleaning old data from "
<< arc_paths_->android_data_old_directory.value();
}
}
void ArcSetup::OnApplyPerBoardConfig() {
ApplyPerBoardConfigurationsInternal(base::FilePath(kArcVmPerBoardConfigPath));
SetUpCameraProperty(base::FilePath(kBuildPropFileVm));
// Unlike ARC container, ARCVM's platform.xml has to be owned by chronos.
brillo::SafeFD fd;
brillo::SafeFD::Error err;
std::tie(fd, err) = brillo::SafeFD::Root().first.OpenExistingFile(
base::FilePath(kArcVmPerBoardConfigPath)
.Append(kPlatformXmlFileRelative));
if (err == brillo::SafeFD::Error::kDoesNotExist)
return; // the board does not have the file.
EXIT_IF(!fd.is_valid());
EXIT_IF(fchown(fd.get(), kHostChronosUid, kHostChronosGid));
}
void ArcSetup::OnCreateData() {
const base::FilePath bind_target(
config_.GetStringOrDie("ANDROID_MUTABLE_SOURCE"));
// bind_target may be already bound if arc-create-data has previously run
// during this session.
EXIT_IF(!arc_mounter_->UmountIfExists(bind_target));
// Create data folder and bind to bind_target. The bind mount will be cleaned
// up in vm_concierge.conf's post-stop script, when the mnt_concierge
// namespace is unmounted.
SetUpAndroidData(bind_target);
}
void ArcSetup::OnMountSdcard() {
// Set up sdcard asynchronously from arc-sdcard so that waiting on installd
// does not add latency to boot-continue (and result in session-manager
// related timeouts).
SetUpSdcard();
}
void ArcSetup::OnUnmountSdcard() {
UnmountSdcard();
}
void ArcSetup::OnUpdateRestoreconLast() {
// On Android, /init writes the security.restorecon_last attribute to /data
// (and /cache on N) after it finishes updating labels of the files in the
// directories, but on ARC, writing the attribute fails silently because
// processes in user namespace are not allowed to write arbitrary entries
// under security.* even with CAP_SYS_ADMIN. (b/33084415, b/33402785)
// As a workaround, let this command outside the container set the
// attribute for ARC.
constexpr char kRestoreconLastXattr[] = "security.restorecon_last";
std::vector<base::FilePath> context_files;
std::vector<base::FilePath> target_directories = {
arc_paths_->android_mutable_source.Append("data")};
// The order of files to read is important. Do not reorder.
context_files.push_back(
arc_paths_->android_rootfs_directory.Append("plat_file_contexts"));
context_files.push_back(
arc_paths_->android_rootfs_directory.Append("vendor_file_contexts"));
std::string hash;
EXIT_IF(!GetSha1HashOfFiles(context_files, &hash));
for (const auto& target : target_directories) {
EXIT_IF(!SetXattr(target, kRestoreconLastXattr, hash));
}
}
void ArcSetup::OnHandleUpgrade() {
ArcBootType boot_type;
AndroidSdkVersion data_sdk_version;
GetBootTypeAndDataSdkVersion(&boot_type, &data_sdk_version);
SendUpgradeMetrics(data_sdk_version);
DeleteAndroidDataOnUpgrade(data_sdk_version);
}
std::string ArcSetup::GetSystemBuildPropertyOrDie(const std::string& name) {
if (system_properties_.empty()) {
// First time read of system properties file.
// We don't know if we are in a container or on VM yet, so try the
// build.prop location on container first and fall back to the
// conbined.prop location on VM if empty.
const base::FilePath build_prop =
arc_paths_->android_generated_properties_directory.Append("build.prop");
GetPropertiesFromFile(build_prop, &system_properties_);
if (system_properties_.empty()) {
const base::FilePath combined_prop_vm(kCombinedPropFileVm);
GetPropertiesFromFile(combined_prop_vm, &system_properties_);
}
}
DCHECK(!system_properties_.empty());
const auto it = system_properties_.find(name);
CHECK(system_properties_.end() != it) << "Failed to read property: " << name;
CHECK(!it->second.empty());
return it->second;
}
void ArcSetup::Run() {
switch (mode_) {
case Mode::SETUP:
bootstat_log("mini-android-start");
OnSetup();
bootstat_log("arc-setup-for-mini-android-end");
break;
case Mode::STOP:
OnStop();
break;
case Mode::BOOT_CONTINUE:
bootstat_log("android-start");
OnBootContinue();
bootstat_log("arc-setup-end");
break;
case Mode::ONETIME_SETUP:
OnOnetimeSetup();
break;
case Mode::ONETIME_STOP:
OnOnetimeStop();
break;
case Mode::PRE_CHROOT:
OnPreChroot();
break;
case Mode::APPLY_PER_BOARD_CONFIG:
OnApplyPerBoardConfig();
break;
case Mode::CREATE_DATA:
OnCreateData();
break;
case Mode::REMOVE_DATA:
OnRemoveData();
break;
case Mode::REMOVE_STALE_DATA:
OnRemoveStaleData();
break;
case Mode::MOUNT_SDCARD:
OnMountSdcard();
break;
case Mode::UNMOUNT_SDCARD:
OnUnmountSdcard();
break;
case Mode::UPDATE_RESTORECON_LAST:
OnUpdateRestoreconLast();
break;
case Mode::HANDLE_UPGRADE:
OnHandleUpgrade();
break;
case Mode::UNKNOWN:
NOTREACHED();
break;
}
}
void ArcSetup::MountOnOnetimeSetupForTesting() {
MountOnOnetimeSetup();
}
void ArcSetup::UnmountOnOnetimeStopForTesting() {
UnmountOnOnetimeStop();
}
} // namespace arc