Merge "liblp: Refactor the partition table update API."
diff --git a/fs_mgr/liblp/include/liblp/writer.h b/fs_mgr/liblp/include/liblp/writer.h
index efa409d..6e3c9cb 100644
--- a/fs_mgr/liblp/include/liblp/writer.h
+++ b/fs_mgr/liblp/include/liblp/writer.h
@@ -22,28 +22,26 @@
 namespace android {
 namespace fs_mgr {
 
-// When flashing the initial logical partition layout, we also write geometry
-// information at the start and end of the big physical partition. This helps
-// locate metadata and backup metadata in the case of corruption or a failed
-// update. For normal changes to the metadata, we never modify the geometry.
-enum class SyncMode {
-    // Write geometry information.
-    Flash,
-    // Normal update of a single slot.
-    Update
-};
+// Place an initial partition table on the device. This will overwrite the
+// existing geometry, and should not be used for normal partition table
+// updates. False can be returned if the geometry is incompatible with the
+// block device or an I/O error occurs.
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                         uint32_t slot_number);
 
-// Write the given partition table to the given block device, writing only
-// copies according to the given sync mode.
-//
-// This will perform some verification, such that the device has enough space
-// to store the metadata as well as all of its extents.
-//
-// The slot number indicates which metadata slot to use.
-bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
-                         uint32_t slot_number);
-bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
-                         uint32_t slot_number);
+// Update the partition table for a given metadata slot number. False is
+// returned if an error occurs, which can include:
+//  - Invalid slot number.
+//  - I/O error.
+//  - Corrupt or missing metadata geometry on disk.
+//  - Incompatible geometry.
+bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                          uint32_t slot_number);
+
+// These variants are for testing only. The path-based functions should be used
+// for actual operation, so that open() is called with the correct flags.
+bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t slot_number);
 
 // Helper function to serialize geometry and metadata to a normal file, for
 // flashing or debugging.
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 2595654..29d8ca5 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -102,7 +102,7 @@
     if (!exported) {
         return {};
     }
-    if (!WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0)) {
+    if (!FlashPartitionTable(fd, *exported.get(), 0)) {
         return {};
     }
     return fd;
@@ -131,7 +131,7 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
-    EXPECT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+    EXPECT_FALSE(FlashPartitionTable(fd, *exported.get(), 0));
 }
 
 // Test the basics of flashing a partition and reading it back.
@@ -146,7 +146,7 @@
     // Export and flash.
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
-    ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
 
     // Read back. Note that some fields are only filled in during
     // serialization, so exported and imported will not be identical. For
@@ -195,7 +195,7 @@
 
     // Change the name before writing to the next slot.
     strncpy(imported->partitions[0].name, "vendor", sizeof(imported->partitions[0].name));
-    ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     // Read back the original slot, make sure it hasn't changed.
     imported = ReadMetadata(fd, 0);
@@ -231,7 +231,7 @@
     unique_ptr<LpMetadata> metadata = ReadMetadata(fd, 0);
     ASSERT_NE(metadata, nullptr);
     for (uint32_t i = 1; i < kMetadataSlots; i++) {
-        ASSERT_TRUE(WritePartitionTable(fd, *metadata.get(), SyncMode::Update, i));
+        ASSERT_TRUE(UpdatePartitionTable(fd, *metadata.get(), i));
     }
 
     // Verify that we can't read unavailable slots.
@@ -246,25 +246,25 @@
 
     unique_ptr<LpMetadata> imported = ReadMetadata(fd, 0);
     ASSERT_NE(imported, nullptr);
-    ASSERT_TRUE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_TRUE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     imported->geometry.metadata_max_size += LP_SECTOR_SIZE;
-    ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     imported = ReadMetadata(fd, 0);
     ASSERT_NE(imported, nullptr);
     imported->geometry.metadata_slot_count++;
-    ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     imported = ReadMetadata(fd, 0);
     ASSERT_NE(imported, nullptr);
     imported->geometry.first_logical_sector++;
-    ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
 
     imported = ReadMetadata(fd, 0);
     ASSERT_NE(imported, nullptr);
     imported->geometry.last_logical_sector--;
-    ASSERT_FALSE(WritePartitionTable(fd, *imported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *imported.get(), 1));
 }
 
 // Test that changing one bit of metadata is enough to break the checksum.
@@ -353,8 +353,8 @@
     ASSERT_GE(fd, 0);
 
     // Check that we are able to write our table.
-    ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Flash, 0));
-    ASSERT_TRUE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
+    ASSERT_TRUE(FlashPartitionTable(fd, *exported.get(), 0));
+    ASSERT_TRUE(UpdatePartitionTable(fd, *exported.get(), 1));
 
     // Check that adding one more partition overflows the metadata allotment.
     partition = builder->AddPartition("final", TEST_GUID, LP_PARTITION_ATTR_NONE);
@@ -364,7 +364,7 @@
     ASSERT_NE(exported, nullptr);
 
     // The new table should be too large to be written.
-    ASSERT_FALSE(WritePartitionTable(fd, *exported.get(), SyncMode::Update, 1));
+    ASSERT_FALSE(UpdatePartitionTable(fd, *exported.get(), 1));
 
     // Check that the first and last logical sectors weren't touched when we
     // wrote this almost-full metadata.
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 89cbabd..e3dba47 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -73,8 +73,14 @@
 
 // Perform sanity checks so we don't accidentally overwrite valid metadata
 // with potentially invalid metadata, or random partition data with metadata.
