Merge "liblp: Add an abstraction layer for opening partitions."

This commit is contained in:
Treehugger Robot 2018-10-25 18:55:47 +00:00 committed by Gerrit Code Review
commit 89eb017780
15 changed files with 413 additions and 162 deletions

View File

@ -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",
],
}

View File

@ -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 @@ uint64_t Partition::BytesOnDisk() const {
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 @@ std::unique_ptr<MetadataBuilder> 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> 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) {

View File

@ -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.

View File

@ -24,6 +24,7 @@
#include <memory>
#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<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

View File

@ -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 @@ 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<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.

View File

@ -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

View File

@ -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<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 @@ TEST(liblp, UpdateAnyMetadataSlot) {
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 @@ TEST(liblp, InvalidMetadataSlot) {
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 @@ TEST(liblp, NoChangingGeometry) {
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 @@ 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<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 @@ 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<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 @@ 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<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 @@ 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<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 @@ 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<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 @@ 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]));

View File

@ -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

View File

@ -342,7 +342,14 @@ std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry&
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 @@ std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) {
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) {

View File

@ -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<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);

View File

@ -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

View File

@ -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

View File

@ -16,7 +16,6 @@
#include <fcntl.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <unistd.h>

View File

@ -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<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 @@ 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

View File

@ -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<bool(int, const std::string&)>& writer);
} // namespace fs_mgr