libsnapshot_fuzzer: map super image

... instead of operating on the image file directly.
Test: run it
Bug: 154633114

Change-Id: Id04c0d15d0d52483647716f8bfb0b8ee1a2876d9
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz.cpp b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
index 12a0531..3671bb0 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz.cpp
@@ -125,14 +125,14 @@
     static FuzzSnapshotManager fuzz_snapshot_manager;
     [[maybe_unused]] static auto stop_logging = StopLoggingAfterGlobalInit();
 
-    CHECK(env.InitOk());
+    env.CheckSoftReset();
     FuzzData fuzz_data(data, size);
 
     auto snapshot_manager_data = fuzz_data.Consume<SnapshotManagerFuzzData>();
     if (!snapshot_manager_data.has_value()) {
         return 0;
     }
-    auto snapshot_manager = env.CreateSnapshotManager(snapshot_manager_data.value());
+    auto snapshot_manager = env.CheckCreateSnapshotManager(snapshot_manager_data.value());
     CHECK(snapshot_manager);
 
     fuzz_snapshot_manager.DepleteData(snapshot_manager.get(), &fuzz_data);
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
index d5e3e96..7829193 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.cpp
@@ -19,6 +19,7 @@
 #include <sys/stat.h>
 #include <sysexits.h>
 
+#include <chrono>
 #include <string>
 
 #include <android-base/file.h>
@@ -29,17 +30,27 @@
 #include <storage_literals/storage_literals.h>
 
 #include "snapshot_fuzz_utils.h"
+#include "utility.h"
+
+// Prepends the errno string, but it is good enough.
+#ifndef PCHECK
+#define PCHECK(x) CHECK(x) << strerror(errno) << ": "
+#endif
 
 using namespace android::storage_literals;
+using namespace std::chrono_literals;
 using namespace std::string_literals;
 
 using android::base::StringPrintf;
 using android::base::unique_fd;
 using android::base::WriteStringToFile;
+using android::dm::LoopControl;
 using android::fiemap::IImageManager;
 using android::fiemap::ImageManager;
 
-static const char MNT_DIR[] = "/mnt";
+// This directory is exempted from pinning in ImageManager.
+static const char MNT_DIR[] = "/data/gsi/ota/test/";
+
 static const char FAKE_ROOT_NAME[] = "snapshot_fuzz";
 static const auto SUPER_IMAGE_SIZE = 16_MiB;
 static const auto FAKE_ROOT_SIZE = 64_MiB;
