Do not map dynamic partitions on VABC devices

With VABC, we no longer need to map all partitions before
reading/writing, so don't try to map them.
1. modify GetPartitionDevice to return empty path for target partitions
on VABC
2. Add a separate GetMountableTargetDevice for obtaining a mountable
device path, specifically for postinstall

Test: treehugger
Change-Id: Ib1f608914fc49c677ce7389140ca79b028171191
diff --git a/aosp/boot_control_android.cc b/aosp/boot_control_android.cc
index bda65be..3b20fc2 100644
--- a/aosp/boot_control_android.cc
+++ b/aosp/boot_control_android.cc
@@ -30,8 +30,6 @@
 
 using std::string;
 
-using android::dm::DmDeviceState;
-using android::hardware::hidl_string;
 using android::hardware::Return;
 using android::hardware::boot::V1_0::BoolResult;
 using android::hardware::boot::V1_0::CommandResult;
@@ -183,4 +181,12 @@
   return dynamic_control_.get();
 }
 
+std::optional<PartitionDevice> BootControlAndroid::GetPartitionDevice(
+    const std::string& partition_name,
+    uint32_t slot,
+    uint32_t current_slot,
+    bool not_in_payload) const {
+  return dynamic_control_->GetPartitionDevice(
+      partition_name, slot, current_slot, not_in_payload);
+}
 }  // namespace chromeos_update_engine
diff --git a/aosp/boot_control_android.h b/aosp/boot_control_android.h
index e288723..926023a 100644
--- a/aosp/boot_control_android.h
+++ b/aosp/boot_control_android.h
@@ -44,6 +44,11 @@
   // BootControlInterface overrides.
   unsigned int GetNumSlots() const override;
   BootControlInterface::Slot GetCurrentSlot() const override;
+  std::optional<PartitionDevice> GetPartitionDevice(
+      const std::string& partition_name,
+      uint32_t slot,
+      uint32_t current_slot,
+      bool not_in_payload = false) const override;
   bool GetPartitionDevice(const std::string& partition_name,
                           BootControlInterface::Slot slot,
                           bool not_in_payload,
diff --git a/aosp/dynamic_partition_control_android.cc b/aosp/dynamic_partition_control_android.cc
index 3ced3e0..1575796 100644
--- a/aosp/dynamic_partition_control_android.cc
+++ b/aosp/dynamic_partition_control_android.cc
@@ -16,6 +16,7 @@
 
 #include "update_engine/aosp/dynamic_partition_control_android.h"
 
+#include <algorithm>
 #include <chrono>  // NOLINT(build/c++11) - using libsnapshot / liblp API
 #include <cstdint>
 #include <map>
@@ -960,47 +961,16 @@
     bool not_in_payload,
     std::string* device,
     bool* is_dynamic) {
-  const auto& partition_name_suffix =
-      partition_name + SlotSuffixForSlotNumber(slot);
-  std::string device_dir_str;
-  TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
-  base::FilePath device_dir(device_dir_str);
-
-  if (is_dynamic) {
-    *is_dynamic = false;
-  }
-
-  // When looking up target partition devices, treat them as static if the
-  // current payload doesn't encode them as dynamic partitions. This may happen
-  // when applying a retrofit update on top of a dynamic-partitions-enabled
-  // build.
-  if (GetDynamicPartitionsFeatureFlag().IsEnabled() &&
-      (slot == current_slot || is_target_dynamic_)) {
-    switch (GetDynamicPartitionDevice(device_dir,
-                                      partition_name_suffix,
-                                      slot,
-                                      current_slot,
-                                      not_in_payload,
-                                      device)) {
-      case DynamicPartitionDeviceStatus::SUCCESS:
-        if (is_dynamic) {
-          *is_dynamic = true;
-        }
-        return true;
-      case DynamicPartitionDeviceStatus::TRY_STATIC:
-        break;
-      case DynamicPartitionDeviceStatus::ERROR:  // fallthrough
-      default:
-        return false;
-    }
-  }
-  base::FilePath path = device_dir.Append(partition_name_suffix);
-  if (!DeviceExists(path.value())) {
-    LOG(ERROR) << "Device file " << path.value() << " does not exist.";
+  auto partition_dev = GetPartitionDevice(partition_name, slot, current_slot);
+  if (!partition_dev.has_value()) {
     return false;
   }
-
-  *device = path.value();
+  if (device) {
+    *device = std::move(partition_dev->rw_device_path);
+  }
+  if (is_dynamic) {
+    *is_dynamic = partition_dev->is_dynamic;
+  }
   return true;
 }
 
@@ -1013,6 +983,73 @@
       partition_name, slot, current_slot, false, device, nullptr);
 }
 
+static std::string GetStaticDevicePath(
+    const base::FilePath& device_dir,
+    const std::string& partition_name_suffixed) {
+  base::FilePath path = device_dir.Append(partition_name_suffixed);
+  return path.value();
+}
+
+std::optional<PartitionDevice>
+DynamicPartitionControlAndroid::GetPartitionDevice(
+    const std::string& partition_name,
+    uint32_t slot,
+    uint32_t current_slot,
+    bool not_in_payload) {
+  std::string device_dir_str;
+  if (!GetDeviceDir(&device_dir_str)) {
+    LOG(ERROR) << "Failed to GetDeviceDir()";
+    return {};
+  }
+  const base::FilePath device_dir(device_dir_str);
+  // When VABC is enabled, we can't get device path for dynamic partitions in
+  // target slot.
+  const auto& partition_name_suffix =
+      partition_name + SlotSuffixForSlotNumber(slot);
+  if (GetVirtualAbCompressionFeatureFlag().IsEnabled() &&
+      IsDynamicPartition(partition_name) && slot != current_slot) {
+    return {{.mountable_device_path =
+                 GetStaticDevicePath(device_dir, partition_name_suffix),
+             .is_dynamic = true}};
+  }
+
+  // When looking up target partition devices, treat them as static if the
+  // current payload doesn't encode them as dynamic partitions. This may happen
+  // when applying a retrofit update on top of a dynamic-partitions-enabled
+  // build.
+  std::string device;
+  if (GetDynamicPartitionsFeatureFlag().IsEnabled() &&
+      (slot == current_slot || is_target_dynamic_)) {
+    switch (GetDynamicPartitionDevice(device_dir,
+                                      partition_name_suffix,
+                                      slot,
+                                      current_slot,
+                                      not_in_payload,
+                                      &device)) {
+      case DynamicPartitionDeviceStatus::SUCCESS:
+        return {{.rw_device_path = device,
+                 .mountable_device_path = device,
+                 .is_dynamic = true}};
+
+      case DynamicPartitionDeviceStatus::TRY_STATIC:
+        break;
+      case DynamicPartitionDeviceStatus::ERROR:  // fallthrough
+      default:
+        return {};
+    }
+  }
+  // Try static partitions.
+  auto static_path = GetStaticDevicePath(device_dir, partition_name_suffix);
+  if (!DeviceExists(static_path)) {
+    LOG(ERROR) << "Device file " << static_path << " does not exist.";
+    return {};
+  }
+
+  return {{.rw_device_path = static_path,
+           .mountable_device_path = static_path,
+           .is_dynamic = false}};
+}
+
 bool DynamicPartitionControlAndroid::IsSuperBlockDevice(
     const base::FilePath& device_dir,
     uint32_t current_slot,
@@ -1294,4 +1331,5 @@
                    dynamic_partition_list_.end(),
                    partition_name) != dynamic_partition_list_.end();
 }
+
 }  // namespace chromeos_update_engine
diff --git a/aosp/dynamic_partition_control_android.h b/aosp/dynamic_partition_control_android.h
index 4a2b114..ecab6fa 100644
--- a/aosp/dynamic_partition_control_android.h
+++ b/aosp/dynamic_partition_control_android.h
@@ -35,6 +35,7 @@
  public:
   DynamicPartitionControlAndroid();
   ~DynamicPartitionControlAndroid();
+
   FeatureFlag GetDynamicPartitionsFeatureFlag() override;
   FeatureFlag GetVirtualAbFeatureFlag() override;
   FeatureFlag GetVirtualAbCompressionFeatureFlag() override;
@@ -71,6 +72,13 @@
   // Note: this function is only used by BootControl*::GetPartitionDevice.
   // Other callers should prefer BootControl*::GetPartitionDevice over
   // BootControl*::GetDynamicPartitionControl()->GetPartitionDevice().
+  std::optional<PartitionDevice> GetPartitionDevice(
+      const std::string& partition_name,
+      uint32_t slot,
+      uint32_t current_slot,
+      bool not_in_payload = false);
+  // Deprecated, please use GetPartitionDevice(string, uint32_t, uint32_t);
+  // TODO(zhangkelvin) Remove below deprecated APIs.
   bool GetPartitionDevice(const std::string& partition_name,
                           uint32_t slot,
                           uint32_t current_slot,
@@ -110,10 +118,10 @@
   virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
       const std::string& super_device, uint32_t slot);
 
-  // Retrieves metadata from |super_device| at slot |source_slot|. And modifies
-  // the metadata so that during updates, the metadata can be written to
-  // |target_slot|. In particular, on retrofit devices, the returned metadata
-  // automatically includes block devices at |target_slot|.
+  // Retrieves metadata from |super_device| at slot |source_slot|. And
+  // modifies the metadata so that during updates, the metadata can be written
+  // to |target_slot|. In particular, on retrofit devices, the returned
+  // metadata automatically includes block devices at |target_slot|.
   virtual std::unique_ptr<android::fs_mgr::MetadataBuilder> LoadMetadataBuilder(
       const std::string& super_device,
       uint32_t source_slot,
@@ -210,6 +218,9 @@
 
   bool MapAllPartitions() override;
 
+  void SetSourceSlot(uint32_t slot) { source_slot_ = slot; }
+  void SetTargetSlot(uint32_t slot) { target_slot_ = slot; }
+
  private:
   friend class DynamicPartitionControlAndroidTest;
   friend class SnapshotPartitionTestP;
@@ -231,8 +242,8 @@
                                uint32_t target_slot,
                                const DeltaArchiveManifest& manifest);
 
