From eb3d280f1ee89dd42bffaf1ec6fd200f9f8d7b3e Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Tue, 5 Nov 2024 09:15:25 -0800 Subject: [PATCH] init: Look for partition only on a boot device if using boot_part_uuid The current code waits for boot partitions to show up by waiting to see a uevent with the right partition name. However, nothing in the waiting code validates that the partition that showed up is actually on the boot device. That means that the current code can be confused if there is another block device in the system (possibly connected via USB) that has a partition name matching one of the system ones. It can be noted that the problem is specifically just that the "waiting" part returns too early. Later parts of the system, specifically the parts of the system that create the "/dev/block/by-name" symlinks, do properly look at the list of "boot devices". This means that the problem we're fixing is that later code, which assumes that the boot partitions have already initialized, can fail to find an initialized partition. To make it concrete, imagine that you have two block devices in your system: the builtin emmc and an external USB disk. Let's say you're booting over USB and "boot_devices" properly lists only USB. Both the "emmc" and "USB" block devices are properly formatted Android disks and have the full slew of partitions. At boot time, you can see: 1. We get to the point where we need to wait for the "boot" source (USB) to show up. 2. We see the eMMC show up. 3. The eMMC has all the needed partitions, so we consider our wait done. ...but eMMC isn't in the list of "boot devices" so we don't create the "/dev/block/by-name" symlinks. 4. Later code assumes that the "/dev/block/by-name" symlinks are already setup and fails. 5. The device fails to boot. Fix it so that the wait makes sure that the partitions are on the boot device. Unfortunately, it appears that in some cases products (especially emulators) aren't setting the "boot devices" and/or are not making sure all boot partitions are on the same device. Limit the fix to only devices using the new "boot_part_uuid" to make sure we don't break old code. NOTE: this is effectively the same change as a previous one ("init: Look for super partition only on a boot device") but with the added fix to only enable the check when using "boot_part_uuid". Bug: 309244873 Bug: 349144493 Bug: 316324155 Test: Boot isn't confused when two boot devices are present Change-Id: Iaae453ed661307f485cdf4dde86294105cae9b2d --- init/block_dev_initializer.cpp | 34 +++++++++++++++++++++++++++++++++- init/devices.cpp | 11 +++++++++++ init/devices.h | 2 ++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp index 4ca5b8f0e..7f83037ae 100644 --- a/init/block_dev_initializer.cpp +++ b/init/block_dev_initializer.cpp @@ -140,11 +140,43 @@ ListenerAction BlockDevInitializer::HandleUevent(const Uevent& uevent, LOG(VERBOSE) << __PRETTY_FUNCTION__ << ": found partition: " << name; - devices->erase(iter); + // Remove the partition from the list of partitions we're waiting for. + // + // Partitions that we're waiting for here are expected to be on the boot + // device, so only remove from the list if they're on the boot device. + // This prevents us from being confused if there are multiple disks (some + // perhaps connected via USB) that have matching partition names. + // + // ...but... + // + // Some products (especialy emulators) don't seem to set up boot_devices + // or possibly not all the partitions that we need to wait for are on the + // specified boot device. Thus, only require partitions to be on the boot + // device in "strict" mode, which should be used on newer systems. + if (device_handler_->IsBootDevice(uevent) || !device_handler_->IsBootDeviceStrict()) { + devices->erase(iter); + } + device_handler_->HandleUevent(uevent); return devices->empty() ? ListenerAction::kStop : ListenerAction::kContinue; } +// Wait for partitions that are expected to be on the "boot device" to initialize. +// +// Wait (for up to 10 seconds) for partitions passed in `devices` to show up. +// All block devices found while waiting will be initialized, which includes +// creating symlinks for them in /dev/block. Once all `devices` are found we'll +// return success (true). If any devices aren't found we'll return failure +// (false). As devices are found they will be removed from `devices`. +// +// The contents of `devices` is the names of the partitions. This can be: +// - The `partition_name` reported by a uevent, or the final component in the +// `path` reported by a uevent if the `partition_name` is blank. +// - The result of DeviceHandler::GetPartitionNameForDevice() on the +// `device_name` reported by a uevent. +// +// NOTE: on newer systems partitions _must_ be on the "boot device". See +// comments inside HandleUevent(). bool BlockDevInitializer::InitDevices(std::set devices) { auto uevent_callback = [&, this](const Uevent& uevent) -> ListenerAction { return HandleUevent(uevent, &devices); diff --git a/init/devices.cpp b/init/devices.cpp index 0b1e13dba..4de1e2030 100644 --- a/init/devices.cpp +++ b/init/devices.cpp @@ -224,6 +224,17 @@ BlockDeviceInfo DeviceHandler::GetBlockDeviceInfo(const std::string& uevent_path return info; } +bool DeviceHandler::IsBootDeviceStrict() const { + // When using the newer "boot_part_uuid" to specify the boot device then + // we require all core system partitions to be on the boot device. + return !boot_part_uuid_.empty(); +} + +bool DeviceHandler::IsBootDevice(const Uevent& uevent) const { + auto device = GetBlockDeviceInfo(uevent.path); + return device.is_boot_device; +} + std::string DeviceHandler::GetPartitionNameForDevice(const std::string& query_device) { static const auto partition_map = [] { std::vector> partition_map; diff --git a/init/devices.h b/init/devices.h index cac52bc17..8b6cf6cb1 100644 --- a/init/devices.h +++ b/init/devices.h @@ -141,6 +141,8 @@ class DeviceHandler : public UeventHandler { // `androidboot.partition_map=vdb,metadata;vdc,userdata` maps `vdb` to `metadata` and `vdc` to // `userdata`. static std::string GetPartitionNameForDevice(const std::string& device); + bool IsBootDeviceStrict() const; + bool IsBootDevice(const Uevent& uevent) const; private: void ColdbootDone() override;