@@ -128,6 +139,9 @@
 class AutoMemBasedDir : public AutoDevice {
   public:
     static std::unique_ptr<AutoMemBasedDir> New(const std::string& name, uint64_t size) {
+        if (!Mkdir(MNT_DIR)) {
+            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+        }
         auto ret = std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(name));
         ret->auto_delete_mount_dir_ = AutoDeleteDir::New(ret->mount_path());
         if (!ret->auto_delete_mount_dir_->HasDevice()) {
@@ -137,20 +151,31 @@
         if (!ret->auto_umount_mount_point_->HasDevice()) {
             return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
         }
-        // path() does not need to be deleted upon destruction, hence it is not wrapped with
-        // AutoDeleteDir.
-        if (!Mkdir(ret->path())) {
+        // tmp_path() and persist_path does not need to be deleted upon destruction, hence it is
+        // not wrapped with AutoDeleteDir.
+        if (!Mkdir(ret->tmp_path())) {
+            return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
+        }
+        if (!Mkdir(ret->persist_path())) {
             return std::unique_ptr<AutoMemBasedDir>(new AutoMemBasedDir(""));
         }
         return ret;
     }
-    // Return the scratch directory.
-    std::string path() const {
+    // Return the temporary scratch directory.
+    std::string tmp_path() const {
         CHECK(HasDevice());
-        return mount_path() + "/root";
+        return mount_path() + "/tmp";
     }
-    // Delete all contents in path() and start over. path() itself is re-created.
-    bool SoftReset() { return RmdirRecursive(path()) && Mkdir(path()); }
+    // Return the temporary scratch directory.
+    std::string persist_path() const {
+        CHECK(HasDevice());
+        return mount_path() + "/persist";
+    }
+    // Delete all contents in tmp_path() and start over. tmp_path() itself is re-created.
+    void CheckSoftReset() {
+        PCHECK(RmdirRecursive(tmp_path()));
+        PCHECK(Mkdir(tmp_path()));
+    }
 
   private:
     AutoMemBasedDir(const std::string& name) : AutoDevice(name) {}
@@ -164,64 +189,83 @@
 
 SnapshotFuzzEnv::SnapshotFuzzEnv() {
     fake_root_ = AutoMemBasedDir::New(FAKE_ROOT_NAME, FAKE_ROOT_SIZE);
+    CHECK(fake_root_ != nullptr);
+    CHECK(fake_root_->HasDevice());
+    loop_control_ = std::make_unique<LoopControl>();
+    mapped_super_ = CheckMapSuper(fake_root_->persist_path(), loop_control_.get(), &fake_super_);
 }
 
 SnapshotFuzzEnv::~SnapshotFuzzEnv() = default;
 
-bool SnapshotFuzzEnv::InitOk() const {
-    if (fake_root_ == nullptr || !fake_root_->HasDevice()) return false;
-    return true;
+void CheckZeroFill(const std::string& file, size_t size) {
+    std::string zeros(size, '\0');
+    PCHECK(WriteStringToFile(zeros, file)) << "Cannot write zeros to " << file;
 }
 
-bool SnapshotFuzzEnv::SoftReset() {
-    return fake_root_->SoftReset();
+void SnapshotFuzzEnv::CheckSoftReset() {
+    fake_root_->CheckSoftReset();
+    CheckZeroFill(super(), SUPER_IMAGE_SIZE);
 }
 
-std::unique_ptr<IImageManager> SnapshotFuzzEnv::CreateFakeImageManager(
-        const std::string& fake_root) {
-    auto images_dir = fake_root + "/images";
+std::unique_ptr<IImageManager> SnapshotFuzzEnv::CheckCreateFakeImageManager(
+        const std::string& path) {
+    auto images_dir = path + "/images";
     auto metadata_dir = images_dir + "/metadata";
     auto data_dir = images_dir + "/data";
 
-    if (!Mkdir(images_dir) || !Mkdir(metadata_dir) || !Mkdir(data_dir)) {
-        return nullptr;
-    }
+    PCHECK(Mkdir(images_dir));
+    PCHECK(Mkdir(metadata_dir));
+    PCHECK(Mkdir(data_dir));
     return ImageManager::Open(metadata_dir, data_dir);
 }
 
-std::unique_ptr<TestPartitionOpener> SnapshotFuzzEnv::CreatePartitionOpener(
-        const std::string& fake_root) {
-    auto fake_super = fake_root + "/super.img";
-    std::string zeros(SUPER_IMAGE_SIZE, '\0');
-
-    if (!WriteStringToFile(zeros, fake_super)) {
-        PLOG(ERROR) << "Cannot write zeros to " << fake_super;
-        return nullptr;
-    }
-
-    return std::make_unique<TestPartitionOpener>(fake_super);
+// Helper to create a loop device for a file.
+static void CheckCreateLoopDevice(LoopControl* control, const std::string& file,
+                                  const std::chrono::milliseconds& timeout_ms, std::string* path) {
+    static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;
+    android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags));
+    PCHECK(file_fd >= 0) << "Could not open file: " << file;
+    CHECK(control->Attach(file_fd, timeout_ms, path))
+            << "Could not create loop device for: " << file;
 }
 
-std::string SnapshotFuzzEnv::root() const {
-    CHECK(InitOk());
-    return fake_root_->path();
+class AutoDetachLoopDevice : public AutoDevice {
+  public:
+    AutoDetachLoopDevice(LoopControl* control, const std::string& device)
+        : AutoDevice(device), control_(control) {}
+    ~AutoDetachLoopDevice() { control_->Detach(name_); }
+
+  private:
+    LoopControl* control_;
+};
+
+std::unique_ptr<AutoDevice> SnapshotFuzzEnv::CheckMapSuper(const std::string& fake_persist_path,
+                                                           LoopControl* control,
+                                                           std::string* fake_super) {
+    auto super_img = fake_persist_path + "/super.img";
+    CheckZeroFill(super_img, SUPER_IMAGE_SIZE);
+    CheckCreateLoopDevice(control, super_img, 1s, fake_super);
+
+    return std::make_unique<AutoDetachLoopDevice>(control, *fake_super);
 }
 
-std::unique_ptr<ISnapshotManager> SnapshotFuzzEnv::CreateSnapshotManager(
+std::unique_ptr<ISnapshotManager> SnapshotFuzzEnv::CheckCreateSnapshotManager(
         const SnapshotManagerFuzzData& data) {
-    // TODO(b/154633114): create valid super partition according to fuzz data
-    auto partition_opener = CreatePartitionOpener(root());
-    if (partition_opener == nullptr) return nullptr;
-    auto metadata_dir = root() + "/snapshot_metadata";
-    if (!Mkdir(metadata_dir)) return nullptr;
+    auto partition_opener = std::make_unique<TestPartitionOpener>(super());
+    auto metadata_dir = fake_root_->tmp_path() + "/snapshot_metadata";
+    PCHECK(Mkdir(metadata_dir));
 
     auto device_info = new SnapshotFuzzDeviceInfo(data.device_info_data,
                                                   std::move(partition_opener), metadata_dir);
     auto snapshot = SnapshotManager::New(device_info /* takes ownership */);
-    snapshot->images_ = CreateFakeImageManager(root());
+    snapshot->images_ = CheckCreateFakeImageManager(fake_root_->tmp_path());
     snapshot->has_local_image_manager_ = data.is_local_image_manager;
 
     return snapshot;
 }
 
+const std::string& SnapshotFuzzEnv::super() const {
+    return fake_super_;
+}
+
 }  // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index 32910a9..8c0d5dd 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -16,6 +16,7 @@
 
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
+#include <libdm/loop_control.h>
 #include <libfiemap/image_manager.h>
 #include <libsnapshot/auto_device.h>
 #include <libsnapshot/test_helpers.h>
@@ -52,27 +53,30 @@
     SnapshotFuzzEnv();
     ~SnapshotFuzzEnv();
 
-    // Check if environment is initialized properly.
-    bool InitOk() const;
-
-    // A scratch directory for the test to play around with. The scratch directory
-    // is backed by tmpfs. SoftReset() clears the directory.
-    std::string root() const;
-
     // Soft reset part of the environment before running the next test.
-    bool SoftReset();
+    // Abort if fails.
+    void CheckSoftReset();
 
     // Create a snapshot manager for this test run.
     // Client is responsible for maintaining the lifetime of |data| over the life time of
     // ISnapshotManager.
-    std::unique_ptr<ISnapshotManager> CreateSnapshotManager(const SnapshotManagerFuzzData& data);
+    std::unique_ptr<ISnapshotManager> CheckCreateSnapshotManager(
+            const SnapshotManagerFuzzData& data);
+
+    // Return path to super partition.
+    const std::string& super() const;
 
   private:
     std::unique_ptr<AutoMemBasedDir> fake_root_;
+    std::unique_ptr<android::dm::LoopControl> loop_control_;
+    std::unique_ptr<AutoDevice> mapped_super_;
+    std::string fake_super_;
 
-    static std::unique_ptr<android::fiemap::IImageManager> CreateFakeImageManager(
-            const std::string& fake_root);
-    static std::unique_ptr<TestPartitionOpener> CreatePartitionOpener(const std::string& fake_root);
+    static std::unique_ptr<android::fiemap::IImageManager> CheckCreateFakeImageManager(
+            const std::string& fake_tmp_path);
+    static std::unique_ptr<AutoDevice> CheckMapSuper(const std::string& fake_persist_path,
+                                                     android::dm::LoopControl* control,
+                                                     std::string* fake_super);
 };
 
 class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo {