diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp index 4007ad9f7..1e031ad81 100644 --- a/fs_mgr/liblp/builder.cpp +++ b/fs_mgr/liblp/builder.cpp @@ -150,7 +150,7 @@ std::unique_ptr MetadataBuilder::New(const LpMetadata& metadata return builder; } -MetadataBuilder::MetadataBuilder() { +MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) { memset(&geometry_, 0, sizeof(geometry_)); geometry_.magic = LP_METADATA_GEOMETRY_MAGIC; geometry_.struct_size = sizeof(geometry_); @@ -564,7 +564,12 @@ std::unique_ptr MetadataBuilder::Export() { metadata->geometry = geometry_; // Assign this early so the extent table can read it. - metadata->block_devices = block_devices_; + for (const auto& block_device : block_devices_) { + metadata->block_devices.emplace_back(block_device); + if (auto_slot_suffixing_) { + metadata->block_devices.back().flags |= LP_BLOCK_DEVICE_SLOT_SUFFIXED; + } + } std::map group_indices; for (const auto& group : groups_) { @@ -600,6 +605,9 @@ std::unique_ptr MetadataBuilder::Export() { part.first_extent_index = static_cast(metadata->extents.size()); part.num_extents = static_cast(partition->extents().size()); part.attributes = partition->attributes(); + if (auto_slot_suffixing_) { + part.attributes |= LP_PARTITION_ATTR_SLOT_SUFFIXED; + } auto iter = group_indices.find(partition->group_name()); if (iter == group_indices.end()) { @@ -836,5 +844,9 @@ bool MetadataBuilder::ImportPartition(const LpMetadata& metadata, return true; } +void MetadataBuilder::SetAutoSlotSuffixing() { + auto_slot_suffixing_ = true; +} + } // namespace fs_mgr } // namespace android diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h index d5e3fed61..47ebf6d3e 100644 --- a/fs_mgr/liblp/include/liblp/builder.h +++ b/fs_mgr/liblp/include/liblp/builder.h @@ -223,6 +223,9 @@ class MetadataBuilder { // Remove all partitions belonging to a group, then remove the group. void RemoveGroupAndPartitions(const std::string& group_name); + // Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag. + void SetAutoSlotSuffixing(); + bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const; bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info); @@ -275,6 +278,7 @@ class MetadataBuilder { std::vector> partitions_; std::vector> groups_; std::vector block_devices_; + bool auto_slot_suffixing_; }; // Read BlockDeviceInfo for a given block device. This always returns false diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h index 8723a7fe6..56332ab9b 100644 --- a/fs_mgr/liblp/include/liblp/liblp.h +++ b/fs_mgr/liblp/include/liblp/liblp.h @@ -95,6 +95,7 @@ std::vector GetBlockDevicePartitionNames(const LpMetadata& metadata // Slot suffix helpers. uint32_t SlotNumberForSlotSuffix(const std::string& suffix); +std::string SlotSuffixForSlotNumber(uint32_t slot_number); std::string GetPartitionSlotSuffix(const std::string& partition_name); } // namespace fs_mgr diff --git a/fs_mgr/liblp/include/liblp/metadata_format.h b/fs_mgr/liblp/include/liblp/metadata_format.h index 1e40df3cf..c2e25fbfb 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 8 +#define LP_METADATA_MAJOR_VERSION 9 #define LP_METADATA_MINOR_VERSION 0 /* Attributes for the LpMetadataPartition::attributes field. @@ -47,10 +47,19 @@ extern "C" { * device mapper, the block device will be created as read-only. */ #define LP_PARTITION_ATTR_NONE 0x0 -#define LP_PARTITION_ATTR_READONLY 0x1 +#define LP_PARTITION_ATTR_READONLY (1 << 0) + +/* This flag is only intended to be used with super_empty.img and super.img on + * retrofit devices. On these devices there are A and B super partitions, and + * we don't know ahead of time which slot the image will be applied to. + * + * If set, the partition name needs a slot suffix applied. The slot suffix is + * determined by the metadata slot number (0 = _a, 1 = _b). + */ +#define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1) /* Mask that defines all valid attributes. */ -#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY) +#define LP_PARTITION_ATTRIBUTE_MASK (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED) /* Default name of the physical partition that holds logical partition entries. * The layout of this partition will look like: @@ -302,8 +311,21 @@ typedef struct LpMetadataBlockDevice { /* 24: Partition name in the GPT. Any unused characters must be 0. */ char partition_name[36]; + + /* 60: Flags (see LP_BLOCK_DEVICE_* flags below). */ + uint32_t flags; } LpMetadataBlockDevice; +/* This flag is only intended to be used with super_empty.img and super.img on + * retrofit devices. On these devices there are A and B super partitions, and + * we don't know ahead of time which slot the image will be applied to. + * + * If set, the block device needs a slot suffix applied before being used with + * IPartitionOpener. The slot suffix is determined by the metadata slot number + * (0 = _a, 1 = _b). + */ +#define LP_BLOCK_DEVICE_SLOT_SUFFIXED (1 << 0) + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp index 603e5c046..459cf828a 100644 --- a/fs_mgr/liblp/io_test.cpp +++ b/fs_mgr/liblp/io_test.cpp @@ -88,11 +88,14 @@ static bool AddDefaultPartitions(MetadataBuilder* builder) { } // Create a temporary disk and flash it with the default partition setup. -static unique_fd CreateFlashedDisk() { +static unique_fd CreateFlashedDisk(bool auto_slot_suffix = false) { unique_ptr builder = CreateDefaultBuilder(); if (!builder || !AddDefaultPartitions(builder.get())) { return {}; } + if (auto_slot_suffix) { + builder->SetAutoSlotSuffixing(); + } unique_fd fd = CreateFakeDisk(); if (fd < 0) { return {}; @@ -608,3 +611,24 @@ TEST(liblp, FlashSparseImage) { ASSERT_NE(ReadPrimaryMetadata(fd.get(), geometry, 0), nullptr); ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr); } + +TEST(liblp, AutoSlotSuffixing) { + auto fd = CreateFlashedDisk(true); + ASSERT_GE(fd, 0); + + TestPartitionOpener opener({{"super", fd}}); + + auto metadata = ReadMetadata(opener, "super", 1); + ASSERT_NE(metadata, nullptr); + ASSERT_EQ(metadata->partitions.size(), static_cast(1)); + EXPECT_EQ(GetPartitionName(metadata->partitions[0]), "system_b"); + ASSERT_EQ(metadata->block_devices.size(), static_cast(1)); + EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "super_b"); + + metadata = ReadMetadata(opener, "super", 0); + ASSERT_NE(metadata, nullptr); + ASSERT_EQ(metadata->partitions.size(), static_cast(1)); + EXPECT_EQ(GetPartitionName(metadata->partitions[0]), "system_a"); + ASSERT_EQ(metadata->block_devices.size(), static_cast(1)); + EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "super_a"); +} diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp index a02e746aa..3319956dd 100644 --- a/fs_mgr/liblp/reader.cpp +++ b/fs_mgr/liblp/reader.cpp @@ -348,6 +348,40 @@ std::unique_ptr ReadBackupMetadata(int fd, const LpMetadataGeometry& return ParseMetadata(geometry, fd); } +namespace { + +bool AdjustMetadataForSlot(LpMetadata* metadata, uint32_t slot_number) { + std::string slot_suffix = SlotSuffixForSlotNumber(slot_number); + for (auto& partition : metadata->partitions) { + if (!(partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED)) { + continue; + } + std::string partition_name = GetPartitionName(partition) + slot_suffix; + if (partition_name.size() > sizeof(partition.name)) { + LERROR << __PRETTY_FUNCTION__ << " partition name too long: " << partition_name; + return false; + } + strncpy(partition.name, partition_name.c_str(), sizeof(partition.name)); + partition.attributes &= ~LP_PARTITION_ATTR_SLOT_SUFFIXED; + } + for (auto& block_device : metadata->block_devices) { + if (!(block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED)) { + continue; + } + std::string partition_name = GetBlockDevicePartitionName(block_device) + slot_suffix; + if (partition_name.size() > sizeof(block_device.partition_name)) { + LERROR << __PRETTY_FUNCTION__ << " partition name too long: " << partition_name; + return false; + } + strncpy(block_device.partition_name, partition_name.c_str(), + sizeof(block_device.partition_name)); + block_device.flags &= ~LP_BLOCK_DEVICE_SLOT_SUFFIXED; + } + return true; +} + +} // namespace + std::unique_ptr ReadMetadata(const IPartitionOpener& opener, const std::string& super_partition, uint32_t slot_number) { android::base::unique_fd fd = opener.Open(super_partition, O_RDONLY); @@ -360,18 +394,30 @@ std::unique_ptr ReadMetadata(const IPartitionOpener& opener, if (!ReadLogicalPartitionGeometry(fd, &geometry)) { return nullptr; } - if (slot_number >= geometry.metadata_slot_count) { LERROR << __PRETTY_FUNCTION__ << " invalid metadata slot number"; return nullptr; } - // Read the primary copy, and if that fails, try the backup. - std::unique_ptr metadata = ReadPrimaryMetadata(fd, geometry, slot_number); - if (metadata) { - return metadata; + std::vector offsets = { + GetPrimaryMetadataOffset(geometry, slot_number), + GetBackupMetadataOffset(geometry, slot_number), + }; + std::unique_ptr metadata; + + for (const auto& offset : offsets) { + if (SeekFile64(fd, offset, SEEK_SET) < 0) { + PERROR << __PRETTY_FUNCTION__ << " lseek failed, offset " << offset; + continue; + } + if ((metadata = ParseMetadata(geometry, fd)) != nullptr) { + break; + } } - return ReadBackupMetadata(fd, geometry, slot_number); + if (!metadata || !AdjustMetadataForSlot(metadata.get(), slot_number)) { + return nullptr; + } + return metadata; } std::unique_ptr ReadMetadata(const std::string& super_partition, uint32_t slot_number) { diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h index d5d518888..7a2490b9b 100644 --- a/fs_mgr/liblp/reader.h +++ b/fs_mgr/liblp/reader.h @@ -38,7 +38,9 @@ bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry); bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry); bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry); -// These functions assume a valid geometry and slot number. +// These functions assume a valid geometry and slot number, and do not obey +// auto-slot-suffixing. They are used for tests and for checking whether +// the metadata is coherent across primary and backup copies. std::unique_ptr ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number); std::unique_ptr ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry, diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp index cce90a308..2d34ce50d 100644 --- a/fs_mgr/liblp/utility.cpp +++ b/fs_mgr/liblp/utility.cpp @@ -132,5 +132,10 @@ std::string GetPartitionSlotSuffix(const std::string& partition_name) { return (suffix == "_a" || suffix == "_b") ? suffix : ""; } +std::string SlotSuffixForSlotNumber(uint32_t slot_number) { + CHECK(slot_number == 0 || slot_number == 1); + return (slot_number == 0) ? "_a" : "_b"; +} + } // namespace fs_mgr } // namespace android diff --git a/fs_mgr/liblp/utility_test.cpp b/fs_mgr/liblp/utility_test.cpp index 0fa4590ee..15f7fff4e 100644 --- a/fs_mgr/liblp/utility_test.cpp +++ b/fs_mgr/liblp/utility_test.cpp @@ -32,6 +32,11 @@ TEST(liblp, SlotNumberForSlotSuffix) { EXPECT_EQ(SlotNumberForSlotSuffix("_d"), 0); } +TEST(liblp, SlotSuffixForSlotNumber) { + EXPECT_EQ(SlotSuffixForSlotNumber(0), "_a"); + EXPECT_EQ(SlotSuffixForSlotNumber(1), "_b"); +} + TEST(liblp, GetMetadataOffset) { LpMetadataGeometry geometry = {LP_METADATA_GEOMETRY_MAGIC, sizeof(geometry), diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp index f4c9b991a..e72cdfa16 100644 --- a/fs_mgr/liblp/writer.cpp +++ b/fs_mgr/liblp/writer.cpp @@ -83,7 +83,7 @@ std::string SerializeMetadata(const LpMetadata& input) { // Perform sanity checks so we don't accidentally overwrite valid metadata // with potentially invalid metadata, or random partition data with metadata. static bool ValidateAndSerializeMetadata(const IPartitionOpener& opener, const LpMetadata& metadata, - std::string* blob) { + const std::string& slot_suffix, std::string* blob) { const LpMetadataHeader& header = metadata.header; const LpMetadataGeometry& geometry = metadata.geometry; @@ -114,6 +114,15 @@ static bool ValidateAndSerializeMetadata(const IPartitionOpener& opener, const L } for (const auto& block_device : metadata.block_devices) { std::string partition_name = GetBlockDevicePartitionName(block_device); + if (block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED) { + if (slot_suffix.empty()) { + LERROR << "Block device " << partition_name << " requires a slot suffix," + << " which could not be derived from the super partition name."; + return false; + } + partition_name += slot_suffix; + } + if ((block_device.first_logical_sector + 1) * LP_SECTOR_SIZE > block_device.size) { LERROR << "Block device " << partition_name << " has invalid first sector " << block_device.first_logical_sector << " for size " << block_device.size; @@ -234,11 +243,16 @@ bool FlashPartitionTable(const IPartitionOpener& opener, const std::string& supe return false; } + // This is only used in update_engine and fastbootd, where the super + // partition should be specified as a name (or by-name link), and + // therefore, we should be able to extract a slot suffix. + std::string slot_suffix = GetPartitionSlotSuffix(super_partition); + // 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. std::string metadata_blob; - if (!ValidateAndSerializeMetadata(opener, metadata, &metadata_blob)) { + if (!ValidateAndSerializeMetadata(opener, metadata, slot_suffix, &metadata_blob)) { return false; } @@ -299,11 +313,13 @@ bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& sup return false; } + std::string slot_suffix = SlotSuffixForSlotNumber(slot_number); + // 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. std::string blob; - if (!ValidateAndSerializeMetadata(opener, metadata, &blob)) { + if (!ValidateAndSerializeMetadata(opener, metadata, slot_suffix, &blob)) { return false; } @@ -335,7 +351,7 @@ bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& sup // synchronize the backup copy. This guarantees that a partial write // still leaves one copy intact. std::string old_blob; - if (!ValidateAndSerializeMetadata(opener, *primary.get(), &old_blob)) { + if (!ValidateAndSerializeMetadata(opener, *primary.get(), slot_suffix, &old_blob)) { LERROR << "Error serializing primary metadata to repair corrupted backup"; return false; } @@ -347,7 +363,7 @@ bool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& sup // The backup copy is coherent, and the primary is not. Sync it for // safety. std::string old_blob; - if (!ValidateAndSerializeMetadata(opener, *backup.get(), &old_blob)) { + if (!ValidateAndSerializeMetadata(opener, *backup.get(), slot_suffix, &old_blob)) { LERROR << "Error serializing primary metadata to repair corrupted backup"; return false; }