Merge changes from topic "dsu-overlayfs"

* changes:
  Refactor fs_mgr_overlayfs_teardown()
  Enable overlayFS on DSU system
diff --git a/fastboot/device/flashing.cpp b/fastboot/device/flashing.cpp
index 1bf4c9c..333ca50 100644
--- a/fastboot/device/flashing.cpp
+++ b/fastboot/device/flashing.cpp
@@ -67,7 +67,7 @@
 
         if ((partition + device->GetCurrentSlot()) == partition_name) {
             mount_metadata.emplace();
-            fs_mgr_overlayfs_teardown(entry.mount_point.c_str());
+            android::fs_mgr::TeardownAllOverlayForMountPoint(entry.mount_point);
         }
     }
 }
@@ -194,7 +194,7 @@
         if (!FlashPartitionTable(super_name, *new_metadata.get())) {
             return device->WriteFail("Unable to flash new partition table");
         }
-        fs_mgr_overlayfs_teardown();
+        android::fs_mgr::TeardownAllOverlayForMountPoint();
         sync();
         return device->WriteOkay("Successfully flashed partition table");
     }
@@ -234,7 +234,7 @@
     if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {
         return device->WriteFail("Unable to write new partition table");
     }
-    fs_mgr_overlayfs_teardown();
+    android::fs_mgr::TeardownAllOverlayForMountPoint();
     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 a7704de..899978f 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -73,6 +73,25 @@
     return ret;
 }
 
+bool fs_mgr_in_recovery() {
+    // Check the existence of recovery binary instead of using the compile time
+    // macro, because first-stage-init is compiled with __ANDROID_RECOVERY__
+    // defined, albeit not in recovery. More details: system/core/init/README.md
+    return fs_mgr_access("/system/bin/recovery");
+}
+
+bool fs_mgr_is_dsu_running() {
+    // Since android::gsi::CanBootIntoGsi() or android::gsi::MarkSystemAsGsi() is
+    // never called in recovery, the return value of android::gsi::IsGsiRunning()
+    // is not well-defined. In this case, just return false as being in recovery
+    // implies not running a DSU system.
+    if (fs_mgr_in_recovery()) return false;
+    auto saved_errno = errno;
+    auto ret = android::gsi::IsGsiRunning();
+    errno = saved_errno;
+    return ret;
+}
+
 // determine if a filesystem is available
 bool fs_mgr_overlayfs_filesystem_available(const std::string& filesystem) {
     std::string filesystems;
@@ -113,8 +132,11 @@
 namespace android {
 namespace fs_mgr {
 
-void MapScratchPartitionIfNeeded(Fstab*,
-                                 const std::function<bool(const std::set<std::string>&)>&) {}
+void MapScratchPartitionIfNeeded(Fstab*, const std::function<bool(const std::set<std::string>&)>&) {
+}
+
+void TeardownAllOverlayForMountPoint(const std::string&) {}
+
 }  // namespace fs_mgr
 }  // namespace android
 
@@ -171,6 +193,10 @@
 
 // Note: this is meant only for recovery/first-stage init.
 bool ScratchIsOnData() {
+    // The scratch partition of DSU is managed by gsid.
+    if (fs_mgr_is_dsu_running()) {
+        return false;
+    }
     return fs_mgr_access(kScratchImageMetadata);
 }
 
@@ -464,6 +490,12 @@
     // umount and delete kScratchMountPoint storage if we have logical partitions
     if (overlay != kScratchMountPoint) return true;
 
+    // Validation check.
+    if (fs_mgr_is_dsu_running()) {
+        LERROR << "Destroying DSU scratch is not allowed.";
+        return false;
+    }
+
     auto save_errno = errno;
     if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
         fs_mgr_overlayfs_umount_scratch();
@@ -512,10 +544,13 @@
 }
 
 bool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,
-                                   bool* change) {
+                                   bool* change, bool* should_destroy_scratch = nullptr) {
     const auto top = overlay + kOverlayTopDir;
 
-    if (!fs_mgr_access(top)) return fs_mgr_overlayfs_teardown_scratch(overlay, change);
+    if (!fs_mgr_access(top)) {
+        if (should_destroy_scratch) *should_destroy_scratch = true;
+        return true;
+    }
 
     auto cleanup_all = mount_point.empty();
     const auto partition_name = android::base::Basename(mount_point);
@@ -571,7 +606,7 @@
             PERROR << "rmdir " << top;
         }
     }