-  // Helper for PreparePartitionsForUpdate. Used for snapshotted partitions for
-  // Virtual A/B update.
+  // Helper for PreparePartitionsForUpdate. Used for snapshotted partitions
+  // for Virtual A/B update.
   bool PrepareSnapshotPartitionsForUpdate(uint32_t source_slot,
                                           uint32_t target_slot,
                                           const DeltaArchiveManifest& manifest,
@@ -270,17 +281,20 @@
   // Returns true if metadata is expected to be mounted, false otherwise.
   // Note that it returns false on non-Virtual A/B devices.
   //
-  // Almost all functions of SnapshotManager depends on metadata being mounted.
+  // Almost all functions of SnapshotManager depends on metadata being
+  // mounted.
   // - In Android mode for Virtual A/B devices, assume it is mounted. If not,
   //   let caller fails when calling into SnapshotManager.
-  // - In recovery for Virtual A/B devices, it is possible that metadata is not
+  // - In recovery for Virtual A/B devices, it is possible that metadata is
+  // not
   //   formatted, hence it cannot be mounted. Caller should not call into
   //   SnapshotManager.
-  // - On non-Virtual A/B devices, updates do not depend on metadata partition.
+  // - On non-Virtual A/B devices, updates do not depend on metadata
+  // partition.
   //   Caller should not call into SnapshotManager.
   //
-  // This function does NOT mount metadata partition. Use EnsureMetadataMounted
-  // to mount metadata partition.
+  // This function does NOT mount metadata partition. Use
+  // EnsureMetadataMounted to mount metadata partition.
   bool ExpectMetadataMounted();
 
   // Ensure /metadata is mounted. Returns true if successful, false otherwise.
@@ -303,6 +317,7 @@
   // Whether the target partitions should be loaded as dynamic partitions. Set
   // by PreparePartitionsForUpdate() per each update.
   bool is_target_dynamic_ = false;
+
   uint32_t source_slot_ = UINT32_MAX;
   uint32_t target_slot_ = UINT32_MAX;
   std::vector<std::string> dynamic_partition_list_;
diff --git a/aosp/dynamic_partition_control_android_unittest.cc b/aosp/dynamic_partition_control_android_unittest.cc
index 7e751db..af5ae2c 100644
--- a/aosp/dynamic_partition_control_android_unittest.cc
+++ b/aosp/dynamic_partition_control_android_unittest.cc
@@ -16,6 +16,7 @@
 
 #include "update_engine/aosp/dynamic_partition_control_android.h"
 
+#include <algorithm>
 #include <set>
 #include <vector>
 
@@ -38,6 +39,7 @@
 using testing::_;
 using testing::AnyNumber;
 using testing::AnyOf;
+using testing::AtLeast;
 using testing::Invoke;
 using testing::NiceMock;
 using testing::Not;
@@ -55,6 +57,8 @@
         .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
     ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
         .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
+    ON_CALL(dynamicControl(), GetVirtualAbCompressionFeatureFlag())
+        .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
 
     ON_CALL(dynamicControl(), GetDeviceDir(_))
         .WillByDefault(Invoke([](auto path) {
@@ -217,6 +221,8 @@
   void SetUp() override {
     DynamicPartitionControlAndroidTest::SetUp();
     SetSlots(GetParam());
+    dynamicControl().SetSourceSlot(source());
+    dynamicControl().SetTargetSlot(target());
   }
 };
 
@@ -386,6 +392,84 @@
   EXPECT_EQ(GetDevice(T("bar")), bar_device);
 }
 
+TEST_P(DynamicPartitionControlAndroidTestP, GetMountableDevicePath) {
+  ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
+      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+  ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
+      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+  ON_CALL(dynamicControl(), GetVirtualAbCompressionFeatureFlag())
+      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::NONE)));
+  ON_CALL(dynamicControl(), IsDynamicPartition(_)).WillByDefault(Return(true));
+
+  EXPECT_CALL(dynamicControl(),
+              DeviceExists(AnyOf(GetDevice(S("vendor")),
+                                 GetDevice(T("vendor")),
+                                 GetDevice(S("system")),
+                                 GetDevice(T("system")))))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(
+      dynamicControl(),
+      GetState(AnyOf(S("vendor"), T("vendor"), S("system"), T("system"))))
+      .WillRepeatedly(Return(DmDeviceState::ACTIVE));
+
+  SetMetadata(source(), {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}});
+  SetMetadata(target(), {{T("system"), 2_GiB}, {T("vendor"), 1_GiB}});
+  std::string device;
+  ASSERT_TRUE(dynamicControl().GetPartitionDevice(
+      "system", source(), source(), &device));
+  ASSERT_EQ(GetDmDevice(S("system")), device);
+
+  ASSERT_TRUE(dynamicControl().GetPartitionDevice(
+      "system", target(), source(), &device));
+  ASSERT_EQ(GetDevice(T("system")), device);
+
+  // If VABC is disabled, mountable device path should be same as device path.
+  auto device_info =
+      dynamicControl().GetPartitionDevice("system", target(), source());
+  ASSERT_TRUE(device_info.has_value());
+  ASSERT_EQ(device_info->mountable_device_path, device);
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, GetMountableDevicePathVABC) {
+  ON_CALL(dynamicControl(), GetDynamicPartitionsFeatureFlag())
+      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+  ON_CALL(dynamicControl(), GetVirtualAbFeatureFlag())
+      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+  ON_CALL(dynamicControl(), GetVirtualAbCompressionFeatureFlag())
+      .WillByDefault(Return(FeatureFlag(FeatureFlag::Value::LAUNCH)));
+  EXPECT_CALL(dynamicControl(), IsDynamicPartition(_))
+      .Times(AtLeast(1))
+      .WillRepeatedly(Return(true));
+
+  EXPECT_CALL(dynamicControl(),
+              DeviceExists(AnyOf(GetDevice(S("vendor")),
+                                 GetDevice(T("vendor")),
+                                 GetDevice(S("system")),
+                                 GetDevice(T("system")))))
+      .WillRepeatedly(Return(true));
+  EXPECT_CALL(
+      dynamicControl(),
+      GetState(AnyOf(S("vendor"), T("vendor"), S("system"), T("system"))))
+      .WillRepeatedly(Return(DmDeviceState::ACTIVE));
+
+  SetMetadata(source(), {{S("system"), 2_GiB}, {S("vendor"), 1_GiB}});
+  SetMetadata(target(), {{T("system"), 2_GiB}, {T("vendor"), 1_GiB}});
+
+  std::string device;
+  ASSERT_TRUE(dynamicControl().GetPartitionDevice(
+      "system", source(), source(), &device));
+  ASSERT_EQ(GetDmDevice(S("system")), device);
+
+  ASSERT_TRUE(dynamicControl().GetPartitionDevice(
+      "system", target(), source(), &device));
+  ASSERT_EQ("", device);
+
+  auto device_info =
+      dynamicControl().GetPartitionDevice("system", target(), source());
+  ASSERT_TRUE(device_info.has_value());
+  ASSERT_EQ(device_info->mountable_device_path, GetDevice(T("system")));
+}
+
 TEST_P(DynamicPartitionControlAndroidTestP,
        GetPartitionDeviceWhenResumingUpdate) {
   // Static partition bar_{a,b} exists.
diff --git a/aosp/dynamic_partition_test_utils.h b/aosp/dynamic_partition_test_utils.h
index c7be1cb..c518382 100644
--- a/aosp/dynamic_partition_test_utils.h
+++ b/aosp/dynamic_partition_test_utils.h
@@ -47,7 +47,7 @@
 
 constexpr const uint32_t kMaxNumSlots = 2;
 constexpr const char* kSlotSuffixes[kMaxNumSlots] = {"_a", "_b"};
-constexpr const char* kFakeDevicePath = "/fake/dev/path/";
+constexpr std::string_view kFakeDevicePath = "/fake/dev/path/";
 constexpr const char* kFakeDmDevicePath = "/fake/dm/dev/path/";
 constexpr const uint32_t kFakeMetadataSize = 65536;
 constexpr const char* kDefaultGroup = "foo";
@@ -112,7 +112,7 @@
 }
 
 inline std::string GetDevice(const std::string& name) {
-  return kFakeDevicePath + name;
+  return std::string(kFakeDevicePath) + name;
 }
 
 inline std::string GetDmDevice(const std::string& name) {
diff --git a/aosp/mock_dynamic_partition_control_android.h b/aosp/mock_dynamic_partition_control_android.h
index 8d8ddb3..1d4bb14 100644
--- a/aosp/mock_dynamic_partition_control_android.h
+++ b/aosp/mock_dynamic_partition_control_android.h
@@ -70,6 +70,7 @@
   MOCK_METHOD(FeatureFlag, GetDynamicPartitionsFeatureFlag, (), (override));
   MOCK_METHOD(std::string, GetSuperPartitionName, (uint32_t), (override));
   MOCK_METHOD(FeatureFlag, GetVirtualAbFeatureFlag, (), (override));
+  MOCK_METHOD(FeatureFlag, GetVirtualAbCompressionFeatureFlag, (), (override));
   MOCK_METHOD(bool, FinishUpdate, (bool), (override));
   MOCK_METHOD(bool,
               GetSystemOtherPath,
@@ -132,6 +133,8 @@
     return DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate(
         source_slot, target_slot, manifest, delete_source);
   }
+  using DynamicPartitionControlAndroid::SetSourceSlot;
+  using DynamicPartitionControlAndroid::SetTargetSlot;
 };
 
 }  // namespace chromeos_update_engine
