Merge "liblp: Add a gtest for sparse image creation."
diff --git a/fs_mgr/liblp/Android.bp b/fs_mgr/liblp/Android.bp
index 69dc065..bbdec5b 100644
--- a/fs_mgr/liblp/Android.bp
+++ b/fs_mgr/liblp/Android.bp
@@ -51,6 +51,7 @@
         "liblp",
         "libbase",
         "libfs_mgr",
+        "libsparse",
     ],
     srcs: [
         "builder_test.cpp",
diff --git a/fs_mgr/liblp/images.cpp b/fs_mgr/liblp/images.cpp
index 742b1d0..511f7be 100644
--- a/fs_mgr/liblp/images.cpp
+++ b/fs_mgr/liblp/images.cpp
@@ -19,8 +19,6 @@
 #include <limits.h>
 
 #include <android-base/file.h>
-#include <android-base/unique_fd.h>
-#include <sparse/sparse.h>
 
 #include "reader.h"
 #include "utility.h"
@@ -89,41 +87,36 @@
     return WriteToImageFile(fd, input);
 }
 
-// We use an object to build the sparse file since it requires that data
-// pointers be held alive until the sparse file is destroyed. It's easier
-// to do this when the data pointers are all in one place.
-class SparseBuilder {
-  public:
-    SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
-                  const std::map<std::string, std::string>& images);
-
-    bool Build();
-    bool Export(const char* file);
-    bool IsValid() const { return file_ != nullptr; }
-
-  private:
-    bool AddData(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);
-
-    const LpMetadata& metadata_;
-    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::map<std::string, std::string> images_;
-    std::vector<android::base::unique_fd> temp_fds_;
-};
-
 SparseBuilder::SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
                              const std::map<std::string, std::string>& images)
     : metadata_(metadata),
       geometry_(metadata.geometry),
       block_size_(block_size),
-      file_(sparse_file_new(block_size_, geometry_.block_device_size), sparse_file_destroy),
-      images_(images) {}
+      file_(nullptr, sparse_file_destroy),
+      images_(images) {
+    if (block_size % LP_SECTOR_SIZE != 0) {
+        LERROR << "Block size must be a multiple of the sector size, " << LP_SECTOR_SIZE;
+        return;
+    }
+    if (metadata.geometry.block_device_size % block_size != 0) {
+        LERROR << "Device size must be a multiple of the block size, " << block_size;
+        return;
+    }
+    if (metadata.geometry.metadata_max_size % block_size != 0) {
+        LERROR << "Metadata max size must be a multiple of the block size, " << block_size;
+        return;
+    }
+
+    uint64_t num_blocks = metadata.geometry.block_device_size % block_size;
+    if (num_blocks >= UINT_MAX) {
+        // 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;
+    }
+
+    file_.reset(sparse_file_new(block_size_, geometry_.block_device_size));
+}
 
 bool SparseBuilder::Export(const char* file) {
     android::base::unique_fd fd(open(file, O_CREAT | O_RDWR | O_TRUNC, 0644));
@@ -206,8 +199,8 @@
     // 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);
-    uint64_t backups_start = geometry_.block_device_size + backup_offset;
-    uint64_t backup_sector = backups_start / LP_SECTOR_SIZE;
+    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)) {
@@ -336,22 +329,6 @@
 
 bool WriteToSparseFile(const char* file, const LpMetadata& metadata, uint32_t block_size,
                        const std::map<std::string, std::string>& images) {
-    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, 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, block_size, images);
     if (!builder.IsValid()) {
         LERROR << "Could not allocate sparse file of size " << metadata.geometry.block_device_size;
diff --git a/fs_mgr/liblp/images.h b/fs_mgr/liblp/images.h
index 3a999b8..2031e33 100644
--- a/fs_mgr/liblp/images.h
+++ b/fs_mgr/liblp/images.h
@@ -14,7 +14,14 @@
  * limitations under the License.
  */
 
+#include <stdint.h>
+#include <map>
+#include <memory>
+#include <string>
+
+#include <android-base/unique_fd.h>
 #include <liblp/liblp.h>
+#include <sparse/sparse.h>
 
 namespace android {
 namespace fs_mgr {
@@ -25,5 +32,35 @@
 bool WriteToImageFile(const char* file, const LpMetadata& metadata);
 bool WriteToImageFile(int fd, const LpMetadata& metadata);
 
+// We use an object to build the sparse file since it requires that data
+// pointers be held alive until the sparse file is destroyed. It's easier
+// to do this when the data pointers are all in one place.
+class SparseBuilder {
+  public:
+    SparseBuilder(const LpMetadata& metadata, uint32_t block_size,
+                  const std::map<std::string, std::string>& images);
+
+    bool Build();
+    bool Export(const char* file);
+    bool IsValid() const { return file_ != nullptr; }
+
+    sparse_file* file() const { return file_.get(); }
+
+  private:
+    bool AddData(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);
+
+    const LpMetadata& metadata_;
+    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::map<std::string, std::string> images_;
+    std::vector<android::base::unique_fd> temp_fds_;
+};
+
 }  // namespace fs_mgr
 }  // namespace android
diff --git a/fs_mgr/liblp/io_test.cpp b/fs_mgr/liblp/io_test.cpp
index 01de3ac..322219b 100644
--- a/fs_mgr/liblp/io_test.cpp
+++ b/fs_mgr/liblp/io_test.cpp
@@ -535,3 +535,36 @@
     ASSERT_GE(new_table->partitions.size(), 1);
     ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));
 }
