From 7a6c511a6d109f2b32329c17217dc4e099d9cfb0 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 22 Oct 2018 18:05:54 -0700 Subject: [PATCH] liblp: Add an abstraction layer for opening partitions. This change introduces an IPartitionOpener abstraction. Methods that interact with live metadata, such as ReadMetadata, UpdatePartitionTable, and FlashPartitionTable, now require an IPartitionOpener object. Its purpose is dependency injection: it will make these methods much easier to test when the super partition spans multiple block devices. All non-test consumers should be using PartitionOpener, and as such, some helper methods have been added that automatically create one. Bug: 116802789 Test: liblp_test gtest device with super partition boots Change-Id: I76725a5830ef643c5007c152c00ccaad8085151f --- fs_mgr/liblp/Android.bp | 2 + fs_mgr/liblp/builder.cpp | 53 ++------ fs_mgr/liblp/builder_test.cpp | 7 +- fs_mgr/liblp/include/liblp/builder.h | 29 ++--- fs_mgr/liblp/include/liblp/liblp.h | 19 ++- fs_mgr/liblp/include/liblp/partition_opener.h | 73 +++++++++++ fs_mgr/liblp/io_test.cpp | 115 +++++++++++------- fs_mgr/liblp/partition_opener.cpp | 93 ++++++++++++++ fs_mgr/liblp/reader.cpp | 18 +-- fs_mgr/liblp/reader.h | 1 - fs_mgr/liblp/test_partition_opener.cpp | 51 ++++++++ fs_mgr/liblp/test_partition_opener.h | 42 +++++++ fs_mgr/liblp/utility.cpp | 1 - fs_mgr/liblp/writer.cpp | 65 +++++----- fs_mgr/liblp/writer.h | 6 +- 15 files changed, 413 insertions(+), 162 deletions(-) create mode 100644 fs_mgr/liblp/include/liblp/partition_opener.h create mode 100644 fs_mgr/liblp/partition_opener.cpp create mode 100644 fs_mgr/liblp/test_partition_opener.cpp create mode 100644 fs_mgr/liblp/test_partition_opener.h diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp index 49536551b..5689bdf60 100644 --- a/fs_mgr/liblp/Android.bp +++ b/fs_mgr/liblp/Android.bp @@ -25,6 +25,7 @@ cc_library { srcs: [ "builder.cpp", "images.cpp", + "partition_opener.cpp", "reader.cpp", "utility.cpp", "writer.cpp", @@ -59,6 +60,7 @@ cc_test { srcs: [ "builder_test.cpp", "io_test.cpp", + "test_partition_opener.cpp", "utility_test.cpp", ], } diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp index 4dd60e98b..1b8ed5776 100644 --- a/fs_mgr/liblp/builder.cpp +++ b/fs_mgr/liblp/builder.cpp @@ -16,11 +16,7 @@ #include "liblp/builder.h" -#if defined(__linux__) -#include -#endif #include -#include #include @@ -33,43 +29,6 @@ namespace android { namespace fs_mgr { -bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) { -#if defined(__linux__) - android::base::unique_fd fd(open(block_device.c_str(), O_RDONLY)); - if (fd < 0) { - PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed"; - return false; - } - if (!GetDescriptorSize(fd, &device_info->size)) { - return false; - } - if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) { - PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed"; - return false; - } - - int alignment_offset; - if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) { - PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed"; - return false; - } - int logical_block_size; - if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) { - PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed"; - return false; - } - - device_info->alignment_offset = static_cast(alignment_offset); - device_info->logical_block_size = static_cast(logical_block_size); - return true; -#else - (void)block_device; - (void)device_info; - LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system."; - return false; -#endif -} - void LinearExtent::AddTo(LpMetadata* out) const { out->extents.push_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_}); } @@ -138,9 +97,10 @@ uint64_t Partition::BytesOnDisk() const { return sectors * LP_SECTOR_SIZE; } -std::unique_ptr MetadataBuilder::New(const std::string& block_device, +std::unique_ptr MetadataBuilder::New(const IPartitionOpener& opener, + const std::string& super_partition, uint32_t slot_number) { - std::unique_ptr metadata = ReadMetadata(block_device.c_str(), slot_number); + std::unique_ptr metadata = ReadMetadata(opener, super_partition, slot_number); if (!metadata) { return nullptr; } @@ -149,12 +109,17 @@ std::unique_ptr MetadataBuilder::New(const std::string& block_d return nullptr; } BlockDeviceInfo device_info; - if (fs_mgr::GetBlockDeviceInfo(block_device, &device_info)) { + if (opener.GetInfo(super_partition, &device_info)) { builder->UpdateBlockDeviceInfo(device_info); } return builder; } +std::unique_ptr MetadataBuilder::New(const std::string& super_partition, + uint32_t slot_number) { + return New(PartitionOpener(), super_partition, slot_number); +} + std::unique_ptr MetadataBuilder::New(const BlockDeviceInfo& device_info, uint32_t metadata_max_size, uint32_t metadata_slot_count) { diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp index c3a5ffe92..c02242ab2 100644 --- a/fs_mgr/liblp/builder_test.cpp +++ b/fs_mgr/liblp/builder_test.cpp @@ -424,13 +424,10 @@ TEST(liblp, block_device_info) { fs_mgr_free_fstab); ASSERT_NE(fstab, nullptr); - // This should read from the "super" partition once we have a well-defined - // way to access it. - struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab.get(), "/data"); - ASSERT_NE(rec, nullptr); + PartitionOpener opener; BlockDeviceInfo device_info; - ASSERT_TRUE(GetBlockDeviceInfo(rec->blk_device, &device_info)); + ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info)); // Sanity check that the device doesn't give us some weird inefficient // alignment. diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h index 6d7324d4d..a0908895a 100644 --- a/fs_mgr/liblp/include/liblp/builder.h +++ b/fs_mgr/liblp/include/liblp/builder.h @@ -24,6 +24,7 @@ #include #include "liblp.h" +#include "partition_opener.h" namespace android { namespace fs_mgr { @@ -34,27 +35,6 @@ class LinearExtent; static const uint32_t kDefaultPartitionAlignment = 1024 * 1024; static const uint32_t kDefaultBlockSize = 4096; -struct BlockDeviceInfo { - BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {} - BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset, - uint32_t logical_block_size) - : size(size), - alignment(alignment), - alignment_offset(alignment_offset), - logical_block_size(logical_block_size) {} - // Size of the block device, in bytes. - uint64_t size; - // Optimal target alignment, in bytes. Partition extents will be aligned to - // this value by default. This value must be 0 or a multiple of 512. - uint32_t alignment; - // Alignment offset to parent device (if any), in bytes. The sector at - // |alignment_offset| on the target device is correctly aligned on its - // parent device. This value must be 0 or a multiple of 512. - uint32_t alignment_offset; - // Block size, for aligning extent sizes and partition sizes. - uint32_t logical_block_size; -}; - // Abstraction around dm-targets that can be encoded into logical partition tables. class Extent { public: @@ -157,7 +137,12 @@ class MetadataBuilder { // Import an existing table for modification. This reads metadata off the // given block device and imports it. It also adjusts alignment information // based on run-time values in the operating system. - static std::unique_ptr New(const std::string& block_device, + static std::unique_ptr New(const IPartitionOpener& opener, + const std::string& super_partition, + uint32_t slot_number); + + // Same as above, but use the default PartitionOpener. + static std::unique_ptr New(const std::string& super_partition, uint32_t slot_number); // Import an existing table for modification. If the table is not valid, for diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h index 15fcd43ac..4669cea55 100644 --- a/fs_mgr/liblp/include/liblp/liblp.h +++ b/fs_mgr/liblp/include/liblp/liblp.h @@ -24,7 +24,10 @@ #include #include +#include + #include "metadata_format.h" +#include "partition_opener.h" namespace android { namespace fs_mgr { @@ -44,7 +47,8 @@ struct LpMetadata { // existing geometry, and should not be used for normal partition table // updates. False can be returned if the geometry is incompatible with the // block device or an I/O error occurs. -bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata); +bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition, + const LpMetadata& metadata); // Update the partition table for a given metadata slot number. False is // returned if an error occurs, which can include: @@ -52,12 +56,19 @@ bool FlashPartitionTable(const std::string& block_device, const LpMetadata& meta // - I/O error. // - Corrupt or missing metadata geometry on disk. // - Incompatible geometry. -bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata, - uint32_t slot_number); +bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition, + const LpMetadata& metadata, uint32_t slot_number); // Read logical partition metadata from its predetermined location on a block // device. If readback fails, we also attempt to load from a backup copy. -std::unique_ptr ReadMetadata(const char* block_device, uint32_t slot_number); +std::unique_ptr ReadMetadata(const IPartitionOpener& opener, + const std::string& super_partition, uint32_t slot_number); + +// Helper functions that use the default PartitionOpener. +bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata); +bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata, + uint32_t slot_number); +std::unique_ptr ReadMetadata(const std::string& super_partition, uint32_t slot_number); // Read/Write logical partition metadata to an image file, for diagnostics or // flashing. diff --git a/fs_mgr/liblp/include/liblp/partition_opener.h b/fs_mgr/liblp/include/liblp/partition_opener.h new file mode 100644 index 000000000..fe61b9c20 --- /dev/null +++ b/fs_mgr/liblp/include/liblp/partition_opener.h @@ -0,0 +1,73 @@ +// +// 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. +// + +#pragma once + +#include + +#include + +#include + +namespace android { +namespace fs_mgr { + +struct BlockDeviceInfo { + BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {} + BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset, + uint32_t logical_block_size) + : size(size), + alignment(alignment), + alignment_offset(alignment_offset), + logical_block_size(logical_block_size) {} + // Size of the block device, in bytes. + uint64_t size; + // Optimal target alignment, in bytes. Partition extents will be aligned to + // this value by default. This value must be 0 or a multiple of 512. + uint32_t alignment; + // Alignment offset to parent device (if any), in bytes. The sector at + // |alignment_offset| on the target device is correctly aligned on its + // parent device. This value must be 0 or a multiple of 512. + uint32_t alignment_offset; + // Block size, for aligning extent sizes and partition sizes. + uint32_t logical_block_size; +}; + +// Test-friendly interface for interacting with partitions. +class IPartitionOpener { + public: + virtual ~IPartitionOpener() = default; + + // Open the given named physical partition with the provided open() flags. + // The name can be an absolute path if the full path is already known. + virtual android::base::unique_fd Open(const std::string& partition_name, int flags) const = 0; + + // Return block device information about the given named physical partition. + // The name can be an absolute path if the full path is already known. + virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const = 0; +}; + +// Helper class to implement IPartitionOpener. If |partition_name| is not an +// absolute path, /dev/block/by-name/ will be prepended. +class PartitionOpener : public IPartitionOpener { + public: + virtual android::base::unique_fd Open(const std::string& partition_name, + int flags) const override; + virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override; +}; + +} // namespace fs_mgr +} // namespace android diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp index 3889e87f2..9c675feba 100644 --- a/fs_mgr/liblp/io_test.cpp +++ b/fs_mgr/liblp/io_test.cpp @@ -26,6 +26,7 @@ #include "images.h" #include "reader.h" +#include "test_partition_opener.h" #include "utility.h" #include "writer.h" @@ -101,7 +102,9 @@ static unique_fd CreateFlashedDisk() { if (!exported) { return {}; } - if (!FlashPartitionTable(fd, *exported.get())) { + + TestPartitionOpener opener({{"super", fd}}); + if (!FlashPartitionTable(opener, "super", *exported.get())) { return {}; } return fd; @@ -116,8 +119,10 @@ TEST(liblp, CreateFakeDisk) { ASSERT_TRUE(GetDescriptorSize(fd, &size)); ASSERT_EQ(size, kDiskSize); + TestPartitionOpener opener({{"super", fd}}); + // Verify that we can't read unwritten metadata. - ASSERT_EQ(ReadMetadata(fd, 1), nullptr); + ASSERT_EQ(ReadMetadata(opener, "super", 1), nullptr); } // Flashing metadata should not work if the metadata was created for a larger @@ -133,7 +138,9 @@ TEST(liblp, ExportDiskTooSmall) { unique_fd fd = CreateFakeDisk(); ASSERT_GE(fd, 0); - EXPECT_FALSE(FlashPartitionTable(fd, *exported.get())); + TestPartitionOpener opener({{"super", fd}}); + + EXPECT_FALSE(FlashPartitionTable(opener, "super", *exported.get())); } // Test the basics of flashing a partition and reading it back. @@ -145,16 +152,18 @@ TEST(liblp, FlashAndReadback) { unique_fd fd = CreateFakeDisk(); ASSERT_GE(fd, 0); + TestPartitionOpener opener({{"super", fd}}); + // Export and flash. unique_ptr exported = builder->Export(); ASSERT_NE(exported, nullptr); - ASSERT_TRUE(FlashPartitionTable(fd, *exported.get())); + ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get())); // Read back. Note that some fields are only filled in during // serialization, so exported and imported will not be identical. For // example, table sizes and checksums are computed in WritePartitionTable. // Therefore we check on a field-by-field basis. - unique_ptr imported = ReadMetadata(fd, 0); + unique_ptr imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); // Check geometry and header. @@ -189,23 +198,25 @@ TEST(liblp, UpdateAnyMetadataSlot) { unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); - unique_ptr imported = ReadMetadata(fd, 0); + TestPartitionOpener opener({{"super", fd}}); + + unique_ptr imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); ASSERT_EQ(imported->partitions.size(), 1); EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system"); // Change the name before writing to the next slot. strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name)); - ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1)); + ASSERT_TRUE(UpdatePartitionTable(opener, "super", *imported.get(), 1)); // Read back the original slot, make sure it hasn't changed. - imported = ReadMetadata(fd, 0); + imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); ASSERT_EQ(imported->partitions.size(), 1); EXPECT_EQ(GetPartitionName(imported->partitions[0]), "system"); // Now read back the new slot, and verify that it has a different name. - imported = ReadMetadata(fd, 1); + imported = ReadMetadata(opener, "super", 1); ASSERT_NE(imported, nullptr); ASSERT_EQ(imported->partitions.size(), 1); EXPECT_EQ(GetPartitionName(imported->partitions[0]), "vendor"); @@ -232,15 +243,17 @@ TEST(liblp, InvalidMetadataSlot) { unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); + TestPartitionOpener opener({{"super", fd}}); + // Make sure all slots are filled. - unique_ptr metadata = ReadMetadata(fd, 0); + unique_ptr metadata = ReadMetadata(opener, "super", 0); ASSERT_NE(metadata, nullptr); for (uint32_t i = 1; i < kMetadataSlots; i++) { - ASSERT_TRUE(UpdatePartitionTable(fd, *metadata.get(), i)); + ASSERT_TRUE(UpdatePartitionTable(opener, "super", *metadata.get(), i)); } // Verify that we can't read unavailable slots. - EXPECT_EQ(ReadMetadata(fd, kMetadataSlots), nullptr); + EXPECT_EQ(ReadMetadata(opener, "super", kMetadataSlots), nullptr); } // Test that updating a metadata slot does not allow it to be computed based @@ -249,25 +262,27 @@ TEST(liblp, NoChangingGeometry) { unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); - unique_ptr imported = ReadMetadata(fd, 0); + TestPartitionOpener opener({{"super", fd}}); + + unique_ptr imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); - ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1)); + ASSERT_TRUE(UpdatePartitionTable(opener, "super", *imported.get(), 1)); imported->geometry.metadata_max_size += LP_SECTOR_SIZE; - ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1)); + ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1)); - imported = ReadMetadata(fd, 0); + imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); imported->geometry.metadata_slot_count++; - ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1)); + ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1)); - imported = ReadMetadata(fd, 0); + imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); ASSERT_EQ(imported->block_devices.size(), 1); imported->block_devices[0].first_logical_sector++; - ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1)); + ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 1)); - imported = ReadMetadata(fd, 0); + imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); } @@ -276,6 +291,8 @@ TEST(liblp, BitFlipGeometry) { unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); + TestPartitionOpener opener({{"super", fd}}); + LpMetadataGeometry geometry; ASSERT_GE(lseek(fd, 0, SEEK_SET), 0); ASSERT_TRUE(android::base::ReadFully(fd, &geometry, sizeof(geometry))); @@ -284,7 +301,7 @@ TEST(liblp, BitFlipGeometry) { bad_geometry.metadata_slot_count++; ASSERT_TRUE(android::base::WriteFully(fd, &bad_geometry, sizeof(bad_geometry))); - unique_ptr metadata = ReadMetadata(fd, 0); + unique_ptr metadata = ReadMetadata(opener, "super", 0); ASSERT_NE(metadata, nullptr); EXPECT_EQ(metadata->geometry.metadata_slot_count, 2); } @@ -293,25 +310,29 @@ TEST(liblp, ReadBackupGeometry) { unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); + TestPartitionOpener opener({{"super", fd}}); + char corruption[LP_METADATA_GEOMETRY_SIZE]; memset(corruption, 0xff, sizeof(corruption)); // Corrupt the primary geometry. ASSERT_GE(lseek(fd, GetPrimaryGeometryOffset(), SEEK_SET), 0); ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption))); - EXPECT_NE(ReadMetadata(fd, 0), nullptr); + EXPECT_NE(ReadMetadata(opener, "super", 0), nullptr); // Corrupt the backup geometry. ASSERT_GE(lseek(fd, GetBackupGeometryOffset(), SEEK_SET), 0); ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption))); - EXPECT_EQ(ReadMetadata(fd, 0), nullptr); + EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr); } TEST(liblp, ReadBackupMetadata) { unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); - unique_ptr metadata = ReadMetadata(fd, 0); + TestPartitionOpener opener({{"super", fd}}); + + unique_ptr metadata = ReadMetadata(opener, "super", 0); char corruption[kMetadataSize]; memset(corruption, 0xff, sizeof(corruption)); @@ -320,14 +341,14 @@ TEST(liblp, ReadBackupMetadata) { ASSERT_GE(lseek(fd, offset, SEEK_SET), 0); ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption))); - EXPECT_NE(ReadMetadata(fd, 0), nullptr); + EXPECT_NE(ReadMetadata(opener, "super", 0), nullptr); offset = GetBackupMetadataOffset(metadata->geometry, 0); // Corrupt the backup metadata. ASSERT_GE(lseek(fd, offset, SEEK_SET), 0); ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption))); - EXPECT_EQ(ReadMetadata(fd, 0), nullptr); + EXPECT_EQ(ReadMetadata(opener, "super", 0), nullptr); } // Test that we don't attempt to write metadata if it would overflow its @@ -357,8 +378,10 @@ TEST(liblp, TooManyPartitions) { unique_fd fd = CreateFakeDisk(); ASSERT_GE(fd, 0); + TestPartitionOpener opener({{"super", fd}}); + // Check that we are able to write our table. - ASSERT_TRUE(FlashPartitionTable(fd, *exported.get())); + ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get())); // Check that adding one more partition overflows the metadata allotment. partition = builder->AddPartition("final", LP_PARTITION_ATTR_NONE); @@ -368,7 +391,7 @@ TEST(liblp, TooManyPartitions) { ASSERT_NE(exported, nullptr); // The new table should be too large to be written. - ASSERT_FALSE(UpdatePartitionTable(fd, *exported.get(), 1)); + ASSERT_FALSE(UpdatePartitionTable(opener, "super", *exported.get(), 1)); auto super_device = GetMetadataSuperBlockDevice(*exported.get()); ASSERT_NE(super_device, nullptr); @@ -464,23 +487,25 @@ TEST(liblp, UpdatePrimaryMetadataFailure) { unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); + TestPartitionOpener opener({{"super", fd}}); + BadWriter writer; // Read and write it back. writer.FailOnWrite(1); - unique_ptr imported = ReadMetadata(fd, 0); + unique_ptr imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); - ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer)); + ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer)); // We should still be able to read the backup copy. - imported = ReadMetadata(fd, 0); + imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); // Flash again, this time fail the backup copy. We should still be able // to read the primary. writer.FailOnWrite(3); - ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer)); - imported = ReadMetadata(fd, 0); + ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer)); + imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); } @@ -490,23 +515,25 @@ TEST(liblp, UpdateBackupMetadataFailure) { unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); + TestPartitionOpener opener({{"super", fd}}); + BadWriter writer; // Read and write it back. writer.FailOnWrite(2); - unique_ptr imported = ReadMetadata(fd, 0); + unique_ptr imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); - ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer)); + ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer)); // We should still be able to read the primary copy. - imported = ReadMetadata(fd, 0); + imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); // Flash again, this time fail the primary copy. We should still be able // to read the primary. writer.FailOnWrite(2); - ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer)); - imported = ReadMetadata(fd, 0); + ASSERT_FALSE(UpdatePartitionTable(opener, "super", *imported.get(), 0, writer)); + imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); } @@ -517,20 +544,22 @@ TEST(liblp, UpdateMetadataCleanFailure) { unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); + TestPartitionOpener opener({{"super", fd}}); + BadWriter writer; // Change the name of the existing partition. - unique_ptr new_table = ReadMetadata(fd, 0); + unique_ptr new_table = ReadMetadata(opener, "super", 0); ASSERT_NE(new_table, nullptr); ASSERT_GE(new_table->partitions.size(), 1); new_table->partitions[0].name[0]++; // Flash it, but fail to write the backup copy. writer.FailAfterWrite(2); - ASSERT_FALSE(UpdatePartitionTable(fd, *new_table.get(), 0, writer)); + ASSERT_FALSE(UpdatePartitionTable(opener, "super", *new_table.get(), 0, writer)); // When we read back, we should get the updated primary copy. - unique_ptr imported = ReadMetadata(fd, 0); + unique_ptr imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); ASSERT_GE(new_table->partitions.size(), 1); ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0])); @@ -539,9 +568,9 @@ TEST(liblp, UpdateMetadataCleanFailure) { // Note that the sync step should have used the primary to sync, not // the backup. writer.Reset(); - ASSERT_TRUE(UpdatePartitionTable(fd, *new_table.get(), 0, writer)); + ASSERT_TRUE(UpdatePartitionTable(opener, "super", *new_table.get(), 0, writer)); - imported = ReadMetadata(fd, 0); + imported = ReadMetadata(opener, "super", 0); ASSERT_NE(imported, nullptr); ASSERT_GE(new_table->partitions.size(), 1); ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0])); diff --git a/fs_mgr/liblp/partition_opener.cpp b/fs_mgr/liblp/partition_opener.cpp new file mode 100644 index 000000000..7381eed84 --- /dev/null +++ b/fs_mgr/liblp/partition_opener.cpp @@ -0,0 +1,93 @@ +/* + * 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 "liblp/partition_opener.h" + +#if defined(__linux__) +#include +#endif +#include +#include +#include +#include + +#include "utility.h" + +namespace android { +namespace fs_mgr { + +using android::base::unique_fd; + +namespace { + +std::string GetPartitionAbsolutePath(const std::string& path) { + if (path[0] == '/') { + return path; + } + return "/dev/block/by-name/" + path; +} + +bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) { +#if defined(__linux__) + unique_fd fd(open(block_device.c_str(), O_RDONLY)); + if (fd < 0) { + PERROR << __PRETTY_FUNCTION__ << "open '" << block_device << "' failed"; + return false; + } + if (!GetDescriptorSize(fd, &device_info->size)) { + return false; + } + if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) { + PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed"; + return false; + } + + int alignment_offset; + if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) { + PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed"; + return false; + } + int logical_block_size; + if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) { + PERROR << __PRETTY_FUNCTION__ << "BLKSSZGET failed"; + return false; + } + + device_info->alignment_offset = static_cast(alignment_offset); + device_info->logical_block_size = static_cast(logical_block_size); + return true; +#else + (void)block_device; + (void)device_info; + LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system."; + return false; +#endif +} + +} // namespace + +unique_fd PartitionOpener::Open(const std::string& partition_name, int flags) const { + std::string path = GetPartitionAbsolutePath(partition_name); + return unique_fd{open(path.c_str(), flags)}; +} + +bool PartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const { + std::string path = GetPartitionAbsolutePath(partition_name); + return GetBlockDeviceInfo(path, info); +} + +} // namespace fs_mgr +} // namespace android diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp index c34b1382d..070573ce3 100644 --- a/fs_mgr/liblp/reader.cpp +++ b/fs_mgr/liblp/reader.cpp @@ -342,7 +342,14 @@ std::unique_ptr ReadBackupMetadata(int fd, const LpMetadataGeometry& return ParseMetadata(geometry, fd); } -std::unique_ptr ReadMetadata(int fd, uint32_t slot_number) { +std::unique_ptr ReadMetadata(const IPartitionOpener& opener, + const std::string& super_partition, uint32_t slot_number) { + android::base::unique_fd fd = opener.Open(super_partition, O_RDONLY); + if (fd < 0) { + PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition; + return nullptr; + } + LpMetadataGeometry geometry; if (!ReadLogicalPartitionGeometry(fd, &geometry)) { return nullptr; @@ -361,13 +368,8 @@ std::unique_ptr ReadMetadata(int fd, uint32_t slot_number) { return ReadBackupMetadata(fd, geometry, slot_number); } -std::unique_ptr ReadMetadata(const char* block_device, uint32_t slot_number) { - android::base::unique_fd fd(open(block_device, O_RDONLY)); - if (fd < 0) { - PERROR << __PRETTY_FUNCTION__ << " open failed: " << block_device; - return nullptr; - } - return ReadMetadata(fd, slot_number); +std::unique_ptr ReadMetadata(const std::string& super_partition, uint32_t slot_number) { + return ReadMetadata(PartitionOpener(), super_partition, slot_number); } static std::string NameFromFixedArray(const char* name, size_t buffer_size) { diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h index 24b261121..d5d518888 100644 --- a/fs_mgr/liblp/reader.h +++ b/fs_mgr/liblp/reader.h @@ -31,7 +31,6 @@ namespace fs_mgr { bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry); // Helper functions for manually reading geometry and metadata. -std::unique_ptr ReadMetadata(int fd, uint32_t slot_number); std::unique_ptr ParseMetadata(const LpMetadataGeometry& geometry, int fd); std::unique_ptr ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer, size_t size); diff --git a/fs_mgr/liblp/test_partition_opener.cpp b/fs_mgr/liblp/test_partition_opener.cpp new file mode 100644 index 000000000..c796f6c95 --- /dev/null +++ b/fs_mgr/liblp/test_partition_opener.cpp @@ -0,0 +1,51 @@ +/* + * 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 "test_partition_opener.h" + +#include + +namespace android { +namespace fs_mgr { + +using android::base::unique_fd; + +TestPartitionOpener::TestPartitionOpener( + const std::map& partition_map, + const std::map& partition_info) + : partition_map_(partition_map), partition_info_(partition_info) {} + +unique_fd TestPartitionOpener::Open(const std::string& partition_name, int flags) const { + auto iter = partition_map_.find(partition_name); + if (iter == partition_map_.end()) { + errno = ENOENT; + return {}; + } + return unique_fd{dup(iter->second)}; +} + +bool TestPartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const { + auto iter = partition_info_.find(partition_name); + if (iter == partition_info_.end()) { + errno = ENOENT; + return false; + } + *info = iter->second; + return true; +} + +} // namespace fs_mgr +} // namespace android diff --git a/fs_mgr/liblp/test_partition_opener.h b/fs_mgr/liblp/test_partition_opener.h new file mode 100644 index 000000000..b90fee759 --- /dev/null +++ b/fs_mgr/liblp/test_partition_opener.h @@ -0,0 +1,42 @@ +/* + * 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. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace android { +namespace fs_mgr { + +class TestPartitionOpener : public PartitionOpener { + public: + explicit TestPartitionOpener(const std::map& partition_map, + const std::map& partition_info = {}); + + android::base::unique_fd Open(const std::string& partition_name, int flags) const override; + bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override; + + private: + std::map partition_map_; + std::map partition_info_; +}; + +} // namespace fs_mgr +} // namespace android diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp index 518920d26..742ad823e 100644 --- a/fs_mgr/liblp/utility.cpp +++ b/fs_mgr/liblp/utility.cpp @@ -16,7 +16,6 @@ #include #include -#include #include #include diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp index ddae8420a..c740bd4c8 100644 --- a/fs_mgr/liblp/writer.cpp +++ b/fs_mgr/liblp/writer.cpp @@ -218,7 +218,14 @@ static bool DefaultWriter(int fd, const std::string& blob) { return android::base::WriteFully(fd, blob.data(), blob.size()); } -bool FlashPartitionTable(int fd, const LpMetadata& metadata) { +bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition, + const LpMetadata& metadata) { + android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC); + if (fd < 0) { + PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition; + return false; + } + // Before writing geometry and/or logical partition tables, perform some // basic checks that the geometry and tables are coherent, and will fit // on the given block device. @@ -238,6 +245,8 @@ bool FlashPartitionTable(int fd, const LpMetadata& metadata) { return false; } + LWARN << "Flashing new logical partition geometry to " << super_partition; + // Write geometry to the primary and backup locations. std::string blob = SerializeGeometry(metadata.geometry); if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) { @@ -264,13 +273,24 @@ bool FlashPartitionTable(int fd, const LpMetadata& metadata) { return ok; } +bool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata) { + return FlashPartitionTable(PartitionOpener(), super_partition, metadata); +} + static bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) { return !memcmp(a.header.header_checksum, b.header.header_checksum, sizeof(a.header.header_checksum)); } -bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number, +bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition, + const LpMetadata& metadata, uint32_t slot_number, const std::function& writer) { + android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC); + if (fd < 0) { + PERROR << __PRETTY_FUNCTION__ << " open failed: " << super_partition; + return false; + } + // Before writing geometry and/or logical partition tables, perform some // basic checks that the geometry and tables are coherent, and will fit // on the given block device. @@ -330,39 +350,24 @@ bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_numb } // Both copies should now be in sync, so we can continue the update. - return WriteMetadata(fd, metadata, slot_number, blob, writer); -} + if (!WriteMetadata(fd, metadata, slot_number, blob, writer)) { + return false; + } -bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata) { - android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC)); - if (fd < 0) { - PERROR << __PRETTY_FUNCTION__ << " open failed: " << block_device; - return false; - } - if (!FlashPartitionTable(fd, metadata)) { - return false; - } - LWARN << "Flashed new logical partition geometry to " << block_device; - return true; -} - -bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata, - uint32_t slot_number) { - android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC)); - if (fd < 0) { - PERROR << __PRETTY_FUNCTION__ << " open failed: " << block_device; - return false; - } - if (!UpdatePartitionTable(fd, metadata, slot_number)) { - return false; - } LINFO << "Updated logical partition table at slot " << slot_number << " on device " - << block_device; + << super_partition; return true; } -bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) { - return UpdatePartitionTable(fd, metadata, slot_number, DefaultWriter); +bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition, + const LpMetadata& metadata, uint32_t slot_number) { + return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter); +} + +bool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata, + uint32_t slot_number) { + PartitionOpener opener; + return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter); } } // namespace fs_mgr diff --git a/fs_mgr/liblp/writer.h b/fs_mgr/liblp/writer.h index ab18d450c..6f1da0f20 100644 --- a/fs_mgr/liblp/writer.h +++ b/fs_mgr/liblp/writer.h @@ -30,10 +30,8 @@ std::string SerializeMetadata(const LpMetadata& input); // These variants are for testing only. The path-based functions should be used // for actual operation, so that open() is called with the correct flags. -bool FlashPartitionTable(int fd, const LpMetadata& metadata); -bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number); - -bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number, +bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition, + const LpMetadata& metadata, uint32_t slot_number, const std::function& writer); } // namespace fs_mgr