Merge "fastbootd: Add an update-super command to sync the super partition."
diff --git a/fastboot/README.md b/fastboot/README.md
index ec7dcb4..15b5965 100644
--- a/fastboot/README.md
+++ b/fastboot/README.md
@@ -186,10 +186,45 @@
                         bootloader requiring a signature before
                         it will install or boot images.
 
+    is-userspace        If the value is "yes", the device is running
+                        fastbootd. Otherwise, it is running fastboot
+                        in the bootloader.
+
 Names starting with a lowercase character are reserved by this
 specification.  OEM-specific names should not start with lowercase
 characters.
 
+## Logical Partitions
+
+There are a number of commands to interact with logical partitions:
+
+    update-super:%s:%s  Write the previously downloaded image to a super
+                        partition. Unlike the "flash" command, this has
+                        special rules. The image must have been created by
+                        the lpmake command, and must not be a sparse image.
+                        If the last argument is "wipe", then all existing
+                        logical partitions are deleted. If no final argument
+                        is specified, the partition tables are merged. Any
+                        partition in the new image that does not exist in the
+                        old image is created with a zero size.
+
+                        In all cases, this will cause the temporary "scratch"
+                        partition to be deleted if it exists.
+
+    create-logical-partition:%s:%d
+                        Create a logical partition with the given name and
+                        size, in the super partition.
+
+    delete-logical-partition:%s
+                        Delete a logical partition with the given name.
+
+    resize-logical-partition:%s:%d
+                        Change the size of the named logical partition.
+
+In addition, there is a variable to test whether a partition is logical:
+
+    is-logical:%s       If the value is "yes", the partition is logical.
+                        Otherwise the partition is physical.
 
 ## TCP Protocol v1
 
diff --git a/fastboot/constants.h b/fastboot/constants.h
index 43daab0..6542b83 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -33,6 +33,7 @@
 #define FB_CMD_CREATE_PARTITION "create-logical-partition"
 #define FB_CMD_DELETE_PARTITION "delete-logical-partition"
 #define FB_CMD_RESIZE_PARTITION "resize-logical-partition"
+#define FB_CMD_UPDATE_SUPER "update-super"
 
 #define RESPONSE_OKAY "OKAY"
 #define RESPONSE_FAIL "FAIL"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 690bfa8..1e853bf 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -336,3 +336,11 @@
     }
     return device->WriteOkay("Partition resized");
 }
+
+bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+    if (args.size() < 2) {
+        return device->WriteFail("Invalid arguments");
+    }
+    bool wipe = (args.size() >= 3 && args[2] == "wipe");
+    return UpdateSuper(device, args[1], wipe);
+}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index f67df91..4778d23 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -44,3 +44,4 @@
 bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
 bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);
+bool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index c306e67..6ed6d32 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -46,6 +46,7 @@
               {FB_CMD_CREATE_PARTITION, CreatePartitionHandler},
               {FB_CMD_DELETE_PARTITION, DeletePartitionHandler},
               {FB_CMD_RESIZE_PARTITION, ResizePartitionHandler},
+              {FB_CMD_UPDATE_SUPER, UpdateSuperHandler},
       }),
       transport_(std::make_unique<ClientUsbTransport>()),
       boot_control_hal_(IBootControl::getService()) {}
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index b5ed170..e3efbcb 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -25,6 +25,8 @@
 #include <android-base/logging.h>
 #include <android-base/strings.h>
 #include <ext4_utils/ext4_utils.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
 #include <sparse/sparse.h>
 
 #include "fastboot_device.h"
@@ -36,6 +38,8 @@
 
 }  // namespace
 
+using namespace android::fs_mgr;
+
 int FlashRawDataChunk(int fd, const char* data, size_t len) {
     size_t ret = 0;
     while (ret < len) {
@@ -99,3 +103,74 @@
     }
     return FlashBlockDevice(handle.fd(), data);
 }
+
+bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe) {
+    std::optional<std::string> super = FindPhysicalPartition(partition_name);
+    if (!super) {
+        return device->WriteFail("Could not find partition: " + partition_name);
+    }
+
+    std::vector<char> data = std::move(device->download_data());
+    if (data.empty()) {
+        return device->WriteFail("No data available");
+    }
+
+    std::unique_ptr<LpMetadata> new_metadata = ReadFromImageBlob(data.data(), data.size());
+    if (!new_metadata) {
+        return device->WriteFail("Data is not a valid logical partition metadata image");
+    }
+
+    // If we are unable to read the existing metadata, then the super partition
+    // is corrupt. In this case we reflash the whole thing using the provided
+    // image.
+    std::string slot_suffix = device->GetCurrentSlot();
+    uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
+    std::unique_ptr<LpMetadata> metadata = ReadMetadata(super->c_str(), slot_number);
+    if (!metadata || wipe) {
+        if (!FlashPartitionTable(super.value(), *new_metadata.get())) {
+            return device->WriteFail("Unable to flash new partition table");
+        }
+        return device->WriteOkay("Successfully flashed partition table");
+    }
+
+    // There's a working super partition, and we don't want to wipe it - it may
+    // may contain partitions created for the user. Instead, we create a zero-
+    // sized partition for each entry in the new partition table. It is then
+    // the host's responsibility to size it correctly via resize-logical-partition.
+    std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(*metadata.get());
+    if (!builder) {
+        return device->WriteFail("Unable to create a metadata builder");
+    }
+    for (const auto& partition : new_metadata->partitions) {
+        std::string name = GetPartitionName(partition);
+        if (builder->FindPartition(name)) {
+            continue;
+        }
+        std::string guid = GetPartitionGuid(partition);
+        if (!builder->AddPartition(name, guid, partition.attributes)) {
+            return device->WriteFail("Unable to add partition: " + name);
+        }
+    }
+
+    // The scratch partition may exist as temporary storage, created for
+    // use by adb remount for overlayfs. If we're performing a flashall
+    // operation then we want to start over with a clean slate, so we
+    // remove the scratch partition until it is requested again.
+    builder->RemovePartition("scratch");
+
+    new_metadata = builder->Export();
+    if (!new_metadata) {
+        return device->WriteFail("Unable to export new partition table");
+    }
+
+    // Write the new table to every metadata slot.
+    bool ok = true;
+    for (size_t i = 0; i < new_metadata->geometry.metadata_slot_count; i++) {
+        ok &= UpdatePartitionTable(super.value(), *new_metadata.get(), i);
+    }
+
+    if (!ok) {
+        return device->WriteFail("Unable to write new partition table");
+    }
+    return device->WriteOkay("Successfully updated partition table");
+}
diff --git a/fastboot/device/flashing.h b/fastboot/device/flashing.h
index 206a407..89e20fc 100644
--- a/fastboot/device/flashing.h
+++ b/fastboot/device/flashing.h
@@ -22,3 +22,4 @@
 class FastbootDevice;
 
 int Flash(FastbootDevice* device, const std::string& partition_name);
+bool UpdateSuper(FastbootDevice* device, const std::string& partition_name, bool wipe);