Merge "liblp: Implement support for request queue alignment."
This commit is contained in:
commit
6a288d8128
|
@ -52,6 +52,7 @@ cc_test {
|
|||
"libcrypto",
|
||||
"libcrypto_utils",
|
||||
"liblp",
|
||||
"libfs_mgr",
|
||||
],
|
||||
srcs: [
|
||||
"builder_test.cpp",
|
||||
|
|
|
@ -16,22 +16,47 @@
|
|||
|
||||
#include "liblp/builder.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#include "liblp/metadata_format.h"
|
||||
#include "liblp/reader.h"
|
||||
#include "utility.h"
|
||||
|
||||
namespace android {
|
||||
namespace fs_mgr {
|
||||
|
||||
// Align a byte count up to the nearest 512-byte sector.
|
||||
template <typename T>
|
||||
static inline T AlignToSector(T value) {
|
||||
return (value + (LP_SECTOR_SIZE - 1)) & ~T(LP_SECTOR_SIZE - 1);
|
||||
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;
|
||||
}
|
||||
if (ioctl(fd, BLKALIGNOFF, &device_info->alignment_offset) < 0) {
|
||||
PERROR << __PRETTY_FUNCTION__ << "BLKIOMIN failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
LERROR << __PRETTY_FUNCTION__ << ": Not supported on this operating system.";
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void LinearExtent::AddTo(LpMetadata* out) const {
|
||||
|
@ -56,7 +81,7 @@ void Partition::RemoveExtents() {
|
|||
}
|
||||
|
||||
void Partition::ShrinkTo(uint64_t requested_size) {
|
||||
uint64_t aligned_size = AlignToSector(requested_size);
|
||||
uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
|
||||
if (size_ <= aligned_size) {
|
||||
return;
|
||||
}
|
||||
|
@ -82,11 +107,28 @@ void Partition::ShrinkTo(uint64_t requested_size) {
|
|||
DCHECK(size_ == requested_size);
|
||||
}
|
||||
|
||||
std::unique_ptr<MetadataBuilder> MetadataBuilder::New(uint64_t blockdevice_size,
|
||||
std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& block_device,
|
||||
uint32_t slot_number) {
|
||||
std::unique_ptr<LpMetadata> metadata = ReadMetadata(block_device.c_str(), slot_number);
|
||||
if (!metadata) {
|
||||
return nullptr;
|
||||
}
|
||||
std::unique_ptr<MetadataBuilder> builder = New(*metadata.get());
|
||||
if (!builder) {
|
||||
return nullptr;
|
||||
}
|
||||
BlockDeviceInfo device_info;
|
||||
if (fs_mgr::GetBlockDeviceInfo(block_device, &device_info)) {
|
||||
builder->set_block_device_info(device_info);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const BlockDeviceInfo& device_info,
|
||||
uint32_t metadata_max_size,
|
||||
uint32_t metadata_slot_count) {
|
||||
std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
|
||||
if (!builder->Init(blockdevice_size, metadata_max_size, metadata_slot_count)) {
|
||||
if (!builder->Init(device_info, metadata_max_size, metadata_slot_count)) {
|
||||
return nullptr;
|
||||
}
|
||||
return builder;
|
||||
|
@ -135,10 +177,13 @@ bool MetadataBuilder::Init(const LpMetadata& metadata) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
device_info_.alignment = geometry_.alignment;
|
||||
device_info_.alignment_offset = geometry_.alignment_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetadataBuilder::Init(uint64_t blockdevice_size, uint32_t metadata_max_size,
|
||||
bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata_max_size,
|
||||
uint32_t metadata_slot_count) {
|
||||
if (metadata_max_size < sizeof(LpMetadataHeader)) {
|
||||
LERROR << "Invalid metadata maximum size.";
|
||||
|
@ -150,7 +195,22 @@ bool MetadataBuilder::Init(uint64_t blockdevice_size, uint32_t metadata_max_size
|
|||
}
|
||||
|
||||
// Align the metadata size up to the nearest sector.
|
||||
metadata_max_size = AlignToSector(metadata_max_size);
|
||||
metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE);
|
||||
|
||||
// Check that device properties are sane.
|
||||
if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) {
|
||||
LERROR << "Alignment offset is not sector-aligned.";
|
||||
return false;
|
||||
}
|
||||
if (device_info_.alignment % LP_SECTOR_SIZE != 0) {
|
||||
LERROR << "Partition alignment is not sector-aligned.";
|
||||
return false;
|
||||
}
|
||||
if (device_info_.alignment_offset > device_info_.alignment) {
|
||||
LERROR << "Partition alignment offset is greater than its alignment.";
|
||||
return false;
|
||||
}
|
||||
device_info_ = device_info;
|
||||
|
||||
// We reserve a geometry block (4KB) plus space for each copy of the
|
||||
// maximum size of a metadata blob. Then, we double that space since
|
||||
|
@ -158,20 +218,36 @@ bool MetadataBuilder::Init(uint64_t blockdevice_size, uint32_t metadata_max_size
|
|||
uint64_t reserved =
|
||||
LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count);
|
||||
uint64_t total_reserved = reserved * 2;
|
||||
|
||||
if (blockdevice_size < total_reserved || blockdevice_size - total_reserved < LP_SECTOR_SIZE) {
|
||||
if (device_info_.size < total_reserved) {
|
||||
LERROR << "Attempting to create metadata on a block device that is too small.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// The last sector is inclusive. We subtract one to make sure that logical
|
||||
// partitions won't overlap with the same sector as the backup metadata,
|
||||
// which could happen if the block device was not aligned to LP_SECTOR_SIZE.
|
||||
geometry_.first_logical_sector = reserved / LP_SECTOR_SIZE;
|
||||
geometry_.last_logical_sector = ((blockdevice_size - reserved) / LP_SECTOR_SIZE) - 1;
|
||||
// Compute the first free sector, factoring in alignment.
|
||||
uint64_t free_area = AlignTo(reserved, device_info_.alignment, device_info_.alignment_offset);
|
||||
uint64_t first_sector = free_area / LP_SECTOR_SIZE;
|
||||
|
||||
// Compute the last free sector, which is inclusive. We subtract 1 to make
|
||||
// sure that logical partitions won't overlap with the same sector as the
|
||||
// backup metadata, which could happen if the block device was not aligned
|
||||
// to LP_SECTOR_SIZE.
|
||||
uint64_t last_sector = ((device_info_.size - reserved) / LP_SECTOR_SIZE) - 1;
|
||||
|
||||
// If this check fails, it means either (1) we did not have free space to
|
||||
// allocate a single sector, or (2) we did, but the alignment was high
|
||||
// enough to bump the first sector out of range. Either way, we cannot
|
||||
// continue.
|
||||
if (first_sector > last_sector) {
|
||||
LERROR << "Not enough space to allocate any partition tables.";
|
||||
return false;
|
||||
}
|
||||
|
||||
geometry_.first_logical_sector = first_sector;
|
||||
geometry_.last_logical_sector = last_sector;
|
||||
geometry_.metadata_max_size = metadata_max_size;
|
||||
geometry_.metadata_slot_count = metadata_slot_count;
|
||||
DCHECK(geometry_.last_logical_sector >= geometry_.first_logical_sector);
|
||||
geometry_.alignment = device_info_.alignment;
|
||||
geometry_.alignment_offset = device_info_.alignment_offset;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -209,7 +285,7 @@ void MetadataBuilder::RemovePartition(const std::string& name) {
|
|||
|
||||
bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_size) {
|
||||
// Align the space needed up to the nearest sector.
|
||||
uint64_t aligned_size = AlignToSector(requested_size);
|
||||
uint64_t aligned_size = AlignTo(requested_size, LP_SECTOR_SIZE);
|
||||
if (partition->size() >= aligned_size) {
|
||||
return true;
|
||||
}
|
||||
|
@ -259,10 +335,16 @@ bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_siz
|
|||
continue;
|
||||
}
|
||||
|
||||
uint64_t aligned = AlignSector(previous.end);
|
||||
if (aligned >= current.start) {
|
||||
// After alignment, this extent is not usable.
|
||||
continue;
|
||||
}
|
||||
|
||||
// This gap is enough to hold the remainder of the space requested, so we
|
||||
// can allocate what we need and return.
|
||||
if (current.start - previous.end >= sectors_needed) {
|
||||
auto extent = std::make_unique<LinearExtent>(sectors_needed, previous.end);
|
||||
if (current.start - aligned >= sectors_needed) {
|
||||
auto extent = std::make_unique<LinearExtent>(sectors_needed, aligned);
|
||||
sectors_needed -= extent->num_sectors();
|
||||
new_extents.push_back(std::move(extent));
|
||||
break;
|
||||
|
@ -270,7 +352,7 @@ bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_siz
|
|||
|
||||
// This gap is not big enough to fit the remainder of the space requested,
|
||||
// so consume the whole thing and keep looking for more.
|
||||
auto extent = std::make_unique<LinearExtent>(current.start - previous.end, previous.end);
|
||||
auto extent = std::make_unique<LinearExtent>(current.start - aligned, aligned);
|
||||
sectors_needed -= extent->num_sectors();
|
||||
new_extents.push_back(std::move(extent));
|
||||
}
|
||||
|
@ -286,8 +368,12 @@ bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t requested_siz
|
|||
}
|
||||
DCHECK(first_sector <= geometry_.last_logical_sector);
|
||||
|
||||
// Note: After alignment, |first_sector| may be > the last usable sector.
|
||||
first_sector = AlignSector(first_sector);
|
||||
|
||||
// Note: the last usable sector is inclusive.
|
||||
if (geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
|
||||
if (first_sector > geometry_.last_logical_sector ||
|
||||
geometry_.last_logical_sector + 1 - first_sector < sectors_needed) {
|
||||
LERROR << "Not enough free space to expand partition: " << partition->name();
|
||||
return false;
|
||||
}
|
||||
|
@ -351,5 +437,26 @@ uint64_t MetadataBuilder::AllocatableSpace() const {
|
|||
return (geometry_.last_logical_sector - geometry_.first_logical_sector + 1) * LP_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
uint64_t MetadataBuilder::AlignSector(uint64_t sector) {
|
||||
// Note: when reading alignment info from the Kernel, we don't assume it
|
||||
// is aligned to the sector size, so we round up to the nearest sector.
|
||||
uint64_t lba = sector * LP_SECTOR_SIZE;
|
||||
uint64_t aligned = AlignTo(lba, device_info_.alignment, device_info_.alignment_offset);
|
||||
return AlignTo(aligned, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
|
||||
}
|
||||
|
||||
void MetadataBuilder::set_block_device_info(const BlockDeviceInfo& device_info) {
|
||||
device_info_.size = device_info.size;
|
||||
|
||||
// The kernel does not guarantee these values are present, so we only
|
||||
// replace existing values if the new values are non-zero.
|
||||
if (device_info.alignment) {
|
||||
device_info_.alignment = device_info.alignment;
|
||||
}
|
||||
if (device_info.alignment_offset) {
|
||||
device_info_.alignment_offset = device_info.alignment_offset;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fs_mgr
|
||||
} // namespace android
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include <gtest/gtest.h>
|
||||
#include <liblp/builder.h>
|
||||
#include "fs_mgr.h"
|
||||
#include "utility.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace android::fs_mgr;
|
||||
|
@ -127,6 +129,89 @@ TEST(liblp, MetadataAlignment) {
|
|||
EXPECT_EQ(exported->geometry.metadata_max_size, 1024);
|
||||
}
|
||||
|
||||
TEST(liblp, InternalAlignment) {
|
||||
// Test the metadata fitting within alignment.
|
||||
BlockDeviceInfo device_info(1024 * 1024, 768 * 1024, 0);
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);
|
||||
ASSERT_NE(builder, nullptr);
|
||||
unique_ptr<LpMetadata> exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
|
||||
EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
|
||||
|
||||
// Test a large alignment offset thrown in.
|
||||
device_info.alignment_offset = 753664;
|
||||
builder = MetadataBuilder::New(device_info, 1024, 2);
|
||||
ASSERT_NE(builder, nullptr);
|
||||
exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
EXPECT_EQ(exported->geometry.first_logical_sector, 1472);
|
||||
EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
|
||||
|
||||
// Test only an alignment offset (which should simply bump up the first
|
||||
// logical sector).
|
||||
device_info.alignment = 0;
|
||||
builder = MetadataBuilder::New(device_info, 1024, 2);
|
||||
ASSERT_NE(builder, nullptr);
|
||||
exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
EXPECT_EQ(exported->geometry.first_logical_sector, 1484);
|
||||
EXPECT_EQ(exported->geometry.last_logical_sector, 2035);
|
||||
|
||||
// Test a small alignment with an alignment offset.
|
||||
device_info.alignment = 12 * 1024;
|
||||
device_info.alignment_offset = 3 * 1024;
|
||||
builder = MetadataBuilder::New(device_info, 16 * 1024, 2);
|
||||
ASSERT_NE(builder, nullptr);
|
||||
exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
EXPECT_EQ(exported->geometry.first_logical_sector, 78);
|
||||
EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
|
||||
|
||||
// Test a small alignment with no alignment offset.
|
||||
device_info.alignment = 11 * 1024;
|
||||
builder = MetadataBuilder::New(device_info, 16 * 1024, 2);
|
||||
ASSERT_NE(builder, nullptr);
|
||||
exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
EXPECT_EQ(exported->geometry.first_logical_sector, 72);
|
||||
EXPECT_EQ(exported->geometry.last_logical_sector, 1975);
|
||||
}
|
||||
|
||||
TEST(liblp, InternalPartitionAlignment) {
|
||||
BlockDeviceInfo device_info(512 * 1024 * 1024, 768 * 1024, 753664);
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);
|
||||
|
||||
Partition* a = builder->AddPartition("a", TEST_GUID, 0);
|
||||
ASSERT_NE(a, nullptr);
|
||||
Partition* b = builder->AddPartition("b", TEST_GUID2, 0);
|
||||
ASSERT_NE(b, nullptr);
|
||||
|
||||
// Add a bunch of small extents to each, interleaving.
|
||||
for (size_t i = 0; i < 10; i++) {
|
||||
ASSERT_TRUE(builder->GrowPartition(a, a->size() + 4096));
|
||||
ASSERT_TRUE(builder->GrowPartition(b, b->size() + 4096));
|
||||
}
|
||||
EXPECT_EQ(a->size(), 40960);
|
||||
EXPECT_EQ(b->size(), 40960);
|
||||
|
||||
unique_ptr<LpMetadata> exported = builder->Export();
|
||||
ASSERT_NE(exported, nullptr);
|
||||
|
||||
// Check that each starting sector is aligned.
|
||||
for (const auto& extent : exported->extents) {
|
||||
ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);
|
||||
EXPECT_EQ(extent.num_sectors, 8);
|
||||
|
||||
uint64_t lba = extent.target_data * LP_SECTOR_SIZE;
|
||||
uint64_t aligned_lba = AlignTo(lba, device_info.alignment, device_info.alignment_offset);
|
||||
EXPECT_EQ(lba, aligned_lba);
|
||||
}
|
||||
|
||||
// Sanity check one extent.
|
||||
EXPECT_EQ(exported->extents.back().target_data, 30656);
|
||||
}
|
||||
|
||||
TEST(liblp, UseAllDiskSpace) {
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);
|
||||
EXPECT_EQ(builder->AllocatableSpace(), 1036288);
|
||||
|
@ -312,15 +397,72 @@ TEST(liblp, MetadataTooLarge) {
|
|||
static const size_t kMetadataSize = 64 * 1024;
|
||||
|
||||
// No space to store metadata + geometry.
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize, kMetadataSize, 1);
|
||||
BlockDeviceInfo device_info(kDiskSize, 0, 0);
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
|
||||
EXPECT_EQ(builder, nullptr);
|
||||
|
||||
// No space to store metadata + geometry + one free sector.
|
||||
builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2, kMetadataSize, 1);
|
||||
device_info.size += LP_METADATA_GEOMETRY_SIZE * 2;
|
||||
builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
|
||||
EXPECT_EQ(builder, nullptr);
|
||||
|
||||
// Space for metadata + geometry + one free sector.
|
||||
builder = MetadataBuilder::New(kDiskSize + LP_METADATA_GEOMETRY_SIZE * 2 + LP_SECTOR_SIZE,
|
||||
kMetadataSize, 1);
|
||||
device_info.size += LP_SECTOR_SIZE;
|
||||
builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
|
||||
EXPECT_NE(builder, nullptr);
|
||||
|
||||
// Test with alignment.
|
||||
device_info.alignment = 131072;
|
||||
builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
|
||||
EXPECT_EQ(builder, nullptr);
|
||||
|
||||
device_info.alignment = 0;
|
||||
device_info.alignment_offset = 32768 - LP_SECTOR_SIZE;
|
||||
builder = MetadataBuilder::New(device_info, kMetadataSize, 1);
|
||||
EXPECT_EQ(builder, nullptr);
|
||||
}
|
||||
|
||||
TEST(liblp, block_device_info) {
|
||||
std::unique_ptr<fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
|
||||
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);
|
||||
|
||||
BlockDeviceInfo device_info;
|
||||
ASSERT_TRUE(GetBlockDeviceInfo(rec->blk_device, &device_info));
|
||||
|
||||
// Sanity check that the device doesn't give us some weird inefficient
|
||||
// alignment.
|
||||
ASSERT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);
|
||||
ASSERT_EQ(device_info.alignment_offset % LP_SECTOR_SIZE, 0);
|
||||
ASSERT_LE(device_info.alignment_offset, INT_MAX);
|
||||
|
||||
// Having an alignment offset > alignment doesn't really make sense.
|
||||
ASSERT_LT(device_info.alignment_offset, device_info.alignment);
|
||||
}
|
||||
|
||||
TEST(liblp, UpdateBlockDeviceInfo) {
|
||||
BlockDeviceInfo device_info(1024 * 1024, 4096, 1024);
|
||||
unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
|
||||
ASSERT_NE(builder, nullptr);
|
||||
|
||||
EXPECT_EQ(builder->block_device_info().size, device_info.size);
|
||||
EXPECT_EQ(builder->block_device_info().alignment, device_info.alignment);
|
||||
EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
|
||||
|
||||
device_info.alignment = 0;
|
||||
device_info.alignment_offset = 2048;
|
||||
builder->set_block_device_info(device_info);
|
||||
EXPECT_EQ(builder->block_device_info().alignment, 4096);
|
||||
EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
|
||||
|
||||
device_info.alignment = 8192;
|
||||
device_info.alignment_offset = 0;
|
||||
builder->set_block_device_info(device_info);
|
||||
EXPECT_EQ(builder->block_device_info().alignment, 8192);
|
||||
EXPECT_EQ(builder->block_device_info().alignment_offset, 2048);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,24 @@ namespace fs_mgr {
|
|||
|
||||
class LinearExtent;
|
||||
|
||||
// By default, partitions are aligned on a 1MiB boundary.
|
||||
static const uint32_t kDefaultPartitionAlignment = 1024 * 1024;
|
||||
|
||||
struct BlockDeviceInfo {
|
||||
BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0) {}
|
||||
BlockDeviceInfo(uint64_t size, uint32_t alignment, uint32_t alignment_offset)
|
||||
: size(size), alignment(alignment), alignment_offset(alignment_offset) {}
|
||||
// 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;
|
||||
};
|
||||
|
||||
// Abstraction around dm-targets that can be encoded into logical partition tables.
|
||||
class Extent {
|
||||
public:
|
||||
|
@ -107,14 +125,29 @@ class MetadataBuilder {
|
|||
// If the parameters would yield invalid metadata, nullptr is returned. This
|
||||
// could happen if the block device size is too small to store the metadata
|
||||
// and backup copies.
|
||||
static std::unique_ptr<MetadataBuilder> New(uint64_t blockdevice_size,
|
||||
static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
|
||||
uint32_t metadata_max_size,
|
||||
uint32_t metadata_slot_count);
|
||||
|
||||
// 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,
|
||||
uint32_t slot_number);
|
||||
|
||||
// Import an existing table for modification. If the table is not valid, for
|
||||
// example it contains duplicate partition names, then nullptr is returned.
|
||||
// This method is for testing or changing off-line tables.
|
||||
static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
|
||||
|
||||
// Wrapper around New() with a BlockDeviceInfo that only specifies a device
|
||||
// size. This is a convenience method for tests.
|
||||
static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,
|
||||
uint32_t metadata_slot_count) {
|
||||
BlockDeviceInfo device_info(blockdev_size, 0, 0);
|
||||
return New(device_info, metadata_max_size, metadata_slot_count);
|
||||
}
|
||||
|
||||
// Export metadata so it can be serialized to an image, to disk, or mounted
|
||||
// via device-mapper.
|
||||
std::unique_ptr<LpMetadata> Export();
|
||||
|
@ -156,16 +189,28 @@ class MetadataBuilder {
|
|||
// Amount of space that can be allocated to logical partitions.
|
||||
uint64_t AllocatableSpace() const;
|
||||
|
||||
// Merge new block device information into previous values. Alignment values
|
||||
// are only overwritten if the new values are non-zero.
|
||||
void set_block_device_info(const BlockDeviceInfo& device_info);
|
||||
const BlockDeviceInfo& block_device_info() const { return device_info_; }
|
||||
|
||||
private:
|
||||
MetadataBuilder();
|
||||
bool Init(uint64_t blockdevice_size, uint32_t metadata_max_size, uint32_t metadata_slot_count);
|
||||
bool Init(const BlockDeviceInfo& info, uint32_t metadata_max_size, uint32_t metadata_slot_count);
|
||||
bool Init(const LpMetadata& metadata);
|
||||
|
||||
uint64_t AlignSector(uint64_t sector);
|
||||
|
||||
LpMetadataGeometry geometry_;
|
||||
LpMetadataHeader header_;
|
||||
std::vector<std::unique_ptr<Partition>> partitions_;
|
||||
BlockDeviceInfo device_info_;
|
||||
};
|
||||
|
||||
// Read BlockDeviceInfo for a given block device. This always returns false
|
||||
// for non-Linux operating systems.
|
||||
bool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info);
|
||||
|
||||
} // namespace fs_mgr
|
||||
} // namespace android
|
||||
|
||||
|
|
|
@ -107,6 +107,28 @@ typedef struct LpMetadataGeometry {
|
|||
* backup geometry block at the very end.
|
||||
*/
|
||||
uint64_t last_logical_sector;
|
||||
|
||||
/* 64: Alignment for defining partitions or partition extents. For example,
|
||||
* an alignment of 1MiB will require that all partitions have a size evenly
|
||||
* divisible by 1MiB, and that the smallest unit the partition can grow by
|
||||
* is 1MiB.
|
||||
*
|
||||
* Alignment is normally determined at runtime when growing or adding
|
||||
* partitions. If for some reason the alignment cannot be determined, then
|
||||
* this predefined alignment in the geometry is used instead. By default
|
||||
* it is set to 1MiB.
|
||||
*/
|
||||
uint32_t alignment;
|
||||
|
||||
/* 68: Alignment offset for "stacked" devices. For example, if the "super"
|
||||
* partition itself is not aligned within the parent block device's
|
||||
* partition table, then we adjust for this in deciding where to place
|
||||
* |first_logical_sector|.
|
||||
*
|
||||
* Similar to |alignment|, this will be derived from the operating system.
|
||||
* If it cannot be determined, it is assumed to be 0.
|
||||
*/
|
||||
uint32_t alignment_offset;
|
||||
} __attribute__((packed)) LpMetadataGeometry;
|
||||
|
||||
/* The logical partition metadata has a number of tables; they are described
|
||||
|
|
|
@ -41,11 +41,18 @@ static bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {
|
|||
LERROR << "Logical partition metadata has invalid geometry magic signature.";
|
||||
return false;
|
||||
}
|
||||
// Reject if the struct size is larger than what we compiled. This is so we
|
||||
// can compute a checksum with the |struct_size| field rather than using
|
||||
// sizeof.
|
||||
if (geometry->struct_size > sizeof(LpMetadataGeometry)) {
|
||||
LERROR << "Logical partition metadata has unrecognized fields.";
|
||||
return false;
|
||||
}
|
||||
// Recompute and check the CRC32.
|
||||
{
|
||||
LpMetadataGeometry temp = *geometry;
|
||||
memset(&temp.checksum, 0, sizeof(temp.checksum));
|
||||
SHA256(&temp, sizeof(temp), temp.checksum);
|
||||
SHA256(&temp, temp.struct_size, temp.checksum);
|
||||
if (memcmp(temp.checksum, geometry->checksum, sizeof(temp.checksum)) != 0) {
|
||||
LERROR << "Logical partition metadata has invalid geometry checksum.";
|
||||
return false;
|
||||
|
|
|
@ -50,6 +50,30 @@ int64_t SeekFile64(int fd, int64_t offset, int whence);
|
|||
// Compute a SHA256 hash.
|
||||
void SHA256(const void* data, size_t length, uint8_t out[32]);
|
||||
|
||||
// Align |base| such that it is evenly divisible by |alignment|, which does not
|
||||
// have to be a power of two.
|
||||
constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment) {
|
||||
if (!alignment) {
|
||||
return base;
|
||||
}
|
||||
uint64_t remainder = base % alignment;
|
||||
if (remainder == 0) {
|
||||
return base;
|
||||
}
|
||||
return base + (alignment - remainder);
|
||||
}
|
||||
|
||||
// Same as the above |AlignTo|, except that |base| is only aligned when added to
|
||||
// |alignment_offset|.
|
||||
constexpr uint64_t AlignTo(uint64_t base, uint32_t alignment, uint32_t alignment_offset) {
|
||||
uint64_t aligned = AlignTo(base, alignment) + alignment_offset;
|
||||
if (aligned - alignment >= base) {
|
||||
// We overaligned (base < alignment_offset).
|
||||
return aligned - alignment;
|
||||
}
|
||||
return aligned;
|
||||
}
|
||||
|
||||
} // namespace fs_mgr
|
||||
} // namespace android
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ TEST(liblp, SlotNumberForSlotSuffix) {
|
|||
|
||||
TEST(liblp, GetMetadataOffset) {
|
||||
LpMetadataGeometry geometry = {
|
||||
LP_METADATA_GEOMETRY_MAGIC, sizeof(geometry), {0}, 16384, 4, 10000, 80000};
|
||||
LP_METADATA_GEOMETRY_MAGIC, sizeof(geometry), {0}, 16384, 4, 10000, 80000, 0, 0};
|
||||
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096);
|
||||
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384);
|
||||
EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2);
|
||||
|
@ -41,3 +41,14 @@ TEST(liblp, GetMetadataOffset) {
|
|||
EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), -4096 - 16384 * 3);
|
||||
EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), -4096 - 16384 * 4);
|
||||
}
|
||||
|
||||
TEST(liblp, AlignTo) {
|
||||
EXPECT_EQ(AlignTo(37, 0), 37);
|
||||
EXPECT_EQ(AlignTo(1024, 1024), 1024);
|
||||
EXPECT_EQ(AlignTo(555, 1024), 1024);
|
||||
EXPECT_EQ(AlignTo(555, 1000), 1000);
|
||||
EXPECT_EQ(AlignTo(0, 1024), 0);
|
||||
EXPECT_EQ(AlignTo(54, 32, 30), 62);
|
||||
EXPECT_EQ(AlignTo(32, 32, 30), 62);
|
||||
EXPECT_EQ(AlignTo(17, 32, 30), 30);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue