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
diff --git a/init/block_dev_initializer.cpp b/init/block_dev_initializer.cpp
index 4ca5b8f..7f83037 100644
--- a/init/block_dev_initializer.cpp
+++ b/init/block_dev_initializer.cpp
@@ -140,11 +140,43 @@
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<std::string> devices) {
auto uevent_callback = [&, this](const Uevent& uevent) -> ListenerAction {
return HandleUevent(uevent, &devices);
diff --git a/init/devices.cpp b/init/devices.cpp
index 0b1e13d..4de1e20 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -224,6 +224,17 @@
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<std::pair<std::string, std::string>> partition_map;
diff --git a/init/devices.h b/init/devices.h
index cac52bc..8b6cf6c 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -141,6 +141,8 @@
// `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;