-static bool ValidateGeometryAndMetadata(const LpMetadata& metadata, uint64_t blockdevice_size,
-                                        uint64_t metadata_size) {
+static bool ValidateAndSerializeMetadata(int fd, const LpMetadata& metadata, std::string* blob) {
+    uint64_t blockdevice_size;
+    if (!GetDescriptorSize(fd, &blockdevice_size)) {
+        return false;
+    }
+
+    *blob = SerializeMetadata(metadata);
+
     const LpMetadataHeader& header = metadata.header;
     const LpMetadataGeometry& geometry = metadata.geometry;
     // Validate the usable sector range.
@@ -83,7 +89,7 @@
         return false;
     }
     // Make sure we're writing within the space reserved.
-    if (metadata_size > geometry.metadata_max_size) {
+    if (blob->size() > geometry.metadata_max_size) {
         LERROR << "Logical partition metadata is too large.";
         return false;
     }
@@ -124,63 +130,14 @@
     return true;
 }
 
-bool WritePartitionTable(int fd, const LpMetadata& metadata, SyncMode sync_mode,
-                         uint32_t slot_number) {
-    uint64_t size;
-    if (!GetDescriptorSize(fd, &size)) {
-        return false;
-    }
-
-    const LpMetadataGeometry& geometry = metadata.geometry;
-    if (sync_mode != SyncMode::Flash) {
-        // Verify that the old geometry is identical. If it's not, then we've
-        // based this new metadata on invalid assumptions.
-        LpMetadataGeometry old_geometry;
-        if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
-            return false;
-        }
-        if (!CompareGeometry(geometry, old_geometry)) {
-            LERROR << "Incompatible geometry in new logical partition metadata";
-            return false;
-        }
-    }
-
+static bool WriteMetadata(int fd, const LpMetadataGeometry& geometry, uint32_t slot_number,
+                          const std::string& blob) {
     // 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;
     }
 
-    // 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 = SerializeMetadata(metadata);
-    if (!ValidateGeometryAndMetadata(metadata, size, blob.size())) {
-        return false;
-    }
-
-    // First write geometry if this is a flash operation. It gets written to
-    // the first and last 4096-byte regions of the device.
-    if (sync_mode == SyncMode::Flash) {
-        std::string blob = SerializeGeometry(metadata.geometry);
-        if (SeekFile64(fd, 0, SEEK_SET) < 0) {
-            PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
-            return false;
-        }
-        if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
-            PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
-            return false;
-        }
-        if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
-            PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
-            return false;
-        }
-        if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
-            PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
-            return false;
-        }
-    }
-
     // Write the primary copy of the metadata.
     int64_t primary_offset = GetPrimaryMetadataOffset(geometry, slot_number);
     if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {
@@ -211,14 +168,80 @@
     return true;
 }
 
-bool WritePartitionTable(const char* block_device, const LpMetadata& metadata, SyncMode sync_mode,
+bool FlashPartitionTable(int fd, const LpMetadata& metadata, uint32_t 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 metadata_blob;
+    if (!ValidateAndSerializeMetadata(fd, metadata, &metadata_blob)) {
+        return false;
+    }
+
+    // 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) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset 0";
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "write " << blob.size() << " bytes failed";
+        return false;
+    }
+    if (SeekFile64(fd, -LP_METADATA_GEOMETRY_SIZE, SEEK_END) < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "lseek failed: offset " << -LP_METADATA_GEOMETRY_SIZE;
+        return false;
+    }
+    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {
+        PERROR << __PRETTY_FUNCTION__ << "backup write " << blob.size() << " bytes failed";
+        return false;
+    }
+
+    // Write metadata to the correct slot, now that geometry is in place.
+    return WriteMetadata(fd, metadata.geometry, slot_number, metadata_blob);
+}
+
+bool UpdatePartitionTable(int fd, const LpMetadata& metadata, uint32_t 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(fd, metadata, &blob)) {
+        return false;
+    }
+
+    // Verify that the old geometry is identical. If it's not, then we might be
+    // writing a table that was built for a different device, so we must reject
+    // it.
+    const LpMetadataGeometry& geometry = metadata.geometry;
+    LpMetadataGeometry old_geometry;
+    if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {
+        return false;
+    }
+    if (!CompareGeometry(geometry, old_geometry)) {
+        LERROR << "Incompatible geometry in new logical partition metadata";
+        return false;
+    }
+    return WriteMetadata(fd, geometry, slot_number, blob);
+}
+
+bool FlashPartitionTable(const std::string& block_device, const LpMetadata& metadata,
                          uint32_t slot_number) {
-    android::base::unique_fd fd(open(block_device, O_RDWR | O_SYNC));
+    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
     if (fd < 0) {
         PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
         return false;
     }
-    return WritePartitionTable(fd, metadata, sync_mode, slot_number);
+    return FlashPartitionTable(fd, metadata, slot_number);
+}
+
+bool UpdatePartitionTable(const std::string& block_device, const LpMetadata& metadata,
+                          uint32_t slot_number) {
+    android::base::unique_fd fd(open(block_device.c_str(), O_RDWR | O_SYNC));
+    if (fd < 0) {
+        PERROR << __PRETTY_FUNCTION__ << "open failed: " << block_device;
+        return false;
+    }
+    return UpdatePartitionTable(fd, metadata, slot_number);
 }
 
 bool WriteToImageFile(int fd, const LpMetadata& input) {