fastbootd: Remove all scratch partitions on update-super.

This ensures that the dynamic "scratch" partition is removed when doing
a flashall operation.

If "scratch" is on /data, disable the partition. Add IsImageDisabled to
ImageManager so EnsureScratchMapped can skip mapping it. Also, fix
"scratch" not getting unmapped if on /data.

Bug: 205987817
Test: adb remount
      adb sync
      adb reboot fastboot
      fastboot flashall --skip-reboot # no errors
Test: adb-remount-test.sh
Change-Id: I4b9702e1dac15fb663635506fb50a8274e1e10d1
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 44dc81f..22f8363 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -186,6 +186,11 @@
     return result;
 }
 
+static void RemoveScratchPartition() {
+    AutoMountMetadata mount_metadata;
+    android::fs_mgr::TeardownAllOverlayForMountPoint();
+}
+
 bool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {
     std::vector<char> data = std::move(device->download_data());
     if (data.empty()) {
@@ -218,7 +223,7 @@
         if (!FlashPartitionTable(super_name, *new_metadata.get())) {
             return device->WriteFail("Unable to flash new partition table");
         }
-        android::fs_mgr::TeardownAllOverlayForMountPoint();
+        RemoveScratchPartition();
         sync();
         return device->WriteOkay("Successfully flashed partition table");
     }
@@ -262,7 +267,7 @@
     if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
         return device->WriteFail("Unable to write new partition table");
     }
-    android::fs_mgr::TeardownAllOverlayForMountPoint();
+    RemoveScratchPartition();
     sync();
     return device->WriteOkay("Successfully updated partition table");
 }
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 2da5b0f..7deb763 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -33,6 +33,7 @@
 
 #include <algorithm>
 #include <memory>
+#include <optional>
 #include <string>
 #include <vector>
 
@@ -1396,18 +1397,35 @@
     return ret;
 }
 
+struct MapInfo {
+    // If set, partition is owned by ImageManager.
+    std::unique_ptr<IImageManager> images;
+    // If set, and images is null, this is a DAP partition.
+    std::string name;
+    // If set, and images and name are empty, this is a non-dynamic partition.
+    std::string device;
+
+    MapInfo() = default;
+    MapInfo(MapInfo&&) = default;
+    ~MapInfo() {
+        if (images) {
+            images->UnmapImageDevice(name);
+        } else if (!name.empty()) {
+            DestroyLogicalPartition(name);
+        }
+    }
+};
+
 // Note: This function never returns the DSU scratch device in recovery or fastbootd,
 // because the DSU scratch is created in the first-stage-mount, which is not run in recovery.
