diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp index 0ca8938837bf3bcaa3136d7a974061a8fe742d71..98f364c227c5d225e3b1a6803d81876eda32c3fa 100644 --- a/fs_mgr/liblp/Android.bp +++ b/fs_mgr/liblp/Android.bp @@ -42,3 +42,19 @@ cc_library_static { ], export_include_dirs: ["include"], } + +cc_test { + name: "liblp_test", + defaults: ["fs_mgr_defaults"], + static_libs: [ + "libbase", + "liblog", + "libcrypto", + "libcrypto_utils", + "liblp", + ], + srcs: [ + "builder_test.cpp", + "utility_test.cpp", + ], +} diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp index a084893225393b58d872130126acdfb08bb7fad8..0e4838c3237f7048c5b31a7010543149637e73d0 100644 --- a/fs_mgr/liblp/builder.cpp +++ b/fs_mgr/liblp/builder.cpp @@ -287,7 +287,7 @@ bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_siz DCHECK(first_sector <= geometry_.last_logical_sector); // Note: the last usable sector is inclusive. - if (first_sector + sectors_needed > geometry_.last_logical_sector) { + if (geometry_.last_logical_sector + 1 - first_sector < sectors_needed) { LERROR << "Not enough free space to expand partition: " << partition->name(); return false; } @@ -347,5 +347,9 @@ std::unique_ptr<LpMetadata> MetadataBuilder::Export() { return metadata; } +uint64_t MetadataBuilder::AllocatableSpace() const { + return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE; +} + } // namespace fs_mgr } // namespace android diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c6e70e154efade49a07c81494452201fd1225bf7 --- /dev/null +++ b/fs_mgr/liblp/builder_test.cpp @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <liblp/builder.h> + +using namespace std; +using namespace android::fs_mgr; + +static const char* TEST_GUID = "A799D1D6-669F-41D8-A3F0-EBB7572D8302"; +static const char* TEST_GUID2 = "A799D1D6-669F-41D8-A3F0-EBB7572D8303"; + +TEST(liblp, BuildBasic) { + unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2); + + Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY); + ASSERT_NE(partition, nullptr); + EXPECT_EQ(partition->name(), "system"); + EXPECT_EQ(partition->guid(), TEST_GUID); + EXPECT_EQ(partition->attributes(), LP_PARTITION_ATTR_READONLY); + EXPECT_EQ(partition->size(), 0); + EXPECT_EQ(builder->FindPartition("system"), partition); + + builder->RemovePartition("system"); + EXPECT_EQ(builder->FindPartition("system"), nullptr); +} + +TEST(liblp, ResizePartition) { + unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2); + + Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY); + ASSERT_NE(system, nullptr); + EXPECT_EQ(builder->GrowPartition(system, 65536), true); + EXPECT_EQ(system->size(), 65536); + ASSERT_EQ(system->extents().size(), 1); + + LinearExtent* extent = system->extents()[0]->AsLinearExtent(); + ASSERT_NE(extent, nullptr); + EXPECT_EQ(extent->num_sectors(), 65536 / LP_SECTOR_SIZE); + // The first logical sector will be (4096+1024*2)/512 = 12. + EXPECT_EQ(extent->physical_sector(), 12); + + // Test growing to the same size. + EXPECT_EQ(builder->GrowPartition(system, 65536), true); + EXPECT_EQ(system->size(), 65536); + EXPECT_EQ(system->extents().size(), 1); + EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE); + // Test growing to a smaller size. + EXPECT_EQ(builder->GrowPartition(system, 0), true); + EXPECT_EQ(system->size(), 65536); + EXPECT_EQ(system->extents().size(), 1); + EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE); + // Test shrinking to a greater size. + builder->ShrinkPartition(system, 131072); + EXPECT_EQ(system->size(), 65536); + EXPECT_EQ(system->extents().size(), 1); + EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE); + + // Test shrinking within the same extent. + builder->ShrinkPartition(system, 32768); + EXPECT_EQ(system->size(), 32768); + EXPECT_EQ(system->extents().size(), 1); + extent = system->extents()[0]->AsLinearExtent(); + ASSERT_NE(extent, nullptr); + EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE); + EXPECT_EQ(extent->physical_sector(), 12); + + // Test shrinking to 0. + builder->ShrinkPartition(system, 0); + EXPECT_EQ(system->size(), 0); + EXPECT_EQ(system->extents().size(), 0); +} + +TEST(liblp, PartitionAlignment) { + unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2); + + // Test that we align up to one sector. + Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY); + ASSERT_NE(system, nullptr); + EXPECT_EQ(builder->GrowPartition(system, 10000), true); + EXPECT_EQ(system->size(), 10240); + EXPECT_EQ(system->extents().size(), 1); + + builder->ShrinkPartition(system, 9000); + EXPECT_EQ(system->size(), 9216); + EXPECT_EQ(system->extents().size(), 1); +} + +TEST(liblp, DiskAlignment) { + static const uint64_t kDiskSize = 1000000; + static const uint32_t kMetadataSize = 1024; + static const uint32_t kMetadataSlots = 2; + + // If the disk size is not aligned to 512 bytes, make sure it still leaves + // space at the end for backup metadata, and that it doesn't overlap with + // the space for logical partitions. + unique_ptr<MetadataBuilder> builder = + MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots); + unique_ptr<LpMetadata> exported = builder->Export(); + ASSERT_NE(exported, nullptr); + + static const size_t kMetadataSpace = + (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE; + uint64_t space_at_end = + kDiskSize - (exported->geometry.last_logical_sector + 1) * LP_SECTOR_SIZE; + EXPECT_GE(space_at_end, kMetadataSpace); +} + +TEST(liblp, MetadataAlignment) { + // Make sure metadata sizes get aligned up. + unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2); + unique_ptr<LpMetadata> exported = builder->Export(); + ASSERT_NE(exported, nullptr); + EXPECT_EQ(exported->geometry.metadata_max_size, 1024); +} + +TEST(liblp, UseAllDiskSpace) { + unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2); + EXPECT_EQ(builder->AllocatableSpace(), 1036288); + + Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY); + ASSERT_NE(system, nullptr); + EXPECT_EQ(builder->GrowPartition(system, 1036288), true); + EXPECT_EQ(system->size(), 1036288); + EXPECT_EQ(builder->GrowPartition(system, 1036289), false); +} + +TEST(liblp, BuildComplex) { + unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2); + + Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY); + Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY); + ASSERT_NE(system, nullptr); + ASSERT_NE(vendor, nullptr); + EXPECT_EQ(builder->GrowPartition(system, 65536), true); + EXPECT_EQ(builder->GrowPartition(vendor, 32768), true); + EXPECT_EQ(builder->GrowPartition(system, 98304), true); + EXPECT_EQ(system->size(), 98304); + EXPECT_EQ(vendor->size(), 32768); + + // We now expect to have 3 extents total: 2 for system, 1 for vendor, since + // our allocation strategy is greedy/first-fit. + ASSERT_EQ(system->extents().size(), 2); + ASSERT_EQ(vendor->extents().size(), 1); + + LinearExtent* system1 = system->extents()[0]->AsLinearExtent(); + LinearExtent* system2 = system->extents()[1]->AsLinearExtent(); + LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent(); + ASSERT_NE(system1, nullptr); + ASSERT_NE(system2, nullptr); + ASSERT_NE(vendor1, nullptr); + EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE); + EXPECT_EQ(system1->physical_sector(), 12); + EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE); + EXPECT_EQ(system2->physical_sector(), 204); + EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE); + EXPECT_EQ(vendor1->physical_sector(), 140); + EXPECT_EQ(system1->physical_sector() + system1->num_sectors(), vendor1->physical_sector()); + EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector()); +} + +TEST(liblp, AddInvalidPartition) { + unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2); + + Partition* partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY); + ASSERT_NE(partition, nullptr); + + // Duplicate name. + partition = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY); + EXPECT_EQ(partition, nullptr); + + // Empty name. + partition = builder->AddPartition("", TEST_GUID, LP_PARTITION_ATTR_READONLY); + EXPECT_EQ(partition, nullptr); +} + +TEST(liblp, BuilderExport) { + static const uint64_t kDiskSize = 1024 * 1024; + static const uint32_t kMetadataSize = 1024; + static const uint32_t kMetadataSlots = 2; + unique_ptr<MetadataBuilder> builder = + MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots); + + Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY); + Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY); + ASSERT_NE(system, nullptr); + ASSERT_NE(vendor, nullptr); + EXPECT_EQ(builder->GrowPartition(system, 65536), true); + EXPECT_EQ(builder->GrowPartition(vendor, 32768), true); + EXPECT_EQ(builder->GrowPartition(system, 98304), true); + + unique_ptr<LpMetadata> exported = builder->Export(); + EXPECT_NE(exported, nullptr); + + // Verify geometry. Some details of this may change if we change the + // metadata structures. So in addition to checking the exact values, we + // also check that they are internally consistent after. + const LpMetadataGeometry& geometry = exported->geometry; + EXPECT_EQ(geometry.magic, LP_METADATA_GEOMETRY_MAGIC); + EXPECT_EQ(geometry.struct_size, sizeof(geometry)); + EXPECT_EQ(geometry.metadata_max_size, 1024); + EXPECT_EQ(geometry.metadata_slot_count, 2); + EXPECT_EQ(geometry.first_logical_sector, 12); + EXPECT_EQ(geometry.last_logical_sector, 2035); + + static const size_t kMetadataSpace = + (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE; + uint64_t space_at_end = kDiskSize - (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE; + EXPECT_GE(space_at_end, kMetadataSpace); + EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace); + + // Verify header. + const LpMetadataHeader& header = exported->header; + EXPECT_EQ(header.magic, LP_METADATA_HEADER_MAGIC); + EXPECT_EQ(header.major_version, LP_METADATA_MAJOR_VERSION); + EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION); + + ASSERT_EQ(exported->partitions.size(), 2); + ASSERT_EQ(exported->extents.size(), 3); + + for (const auto& partition : exported->partitions) { + Partition* original = builder->FindPartition(GetPartitionName(partition)); + ASSERT_NE(original, nullptr); + EXPECT_EQ(original->guid(), GetPartitionGuid(partition)); + for (size_t i = 0; i < partition.num_extents; i++) { + const auto& extent = exported->extents[partition.first_extent_index + i]; + LinearExtent* original_extent = original->extents()[i]->AsLinearExtent(); + EXPECT_EQ(extent.num_sectors, original_extent->num_sectors()); + EXPECT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR); + EXPECT_EQ(extent.target_data, original_extent->physical_sector()); + } + EXPECT_EQ(partition.attributes, original->attributes()); + } +} + +TEST(liblp, BuilderImport) { + unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2); + + Partition* system = builder->AddPartition("system", TEST_GUID, LP_PARTITION_ATTR_READONLY); + Partition* vendor = builder->AddPartition("vendor", TEST_GUID2, LP_PARTITION_ATTR_READONLY); + ASSERT_NE(system, nullptr); + ASSERT_NE(vendor, nullptr); + EXPECT_EQ(builder->GrowPartition(system, 65536), true); + EXPECT_EQ(builder->GrowPartition(vendor, 32768), true); + EXPECT_EQ(builder->GrowPartition(system, 98304), true); + + unique_ptr<LpMetadata> exported = builder->Export(); + ASSERT_NE(exported, nullptr); + + builder = MetadataBuilder::New(*exported.get()); + ASSERT_NE(builder, nullptr); + system = builder->FindPartition("system"); + ASSERT_NE(system, nullptr); + vendor = builder->FindPartition("vendor"); + ASSERT_NE(vendor, nullptr); + + EXPECT_EQ(system->size(), 98304); + ASSERT_EQ(system->extents().size(), 2); + EXPECT_EQ(system->guid(), TEST_GUID); + EXPECT_EQ(system->attributes(), LP_PARTITION_ATTR_READONLY); + EXPECT_EQ(vendor->size(), 32768); + ASSERT_EQ(vendor->extents().size(), 1); + EXPECT_EQ(vendor->guid(), TEST_GUID2); + EXPECT_EQ(vendor->attributes(), LP_PARTITION_ATTR_READONLY); + + LinearExtent* system1 = system->extents()[0]->AsLinearExtent(); + LinearExtent* system2 = system->extents()[1]->AsLinearExtent(); + LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent(); + EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE); + EXPECT_EQ(system1->physical_sector(), 12); + EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE); + EXPECT_EQ(system2->physical_sector(), 204); + EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE); +} + +TEST(liblp, ExportNameTooLong) { + unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2); + + std::string name = "abcdefghijklmnopqrstuvwxyz0123456789"; + Partition* system = builder->AddPartition(name + name, TEST_GUID, LP_PARTITION_ATTR_READONLY); + EXPECT_NE(system, nullptr); + + unique_ptr<LpMetadata> exported = builder->Export(); + EXPECT_EQ(exported, nullptr); +} + +TEST(liblp, ExportInvalidGuid) { + unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2); + + Partition* system = builder->AddPartition("system", "bad", LP_PARTITION_ATTR_READONLY); + EXPECT_NE(system, nullptr); + + unique_ptr<LpMetadata> exported = builder->Export(); + EXPECT_EQ(exported, nullptr); +} diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h index fb982e27ae2bdb6d9cb1d017c232298c888a3747..671a3bd2b97b1d6c7265e746174c3d5d46bcbd17 100644 --- a/fs_mgr/liblp/include/liblp/builder.h +++ b/fs_mgr/liblp/include/liblp/builder.h @@ -153,6 +153,9 @@ class MetadataBuilder { // underlying filesystem or contents of the partition on disk. void ShrinkPartition(Partition* partition, uint64_t requested_size); + // Amount of space that can be allocated to logical partitions. + uint64_t AllocatableSpace() const; + private: MetadataBuilder(); bool Init(uint64_t blockdevice_size, uint32_t metadata_max_size, uint32_t metadata_slot_count); diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h index 6a2c65583250419ed36d9c7ce90bbee0ceda52fa..85224352ed6604f503afed151d110e240dc84574 100644 --- a/fs_mgr/liblp/include/liblp/metadata_format.h +++ b/fs_mgr/liblp/include/liblp/metadata_format.h @@ -46,6 +46,7 @@ extern "C" { * READONLY - The partition should not be considered writable. When used with * device mapper, the block device will be created as read-only. */ +#define LP_PARTITION_ATTR_NONE 0x0 #define LP_PARTITION_ATTR_READONLY 0x1 /* Mask that defines all valid attributes. */ diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp index 217d8025677e05bc5fefe403a482f64c8724e035..5310cabbc1acd27387e6062cc93aa7c9af8aa559 100644 --- a/fs_mgr/liblp/utility.cpp +++ b/fs_mgr/liblp/utility.cpp @@ -84,7 +84,7 @@ std::string GetPartitionGuid(const LpMetadataPartition& partition) { // macro to assist with buffer sizing. static const size_t kGuidLen = 36; char buffer[kGuidLen + 1]; - uuid_unparse(partition.guid, buffer); + uuid_unparse_upper(partition.guid, buffer); return buffer; } diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..25e8a254ed52d0ca82badde39251d876900cc9b0 --- /dev/null +++ b/fs_mgr/liblp/utility_test.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utility.h" +#include <gtest/gtest.h> + +using namespace android; +using namespace android::fs_mgr; + +TEST(liblp, SlotNumberForSlotSuffix) { + EXPECT_EQ(SlotNumberForSlotSuffix(""), 0); + EXPECT_EQ(SlotNumberForSlotSuffix("_a"), 0); + EXPECT_EQ(SlotNumberForSlotSuffix("_b"), 1); + EXPECT_EQ(SlotNumberForSlotSuffix("_c"), 2); + EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 3); +} + +TEST(liblp, GetMetadataOffset) { + LpMetadataGeometry geometry = { + LP_METADATA_GEOMETRY_MAGIC, sizeof(geometry), {0}, 16384, 4, 10000, 80000}; + EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096); + EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384); + EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2); + EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), 4096 + 16384 * 3); + + EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), -4096 - 16384 * 1); + EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), -4096 - 16384 * 2); + EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), -4096 - 16384 * 3); + EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), -4096 - 16384 * 4); +}