-    if (cleanup_all) ret &= fs_mgr_overlayfs_teardown_scratch(overlay, change);
+    if (should_destroy_scratch) *should_destroy_scratch = cleanup_all;
     return ret;
 }
 
@@ -881,12 +916,29 @@
     return "";
 }
 
+// Note: The scratch partition of DSU is managed by gsid, and should be initialized during
+// first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.
+static std::string GetDsuScratchDevice() {
+    auto& dm = DeviceMapper::Instance();
+    std::string device;
+    if (dm.GetState(android::gsi::kDsuScratch) != DmDeviceState::INVALID &&
+        dm.GetDmDevicePathByName(android::gsi::kDsuScratch, &device)) {
+        return device;
+    }
+    return "";
+}
+
 // This returns the scratch device that was detected during early boot (first-
 // stage init). If the device was created later, for example during setup for
 // the adb remount command, it can return an empty string since it does not
 // query ImageManager. (Note that ImageManager in first-stage init will always
 // use device-mapper, since /data is not available to use loop devices.)
 static std::string GetBootScratchDevice() {
+    // Note: fs_mgr_is_dsu_running() always returns false in recovery or fastbootd.
+    if (fs_mgr_is_dsu_running()) {
+        return GetDsuScratchDevice();
+    }
+
     auto& dm = DeviceMapper::Instance();
 
     // If there is a scratch partition allocated in /data or on super, we
@@ -1108,6 +1160,14 @@
 
 bool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,
                                      bool* partition_exists, bool* change) {
+    // Use the DSU scratch device managed by gsid if within a DSU system.
+    if (fs_mgr_is_dsu_running()) {
+        *scratch_device = GetDsuScratchDevice();
+        *partition_exists = !scratch_device->empty();
+        *change = false;
+        return *partition_exists;
+    }
+
     // Try a physical partition first.
     *scratch_device = GetPhysicalScratchDevice();
     if (!scratch_device->empty() && fs_mgr_rw_access(*scratch_device)) {
@@ -1166,12 +1226,8 @@
 bool fs_mgr_overlayfs_invalid() {
     if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) return true;
 
-    // in recovery, fastbootd, or gsi mode, not allowed!
-    if (fs_mgr_access("/system/bin/recovery")) return true;
-    auto save_errno = errno;
-    auto ret = android::gsi::IsGsiRunning();
-    errno = save_errno;
-    return ret;
+    // in recovery or fastbootd, not allowed!
+    return fs_mgr_in_recovery();
 }
 
 }  // namespace
@@ -1314,6 +1370,8 @@
     return ret;
 }
 
+// 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();
@@ -1321,6 +1379,11 @@
         return true;
     }
 
+    if (!fs_mgr_in_recovery()) {
+        errno = EINVAL;
+        return false;
+    }
+
     auto partition_name = android::base::Basename(kScratchMountPoint);
 
     // Check for scratch on /data first, before looking for a modified super
@@ -1362,10 +1425,27 @@
     return true;
 }
 
