Merge changes I99f292a0,I6a0f13d5,I2bf3bebf into rvc-dev

* changes:
  init: Forcefully mount system_ext on DAP devices if not present in fstab.
  init: Factor out first-stage uevent handling into a separate class.
  init: Remove special-case hacks for the super partition.
This commit is contained in:
David Anderson 2020-03-27 04:43:34 +00:00 committed by Android (Google) Code Review
commit 3a5e138964
6 changed files with 297 additions and 153 deletions

View file

@ -36,6 +36,7 @@ init_common_sources = [
"util.cpp",
]
init_device_sources = [
"block_dev_initializer.cpp",
"bootchart.cpp",
"builtins.cpp",
"devices.cpp",

View file

@ -48,6 +48,7 @@ ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
include $(CLEAR_VARS)
LOCAL_CPPFLAGS := $(init_cflags)
LOCAL_SRC_FILES := \
block_dev_initializer.cpp \
devices.cpp \
first_stage_init.cpp \
first_stage_main.cpp \

View file

@ -0,0 +1,148 @@
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <chrono>
#include <string_view>
#include <vector>
#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <fs_mgr.h>
#include "block_dev_initializer.h"
namespace android {
namespace init {
using android::base::Timer;
using namespace std::chrono_literals;
BlockDevInitializer::BlockDevInitializer() : uevent_listener_(16 * 1024 * 1024) {
auto boot_devices = android::fs_mgr::GetBootDevices();
device_handler_ = std::make_unique<DeviceHandler>(
std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
std::move(boot_devices), false);
}
bool BlockDevInitializer::InitDeviceMapper() {
const std::string dm_path = "/devices/virtual/misc/device-mapper";
bool found = false;
auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
if (uevent.path == dm_path) {
device_handler_->HandleUevent(uevent);
found = true;
return ListenerAction::kStop;
}
return ListenerAction::kContinue;
};
uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
if (!found) {
LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
Timer t;
uevent_listener_.Poll(dm_callback, 10s);
LOG(INFO) << "Wait for device-mapper returned after " << t;
}
if (!found) {
LOG(ERROR) << "device-mapper device not found after polling timeout";
return false;
}
return true;
}
ListenerAction BlockDevInitializer::HandleUevent(const Uevent& uevent,
std::set<std::string>* devices) {
// Ignore everything that is not a block device.
if (uevent.subsystem != "block") {
return ListenerAction::kContinue;
}
auto name = uevent.partition_name;
if (name.empty()) {
size_t base_idx = uevent.path.rfind('/');
if (base_idx == std::string::npos) {
return ListenerAction::kContinue;
}
name = uevent.path.substr(base_idx + 1);
}
auto iter = devices->find(name);
if (iter == devices->end()) {
return ListenerAction::kContinue;
}
LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << name;
devices->erase(iter);
device_handler_->HandleUevent(uevent);
return devices->empty() ? ListenerAction::kStop : ListenerAction::kContinue;
}
bool BlockDevInitializer::InitDevices(std::set<std::string> devices) {
auto uevent_callback = [&, this](const Uevent& uevent) -> ListenerAction {
return HandleUevent(uevent, &devices);
};
uevent_listener_.RegenerateUevents(uevent_callback);
// UeventCallback() will remove found partitions from |devices|. So if it
// isn't empty here, it means some partitions are not found.
if (!devices.empty()) {
LOG(INFO) << __PRETTY_FUNCTION__
<< ": partition(s) not found in /sys, waiting for their uevent(s): "
<< android::base::Join(devices, ", ");
Timer t;
uevent_listener_.Poll(uevent_callback, 10s);
LOG(INFO) << "Wait for partitions returned after " << t;
}
if (!devices.empty()) {
LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
<< android::base::Join(devices, ", ");
return false;
}
return true;
}
// Creates "/dev/block/dm-XX" for dm nodes by running coldboot on /sys/block/dm-XX.
bool BlockDevInitializer::InitDmDevice(const std::string& device) {
const std::string device_name(basename(device.c_str()));
const std::string syspath = "/sys/block/" + device_name;
bool found = false;
auto uevent_callback = [&device_name, &device, this, &found](const Uevent& uevent) {
if (uevent.device_name == device_name) {
LOG(VERBOSE) << "Creating device-mapper device : " << device;
device_handler_->HandleUevent(uevent);
found = true;
return ListenerAction::kStop;
}
return ListenerAction::kContinue;
};
uevent_listener_.RegenerateUeventsForPath(syspath, uevent_callback);
if (!found) {
LOG(INFO) << "dm device '" << device << "' not found in /sys, waiting for its uevent";
Timer t;
uevent_listener_.Poll(uevent_callback, 10s);
LOG(INFO) << "wait for dm device '" << device << "' returned after " << t;
}
if (!found) {
LOG(ERROR) << "dm device '" << device << "' not found after polling timeout";
return false;
}
return true;
}
} // namespace init
} // namespace android

