From 6befe78c3ca849679ff30fb1252ccb10d4586d58 Mon Sep 17 00:00:00 2001 From: Kelvin Zhang Date: Tue, 21 Jun 2022 14:20:54 -0700 Subject: [PATCH] Convert fastboot/libsnapshot to new BootControl client Thew new client will use AIDL if available, and fallback to HIDL otherwise. Test: th Bug: 227536004 Change-Id: I9af21037a76a4a6db00144f5b2774ea23f3a5cc2 --- fastboot/Android.bp | 2 ++ fastboot/device/commands.cpp | 27 ++++++++++--------- fastboot/device/fastboot_device.cpp | 21 ++++++++------- fastboot/device/fastboot_device.h | 13 ++++----- fastboot/device/utility.cpp | 5 ++-- fastboot/device/utility.h | 3 +-- fastboot/device/variables.cpp | 17 +++++------- fs_mgr/libsnapshot/Android.bp | 7 ++--- fs_mgr/libsnapshot/device_info.cpp | 20 +++++++------- fs_mgr/libsnapshot/device_info.h | 6 ++--- .../include/libsnapshot/snapshot.h | 17 +++++------- .../include_test/libsnapshot/test_helpers.h | 3 +-- fs_mgr/libsnapshot/snapshot.cpp | 6 ++--- fs_mgr/libsnapshot/snapshot_fuzz_utils.h | 4 ++- fs_mgr/libsnapshot/snapshot_test.cpp | 1 + 15 files changed, 74 insertions(+), 78 deletions(-) diff --git a/fastboot/Android.bp b/fastboot/Android.bp index 22665edc9..26e6e00cf 100644 --- a/fastboot/Android.bp +++ b/fastboot/Android.bp @@ -164,6 +164,8 @@ cc_binary { shared_libs: [ "android.hardware.boot@1.0", "android.hardware.boot@1.1", + "android.hardware.boot-V1-ndk", + "libboot_control_client", "android.hardware.fastboot@1.1", "android.hardware.health@2.0", "android.hardware.health-V1-ndk", diff --git a/fastboot/device/commands.cpp b/fastboot/device/commands.cpp index 524196c80..823783eb2 100644 --- a/fastboot/device/commands.cpp +++ b/fastboot/device/commands.cpp @@ -40,6 +40,7 @@ #include #include +#include "BootControlClient.h" #include "constants.h" #include "fastboot_device.h" #include "flashing.h" @@ -52,15 +53,12 @@ static constexpr bool kEnableFetch = false; #endif using android::fs_mgr::MetadataBuilder; +using android::hal::CommandResult; using ::android::hardware::hidl_string; -using ::android::hardware::boot::V1_0::BoolResult; -using ::android::hardware::boot::V1_0::CommandResult; -using ::android::hardware::boot::V1_0::Slot; -using ::android::hardware::boot::V1_1::MergeStatus; using ::android::hardware::fastboot::V1_0::Result; using ::android::hardware::fastboot::V1_0::Status; using android::snapshot::SnapshotManager; -using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl; +using MergeStatus = android::hal::BootControlClient::MergeStatus; using namespace android::storage_literals; @@ -317,7 +315,7 @@ bool SetActiveHandler(FastbootDevice* device, const std::vector& ar "set_active command is not allowed on locked devices"); } - Slot slot; + int32_t slot = 0; if (!GetSlotNumber(args[1], &slot)) { // Slot suffix needs to be between 'a' and 'z'. return device->WriteStatus(FastbootResult::FAIL, "Bad slot suffix"); @@ -329,7 +327,7 @@ bool SetActiveHandler(FastbootDevice* device, const std::vector& ar return device->WriteStatus(FastbootResult::FAIL, "Cannot set slot: boot control HAL absent"); } - if (slot >= boot_control_hal->getNumberSlots()) { + if (slot >= boot_control_hal->GetNumSlots()) { return device->WriteStatus(FastbootResult::FAIL, "Slot out of range"); } @@ -358,10 +356,8 @@ bool SetActiveHandler(FastbootDevice* device, const std::vector& ar } } - CommandResult ret; - auto cb = [&ret](CommandResult result) { ret = result; }; - auto result = boot_control_hal->setActiveBootSlot(slot, cb); - if (result.isOk() && ret.success) { + CommandResult ret = boot_control_hal->SetActiveBootSlot(slot); + if (ret.success) { // Save as slot suffix to match the suffix format as returned from // the boot control HAL. auto current_slot = "_" + args[1]; @@ -682,9 +678,14 @@ bool SnapshotUpdateHandler(FastbootDevice* device, const std::vectorsetSnapshotMergeStatus(MergeStatus::CANCELLED); + case MergeStatus::MERGING: { + const auto ret = hal->SetSnapshotMergeStatus(MergeStatus::CANCELLED); + if (!ret.success) { + device->WriteFail("Failed to SetSnapshotMergeStatus(MergeStatus::CANCELLED) " + + ret.errMsg); + } break; + } default: break; } diff --git a/fastboot/device/fastboot_device.cpp b/fastboot/device/fastboot_device.cpp index ae225debc..4932e5ceb 100644 --- a/fastboot/device/fastboot_device.cpp +++ b/fastboot/device/fastboot_device.cpp @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -38,9 +39,8 @@ using std::string_literals::operator""s; using android::fs_mgr::EnsurePathUnmounted; using android::fs_mgr::Fstab; using ::android::hardware::hidl_string; -using ::android::hardware::boot::V1_0::IBootControl; -using ::android::hardware::boot::V1_0::Slot; using ::android::hardware::fastboot::V1_1::IFastboot; +using BootControlClient = FastbootDevice::BootControlClient; namespace sph = std::placeholders; @@ -85,7 +85,7 @@ FastbootDevice::FastbootDevice() {FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler}, {FB_CMD_FETCH, FetchHandler}, }), - boot_control_hal_(IBootControl::getService()), + boot_control_hal_(BootControlClient::WaitForService()), health_hal_(get_health_service()), fastboot_hal_(IFastboot::getService()), active_slot_("") { @@ -95,10 +95,6 @@ FastbootDevice::FastbootDevice() transport_ = std::make_unique(); } - if (boot_control_hal_) { - boot1_1_ = android::hardware::boot::V1_1::IBootControl::castFrom(boot_control_hal_); - } - // Make sure cache is unmounted, since recovery will have mounted it for // logging. Fstab fstab; @@ -125,12 +121,17 @@ std::string FastbootDevice::GetCurrentSlot() { if (!boot_control_hal_) { return ""; } - std::string suffix; - auto cb = [&suffix](hidl_string s) { suffix = s; }; - boot_control_hal_->getSuffix(boot_control_hal_->getCurrentSlot(), cb); + std::string suffix = boot_control_hal_->GetSuffix(boot_control_hal_->GetCurrentSlot()); return suffix; } +BootControlClient* FastbootDevice::boot1_1() const { + if (boot_control_hal_->GetVersion() >= android::hal::BootControlVersion::BOOTCTL_V1_1) { + return boot_control_hal_.get(); + } + return nullptr; +} + bool FastbootDevice::WriteStatus(FastbootResult result, const std::string& message) { constexpr size_t kResponseReasonSize = 4; constexpr size_t kNumResponseTypes = 4; // "FAIL", "OKAY", "INFO", "DATA" diff --git a/fastboot/device/fastboot_device.h b/fastboot/device/fastboot_device.h index 91ffce3d9..9df8fa58f 100644 --- a/fastboot/device/fastboot_device.h +++ b/fastboot/device/fastboot_device.h @@ -22,9 +22,8 @@ #include #include +#include #include -#include -#include #include #include "commands.h" @@ -33,6 +32,7 @@ class FastbootDevice { public: + using BootControlClient = android::hal::BootControlClient; FastbootDevice(); ~FastbootDevice(); @@ -50,10 +50,8 @@ class FastbootDevice { std::vector& download_data() { return download_data_; } Transport* get_transport() { return transport_.get(); } - android::sp boot_control_hal() { - return boot_control_hal_; - } - android::sp boot1_1() { return boot1_1_; } + BootControlClient* boot_control_hal() const { return boot_control_hal_.get(); } + BootControlClient* boot1_1() const; android::sp fastboot_hal() { return fastboot_hal_; } @@ -65,8 +63,7 @@ class FastbootDevice { const std::unordered_map kCommandMap; std::unique_ptr transport_; - android::sp boot_control_hal_; - android::sp boot1_1_; + std::unique_ptr boot_control_hal_; std::shared_ptr health_hal_; android::sp fastboot_hal_; std::vector download_data_; diff --git a/fastboot/device/utility.cpp b/fastboot/device/utility.cpp index 3302c4310..e12ee6479 100644 --- a/fastboot/device/utility.cpp +++ b/fastboot/device/utility.cpp @@ -36,7 +36,6 @@ using namespace android::fs_mgr; using namespace std::chrono_literals; using android::base::unique_fd; -using android::hardware::boot::V1_0::Slot; namespace { @@ -137,7 +136,7 @@ bool LogicalPartitionExists(FastbootDevice* device, const std::string& name, boo return true; } -bool GetSlotNumber(const std::string& slot, Slot* number) { +bool GetSlotNumber(const std::string& slot, int32_t* number) { if (slot.size() != 1) { return false; } @@ -204,7 +203,7 @@ bool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super size_t num_slots = 1; auto boot_control_hal = device->boot_control_hal(); if (boot_control_hal) { - num_slots = boot_control_hal->getNumberSlots(); + num_slots = boot_control_hal->GetNumSlots(); } bool ok = true; diff --git a/fastboot/device/utility.h b/fastboot/device/utility.h index 6e1453f83..a62125e4f 100644 --- a/fastboot/device/utility.h +++ b/fastboot/device/utility.h @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -124,7 +123,7 @@ bool LogicalPartitionExists(FastbootDevice* device, const std::string& name, bool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle, int flags = O_WRONLY); -bool GetSlotNumber(const std::string& slot, android::hardware::boot::V1_0::Slot* number); +bool GetSlotNumber(const std::string& slot, int32_t* number); std::vector ListPartitions(FastbootDevice* device); bool GetDeviceLockStatus(); diff --git a/fastboot/device/variables.cpp b/fastboot/device/variables.cpp index 0de00b1f6..b6eb2cdd5 100644 --- a/fastboot/device/variables.cpp +++ b/fastboot/device/variables.cpp @@ -29,7 +29,7 @@ #include #include -#include "constants.h" +#include "BootControlClient.h" #include "fastboot_device.h" #include "flashing.h" #include "utility.h" @@ -40,13 +40,10 @@ static constexpr bool kEnableFetch = true; static constexpr bool kEnableFetch = false; #endif -using ::android::hardware::boot::V1_0::BoolResult; -using ::android::hardware::boot::V1_0::Slot; -using ::android::hardware::boot::V1_1::MergeStatus; +using MergeStatus = android::hal::BootControlClient::MergeStatus; using ::android::hardware::fastboot::V1_0::FileSystemType; using ::android::hardware::fastboot::V1_0::Result; using ::android::hardware::fastboot::V1_0::Status; -using IBootControl1_1 = ::android::hardware::boot::V1_1::IBootControl; using namespace android::fs_mgr; using namespace std::string_literals; @@ -211,7 +208,7 @@ bool GetSlotCount(FastbootDevice* device, const std::vector& /* arg if (!boot_control_hal) { *message = "0"; } else { - *message = std::to_string(boot_control_hal->getNumberSlots()); + *message = std::to_string(boot_control_hal->GetNumSlots()); } return true; } @@ -222,7 +219,7 @@ bool GetSlotSuccessful(FastbootDevice* device, const std::vector& a *message = "Missing argument"; return false; } - Slot slot; + int32_t slot = -1; if (!GetSlotNumber(args[0], &slot)) { *message = "Invalid slot"; return false; @@ -232,7 +229,7 @@ bool GetSlotSuccessful(FastbootDevice* device, const std::vector& a *message = "Device has no slots"; return false; } - if (boot_control_hal->isSlotMarkedSuccessful(slot) != BoolResult::TRUE) { + if (boot_control_hal->IsSlotMarkedSuccessful(slot).value_or(false)) { *message = "no"; } else { *message = "yes"; @@ -246,7 +243,7 @@ bool GetSlotUnbootable(FastbootDevice* device, const std::vector& a *message = "Missing argument"; return false; } - Slot slot; + int32_t slot = -1; if (!GetSlotNumber(args[0], &slot)) { *message = "Invalid slot"; return false; @@ -256,7 +253,7 @@ bool GetSlotUnbootable(FastbootDevice* device, const std::vector& a *message = "Device has no slots"; return false; } - if (boot_control_hal->isSlotBootable(slot) != BoolResult::TRUE) { + if (!boot_control_hal->IsSlotBootable(slot).value_or(false)) { *message = "yes"; } else { *message = "no"; diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp index dffb2e7c6..d5e85e60f 100644 --- a/fs_mgr/libsnapshot/Android.bp +++ b/fs_mgr/libsnapshot/Android.bp @@ -74,6 +74,8 @@ cc_defaults { shared_libs: [ "android.hardware.boot@1.0", "android.hardware.boot@1.1", + "android.hardware.boot-V1-ndk", + "libboot_control_client", ], } @@ -233,6 +235,7 @@ cc_defaults { static_libs: [ "android.hardware.boot@1.0", "android.hardware.boot@1.1", + "android.hardware.boot-V1-ndk", "libbrotli", "libc++fs", "libfs_mgr_binder", @@ -261,7 +264,7 @@ cc_defaults { cc_test { name: "vts_libsnapshot_test", - defaults: ["libsnapshot_test_defaults"], + defaults: ["libsnapshot_test_defaults", "libsnapshot_hal_deps"], } sh_test { @@ -309,8 +312,6 @@ cc_binary { "update_metadata-protos", ], shared_libs: [ - "android.hardware.boot@1.0", - "android.hardware.boot@1.1", "libbase", "libext2_uuid", "libext4_utils", diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp index a6d96ed82..0ab61033f 100644 --- a/fs_mgr/libsnapshot/device_info.cpp +++ b/fs_mgr/libsnapshot/device_info.cpp @@ -23,8 +23,9 @@ namespace android { namespace snapshot { #ifdef LIBSNAPSHOT_USE_HAL -using android::hardware::boot::V1_0::BoolResult; -using android::hardware::boot::V1_0::CommandResult; +using android::hal::BootControlClient; +using android::hal::BootControlVersion; +using android::hal::CommandResult; #endif using namespace std::chrono_literals; @@ -63,16 +64,16 @@ bool DeviceInfo::IsOverlayfsSetup() const { #ifdef LIBSNAPSHOT_USE_HAL bool DeviceInfo::EnsureBootHal() { if (!boot_control_) { - auto hal = android::hardware::boot::V1_0::IBootControl::getService(); + auto hal = BootControlClient::WaitForService(); if (!hal) { LOG(ERROR) << "Could not find IBootControl HAL"; return false; } - boot_control_ = android::hardware::boot::V1_1::IBootControl::castFrom(hal); - if (!boot_control_) { + if (hal->GetVersion() < BootControlVersion::BOOTCTL_V1_1) { LOG(ERROR) << "Could not find IBootControl 1.1 HAL"; return false; } + boot_control_ = std::move(hal); } return true; } @@ -83,8 +84,9 @@ bool DeviceInfo::SetBootControlMergeStatus([[maybe_unused]] MergeStatus status) if (!EnsureBootHal()) { return false; } - if (!boot_control_->setSnapshotMergeStatus(status)) { - LOG(ERROR) << "Unable to set the snapshot merge status"; + const auto ret = boot_control_->SetSnapshotMergeStatus(status); + if (!ret.IsOk()) { + LOG(ERROR) << "Unable to set the snapshot merge status " << ret.errMsg; return false; } return true; @@ -108,9 +110,7 @@ bool DeviceInfo::SetSlotAsUnbootable([[maybe_unused]] unsigned int slot) { return false; } - CommandResult result = {}; - auto cb = [&](CommandResult r) -> void { result = r; }; - boot_control_->setSlotAsUnbootable(slot, cb); + CommandResult result = boot_control_->MarkSlotUnbootable(slot); if (!result.success) { LOG(ERROR) << "Error setting slot " << slot << " unbootable: " << result.errMsg; return false; diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h index 8aefb8507..d06f1be7c 100644 --- a/fs_mgr/libsnapshot/device_info.h +++ b/fs_mgr/libsnapshot/device_info.h @@ -17,7 +17,7 @@ #include #ifdef LIBSNAPSHOT_USE_HAL -#include +#include #endif #include #include @@ -26,7 +26,7 @@ namespace android { namespace snapshot { class DeviceInfo final : public SnapshotManager::IDeviceInfo { - using MergeStatus = android::hardware::boot::V1_1::MergeStatus; + using MergeStatus = ::aidl::android::hardware::boot::MergeStatus; public: std::string GetMetadataDir() const override; @@ -50,7 +50,7 @@ class DeviceInfo final : public SnapshotManager::IDeviceInfo { android::fs_mgr::PartitionOpener opener_; bool first_stage_init_ = false; #ifdef LIBSNAPSHOT_USE_HAL - android::sp boot_control_; + std::unique_ptr<::android::hal::BootControlClient> boot_control_; #endif }; diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h index 5fe52809b..01268c7a1 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h @@ -46,6 +46,10 @@ #define DEFINED_FRIEND_TEST #endif +namespace aidl::android::hardware::boot { +enum class MergeStatus; +} + namespace android { namespace fiemap { @@ -59,13 +63,6 @@ class IPartitionOpener; // Forward declare IBootControl types since we cannot include only the headers // with Soong. Note: keep the enum width in sync. -namespace hardware { -namespace boot { -namespace V1_1 { -enum class MergeStatus : int32_t; -} // namespace V1_1 -} // namespace boot -} // namespace hardware namespace snapshot { @@ -95,6 +92,7 @@ class ISnapshotManager { class IDeviceInfo { public: using IImageManager = android::fiemap::IImageManager; + using MergeStatus = aidl::android::hardware::boot::MergeStatus; virtual ~IDeviceInfo() {} virtual std::string GetMetadataDir() const = 0; @@ -103,8 +101,7 @@ class ISnapshotManager { virtual std::string GetSuperDevice(uint32_t slot) const = 0; virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0; virtual bool IsOverlayfsSetup() const = 0; - virtual bool SetBootControlMergeStatus( - android::hardware::boot::V1_1::MergeStatus status) = 0; + virtual bool SetBootControlMergeStatus(MergeStatus status) = 0; virtual bool SetSlotAsUnbootable(unsigned int slot) = 0; virtual bool IsRecovery() const = 0; virtual bool IsTestDevice() const { return false; } @@ -311,7 +308,7 @@ class SnapshotManager final : public ISnapshotManager { using LpMetadata = android::fs_mgr::LpMetadata; using MetadataBuilder = android::fs_mgr::MetadataBuilder; using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest; - using MergeStatus = android::hardware::boot::V1_1::MergeStatus; + using MergeStatus = aidl::android::hardware::boot::MergeStatus; using FiemapStatus = android::fiemap::FiemapStatus; friend class SnapshotMergeStats; diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h index c3b40dca4..be18b42f7 100644 --- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h +++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -33,10 +32,10 @@ namespace android { namespace snapshot { +using aidl::android::hardware::boot::MergeStatus; using android::fs_mgr::IPropertyFetcher; using android::fs_mgr::MetadataBuilder; using android::fs_mgr::testing::MockPropertyFetcher; -using android::hardware::boot::V1_1::MergeStatus; using chromeos_update_engine::DeltaArchiveManifest; using chromeos_update_engine::PartitionUpdate; using testing::_; diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index 967070656..93d15d06e 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -53,6 +53,7 @@ namespace android { namespace snapshot { +using aidl::android::hardware::boot::MergeStatus; using android::base::unique_fd; using android::dm::DeviceMapper; using android::dm::DmDeviceState; @@ -72,7 +73,6 @@ using android::fs_mgr::GetPartitionName; using android::fs_mgr::LpMetadata; using android::fs_mgr::MetadataBuilder; using android::fs_mgr::SlotNumberForSlotSuffix; -using android::hardware::boot::V1_1::MergeStatus; using chromeos_update_engine::DeltaArchiveManifest; using chromeos_update_engine::Extent; using chromeos_update_engine::FileDescriptor; @@ -2250,8 +2250,8 @@ bool SnapshotManager::MapAllPartitions(LockedFile* lock, const std::string& supe .block_device = super_device, .metadata = metadata.get(), .partition = &partition, - .partition_opener = &opener, .timeout_ms = timeout_ms, + .partition_opener = &opener, }; if (!MapPartitionWithSnapshot(lock, std::move(params), SnapshotContext::Mount, nullptr)) { return false; @@ -2728,8 +2728,8 @@ bool SnapshotManager::MapAllSnapshots(const std::chrono::milliseconds& timeout_m .block_device = super_device, .metadata = metadata.get(), .partition_name = snapshot, - .partition_opener = &opener, .timeout_ms = timeout_ms, + .partition_opener = &opener, }; if (!MapPartitionWithSnapshot(lock.get(), std::move(params), SnapshotContext::Mount, nullptr)) { diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h index a648384ce..eb8246aba 100644 --- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h +++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h @@ -29,6 +29,7 @@ // by SnapshotManager. #include "android/snapshot/snapshot_fuzz.pb.h" +#include "libsnapshot/snapshot.h" namespace android::snapshot { @@ -94,6 +95,7 @@ class SnapshotFuzzEnv { class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo { public: + using MergeStatus = ISnapshotManager::IDeviceInfo::MergeStatus; // Client is responsible for maintaining the lifetime of |data|. SnapshotFuzzDeviceInfo(SnapshotFuzzEnv* env, const FuzzDeviceInfoData& data, std::unique_ptr&& partition_opener, @@ -118,7 +120,7 @@ class SnapshotFuzzDeviceInfo : public ISnapshotManager::IDeviceInfo { std::string GetSlotSuffix() const override { return CurrentSlotIsA() ? "_a" : "_b"; } std::string GetOtherSlotSuffix() const override { return CurrentSlotIsA() ? "_b" : "_a"; } bool IsOverlayfsSetup() const override { return data_->is_overlayfs_setup(); } - bool SetBootControlMergeStatus(android::hardware::boot::V1_1::MergeStatus) override { + bool SetBootControlMergeStatus(MergeStatus) override { return data_->allow_set_boot_control_merge_status(); } bool SetSlotAsUnbootable(unsigned int) override { diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp index 6a348b4e5..74d622ab0 100644 --- a/fs_mgr/libsnapshot/snapshot_test.cpp +++ b/fs_mgr/libsnapshot/snapshot_test.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include