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
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 4953655..5689bdf 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -25,6 +25,7 @@
srcs: [
"builder.cpp",
"images.cpp",
+ "partition_opener.cpp",
"reader.cpp",
"utility.cpp",
"writer.cpp",
@@ -59,6 +60,7 @@
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 4dd60e9..1b8ed57 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 <linux/fs.h>
-#endif
#include <string.h>
-#include <sys/ioctl.h>
#include <algorithm>
@@ -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<uint32_t>(alignment_offset);
- device_info->logical_block_size = static_cast<uint32_t>(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 @@
return sectors * LP_SECTOR_SIZE;
}
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const IPartitionOpener& opener,
+ const std::string& super_partition,
uint32_t slot_number) {
- std::unique_ptr<LpMetadata> metadata = ReadMetadata(block_device.c_str(), slot_number);
+ std::unique_ptr<LpMetadata> metadata = ReadMetadata(opener, super_partition, slot_number);
if (!metadata) {
return nullptr;
}
@@ -149,12 +109,17 @@
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> MetadataBuilder::New(const std::string& super_partition,
+ uint32_t slot_number) {
+ return New(PartitionOpener(), super_partition, slot_number);
+}
+
std::unique_ptr<MetadataBuilder> 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 c3a5ffe..c02242a 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -424,13 +424,10 @@
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 6d7324d..a090889 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -24,6 +24,7 @@
#include <memory>
#include "liblp.h"
+#include "partition_opener.h"
namespace android {
namespace fs_mgr {
@@ -34,27 +35,6 @@
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 @@
// 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<MetadataBuilder> New(const std::string& block_device,
+ static std::unique_ptr<MetadataBuilder> 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<MetadataBuilder> 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 15fcd43..4669cea 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -24,7 +24,10 @@
#include <memory>
#include <string>
+#include <android-base/unique_fd.h>
+
#include "metadata_format.h"
+#include "partition_opener.h"
namespace android {
namespace fs_mgr {
@@ -44,7 +47,8 @@
// 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 @@
// - 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<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number);
+std::unique_ptr<LpMetadata> 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<LpMetadata> 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 0000000..fe61b9c
--- /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 <stdint.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+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 3889e87..9c675fe 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 @@
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 @@
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 @@
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 @@
unique_fd fd = CreateFakeDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
// Export and flash.
unique_ptr<LpMetadata> 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<LpMetadata> imported = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
ASSERT_NE(imported, nullptr);
// Check geometry and header.
@@ -189,23 +198,25 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
+ unique_ptr<LpMetadata> 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 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
// Make sure all slots are filled.
- unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> 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 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
+ unique_ptr<LpMetadata> 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 @@
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 @@
bad_geometry.metadata_slot_count++;
ASSERT_TRUE(android::base::WriteFully(fd, &bad_geometry, sizeof(bad_geometry)));
- unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
ASSERT_NE(metadata, nullptr);
EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);
}
@@ -293,25 +310,29 @@
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<LpMetadata> metadata = ReadMetadata(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
+ unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
char corruption[kMetadataSize];
memset(corruption, 0xff, sizeof(corruption));
@@ -320,14 +341,14 @@
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 @@
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 @@
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 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
BadWriter writer;
// Read and write it back.
writer.FailOnWrite(1);
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> 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 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
BadWriter writer;
// Read and write it back.
writer.FailOnWrite(2);
- unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> 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 @@
unique_fd fd = CreateFlashedDisk();
ASSERT_GE(fd, 0);
+ TestPartitionOpener opener({{"super", fd}});
+
BadWriter writer;
// Change the name of the existing partition.
- unique_ptr<LpMetadata> new_table = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> 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<LpMetadata> imported = ReadMetadata(fd, 0);
+ unique_ptr<LpMetadata> 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 @@
// 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 0000000..7381eed
--- /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 <linux/fs.h>
+#endif
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#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<uint32_t>(alignment_offset);
+ device_info->logical_block_size = static_cast<uint32_t>(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 c34b138..070573c 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -342,7 +342,14 @@
return ParseMetadata(geometry, fd);
}
-std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
+std::unique_ptr<LpMetadata> 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 @@
return ReadBackupMetadata(fd, geometry, slot_number);
}
-std::unique_ptr<LpMetadata> 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<LpMetadata> 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 24b2611..d5d5188 100644
--- a/fs_mgr/liblp/reader.h
+++ b/fs_mgr/liblp/reader.h
@@ -31,7 +31,6 @@
bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry);
// Helper functions for manually reading geometry and metadata.
-std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number);
std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd);
std::unique_ptr<LpMetadata> 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 0000000..c796f6c
--- /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 <errno.h>
+
+namespace android {
+namespace fs_mgr {
+
+using android::base::unique_fd;
+
+TestPartitionOpener::TestPartitionOpener(
+ const std::map<std::string, int>& partition_map,
+ const std::map<std::string, BlockDeviceInfo>& 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 0000000..b90fee7
--- /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 <map>
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <liblp/partition_opener.h>
+
+namespace android {
+namespace fs_mgr {
+
+class TestPartitionOpener : public PartitionOpener {
+ public:
+ explicit TestPartitionOpener(const std::map<std::string, int>& partition_map,
+ const std::map<std::string, BlockDeviceInfo>& 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<std::string, int> partition_map_;
+ std::map<std::string, BlockDeviceInfo> partition_info_;
+};
+
+} // namespace fs_mgr
+} // namespace android
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 518920d..742ad82 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -16,7 +16,6 @@
#include <fcntl.h>
#include <stdint.h>
-#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index ddae842..c740bd4 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -218,7 +218,14 @@
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 @@
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 @@
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<bool(int, const std::string&)>& 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 @@
}
// 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 ab18d45..6f1da0f 100644
--- a/fs_mgr/liblp/writer.h
+++ b/fs_mgr/liblp/writer.h
@@ -30,10 +30,8 @@
// 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<bool(int, const std::string&)>& writer);
} // namespace fs_mgr