Merge "init parses *.rc files from APEXes"
diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp
index 49baf36..e380c84 100644
--- a/adb/client/usb_osx.cpp
+++ b/adb/client/usb_osx.cpp
@@ -136,8 +136,8 @@
     io_service_t             usbDevice;
     io_service_t             usbInterface;
     IOCFPlugInInterface      **plugInInterface = NULL;
-    IOUSBInterfaceInterface220  **iface = NULL;
-    IOUSBDeviceInterface197  **dev = NULL;
+    IOUSBInterfaceInterface500  **iface = NULL;
+    IOUSBDeviceInterface500  **dev = NULL;
     HRESULT                  result;
     SInt32                   score;
     uint32_t                 locationId;
@@ -163,7 +163,7 @@
         //* This gets us the interface object
         result = (*plugInInterface)->QueryInterface(
             plugInInterface,
-            CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), (LPVOID*)&iface);
+            CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID500), (LPVOID*)&iface);
         //* We only needed the plugin to get the interface, so discard it
         (*plugInInterface)->Release(plugInInterface);
         if (result || !iface) {
@@ -209,7 +209,7 @@
         }
 
         result = (*plugInInterface)->QueryInterface(plugInInterface,
-            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*)&dev);
+            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500), (LPVOID*)&dev);
         //* only needed this to query the plugin
         (*plugInInterface)->Release(plugInInterface);
         if (result || !dev) {
diff --git a/fs_mgr/liblp/builder.cpp b/fs_mgr/liblp/builder.cpp
index 1e031ad..6b4901b 100644
--- a/fs_mgr/liblp/builder.cpp
+++ b/fs_mgr/liblp/builder.cpp
@@ -113,18 +113,7 @@
     if (!metadata) {
         return nullptr;
     }
-    std::unique_ptr<MetadataBuilder> builder = New(*metadata.get());
-    if (!builder) {
-        return nullptr;
-    }
-    for (size_t i = 0; i < builder->block_devices_.size(); i++) {
-        std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
-        BlockDeviceInfo device_info;
-        if (opener.GetInfo(partition_name, &device_info)) {
-            builder->UpdateBlockDeviceInfo(i, device_info);
-        }
-    }
-    return builder;
+    return New(*metadata.get(), &opener);
 }
 
 std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& super_partition,
@@ -142,14 +131,69 @@
     return builder;
 }
 
-std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata) {
+std::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata,
+                                                      const IPartitionOpener* opener) {
     std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());
     if (!builder->Init(metadata)) {
         return nullptr;
     }
+    if (opener) {
+        for (size_t i = 0; i < builder->block_devices_.size(); i++) {
+            std::string partition_name = GetBlockDevicePartitionName(builder->block_devices_[i]);
+            BlockDeviceInfo device_info;
+            if (opener->GetInfo(partition_name, &device_info)) {
+                builder->UpdateBlockDeviceInfo(i, device_info);
+            }
+        }
+    }
     return builder;
 }
 