-static bool EnsureScratchMapped(std::string* device, bool* mapped) {
-    *mapped = false;
-    *device = GetBootScratchDevice();
-    if (!device->empty()) {
-        return true;
+static std::optional<MapInfo> EnsureScratchMapped() {
+    MapInfo info;
+    info.device = GetBootScratchDevice();
+    if (!info.device.empty()) {
+        return {std::move(info)};
     }
-
     if (!fs_mgr_in_recovery()) {
-        errno = EINVAL;
-        return false;
+        return {};
     }
 
     auto partition_name = android::base::Basename(kScratchMountPoint);
@@ -1417,11 +1435,15 @@
     // would otherwise always be mapped.
     auto images = IImageManager::Open("remount", 10s);
     if (images && images->BackingImageExists(partition_name)) {
-        if (!images->MapImageDevice(partition_name, 10s, device)) {
-            return false;
+        if (images->IsImageDisabled(partition_name)) {
+            return {};
         }
-        *mapped = true;
-        return true;
+        if (!images->MapImageDevice(partition_name, 10s, &info.device)) {
+            return {};
+        }
+        info.name = partition_name;
+        info.images = std::move(images);
+        return {std::move(info)};
     }
 
     // Avoid uart spam by first checking for a scratch partition.
@@ -1429,12 +1451,12 @@
     auto super_device = fs_mgr_overlayfs_super_device(metadata_slot);
     auto metadata = ReadCurrentMetadata(super_device);
     if (!metadata) {
-        return false;
+        return {};
     }
 
     auto partition = FindPartition(*metadata.get(), partition_name);
     if (!partition) {
-        return false;
+        return {};
     }
 
     CreateLogicalPartitionParams params = {
@@ -1444,11 +1466,11 @@
             .force_writable = true,
             .timeout_ms = 10s,
     };
-    if (!CreateLogicalPartition(params, device)) {
-        return false;
+    if (!CreateLogicalPartition(params, &info.device)) {
+        return {};
     }
-    *mapped = true;
-    return true;
+    info.name = partition_name;
+    return {std::move(info)};
 }
 
 // This should only be reachable in recovery, where DSU scratch is not
@@ -1602,26 +1624,35 @@
         fs_mgr_overlayfs_teardown_one(overlay_mount_point, teardown_dir, ignore_change);
     }
 
-    // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
-    bool mapped = false;
-    std::string scratch_device;
-    if (EnsureScratchMapped(&scratch_device, &mapped)) {
+    if (mount_point.empty()) {
+        // Throw away the entire partition.
+        auto partition_name = android::base::Basename(kScratchMountPoint);
+        auto images = IImageManager::Open("remount", 10s);
+        if (images && images->BackingImageExists(partition_name)) {
+            if (images->DisableImage(partition_name)) {
+                LOG(INFO) << "Disabled scratch partition for: " << kScratchMountPoint;
+            } else {
+                LOG(ERROR) << "Unable to disable scratch partition for " << kScratchMountPoint;
+            }
+        }
+    }
+
+    if (auto info = EnsureScratchMapped(); info.has_value()) {
+        // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.
         fs_mgr_overlayfs_umount_scratch();
-        if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
+        if (fs_mgr_overlayfs_mount_scratch(info->device, fs_mgr_overlayfs_scratch_mount_type())) {
             bool should_destroy_scratch = false;
             fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,
                                           &should_destroy_scratch);
+            fs_mgr_overlayfs_umount_scratch();
             if (should_destroy_scratch) {
                 fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);
             }
-            fs_mgr_overlayfs_umount_scratch();
-        }
-        if (mapped) {
-            DestroyLogicalPartition(android::base::Basename(kScratchMountPoint));
         }
     }
 
     // Teardown DSU overlay if present.
+    std::string scratch_device;
     if (MapDsuScratchDevice(&scratch_device)) {
         fs_mgr_overlayfs_umount_scratch();
         if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
diff --git a/fs_mgr/libfiemap/binder.cpp b/fs_mgr/libfiemap/binder.cpp
index 31a57a8..003e6ed 100644
--- a/fs_mgr/libfiemap/binder.cpp
+++ b/fs_mgr/libfiemap/binder.cpp
@@ -66,6 +66,7 @@
     bool RemoveDisabledImages() override;
     bool GetMappedImageDevice(const std::string& name, std::string* device) override;
     bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
+    bool IsImageDisabled(const std::string& name) override;
 
     std::vector<std::string> GetAllBackingImages() override;
 
@@ -219,6 +220,17 @@
     return !device->empty();
 }
 
+bool ImageManagerBinder::IsImageDisabled(const std::string& name) {
+    bool retval;
+    auto status = manager_->isImageDisabled(name, &retval);
+    if (!status.isOk()) {
+        LOG(ERROR) << __PRETTY_FUNCTION__
+                   << " binder returned: " << status.exceptionMessage().string();
+        return false;
+    }
+    return retval;
+}
+
 bool ImageManagerBinder::MapAllImages(const std::function<bool(std::set<std::string>)>&) {
     LOG(ERROR) << __PRETTY_FUNCTION__ << " not available over binder";
     return false;
diff --git a/fs_mgr/libfiemap/image_manager.cpp b/fs_mgr/libfiemap/image_manager.cpp
index e3f5716..7ccdbf9 100644
--- a/fs_mgr/libfiemap/image_manager.cpp
+++ b/fs_mgr/libfiemap/image_manager.cpp
@@ -854,6 +854,24 @@
     return true;
 }
 
+bool ImageManager::IsImageDisabled(const std::string& name) {
+    if (!MetadataExists(metadata_dir_)) {
+        return true;
+    }
+
+    auto metadata = OpenMetadata(metadata_dir_);
+    if (!metadata) {
+        return false;
+    }
+
+    auto partition = FindPartition(*metadata.get(), name);
+    if (!partition) {
+        return false;
+    }
+
+    return !!(partition->attributes & LP_PARTITION_ATTR_DISABLED);
+}
+
 std::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,
                                                  const std::chrono::milliseconds& timeout_ms,
                                                  const std::string& name) {
diff --git a/fs_mgr/libfiemap/image_test.cpp b/fs_mgr/libfiemap/image_test.cpp
index 6d09751..7472949 100644
--- a/fs_mgr/libfiemap/image_test.cpp
+++ b/fs_mgr/libfiemap/image_test.cpp
@@ -119,6 +119,7 @@
     ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
     ASSERT_TRUE(manager_->BackingImageExists(base_name_));
     ASSERT_TRUE(manager_->DisableImage(base_name_));
+    ASSERT_TRUE(manager_->IsImageDisabled(base_name_));
     ASSERT_TRUE(manager_->RemoveDisabledImages());
     ASSERT_TRUE(!manager_->BackingImageExists(base_name_));
 }
diff --git a/fs_mgr/libfiemap/include/libfiemap/image_manager.h b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
index b23a7f7..00dd661 100644
--- a/fs_mgr/libfiemap/include/libfiemap/image_manager.h
+++ b/fs_mgr/libfiemap/include/libfiemap/image_manager.h
@@ -131,6 +131,9 @@
     virtual bool RemoveAllImages() = 0;
 
     virtual bool UnmapImageIfExists(const std::string& name);
+
+    // Returns whether DisableImage() was called.
+    virtual bool IsImageDisabled(const std::string& name) = 0;
 };
 
 class ImageManager final : public IImageManager {
@@ -162,6 +165,7 @@
     bool RemoveDisabledImages() override;
     bool GetMappedImageDevice(const std::string& name, std::string* device) override;
     bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;
+    bool IsImageDisabled(const std::string& name) override;
 
     std::vector<std::string> GetAllBackingImages();
 
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index c1a5af7..a648384 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -199,6 +199,7 @@
     bool UnmapImageIfExists(const std::string& name) override {
         return impl_->UnmapImageIfExists(name);
     }
+    bool IsImageDisabled(const std::string& name) override { return impl_->IsImageDisabled(name); }
 
   private:
     std::unique_ptr<android::fiemap::IImageManager> impl_;