+
+// Test that writing a sparse image can be read back.
+TEST(liblp, FlashSparseImage) {
+    unique_fd fd = CreateFakeDisk();
+    ASSERT_GE(fd, 0);
+
+    BlockDeviceInfo device_info(kDiskSize, 0, 0, 512);
+    unique_ptr<MetadataBuilder> builder =
+            MetadataBuilder::New(device_info, kMetadataSize, kMetadataSlots);
+    ASSERT_NE(builder, nullptr);
+    ASSERT_TRUE(AddDefaultPartitions(builder.get()));
+
+    unique_ptr<LpMetadata> exported = builder->Export();
+    ASSERT_NE(exported, nullptr);
+
+    // Build the sparse file.
+    SparseBuilder sparse(*exported.get(), 512, {});
+    ASSERT_TRUE(sparse.IsValid());
+    sparse_file_verbose(sparse.file());
+    ASSERT_TRUE(sparse.Build());
+
+    // 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);
+    ASSERT_EQ(ret, 0);
+
+    // Verify that we can read both sets of metadata.
+    LpMetadataGeometry geometry;
+    ASSERT_TRUE(ReadPrimaryGeometry(fd.get(), &geometry));
+    ASSERT_TRUE(ReadBackupGeometry(fd.get(), &geometry));
+    ASSERT_NE(ReadPrimaryMetadata(fd.get(), geometry, 0), nullptr);
+    ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr);
+}
diff --git a/fs_mgr/liblp/reader.cpp b/fs_mgr/liblp/reader.cpp
index 005d493..87411cb 100644
--- a/fs_mgr/liblp/reader.cpp
+++ b/fs_mgr/liblp/reader.cpp
@@ -120,10 +120,7 @@
     return true;
 }
 
-// Read and validate geometry information from a block device that holds
-// logical partitions. If the information is corrupted, this will attempt
-// to read it from a secondary backup location.
-bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
+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) {
@@ -134,11 +131,12 @@
         PERROR << __PRETTY_FUNCTION__ << "read " << LP_METADATA_GEOMETRY_SIZE << " bytes failed";
         return false;
     }
-    if (ParseGeometry(buffer.get(), geometry)) {
-        return true;
-    }
+    return ParseGeometry(buffer.get(), geometry);
+}
 
+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) {
         PERROR << __PRETTY_FUNCTION__ << "lseek failed, offset " << -LP_METADATA_GEOMETRY_SIZE;
         return false;
@@ -151,6 +149,16 @@
     return ParseGeometry(buffer.get(), geometry);
 }
 
+// Read and validate geometry information from a block device that holds
+// logical partitions. If the information is corrupted, this will attempt
+// to read it from a secondary backup location.
+bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {
+    if (ReadPrimaryGeometry(fd, geometry)) {
+        return true;
+    }
+    return ReadBackupGeometry(fd, geometry);
+}
+
 static bool ValidateTableBounds(const LpMetadataHeader& header,
                                 const LpMetadataTableDescriptor& table) {
     if (table.offset > header.tables_size) {
diff --git a/fs_mgr/liblp/reader.h b/fs_mgr/liblp/reader.h
index 9f6ca6e..24b2611 100644
--- a/fs_mgr/liblp/reader.h
+++ b/fs_mgr/liblp/reader.h
@@ -36,6 +36,8 @@
 std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,
                                           size_t size);
 bool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);
+bool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry);
+bool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry);
 
 // These functions assume a valid geometry and slot number.
 std::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,