recovery: Support writing to Virtual A/B partitions

Change-Id: Ice74e460242a58140fe31240b9fc464848b3aeea
diff --git a/install/install.cpp b/install/install.cpp
index cb573de..5bb23e3 100644
--- a/install/install.cpp
+++ b/install/install.cpp
@@ -46,6 +46,7 @@
 #include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 
+#include "install/snapshot_utils.h"
 #include "install/spl_check.h"
 #include "install/wipe_data.h"
 #include "install/wipe_device.h"
@@ -409,6 +410,7 @@
   bool device_supports_ab = android::base::GetBoolProperty("ro.build.ab_update", false);
   bool ab_device_supports_nonab = true;
   bool device_only_supports_ab = device_supports_ab && !ab_device_supports_nonab;
+  bool device_supports_virtual_ab = android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
 
   const auto current_spl = android::base::GetProperty("ro.build.version.security_patch", "");
   if (ViolatesSPLDowngrade(zip, current_spl)) {
@@ -431,6 +433,15 @@
     }
   }
 
+  if (!package_is_ab && !logical_partitions_mapped()) {
+    CreateSnapshotPartitions();
+    map_logical_partitions();
+  } else if (package_is_ab && device_supports_virtual_ab && logical_partitions_mapped()) {
+    LOG(ERROR) << "Logical partitions are mapped. "
+               << "Please reboot recovery before installing an OTA update.";
+    return INSTALL_ERROR;
+  }
+
   ReadSourceTargetBuild(metadata, log_buffer);
 
   // The updater in child process writes to the pipe to communicate with recovery.
diff --git a/recovery_utils/include/recovery_utils/roots.h b/recovery_utils/include/recovery_utils/roots.h
index 783eea4..cbc8b07 100644
--- a/recovery_utils/include/recovery_utils/roots.h
+++ b/recovery_utils/include/recovery_utils/roots.h
@@ -61,4 +61,6 @@
 // Returns true if there is /cache in the volumes.
 bool HasCache();
 
-bool logical_partitions_mapped();
\ No newline at end of file
+void map_logical_partitions();
+
+bool logical_partitions_mapped();
diff --git a/recovery_utils/roots.cpp b/recovery_utils/roots.cpp
index 9e76997..010b98c 100644
--- a/recovery_utils/roots.cpp
+++ b/recovery_utils/roots.cpp
@@ -45,6 +45,8 @@
 using android::fs_mgr::Fstab;
 using android::fs_mgr::FstabEntry;
 using android::fs_mgr::ReadDefaultFstab;
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
 
 static Fstab fstab;
 
@@ -316,8 +318,6 @@
   return format_volume(volume, "");
 }
 
-static bool logical_partitions_auto_mapped = false;
-
 int setup_install_mounts() {
   if (fstab.empty()) {
     LOG(ERROR) << "can't set up install mounts: no fstab loaded";
@@ -341,16 +341,6 @@
       }
     }
   }
-  // Map logical partitions
-  if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
-      !logical_partitions_mapped()) {
-    std::string super_name = fs_mgr_get_super_partition_name();
-    if (!android::fs_mgr::CreateLogicalPartitions("/dev/block/by-name/" + super_name)) {
-      LOG(ERROR) << "Failed to map logical partitions";
-    } else {
-      logical_partitions_auto_mapped = true;
-    }
-  }
   return 0;
 }
 
@@ -360,6 +350,35 @@
   return has_cache;
 }
 
+static bool logical_partitions_auto_mapped = false;
+
+void map_logical_partitions() {
+  if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false) &&
+      !logical_partitions_mapped()) {
+    std::string super_name = fs_mgr_get_super_partition_name();
+    if (!android::fs_mgr::CreateLogicalPartitions("/dev/block/by-name/" + super_name)) {
+      LOG(ERROR) << "Failed to map logical partitions";
+    } else {
+      logical_partitions_auto_mapped = true;
+    }
+  }
+}
+
+bool dm_find_system() {
+  auto rec = GetEntryForPath(&fstab, android::fs_mgr::GetSystemRoot());
+  if (!rec->fs_mgr_flags.logical) {
+    return false;
+  }
+  // If the fstab entry for system it's a path instead of a name, then it was already mapped
+  if (rec->blk_device[0] != '/') {
+    if (DeviceMapper::Instance().GetState(rec->blk_device) == DmDeviceState::INVALID) {
+      return false;
+    }
+  }
+  return true;
+}
+
 bool logical_partitions_mapped() {
-  return android::fs_mgr::LogicalPartitionsMapped() || logical_partitions_auto_mapped;
+  return android::fs_mgr::LogicalPartitionsMapped() || logical_partitions_auto_mapped ||
+      dm_find_system();
 }