diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp index 97b15bdcd..743a3fec3 100644 --- a/fs_mgr/liblp/builder.cpp +++ b/fs_mgr/liblp/builder.cpp @@ -150,7 +150,7 @@ std::unique_ptr MetadataBuilder::New(const std::string& block_d } BlockDeviceInfo device_info; if (fs_mgr::GetBlockDeviceInfo(block_device, &device_info)) { - builder->set_block_device_info(device_info); + builder->UpdateBlockDeviceInfo(device_info); } return builder; } @@ -217,10 +217,6 @@ bool MetadataBuilder::Init(const LpMetadata& metadata) { } } } - - device_info_.alignment = geometry_.alignment; - device_info_.alignment_offset = geometry_.alignment_offset; - device_info_.logical_block_size = geometry_.logical_block_size; return true; } @@ -239,24 +235,23 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata metadata_max_size = AlignTo(metadata_max_size, LP_SECTOR_SIZE); // Check that device properties are sane. - device_info_ = device_info; - if (device_info_.size % LP_SECTOR_SIZE != 0) { + if (device_info.size % LP_SECTOR_SIZE != 0) { LERROR << "Block device size must be a multiple of 512."; return false; } - if (device_info_.logical_block_size % LP_SECTOR_SIZE != 0) { + if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) { LERROR << "Logical block size must be a multiple of 512."; return false; } - if (device_info_.alignment_offset % LP_SECTOR_SIZE != 0) { + 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) { + 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) { + if (device_info.alignment_offset > device_info.alignment) { LERROR << "Partition alignment offset is greater than its alignment."; return false; } @@ -267,20 +262,21 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata uint64_t reserved = LP_METADATA_GEOMETRY_SIZE + (uint64_t(metadata_max_size) * metadata_slot_count); uint64_t total_reserved = reserved * 2; - if (device_info_.size < total_reserved) { + if (device_info.size < total_reserved) { LERROR << "Attempting to create metadata on a block device that is too small."; return false; } // Compute the first free sector, factoring in alignment. - uint64_t free_area = AlignTo(reserved, device_info_.alignment, device_info_.alignment_offset); + uint64_t free_area = + AlignTo(total_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; + uint64_t last_sector = (device_info.size / 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 @@ -296,7 +292,7 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata // computation, then we abort. Note that the last sector is inclusive, // so we have to account for that. uint64_t num_free_sectors = last_sector - first_sector + 1; - uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE; + uint64_t sectors_per_block = device_info.logical_block_size / LP_SECTOR_SIZE; if (num_free_sectors < sectors_per_block) { LERROR << "Not enough space to allocate any partition tables."; return false; @@ -307,9 +303,9 @@ bool MetadataBuilder::Init(const BlockDeviceInfo& device_info, uint32_t metadata geometry_.last_logical_sector = last_sector; geometry_.metadata_max_size = metadata_max_size; geometry_.metadata_slot_count = metadata_slot_count; - geometry_.alignment = device_info_.alignment; - geometry_.alignment_offset = device_info_.alignment_offset; - geometry_.block_device_size = device_info_.size; + geometry_.alignment = device_info.alignment; + geometry_.alignment_offset = device_info.alignment_offset; + geometry_.block_device_size = device_info.size; geometry_.logical_block_size = device_info.logical_block_size; if (!AddGroup("default", 0)) { @@ -460,7 +456,7 @@ bool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size) free_regions.emplace_back(last_free_extent_start, geometry_.last_logical_sector + 1); } - const uint64_t sectors_per_block = device_info_.logical_block_size / LP_SECTOR_SIZE; + const uint64_t sectors_per_block = geometry_.logical_block_size / LP_SECTOR_SIZE; CHECK_NE(sectors_per_block, 0); CHECK(sectors_needed % sectors_per_block == 0); @@ -578,32 +574,44 @@ 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); + uint64_t aligned = AlignTo(lba, geometry_.alignment, geometry_.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; +bool MetadataBuilder::GetBlockDeviceInfo(BlockDeviceInfo* info) const { + info->size = geometry_.block_device_size; + info->alignment = geometry_.alignment; + info->alignment_offset = geometry_.alignment_offset; + info->logical_block_size = geometry_.logical_block_size; + return true; +} - // Note that if the logical block size changes, we're probably in trouble: - // we could have already built extents that will only work on the previous - // size. - DCHECK(partitions_.empty() || - device_info_.logical_block_size == device_info.logical_block_size); +bool MetadataBuilder::UpdateBlockDeviceInfo(const BlockDeviceInfo& device_info) { + if (device_info.size != geometry_.block_device_size) { + LERROR << "Device size does not match (got " << device_info.size << ", expected " + << geometry_.block_device_size << ")"; + return false; + } + if (device_info.logical_block_size != geometry_.logical_block_size) { + LERROR << "Device logical block size does not match (got " << device_info.logical_block_size + << ", expected " << geometry_.logical_block_size << ")"; + return false; + } // 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; + geometry_.alignment = device_info.alignment; } if (device_info.alignment_offset) { - device_info_.alignment_offset = device_info.alignment_offset; + geometry_.alignment_offset = device_info.alignment_offset; } + return true; } bool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size) { // Align the space needed up to the nearest sector. - uint64_t aligned_size = AlignTo(requested_size, device_info_.logical_block_size); + uint64_t aligned_size = AlignTo(requested_size, geometry_.logical_block_size); uint64_t old_size = partition->size(); if (aligned_size > old_size) { diff --git a/fs_mgr/liblp/builder_test.cpp b/fs_mgr/liblp/builder_test.cpp index c916b4426..ffa7d3bac 100644 --- a/fs_mgr/liblp/builder_test.cpp +++ b/fs_mgr/liblp/builder_test.cpp @@ -48,8 +48,8 @@ TEST(liblp, ResizePartition) { LinearExtent* extent = system->extents()[0]->AsLinearExtent(); ASSERT_NE(extent, nullptr); EXPECT_EQ(extent->num_sectors(), 65536 / LP_SECTOR_SIZE); - // The first logical sector will be (4096+1024*2)/512 = 12. - EXPECT_EQ(extent->physical_sector(), 12); + // The first logical sector will be (8192+1024*4)/512 = 12. + EXPECT_EQ(extent->physical_sector(), 24); // Test resizing to the same size. EXPECT_EQ(builder->ResizePartition(system, 65536), true); @@ -78,7 +78,7 @@ TEST(liblp, ResizePartition) { extent = system->extents()[0]->AsLinearExtent(); ASSERT_NE(extent, nullptr); EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE); - EXPECT_EQ(extent->physical_sector(), 12); + EXPECT_EQ(extent->physical_sector(), 24); // Test shrinking to 0. builder->ResizePartition(system, 0); @@ -127,7 +127,7 @@ TEST(liblp, InternalAlignment) { unique_ptr exported = builder->Export(); ASSERT_NE(exported, nullptr); EXPECT_EQ(exported->geometry.first_logical_sector, 1536); - EXPECT_EQ(exported->geometry.last_logical_sector, 2031); + EXPECT_EQ(exported->geometry.last_logical_sector, 2047); // Test a large alignment offset thrown in. device_info.alignment_offset = 753664; @@ -136,7 +136,7 @@ TEST(liblp, InternalAlignment) { exported = builder->Export(); ASSERT_NE(exported, nullptr); EXPECT_EQ(exported->geometry.first_logical_sector, 1472); - EXPECT_EQ(exported->geometry.last_logical_sector, 2031); + EXPECT_EQ(exported->geometry.last_logical_sector, 2047); // Alignment offset without alignment doesn't mean anything. device_info.alignment = 0; @@ -150,8 +150,8 @@ TEST(liblp, InternalAlignment) { 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, 1973); + EXPECT_EQ(exported->geometry.first_logical_sector, 150); + EXPECT_EQ(exported->geometry.last_logical_sector, 2045); // Test a small alignment with no alignment offset. device_info.alignment = 11 * 1024; @@ -159,8 +159,8 @@ TEST(liblp, InternalAlignment) { 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); + EXPECT_EQ(exported->geometry.first_logical_sector, 160); + EXPECT_EQ(exported->geometry.last_logical_sector, 2047); } TEST(liblp, InternalPartitionAlignment) { @@ -247,11 +247,11 @@ TEST(liblp, BuildComplex) { ASSERT_NE(system2, nullptr); ASSERT_NE(vendor1, nullptr); EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE); - EXPECT_EQ(system1->physical_sector(), 12); + EXPECT_EQ(system1->physical_sector(), 24); EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE); - EXPECT_EQ(system2->physical_sector(), 204); + EXPECT_EQ(system2->physical_sector(), 216); EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE); - EXPECT_EQ(vendor1->physical_sector(), 140); + EXPECT_EQ(vendor1->physical_sector(), 152); EXPECT_EQ(system1->physical_sector() + system1->num_sectors(), vendor1->physical_sector()); EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector()); } @@ -297,13 +297,11 @@ TEST(liblp, BuilderExport) { EXPECT_EQ(geometry.struct_size, sizeof(geometry)); EXPECT_EQ(geometry.metadata_max_size, 1024); EXPECT_EQ(geometry.metadata_slot_count, 2); - EXPECT_EQ(geometry.first_logical_sector, 12); - EXPECT_EQ(geometry.last_logical_sector, 2035); + EXPECT_EQ(geometry.first_logical_sector, 24); + EXPECT_EQ(geometry.last_logical_sector, 2047); static const size_t kMetadataSpace = - (kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE; - uint64_t space_at_end = kDiskSize - (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE; - EXPECT_GE(space_at_end, kMetadataSpace); + ((kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE) * 2; EXPECT_GE(geometry.first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace); // Verify header. @@ -361,9 +359,9 @@ TEST(liblp, BuilderImport) { LinearExtent* system2 = system->extents()[1]->AsLinearExtent(); LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent(); EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE); - EXPECT_EQ(system1->physical_sector(), 12); + EXPECT_EQ(system1->physical_sector(), 24); EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE); - EXPECT_EQ(system2->physical_sector(), 204); + EXPECT_EQ(system2->physical_sector(), 216); EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE); } @@ -437,22 +435,37 @@ TEST(liblp, UpdateBlockDeviceInfo) { unique_ptr 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); - EXPECT_EQ(builder->block_device_info().logical_block_size, device_info.logical_block_size); + BlockDeviceInfo new_info; + ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info)); + + EXPECT_EQ(new_info.size, device_info.size); + EXPECT_EQ(new_info.alignment, device_info.alignment); + EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset); + EXPECT_EQ(new_info.logical_block_size, device_info.logical_block_size); 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); + ASSERT_TRUE(builder->UpdateBlockDeviceInfo(device_info)); + ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info)); + EXPECT_EQ(new_info.alignment, 4096); + EXPECT_EQ(new_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); + ASSERT_TRUE(builder->UpdateBlockDeviceInfo(device_info)); + ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info)); + EXPECT_EQ(new_info.alignment, 8192); + EXPECT_EQ(new_info.alignment_offset, 2048); + + new_info.size += 4096; + ASSERT_FALSE(builder->UpdateBlockDeviceInfo(new_info)); + ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info)); + EXPECT_EQ(new_info.size, 1024 * 1024); + + new_info.logical_block_size = 512; + ASSERT_FALSE(builder->UpdateBlockDeviceInfo(new_info)); + ASSERT_TRUE(builder->GetBlockDeviceInfo(&new_info)); + EXPECT_EQ(new_info.logical_block_size, 4096); } TEST(liblp, InvalidBlockSize) { diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp index 511f7be1b..87169881d 100644 --- a/fs_mgr/liblp/images.cpp +++ b/fs_mgr/liblp/images.cpp @@ -167,16 +167,12 @@ bool SparseBuilder::Build() { std::string metadata_blob = SerializeMetadata(metadata_); metadata_blob.resize(geometry_.metadata_max_size); - std::string all_metadata; - for (size_t i = 0; i < geometry_.metadata_slot_count; i++) { - all_metadata += metadata_blob; + // Two copies of geometry, then two copies of each metadata slot. + all_metadata_ += geometry_blob + geometry_blob; + for (size_t i = 0; i < geometry_.metadata_slot_count * 2; i++) { + all_metadata_ += metadata_blob; } - - // Metadata immediately follows geometry, and we write the same metadata - // to all slots. Note that we don't bother trying to write skip chunks - // here since it's a small amount of data. - primary_blob_ = geometry_blob + all_metadata; - if (!AddData(primary_blob_, 0)) { + if (!AddData(all_metadata_, 0)) { return false; } @@ -195,17 +191,6 @@ bool SparseBuilder::Build() { LERROR << "Partition image was specified but no partition was found."; return false; } - - // The backup area contains all metadata slots, and then geometry. Similar - // to before we write the metadata to every slot. - int64_t backup_offset = GetBackupMetadataOffset(geometry_, 0); - int64_t backups_start = static_cast(geometry_.block_device_size) + backup_offset; - int64_t backup_sector = backups_start / LP_SECTOR_SIZE; - - backup_blob_ = all_metadata + geometry_blob; - if (!AddData(backup_blob_, backup_sector)) { - return false; - } return true; } diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h index 2031e3360..a9ef8ce17 100644 --- a/fs_mgr/liblp/images.h +++ b/fs_mgr/liblp/images.h @@ -56,8 +56,7 @@ class SparseBuilder { const LpMetadataGeometry& geometry_; uint32_t block_size_; std::unique_ptr file_; - std::string primary_blob_; - std::string backup_blob_; + std::string all_metadata_; std::map images_; std::vector temp_fds_; }; diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h index a6044d0a2..8dbba8449 100644 --- a/fs_mgr/liblp/include/liblp/builder.h +++ b/fs_mgr/liblp/include/liblp/builder.h @@ -215,10 +215,8 @@ class MetadataBuilder { uint64_t AllocatableSpace() const; uint64_t UsedSpace() 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_; } + bool GetBlockDeviceInfo(BlockDeviceInfo* info) const; + bool UpdateBlockDeviceInfo(const BlockDeviceInfo& info); private: MetadataBuilder(); @@ -238,7 +236,6 @@ class MetadataBuilder { LpMetadataHeader header_; std::vector> partitions_; std::vector> groups_; - BlockDeviceInfo device_info_; }; // Read BlockDeviceInfo for a given block device. This always returns false diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h index 7d1a2a9ce..a4ff1a732 100644 --- a/fs_mgr/liblp/include/liblp/metadata_format.h +++ b/fs_mgr/liblp/include/liblp/metadata_format.h @@ -38,7 +38,7 @@ extern "C" { #define LP_METADATA_HEADER_MAGIC 0x414C5030 /* Current metadata version. */ -#define LP_METADATA_MAJOR_VERSION 3 +#define LP_METADATA_MAJOR_VERSION 4 #define LP_METADATA_MINOR_VERSION 0 /* Attributes for the LpMetadataPartition::attributes field. @@ -58,13 +58,13 @@ extern "C" { * +--------------------+ * | Disk Geometry | * +--------------------+ - * | Metadata | + * | Geometry Backup | * +--------------------+ - * | Logical Partitions | + * | Metadata | * +--------------------+ * | Backup Metadata | * +--------------------+ - * | Geometry Backup | + * | Logical Partitions | * +--------------------+ */ #define LP_METADATA_DEFAULT_PARTITION_NAME "super" @@ -72,8 +72,8 @@ extern "C" { /* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */ #define LP_SECTOR_SIZE 512 -/* This structure is stored at sector 0 in the first 4096 bytes of the - * partition, and again in the very last 4096 bytes. It is never modified and +/* This structure is stored at block 0 in the first 4096 bytes of the + * partition, and again in the following block. It is never modified and * describes how logical partition information can be located. */ typedef struct LpMetadataGeometry { @@ -99,8 +99,8 @@ typedef struct LpMetadataGeometry { uint32_t metadata_slot_count; /* 48: First usable sector for allocating logical partitions. this will be - * the first sector after the initial 4096 geometry block, followed by the - * space consumed by metadata_max_size*metadata_slot_count. + * the first sector after the initial geometry blocks, followed by the + * space consumed by metadata_max_size*metadata_slot_count*2. */ uint64_t first_logical_sector; diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp index 322219bc9..220d651d7 100644 --- a/fs_mgr/liblp/io_test.cpp +++ b/fs_mgr/liblp/io_test.cpp @@ -290,13 +290,13 @@ TEST(liblp, ReadBackupGeometry) { char corruption[LP_METADATA_GEOMETRY_SIZE]; memset(corruption, 0xff, sizeof(corruption)); - // Corrupt the first 4096 bytes of the disk. - ASSERT_GE(lseek(fd, 0, SEEK_SET), 0); + // 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); - // Corrupt the last 4096 bytes too. - ASSERT_GE(lseek(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END), 0); + // 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); } @@ -310,14 +310,16 @@ TEST(liblp, ReadBackupMetadata) { char corruption[kMetadataSize]; memset(corruption, 0xff, sizeof(corruption)); - ASSERT_GE(lseek(fd, LP_METADATA_GEOMETRY_SIZE, SEEK_SET), 0); + off_t offset = GetPrimaryMetadataOffset(metadata->geometry, 0); + + ASSERT_GE(lseek(fd, offset, SEEK_SET), 0); ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption))); EXPECT_NE(ReadMetadata(fd, 0), nullptr); - off_t offset = LP_METADATA_GEOMETRY_SIZE + kMetadataSize * 2; + offset = GetBackupMetadataOffset(metadata->geometry, 0); // Corrupt the backup metadata. - ASSERT_GE(lseek(fd, -offset, SEEK_END), 0); + ASSERT_GE(lseek(fd, offset, SEEK_SET), 0); ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption))); EXPECT_EQ(ReadMetadata(fd, 0), nullptr); } diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp index 87411cb35..835df9bc8 100644 --- a/fs_mgr/liblp/reader.cpp +++ b/fs_mgr/liblp/reader.cpp @@ -121,9 +121,8 @@ bool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) { } bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry) { - // Read the first 4096 bytes. std::unique_ptr buffer = std::make_unique(LP_METADATA_GEOMETRY_SIZE); - if (SeekFile64(fd, 0, SEEK_SET) < 0) { + if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed"; return false; } @@ -135,9 +134,8 @@ bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry) { } bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry) { - // Try the backup copy in the last 4096 bytes. std::unique_ptr buffer = std::make_unique(LP_METADATA_GEOMETRY_SIZE); - if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) { + if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE; return false; } @@ -323,7 +321,7 @@ std::unique_ptr ReadPrimaryMetadata(int fd, const LpMetadataGeometry std::unique_ptr ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number) { int64_t offset = GetBackupMetadataOffset(geometry, slot_number); - if (SeekFile64(fd, offset, SEEK_END) < 0) { + if (SeekFile64(fd, offset, SEEK_SET) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset; return nullptr; } diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp index b08f96cc2..2f7692f5b 100644 --- a/fs_mgr/liblp/utility.cpp +++ b/fs_mgr/liblp/utility.cpp @@ -56,10 +56,18 @@ int64_t SeekFile64(int fd, int64_t offset, int whence) { return lseek(fd, offset, whence); } +int64_t GetPrimaryGeometryOffset() { + return 0; +} + +int64_t GetBackupGeometryOffset() { + return LP_METADATA_GEOMETRY_SIZE; +} + int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) { CHECK(slot_number < geometry.metadata_slot_count); - int64_t offset = LP_METADATA_GEOMETRY_SIZE + geometry.metadata_max_size * slot_number; + int64_t offset = (LP_METADATA_GEOMETRY_SIZE * 2) + geometry.metadata_max_size * slot_number; CHECK(offset + geometry.metadata_max_size <= int64_t(geometry.first_logical_sector * LP_SECTOR_SIZE)); return offset; @@ -67,7 +75,7 @@ int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t sl int64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) { CHECK(slot_number < geometry.metadata_slot_count); - int64_t start = int64_t(-LP_METADATA_GEOMETRY_SIZE) - + int64_t start = LP_METADATA_GEOMETRY_SIZE * 2 + int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count; return start + int64_t(geometry.metadata_max_size * slot_number); } diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h index 6ef512479..61e7d316c 100644 --- a/fs_mgr/liblp/utility.h +++ b/fs_mgr/liblp/utility.h @@ -38,6 +38,10 @@ namespace fs_mgr { // error. After calling this, the position of |fd| may have changed. bool GetDescriptorSize(int fd, uint64_t* size); +// Return the offset of the primary or backup geometry. +int64_t GetPrimaryGeometryOffset(); +int64_t GetBackupGeometryOffset(); + // Return the offset of a primary metadata slot, relative to the start of the // device. int64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number); diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp index 7bf42aed9..ff50e0956 100644 --- a/fs_mgr/liblp/utility_test.cpp +++ b/fs_mgr/liblp/utility_test.cpp @@ -42,15 +42,17 @@ TEST(liblp, GetMetadataOffset) { 0, 1024 * 1024, 4096}; - EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 4096); - EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 4096 + 16384); - EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 4096 + 16384 * 2); - EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), 4096 + 16384 * 3); + EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), 8192); + EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), 8192 + 16384); + EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), 8192 + 16384 * 2); + EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), 8192 + 16384 * 3); - EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), -4096 - 16384 * 1); - EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), -4096 - 16384 * 2); - EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), -4096 - 16384 * 3); - EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), -4096 - 16384 * 4); + static const uint64_t backup_start = 8192 + 16384 * 4; + + EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), backup_start + 16384 * 3); + EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), backup_start + 16384 * 2); + EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), backup_start + 16384 * 1); + EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0); } TEST(liblp, AlignTo) { diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp index 241562999..0c0cc494f 100644 --- a/fs_mgr/liblp/writer.cpp +++ b/fs_mgr/liblp/writer.cpp @@ -106,15 +106,13 @@ static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std // metadata. uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE + uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count; - if (reserved_size > blockdevice_size || - reserved_size > geometry.first_logical_sector * LP_SECTOR_SIZE) { + uint64_t total_reserved = reserved_size * 2; + + if (total_reserved > blockdevice_size || + total_reserved > geometry.first_logical_sector * LP_SECTOR_SIZE) { LERROR << "Not enough space to store all logical partition metadata slots."; return false; } - if (blockdevice_size - reserved_size < (geometry.last_logical_sector + 1) * LP_SECTOR_SIZE) { - LERROR << "Not enough space to backup all logical partition metadata slots."; - return false; - } if (blockdevice_size != metadata.geometry.block_device_size) { LERROR << "Block device size " << blockdevice_size << " does not match metadata requested size " << metadata.geometry.block_device_size; @@ -162,12 +160,12 @@ static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint const std::string& blob, const std::function& writer) { int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number); - int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_END); + int64_t abs_offset = SeekFile64(fd, backup_offset, SEEK_SET); if (abs_offset == (int64_t)-1) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << backup_offset; return false; } - if (abs_offset < int64_t((geometry.last_logical_sector + 1) * LP_SECTOR_SIZE)) { + if (abs_offset >= int64_t(geometry.first_logical_sector) * LP_SECTOR_SIZE) { PERROR << __PRETTY_FUNCTION__ << "backup offset " << abs_offset << " is within logical partition bounds, sector " << geometry.last_logical_sector; return false; @@ -211,7 +209,7 @@ bool FlashPartitionTable(int fd, const LpMetadata& metadata) { // Write geometry to the first and last 4096 bytes of the device. std::string blob = SerializeGeometry(metadata.geometry); - if (SeekFile64(fd, 0, SEEK_SET) < 0) { + if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0"; return false; } @@ -219,7 +217,7 @@ bool FlashPartitionTable(int fd, const LpMetadata& metadata) { PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed"; return false; } - if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) { + if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE; return false; }