Commit 7272561a authored by Yusuke Sato's avatar Yusuke Sato Committed by Commit Bot

arcvm: Generate first stage fstab file for ARCVM

The new file, /run/arcvm/host_generated/fstab, is exported by crosvm
to the guest via the device tree so the guest can read certain files
in its init's first stage.

Previously, the generated file was just:

/run/arcvm/host_generated/factory/factory.prop

With this CL, they will be:

/run/arcvm/host_generated/fstab
/run/arcvm/host_generated/combined.prop

This CL depends on
https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2217082

BUG=b:157188856
TEST=arc.BuildProperties.vm

Change-Id: Ia646fe592e94e83ddce80e21177086f5b996ee3e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2216916Reviewed-by: default avatarLong Cheng <lgcheng@google.com>
Commit-Queue: Yusuke Sato <yusukes@chromium.org>
Cr-Commit-Position: refs/heads/master@{#773532}
parent 1a7be4af
......@@ -10,6 +10,7 @@
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/strings/string16.h"
......@@ -185,6 +186,18 @@ std::string GetOrCreateSerialNumber(PrefService* prefs) {
return serial_number;
}
bool ExpandPropertyFilesInternal(const base::FilePath& source_path,
const base::FilePath& dest_path,
bool single_file) {
if (!arc::ExpandPropertyFiles(source_path, dest_path, single_file))
return false;
if (!arc::IsArcVmEnabled())
return true;
// For ARCVM, the first stage fstab file needs to be generated.
return arc::GenerateFirstStageFstab(dest_path,
dest_path.DirName().Append("fstab"));
}
} // namespace
// This class is used to track statuses on OptIn flow. It is created in case ARC
......@@ -1284,13 +1297,13 @@ void ArcSessionManager::EmitLoginPromptVisibleCalled() {
void ArcSessionManager::ExpandPropertyFiles() {
VLOG(1) << "Started expanding *.prop files";
// For ARCVM, generate <dest_path>/factory/factory.prop. For ARC, generate
// For ARCVM, generate <dest_path>/{combined.prop,fstab}. For ARC, generate
// <dest_path>/{default,build,vendor_build}.prop.
const bool is_arcvm = arc::IsArcVmEnabled();
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_VISIBLE},
base::BindOnce(&arc::ExpandPropertyFiles, property_files_source_dir_,
is_arcvm ? property_files_dest_dir_.Append("factory")
base::BindOnce(&ExpandPropertyFilesInternal, property_files_source_dir_,
is_arcvm ? property_files_dest_dir_.Append("combined.prop")
: property_files_dest_dir_,
/*single_file=*/is_arcvm),
base::BindOnce(&ArcSessionManager::OnExpandPropertyFiles,
......
......@@ -11,6 +11,8 @@
#include "base/bind.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/optional.h"
#include "base/strings/string_number_conversions.h"
#include "chromeos/constants/chromeos_switches.h"
......@@ -303,7 +305,6 @@ bool IsArcPlayAutoInstallDisabled() {
chromeos::switches::kArcDisablePlayAutoInstall);
}
// static
int32_t GetLcdDensityForDeviceScaleFactor(float device_scale_factor) {
const auto* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(chromeos::switches::kArcScale)) {
......@@ -333,4 +334,26 @@ int32_t GetLcdDensityForDeviceScaleFactor(float device_scale_factor) {
kDefaultDensityDpi);
}
bool GenerateFirstStageFstab(const base::FilePath& combined_property_file_name,
const base::FilePath& fstab_path) {
DCHECK(IsArcVmEnabled());
// The file is exposed to the guest by crosvm via /sys/firmware/devicetree,
// which in turn allows the guest's init process to mount /vendor very early,
// in its first stage (device) initialization step. crosvm also special-cases
// #dt-vendor line and expose |combined_property_file_name| via the device
// tree file system too. This also allow the init process to load the expanded
// properties very early even before all file systems are mounted.
//
// The device name for /vendor has to match what arc_vm_client_adapter.cc
// configures.
constexpr const char kFirstStageFstabTemplate[] =
"/dev/block/vdb /vendor squashfs ro,noatime,nosuid,nodev "
"wait,check,formattable,reservedsize=128M\n"
"#dt-vendor build.prop %s default default\n";
return base::WriteFile(
fstab_path,
base::StringPrintf(kFirstStageFstabTemplate,
combined_property_file_name.value().c_str()));
}
} // namespace arc
......@@ -18,6 +18,7 @@ class Window;
namespace base {
class CommandLine;
class FilePath;
} // namespace base
namespace user_manager {
......@@ -149,6 +150,12 @@ void SetArcCpuRestriction(CpuRestrictionState cpu_restriction_state);
// factor used on chrome.
int32_t GetLcdDensityForDeviceScaleFactor(float device_scale_factor);
// Generates a file called first stage fstab at |fstab_path| which is exported
// by crosvm to the guest via the device tree so the guest can read certain
// files in its init's first stage.
bool GenerateFirstStageFstab(const base::FilePath& combined_property_file_name,
const base::FilePath& fstab_path);
} // namespace arc
#endif // COMPONENTS_ARC_ARC_UTIL_H_
......@@ -10,6 +10,8 @@
#include "ash/public/cpp/app_types.h"
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/test/scoped_feature_list.h"
......@@ -275,5 +277,32 @@ TEST_F(ArcUtilTest, ScaleFactorToDensity) {
EXPECT_EQ(240, GetLcdDensityForDeviceScaleFactor(2.0));
}
TEST_F(ArcUtilTest, GenerateFirstStageFstab) {
constexpr const char kFakeCombinedBuildPropPath[] = "/path/to/build.prop";
constexpr const char kAnotherFakeCombinedBuildPropPath[] =
"/foo/bar/baz.prop";
auto* command_line = base::CommandLine::ForCurrentProcess();
command_line->InitFromArgv({"", "--enable-arcvm"});
std::string content;
base::ScopedTempDir dir;
ASSERT_TRUE(dir.CreateUniqueTempDir());
const base::FilePath fstab(dir.GetPath().Append("fstab"));
// Generate the fstab and verify the content.
EXPECT_TRUE(GenerateFirstStageFstab(
base::FilePath(kFakeCombinedBuildPropPath), fstab));
EXPECT_TRUE(base::ReadFileToString(fstab, &content));
EXPECT_NE(std::string::npos, content.find(kFakeCombinedBuildPropPath));
// Generate the fstab again with the other prop file and verify the content.
EXPECT_TRUE(GenerateFirstStageFstab(
base::FilePath(kAnotherFakeCombinedBuildPropPath), fstab));
EXPECT_TRUE(base::ReadFileToString(fstab, &content));
EXPECT_EQ(std::string::npos, content.find(kFakeCombinedBuildPropPath));
EXPECT_NE(std::string::npos, content.find(kAnotherFakeCombinedBuildPropPath));
}
} // namespace
} // namespace arc
......@@ -302,14 +302,13 @@ bool ExpandPropertyFiles(const base::FilePath& source_path,
const base::FilePath& dest_path,
bool single_file) {
CrosConfig config;
const base::FilePath single_dest_file = dest_path.Append("factory.prop");
if (single_file)
base::DeleteFile(single_dest_file, /*recursive=*/false);
base::DeleteFile(dest_path, /*recursive=*/false);
for (const char* file : {"default.prop", "build.prop", "vendor_build.prop"}) {
if (!ExpandPropertyFile(
source_path.Append(file),
single_file ? single_dest_file : dest_path.Append(file), &config,
if (!ExpandPropertyFile(source_path.Append(file),
single_file ? dest_path : dest_path.Append(file),
&config,
/*append=*/single_file)) {
LOG(ERROR) << "Failed to expand " << source_path.Append(file);
return false;
......
......@@ -59,9 +59,8 @@ bool ExpandPropertyFileForTesting(const base::FilePath& input,
// Calls ExpandPropertyFile for {build,default,vendor_build}.prop files in
// |source_path|. Expanded files are written in |dest_path|. Returns true on
// success. When |single_file| is true, only one file (factory.prop) is written
// in |dest_path|, and also, properties that are actually expanded are written
// to the file.
// success. When |single_file| is true, only one file (|dest_path| itself) is
// written. All expanded properties are included in the single file.
bool ExpandPropertyFiles(const base::FilePath& source_path,
const base::FilePath& dest_path,
bool single_file);
......
......@@ -357,48 +357,50 @@ TEST_F(ArcPropertyUtilTest, ExpandPropertyFiles_SingleFile) {
// Both source and dest exist, but the source directory is empty.
base::FilePath source_dir;
ASSERT_TRUE(base::CreateTemporaryDirInDir(GetTempDir(), "test", &source_dir));
base::FilePath dest_dir;
ASSERT_TRUE(base::CreateTemporaryDirInDir(GetTempDir(), "test", &dest_dir));
EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_dir, true));
base::FilePath dest_prop_file;
ASSERT_TRUE(
base::CreateTemporaryDirInDir(GetTempDir(), "test", &dest_prop_file));
dest_prop_file = dest_prop_file.Append("combined.prop");
EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_prop_file, true));
// Add default.prop to the source, but not build.prop.
base::FilePath default_prop = source_dir.Append("default.prop");
constexpr const char kDefaultProp[] = "ro.foo=bar\n";
base::WriteFile(default_prop, kDefaultProp, strlen(kDefaultProp));
EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_dir, true));
EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_prop_file, true));
// Add build.prop too. The call should not succeed still.
base::FilePath build_prop = source_dir.Append("build.prop");
constexpr const char kBuildProp[] = "ro.baz=boo\n";
base::WriteFile(build_prop, kBuildProp, strlen(kBuildProp));
EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_dir, true));
EXPECT_FALSE(ExpandPropertyFiles(source_dir, dest_prop_file, true));
// Add vendor_build.prop too. Then the call should succeed.
base::FilePath vendor_build_prop = source_dir.Append("vendor_build.prop");
constexpr const char kVendorBuildProp[] = "ro.a=b\n";
base::WriteFile(vendor_build_prop, kVendorBuildProp,
strlen(kVendorBuildProp));
EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_dir, true));
EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_prop_file, true));
// Verify only one dest file exists.
EXPECT_FALSE(base::PathExists(dest_dir.Append("default.prop")));
EXPECT_FALSE(base::PathExists(dest_dir.Append("build.prop")));
EXPECT_FALSE(base::PathExists(dest_dir.Append("vendor_build.prop")));
EXPECT_TRUE(base::PathExists(dest_dir.Append("factory.prop")));
EXPECT_FALSE(
base::PathExists(dest_prop_file.DirName().Append("default.prop")));
EXPECT_FALSE(base::PathExists(dest_prop_file.DirName().Append("build.prop")));
EXPECT_FALSE(
base::PathExists(dest_prop_file.DirName().Append("vendor_build.prop")));
EXPECT_TRUE(base::PathExists(dest_prop_file));
// Verify the content.
// Note: ExpandPropertyFileForTesting() adds a trailing LF.
std::string content;
EXPECT_TRUE(
base::ReadFileToString(dest_dir.Append("factory.prop"), &content));
EXPECT_TRUE(base::ReadFileToString(dest_prop_file, &content));
EXPECT_EQ(base::StringPrintf("%s\n%s\n%s\n", kDefaultProp, kBuildProp,
kVendorBuildProp),
content);
// Expand it again, verify the previous result is cleared.
EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_dir, true));
EXPECT_TRUE(
base::ReadFileToString(dest_dir.Append("factory.prop"), &content));
EXPECT_TRUE(ExpandPropertyFiles(source_dir, dest_prop_file, true));
EXPECT_TRUE(base::ReadFileToString(dest_prop_file, &content));
EXPECT_EQ(base::StringPrintf("%s\n%s\n%s\n", kDefaultProp, kBuildProp,
kVendorBuildProp),
content);
......
......@@ -227,19 +227,21 @@ vm_tools::concierge::StartArcVmRequest CreateStartArcVmRequest(
vm->set_kernel(file_system_status.guest_kernel_path().value());
// Add / as /dev/vda.
// Add rootfs as /dev/vda.
vm->set_rootfs(file_system_status.system_image_path().value());
request.set_rootfs_writable(file_system_status.is_host_rootfs_writable() &&
file_system_status.is_system_image_ext_format());
// Add /vendor as /dev/vdb.
// Add /vendor as /dev/block/vdb. The device name has to be consistent with
// the one in GenerateFirstStageFstab() in ../arc_util.cc.
vm_tools::concierge::DiskImage* disk_image = request.add_disks();
disk_image->set_path(file_system_status.vendor_image_path().value());
disk_image->set_image_type(vm_tools::concierge::DISK_IMAGE_AUTO);
disk_image->set_writable(false);
disk_image->set_do_mount(true);
// Add /run/imageloader/.../android_demo_apps.squash as /dev/vdc if needed.
// Add /run/imageloader/.../android_demo_apps.squash as /dev/block/vdc if
// needed.
// TODO(b/144542975): Do this on upgrade instead.
if (!demo_session_apps_path.empty()) {
disk_image = request.add_disks();
......
......@@ -22,7 +22,7 @@ namespace {
constexpr const char kArcVmConfigJsonPath[] = "/usr/share/arcvm/config.json";
constexpr const char kBuiltinPath[] = "/opt/google/vms/android";
constexpr const char kFstab[] = "fstab";
constexpr const char kFstabPath[] = "/run/arcvm/host_generated/fstab";
constexpr const char kKernel[] = "vmlinux";
constexpr const char kRootFs[] = "system.raw.img";
constexpr const char kVendorImage[] = "vendor.raw.img";
......@@ -41,7 +41,7 @@ FileSystemStatus::FileSystemStatus()
system_image_path_(base::FilePath(kBuiltinPath).Append(kRootFs)),
vendor_image_path_(base::FilePath(kBuiltinPath).Append(kVendorImage)),
guest_kernel_path_(base::FilePath(kBuiltinPath).Append(kKernel)),
fstab_path_(base::FilePath(kBuiltinPath).Append(kFstab)),
fstab_path_(kFstabPath),
is_system_image_ext_format_(IsSystemImageExtFormat(system_image_path_)) {}
// static
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment