fastbootd: Add support for flashing logical partitions.

When flashing logical partitions, we read the "super" partition metadata
corresponding to the current slot. We then temporarily create a
device-mapper device for that partition, and immediately destroy the
device after all operations are complete. We do not mount partitions
ahead of time, or keep them mounted, because a fastboot operation may
change the layout of the logical partition table (or change which slot
is current).

Bug: 78793464
Test: fastboot flash a logical partition under "super"
Change-Id: Id2a61d3592decabeebfd283c4fd6e6cbe576a18c
diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp
index 73cf1bf..ec84576 100644
--- a/fastboot/device/utility.cpp
+++ b/fastboot/device/utility.cpp
@@ -17,9 +17,12 @@
 #include "utility.h"
 
 #include <android-base/logging.h>
+#include <fs_mgr_dm_linear.h>
+#include <liblp/liblp.h>
 
 #include "fastboot_device.h"
 
+using namespace android::fs_mgr;
 using android::base::unique_fd;
 using android::hardware::boot::V1_0::Slot;
 
@@ -32,8 +35,31 @@
     return true;
 }
 
-bool OpenPartition(FastbootDevice* /* device */, const std::string& name, PartitionHandle* handle) {
-    if (!OpenPhysicalPartition(name, handle)) {
+static bool OpenLogicalPartition(const std::string& name, const std::string& slot,
+                                 PartitionHandle* handle) {
+    std::optional<std::string> path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+    if (!path) {
+        return false;
+    }
+    uint32_t slot_number = SlotNumberForSlotSuffix(slot);
+    std::string dm_path;
+    if (!CreateLogicalPartition(path->c_str(), slot_number, name, true, &dm_path)) {
+        LOG(ERROR) << "Could not map partition: " << name;
+        return false;
+    }
+    auto closer = [name]() -> void { DestroyLogicalPartition(name); };
+    *handle = PartitionHandle(dm_path, std::move(closer));
+    return true;
+}
+
+bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle) {
+    // We prioritize logical partitions over physical ones, and do this
+    // consistently for other partition operations (like getvar:partition-size).
+    if (LogicalPartitionExists(name, device->GetCurrentSlot())) {
+        if (!OpenLogicalPartition(name, device->GetCurrentSlot(), handle)) {
+            return false;
+        }
+    } else if (!OpenPhysicalPartition(name, handle)) {
         LOG(ERROR) << "No such partition: " << name;
         return false;
     }
@@ -55,6 +81,38 @@
     return path;
 }
 
+static const LpMetadataPartition* FindLogicalPartition(const LpMetadata& metadata,
+                                                       const std::string& name) {
+    for (const auto& partition : metadata.partitions) {
+        if (GetPartitionName(partition) == name) {
+            return &partition;
+        }
+    }
+    return nullptr;
+}
+
+bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
+                            bool* is_zero_length) {
+    auto path = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+    if (!path) {
+        return false;
+    }
+
+    uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+    std::unique_ptr<LpMetadata> metadata = ReadMetadata(path->c_str(), slot_number);
+    if (!metadata) {
+        return false;
+    }
+    const LpMetadataPartition* partition = FindLogicalPartition(*metadata.get(), name);
+    if (!partition) {
+        return false;
+    }
+    if (is_zero_length) {
+        *is_zero_length = (partition->num_extents == 0);
+    }
+    return true;
+}
+
 bool GetSlotNumber(const std::string& slot, Slot* number) {
     if (slot.size() != 1) {
         return false;