diff --git a/common/boot_control_interface.h b/common/boot_control_interface.h
index c93de5c..3b61add 100644
--- a/common/boot_control_interface.h
+++ b/common/boot_control_interface.h
@@ -75,6 +75,11 @@
                                   Slot slot,
                                   std::string* device) const = 0;
 
+  virtual std::optional<PartitionDevice> GetPartitionDevice(
+      const std::string& partition_name,
+      uint32_t slot,
+      uint32_t current_slot,
+      bool not_in_payload = false) const = 0;
   // Returns whether the passed |slot| is marked as bootable. Returns false if
   // the slot is invalid.
   virtual bool IsSlotBootable(Slot slot) const = 0;
diff --git a/common/boot_control_stub.cc b/common/boot_control_stub.cc
index 907f670..a1cc055 100644
--- a/common/boot_control_stub.cc
+++ b/common/boot_control_stub.cc
@@ -44,6 +44,15 @@
   return false;
 }
 
+std::optional<PartitionDevice> BootControlStub::GetPartitionDevice(
+    const std::string& partition_name,
+    uint32_t slot,
+    uint32_t current_slot,
+    bool not_in_payload) const {
+  LOG(ERROR) << __FUNCTION__ << " should never be called.";
+  return {};
+}
+
 bool BootControlStub::GetPartitionDevice(const string& partition_name,
                                          Slot slot,
                                          string* device) const {
diff --git a/common/boot_control_stub.h b/common/boot_control_stub.h
index a1bdb96..dcddbae 100644
--- a/common/boot_control_stub.h
+++ b/common/boot_control_stub.h
@@ -48,6 +48,11 @@
   bool GetPartitionDevice(const std::string& partition_name,
                           BootControlInterface::Slot slot,
                           std::string* device) const override;
+  std::optional<PartitionDevice> GetPartitionDevice(
+      const std::string& partition_name,
+      uint32_t slot,
+      uint32_t current_slot,
+      bool not_in_payload = false) const override;
   bool IsSlotBootable(BootControlInterface::Slot slot) const override;
   bool MarkSlotUnbootable(BootControlInterface::Slot slot) override;
   bool SetActiveBootSlot(BootControlInterface::Slot slot) override;
diff --git a/common/dynamic_partition_control_interface.h b/common/dynamic_partition_control_interface.h
index 1362c19..855d6e8 100644
--- a/common/dynamic_partition_control_interface.h
+++ b/common/dynamic_partition_control_interface.h
@@ -36,6 +36,12 @@
 
 namespace chromeos_update_engine {
 
+struct PartitionDevice {
+  std::string rw_device_path;
+  std::string mountable_device_path;
+  bool is_dynamic;
+};
+
 struct FeatureFlag {
   enum class Value { NONE = 0, RETROFIT, LAUNCH };
   constexpr explicit FeatureFlag(Value value) : value_(value) {}
diff --git a/common/fake_boot_control.h b/common/fake_boot_control.h
index 98b93e6..fc7839d 100644
--- a/common/fake_boot_control.h
+++ b/common/fake_boot_control.h
@@ -51,14 +51,16 @@
                           bool not_in_payload,
                           std::string* device,
                           bool* is_dynamic) const override {
-    if (slot >= num_slots_)
+    auto dev =
+        GetPartitionDevice(partition_name, slot, current_slot_, not_in_payload);
+    if (!dev.has_value()) {
       return false;
-    auto part_it = devices_[slot].find(partition_name);
-    if (part_it == devices_[slot].end())
-      return false;
-    *device = part_it->second;
-    if (is_dynamic != nullptr) {
-      *is_dynamic = false;
+    }
+    if (is_dynamic) {
+      *is_dynamic = dev->is_dynamic;
+    }
+    if (device) {
+      *device = dev->rw_device_path;
     }
     return true;
   }
@@ -120,6 +122,25 @@
     return dynamic_partition_control_.get();
   }
 
+  std::optional<PartitionDevice> GetPartitionDevice(
+      const std::string& partition_name,
+      uint32_t slot,
+      uint32_t current_slot,
+      bool not_in_payload = false) const override {
+    if (slot >= devices_.size()) {
+      return {};
+    }
+    auto device_path = devices_[slot].find(partition_name);
+    if (device_path == devices_[slot].end()) {
+      return {};
+    }
+    PartitionDevice device;
+    device.is_dynamic = false;
+    device.rw_device_path = device_path->second;
+    device.mountable_device_path = device.rw_device_path;
+    return device;
+  }
+
  private:
   BootControlInterface::Slot num_slots_{2};
   BootControlInterface::Slot current_slot_{0};
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index 4a37836..f4bbeb4 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -109,18 +109,19 @@
   for (Partition& partition : partitions) {
     if (source_slot != BootControlInterface::kInvalidSlot &&
         partition.source_size > 0) {
-      result = boot_control->GetPartitionDevice(
-                   partition.name, source_slot, &partition.source_path) &&
-               result;
+      TEST_AND_RETURN_FALSE(boot_control->GetPartitionDevice(
+          partition.name, source_slot, &partition.source_path));
     } else {
       partition.source_path.clear();
     }
 
     if (target_slot != BootControlInterface::kInvalidSlot &&
         partition.target_size > 0) {
-      result = boot_control->GetPartitionDevice(
-                   partition.name, target_slot, &partition.target_path) &&
-               result;
+      auto device = boot_control->GetPartitionDevice(
+          partition.name, target_slot, source_slot);
+      TEST_AND_RETURN_FALSE(device.has_value());
+      partition.target_path = device->rw_device_path;
+      partition.postinstall_mount_device = device->mountable_device_path;
     } else {
       partition.target_path.clear();
     }
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index ee1a72b..7068f6c 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -98,9 +98,17 @@
     uint64_t source_size{0};
     brillo::Blob source_hash;
 
+    // |target_path| is intended to be a path to block device, which you can
+    // open with |open| syscall and perform regular unix style read/write.
+    // For VABC, this will be empty. As you can't read/write VABC devices with
+    // regular syscall.
     std::string target_path;
+    // |mountable_target_device| is intended to be a path to block device which
+    // can be used for mounting this block device's underlying filesystem.
+    std::string postinstall_mount_device;
     uint64_t target_size{0};
     brillo::Blob target_hash;
+
     uint32_t block_size{0};
 
     // Whether we should run the postinstall script from this partition and the
diff --git a/payload_consumer/postinstall_runner_action.cc b/payload_consumer/postinstall_runner_action.cc
index d51241f..bd49639 100644
--- a/payload_consumer/postinstall_runner_action.cc
+++ b/payload_consumer/postinstall_runner_action.cc
@@ -50,7 +50,6 @@
 
 namespace chromeos_update_engine {
 
-using brillo::MessageLoop;
 using std::string;
 using std::vector;
 
@@ -127,7 +126,7 @@
   const InstallPlan::Partition& partition =
       install_plan_.partitions[current_partition_];
 
-  const string mountable_device = partition.target_path;
+  const string mountable_device = partition.postinstall_mount_device;
   if (mountable_device.empty()) {
     LOG(ERROR) << "Cannot make mountable device from " << partition.target_path;
     return CompletePostinstall(ErrorCode::kPostinstallRunnerError);
diff --git a/payload_consumer/postinstall_runner_action_unittest.cc b/payload_consumer/postinstall_runner_action_unittest.cc
index cce86e9..9b330d9 100644
--- a/payload_consumer/postinstall_runner_action_unittest.cc
+++ b/payload_consumer/postinstall_runner_action_unittest.cc
@@ -195,6 +195,7 @@
   InstallPlan::Partition part;
   part.name = "part";
   part.target_path = device_path;
+  part.postinstall_mount_device = device_path;
   part.run_postinstall = true;
   part.postinstall_path = postinstall_program;
   InstallPlan install_plan;
@@ -356,6 +357,7 @@
   InstallPlan::Partition part;
   part.name = "part";
   part.target_path = "/dev/null";
+  part.postinstall_mount_device = "/dev/null";
   part.run_postinstall = true;
   part.postinstall_path = kPostinstallDefaultScript;
   part.postinstall_optional = true;