From 4117559746b4d6877338264c7a75d8262899957d Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 20 Mar 2020 19:38:28 -0700 Subject: [PATCH] init: Factor out first-stage uevent handling into a separate class. This will make it possible to re-use in second-stage init. Bug: 150315914 Test: first-stage init boots Change-Id: I6a0f13d5c71ab5529a76751f68ac0f15834323d1 --- init/Android.bp | 1 + init/Android.mk | 1 + init/block_dev_initializer.cpp | 148 ++++++++++++++++++++++++++++++++ init/block_dev_initializer.h | 41 +++++++++ init/first_stage_mount.cpp | 150 +++------------------------------ 5 files changed, 202 insertions(+), 139 deletions(-) create mode 100644 init/block_dev_initializer.cpp create mode 100644 init/block_dev_initializer.h diff --git a/init/Android.bp b/init/Android.bp index 72a7bfed4..17e4adf04 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -36,6 +36,7 @@ init_common_sources = [ "util.cpp", ] init_device_sources = [ + "block_dev_initializer.cpp", "bootchart.cpp", "builtins.cpp", "devices.cpp", diff --git a/init/Android.mk b/init/Android.mk index 07b0f950f..e647d2b1e 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -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 \ diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp new file mode 100644 index 000000000..b423f864e --- /dev/null +++ b/init/block_dev_initializer.cpp @@ -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 +#include +#include + +#include +#include +#include +#include + +#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( + std::vector{}, std::vector{}, std::vector{}, + 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* 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 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 diff --git a/init/block_dev_initializer.h b/init/block_dev_initializer.h new file mode 100644 index 000000000..0d4c6e9fc --- /dev/null +++ b/init/block_dev_initializer.h @@ -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 +#include +#include + +#include "devices.h" +#include "uevent_listener.h" + +namespace android { +namespace init { + +class BlockDevInitializer final { + public: + BlockDevInitializer(); + + bool InitDeviceMapper(); + bool InitDevices(std::set devices); + bool InitDmDevice(const std::string& device); + + private: + ListenerAction HandleUevent(const Uevent& uevent, std::set* devices); + + std::unique_ptr device_handler_; + UeventListener uevent_listener_; +}; + +} // namespace init +} // namespace android diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp index 3451264ab..8eb2f9748 100644 --- a/init/first_stage_mount.cpp +++ b/init/first_stage_mount.cpp @@ -42,6 +42,7 @@ #include #include +#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* required_devices); bool InitRequiredDevices(std::set 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); @@ -106,8 +103,6 @@ class FirstStageMount { // revocation check by DSU installation service. void CopyDsuAvbKeys(); - ListenerAction UeventCallback(const Uevent& uevent, std::set* required_devices); - // Pure virtual functions. virtual bool GetDmVerityDevices(std::set* devices) = 0; virtual bool SetUpDmVerity(FstabEntry* fstab_entry) = 0; @@ -119,8 +114,7 @@ class FirstStageMount { // The super path is only set after InitDevices, and is invalid before. std::string super_path_; std::string super_partition_name_; - std::unique_ptr 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> preload_avb_key_blobs_; @@ -234,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( - std::vector{}, std::vector{}, std::vector{}, - 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(); } @@ -308,62 +296,13 @@ void FirstStageMount::GetSuperDeviceName(std::set* 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 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) { @@ -419,75 +358,6 @@ bool FirstStageMount::CreateLogicalPartitions() { return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_); } -ListenerAction FirstStageMount::HandleBlockDevice(const std::string& name, const Uevent& uevent, - std::set* 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; - 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* 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; -} - bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts, Fstab::iterator* end) { // Sets end to begin + 1, so we can just return on failure below. @@ -499,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; } } @@ -666,7 +536,9 @@ bool FirstStageMount::MountPartitions() { auto init_devices = [this](std::set 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++; @@ -782,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; } @@ -902,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; }