fastbootd: Add commands for logical partitions.
This patch adds the following new commands to fastbootd:
getvar is_logical:<partition>
create-logical-logical-partition <partition> <size>
delete-logical-partition <partition>
resize-logicallogical-partition <partition> <size>
All of these commands operate on logical partitions only, and require a
properly configured "super" partition to operate.
Bug: 78793464
Test: fastboot create-logical-partition example 4096
fastboot create-logical-partition example 4096 returns error
fastboot getvar is-logical:example returns "yes"
fastboot getvar partition-size:example returns "1000"
fastboot resize-logicalpartition example 8000
fastboot getvar partition-size:example returns "2000"
fastboot delete-logical-partition example
fastboot getvar is-logical:example returns error
fastboot getvar is-logical:system_a returns "no"
Change-Id: Iff323eabcf5c559fa04c7c92574650b01803eb1f
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 9995052..3414d53 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -108,6 +108,7 @@
"libbase",
"libbootloader_message",
"libcutils",
+ "libext2_uuid",
"libext4_utils",
"libfs_mgr",
"libhidlbase",
diff --git a/fastboot/constants.h b/fastboot/constants.h
index fa17070..b780ba5 100644
--- a/fastboot/constants.h
+++ b/fastboot/constants.h
@@ -30,6 +30,9 @@
#define FB_CMD_REBOOT_RECOVERY "reboot-recovery"
#define FB_CMD_REBOOT_FASTBOOT "reboot-fastboot"
#define FB_CMD_POWERDOWN "powerdown"
+#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 RESPONSE_OKAY "OKAY"
#define RESPONSE_FAIL "FAIL"
@@ -53,3 +56,4 @@
#define FB_VAR_PARTITION_SIZE "partition-size"
#define FB_VAR_SLOT_SUCCESSFUL "slot-successful"
#define FB_VAR_SLOT_UNBOOTABLE "slot-unbootable"
+#define FB_VAR_IS_LOGICAL "is-logical"
diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp
index 7eaefe6..053d654 100644
--- a/fastboot/device/commands.cpp
+++ b/fastboot/device/commands.cpp
@@ -27,6 +27,9 @@
#include <android-base/unique_fd.h>
#include <cutils/android_reboot.h>
#include <ext4_utils/wipe.h>
+#include <liblp/builder.h>
+#include <liblp/liblp.h>
+#include <uuid/uuid.h>
#include "constants.h"
#include "fastboot_device.h"
@@ -37,6 +40,7 @@
using ::android::hardware::boot::V1_0::BoolResult;
using ::android::hardware::boot::V1_0::CommandResult;
using ::android::hardware::boot::V1_0::Slot;
+using namespace android::fs_mgr;
bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {
using VariableHandler = std::function<bool(FastbootDevice*, const std::vector<std::string>&)>;
@@ -54,7 +58,8 @@
{FB_VAR_HAS_SLOT, GetHasSlot},
{FB_VAR_SLOT_SUCCESSFUL, GetSlotSuccessful},
{FB_VAR_SLOT_UNBOOTABLE, GetSlotUnbootable},
- {FB_VAR_PARTITION_SIZE, GetPartitionSize}};
+ {FB_VAR_PARTITION_SIZE, GetPartitionSize},
+ {FB_VAR_IS_LOGICAL, GetPartitionIsLogical}};
// args[0] is command name, args[1] is variable.
auto found_variable = kVariableMap.find(args[1]);
@@ -209,3 +214,124 @@
TEMP_FAILURE_RETRY(pause());
return status;
}
+
+// Helper class for opening a handle to a MetadataBuilder and writing the new
+// partition table to the same place it was read.
+class PartitionBuilder {
+ public:
+ explicit PartitionBuilder(FastbootDevice* device);
+
+ bool Write();
+ bool Valid() const { return !!builder_; }
+ MetadataBuilder* operator->() const { return builder_.get(); }
+
+ private:
+ std::string super_device_;
+ uint32_t slot_number_;
+ std::unique_ptr<MetadataBuilder> builder_;
+};
+
+PartitionBuilder::PartitionBuilder(FastbootDevice* device) {
+ auto super_device = FindPhysicalPartition(LP_METADATA_PARTITION_NAME);
+ if (!super_device) {
+ return;
+ }
+ super_device_ = *super_device;
+
+ std::string slot = device->GetCurrentSlot();
+ slot_number_ = SlotNumberForSlotSuffix(slot);
+ builder_ = MetadataBuilder::New(super_device_, slot_number_);
+}
+
+bool PartitionBuilder::Write() {
+ std::unique_ptr<LpMetadata> metadata = builder_->Export();
+ if (!metadata) {
+ return false;
+ }
+ return UpdatePartitionTable(super_device_, *metadata.get(), slot_number_);
+}
+
+bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+ if (args.size() < 3) {
+ return device->WriteFail("Invalid partition name and size");
+ }
+
+ uint64_t partition_size;
+ std::string partition_name = args[1];
+ if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {
+ return device->WriteFail("Invalid partition size");
+ }
+
+ PartitionBuilder builder(device);
+ if (!builder.Valid()) {
+ return device->WriteFail("Could not open super partition");
+ }
+ // TODO(112433293) Disallow if the name is in the physical table as well.
+ if (builder->FindPartition(partition_name)) {
+ return device->WriteFail("Partition already exists");
+ }
+
+ // Make a random UUID, since they're not currently used.
+ uuid_t uuid;
+ char uuid_str[37];
+ uuid_generate_random(uuid);
+ uuid_unparse(uuid, uuid_str);
+
+ Partition* partition = builder->AddPartition(partition_name, uuid_str, 0);
+ if (!partition) {
+ return device->WriteFail("Failed to add partition");
+ }
+ if (!builder->ResizePartition(partition, partition_size)) {
+ builder->RemovePartition(partition_name);
+ return device->WriteFail("Not enough space for partition");
+ }
+ if (!builder.Write()) {
+ return device->WriteFail("Failed to write partition table");
+ }
+ return device->WriteOkay("Partition created");
+}
+
+bool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+ if (args.size() < 2) {
+ return device->WriteFail("Invalid partition name and size");
+ }
+
+ PartitionBuilder builder(device);
+ if (!builder.Valid()) {
+ return device->WriteFail("Could not open super partition");
+ }
+ builder->RemovePartition(args[1]);
+ if (!builder.Write()) {
+ return device->WriteFail("Failed to write partition table");
+ }
+ return device->WriteOkay("Partition deleted");
+}
+
+bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
+ if (args.size() < 3) {
+ return device->WriteFail("Invalid partition name and size");
+ }
+
+ uint64_t partition_size;
+ std::string partition_name = args[1];
+ if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {
+ return device->WriteFail("Invalid partition size");
+ }
+
+ PartitionBuilder builder(device);
+ if (!builder.Valid()) {
+ return device->WriteFail("Could not open super partition");
+ }
+
+ Partition* partition = builder->FindPartition(partition_name);
+ if (!partition) {
+ return device->WriteFail("Partition does not exist");
+ }
+ if (!builder->ResizePartition(partition, partition_size)) {
+ return device->WriteFail("Not enough space to resize partition");
+ }
+ if (!builder.Write()) {
+ return device->WriteFail("Failed to write partition table");
+ }
+ return device->WriteOkay("Partition resized");
+}
diff --git a/fastboot/device/commands.h b/fastboot/device/commands.h
index 830eb55..f67df91 100644
--- a/fastboot/device/commands.h
+++ b/fastboot/device/commands.h
@@ -41,3 +41,6 @@
bool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args);
bool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args);
bool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args);
+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);
diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp
index b94fbb0..c306e67 100644
--- a/fastboot/device/fastboot_device.cpp
+++ b/fastboot/device/fastboot_device.cpp
@@ -43,6 +43,9 @@
{FB_CMD_REBOOT_RECOVERY, RebootRecoveryHandler},
{FB_CMD_ERASE, EraseHandler},
{FB_CMD_FLASH, FlashHandler},
+ {FB_CMD_CREATE_PARTITION, CreatePartitionHandler},
+ {FB_CMD_DELETE_PARTITION, DeletePartitionHandler},
+ {FB_CMD_RESIZE_PARTITION, ResizePartitionHandler},
}),
transport_(std::make_unique<ClientUsbTransport>()),
boot_control_hal_(IBootControl::getService()) {}
diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp
index b51b985..70e4bcc 100644
--- a/fastboot/device/variables.cpp
+++ b/fastboot/device/variables.cpp
@@ -148,3 +148,20 @@
uint64_t size = get_block_device_size(handle.fd());
return device->WriteOkay(android::base::StringPrintf("%" PRIX64, size));
}
+
+bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args) {
+ if (args.size() < 1) {
+ return device->WriteFail("Missing argument");
+ }
+ // Note: if a partition name is in both the GPT and the super partition, we
+ // return "true", to be consistent with prefering to flash logical partitions
+ // over physical ones.
+ std::string partition_name = args[0];
+ if (LogicalPartitionExists(partition_name, device->GetCurrentSlot())) {
+ return device->WriteOkay("yes");
+ }
+ if (FindPhysicalPartition(partition_name)) {
+ return device->WriteOkay("no");
+ }
+ return device->WriteFail("Partition not found");
+}
diff --git a/fastboot/device/variables.h b/fastboot/device/variables.h
index 88947e0..0c706d7 100644
--- a/fastboot/device/variables.h
+++ b/fastboot/device/variables.h
@@ -35,3 +35,4 @@
bool GetUnlocked(FastbootDevice* device, const std::vector<std::string>& args);
bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args);
bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args);
+bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args);
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 63ee2af..6890643 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -155,6 +155,21 @@
total);
}
+void fb_queue_create_partition(const std::string& partition, const std::string& size) {
+ Action& a = queue_action(OP_COMMAND, FB_CMD_CREATE_PARTITION ":" + partition + ":" + size);
+ a.msg = "Creating '" + partition + "'";
+}
+
+void fb_queue_delete_partition(const std::string& partition) {
+ Action& a = queue_action(OP_COMMAND, FB_CMD_DELETE_PARTITION ":" + partition);
+ a.msg = "Deleting '" + partition + "'";
+}
+
+void fb_queue_resize_partition(const std::string& partition, const std::string& size) {
+ Action& a = queue_action(OP_COMMAND, FB_CMD_RESIZE_PARTITION ":" + partition + ":" + size);
+ a.msg = "Resizing '" + partition + "'";
+}
+
static int match(const char* str, const char** value, unsigned count) {
unsigned n;
diff --git a/fastboot/engine.h b/fastboot/engine.h
index 74aaa6a..8aebdd7 100644
--- a/fastboot/engine.h
+++ b/fastboot/engine.h
@@ -69,6 +69,9 @@
void fb_queue_upload(const std::string& outfile);
void fb_queue_notice(const std::string& notice);
void fb_queue_wait_for_disconnect(void);
+void fb_queue_create_partition(const std::string& partition, const std::string& size);
+void fb_queue_delete_partition(const std::string& partition);
+void fb_queue_resize_partition(const std::string& partition, const std::string& size);
int64_t fb_execute_queue();
void fb_set_active(const std::string& slot);
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index d652c0d..dc94952 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1691,6 +1691,17 @@
} else {
syntax_error("unknown 'flashing' command %s", args[0].c_str());
}
+ } else if (command == "create-logical-partition") {
+ std::string partition = next_arg(&args);
+ std::string size = next_arg(&args);
+ fb_queue_create_partition(partition, size);
+ } else if (command == "delete-logical-partition") {
+ std::string partition = next_arg(&args);
+ fb_queue_delete_partition(partition);
+ } else if (command == "resize-logical-partition") {
+ std::string partition = next_arg(&args);
+ std::string size = next_arg(&args);
+ fb_queue_resize_partition(partition, size);
} else {
syntax_error("unknown command %s", command.c_str());
}