liblp: Allow specifying a custom block size when building sparse images.

liblp currently creates sparse files with a block size of 512 bytes.
Unfortunately devices can reject sparse files that don't have a specific
block size. To accomodate this, SparseBuilder now requires that
partition metadata be built with an explicit block size, and the device
alignment must be a multiple of that block size.

This change also modifies SerializeGeometry to automatically include
0-padding to 4096 bytes.

Bug: 79173901
Test: lpmake -S can be flashed with fastboot to walleye
Change-Id: Iab04f6f55bb373e21767963335c85ad1119a65a7
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 93c5618..aeb339b 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -57,10 +57,9 @@
 
 bool WriteToImageFile(int fd, const LpMetadata& input) {
     std::string geometry = SerializeGeometry(input.geometry);
-    std::string padding(LP_METADATA_GEOMETRY_SIZE - geometry.size(), '\0');
     std::string metadata = SerializeMetadata(input);
 
-    std::string everything = geometry + padding + metadata;
+    std::string everything = geometry + metadata;
 
     if (!android::base::WriteFully(fd, everything.data(), everything.size())) {
         PERROR << __PRETTY_FUNCTION__ << "write " << everything.size() << " bytes failed";
@@ -83,26 +82,29 @@
 // to do this when the data pointers are all in one place.
 class SparseBuilder {
   public:
-    explicit SparseBuilder(const LpMetadata& metadata);
+    SparseBuilder(const LpMetadata& metadata, uint32_t block_size);
 
     bool Build();
     bool Export(const char* file);
     bool IsValid() const { return file_ != nullptr; }
 
   private:
-    bool AddData(const std::string& blob, uint32_t block);
+    bool AddData(const std::string& blob, uint64_t sector);
+    bool SectorToBlock(uint64_t sector, uint32_t* block);
 
     const LpMetadata& metadata_;
     const LpMetadataGeometry& geometry_;
+    uint32_t block_size_;
     std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)> file_;
-    std::string geometry_blob_;
-    std::string metadata_blob_;
+    std::string primary_blob_;
+    std::string backup_blob_;
 };
 
-SparseBuilder::SparseBuilder(const LpMetadata& metadata)
+SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size)
     : metadata_(metadata),
       geometry_(metadata.geometry),
-      file_(sparse_file_new(LP_SECTOR_SIZE, geometry_.block_device_size), sparse_file_destroy) {}
+      block_size_(block_size),
+      file_(sparse_file_new(block_size_, geometry_.block_device_size), sparse_file_destroy) {}
 
 bool SparseBuilder::Export(const char* file) {
     android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
@@ -119,7 +121,11 @@
     return true;
 }
 
