Merge "liblp: Add unit tests."
am: 8ddd105545
Change-Id: I1c69cf75c70b56b87dac05c5fc0654c13f1dd9fe
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 0ca8938..98f364c 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -42,3 +42,19 @@
],
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 a084893..0e4838c 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -287,7 +287,7 @@
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 @@
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 0000000..c6e70e1
--- /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 fb982e2..671a3bd 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -153,6 +153,9 @@
// 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 6a2c655..8522435 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -46,6 +46,7 @@
* 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 217d802..5310cab 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -84,7 +84,7 @@
// 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 0000000..25e8a25
--- /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);
+}