+std::unique_ptr<MetadataBuilder> MetadataBuilder::NewForUpdate(const IPartitionOpener& opener,
+                                                               const std::string& source_partition,
+                                                               uint32_t source_slot_number,
+                                                               uint32_t target_slot_number) {
+    auto metadata = ReadMetadata(opener, source_partition, source_slot_number);
+    if (!metadata) {
+        return nullptr;
+    }
+
+    // Get the list of devices we already have.
+    std::set<std::string> block_devices;
+    for (const auto& block_device : metadata->block_devices) {
+        block_devices.emplace(GetBlockDevicePartitionName(block_device));
+    }
+
+    auto new_block_devices = metadata->block_devices;
+
+    // Add missing block devices.
+    std::string source_slot_suffix = SlotSuffixForSlotNumber(source_slot_number);
+    std::string target_slot_suffix = SlotSuffixForSlotNumber(target_slot_number);
+    for (const auto& block_device : metadata->block_devices) {
+        std::string partition_name = GetBlockDevicePartitionName(block_device);
+        std::string slot_suffix = GetPartitionSlotSuffix(partition_name);
+        if (slot_suffix.empty() || slot_suffix != source_slot_suffix) {
+            continue;
+        }
+        std::string new_name =
+                partition_name.substr(0, partition_name.size() - slot_suffix.size()) +
+                target_slot_suffix;
+        if (block_devices.find(new_name) != block_devices.end()) {
+            continue;
+        }
+
+        auto new_device = block_device;
+        if (!UpdateBlockDevicePartitionName(&new_device, new_name)) {
+            LERROR << "Partition name too long: " << new_name;
+            return nullptr;
+        }
+        new_block_devices.emplace_back(new_device);
+    }
+
+    metadata->block_devices = new_block_devices;
+    return New(*metadata.get(), &opener);
+}
+
 MetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {
     memset(&geometry_, 0, sizeof(geometry_));
     geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 46bdfa4..a976643 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -62,7 +62,7 @@
 }
 
 std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file) {
-    android::base::unique_fd fd(open(file, O_RDONLY));
+    android::base::unique_fd fd(open(file, O_RDONLY | O_CLOEXEC));
     if (fd < 0) {
         PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
         return nullptr;
@@ -84,7 +84,7 @@
 }
 
 bool WriteToImageFile(const char* file, const LpMetadata& input) {
-    android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+    android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
     if (fd < 0) {
         PERROR << __PRETTY_FUNCTION__ << " open failed: " << file;
         return false;
@@ -97,7 +97,6 @@
     : metadata_(metadata),
       geometry_(metadata.geometry),
       block_size_(block_size),
-      file_(nullptr, sparse_file_destroy),
       images_(images) {
     uint64_t total_size = GetTotalSuperPartitionSize(metadata);
     if (block_size % LP_SECTOR_SIZE != 0) {
@@ -129,20 +128,32 @@
         return;
     }
 
-    file_.reset(sparse_file_new(block_size_, total_size));
-    if (!file_) {
-        LERROR << "Could not allocate sparse file of size " << total_size;
+    for (const auto& block_device : metadata.block_devices) {
+        SparsePtr file(sparse_file_new(block_size_, block_device.size), sparse_file_destroy);
+        if (!file) {
+            LERROR << "Could not allocate sparse file of size " << block_device.size;
+            return;
+        }
+        device_images_.emplace_back(std::move(file));
     }
 }
 
+bool SparseBuilder::IsValid() const {
+    return device_images_.size() == metadata_.block_devices.size();
+}
+
 bool SparseBuilder::Export(const char* file) {
-    android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
+    android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644));
     if (fd < 0) {
         PERROR << "open failed: " << file;
         return false;
     }
+    if (device_images_.size() > 1) {
+        LERROR << "Cannot export to a single image on retrofit builds.";
+        return false;
+    }
     // No gzip compression; sparseify; no checksum.
-    int ret = sparse_file_write(file_.get(), fd, false, true, false);
+    int ret = sparse_file_write(device_images_[0].get(), fd, false, true, false);
     if (ret != 0) {
         LERROR << "sparse_file_write failed (error code " << ret << ")";
         return false;
@@ -150,13 +161,39 @@
     return true;
 }
 
-bool SparseBuilder::AddData(const std::string& blob, uint64_t sector) {
+bool SparseBuilder::ExportFiles(const std::string& output_dir) {
+    android::base::unique_fd dir(open(output_dir.c_str(), O_CLOEXEC | O_DIRECTORY | O_NOFOLLOW));
+    if (dir < 0) {
+        PERROR << "open dir failed: " << output_dir;
+        return false;
+    }
+
+    for (size_t i = 0; i < device_images_.size(); i++) {
+        std::string name = GetBlockDevicePartitionName(metadata_.block_devices[i]);
+        std::string path = output_dir + "/super_" + name + ".img";
+        android::base::unique_fd fd(openat(
+                dir, path.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, 0644));
+        if (fd < 0) {
+            PERROR << "open failed: " << path;
+            return false;
+        }
+        // No gzip compression; sparseify; no checksum.
+        int ret = sparse_file_write(device_images_[i].get(), fd, false, true, false);
+        if (ret != 0) {
+            LERROR << "sparse_file_write failed (error code " << ret << ")";
+            return false;
+        }
+    }
+    return true;
+}
+
+bool SparseBuilder::AddData(sparse_file* file, const std::string& blob, uint64_t sector) {
     uint32_t block;
     if (!SectorToBlock(sector, &block)) {
         return false;
     }
     void* data = const_cast<char*>(blob.data());
-    int ret = sparse_file_add_data(file_.get(), data, blob.size(), block);
+    int ret = sparse_file_add_data(file, data, blob.size(), block);
     if (ret != 0) {
         LERROR << "sparse_file_add_data failed (error code " << ret << ")";
         return false;
@@ -179,8 +216,12 @@
     return true;
 }
 
+uint64_t SparseBuilder::BlockToSector(uint64_t block) const {
+    return (block * block_size_) / LP_SECTOR_SIZE;
+}
+
 bool SparseBuilder::Build() {
-    if (sparse_file_add_fill(file_.get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
+    if (sparse_file_add_fill(device_images_[0].get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
         LERROR << "Could not add initial sparse block for reserved zeroes";
         return false;
     }
@@ -194,7 +235,13 @@
     for (size_t i = 0; i < geometry_.metadata_slot_count * 2; i++) {
         all_metadata_ += metadata_blob;
     }
-    if (!AddData(all_metadata_, 0)) {
+
+    uint64_t first_sector = LP_PARTITION_RESERVED_BYTES / LP_SECTOR_SIZE;
+    if (!AddData(device_images_[0].get(), all_metadata_, first_sector)) {
+        return false;
+    }
+
+    if (!CheckExtentOrdering()) {
         return false;
     }
 
@@ -228,13 +275,10 @@
 
 bool SparseBuilder::AddPartitionImage(const LpMetadataPartition& partition,
                                       const std::string& file) {
-    if (partition.num_extents != 1) {
-        LERROR << "Partition for new tables should not have more than one extent: "
-               << GetPartitionName(partition);
-        return false;
-    }
+    // Track which extent we're processing.
+    uint32_t extent_index = partition.first_extent_index;
 
-    const LpMetadataExtent& extent = metadata_.extents[partition.first_extent_index];
+    const LpMetadataExtent& extent = metadata_.extents[extent_index];
     if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
         LERROR << "Partition should only have linear extents: " << GetPartitionName(partition);
         return false;
@@ -252,9 +296,11 @@
         LERROR << "Could not compute image size";
         return false;
     }
-    if (file_length > extent.num_sectors * LP_SECTOR_SIZE) {
+    uint64_t partition_size = ComputePartitionSize(partition);
+    if (file_length > partition_size) {
         LERROR << "Image for partition '" << GetPartitionName(partition)
-               << "' is greater than its size";
+               << "' is greater than its size (" << file_length << ", excepted " << partition_size
+               << ")";
         return false;
     }
     if (SeekFile64(fd, 0, SEEK_SET)) {
@@ -262,14 +308,39 @@
         return false;
     }
 
+    // We track the current logical sector and the position the current extent
+    // ends at.
+    uint64_t output_sector = 0;
+    uint64_t extent_last_sector = extent.num_sectors;
+
+    // We also track the output device and the current output block within that
+    // device.
     uint32_t output_block;
     if (!SectorToBlock(extent.target_data, &output_block)) {
         return false;
     }
+    sparse_file* output_device = device_images_[extent.target_source].get();
 
+    // Proceed to read the file and build sparse images.
     uint64_t pos = 0;
     uint64_t remaining = file_length;
     while (remaining) {
+        // Check if we need to advance to the next extent.
+        if (output_sector == extent_last_sector) {
+            extent_index++;
+            if (extent_index >= partition.first_extent_index + partition.num_extents) {
+                LERROR << "image is larger than extent table";
+                return false;
+            }
+
+            const LpMetadataExtent& extent = metadata_.extents[extent_index];
+            extent_last_sector += extent.num_sectors;
+            output_device = device_images_[extent.target_source].get();
+            if (!SectorToBlock(extent.target_data, &output_block)) {
+                return false;
+            }
+        }
+
         uint32_t buffer[block_size_ / sizeof(uint32_t)];
         size_t read_size = remaining >= sizeof(buffer) ? sizeof(buffer) : size_t(remaining);
         if (!android::base::ReadFully(fd, buffer, sizeof(buffer))) {
@@ -277,13 +348,13 @@
             return false;
         }
         if (read_size != sizeof(buffer) || !HasFillValue(buffer, read_size / sizeof(uint32_t))) {
-            int rv = sparse_file_add_fd(file_.get(), fd, pos, read_size, output_block);
+            int rv = sparse_file_add_fd(output_device, fd, pos, read_size, output_block);
             if (rv) {
                 LERROR << "sparse_file_add_fd failed with code: " << rv;
                 return false;
             }
         } else {
-            int rv = sparse_file_add_fill(file_.get(), buffer[0], read_size, output_block);
+            int rv = sparse_file_add_fill(output_device, buffer[0], read_size, output_block);
             if (rv) {
                 LERROR << "sparse_file_add_fill failed with code: " << rv;
                 return false;
@@ -291,21 +362,57 @@
         }
         pos += read_size;
         remaining -= read_size;
+        output_sector += block_size_ / LP_SECTOR_SIZE;
         output_block++;
     }
 
     return true;
 }
 
+uint64_t SparseBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {
+    uint64_t sectors = 0;
+    for (size_t i = 0; i < partition.num_extents; i++) {
+        sectors += metadata_.extents[partition.first_extent_index + i].num_sectors;
+    }
+    return sectors * LP_SECTOR_SIZE;
+}
+
+// For simplicity, we don't allow serializing any configuration: extents must
+// be ordered, such that any extent at position I in the table occurs *before*
+// any extent after position I, for the same block device. We validate that
+// here.
+//
+// Without this, it would be more difficult to find the appropriate extent for
+// an output block. With this guarantee it is a linear walk.
+bool SparseBuilder::CheckExtentOrdering() {
+    std::vector<uint64_t> last_sectors(metadata_.block_devices.size());
+
+    for (const auto& extent : metadata_.extents) {
+        if (extent.target_type != LP_TARGET_TYPE_LINEAR) {
+            LERROR << "Extents must all be type linear.";
+            return false;
+        }
+        if (extent.target_data <= last_sectors[extent.target_source]) {
+            LERROR << "Extents must appear in increasing order.";
+            return false;
+        }
+        if ((extent.num_sectors * LP_SECTOR_SIZE) % block_size_ != 0) {
+            LERROR << "Extents must be aligned to the block size.";
+            return false;
+        }
+        last_sectors[extent.target_source] = extent.target_data;
+    }
+    return true;
+}
+
 int SparseBuilder::OpenImageFile(const std::string& file) {
-    android::base::unique_fd source_fd(open(file.c_str(), O_RDONLY));
+    android::base::unique_fd source_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
     if (source_fd < 0) {
         PERROR << "open image file failed: " << file;
         return -1;
     }
 
-    std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> source(
-            sparse_file_import(source_fd, true, true), sparse_file_destroy);
+    SparsePtr source(sparse_file_import(source_fd, true, true), sparse_file_destroy);
     if (!source) {
         int fd = source_fd.get();
         temp_fds_.push_back(std::move(source_fd));
@@ -340,5 +447,11 @@
     return builder.IsValid() && builder.Build() && builder.Export(file);
 }
 
+bool WriteSplitSparseFiles(const std::string& output_dir, const LpMetadata& metadata,
+                           uint32_t block_size, const std::map<std::string, std::string>& images) {
+    SparseBuilder builder(metadata, block_size, images);
+    return builder.IsValid() && builder.Build() && builder.ExportFiles(output_dir);
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index a9ef8ce..44217a0 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -42,20 +42,26 @@
 
     bool Build();
     bool Export(const char* file);
-    bool IsValid() const { return file_ != nullptr; }
+    bool ExportFiles(const std::string& dir);
+    bool IsValid() const;
 
-    sparse_file* file() const { return file_.get(); }
+    using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;
+    const std::vector<SparsePtr>& device_images() const { return device_images_; }
 
   private:
-    bool AddData(const std::string& blob, uint64_t sector);
+    bool AddData(sparse_file* file, const std::string& blob, uint64_t sector);
     bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file);
     int OpenImageFile(const std::string& file);
     bool SectorToBlock(uint64_t sector, uint32_t* block);
+    uint64_t BlockToSector(uint64_t block) const;
+    bool CheckExtentOrdering();
+    uint64_t ComputePartitionSize(const LpMetadataPartition& partition) const;
 
     const LpMetadata& metadata_;
     const LpMetadataGeometry& geometry_;
     uint32_t block_size_;
-    std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
+
+    std::vector<SparsePtr> device_images_;
     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 47ebf6d..59717d1 100644
--- a/fs_mgr/liblp/include/liblp/builder.h
+++ b/fs_mgr/liblp/include/liblp/builder.h
@@ -150,10 +150,24 @@
     static std::unique_ptr<MetadataBuilder> New(const std::string& super_partition,
                                                 uint32_t slot_number);
 
+    // This is when performing an A/B update. The source partition must be a
+    // super partition. On a normal device, the metadata for the source slot
+    // is imported and the target slot is ignored. On a retrofit device, the
+    // metadata may not have the target slot's devices listed yet, in which
+    // case, it is automatically upgraded to include all available block
+    // devices.
+    static std::unique_ptr<MetadataBuilder> NewForUpdate(const IPartitionOpener& opener,
+                                                         const std::string& source_partition,
+                                                         uint32_t source_slot_number,
+                                                         uint32_t target_slot_number);
+
     // Import an existing table for modification. If the table is not valid, for
     // example it contains duplicate partition names, then nullptr is returned.
-    // This method is for testing or changing off-line tables.
-    static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata);
+    //
+    // If an IPartitionOpener is specified, then block device informatiom will
+    // be updated.
+    static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata,
+                                                const IPartitionOpener* opener = nullptr);
 
     // Helper function for a single super partition, for tests.
     static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index 56332ab..1af1e80 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -78,6 +78,14 @@
 std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
 std::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);
 
+// Similar to WriteToSparseFile, this will generate an image that can be
+// flashed to a device directly. However unlike WriteToSparseFile, it
+// is intended for retrofit devices, and will generate one sparse file per
+// block device (each named super_<name>.img) and placed in the specified
+// output folder.
+bool WriteSplitSparseFiles(const std::string& output_dir, const LpMetadata& metadata,
+                           uint32_t block_size, const std::map<std::string, std::string>& images);
+
 // Helper to extract safe C++ strings from partition info.
 std::string GetPartitionName(const LpMetadataPartition& partition);
 std::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 459cf82..9acf23e 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -38,6 +38,7 @@
 static const size_t kDiskSize = 131072;
 static const size_t kMetadataSize = 512;
 static const size_t kMetadataSlots = 2;
+static const BlockDeviceInfo kSuperInfo{"super", kDiskSize, 0, 0, 4096};
 
 // Helper function for creating an in-memory file descriptor. This lets us
 // simulate read/writing logical partition metadata as if we had a block device
@@ -79,6 +80,12 @@
     return builder;
 }
 
+class DefaultPartitionOpener final : public TestPartitionOpener {
+  public:
+    explicit DefaultPartitionOpener(int fd)
+        : TestPartitionOpener({{"super", fd}}, {{"super", kSuperInfo}}) {}
+};
+
 static bool AddDefaultPartitions(MetadataBuilder* builder) {
     Partition* system = builder->AddPartition("system", LP_PARTITION_ATTR_NONE);
     if (!system) {
@@ -88,14 +95,11 @@
 }
 
 // Create a temporary disk and flash it with the default partition setup.
-static unique_fd CreateFlashedDisk(bool auto_slot_suffix = false) {
+static unique_fd CreateFlashedDisk() {
     unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
     if (!builder || !AddDefaultPartitions(builder.get())) {
         return {};
     }
-    if (auto_slot_suffix) {
-        builder->SetAutoSlotSuffixing();
-    }
     unique_fd fd = CreateFakeDisk();
     if (fd < 0) {
         return {};
@@ -106,7 +110,7 @@
         return {};
     }
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
     if (!FlashPartitionTable(opener, "super", *exported.get())) {
         return {};
     }
@@ -122,7 +126,7 @@
     ASSERT_TRUE(GetDescriptorSize(fd, &size));
     ASSERT_EQ(size, kDiskSize);
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
 
     // Verify that we can't read unwritten metadata.
     ASSERT_EQ(ReadMetadata(opener, "super", 1), nullptr);
@@ -141,7 +145,7 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
 
     EXPECT_FALSE(FlashPartitionTable(opener, "super", *exported.get()));
 }
@@ -155,7 +159,7 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
 
     // Export and flash.
     unique_ptr<LpMetadata> exported = builder->Export();
@@ -201,7 +205,7 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
 
     unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
@@ -246,7 +250,7 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
 
     // Make sure all slots are filled.
     unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
@@ -265,7 +269,7 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
 
     unique_ptr<LpMetadata> imported = ReadMetadata(opener, "super", 0);
     ASSERT_NE(imported, nullptr);
@@ -294,7 +298,7 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
 
     LpMetadataGeometry geometry;
     ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);
@@ -313,7 +317,7 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
 
     char corruption[LP_METADATA_GEOMETRY_SIZE];
     memset(corruption, 0xff, sizeof(corruption));
@@ -333,7 +337,7 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
 
     unique_ptr<LpMetadata> metadata = ReadMetadata(opener, "super", 0);
 
@@ -381,7 +385,7 @@
     unique_fd fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
 
     // Check that we are able to write our table.
     ASSERT_TRUE(FlashPartitionTable(opener, "super", *exported.get()));
@@ -490,7 +494,7 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
 
     BadWriter writer;
 
@@ -518,7 +522,7 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
 
     BadWriter writer;
 
@@ -547,7 +551,7 @@
     unique_fd fd = CreateFlashedDisk();
     ASSERT_GE(fd, 0);
 
-    TestPartitionOpener opener({{"super", fd}});
+    DefaultPartitionOpener opener(fd);
 
     BadWriter writer;
 
@@ -596,12 +600,14 @@
     // Build the sparse file.
     SparseBuilder sparse(*exported.get(), 512, {});
     ASSERT_TRUE(sparse.IsValid());
-    sparse_file_verbose(sparse.file());
     ASSERT_TRUE(sparse.Build());
 
+    const auto& images = sparse.device_images();
+    ASSERT_EQ(images.size(), static_cast<size_t>(1));
+
     // Write it to the fake disk.
     ASSERT_NE(lseek(fd.get(), 0, SEEK_SET), -1);
-    int ret = sparse_file_write(sparse.file(), fd.get(), false, false, false);
+    int ret = sparse_file_write(images[0].get(), fd.get(), false, false, false);
     ASSERT_EQ(ret, 0);
 
     // Verify that we can read both sets of metadata.
@@ -613,22 +619,72 @@
 }
 
 TEST(liblp, AutoSlotSuffixing) {
-    auto fd = CreateFlashedDisk(true);
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+    builder->SetAutoSlotSuffixing();
+
+    auto fd = CreateFakeDisk();
     ASSERT_GE(fd, 0);
 
-    TestPartitionOpener opener({{"super", fd}});
+    // Note: we bind the same fd to both names, since we want to make sure the
+    // exact same bits are getting read back in each test.
+    TestPartitionOpener opener({{"super_a", fd}, {"super_b", fd}},
+                               {{"super_a", kSuperInfo}, {"super_b", kSuperInfo}});
+    auto exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    ASSERT_TRUE(FlashPartitionTable(opener, "super_a", *exported.get()));
 
-    auto metadata = ReadMetadata(opener, "super", 1);
+    auto metadata = ReadMetadata(opener, "super_b", 1);
     ASSERT_NE(metadata, nullptr);
     ASSERT_EQ(metadata->partitions.size(), static_cast<size_t>(1));
     EXPECT_EQ(GetPartitionName(metadata->partitions[0]), "system_b");
     ASSERT_EQ(metadata->block_devices.size(), static_cast<size_t>(1));
     EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "super_b");
 
-    metadata = ReadMetadata(opener, "super", 0);
+    metadata = ReadMetadata(opener, "super_a", 0);
     ASSERT_NE(metadata, nullptr);
     ASSERT_EQ(metadata->partitions.size(), static_cast<size_t>(1));
     EXPECT_EQ(GetPartitionName(metadata->partitions[0]), "system_a");
     ASSERT_EQ(metadata->block_devices.size(), static_cast<size_t>(1));
     EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), "super_a");
 }
+
+TEST(liblp, UpdateRetrofit) {
+    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+    builder->SetAutoSlotSuffixing();
+
+    auto fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    // Note: we bind the same fd to both names, since we want to make sure the
+    // exact same bits are getting read back in each test.
+    TestPartitionOpener opener({{"super_a", fd}, {"super_b", fd}},
+                               {{"super_a", kSuperInfo}, {"super_b", kSuperInfo}});
+    auto exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+    ASSERT_TRUE(FlashPartitionTable(opener, "super_a", *exported.get()));
+
+    builder = MetadataBuilder::NewForUpdate(opener, "super_a", 0, 1);
+    ASSERT_NE(builder, nullptr);
+    auto updated = builder->Export();
+    ASSERT_NE(updated, nullptr);
+    ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(2));
+    EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super_a");
+    EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[1]), "super_b");
+}
+
+TEST(liblp, UpdateNonRetrofit) {
+    unique_fd fd = CreateFlashedDisk();
+    ASSERT_GE(fd, 0);
+
+    DefaultPartitionOpener opener(fd);
+    auto builder = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
+    ASSERT_NE(builder, nullptr);
+    auto updated = builder->Export();
+    ASSERT_NE(updated, nullptr);
+    ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));
+    EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), "super");
+}
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 3319956..b36ba0e 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -369,12 +369,10 @@
             continue;
         }
         std::string partition_name = GetBlockDevicePartitionName(block_device) + slot_suffix;
-        if (partition_name.size() > sizeof(block_device.partition_name)) {
+        if (!UpdateBlockDevicePartitionName(&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;
diff --git a/fs_mgr/liblp/utility.cpp b/fs_mgr/liblp/utility.cpp
index 2d34ce5..4f20b6b 100644
--- a/fs_mgr/liblp/utility.cpp
+++ b/fs_mgr/liblp/utility.cpp
@@ -137,5 +137,13 @@
     return (slot_number == 0) ? "_a" : "_b";
 }
 
+bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name) {
+    if (name.size() > sizeof(device->partition_name)) {
+        return false;
+    }
+    strncpy(device->partition_name, name.c_str(), sizeof(device->partition_name));
+    return true;
+}
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/utility.h b/fs_mgr/liblp/utility.h
index 65e643b..55ecb5a 100644
--- a/fs_mgr/liblp/utility.h
+++ b/fs_mgr/liblp/utility.h
@@ -84,6 +84,9 @@
     return aligned;
 }
 
+// Update names from C++ strings.
+bool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);
+
 }  // namespace fs_mgr
 }  // namespace android