View file

@ -0,0 +1,41 @@
// Copyright (C) 2020 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <set>
#include <string>
#include "devices.h"
#include "uevent_listener.h"
namespace android {
namespace init {
class BlockDevInitializer final {
public:
BlockDevInitializer();
bool InitDeviceMapper();
bool InitDevices(std::set<std::string> devices);
bool InitDmDevice(const std::string& device);
private:
ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices);
std::unique_ptr<DeviceHandler> device_handler_;
UeventListener uevent_listener_;
};
} // namespace init
} // namespace android

View file

@ -42,6 +42,7 @@
#include <liblp/liblp.h>
#include <libsnapshot/snapshot.h>
#include "block_dev_initializer.h"
#include "devices.h"
#include "switch_root.h"
#include "uevent.h"
@ -84,11 +85,7 @@ class FirstStageMount {
bool InitDevices();
protected:
ListenerAction HandleBlockDevice(const std::string& name, const Uevent&,
std::set<std::string>* required_devices);
bool InitRequiredDevices(std::set<std::string> devices);
bool InitMappedDevice(const std::string& verity_device);
bool InitDeviceMapper();
bool CreateLogicalPartitions();
bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
Fstab::iterator* end = nullptr);
@ -97,7 +94,7 @@ class FirstStageMount {
bool TrySwitchSystemAsRoot();
bool TrySkipMountingPartitions();
bool IsDmLinearEnabled();
void GetDmLinearMetadataDevice(std::set<std::string>* devices);
void GetSuperDeviceName(std::set<std::string>* devices);
bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);
void UseDsuIfPresent();
// Reads all fstab.avb_keys from the ramdisk for first-stage mount.
@ -106,8 +103,6 @@ class FirstStageMount {
// revocation check by DSU installation service.
void CopyDsuAvbKeys();
ListenerAction UeventCallback(const Uevent& uevent, std::set<std::string>* required_devices);
// Pure virtual functions.
virtual bool GetDmVerityDevices(std::set<std::string>* devices) = 0;
virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0;
@ -116,10 +111,10 @@ class FirstStageMount {
bool dsu_not_on_userdata_ = false;
Fstab fstab_;
std::string lp_metadata_partition_;
// The super path is only set after InitDevices, and is invalid before.
std::string super_path_;
std::string super_partition_name_;
std::unique_ptr<DeviceHandler> device_handler_;
UeventListener uevent_listener_;
BlockDevInitializer block_dev_init_;
// Reads all AVB keys before chroot into /system, as they might be used
// later when mounting other partitions, e.g., /vendor and /product.
std::map<std::string, std::vector<std::string>> preload_avb_key_blobs_;
@ -233,13 +228,7 @@ static bool IsStandaloneImageRollback(const AvbHandle& builtin_vbmeta,
// Class Definitions
// -----------------
FirstStageMount::FirstStageMount(Fstab fstab)
: need_dm_verity_(false), fstab_(std::move(fstab)), uevent_listener_(16 * 1024 * 1024) {
auto boot_devices = android::fs_mgr::GetBootDevices();
device_handler_ = std::make_unique<DeviceHandler>(
std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},
std::move(boot_devices), false);
FirstStageMount::FirstStageMount(Fstab fstab) : need_dm_verity_(false), fstab_(std::move(fstab)) {
super_partition_name_ = fs_mgr_get_super_partition_name();
}
@ -268,12 +257,23 @@ bool FirstStageMount::DoFirstStageMount() {
bool FirstStageMount::InitDevices() {
std::set<std::string> devices;
GetDmLinearMetadataDevice(&devices);
GetSuperDeviceName(&devices);
if (!GetDmVerityDevices(&devices)) {
return false;
}
return InitRequiredDevices(std::move(devices));
if (!InitRequiredDevices(std::move(devices))) {
return false;
}
if (IsDmLinearEnabled()) {
auto super_symlink = "/dev/block/by-name/"s + super_partition_name_;
if (!android::base::Realpath(super_symlink, &super_path_)) {
PLOG(ERROR) << "realpath failed: " << super_symlink;
return false;
}
}
return true;
}
bool FirstStageMount::IsDmLinearEnabled() {
@ -283,7 +283,7 @@ bool FirstStageMount::IsDmLinearEnabled() {
return false;
}
void FirstStageMount::GetDmLinearMetadataDevice(std::set<std::string>* devices) {
void FirstStageMount::GetSuperDeviceName(std::set<std::string>* devices) {
// Add any additional devices required for dm-linear mappings.
if (!IsDmLinearEnabled()) {
return;
@ -296,62 +296,13 @@ void FirstStageMount::GetDmLinearMetadataDevice(std::set<std::string>* devices)
// Found partitions will then be removed from it for the subsequent member
// function to check which devices are NOT created.
bool FirstStageMount::InitRequiredDevices(std::set<std::string> devices) {
if (!InitDeviceMapper()) {
if (!block_dev_init_.InitDeviceMapper()) {
return false;
}
if (devices.empty()) {
return true;
}
auto uevent_callback = [&, this](const Uevent& uevent) {
return UeventCallback(uevent, &devices);
};
uevent_listener_.RegenerateUevents(uevent_callback);
// UeventCallback() will remove found partitions from |devices|. So if it
// isn't empty here, it means some partitions are not found.
if (!devices.empty()) {
LOG(INFO) << __PRETTY_FUNCTION__
<< ": partition(s) not found in /sys, waiting for their uevent(s): "
<< android::base::Join(devices, ", ");
Timer t;
uevent_listener_.Poll(uevent_callback, 10s);
LOG(INFO) << "Wait for partitions returned after " << t;
}
if (!devices.empty()) {
LOG(ERROR) << __PRETTY_FUNCTION__ << ": partition(s) not found after polling timeout: "
<< android::base::Join(devices, ", ");
return false;
}
return true;
}
bool FirstStageMount::InitDeviceMapper() {
const std::string dm_path = "/devices/virtual/misc/device-mapper";
bool found = false;
auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {
if (uevent.path == dm_path) {
device_handler_->HandleUevent(uevent);
found = true;
return ListenerAction::kStop;
}
return ListenerAction::kContinue;
};
uevent_listener_.RegenerateUeventsForPath("/sys" + dm_path, dm_callback);
if (!found) {
LOG(INFO) << "device-mapper device not found in /sys, waiting for its uevent";
Timer t;
uevent_listener_.Poll(dm_callback, 10s);
LOG(INFO) << "Wait for device-mapper returned after " << t;
}
if (!found) {
LOG(ERROR) << "device-mapper device not found after polling timeout";
return false;
}
return true;
return block_dev_init_.InitDevices(std::move(devices));
}
bool FirstStageMount::InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata) {
@ -375,7 +326,7 @@ bool FirstStageMount::CreateLogicalPartitions() {
if (!IsDmLinearEnabled()) {
return true;
}
if (lp_metadata_partition_.empty()) {
if (super_path_.empty()) {
LOG(ERROR) << "Could not locate logical partition tables in partition "
<< super_partition_name_;
return false;
@ -392,92 +343,19 @@ bool FirstStageMount::CreateLogicalPartitions() {
if (!InitRequiredDevices({"userdata"})) {
return false;
}
return sm->CreateLogicalAndSnapshotPartitions(lp_metadata_partition_);
return sm->CreateLogicalAndSnapshotPartitions(super_path_);
}
}
auto metadata = android::fs_mgr::ReadCurrentMetadata(lp_metadata_partition_);
auto metadata = android::fs_mgr::ReadCurrentMetadata(super_path_);
if (!metadata) {
LOG(ERROR) << "Could not read logical partition metadata from " << lp_metadata_partition_;
LOG(ERROR) << "Could not read logical partition metadata from " << super_path_;
return false;
}
if (!InitDmLinearBackingDevices(*metadata.get())) {
return false;
}
return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), lp_metadata_partition_);
}
ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent,
std::set<std::string>* required_devices) {
// Matches partition name to create device nodes.
// Both required_devices_partition_names_ and uevent->partition_name have A/B
// suffix when A/B is used.
auto iter = required_devices->find(name);
if (iter != required_devices->end()) {
LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << *iter;
if (IsDmLinearEnabled() && name == super_partition_name_) {
std::vector<std::string> links = device_handler_->GetBlockDeviceSymlinks(uevent);
lp_metadata_partition_ = links[0];
}
required_devices->erase(iter);
device_handler_->HandleUevent(uevent);
if (required_devices->empty()) {
return ListenerAction::kStop;
} else {
return ListenerAction::kContinue;
}
}
return ListenerAction::kContinue;
}
ListenerAction FirstStageMount::UeventCallback(const Uevent& uevent,
std::set<std::string>* required_devices) {
// Ignores everything that is not a block device.
if (uevent.subsystem != "block") {
return ListenerAction::kContinue;
}
if (!uevent.partition_name.empty()) {
return HandleBlockDevice(uevent.partition_name, uevent, required_devices);
} else {
size_t base_idx = uevent.path.rfind('/');
if (base_idx != std::string::npos) {
return HandleBlockDevice(uevent.path.substr(base_idx + 1), uevent, required_devices);
}
}
// Not found a partition or find an unneeded partition, continue to find others.
return ListenerAction::kContinue;
}
// Creates "/dev/block/dm-XX" for dm-verity by running coldboot on /sys/block/dm-XX.
bool FirstStageMount::InitMappedDevice(const std::string& dm_device) {
const std::string device_name(basename(dm_device.c_str()));
const std::string syspath = "/sys/block/" + device_name;
bool found = false;
auto verity_callback = [&device_name, &dm_device, this, &found](const Uevent& uevent) {
if (uevent.device_name == device_name) {
LOG(VERBOSE) << "Creating device-mapper device : " << dm_device;
device_handler_->HandleUevent(uevent);
found = true;
return ListenerAction::kStop;
}
return ListenerAction::kContinue;
};
uevent_listener_.RegenerateUeventsForPath(syspath, verity_callback);
if (!found) {
LOG(INFO) << "dm device '" << dm_device << "' not found in /sys, waiting for its uevent";
Timer t;
uevent_listener_.Poll(verity_callback, 10s);
LOG(INFO) << "wait for dm device '" << dm_device << "' returned after " << t;
}
if (!found) {
LOG(ERROR) << "dm device '" << dm_device << "' not found after polling timeout";
return false;
}
return true;
return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_);
}
bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
@ -491,7 +369,7 @@ bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_sa
if (!fs_mgr_update_logical_partition(&(*begin))) {
return false;
}
if (!InitMappedDevice(begin->blk_device)) {
if (!block_dev_init_.InitDmDevice(begin->blk_device)) {
return false;
}
}
@ -658,7 +536,9 @@ bool FirstStageMount::MountPartitions() {
auto init_devices = [this](std::set<std::string> devices) -> bool {
for (auto iter = devices.begin(); iter != devices.end();) {
if (android::base::StartsWith(*iter, "/dev/block/dm-")) {
if (!InitMappedDevice(*iter)) return false;
if (!block_dev_init_.InitDmDevice(*iter)) {
return false;
}
iter = devices.erase(iter);
} else {
iter++;
@ -774,7 +654,7 @@ bool FirstStageMountVBootV1::SetUpDmVerity(FstabEntry* fstab_entry) {
// The exact block device name (fstab_rec->blk_device) is changed to
// "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
// first stage.
return InitMappedDevice(fstab_entry->blk_device);
return block_dev_init_.InitDmDevice(fstab_entry->blk_device);
default:
return false;
}
@ -894,7 +774,7 @@ bool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) {
// The exact block device name (fstab_rec->blk_device) is changed to
// "/dev/block/dm-XX". Needs to create it because ueventd isn't started in init
// first stage.
return InitMappedDevice(fstab_entry->blk_device);
return block_dev_init_.InitDmDevice(fstab_entry->blk_device);
default:
return false;
}

View file

@ -63,12 +63,15 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr.h>
#include <libgsi/libgsi.h>
#include <libsnapshot/snapshot.h>
#include <selinux/android.h>
#include "block_dev_initializer.h"
#include "debug_ramdisk.h"
#include "reboot_utils.h"
#include "util.h"
@ -598,6 +601,74 @@ int SelinuxGetVendorAndroidVersion() {
return vendor_android_version;
}
// This is for R system.img/system_ext.img to work on old vendor.img as system_ext.img
// is introduced in R. We mount system_ext in second stage init because the first-stage
// init in boot.img won't be updated in the system-only OTA scenario.
void MountMissingSystemPartitions() {
android::fs_mgr::Fstab fstab;
if (!ReadDefaultFstab(&fstab)) {
LOG(ERROR) << "Could not read default fstab";
}
android::fs_mgr::Fstab mounts;
if (!ReadFstabFromFile("/proc/mounts", &mounts)) {
LOG(ERROR) << "Could not read /proc/mounts";
}
static const std::vector<std::string> kPartitionNames = {"system_ext", "product"};
android::fs_mgr::Fstab extra_fstab;
for (const auto& name : kPartitionNames) {
if (GetEntryForMountPoint(&mounts, "/"s + name)) {
// The partition is already mounted.
continue;
}
auto system_entry = GetEntryForMountPoint(&fstab, "/system");
if (!system_entry) {
LOG(ERROR) << "Could not find mount entry for /system";
break;
}
if (!system_entry->fs_mgr_flags.logical) {
LOG(INFO) << "Skipping mount of " << name << ", system is not dynamic.";
break;
}
auto entry = *system_entry;
auto partition_name = name + fs_mgr_get_slot_suffix();
auto replace_name = "system"s + fs_mgr_get_slot_suffix();
entry.mount_point = "/"s + name;
entry.blk_device =
android::base::StringReplace(entry.blk_device, replace_name, partition_name, false);
if (!fs_mgr_update_logical_partition(&entry)) {
LOG(ERROR) << "Could not update logical partition";
continue;
}
extra_fstab.emplace_back(std::move(entry));
}
SkipMountingPartitions(&extra_fstab);
if (extra_fstab.empty()) {
return;
}
BlockDevInitializer block_dev_init;
for (auto& entry : extra_fstab) {
if (access(entry.blk_device.c_str(), F_OK) != 0) {
auto block_dev = android::base::Basename(entry.blk_device);
if (!block_dev_init.InitDmDevice(block_dev)) {
LOG(ERROR) << "Failed to find device-mapper node: " << block_dev;
continue;
}
}
if (fs_mgr_do_mount_one(entry)) {
LOG(ERROR) << "Could not mount " << entry.mount_point;
}
}
}
int SetupSelinux(char** argv) {
SetStdioToDevNull(argv);
InitKernelLogging(argv);
@ -608,6 +679,8 @@ int SetupSelinux(char** argv) {
boot_clock::time_point start_time = boot_clock::now();
MountMissingSystemPartitions();
// Set up SELinux, loading the SELinux policy.
SelinuxSetupKernelLogging();
SelinuxInitialize();