Merge "liblp: Add a helper method for upgrading metadata on retrofit devices."
This commit is contained in:
commit
b6ae0e4fc3
|
@ -113,18 +113,7 @@ std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const IPartitionOpener& op
|
|||
if (!metadata) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<MetadataBuilder> builder = New(*metadata.get());
|
||||
if (!builder) {
|
||||
return nullptr;
|
||||
}
|
||||
for (size_t i = 0; i < builder->block_devices_.size(); i++) {
|
||||
std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
|
||||
BlockDeviceInfo device_info;
|
||||
if (opener.GetInfo(partition_name, &device_info)) {
|
||||
builder->UpdateBlockDeviceInfo(i, device_info);
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
return New(*metadata.get(), &opener);
|
||||
}
|
||||
|
||||
std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& super_partition,
|
||||
|
@ -142,14 +131,69 @@ std::unique_ptr<MetadataBuilder> MetadataBuilder::New(
|
|||
return builder;
|
||||
}
|
||||
|
||||
std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata) {
|
||||
std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata,
|
||||
const IPartitionOpener* opener) {
|
||||
std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
|
||||
if (!builder->Init(metadata)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (opener) {
|
||||
for (size_t i = 0; i < builder->block_devices_.size(); i++) {
|
||||
std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
|
||||
BlockDeviceInfo device_info;
|
||||
if (opener->GetInfo(partition_name, &device_info)) {
|
||||
builder->UpdateBlockDeviceInfo(i, device_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
std::unique_ptr<MetadataBuilder> MetadataBuilder::NewForUpdate(const IPartitionOpener& opener,
|
||||
const std::string& source_partition,
|
||||
uint32_t source_slot_number,
|
||||
uint32_t target_slot_number) {
|
||||
auto metadata = ReadMetadata(opener, source_partition, source_slot_number);
|
||||
if (!metadata) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the list of devices we already have.
|
||||
std::set<std::string> block_devices;
|
||||
for (const auto& block_device : metadata->block_devices) {
|
||||
block_devices.emplace(GetBlockDevicePartitionName(block_device));
|
||||
}
|
||||
|
||||
auto new_block_devices = metadata->block_devices;
|
||||
|
||||
// Add missing block devices.
|
||||
std::string source_slot_suffix = SlotSuffixForSlotNumber(source_slot_number);
|
||||
std::string target_slot_suffix = SlotSuffixForSlotNumber(target_slot_number);
|
||||
for (const auto& block_device : metadata->block_devices) {
|
||||
std::string partition_name = GetBlockDevicePartitionName(block_device);
|
||||
std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
|
||||
if (slot_suffix.empty() || slot_suffix != source_slot_suffix) {
|
||||
continue;
|
||||
}
|
||||
std::string new_name =
|
||||
partition_name.substr(0, partition_name.size() - slot_suffix.size()) +
|
||||
target_slot_suffix;
|
||||
if (block_devices.find(new_name) != block_devices.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto new_device = block_device;
|
||||
if (!UpdateBlockDevicePartitionName(&new_device, new_name)) {
|
||||
LERROR << "Partition name too long: " << new_name;
|
||||
return nullptr;
|
||||
}
|
||||
new_block_devices.emplace_back(new_device);
|
||||
}
|
||||
|
||||
metadata->block_devices = new_block_devices;
|
||||
return New(*metadata.get(), &opener);
|
||||
}
|
||||
|
||||
MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
|
||||
memset(&geometry_, 0, sizeof(geometry_));
|
||||
geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
|
||||
|
|
|
@ -150,10 +150,24 @@ class MetadataBuilder {
|
|||
static std::unique_ptr<MetadataBuilder> New(const std::string& super_partition,
|
||||
uint32_t slot_number);
|
||||
|
||||
// This is when performing an A/B update. The source partition must be a
|
||||
// super partition. On a normal device, the metadata for the source slot
|
||||
// is imported and the target slot is ignored. On a retrofit device, the
|
||||
// metadata may not have the target slot's devices listed yet, in which
|
||||
// case, it is automatically upgraded to include all available block
|
||||
// devices.
|
||||
static std::unique_ptr<MetadataBuilder> NewForUpdate(const IPartitionOpener& opener,
|
||||
const std::string& source_partition,
|
||||
uint32_t source_slot_number,
|
||||
uint32_t target_slot_number);
|
||||
|
||||
// Import an existing table for modification. If the table is not valid, for
|
||||
// example it contains duplicate partition names, then nullptr is returned.
|
||||
// This method is for testing or changing off-line tables.
|
||||
static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
|
||||
//
|
||||
// If an IPartitionOpener is specified, then block device informatiom will
|
||||
// be updated.
|
||||
static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata,
|
||||
const IPartitionOpener* opener = nullptr);
|
||||
|
||||
// Helper function for a single super partition, for tests.
|
||||
static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
|
||||
|
|
|
@ -649,3 +649,42 @@ TEST(liblp, AutoSlotSuffixing) {
|
|||
ASSERT_EQ(metadata->block_devices.size(), static_cast<size_t>(1));
|
||||
EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "super_a");
|
||||
}
|
||||
|
||||
TEST(liblp, UpdateRetrofit) {
|
||||
unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
|
||||
ASSERT_NE(builder, nullptr);
|
||||
ASSERT_TRUE(AddDefaultPartitions(builder.get()));
|
||||
builder->SetAutoSlotSuffixing();
|
||||
|
||||
auto fd = CreateFakeDisk();
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
// Note: we bind the same fd to both names, since we want to make sure the
|
||||
// exact same bits are getting read back in each test.
|
||||
TestPartitionOpener opener({{"super_a", fd}, {"super_b", fd}},
|
||||
{{"super_a", kSuperInfo}, {"super_b", kSuperInfo}});
|
||||
auto exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
ASSERT_TRUE(FlashPartitionTable(opener, "super_a", *exported.get()));
|
||||
|
||||
builder = MetadataBuilder::NewForUpdate(opener, "super_a", 0, 1);
|
||||
ASSERT_NE(builder, nullptr);
|
||||
auto updated = builder->Export();
|
||||
ASSERT_NE(updated, nullptr);
|
||||
ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(2));
|
||||
EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super_a");
|
||||
EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[1]), "super_b");
|
||||
}
|
||||
|
||||
TEST(liblp, UpdateNonRetrofit) {
|
||||
unique_fd fd = CreateFlashedDisk();
|
||||
ASSERT_GE(fd, 0);
|
||||
|
||||
DefaultPartitionOpener opener(fd);
|
||||
auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
|
||||
ASSERT_NE(builder, nullptr);
|
||||
auto updated = builder->Export();
|
||||
ASSERT_NE(updated, nullptr);
|
||||
ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
|
||||
EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
|
||||
}
|
||||
|
|
|
@ -369,12 +369,10 @@ bool AdjustMetadataForSlot(LpMetadata* metadata, uint32_t slot_number) {
|
|||
continue;
|
||||
}
|
||||
std::string partition_name = GetBlockDevicePartitionName(block_device) + slot_suffix;
|
||||
if (partition_name.size() > sizeof(block_device.partition_name)) {
|
||||
if (!UpdateBlockDevicePartitionName(&block_device, partition_name)) {
|
||||
LERROR << __PRETTY_FUNCTION__ << " partition name too long: " << partition_name;
|
||||
return false;
|
||||
}
|
||||
strncpy(block_device.partition_name, partition_name.c_str(),
|
||||
sizeof(block_device.partition_name));
|
||||
block_device.flags &= ~LP_BLOCK_DEVICE_SLOT_SUFFIXED;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -137,5 +137,13 @@ std::string SlotSuffixForSlotNumber(uint32_t slot_number) {
|
|||
return (slot_number == 0) ? "_a" : "_b";
|
||||
}
|
||||
|
||||
bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name) {
|
||||
if (name.size() > sizeof(device->partition_name)) {
|
||||
return false;
|
||||
}
|
||||
strncpy(device->partition_name, name.c_str(), sizeof(device->partition_name));
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace fs_mgr
|
||||
} // namespace android
|
||||
|
|
|
@ -84,6 +84,9 @@ constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment, uint32_t alignment
|
|||
return aligned;
|
||||
}
|
||||
|
||||
// Update names from C++ strings.
|
||||
bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
|
||||
|
||||
} // namespace fs_mgr
|
||||
} // namespace android
|
||||
|
||||
|
|
Loading…
Reference in New Issue