-bool SparseBuilder::AddData(const std::string& blob, uint32_t block) {
+bool SparseBuilder::AddData(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);
     if (ret != 0) {
@@ -129,22 +135,37 @@
     return true;
 }
 
-bool SparseBuilder::Build() {
-    geometry_blob_ = SerializeGeometry(geometry_);
-    geometry_blob_.resize(LP_METADATA_GEOMETRY_SIZE);
-    if (!AddData(geometry_blob_, 0)) {
+bool SparseBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {
+    // The caller must ensure that the metadata has an alignment that is a
+    // multiple of the block size. liblp will take care of the rest, ensuring
+    // that all partitions are on an aligned boundary. Therefore all writes
+    // should be block-aligned, and if they are not, the table was misconfigured.
+    // Note that the default alignment is 1MiB, which is a multiple of the
+    // default block size (4096).
+    if ((sector * LP_SECTOR_SIZE) % block_size_ != 0) {
+        LERROR << "sector " << sector << " is not aligned to block size " << block_size_;
         return false;
     }
+    *block = (sector * LP_SECTOR_SIZE) / block_size_;
+    return true;
+}
+
+bool SparseBuilder::Build() {
+    std::string geometry_blob = SerializeGeometry(geometry_);
+    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;
+    }
 
     // Metadata immediately follows geometry, and we write the same metadata
-    // to all slots.
-    uint32_t metadata_block = LP_METADATA_GEOMETRY_SIZE / LP_SECTOR_SIZE;
-    metadata_blob_ = SerializeMetadata(metadata_);
-    for (size_t i = 0; i < geometry_.metadata_slot_count; i++) {
-        if (!AddData(metadata_blob_, metadata_block)) {
-            return false;
-        }
-        metadata_block += geometry_.metadata_max_size / LP_SECTOR_SIZE;
+    // 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)) {
+        return false;
     }
 
     // The backup area contains all metadata slots, and then geometry. Similar
@@ -152,31 +173,32 @@
     int64_t backup_offset = GetBackupMetadataOffset(geometry_, 0);
     uint64_t backups_start = geometry_.block_device_size + backup_offset;
     uint64_t backup_sector = backups_start / LP_SECTOR_SIZE;
-    for (size_t i = 0; i < geometry_.metadata_slot_count; i++) {
-        if (!AddData(metadata_blob_, backup_sector)) {
-            return false;
-        }
-        backup_sector += geometry_.metadata_max_size / LP_SECTOR_SIZE;
-    }
-    if (!AddData(geometry_blob_, backup_sector)) {
+
+    backup_blob_ = all_metadata + geometry_blob;
+    if (!AddData(backup_blob_, backup_sector)) {
         return false;
     }
     return true;
 }
 
-bool WriteToSparseFile(const char* file, const LpMetadata& metadata) {
-    uint64_t num_blocks =
-            AlignTo(metadata.geometry.block_device_size, LP_SECTOR_SIZE) / LP_SECTOR_SIZE;
+bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size) {
+    if (block_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
+        return false;
+    }
+    if (metadata.geometry.block_device_size % block_size != 0) {
+        LERROR << "Device size must be a multiple of the block size, " << block_size;
+        return false;
+    }
+    uint64_t num_blocks = metadata.geometry.block_device_size % block_size;
     if (num_blocks >= UINT_MAX) {
-        // libsparse counts blocks in unsigned 32-bit integers, but our block
-        // size is rather low (512 bytes), since we operate in sectors.
-        // Therefore the maximum block device size we can represent with a
-        // sparse file is 2TB for now.
+        // libsparse counts blocks in unsigned 32-bit integers, so we check to
+        // make sure we're not going to overflow.
         LERROR << "Block device is too large to encode with libsparse.";
         return false;
     }
 
-    SparseBuilder builder(metadata);
+    SparseBuilder builder(metadata, block_size);
     if (!builder.IsValid()) {
         LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size;
         return false;
diff --git a/fs_mgr/liblp/include/liblp/liblp.h b/fs_mgr/liblp/include/liblp/liblp.h
index c8d34d9..a9f01c6 100644
--- a/fs_mgr/liblp/include/liblp/liblp.h
+++ b/fs_mgr/liblp/include/liblp/liblp.h
@@ -59,7 +59,7 @@
 
 // Read/Write logical partition metadata to an image file, for diagnostics or
 // flashing.
-bool WriteToSparseFile(const char* file, const LpMetadata& metadata);
+bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size);
 bool WriteToImageFile(const char* file, const LpMetadata& metadata);
 std::unique_ptr<LpMetadata> ReadFromImageFile(const char* file);
 
diff --git a/fs_mgr/liblp/writer.cpp b/fs_mgr/liblp/writer.cpp
index 74c03bf..156319b 100644
--- a/fs_mgr/liblp/writer.cpp
+++ b/fs_mgr/liblp/writer.cpp
@@ -34,7 +34,10 @@
     LpMetadataGeometry geometry = input;
     memset(geometry.checksum, 0, sizeof(geometry.checksum));
     SHA256(&geometry, sizeof(geometry), geometry.checksum);
-    return std::string(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
+
+    std::string blob(reinterpret_cast<const char*>(&geometry), sizeof(geometry));
+    blob.resize(LP_METADATA_GEOMETRY_SIZE);
+    return blob;
 }
 
 static bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {