fastbootd: Support two super partitions for retrofit devices.

Retrofit devices will have two super partitions, spanning the A and B
slots separately. By design an OTA will never cause "A" or "B"
partitions to be assigned to the wrong super. However, the same is not
true of fastbootd, where it is possible to flash the inactive slot. We
do not want, for example, logical "system_a" flashing to super_b.

When interacting with partitions, fastbootd now extracts the slot suffix
from a GetSuperSlotSuffix() helper. On retrofit devices, if the partition
name has a slot, that slot will override FastbootDevice::GetCurrentSlot.
This forces partitions in the inactive slot to be assigned to the correct
super.

There are two consequences of this. First, partitions with no slot
suffix will default to the current slot. That means it is possible to
wind up with two "scratch" partitions, if "adb remount" is used on both
the "A" and "B" slots. However, only the active slot's "scratch" will be
visible to the user (either through adb or fastboot).

Second, if one slot does not have dynamic partitions, flashing will
default to fixed partitions. For example, if the A slot is logical and B
is not, flashing "system_a" will be logical and "system_b" will be
fixed. This works no matter which slot is active. We do not try to
upgrade the inactive slot to dynamic partitions.

Bug: 116802789
Test: fastboot set_active a
      fastboot flashall # dynamic partitions
      fastboot getvar is-logical:system_a # true
      fastboot getvar is-logical:system_b # false
      fastboot set_active b
      fastboot flashall --skip-secondary
      fastboot getvar is-logical:system_a # true
      fastboot getvar is-logical:system_b # true
      Booting both slots works.

Change-Id: Ib3c91944aaee1a96b2f5ad69c90e215bd6c5a2e8
This commit is contained in:
David Anderson 2018-11-09 20:41:33 -08:00
parent eb1213f170
commit d25f1c3775
8 changed files with 185 additions and 77 deletions

View File

@ -322,7 +322,7 @@ bool RebootRecoveryHandler(FastbootDevice* device, const std::vector<std::string
// partition table to the same place it was read.
class PartitionBuilder {
public:
explicit PartitionBuilder(FastbootDevice* device);
explicit PartitionBuilder(FastbootDevice* device, const std::string& partition_name);
bool Write();
bool Valid() const { return !!builder_; }
@ -330,19 +330,19 @@ class PartitionBuilder {
private:
std::string super_device_;
uint32_t slot_number_;
std::unique_ptr<MetadataBuilder> builder_;
};
PartitionBuilder::PartitionBuilder(FastbootDevice* device) {
auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name());
PartitionBuilder::PartitionBuilder(FastbootDevice* device, const std::string& partition_name) {
std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);
slot_number_ = SlotNumberForSlotSuffix(slot_suffix);
auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number_));
if (!super_device) {
return;
}
super_device_ = *super_device;
std::string slot = device->GetCurrentSlot();
uint32_t slot_number = SlotNumberForSlotSuffix(slot);
builder_ = MetadataBuilder::New(super_device_, slot_number);
builder_ = MetadataBuilder::New(super_device_, slot_number_);
}
bool PartitionBuilder::Write() {
@ -350,11 +350,7 @@ bool PartitionBuilder::Write() {
if (!metadata) {
return false;
}
bool ok = true;
for (uint32_t i = 0; i < metadata->geometry.metadata_slot_count; i++) {
ok &= UpdatePartitionTable(super_device_, *metadata.get(), i);
}
return ok;
return UpdateAllPartitionMetadata(super_device_, *metadata.get());
}
bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {
@ -372,7 +368,7 @@ bool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::strin
return device->WriteFail("Invalid partition size");
}
PartitionBuilder builder(device);
PartitionBuilder builder(device, partition_name);
if (!builder.Valid()) {
return device->WriteFail("Could not open super partition");
}
@ -404,11 +400,13 @@ bool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::strin
return device->WriteStatus(FastbootResult::FAIL, "Command not available on locked devices");
}
PartitionBuilder builder(device);
std::string partition_name = args[1];
PartitionBuilder builder(device, partition_name);
if (!builder.Valid()) {
return device->WriteFail("Could not open super partition");
}
builder->RemovePartition(args[1]);
builder->RemovePartition(partition_name);
if (!builder.Write()) {
return device->WriteFail("Failed to write partition table");
}
@ -430,7 +428,7 @@ bool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::strin
return device->WriteFail("Invalid partition size");
}
PartitionBuilder builder(device);
PartitionBuilder builder(device, partition_name);
if (!builder.Valid()) {
return device->WriteFail("Could not open super partition");
}

View File

@ -183,12 +183,7 @@ bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wip
}
// 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_name, *new_metadata.get(), i);
}
if (!ok) {
if (!UpdateAllPartitionMetadata(super_name, *new_metadata.get())) {
return device->WriteFail("Unable to write new partition table");
}
return device->WriteOkay("Successfully updated partition table");

View File

@ -23,9 +23,11 @@
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
#include <liblp/builder.h>
#include <liblp/liblp.h>
#include "fastboot_device.h"
@ -35,7 +37,9 @@ using namespace std::chrono_literals;
using android::base::unique_fd;
using android::hardware::boot::V1_0::Slot;
static bool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) {
namespace {
bool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) {
std::optional<std::string> path = FindPhysicalPartition(name);
if (!path) {
return false;
@ -44,28 +48,31 @@ static bool OpenPhysicalPartition(const std::string& name, PartitionHandle* hand
return true;
}
static bool OpenLogicalPartition(const std::string& name, const std::string& slot,
PartitionHandle* handle) {
std::optional<std::string> path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
bool OpenLogicalPartition(FastbootDevice* device, const std::string& partition_name,
PartitionHandle* handle) {
std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);
uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number));
if (!path) {
return false;
}
uint32_t slot_number = SlotNumberForSlotSuffix(slot);
std::string dm_path;
if (!CreateLogicalPartition(path->c_str(), slot_number, name, true, 5s, &dm_path)) {
LOG(ERROR) << "Could not map partition: " << name;
if (!CreateLogicalPartition(path->c_str(), slot_number, partition_name, true, 5s, &dm_path)) {
LOG(ERROR) << "Could not map partition: " << partition_name;
return false;
}
auto closer = [name]() -> void { DestroyLogicalPartition(name, 5s); };
auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name, 5s); };
*handle = PartitionHandle(dm_path, std::move(closer));
return true;
}
} // namespace
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)) {
if (LogicalPartitionExists(device, name)) {
if (!OpenLogicalPartition(device, name, handle)) {
return false;
}
} else if (!OpenPhysicalPartition(name, handle)) {
@ -104,14 +111,14 @@ static const LpMetadataPartition* FindLogicalPartition(const LpMetadata& metadat
return nullptr;
}
bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
bool* is_zero_length) {
auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name());
bool LogicalPartitionExists(FastbootDevice* device, const std::string& name, bool* is_zero_length) {
std::string slot_suffix = GetSuperSlotSuffix(device, name);
uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);
auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number));
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;
@ -154,12 +161,29 @@ std::vector<std::string> ListPartitions(FastbootDevice* device) {
}
}
// Next get logical partitions.
if (auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name())) {
uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());
if (auto metadata = ReadMetadata(path->c_str(), slot_number)) {
for (const auto& partition : metadata->partitions) {
std::string partition_name = GetPartitionName(partition);
// Find metadata in each super partition (on retrofit devices, there will
// be two).
std::vector<std::unique_ptr<LpMetadata>> metadata_list;
uint32_t current_slot = SlotNumberForSlotSuffix(device->GetCurrentSlot());
std::string super_name = fs_mgr_get_super_partition_name(current_slot);
if (auto metadata = ReadMetadata(super_name, current_slot)) {
metadata_list.emplace_back(std::move(metadata));
}
uint32_t other_slot = (current_slot == 0) ? 1 : 0;
std::string other_super = fs_mgr_get_super_partition_name(other_slot);
if (super_name != other_super) {
if (auto metadata = ReadMetadata(other_super, other_slot)) {
metadata_list.emplace_back(std::move(metadata));
}
}
for (const auto& metadata : metadata_list) {
for (const auto& partition : metadata->partitions) {
std::string partition_name = GetPartitionName(partition);
if (std::find(partitions.begin(), partitions.end(), partition_name) ==
partitions.end()) {
partitions.emplace_back(partition_name);
}
}
@ -175,3 +199,30 @@ bool GetDeviceLockStatus() {
}
return cmdline.find("androidboot.verifiedbootstate=orange") == std::string::npos;
}
bool UpdateAllPartitionMetadata(const std::string& super_name,
const android::fs_mgr::LpMetadata& metadata) {
bool ok = true;
for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {
ok &= UpdatePartitionTable(super_name, metadata, i);
}
return ok;
}
std::string GetSuperSlotSuffix(FastbootDevice* device, const std::string& partition_name) {
// If the super partition does not have a slot suffix, this is not a
// retrofit device, and we should take the current slot.
std::string current_slot_suffix = device->GetCurrentSlot();
uint32_t current_slot_number = SlotNumberForSlotSuffix(current_slot_suffix);
std::string super_partition = fs_mgr_get_super_partition_name(current_slot_number);
if (GetPartitionSlotSuffix(super_partition).empty()) {
return current_slot_suffix;
}
// Otherwise, infer the slot from the partition name.
std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
if (!slot_suffix.empty()) {
return slot_suffix;
}
return current_slot_suffix;
}

View File

@ -20,6 +20,7 @@
#include <android-base/unique_fd.h>
#include <android/hardware/boot/1.0/IBootControl.h>
#include <liblp/liblp.h>
// Logical partitions are only mapped to a block device as needed, and
// immediately unmapped when no longer needed. In order to enforce this we
@ -52,10 +53,20 @@ class PartitionHandle {
class FastbootDevice;
// On normal devices, the super partition is always named "super". On retrofit
// devices, the name must be derived from the partition name or current slot.
// This helper assists in choosing the correct super for a given partition
// name.
std::string GetSuperSlotSuffix(FastbootDevice* device, const std::string& partition_name);
std::optional<std::string> FindPhysicalPartition(const std::string& name);
bool LogicalPartitionExists(const std::string& name, const std::string& slot_suffix,
bool LogicalPartitionExists(FastbootDevice* device, const std::string& name,
bool* is_zero_length = nullptr);
bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle);
bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number);
std::vector<std::string> ListPartitions(FastbootDevice* device);
bool GetDeviceLockStatus();
// Update all copies of metadata.
bool UpdateAllPartitionMetadata(const std::string& super_name,
const android::fs_mgr::LpMetadata& metadata);

View File

@ -271,8 +271,7 @@ bool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args,
return true;
}
std::string partition_name = args[0] + slot_suffix;
if (FindPhysicalPartition(partition_name) ||
LogicalPartitionExists(partition_name, slot_suffix)) {
if (FindPhysicalPartition(partition_name) || LogicalPartitionExists(device, partition_name)) {
*message = "yes";
} else {
*message = "no";
@ -289,8 +288,7 @@ bool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& ar
// Zero-length partitions cannot be created through device-mapper, so we
// special case them here.
bool is_zero_length;
if (LogicalPartitionExists(args[0], device->GetCurrentSlot(), &is_zero_length) &&
is_zero_length) {
if (LogicalPartitionExists(device, args[0], &is_zero_length) && is_zero_length) {
*message = "0x0";
return true;
}
@ -313,8 +311,7 @@ bool GetPartitionType(FastbootDevice* device, const std::vector<std::string>& ar
}
std::string partition_name = args[0];
if (!FindPhysicalPartition(partition_name) &&
!LogicalPartitionExists(partition_name, device->GetCurrentSlot())) {
if (!FindPhysicalPartition(partition_name) && !LogicalPartitionExists(device, partition_name)) {
*message = "Invalid partition";
return false;
}
@ -363,7 +360,7 @@ bool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string
// 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())) {
if (LogicalPartitionExists(device, partition_name)) {
*message = "yes";
return true;
}

View File

@ -20,6 +20,7 @@
#include <algorithm>
#include <android-base/properties.h>
#include <android-base/unique_fd.h>
#include "liblp/liblp.h"
@ -29,6 +30,9 @@
namespace android {
namespace fs_mgr {
bool MetadataBuilder::sABOverrideSet;
bool MetadataBuilder::sABOverrideValue;
bool LinearExtent::AddTo(LpMetadata* out) const {
if (device_index_ >= out->block_devices.size()) {
LERROR << "Extent references unknown block device.";
@ -194,6 +198,11 @@ std::unique_ptr<MetadataBuilder> MetadataBuilder::NewForUpdate(const IPartitionO
return New(*metadata.get(), &opener);
}
void MetadataBuilder::OverrideABForTesting(bool ab_device) {
sABOverrideSet = true;
sABOverrideValue = ab_device;
}
MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
memset(&geometry_, 0, sizeof(geometry_));
geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
@ -418,6 +427,11 @@ Partition* MetadataBuilder::AddPartition(const std::string& name, const std::str
LERROR << "Could not find partition group: " << group_name;
return nullptr;
}
if (IsABDevice() && !auto_slot_suffixing_ && name != "scratch" &&
GetPartitionSlotSuffix(name).empty()) {
LERROR << "Unsuffixed partition not allowed on A/B device: " << name;
return nullptr;
}
partitions_.push_back(std::make_unique<Partition>(name, group_name, attributes));
return partitions_.back().get();
}
@ -900,5 +914,12 @@ void MetadataBuilder::SetAutoSlotSuffixing() {
auto_slot_suffixing_ = true;
}
bool MetadataBuilder::IsABDevice() const {
if (sABOverrideSet) {
return sABOverrideValue;
}
return android::base::GetBoolProperty("ro.build.ab_update", false);
}
} // namespace fs_mgr
} // namespace android

View File

@ -25,7 +25,25 @@ using namespace std;
using namespace android::fs_mgr;
using ::testing::ElementsAre;
TEST(liblp, BuildBasic) {
class Environment : public ::testing::Environment {
public:
void SetUp() override { MetadataBuilder::OverrideABForTesting(false); }
};
int main(int argc, char** argv) {
std::unique_ptr<Environment> env(new Environment);
::testing::AddGlobalTestEnvironment(env.get());
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
class BuilderTest : public ::testing::Test {
public:
void SetUp() override { MetadataBuilder::OverrideABForTesting(false); }
void TearDown() override { MetadataBuilder::OverrideABForTesting(false); }
};
TEST_F(BuilderTest, BuildBasic) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
ASSERT_NE(builder, nullptr);
@ -40,7 +58,7 @@ TEST(liblp, BuildBasic) {
EXPECT_EQ(builder->FindPartition("system"), nullptr);
}
TEST(liblp, ResizePartition) {
TEST_F(BuilderTest, ResizePartition) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
ASSERT_NE(builder, nullptr);
@ -94,7 +112,7 @@ TEST(liblp, ResizePartition) {
EXPECT_EQ(system->extents().size(), 0);
}
TEST(liblp, PartitionAlignment) {
TEST_F(BuilderTest, PartitionAlignment) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
ASSERT_NE(builder, nullptr);
@ -110,7 +128,7 @@ TEST(liblp, PartitionAlignment) {
EXPECT_EQ(system->extents().size(), 1);
}
TEST(liblp, DiskAlignment) {
TEST_F(BuilderTest, DiskAlignment) {
static const uint64_t kDiskSize = 1000000;
static const uint32_t kMetadataSize = 1024;
static const uint32_t kMetadataSlots = 2;
@ -120,7 +138,7 @@ TEST(liblp, DiskAlignment) {
ASSERT_EQ(builder, nullptr);
}
TEST(liblp, MetadataAlignment) {
TEST_F(BuilderTest, MetadataAlignment) {
// Make sure metadata sizes get aligned up.
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);
ASSERT_NE(builder, nullptr);
@ -129,7 +147,7 @@ TEST(liblp, MetadataAlignment) {
EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
}
TEST(liblp, InternalAlignment) {
TEST_F(BuilderTest, InternalAlignment) {
// Test the metadata fitting within alignment.
BlockDeviceInfo device_info("super", 1024 * 1024, 768 * 1024, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
@ -177,7 +195,7 @@ TEST(liblp, InternalAlignment) {
EXPECT_EQ(super_device->first_logical_sector, 160);
}
TEST(liblp, InternalPartitionAlignment) {
TEST_F(BuilderTest, InternalPartitionAlignment) {
BlockDeviceInfo device_info("super", 512 * 1024 * 1024, 768 * 1024, 753664, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
@ -211,7 +229,7 @@ TEST(liblp, InternalPartitionAlignment) {
EXPECT_EQ(exported->extents.back().target_data, 30656);
}
TEST(liblp, UseAllDiskSpace) {
TEST_F(BuilderTest, UseAllDiskSpace) {
static constexpr uint64_t total = 1024 * 1024;
static constexpr uint64_t metadata = 1024;
static constexpr uint64_t slots = 2;
@ -237,7 +255,7 @@ TEST(liblp, UseAllDiskSpace) {
EXPECT_EQ(builder->AllocatableSpace(), allocatable);
}
TEST(liblp, BuildComplex) {
TEST_F(BuilderTest, BuildComplex) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
@ -271,7 +289,7 @@ TEST(liblp, BuildComplex) {
EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());
}
TEST(liblp, AddInvalidPartition) {
TEST_F(BuilderTest, AddInvalidPartition) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
Partition* partition = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
@ -286,7 +304,7 @@ TEST(liblp, AddInvalidPartition) {
EXPECT_EQ(partition, nullptr);
}
TEST(liblp, BuilderExport) {
TEST_F(BuilderTest, BuilderExport) {
static const uint64_t kDiskSize = 1024 * 1024;
static const uint32_t kMetadataSize = 1024;
static const uint32_t kMetadataSlots = 2;
@ -344,7 +362,7 @@ TEST(liblp, BuilderExport) {
}
}
TEST(liblp, BuilderImport) {
TEST_F(BuilderTest, BuilderImport) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_READONLY);
@ -382,7 +400,7 @@ TEST(liblp, BuilderImport) {
EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);
}
TEST(liblp, ExportNameTooLong) {
TEST_F(BuilderTest, ExportNameTooLong) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
std::string name = "abcdefghijklmnopqrstuvwxyz0123456789";
@ -393,7 +411,7 @@ TEST(liblp, ExportNameTooLong) {
EXPECT_EQ(exported, nullptr);
}
TEST(liblp, MetadataTooLarge) {
TEST_F(BuilderTest, MetadataTooLarge) {
static const size_t kDiskSize = 128 * 1024;
static const size_t kMetadataSize = 64 * 1024;
@ -423,7 +441,7 @@ TEST(liblp, MetadataTooLarge) {
EXPECT_EQ(builder, nullptr);
}
TEST(liblp, block_device_info) {
TEST_F(BuilderTest, block_device_info) {
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
fs_mgr_free_fstab);
ASSERT_NE(fstab, nullptr);
@ -444,7 +462,7 @@ TEST(liblp, block_device_info) {
ASSERT_LT(device_info.alignment_offset, device_info.alignment);
}
TEST(liblp, UpdateBlockDeviceInfo) {
TEST_F(BuilderTest, UpdateBlockDeviceInfo) {
BlockDeviceInfo device_info("super", 1024 * 1024, 4096, 1024, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@ -482,13 +500,13 @@ TEST(liblp, UpdateBlockDeviceInfo) {
EXPECT_EQ(new_info.logical_block_size, 4096);
}
TEST(liblp, InvalidBlockSize) {
TEST_F(BuilderTest, InvalidBlockSize) {
BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 513);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
EXPECT_EQ(builder, nullptr);
}
TEST(liblp, AlignedExtentSize) {
TEST_F(BuilderTest, AlignedExtentSize) {
BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@ -499,14 +517,14 @@ TEST(liblp, AlignedExtentSize) {
EXPECT_EQ(partition->size(), 4096);
}
TEST(liblp, AlignedFreeSpace) {
TEST_F(BuilderTest, AlignedFreeSpace) {
// Only one sector free - at least one block is required.
BlockDeviceInfo device_info("super", 10240, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);
ASSERT_EQ(builder, nullptr);
}
TEST(liblp, HasDefaultGroup) {
TEST_F(BuilderTest, HasDefaultGroup) {
BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@ -514,7 +532,7 @@ TEST(liblp, HasDefaultGroup) {
EXPECT_FALSE(builder->AddGroup("default", 0));
}
TEST(liblp, GroupSizeLimits) {
TEST_F(BuilderTest, GroupSizeLimits) {
BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@ -538,7 +556,7 @@ constexpr unsigned long long operator"" _MiB(unsigned long long x) { // NOLINT
return x << 20;
}
TEST(liblp, RemoveAndAddFirstPartition) {
TEST_F(BuilderTest, RemoveAndAddFirstPartition) {
auto builder = MetadataBuilder::New(10_GiB, 65536, 2);
ASSERT_NE(nullptr, builder);
ASSERT_TRUE(builder->AddGroup("foo_a", 5_GiB));
@ -561,7 +579,7 @@ TEST(liblp, RemoveAndAddFirstPartition) {
ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));
}
TEST(liblp, ListGroups) {
TEST_F(BuilderTest, ListGroups) {
BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@ -571,7 +589,7 @@ TEST(liblp, ListGroups) {
ASSERT_THAT(groups, ElementsAre("default", "example"));
}
TEST(liblp, RemoveGroupAndPartitions) {
TEST_F(BuilderTest, RemoveGroupAndPartitions) {
BlockDeviceInfo device_info("super", 1024 * 1024, 0, 0, 4096);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
ASSERT_NE(builder, nullptr);
@ -588,7 +606,7 @@ TEST(liblp, RemoveGroupAndPartitions) {
ASSERT_NE(builder->FindPartition("system"), nullptr);
}
TEST(liblp, MultipleBlockDevices) {
TEST_F(BuilderTest, MultipleBlockDevices) {
std::vector<BlockDeviceInfo> partitions = {
BlockDeviceInfo("system_a", 256_MiB, 786432, 229376, 4096),
BlockDeviceInfo("vendor_a", 128_MiB, 786432, 753664, 4096),
@ -633,7 +651,7 @@ TEST(liblp, MultipleBlockDevices) {
EXPECT_EQ(metadata->extents[2].target_source, 2);
}
TEST(liblp, ImportPartitionsOk) {
TEST_F(BuilderTest, ImportPartitionsOk) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
ASSERT_NE(builder, nullptr);
@ -673,7 +691,7 @@ TEST(liblp, ImportPartitionsOk) {
EXPECT_EQ(extent_a.target_source, extent_b.target_source);
}
TEST(liblp, ImportPartitionsFail) {
TEST_F(BuilderTest, ImportPartitionsFail) {
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
ASSERT_NE(builder, nullptr);
@ -693,3 +711,12 @@ TEST(liblp, ImportPartitionsFail) {
ASSERT_NE(builder, nullptr);
EXPECT_FALSE(builder->ImportPartitions(*exported.get(), {"system"}));
}
TEST_F(BuilderTest, UnsuffixedPartitions) {
MetadataBuilder::OverrideABForTesting(true);
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
ASSERT_NE(builder, nullptr);
ASSERT_EQ(builder->AddPartition("system", 0), nullptr);
ASSERT_NE(builder->AddPartition("system_a", 0), nullptr);
}

View File

@ -22,6 +22,7 @@
#include <map>
#include <memory>
#include <optional>
#include <set>
#include "liblp.h"
@ -186,6 +187,9 @@ class MetadataBuilder {
return New(device_info, metadata_max_size, metadata_slot_count);
}
// Used by the test harness to override whether the device is "A/B".
static void OverrideABForTesting(bool ab_device);
// Define a new partition group. By default there is one group called
// "default", with an unrestricted size. A non-zero size will restrict the
// total space used by all partitions in the group.
@ -270,6 +274,7 @@ class MetadataBuilder {
void ImportExtents(Partition* dest, const LpMetadata& metadata,
const LpMetadataPartition& source);
bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source);
bool IsABDevice() const;
struct Interval {
uint32_t device_index;
@ -290,6 +295,9 @@ class MetadataBuilder {
void ExtentsToFreeList(const std::vector<Interval>& extents,
std::vector<Interval>* free_regions) const;
static bool sABOverrideValue;
static bool sABOverrideSet;
LpMetadataGeometry geometry_;
LpMetadataHeader header_;
std::vector<std::unique_ptr<Partition>> partitions_;