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