cryptohome: Add logical volumes as a backing device
Conditionally add logical volume support as a backing device for
cryptohomes. In subsequent CLs, we use these devices as a backing
store for dm-crypt based cryptohomes.
BUG=b:172344853
TEST=unittests
Cq-Depend: chromium:2593050
Change-Id: Ia277e2999d4ced6997a723959f378f7aa95b4480
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform2/+/2579890
Reviewed-by: Leo Lai <cylai@google.com>
Reviewed-by: Daniil Lunev <dlunev@chromium.org>
Tested-by: Sarthak Kukreti <sarthakkukreti@chromium.org>
Commit-Queue: Sarthak Kukreti <sarthakkukreti@chromium.org>
diff --git a/cryptohome/BUILD.gn b/cryptohome/BUILD.gn
index 2939a03..a8c06cf 100644
--- a/cryptohome/BUILD.gn
+++ b/cryptohome/BUILD.gn
@@ -551,6 +551,12 @@
deps += [ ":cert_provision-static" ]
}
+ if (use.lvm_stateful_partition) {
+ sources += [
+ "storage/encrypted_container/logical_volume_backing_device_test.cc",
+ ]
+ }
+
# TODO(crbug.com/1082873): Remove after fixing usage of deprecated
# declarations.
cflags_cc = [ "-Wno-error=deprecated-declarations" ]
diff --git a/cryptohome/libs/BUILD.gn b/cryptohome/libs/BUILD.gn
index 931d9eb..cc16899 100644
--- a/cryptohome/libs/BUILD.gn
+++ b/cryptohome/libs/BUILD.gn
@@ -13,6 +13,7 @@
config("target_defaults") {
defines = [
+ "USE_LVM_STATEFUL_PARTITION=${use.lvm_stateful_partition}",
"USE_PINWEAVER=${use.pinweaver}",
"USE_SELINUX=${use.selinux}",
"USE_TPM2=${use.tpm2}",
@@ -157,6 +158,13 @@
if (use.selinux) {
libs += [ "selinux" ]
}
+
+ defines = [ "USE_LVM_STATEFUL_PARTIITON=${use.lvm_stateful_partition}" ]
+ if (use.lvm_stateful_partition) {
+ sources +=
+ [ "../storage/encrypted_container/logical_volume_backing_device.cc" ]
+ }
+
all_dependent_configs = [ ":libcrosplatform_dependent_config" ]
deps = [ ":cryptohome-proto" ]
diff --git a/cryptohome/storage/encrypted_container/backing_device.cc b/cryptohome/storage/encrypted_container/backing_device.cc
index 9886d9f..9c9524b 100644
--- a/cryptohome/storage/encrypted_container/backing_device.cc
+++ b/cryptohome/storage/encrypted_container/backing_device.cc
@@ -6,8 +6,11 @@
#include <memory>
-#include "cryptohome/storage/encrypted_container/loopback_device.h"
#include "cryptohome/platform.h"
+#include "cryptohome/storage/encrypted_container/loopback_device.h"
+#if USE_LVM_STATEFUL_PARTITION
+#include "cryptohome/storage/encrypted_container/logical_volume_backing_device.h"
+#endif // USE_LVM_STATEFUL_PARTITION
namespace cryptohome {
@@ -18,6 +21,10 @@
case BackingDeviceType::kLoopbackDevice:
return std::make_unique<LoopbackDevice>(
config, platform, std::make_unique<brillo::LoopDeviceManager>());
+#if USE_LVM_STATEFUL_PARTITION
+ case BackingDeviceType::kLogicalVolumeBackingDevice:
+ return std::make_unique<LogicalVolumeBackingDevice>(config);
+#endif // USE_LVM_STATEFUL_PARTITION
default:
return nullptr;
}
diff --git a/cryptohome/storage/encrypted_container/backing_device.h b/cryptohome/storage/encrypted_container/backing_device.h
index daf964a..cac53a0 100644
--- a/cryptohome/storage/encrypted_container/backing_device.h
+++ b/cryptohome/storage/encrypted_container/backing_device.h
@@ -20,6 +20,7 @@
enum class BackingDeviceType {
kUnknown = 0,
kLoopbackDevice,
+ kLogicalVolumeBackingDevice,
};
// Configuration for backing devices.
@@ -30,6 +31,10 @@
struct {
base::FilePath backing_file_path;
} loopback;
+ struct {
+ std::string thinpool_name;
+ base::FilePath physical_volume;
+ } logical_volume;
};
// `BackingDevice` represents a backing block device that can be used as a
diff --git a/cryptohome/storage/encrypted_container/logical_volume_backing_device.cc b/cryptohome/storage/encrypted_container/logical_volume_backing_device.cc
new file mode 100644
index 0000000..27fd562
--- /dev/null
+++ b/cryptohome/storage/encrypted_container/logical_volume_backing_device.cc
@@ -0,0 +1,129 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cryptohome/storage/encrypted_container/logical_volume_backing_device.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <base/files/file_path.h>
+#include <base/values.h>
+#include <base/strings/string_number_conversions.h>
+#include <brillo/blkdev_utils/lvm.h>
+#include <brillo/blkdev_utils/lvm_device.h>
+
+#include "cryptohome/storage/encrypted_container/backing_device.h"
+
+namespace cryptohome {
+
+LogicalVolumeBackingDevice::LogicalVolumeBackingDevice(
+ const BackingDeviceConfig& config,
+ std::unique_ptr<brillo::LogicalVolumeManager> lvm)
+ : name_(config.name),
+ size_(config.size),
+ physical_volume_(config.logical_volume.physical_volume),
+ thinpool_name_(config.logical_volume.thinpool_name),
+ lvm_(std::move(lvm)) {}
+
+LogicalVolumeBackingDevice::LogicalVolumeBackingDevice(
+ const BackingDeviceConfig& config)
+ : LogicalVolumeBackingDevice(
+ config, std::make_unique<brillo::LogicalVolumeManager>()) {}
+
+base::Optional<brillo::LogicalVolume>
+LogicalVolumeBackingDevice::GetLogicalVolume() {
+ base::Optional<brillo::PhysicalVolume> pv =
+ lvm_->GetPhysicalVolume(physical_volume_);
+
+ if (!pv || !pv->IsValid()) {
+ return base::nullopt;
+ }
+
+ base::Optional<brillo::VolumeGroup> vg = lvm_->GetVolumeGroup(*pv);
+
+ if (!vg || !vg->IsValid()) {
+ return base::nullopt;
+ }
+
+ return lvm_->GetLogicalVolume(*vg, name_);
+}
+
+bool LogicalVolumeBackingDevice::Purge() {
+ base::Optional<brillo::LogicalVolume> lv = GetLogicalVolume();
+
+ if (lv && lv->IsValid()) {
+ return lv->Remove();
+ }
+
+ return false;
+}
+
+bool LogicalVolumeBackingDevice::Create() {
+ base::Optional<brillo::PhysicalVolume> pv =
+ lvm_->GetPhysicalVolume(physical_volume_);
+
+ if (!pv || !pv->IsValid()) {
+ return false;
+ }
+
+ base::Optional<brillo::VolumeGroup> vg = lvm_->GetVolumeGroup(*pv);
+
+ if (!vg || !vg->IsValid()) {
+ return false;
+ }
+
+ base::Optional<brillo::Thinpool> thinpool =
+ lvm_->GetThinpool(*vg, thinpool_name_);
+ if (!thinpool || !thinpool->IsValid()) {
+ return false;
+ }
+
+ base::DictionaryValue lv_config;
+ lv_config.SetString("name", name_);
+ lv_config.SetString("size", base::NumberToString(size_));
+
+ base::Optional<brillo::LogicalVolume> lv =
+ lvm_->CreateLogicalVolume(*vg, *thinpool, lv_config);
+ if (!lv || !lv->IsValid()) {
+ return false;
+ }
+
+ return true;
+}
+
+bool LogicalVolumeBackingDevice::Setup() {
+ base::Optional<brillo::LogicalVolume> lv = GetLogicalVolume();
+
+ if (!lv || !lv->IsValid()) {
+ LOG(ERROR) << "Failed to set up logical volume.";
+ return false;
+ }
+
+ return lv->Activate();
+}
+
+bool LogicalVolumeBackingDevice::Teardown() {
+ base::Optional<brillo::LogicalVolume> lv = GetLogicalVolume();
+
+ if (!lv || !lv->IsValid()) {
+ LOG(ERROR) << "Invalid logical volume";
+ return false;
+ }
+
+ return lv->Deactivate();
+}
+
+base::Optional<base::FilePath> LogicalVolumeBackingDevice::GetPath() {
+ base::Optional<brillo::LogicalVolume> lv = GetLogicalVolume();
+
+ if (!lv || !lv->IsValid()) {
+ LOG(ERROR) << "Invalid logical volume";
+ return base::nullopt;
+ }
+
+ return lv->GetPath();
+}
+
+} // namespace cryptohome
diff --git a/cryptohome/storage/encrypted_container/logical_volume_backing_device.h b/cryptohome/storage/encrypted_container/logical_volume_backing_device.h
new file mode 100644
index 0000000..66fd7b9
--- /dev/null
+++ b/cryptohome/storage/encrypted_container/logical_volume_backing_device.h
@@ -0,0 +1,67 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTOHOME_STORAGE_ENCRYPTED_CONTAINER_LOGICAL_VOLUME_BACKING_DEVICE_H_
+#define CRYPTOHOME_STORAGE_ENCRYPTED_CONTAINER_LOGICAL_VOLUME_BACKING_DEVICE_H_
+
+#include "cryptohome/storage/encrypted_container/backing_device.h"
+
+#include <memory>
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/values.h>
+#include <brillo/blkdev_utils/lvm.h>
+
+namespace cryptohome {
+
+// `LogicalVolumeBackingDevice` represents a thin volume backing device.
+class LogicalVolumeBackingDevice : public BackingDevice {
+ public:
+ // `LogicalVolumeBackingDevice` are defined by the following config values:
+ // - `name`: Name of the logical volume.
+ // - `thinpool_name`: Name of thinpool on which the logical volume resides.
+ // - `physical_volume`: Name of device on which the logical volume should be
+ // set up.
+ // - `size`: Size of thin logical volume.
+ LogicalVolumeBackingDevice(const BackingDeviceConfig& config,
+ std::unique_ptr<brillo::LogicalVolumeManager> lvm);
+ explicit LogicalVolumeBackingDevice(const BackingDeviceConfig& config);
+ ~LogicalVolumeBackingDevice() = default;
+
+ // Creates the thin logical volume.
+ bool Create() override;
+
+ // Removed the thin logical volume. The volume should not be in-use before
+ // calling this function.
+ bool Purge() override;
+
+ // Activates the logical volume.
+ bool Setup() override;
+
+ // Deactivates the logical volume.
+ bool Teardown() override;
+
+ // Gets the device type for reporting.
+ BackingDeviceType GetType() override {
+ return BackingDeviceType::kLogicalVolumeBackingDevice;
+ }
+
+ // Gets path to the logical volume's block device.
+ base::Optional<base::FilePath> GetPath() override;
+
+ private:
+ base::Optional<brillo::LogicalVolume> GetLogicalVolume();
+
+ const std::string name_;
+ const uint64_t size_;
+ const base::FilePath physical_volume_;
+ const std::string thinpool_name_;
+
+ std::unique_ptr<brillo::LogicalVolumeManager> lvm_;
+};
+
+} // namespace cryptohome
+
+#endif // CRYPTOHOME_STORAGE_ENCRYPTED_CONTAINER_LOGICAL_VOLUME_BACKING_DEVICE_H_
diff --git a/cryptohome/storage/encrypted_container/logical_volume_backing_device_test.cc b/cryptohome/storage/encrypted_container/logical_volume_backing_device_test.cc
new file mode 100644
index 0000000..376ffa6
--- /dev/null
+++ b/cryptohome/storage/encrypted_container/logical_volume_backing_device_test.cc
@@ -0,0 +1,133 @@
+// Copyright 2020 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cryptohome/storage/encrypted_container/logical_volume_backing_device.h"
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <base/files/file_util.h>
+#include <brillo/blkdev_utils/mock_lvm.h>
+#include <brillo/blkdev_utils/lvm_device.h>
+
+#include "cryptohome/storage/encrypted_container/backing_device.h"
+
+namespace cryptohome {
+
+namespace {
+constexpr char kPhysicalVolumeReport[] =
+ "{\"report\": [{ \"pv\": [ {\"pv_name\":\"/dev/mmcblk0p1\", "
+ "\"vg_name\":\"stateful\"}]}]}";
+constexpr char kThinpoolReport[] =
+ "{\"report\": [{ \"lv\": [ {\"lv_name\":\"thinpool\", "
+ "\"vg_name\":\"stateful\"}]}]}";
+constexpr char kLogicalVolumeReport[] =
+ "{\"report\": [{ \"lv\": [ {\"lv_name\":\"foo\", "
+ "\"vg_name\":\"stateful\"}]}]}";
+} // namespace
+
+class LogicalVolumeBackingDeviceTest : public ::testing::Test {
+ public:
+ LogicalVolumeBackingDeviceTest()
+ : config_({.type = BackingDeviceType::kLogicalVolumeBackingDevice,
+ .name = "foo",
+ .size = 1024,
+ .logical_volume = {.thinpool_name = "thinpool",
+ .physical_volume =
+ base::FilePath("/dev/mmcblk0p1")}}),
+ lvm_command_runner_(std::make_shared<brillo::MockLvmCommandRunner>()),
+ backing_device_(std::make_unique<LogicalVolumeBackingDevice>(
+ config_,
+ std::make_unique<brillo::LogicalVolumeManager>(
+ lvm_command_runner_))) {}
+ ~LogicalVolumeBackingDeviceTest() override = default;
+
+ void ExpectVolumeGroup() {
+ std::vector<std::string> pvdisplay = {
+ "/sbin/pvdisplay", "-C", "--reportformat", "json",
+ config_.logical_volume.physical_volume.value()};
+ EXPECT_CALL(*lvm_command_runner_.get(), RunProcess(pvdisplay, _))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<1>(std::string(kPhysicalVolumeReport)),
+ Return(true)));
+ }
+
+ void ExpectThinpool() {
+ std::vector<std::string> thinpool_display = {
+ "/sbin/lvdisplay", "-S", "pool_lv=\"\"", "-C",
+ "--reportformat", "json", "stateful/thinpool"};
+ EXPECT_CALL(*lvm_command_runner_.get(), RunProcess(thinpool_display, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(std::string(kThinpoolReport)),
+ Return(true)));
+ }
+ void ExpectLogicalVolume() {
+ std::vector<std::string> thinpool_display = {
+ "/sbin/lvdisplay", "-S", "pool_lv!=\"\"", "-C",
+ "--reportformat", "json", "stateful/" + config_.name};
+ EXPECT_CALL(*lvm_command_runner_.get(), RunProcess(thinpool_display, _))
+ .WillRepeatedly(DoAll(
+ SetArgPointee<1>(std::string(kLogicalVolumeReport)), Return(true)));
+ }
+
+ protected:
+ BackingDeviceConfig config_;
+ std::shared_ptr<brillo::MockLvmCommandRunner> lvm_command_runner_;
+
+ std::unique_ptr<BackingDevice> backing_device_;
+};
+
+TEST_F(LogicalVolumeBackingDeviceTest, LogicalVolumeDeviceSetup) {
+ ExpectVolumeGroup();
+ ExpectLogicalVolume();
+
+ std::vector<std::string> lv_enable = {"lvchange", "-ay", "stateful/foo"};
+ EXPECT_CALL(*lvm_command_runner_.get(), RunCommand(lv_enable))
+ .Times(1)
+ .WillOnce(Return(true));
+
+ EXPECT_TRUE(backing_device_->Setup());
+}
+
+TEST_F(LogicalVolumeBackingDeviceTest, LogicalVolumeDeviceCreate) {
+ ExpectVolumeGroup();
+ ExpectThinpool();
+
+ std::vector<std::string> lv_create = {
+ "lvcreate", "--thin", "-V", "1024M", "-n",
+ config_.name, "stateful/thinpool"};
+ EXPECT_CALL(*lvm_command_runner_.get(), RunCommand(lv_create))
+ .Times(1)
+ .WillOnce(Return(true));
+
+ EXPECT_TRUE(backing_device_->Create());
+}
+
+TEST_F(LogicalVolumeBackingDeviceTest, LogicalVolumeDeviceTeardown) {
+ ExpectVolumeGroup();
+ ExpectLogicalVolume();
+
+ std::vector<std::string> lv_disable = {"lvchange", "-an", "stateful/foo"};
+ EXPECT_CALL(*lvm_command_runner_.get(), RunCommand(lv_disable))
+ .Times(1)
+ .WillOnce(Return(true));
+
+ EXPECT_TRUE(backing_device_->Teardown());
+}
+
+TEST_F(LogicalVolumeBackingDeviceTest, LogicalVolumeDevicePurge) {
+ ExpectVolumeGroup();
+ ExpectLogicalVolume();
+
+ std::vector<std::string> lv_disable = {"lvremove", "stateful/foo"};
+ EXPECT_CALL(*lvm_command_runner_.get(), RunCommand(lv_disable))
+ .Times(1)
+ .WillOnce(Return(true));
+
+ EXPECT_TRUE(backing_device_->Purge());
+}
+
+} // namespace cryptohome