-static void UnmapScratchDevice() {
-    // This should only be reachable in recovery, where scratch is not
-    // automatically mapped and therefore can be unmapped.
-    DestroyLogicalPartition(android::base::Basename(kScratchMountPoint));
+// This should only be reachable in recovery, where DSU scratch is not
+// automatically mapped.
+static bool MapDsuScratchDevice(std::string* device) {
+    std::string dsu_slot;
+    if (!android::gsi::IsGsiInstalled() || !android::gsi::GetActiveDsu(&dsu_slot) ||
+        dsu_slot.empty()) {
+        // Nothing to do if no DSU installation present.
+        return false;
+    }
+
+    auto images = IImageManager::Open("dsu/" + dsu_slot, 10s);
+    if (!images || !images->BackingImageExists(android::gsi::kDsuScratch)) {
+        // Nothing to do if DSU scratch device doesn't exist.
+        return false;
+    }
+
+    images->UnmapImageDevice(android::gsi::kDsuScratch);
+    if (!images->MapImageDevice(android::gsi::kDsuScratch, 10s, device)) {
+        return false;
+    }
+    return true;
 }
 
 // Returns false if teardown not permitted, errno set to last error.
@@ -1377,21 +1457,27 @@
     // If scratch exists, but is not mounted, lets gain access to clean
     // specific override entries.
     auto mount_scratch = false;
-    bool unmap = false;
     if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {
-        std::string scratch_device;
-        if (EnsureScratchMapped(&scratch_device, &unmap)) {
+        std::string scratch_device = GetBootScratchDevice();
+        if (!scratch_device.empty()) {
             mount_scratch = fs_mgr_overlayfs_mount_scratch(scratch_device,
                                                            fs_mgr_overlayfs_scratch_mount_type());
         }
     }
+    bool should_destroy_scratch = false;
     for (const auto& overlay_mount_point : kOverlayMountPoints) {
         ret &= fs_mgr_overlayfs_teardown_one(
-                overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "", change);
+                overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : "", change,
+                overlay_mount_point == kScratchMountPoint ? &should_destroy_scratch : nullptr);
+    }
+    // Do not attempt to destroy DSU scratch if within a DSU system,
+    // because DSU scratch partition is managed by gsid.
+    if (should_destroy_scratch && !fs_mgr_is_dsu_running()) {
+        ret &= fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, change);
     }
     if (fs_mgr_overlayfs_valid() == OverlayfsValidResult::kNotSupported) {
         // After obligatory teardown to make sure everything is clean, but if
-        // we didn't want overlayfs in the the first place, we do not want to
+        // we didn't want overlayfs in the first place, we do not want to
         // waste time on a reboot (or reboot request message).
         if (change) *change = false;
     }
@@ -1405,9 +1491,6 @@
     if (mount_scratch) {
         fs_mgr_overlayfs_umount_scratch();
     }
-    if (unmap) {
-        UnmapScratchDevice();
-    }
     return ret;
 }
 
@@ -1475,6 +1558,54 @@
     }
 }
 
+void TeardownAllOverlayForMountPoint(const std::string& mount_point) {
+    if (!fs_mgr_in_recovery()) {
+        LERROR << __FUNCTION__ << "(): must be called within recovery.";
+        return;
+    }
+
+    // Empty string means teardown everything.
+    const std::string teardown_dir = mount_point.empty() ? "" : fs_mgr_mount_point(mount_point);
+    constexpr bool* ignore_change = nullptr;
+
+    // Teardown legacy overlay mount points that's not backed by a scratch device.
+    for (const auto& overlay_mount_point : kOverlayMountPoints) {
+        if (overlay_mount_point == kScratchMountPoint) {
+            continue;
+        }
+        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)) {
+        fs_mgr_overlayfs_umount_scratch();
+        if (fs_mgr_overlayfs_mount_scratch(scratch_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);
+            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.
+    if (MapDsuScratchDevice(&scratch_device)) {
+        fs_mgr_overlayfs_umount_scratch();
+        if (fs_mgr_overlayfs_mount_scratch(scratch_device, fs_mgr_overlayfs_scratch_mount_type())) {
+            fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change);
+            fs_mgr_overlayfs_umount_scratch();
+        }
+        DestroyLogicalPartition(android::gsi::kDsuScratch);
+    }
+}
+
 }  // namespace fs_mgr
 }  // namespace android
 
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 34aded9..d45e2de 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -49,5 +49,12 @@
                                  const std::function<bool(const std::set<std::string>&)>& init);
 void CleanupOldScratchFiles();
 
+// Teardown overlays of all sources (cache dir, scratch device, DSU) for |mount_point|.
+// Teardown all overlays if |mount_point| is empty.
+//
+// Note: This should be called if and only if in recovery or fastbootd to teardown
+// overlays if any partition is flashed or updated.
+void TeardownAllOverlayForMountPoint(const std::string& mount_point = {});
+
 }  // namespace fs_mgr
 }  // namespace android