Merge "libfiemap: Add a helper to verify image fiemaps."
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index 2c14c8a..e3f5716 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -744,6 +744,116 @@
     return CreateLogicalPartitions(*metadata.get(), data_partition_name);
 }
 
+std::ostream& operator<<(std::ostream& os, android::fs_mgr::Extent* extent) {
+    if (auto e = extent->AsLinearExtent()) {
+        return os << "<begin:" << e->physical_sector() << ", end:" << e->end_sector()
+                  << ", device:" << e->device_index() << ">";
+    }
+    return os << "<unknown>";
+}
+
+static bool CompareExtent(android::fs_mgr::Extent* a, android::fs_mgr::Extent* b) {
+    if (auto linear_a = a->AsLinearExtent()) {
+        auto linear_b = b->AsLinearExtent();
+        if (!linear_b) {
+            return false;
+        }
+        return linear_a->physical_sector() == linear_b->physical_sector() &&
+               linear_a->num_sectors() == linear_b->num_sectors() &&
+               linear_a->device_index() == linear_b->device_index();
+    }
+    return false;
+}
+
+static bool CompareExtents(android::fs_mgr::Partition* oldp, android::fs_mgr::Partition* newp) {
+    const auto& old_extents = oldp->extents();
+    const auto& new_extents = newp->extents();
+
+    auto old_iter = old_extents.begin();
+    auto new_iter = new_extents.begin();
+    while (true) {
+        if (old_iter == old_extents.end()) {
+            if (new_iter == new_extents.end()) {
+                break;
+            }
+            LOG(ERROR) << "Unexpected extent added: " << (*new_iter);
+            return false;
+        }
+        if (new_iter == new_extents.end()) {
+            LOG(ERROR) << "Unexpected extent removed: " << (*old_iter);
+            return false;
+        }
+
+        if (!CompareExtent(old_iter->get(), new_iter->get())) {
+            LOG(ERROR) << "Extents do not match: " << old_iter->get() << ", " << new_iter->get();
+            return false;
+        }
+
+        old_iter++;
+        new_iter++;
+    }
+    return true;
+}
+
+bool ImageManager::ValidateImageMaps() {
+    if (!MetadataExists(metadata_dir_)) {
+        LOG(INFO) << "ImageManager skipping verification; no images for " << metadata_dir_;
+        return true;
+    }
+
+    auto metadata = OpenMetadata(metadata_dir_);
+    if (!metadata) {
+        LOG(ERROR) << "ImageManager skipping verification; failed to open " << metadata_dir_;
+        return true;
+    }
+
+    for (const auto& partition : metadata->partitions) {
+        auto name = GetPartitionName(partition);
+        auto image_path = GetImageHeaderPath(name);
+        auto fiemap = SplitFiemap::Open(image_path);
+        if (fiemap == nullptr) {
+            LOG(ERROR) << "SplitFiemap::Open(\"" << image_path << "\") failed";
+            return false;
+        }
+        if (!fiemap->HasPinnedExtents()) {
+            LOG(ERROR) << "Image doesn't have pinned extents: " << image_path;
+            return false;
+        }
+
+        android::fs_mgr::PartitionOpener opener;
+        auto builder = android::fs_mgr::MetadataBuilder::New(*metadata.get(), &opener);
+        if (!builder) {
+            LOG(ERROR) << "Could not create metadata builder: " << image_path;
+            return false;
+        }
+
+        auto new_p = builder->AddPartition("_temp_for_verify", 0);
+        if (!new_p) {
+            LOG(ERROR) << "Could not add temporary partition: " << image_path;
+            return false;
+        }
+
+        auto partition_size = android::fs_mgr::GetPartitionSize(*metadata.get(), partition);
+        if (!FillPartitionExtents(builder.get(), new_p, fiemap.get(), partition_size)) {
+            LOG(ERROR) << "Could not fill partition extents: " << image_path;
+            return false;
+        }
+
+        auto old_p = builder->FindPartition(name);
+        if (!old_p) {
+            LOG(ERROR) << "Could not find metadata for " << image_path;
+            return false;
+        }
+
+        if (!CompareExtents(old_p, new_p)) {
+            LOG(ERROR) << "Metadata for " << image_path << " does not match fiemap";
+            return false;
+        }
+    }
+
+    return true;
+}
+
 std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
                                                  const std::chrono::milliseconds& timeout_ms,
                                                  const std::string& name) {
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index 3c87000..b23a7f7 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -174,6 +174,9 @@
     // Writes |bytes| zeros at the beginning of the passed image
     FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes);
 
+    // Validate that all images still have the same block map.
+    bool ValidateImageMaps();
+
   private:
     ImageManager(const std::string& metadata_dir, const std::string& data_dir,
                  const DeviceInfo& device_info);
diff --git a/fs_mgr/libfiemap/metadata.h b/fs_mgr/libfiemap/metadata.h
index 4eb3ad5..30b2c61 100644
--- a/fs_mgr/libfiemap/metadata.h
+++ b/fs_mgr/libfiemap/metadata.h
@@ -20,6 +20,7 @@
 #include <string>
 
 #include <libfiemap/split_fiemap_writer.h>
+#include <liblp/builder.h>
 #include <liblp/liblp.h>
 
 namespace android {
@@ -34,5 +35,9 @@
 bool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name);
 bool RemoveAllMetadata(const std::string& dir);
 
+bool FillPartitionExtents(android::fs_mgr::MetadataBuilder* builder,
+                          android::fs_mgr::Partition* partition, android::fiemap::SplitFiemap* file,
+                          uint64_t partition_size);
+
 }  // namespace fiemap
 }  // namespace android