Merge "lmkd: Fix an invalid access to a pointer after it's freed"
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index ff7a75c..4934f5a 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -556,17 +556,11 @@
         return mounts;
     }
 
-    // Manually check dm state because stunted fstab (w/o system as root) borken
-    auto& dm = DeviceMapper::Instance();
-    auto found = false;
-    for (auto& system : {"system", "vroot"}) {
-        if (dm.GetState(system) == DmDeviceState::INVALID) continue;
-        std::vector<DeviceMapper::TargetInfo> table;
-        found = !dm.GetTableStatus(system, &table) || table.empty() || table[0].data.empty() ||
-                (table[0].data[0] == 'C') || (table[0].data[0] == 'V');
-        if (found) break;
-    }
-    if (!found) mounts.emplace_back("/system");
+    // We have a stunted fstab (w/o system or / ) passed in by the caller,
+    // verity claims are assumed accurate because they are collected internally
+    // from fs_mgr_fstab_default() from within fs_mgr_update_verity_state(),
+    // Can (re)evaluate /system with impunity since we know it is ever-present.
+    mounts.emplace_back("/system");
     return mounts;
 }
 
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 97b15bd..743a3fe 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -150,7 +150,7 @@
     }
     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 @@
             }
         }
     }
-
-    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 @@
     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 @@
     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 @@
     // 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 @@
     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 @@
         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 @@
     // 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 c916b44..ffa7d3b 100644
--- a/fs_mgr/liblp/builder_test.cpp
+++ b/fs_mgr/liblp/builder_test.cpp
@@ -48,8 +48,8 @@
     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 @@
     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 @@
     unique_ptr<LpMetadata> exported = builder->Export();
     ASSERT_NE(exported, nullptr);
     EXPECT_EQ(exported->geometry.first_logical_sector, 1536);
-    EXPECT_EQ(exported->geometry.last_logical_sector, 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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     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 @@
     unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);
     ASSERT_NE(builder, nullptr);
 
-    EXPECT_EQ(builder->block_device_info().size, device_info.size);
-    EXPECT_EQ(builder->block_device_info().alignment, device_info.alignment);
-    EXPECT_EQ(builder->block_device_info().alignment_offset, device_info.alignment_offset);
-    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 511f7be..8716988 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -167,16 +167,12 @@
     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 @@
         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<int64_t>(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 2031e33..a9ef8ce 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -56,8 +56,7 @@
     const LpMetadataGeometry& geometry_;
     uint32_t block_size_;
     std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
-    std::string primary_blob_;
-    std::string backup_blob_;
+    std::string all_metadata_;
     std::map<std::string, std::string> images_;
     std::vector<android::base::unique_fd> temp_fds_;
 };
diff --git a/fs_mgr/liblp/include/liblp/builder.h b/fs_mgr/liblp/include/liblp/builder.h
index a6044d0..8dbba84 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -215,10 +215,8 @@
     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 @@
     LpMetadataHeader header_;
     std::vector<std::unique_ptr<Partition>> partitions_;
     std::vector<std::unique_ptr<PartitionGroup>> 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 7d1a2a9..a4ff1a7 100644
--- a/fs_mgr/liblp/include/liblp/metadata_format.h
+++ b/fs_mgr/liblp/include/liblp/metadata_format.h
@@ -38,7 +38,7 @@
 #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 @@
  *     +--------------------+
  *     | 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 @@
 /* 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 @@
     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 322219b..220d651 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -290,13 +290,13 @@
     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 @@
     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 87411cb..835df9b 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -121,9 +121,8 @@
 }
 
 bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry) {
-    // Read the first 4096 bytes.
     std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(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 ReadBackupGeometry(int fd, LpMetadataGeometry* geometry) {
-    // Try the backup copy in the last 4096 bytes.
     std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(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<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) {
+    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 b08f96c..2f7692f 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -56,10 +56,18 @@
     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 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 6ef5124..61e7d31 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -38,6 +38,10 @@
 // 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 7bf42ae..ff50e09 100644
--- a/fs_mgr/liblp/utility_test.cpp
+++ b/fs_mgr/liblp/utility_test.cpp
@@ -42,15 +42,17 @@
                                    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 2415629..0c0cc49 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -106,15 +106,13 @@
     // 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 @@
                                 const std::string& blob,
                                 const std::function<bool(int, const std::string&)>& 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 @@
 
     // 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 @@
         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;
     }