Loading fs_mgr/liblp/include/liblp/reader.h +7 −2 Original line number Original line Diff line number Diff line Loading @@ -31,10 +31,15 @@ namespace fs_mgr { std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number); std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number); std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number); std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number); // Read and validate the logical partition geometry from a block device. // Helper functions for manually reading geometry and metadata. bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry); bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry); bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry); // These functions assume a valid geometry and slot number. std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number); std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number); // Read logical partition metadata from an image file that was created with // Read logical partition metadata from an image file that was created with // WriteToImageFile(). // WriteToImageFile(). std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file); std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file); Loading fs_mgr/liblp/include/liblp/writer.h +1 −1 Original line number Original line Diff line number Diff line Loading @@ -45,7 +45,7 @@ bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_numbe bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number); bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number); bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number, bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number, std::function<bool(int, const std::string&)> writer); const std::function<bool(int, const std::string&)>& writer); // Helper function to serialize geometry and metadata to a normal file, for // Helper function to serialize geometry and metadata to a normal file, for // flashing or debugging. // flashing or debugging. Loading fs_mgr/liblp/io_test.cpp +59 −14 Original line number Original line Diff line number Diff line Loading @@ -399,29 +399,42 @@ class BadWriter { // When requested, write garbage instead of the requested bytes, then // When requested, write garbage instead of the requested bytes, then // return false. // return false. bool operator()(int fd, const std::string& blob) { bool operator()(int fd, const std::string& blob) { if (++write_count_ == fail_on_write_) { write_count_++; if (write_count_ == fail_on_write_) { std::unique_ptr<char[]> new_data = std::make_unique<char[]>(blob.size()); std::unique_ptr<char[]> new_data = std::make_unique<char[]>(blob.size()); memset(new_data.get(), 0xe5, blob.size()); memset(new_data.get(), 0xe5, blob.size()); EXPECT_TRUE(android::base::WriteFully(fd, new_data.get(), blob.size())); EXPECT_TRUE(android::base::WriteFully(fd, new_data.get(), blob.size())); return false; return false; } else { } else { return android::base::WriteFully(fd, blob.data(), blob.size()); if (!android::base::WriteFully(fd, blob.data(), blob.size())) { return false; } return fail_after_write_ != write_count_; } } } } void Reset() { fail_on_write_ = 0; fail_after_write_ = 0; write_count_ = 0; } void FailOnWrite(int number) { void FailOnWrite(int number) { Reset(); fail_on_write_ = number; fail_on_write_ = number; write_count_ = 0; } void FailAfterWrite(int number) { Reset(); fail_after_write_ = number; } } private: private: int fail_on_write_ = 0; int fail_on_write_ = 0; int fail_after_write_ = 0; int write_count_ = 0; int write_count_ = 0; }; }; // Test that an interrupted flash operation on the "primary" copy of metadata // Test that an interrupted flash operation on the "primary" copy of metadata // is not fatal. // is not fatal. TEST(liblp, FlashPrimaryMetadataFailure) { TEST(liblp, UpdatePrimaryMetadataFailure) { // Initial state. unique_fd fd = CreateFlashedDisk(); unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); ASSERT_GE(fd, 0); Loading @@ -439,7 +452,7 @@ TEST(liblp, FlashPrimaryMetadataFailure) { // Flash again, this time fail the backup copy. We should still be able // Flash again, this time fail the backup copy. We should still be able // to read the primary. // to read the primary. writer.FailOnWrite(2); writer.FailOnWrite(3); ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer)); ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer)); imported = ReadMetadata(fd, 0); imported = ReadMetadata(fd, 0); ASSERT_NE(imported, nullptr); ASSERT_NE(imported, nullptr); Loading @@ -447,8 +460,7 @@ TEST(liblp, FlashPrimaryMetadataFailure) { // Test that an interrupted flash operation on the "backup" copy of metadata // Test that an interrupted flash operation on the "backup" copy of metadata // is not fatal. // is not fatal. TEST(liblp, FlashBackupMetadataFailure) { TEST(liblp, UpdateBackupMetadataFailure) { // Initial state. unique_fd fd = CreateFlashedDisk(); unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); ASSERT_GE(fd, 0); Loading @@ -466,12 +478,45 @@ TEST(liblp, FlashBackupMetadataFailure) { // Flash again, this time fail the primary copy. We should still be able // Flash again, this time fail the primary copy. We should still be able // to read the primary. // to read the primary. // writer.FailOnWrite(2); // TODO(dvander): This is currently not handled correctly. liblp does not // guarantee both copies are in sync before the update. The ASSERT_EQ // will change to an ASSERT_NE when this is fixed. writer.FailOnWrite(1); ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer)); ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer)); imported = ReadMetadata(fd, 0); imported = ReadMetadata(fd, 0); ASSERT_EQ(imported, nullptr); ASSERT_NE(imported, nullptr); } // Test that an interrupted write *in between* writing metadata will read // the correct metadata copy. The primary is always considered newer than // the backup. TEST(liblp, UpdateMetadataCleanFailure) { unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); BadWriter writer; // Change the name of the existing partition. unique_ptr<LpMetadata> new_table = ReadMetadata(fd, 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)); // When we read back, we should get the updated primary copy. unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0); ASSERT_NE(imported, nullptr); ASSERT_GE(new_table->partitions.size(), 1); ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0])); // Flash again. After, the backup and primary copy should be coherent. // 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)); imported = ReadMetadata(fd, 0); ASSERT_NE(imported, nullptr); ASSERT_GE(new_table->partitions.size(), 1); ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0])); } } fs_mgr/liblp/reader.cpp +23 −26 Original line number Original line Diff line number Diff line Loading @@ -111,16 +111,6 @@ bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) { return ParseGeometry(buffer.get(), geometry); return ParseGeometry(buffer.get(), geometry); } } // Helper function to read geometry from a device without an open descriptor. bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry) { android::base::unique_fd fd(open(block_device, O_RDONLY)); if (fd < 0) { PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device; return false; } return ReadLogicalPartitionGeometry(fd, geometry); } static bool ValidateTableBounds(const LpMetadataHeader& header, static bool ValidateTableBounds(const LpMetadataHeader& header, const LpMetadataTableDescriptor& table) { const LpMetadataTableDescriptor& table) { if (table.offset > header.tables_size) { if (table.offset > header.tables_size) { Loading Loading @@ -243,6 +233,26 @@ static std::unique_ptr<LpMetadata> ParseMetadata(int fd) { return metadata; return metadata; } } std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number) { int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number); if (SeekFile64(fd, offset, SEEK_SET) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset; return nullptr; } return ParseMetadata(fd); } std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number) { int64_t offset = GetBackupMetadataOffset(geometry, slot_number); if (SeekFile64(fd, offset, SEEK_END) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset; return nullptr; } return ParseMetadata(fd); } std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) { std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) { LpMetadataGeometry geometry; LpMetadataGeometry geometry; if (!ReadLogicalPartitionGeometry(fd, &geometry)) { if (!ReadLogicalPartitionGeometry(fd, &geometry)) { Loading @@ -254,24 +264,11 @@ std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) { return nullptr; return nullptr; } } // First try the primary copy. // Read the priamry copy, and if that fails, try the backup. int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number); std::unique_ptr<LpMetadata> metadata = ReadPrimaryMetadata(fd, geometry, slot_number); if (SeekFile64(fd, offset, SEEK_SET) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset; return nullptr; } std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd); // If the primary copy failed, try the backup copy. if (!metadata) { if (!metadata) { offset = GetBackupMetadataOffset(geometry, slot_number); metadata = ReadBackupMetadata(fd, geometry, slot_number); if (SeekFile64(fd, offset, SEEK_END) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset; return nullptr; } } metadata = ParseMetadata(fd); } if (metadata) { if (metadata) { metadata->geometry = geometry; metadata->geometry = geometry; } } Loading fs_mgr/liblp/writer.cpp +74 −16 Original line number Original line Diff line number Diff line Loading @@ -130,20 +130,9 @@ static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std return true; return true; } } static bool DefaultWriter(int fd, const std::string& blob) { static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number, return android::base::WriteFully(fd, blob.data(), blob.size()); } static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number, const std::string& blob, const std::string& blob, std::function<bool(int, const std::string&)> writer) { const std::function<bool(int, const std::string&)>& writer) { // Make sure we're writing to a valid metadata slot. if (slot_number >= geometry.metadata_slot_count) { LERROR << "Invalid logical partition metadata slot number."; return false; } // Write the primary copy of the metadata. int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number); int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number); if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) { if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset; PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset; Loading @@ -153,8 +142,12 @@ static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t s PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed"; PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed"; return false; return false; } } return true; } // Write the backup copy of the metadata. static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number, const std::string& blob, const std::function<bool(int, const std::string&)>& writer) { int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number); 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_END); if (abs_offset == (int64_t)-1) { if (abs_offset == (int64_t)-1) { Loading @@ -173,6 +166,27 @@ static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t s return true; return true; } } static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number, const std::string& blob, const std::function<bool(int, const std::string&)>& writer) { // Make sure we're writing to a valid metadata slot. if (slot_number >= geometry.metadata_slot_count) { LERROR << "Invalid logical partition metadata slot number."; return false; } if (!WritePrimaryMetadata(fd, geometry, slot_number, blob, writer)) { return false; } if (!WriteBackupMetadata(fd, geometry, slot_number, blob, writer)) { return false; } return true; } 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, uint32_t slot_number) { bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) { // Before writing geometry and/or logical partition tables, perform some // Before writing geometry and/or logical partition tables, perform some // basic checks that the geometry and tables are coherent, and will fit // basic checks that the geometry and tables are coherent, and will fit Loading Loading @@ -205,8 +219,13 @@ bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_numbe return WriteMetadata(fd, metadata.geometry, slot_number, metadata_blob, DefaultWriter); return WriteMetadata(fd, metadata.geometry, slot_number, metadata_blob, DefaultWriter); } } 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(int fd, const LpMetadata& metadata, uint32_t slot_number, std::function<bool(int, const std::string&)> writer) { const std::function<bool(int, const std::string&)>& writer) { // Before writing geometry and/or logical partition tables, perform some // Before writing geometry and/or logical partition tables, perform some // basic checks that the geometry and tables are coherent, and will fit // basic checks that the geometry and tables are coherent, and will fit // on the given block device. // on the given block device. Loading @@ -227,6 +246,45 @@ bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_numb LERROR << "Incompatible geometry in new logical partition metadata"; LERROR << "Incompatible geometry in new logical partition metadata"; return false; return false; } } // Validate the slot number now, before we call Read*Metadata. if (slot_number >= geometry.metadata_slot_count) { LERROR << "Invalid logical partition metadata slot number."; return false; } // Try to read both existing copies of the metadata, if any. std::unique_ptr<LpMetadata> primary = ReadPrimaryMetadata(fd, geometry, slot_number); std::unique_ptr<LpMetadata> backup = ReadBackupMetadata(fd, geometry, slot_number); if (primary && (!backup || !CompareMetadata(*primary.get(), *backup.get()))) { // If the backup copy does not match the primary copy, we first // synchronize the backup copy. This guarantees that a partial write // still leaves one copy intact. std::string old_blob; if (!ValidateAndSerializeMetadata(fd, *primary.get(), &old_blob)) { LERROR << "Error serializing primary metadata to repair corrupted backup"; return false; } if (!WriteBackupMetadata(fd, geometry, slot_number, old_blob, writer)) { LERROR << "Error writing primary metadata to repair corrupted backup"; return false; } } else if (backup && !primary) { // The backup copy is coherent, and the primary is not. Sync it for // safety. std::string old_blob; if (!ValidateAndSerializeMetadata(fd, *backup.get(), &old_blob)) { LERROR << "Error serializing primary metadata to repair corrupted backup"; return false; } if (!WritePrimaryMetadata(fd, geometry, slot_number, old_blob, writer)) { LERROR << "Error writing primary metadata to repair corrupted backup"; return false; } } // Both copies should now be in sync, so we can continue the update. return WriteMetadata(fd, geometry, slot_number, blob, writer); return WriteMetadata(fd, geometry, slot_number, blob, writer); } } Loading Loading
fs_mgr/liblp/include/liblp/reader.h +7 −2 Original line number Original line Diff line number Diff line Loading @@ -31,10 +31,15 @@ namespace fs_mgr { std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number); std::unique_ptr<LpMetadata> ReadMetadata(const char* block_device, uint32_t slot_number); std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number); std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number); // Read and validate the logical partition geometry from a block device. // Helper functions for manually reading geometry and metadata. bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry); bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry); bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry); // These functions assume a valid geometry and slot number. std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number); std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number); // Read logical partition metadata from an image file that was created with // Read logical partition metadata from an image file that was created with // WriteToImageFile(). // WriteToImageFile(). std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file); std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file); Loading
fs_mgr/liblp/include/liblp/writer.h +1 −1 Original line number Original line Diff line number Diff line Loading @@ -45,7 +45,7 @@ bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_numbe bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number); bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number); bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number, bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number, std::function<bool(int, const std::string&)> writer); const std::function<bool(int, const std::string&)>& writer); // Helper function to serialize geometry and metadata to a normal file, for // Helper function to serialize geometry and metadata to a normal file, for // flashing or debugging. // flashing or debugging. Loading
fs_mgr/liblp/io_test.cpp +59 −14 Original line number Original line Diff line number Diff line Loading @@ -399,29 +399,42 @@ class BadWriter { // When requested, write garbage instead of the requested bytes, then // When requested, write garbage instead of the requested bytes, then // return false. // return false. bool operator()(int fd, const std::string& blob) { bool operator()(int fd, const std::string& blob) { if (++write_count_ == fail_on_write_) { write_count_++; if (write_count_ == fail_on_write_) { std::unique_ptr<char[]> new_data = std::make_unique<char[]>(blob.size()); std::unique_ptr<char[]> new_data = std::make_unique<char[]>(blob.size()); memset(new_data.get(), 0xe5, blob.size()); memset(new_data.get(), 0xe5, blob.size()); EXPECT_TRUE(android::base::WriteFully(fd, new_data.get(), blob.size())); EXPECT_TRUE(android::base::WriteFully(fd, new_data.get(), blob.size())); return false; return false; } else { } else { return android::base::WriteFully(fd, blob.data(), blob.size()); if (!android::base::WriteFully(fd, blob.data(), blob.size())) { return false; } return fail_after_write_ != write_count_; } } } } void Reset() { fail_on_write_ = 0; fail_after_write_ = 0; write_count_ = 0; } void FailOnWrite(int number) { void FailOnWrite(int number) { Reset(); fail_on_write_ = number; fail_on_write_ = number; write_count_ = 0; } void FailAfterWrite(int number) { Reset(); fail_after_write_ = number; } } private: private: int fail_on_write_ = 0; int fail_on_write_ = 0; int fail_after_write_ = 0; int write_count_ = 0; int write_count_ = 0; }; }; // Test that an interrupted flash operation on the "primary" copy of metadata // Test that an interrupted flash operation on the "primary" copy of metadata // is not fatal. // is not fatal. TEST(liblp, FlashPrimaryMetadataFailure) { TEST(liblp, UpdatePrimaryMetadataFailure) { // Initial state. unique_fd fd = CreateFlashedDisk(); unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); ASSERT_GE(fd, 0); Loading @@ -439,7 +452,7 @@ TEST(liblp, FlashPrimaryMetadataFailure) { // Flash again, this time fail the backup copy. We should still be able // Flash again, this time fail the backup copy. We should still be able // to read the primary. // to read the primary. writer.FailOnWrite(2); writer.FailOnWrite(3); ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer)); ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer)); imported = ReadMetadata(fd, 0); imported = ReadMetadata(fd, 0); ASSERT_NE(imported, nullptr); ASSERT_NE(imported, nullptr); Loading @@ -447,8 +460,7 @@ TEST(liblp, FlashPrimaryMetadataFailure) { // Test that an interrupted flash operation on the "backup" copy of metadata // Test that an interrupted flash operation on the "backup" copy of metadata // is not fatal. // is not fatal. TEST(liblp, FlashBackupMetadataFailure) { TEST(liblp, UpdateBackupMetadataFailure) { // Initial state. unique_fd fd = CreateFlashedDisk(); unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); ASSERT_GE(fd, 0); Loading @@ -466,12 +478,45 @@ TEST(liblp, FlashBackupMetadataFailure) { // Flash again, this time fail the primary copy. We should still be able // Flash again, this time fail the primary copy. We should still be able // to read the primary. // to read the primary. // writer.FailOnWrite(2); // TODO(dvander): This is currently not handled correctly. liblp does not // guarantee both copies are in sync before the update. The ASSERT_EQ // will change to an ASSERT_NE when this is fixed. writer.FailOnWrite(1); ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer)); ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 0, writer)); imported = ReadMetadata(fd, 0); imported = ReadMetadata(fd, 0); ASSERT_EQ(imported, nullptr); ASSERT_NE(imported, nullptr); } // Test that an interrupted write *in between* writing metadata will read // the correct metadata copy. The primary is always considered newer than // the backup. TEST(liblp, UpdateMetadataCleanFailure) { unique_fd fd = CreateFlashedDisk(); ASSERT_GE(fd, 0); BadWriter writer; // Change the name of the existing partition. unique_ptr<LpMetadata> new_table = ReadMetadata(fd, 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)); // When we read back, we should get the updated primary copy. unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0); ASSERT_NE(imported, nullptr); ASSERT_GE(new_table->partitions.size(), 1); ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0])); // Flash again. After, the backup and primary copy should be coherent. // 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)); imported = ReadMetadata(fd, 0); ASSERT_NE(imported, nullptr); ASSERT_GE(new_table->partitions.size(), 1); ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0])); } }
fs_mgr/liblp/reader.cpp +23 −26 Original line number Original line Diff line number Diff line Loading @@ -111,16 +111,6 @@ bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) { return ParseGeometry(buffer.get(), geometry); return ParseGeometry(buffer.get(), geometry); } } // Helper function to read geometry from a device without an open descriptor. bool ReadLogicalPartitionGeometry(const char* block_device, LpMetadataGeometry* geometry) { android::base::unique_fd fd(open(block_device, O_RDONLY)); if (fd < 0) { PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device; return false; } return ReadLogicalPartitionGeometry(fd, geometry); } static bool ValidateTableBounds(const LpMetadataHeader& header, static bool ValidateTableBounds(const LpMetadataHeader& header, const LpMetadataTableDescriptor& table) { const LpMetadataTableDescriptor& table) { if (table.offset > header.tables_size) { if (table.offset > header.tables_size) { Loading Loading @@ -243,6 +233,26 @@ static std::unique_ptr<LpMetadata> ParseMetadata(int fd) { return metadata; return metadata; } } std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number) { int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number); if (SeekFile64(fd, offset, SEEK_SET) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset; return nullptr; } return ParseMetadata(fd); } std::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number) { int64_t offset = GetBackupMetadataOffset(geometry, slot_number); if (SeekFile64(fd, offset, SEEK_END) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset; return nullptr; } return ParseMetadata(fd); } std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) { std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) { LpMetadataGeometry geometry; LpMetadataGeometry geometry; if (!ReadLogicalPartitionGeometry(fd, &geometry)) { if (!ReadLogicalPartitionGeometry(fd, &geometry)) { Loading @@ -254,24 +264,11 @@ std::unique_ptr<LpMetadata> ReadMetadata(int fd, uint32_t slot_number) { return nullptr; return nullptr; } } // First try the primary copy. // Read the priamry copy, and if that fails, try the backup. int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number); std::unique_ptr<LpMetadata> metadata = ReadPrimaryMetadata(fd, geometry, slot_number); if (SeekFile64(fd, offset, SEEK_SET) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset; return nullptr; } std::unique_ptr<LpMetadata> metadata = ParseMetadata(fd); // If the primary copy failed, try the backup copy. if (!metadata) { if (!metadata) { offset = GetBackupMetadataOffset(geometry, slot_number); metadata = ReadBackupMetadata(fd, geometry, slot_number); if (SeekFile64(fd, offset, SEEK_END) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << offset; return nullptr; } } metadata = ParseMetadata(fd); } if (metadata) { if (metadata) { metadata->geometry = geometry; metadata->geometry = geometry; } } Loading
fs_mgr/liblp/writer.cpp +74 −16 Original line number Original line Diff line number Diff line Loading @@ -130,20 +130,9 @@ static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std return true; return true; } } static bool DefaultWriter(int fd, const std::string& blob) { static bool WritePrimaryMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number, return android::base::WriteFully(fd, blob.data(), blob.size()); } static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number, const std::string& blob, const std::string& blob, std::function<bool(int, const std::string&)> writer) { const std::function<bool(int, const std::string&)>& writer) { // Make sure we're writing to a valid metadata slot. if (slot_number >= geometry.metadata_slot_count) { LERROR << "Invalid logical partition metadata slot number."; return false; } // Write the primary copy of the metadata. int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number); int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number); if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) { if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) { PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset; PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << primary_offset; Loading @@ -153,8 +142,12 @@ static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t s PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed"; PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed"; return false; return false; } } return true; } // Write the backup copy of the metadata. static bool WriteBackupMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number, const std::string& blob, const std::function<bool(int, const std::string&)>& writer) { int64_t backup_offset = GetBackupMetadataOffset(geometry, slot_number); 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_END); if (abs_offset == (int64_t)-1) { if (abs_offset == (int64_t)-1) { Loading @@ -173,6 +166,27 @@ static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t s return true; return true; } } static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number, const std::string& blob, const std::function<bool(int, const std::string&)>& writer) { // Make sure we're writing to a valid metadata slot. if (slot_number >= geometry.metadata_slot_count) { LERROR << "Invalid logical partition metadata slot number."; return false; } if (!WritePrimaryMetadata(fd, geometry, slot_number, blob, writer)) { return false; } if (!WriteBackupMetadata(fd, geometry, slot_number, blob, writer)) { return false; } return true; } 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, uint32_t slot_number) { bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number) { // Before writing geometry and/or logical partition tables, perform some // Before writing geometry and/or logical partition tables, perform some // basic checks that the geometry and tables are coherent, and will fit // basic checks that the geometry and tables are coherent, and will fit Loading Loading @@ -205,8 +219,13 @@ bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_numbe return WriteMetadata(fd, metadata.geometry, slot_number, metadata_blob, DefaultWriter); return WriteMetadata(fd, metadata.geometry, slot_number, metadata_blob, DefaultWriter); } } 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(int fd, const LpMetadata& metadata, uint32_t slot_number, std::function<bool(int, const std::string&)> writer) { const std::function<bool(int, const std::string&)>& writer) { // Before writing geometry and/or logical partition tables, perform some // Before writing geometry and/or logical partition tables, perform some // basic checks that the geometry and tables are coherent, and will fit // basic checks that the geometry and tables are coherent, and will fit // on the given block device. // on the given block device. Loading @@ -227,6 +246,45 @@ bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_numb LERROR << "Incompatible geometry in new logical partition metadata"; LERROR << "Incompatible geometry in new logical partition metadata"; return false; return false; } } // Validate the slot number now, before we call Read*Metadata. if (slot_number >= geometry.metadata_slot_count) { LERROR << "Invalid logical partition metadata slot number."; return false; } // Try to read both existing copies of the metadata, if any. std::unique_ptr<LpMetadata> primary = ReadPrimaryMetadata(fd, geometry, slot_number); std::unique_ptr<LpMetadata> backup = ReadBackupMetadata(fd, geometry, slot_number); if (primary && (!backup || !CompareMetadata(*primary.get(), *backup.get()))) { // If the backup copy does not match the primary copy, we first // synchronize the backup copy. This guarantees that a partial write // still leaves one copy intact. std::string old_blob; if (!ValidateAndSerializeMetadata(fd, *primary.get(), &old_blob)) { LERROR << "Error serializing primary metadata to repair corrupted backup"; return false; } if (!WriteBackupMetadata(fd, geometry, slot_number, old_blob, writer)) { LERROR << "Error writing primary metadata to repair corrupted backup"; return false; } } else if (backup && !primary) { // The backup copy is coherent, and the primary is not. Sync it for // safety. std::string old_blob; if (!ValidateAndSerializeMetadata(fd, *backup.get(), &old_blob)) { LERROR << "Error serializing primary metadata to repair corrupted backup"; return false; } if (!WritePrimaryMetadata(fd, geometry, slot_number, old_blob, writer)) { LERROR << "Error writing primary metadata to repair corrupted backup"; return false; } } // Both copies should now be in sync, so we can continue the update. return WriteMetadata(fd, geometry, slot_number, blob, writer); return WriteMetadata(fd, geometry, slot_number, blob, writer); } } Loading