Add DynamicPartitionControl::EraseSystemOtherAvbFooter
Erase AVB footer of system other partition prior to any updates so that if
an update overwrites it partially, and the device rolled back (or even
before we finish writing the partition), and the device factory resets,
mapping system_other as /postinstall won't trigger verity errors and
reboots the device.
Fixes: 152444348
Test: apply update, rollback, then FDR
Test: apply update, then set sys.cppreopt=requested; observe that
/postinstall cannot be mounted.
Change-Id: I62e5bb8f4c31d9a1beff485c47fc4b07a3a5686b
(cherry picked from commit 2969290920696611a67aed184baf71cac062b416)
Merged-In: I62e5bb8f4c31d9a1beff485c47fc4b07a3a5686b
diff --git a/Android.bp b/Android.bp
index e3116f5..07eee63 100644
--- a/Android.bp
+++ b/Android.bp
@@ -211,6 +211,9 @@
"android.hardware.boot@1.0",
"android.hardware.boot@1.1",
],
+ header_libs: [
+ "avb_headers",
+ ],
target: {
recovery: {
static_libs: [
diff --git a/dynamic_partition_control_android.cc b/dynamic_partition_control_android.cc
index 09f61ad..1e92f45 100644
--- a/dynamic_partition_control_android.cc
+++ b/dynamic_partition_control_android.cc
@@ -32,6 +32,7 @@
#include <fs_mgr.h>
#include <fs_mgr_dm_linear.h>
#include <fs_mgr_overlayfs.h>
+#include <libavb/libavb.h>
#include <libdm/dm.h>
#include <libsnapshot/snapshot.h>
@@ -42,12 +43,14 @@
#include "update_engine/payload_consumer/delta_performer.h"
using android::base::GetBoolProperty;
+using android::base::GetProperty;
using android::base::Join;
using android::dm::DeviceMapper;
using android::dm::DmDeviceState;
using android::fs_mgr::CreateLogicalPartition;
using android::fs_mgr::CreateLogicalPartitionParams;
using android::fs_mgr::DestroyLogicalPartition;
+using android::fs_mgr::Fstab;
using android::fs_mgr::MetadataBuilder;
using android::fs_mgr::Partition;
using android::fs_mgr::PartitionOpener;
@@ -64,6 +67,7 @@
"ro.boot.dynamic_partitions_retrofit";
constexpr char kVirtualAbEnabled[] = "ro.virtual_ab.enabled";
constexpr char kVirtualAbRetrofit[] = "ro.virtual_ab.retrofit";
+constexpr char kPostinstallFstabPrefix[] = "ro.postinstall.fstab.prefix";
// Map timeout for dynamic partitions.
constexpr std::chrono::milliseconds kMapTimeout{1000};
// Map timeout for dynamic partitions with snapshots. Since several devices
@@ -401,6 +405,15 @@
<< "run adb enable-verity to deactivate if required and try again.";
}
+ if (GetVirtualAbFeatureFlag().IsEnabled() && metadata_device_ == nullptr) {
+ metadata_device_ = snapshot_->EnsureMetadataMounted();
+ TEST_AND_RETURN_FALSE(metadata_device_ != nullptr);
+ }
+
+ if (update) {
+ TEST_AND_RETURN_FALSE(EraseSystemOtherAvbFooter(source_slot, target_slot));
+ }
+
if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) {
return true;
}
@@ -421,11 +434,6 @@
target_supports_snapshot_ =
manifest.dynamic_partition_metadata().snapshot_enabled();
- if (GetVirtualAbFeatureFlag().IsEnabled()) {
- metadata_device_ = snapshot_->EnsureMetadataMounted();
- TEST_AND_RETURN_FALSE(metadata_device_ != nullptr);
- }
-
if (!update)
return true;
@@ -471,6 +479,202 @@
source_slot, target_slot, manifest, delete_source);
}
+namespace {
+// Try our best to erase AVB footer.
+class AvbFooterEraser {
+ public:
+ explicit AvbFooterEraser(const std::string& path) : path_(path) {}
+ bool Erase() {
+ // Try to mark the block device read-only. Ignore any
+ // failure since this won't work when passing regular files.
+ ignore_result(utils::SetBlockDeviceReadOnly(path_, false /* readonly */));
+
+ fd_.reset(new EintrSafeFileDescriptor());
+ int flags = O_WRONLY | O_TRUNC | O_CLOEXEC | O_SYNC;
+ TEST_AND_RETURN_FALSE(fd_->Open(path_.c_str(), flags));
+
+ // Need to write end-AVB_FOOTER_SIZE to end.
+ static_assert(AVB_FOOTER_SIZE > 0);
+ off64_t offset = fd_->Seek(-AVB_FOOTER_SIZE, SEEK_END);
+ TEST_AND_RETURN_FALSE_ERRNO(offset >= 0);
+ uint64_t write_size = AVB_FOOTER_SIZE;
+ LOG(INFO) << "Zeroing " << path_ << " @ [" << offset << ", "
+ << (offset + write_size) << "] (" << write_size << " bytes)";
+ brillo::Blob zeros(write_size);
+ TEST_AND_RETURN_FALSE(utils::WriteAll(fd_, zeros.data(), zeros.size()));
+ return true;
+ }
+ ~AvbFooterEraser() {
+ TEST_AND_RETURN(fd_ != nullptr && fd_->IsOpen());
+ if (!fd_->Close()) {
+ LOG(WARNING) << "Failed to close fd for " << path_;
+ }
+ }
+
+ private:
+ std::string path_;
+ FileDescriptorPtr fd_;
+};
+
+} // namespace
+
+std::optional<bool>
+DynamicPartitionControlAndroid::IsAvbEnabledOnSystemOther() {
+ auto prefix = GetProperty(kPostinstallFstabPrefix, "");
+ if (prefix.empty()) {
+ LOG(WARNING) << "Cannot get " << kPostinstallFstabPrefix;
+ return std::nullopt;
+ }
+ auto path = base::FilePath(prefix).Append("etc/fstab.postinstall").value();
+ return IsAvbEnabledInFstab(path);
+}
+
+std::optional<bool> DynamicPartitionControlAndroid::IsAvbEnabledInFstab(
+ const std::string& path) {
+ Fstab fstab;
+ if (!ReadFstabFromFile(path, &fstab)) {
+ LOG(WARNING) << "Cannot read fstab from " << path;
+ return std::nullopt;
+ }
+ for (const auto& entry : fstab) {
+ if (!entry.avb_keys.empty()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DynamicPartitionControlAndroid::GetSystemOtherPath(
+ uint32_t source_slot,
+ uint32_t target_slot,
+ const std::string& partition_name_suffix,
+ std::string* path,
+ bool* should_unmap) {
+ path->clear();
+ *should_unmap = false;
+
+ // In recovery, just erase no matter what.
+ // - On devices with retrofit dynamic partitions, no logical partitions
+ // should be mounted at this point. Hence it should be safe to erase.
+ // Otherwise, do check that AVB is enabled on system_other before erasing.
+ if (!IsRecovery()) {
+ auto has_avb = IsAvbEnabledOnSystemOther();
+ TEST_AND_RETURN_FALSE(has_avb.has_value());
+ if (!has_avb.value()) {
+ LOG(INFO) << "AVB is not enabled on system_other. Skip erasing.";
+ return true;
+ }
+
+ // Found unexpected avb_keys for system_other on devices retrofitting
+ // dynamic partitions. Previous crash in update_engine may leave logical
+ // partitions mapped on physical system_other partition. It is difficult to
+ // handle these cases. Just fail.
+ if (GetDynamicPartitionsFeatureFlag().IsRetrofit()) {
+ LOG(ERROR) << "Cannot erase AVB footer on system_other on devices with "
+ << "retrofit dynamic partitions. They should not have AVB "
+ << "enabled on system_other.";
+ return false;
+ }
+ }
+
+ std::string device_dir_str;
+ TEST_AND_RETURN_FALSE(GetDeviceDir(&device_dir_str));
+ base::FilePath device_dir(device_dir_str);
+
+ // On devices without dynamic partition, search for static partitions.
+ if (!GetDynamicPartitionsFeatureFlag().IsEnabled()) {
+ *path = device_dir.Append(partition_name_suffix).value();
+ TEST_AND_RETURN_FALSE(DeviceExists(*path));
+ return true;
+ }
+
+ auto source_super_device =
+ device_dir.Append(GetSuperPartitionName(source_slot)).value();
+
+ auto builder = LoadMetadataBuilder(source_super_device, source_slot);
+ if (builder == nullptr) {
+ if (IsRecovery()) {
+ // It might be corrupted for some reason. It should still be able to
+ // sideload.
+ LOG(WARNING) << "Super partition metadata cannot be read from the source "
+ << "slot, skip erasing.";
+ return true;
+ } else {
+ // Device has booted into Android mode, indicating that the super
+ // partition metadata should be there.
+ LOG(ERROR) << "Super partition metadata cannot be read from the source "
+ << "slot. This is unexpected on devices with dynamic "
+ << "partitions enabled.";
+ return false;
+ }
+ }
+ auto p = builder->FindPartition(partition_name_suffix);
+ if (p == nullptr) {
+ // If the source slot is flashed without system_other, it does not exist
+ // in super partition metadata at source slot. It is safe to skip it.
+ LOG(INFO) << "Can't find " << partition_name_suffix
+ << " in metadata source slot, skip erasing.";
+ return true;
+ }
+ // System_other created by flashing tools should be erased.
+ // If partition is created by update_engine (via NewForUpdate), it is a
+ // left-over partition from the previous update and does not contain
+ // system_other, hence there is no need to erase.
+ // Note the reverse is not necessary true. If the flag is not set, we don't
+ // know if the partition is created by update_engine or by flashing tools
+ // because older versions of super partition metadata does not contain this
+ // flag. It is okay to erase the AVB footer anyways.
+ if (p->attributes() & LP_PARTITION_ATTR_UPDATED) {
+ LOG(INFO) << partition_name_suffix
+ << " does not contain system_other, skip erasing.";
+ return true;
+ }
+
+ // Delete any pre-existing device with name |partition_name_suffix| and
+ // also remove it from |mapped_devices_|.
+ TEST_AND_RETURN_FALSE(UnmapPartitionOnDeviceMapper(partition_name_suffix));
+ // Use CreateLogicalPartition directly to avoid mapping with existing
+ // snapshots.
+ CreateLogicalPartitionParams params = {
+ .block_device = source_super_device,
+ .metadata_slot = source_slot,
+ .partition_name = partition_name_suffix,
+ .force_writable = true,
+ .timeout_ms = kMapTimeout,
+ };
+ TEST_AND_RETURN_FALSE(CreateLogicalPartition(params, path));
+ *should_unmap = true;
+ return true;
+}
+
+bool DynamicPartitionControlAndroid::EraseSystemOtherAvbFooter(
+ uint32_t source_slot, uint32_t target_slot) {
+ LOG(INFO) << "Erasing AVB footer of system_other partition before update.";
+
+ const std::string target_suffix = SlotSuffixForSlotNumber(target_slot);
+ const std::string partition_name_suffix = "system" + target_suffix;
+
+ std::string path;
+ bool should_unmap = false;
+
+ TEST_AND_RETURN_FALSE(GetSystemOtherPath(
+ source_slot, target_slot, partition_name_suffix, &path, &should_unmap));
+
+ if (path.empty()) {
+ return true;
+ }
+
+ bool ret = AvbFooterEraser(path).Erase();
+
+ // Delete |partition_name_suffix| from device mapper and from
+ // |mapped_devices_| again so that it does not interfere with update process.
+ if (should_unmap) {
+ TEST_AND_RETURN_FALSE(UnmapPartitionOnDeviceMapper(partition_name_suffix));
+ }
+
+ return ret;
+}
+
bool DynamicPartitionControlAndroid::PrepareDynamicPartitionsForUpdate(
uint32_t source_slot,
uint32_t target_slot,
diff --git a/dynamic_partition_control_android.h b/dynamic_partition_control_android.h
index 6dbe370..9dcdcf1 100644
--- a/dynamic_partition_control_android.h
+++ b/dynamic_partition_control_android.h
@@ -136,6 +136,43 @@
// Allow mock objects to override this to test recovery mode.
virtual bool IsRecovery();
+ // Determine path for system_other partition.
+ // |source_slot| should be current slot.
+ // |target_slot| should be "other" slot.
+ // |partition_name_suffix| should be "system" + suffix(|target_slot|).
+ // Return true and set |path| if successful.
+ // Set |path| to empty if no need to erase system_other.
+ // Set |should_unmap| to true if path needs to be unmapped later.
+ //
+ // Note: system_other cannot use GetPartitionDevice or
+ // GetDynamicPartitionDevice because:
+ // - super partition metadata may be loaded from the source slot
+ // - UPDATED flag needs to be check to skip erasing if partition is not
+ // created by flashing tools
+ // - Snapshots from previous update attempts should not be used.
+ virtual bool GetSystemOtherPath(uint32_t source_slot,
+ uint32_t target_slot,
+ const std::string& partition_name_suffix,
+ std::string* path,
+ bool* should_unmap);
+
+ // Returns true if any entry in the fstab file in |path| has AVB enabled,
+ // false if not enabled, and nullopt for any error.
+ virtual std::optional<bool> IsAvbEnabledInFstab(const std::string& path);
+
+ // Returns true if system_other has AVB enabled, false if not enabled, and
+ // nullopt for any error.
+ virtual std::optional<bool> IsAvbEnabledOnSystemOther();
+
+ // Erase system_other partition that may contain system_other.img.
+ // After the update, the content of system_other may be corrupted but with
+ // valid AVB footer. If the update is rolled back and factory data reset is
+ // triggered, system_b fails to be mapped with verity errors (see
+ // b/152444348). Erase the system_other so that mapping system_other is
+ // skipped.
+ virtual bool EraseSystemOtherAvbFooter(uint32_t source_slot,
+ uint32_t target_slot);
+
private:
friend class DynamicPartitionControlAndroidTest;
diff --git a/dynamic_partition_control_android_unittest.cc b/dynamic_partition_control_android_unittest.cc
index 457ea10..2081918 100644
--- a/dynamic_partition_control_android_unittest.cc
+++ b/dynamic_partition_control_android_unittest.cc
@@ -23,12 +23,16 @@
#include <base/strings/string_util.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <libavb/libavb.h>
#include "update_engine/common/mock_prefs.h"
+#include "update_engine/common/test_utils.h"
#include "update_engine/dynamic_partition_test_utils.h"
#include "update_engine/mock_dynamic_partition_control.h"
using android::dm::DmDeviceState;
+using chromeos_update_engine::test_utils::ScopedLoopbackDeviceBinder;
+using chromeos_update_engine::test_utils::ScopedTempFile;
using std::string;
using testing::_;
using testing::AnyNumber;
@@ -36,6 +40,7 @@
using testing::Invoke;
using testing::NiceMock;
using testing::Not;
+using testing::Optional;
using testing::Return;
namespace chromeos_update_engine {
@@ -64,6 +69,9 @@
*device = GetDmDevice(partition_name_suffix);
return true;
}));
+
+ ON_CALL(dynamicControl(), EraseSystemOtherAvbFooter(_, _))
+ .WillByDefault(Return(true));
}
// Return the mocked DynamicPartitionControlInterface.
@@ -90,12 +98,15 @@
// Set the fake metadata to return when LoadMetadataBuilder is called on
// |slot|.
- void SetMetadata(uint32_t slot, const PartitionSuffixSizes& sizes) {
+ void SetMetadata(uint32_t slot,
+ const PartitionSuffixSizes& sizes,
+ uint32_t partition_attr = 0) {
EXPECT_CALL(dynamicControl(),
LoadMetadataBuilder(GetSuperDevice(slot), slot, _))
.Times(AnyNumber())
- .WillRepeatedly(Invoke([sizes](auto, auto, auto) {
- return NewFakeMetadata(PartitionSuffixSizesToManifest(sizes));
+ .WillRepeatedly(Invoke([sizes, partition_attr](auto, auto, auto) {
+ return NewFakeMetadata(PartitionSuffixSizesToManifest(sizes),
+ partition_attr);
}));
}
@@ -757,4 +768,128 @@
ASSERT_TRUE(dynamicControl().ResetUpdate(&prefs));
}
+TEST_F(DynamicPartitionControlAndroidTest, IsAvbNotEnabledInFstab) {
+ // clang-format off
+ std::string fstab_content =
+ "system /postinstall ext4 ro,nosuid,nodev,noexec slotselect_other,logical\n" // NOLINT(whitespace/line_length)
+ "/dev/block/by-name/system /postinstall ext4 ro,nosuid,nodev,noexec slotselect_other\n"; // NOLINT(whitespace/line_length)
+ // clang-format on
+ ScopedTempFile fstab;
+ ASSERT_TRUE(test_utils::WriteFileString(fstab.path(), fstab_content));
+ ASSERT_THAT(dynamicControl().RealIsAvbEnabledInFstab(fstab.path()),
+ Optional(false));
+}
+
+TEST_F(DynamicPartitionControlAndroidTest, IsAvbEnabledInFstab) {
+ // clang-format off
+ std::string fstab_content =
+ "system /postinstall ext4 ro,nosuid,nodev,noexec slotselect_other,logical,avb_keys=/foo\n"; // NOLINT(whitespace/line_length)
+ // clang-format on
+ ScopedTempFile fstab;
+ ASSERT_TRUE(test_utils::WriteFileString(fstab.path(), fstab_content));
+ ASSERT_THAT(dynamicControl().RealIsAvbEnabledInFstab(fstab.path()),
+ Optional(true));
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, AvbNotEnabledOnSystemOther) {
+ ON_CALL(dynamicControl(), GetSystemOtherPath(_, _, _, _, _))
+ .WillByDefault(Invoke([&](auto source_slot,
+ auto target_slot,
+ const auto& name,
+ auto path,
+ auto should_unmap) {
+ return dynamicControl().RealGetSystemOtherPath(
+ source_slot, target_slot, name, path, should_unmap);
+ }));
+ ON_CALL(dynamicControl(), IsAvbEnabledOnSystemOther())
+ .WillByDefault(Return(false));
+ EXPECT_TRUE(
+ dynamicControl().RealEraseSystemOtherAvbFooter(source(), target()));
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, NoSystemOtherToErase) {
+ SetMetadata(source(), {{S("system"), 100_MiB}});
+ ON_CALL(dynamicControl(), IsAvbEnabledOnSystemOther())
+ .WillByDefault(Return(true));
+ std::string path;
+ bool should_unmap;
+ ASSERT_TRUE(dynamicControl().RealGetSystemOtherPath(
+ source(), target(), T("system"), &path, &should_unmap));
+ ASSERT_TRUE(path.empty()) << path;
+ ASSERT_FALSE(should_unmap);
+ ON_CALL(dynamicControl(), GetSystemOtherPath(_, _, _, _, _))
+ .WillByDefault(Invoke([&](auto source_slot,
+ auto target_slot,
+ const auto& name,
+ auto path,
+ auto should_unmap) {
+ return dynamicControl().RealGetSystemOtherPath(
+ source_slot, target_slot, name, path, should_unmap);
+ }));
+ EXPECT_TRUE(
+ dynamicControl().RealEraseSystemOtherAvbFooter(source(), target()));
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, SkipEraseUpdatedSystemOther) {
+ PartitionSuffixSizes sizes{{S("system"), 100_MiB}, {T("system"), 100_MiB}};
+ SetMetadata(source(), sizes, LP_PARTITION_ATTR_UPDATED);
+ ON_CALL(dynamicControl(), IsAvbEnabledOnSystemOther())
+ .WillByDefault(Return(true));
+ std::string path;
+ bool should_unmap;
+ ASSERT_TRUE(dynamicControl().RealGetSystemOtherPath(
+ source(), target(), T("system"), &path, &should_unmap));
+ ASSERT_TRUE(path.empty()) << path;
+ ASSERT_FALSE(should_unmap);
+ ON_CALL(dynamicControl(), GetSystemOtherPath(_, _, _, _, _))
+ .WillByDefault(Invoke([&](auto source_slot,
+ auto target_slot,
+ const auto& name,
+ auto path,
+ auto should_unmap) {
+ return dynamicControl().RealGetSystemOtherPath(
+ source_slot, target_slot, name, path, should_unmap);
+ }));
+ EXPECT_TRUE(
+ dynamicControl().RealEraseSystemOtherAvbFooter(source(), target()));
+}
+
+TEST_P(DynamicPartitionControlAndroidTestP, EraseSystemOtherAvbFooter) {
+ constexpr uint64_t file_size = 1_MiB;
+ static_assert(file_size > AVB_FOOTER_SIZE);
+ ScopedTempFile system_other;
+ brillo::Blob original(file_size, 'X');
+ ASSERT_TRUE(test_utils::WriteFileVector(system_other.path(), original));
+ std::string mnt_path;
+ ScopedLoopbackDeviceBinder dev(system_other.path(), true, &mnt_path);
+ ASSERT_TRUE(dev.is_bound());
+
+ brillo::Blob device_content;
+ ASSERT_TRUE(utils::ReadFile(mnt_path, &device_content));
+ ASSERT_EQ(original, device_content);
+
+ PartitionSuffixSizes sizes{{S("system"), 100_MiB}, {T("system"), file_size}};
+ SetMetadata(source(), sizes);
+ ON_CALL(dynamicControl(), IsAvbEnabledOnSystemOther())
+ .WillByDefault(Return(true));
+ EXPECT_CALL(dynamicControl(),
+ GetSystemOtherPath(source(), target(), T("system"), _, _))
+ .WillRepeatedly(
+ Invoke([&](auto, auto, const auto&, auto path, auto should_unmap) {
+ *path = mnt_path;
+ *should_unmap = false;
+ return true;
+ }));
+ ASSERT_TRUE(
+ dynamicControl().RealEraseSystemOtherAvbFooter(source(), target()));
+
+ device_content.clear();
+ ASSERT_TRUE(utils::ReadFile(mnt_path, &device_content));
+ brillo::Blob new_expected(original);
+ // Clear the last AVB_FOOTER_SIZE bytes.
+ new_expected.resize(file_size - AVB_FOOTER_SIZE);
+ new_expected.resize(file_size, '\0');
+ ASSERT_EQ(new_expected, device_content);
+}
+
} // namespace chromeos_update_engine
diff --git a/dynamic_partition_test_utils.h b/dynamic_partition_test_utils.h
index 346998f..70a176b 100644
--- a/dynamic_partition_test_utils.h
+++ b/dynamic_partition_test_utils.h
@@ -175,7 +175,7 @@
}
inline std::unique_ptr<MetadataBuilder> NewFakeMetadata(
- const DeltaArchiveManifest& manifest) {
+ const DeltaArchiveManifest& manifest, uint32_t partition_attr = 0) {
auto builder =
MetadataBuilder::New(kDefaultSuperSize, kFakeMetadataSize, kMaxNumSlots);
for (const auto& group : manifest.dynamic_partition_metadata().groups()) {
@@ -183,7 +183,7 @@
for (const auto& partition_name : group.partition_names()) {
EXPECT_NE(
nullptr,
- builder->AddPartition(partition_name, group.name(), 0 /* attr */));
+ builder->AddPartition(partition_name, group.name(), partition_attr));
}
}
for (const auto& partition : manifest.partitions()) {
diff --git a/mock_dynamic_partition_control.h b/mock_dynamic_partition_control.h
index 169c265..1e4e5fd 100644
--- a/mock_dynamic_partition_control.h
+++ b/mock_dynamic_partition_control.h
@@ -77,10 +77,34 @@
MOCK_METHOD1(GetSuperPartitionName, std::string(uint32_t));
MOCK_METHOD0(GetVirtualAbFeatureFlag, FeatureFlag());
MOCK_METHOD1(FinishUpdate, bool(bool));
+ MOCK_METHOD5(
+ GetSystemOtherPath,
+ bool(uint32_t, uint32_t, const std::string&, std::string*, bool*));
+ MOCK_METHOD2(EraseSystemOtherAvbFooter, bool(uint32_t, uint32_t));
+ MOCK_METHOD0(IsAvbEnabledOnSystemOther, std::optional<bool>());
void set_fake_mapped_devices(const std::set<std::string>& fake) override {
DynamicPartitionControlAndroid::set_fake_mapped_devices(fake);
}
+
+ bool RealGetSystemOtherPath(uint32_t source_slot,
+ uint32_t target_slot,
+ const std::string& partition_name_suffix,
+ std::string* path,
+ bool* should_unmap) {
+ return DynamicPartitionControlAndroid::GetSystemOtherPath(
+ source_slot, target_slot, partition_name_suffix, path, should_unmap);
+ }
+
+ bool RealEraseSystemOtherAvbFooter(uint32_t source_slot,
+ uint32_t target_slot) {
+ return DynamicPartitionControlAndroid::EraseSystemOtherAvbFooter(
+ source_slot, target_slot);
+ }
+
+ std::optional<bool> RealIsAvbEnabledInFstab(const std::string& path) {
+ return DynamicPartitionControlAndroid::IsAvbEnabledInFstab(path);
+ }
};
} // namespace chromeos_update_engine