| // Copyright 2022 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "dlcservice/lvm/lvmd_proxy_wrapper.h" |
| |
| #include <memory> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/files/file_path.h> |
| #include <base/functional/bind.h> |
| #include <base/functional/callback_helpers.h> |
| #include <lvmd/proto_bindings/lvmd.pb.h> |
| |
| #include "dlcservice/system_state.h" |
| |
| // TODO(b/254557435): Reduce the # of calls made into lvmd. |
| namespace dlcservice { |
| namespace { |
| |
| // CrOS currently only uses "thinpool" as thinpool name. |
| constexpr char kThinpoolName[] = "thinpool"; |
| |
| // The numeric group value for `disk-dlc`. |
| constexpr int kDiskDlcGid = 20777; |
| |
| } // namespace |
| |
| LvmdProxyWrapper::LvmdProxyWrapper( |
| std::unique_ptr<org::chromium::LvmdProxyInterface> lvmd_proxy) |
| : LvmdProxyWrapper(std::move(lvmd_proxy), std::make_unique<Utils>()) {} |
| |
| LvmdProxyWrapper::LvmdProxyWrapper( |
| std::unique_ptr<org::chromium::LvmdProxyInterface> lvmd_proxy, |
| std::unique_ptr<UtilsInterface> utils) |
| : lvmd_proxy_(std::move(lvmd_proxy)), utils_(std::move(utils)) {} |
| |
| bool LvmdProxyWrapper::GetPhysicalVolume(const std::string& device_path, |
| lvmd::PhysicalVolume* pv) { |
| brillo::ErrorPtr err; |
| if (!lvmd_proxy_->GetPhysicalVolume(device_path, pv, &err)) { |
| LOG(WARNING) << "Failed to GetPhysicalVolume from lvmd: " |
| << Error::ToString(err); |
| return false; |
| } |
| return true; |
| } |
| |
| bool LvmdProxyWrapper::GetVolumeGroup(const lvmd::PhysicalVolume& pv, |
| lvmd::VolumeGroup* vg) { |
| brillo::ErrorPtr err; |
| if (!lvmd_proxy_->GetVolumeGroup(pv, vg, &err)) { |
| LOG(WARNING) << "Failed to GetVolumeGroup from lvmd: " |
| << Error::ToString(err); |
| return false; |
| } |
| return true; |
| } |
| |
| bool LvmdProxyWrapper::GetThinpool(const lvmd::VolumeGroup& vg, |
| lvmd::Thinpool* thinpool) { |
| brillo::ErrorPtr err; |
| if (!lvmd_proxy_->GetThinpool(vg, kThinpoolName, thinpool, &err)) { |
| LOG(WARNING) << "Failed to GetThinpool from lvmd: " << Error::ToString(err); |
| return false; |
| } |
| return true; |
| } |
| |
| bool LvmdProxyWrapper::GetLogicalVolume(const lvmd::VolumeGroup& vg, |
| const std::string& lv_name, |
| lvmd::LogicalVolume* lv) { |
| brillo::ErrorPtr err; |
| if (!lvmd_proxy_->GetLogicalVolume(vg, lv_name, lv, &err)) { |
| LOG(WARNING) << "Failed to GetLogicalVolume from lvmd: " |
| << Error::ToString(err); |
| return false; |
| } |
| return true; |
| } |
| |
| bool LvmdProxyWrapper::GetLogicalVolume(const std::string& lv_name, |
| lvmd::LogicalVolume* lv) { |
| auto stateful_path = |
| SystemState::Get()->boot_slot()->GetStatefulPartitionPath(); |
| |
| if (stateful_path.empty()) { |
| LOG(ERROR) << "Failed to GetStatefulPartitionPath."; |
| return false; |
| } |
| |
| lvmd::PhysicalVolume pv; |
| if (!GetPhysicalVolume(stateful_path.value(), &pv)) { |
| LOG(ERROR) << "Failed to GetPhysicalVolume."; |
| return false; |
| } |
| |
| lvmd::VolumeGroup vg; |
| if (!GetVolumeGroup(pv, &vg)) { |
| LOG(ERROR) << "Failed to GetVolumeGroup."; |
| return false; |
| } |
| |
| brillo::ErrorPtr err; |
| if (!lvmd_proxy_->GetLogicalVolume(vg, lv_name, lv, &err)) { |
| LOG(ERROR) << "Failed to GetLogicalVolume."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool LvmdProxyWrapper::CreateLogicalVolume( |
| const lvmd::Thinpool& thinpool, |
| const lvmd::LogicalVolumeConfiguration& lv_config, |
| lvmd::LogicalVolume* lv) { |
| brillo::ErrorPtr err; |
| if (!lvmd_proxy_->CreateLogicalVolume(thinpool, lv_config, lv, &err)) { |
| LOG(WARNING) << "Failed to CreateLogicalVolume in lvmd: " |
| << Error::ToString(err); |
| return false; |
| } |
| // Check for permission changes, this is to handle race condition with |
| // current DLC udev rules. |
| const auto& lv_mapper_path = |
| utils_->MakeAbsoluteFilePath(base::FilePath{lv->path()}); |
| if (!utils_->WaitForGid(lv_mapper_path, kDiskDlcGid)) { |
| LOG(ERROR) |
| << "Failed to CreateLogicalVolume as udev rules did not run for path=" |
| << lv_mapper_path; |
| return false; |
| } |
| return true; |
| } |
| |
| bool LvmdProxyWrapper::ToggleLogicalVolumeActivation( |
| const lvmd::LogicalVolume& lv, bool activate) { |
| brillo::ErrorPtr err; |
| if (!lvmd_proxy_->ToggleLogicalVolumeActivation(lv, activate, &err)) { |
| LOG(WARNING) << "Failed to ToggleLogicalVolumeActivation in lvmd: " |
| << Error::ToString(err); |
| return false; |
| } |
| return true; |
| } |
| |
| bool LvmdProxyWrapper::CreateLogicalVolumes( |
| const std::vector<lvmd::LogicalVolumeConfiguration>& lv_configs) { |
| auto stateful_path = |
| SystemState::Get()->boot_slot()->GetStatefulPartitionPath(); |
| |
| if (stateful_path.empty()) { |
| LOG(ERROR) << "Failed to GetStatefulPartitionPath."; |
| return false; |
| } |
| |
| lvmd::PhysicalVolume pv; |
| if (!GetPhysicalVolume(stateful_path.value(), &pv)) { |
| LOG(ERROR) << "Failed to GetPhysicalVolume."; |
| return false; |
| } |
| |
| lvmd::VolumeGroup vg; |
| if (!GetVolumeGroup(pv, &vg)) { |
| LOG(ERROR) << "Failed to GetVolumeGroup."; |
| return false; |
| } |
| |
| lvmd::Thinpool thinpool; |
| if (!GetThinpool(vg, &thinpool)) { |
| LOG(ERROR) << "Failed to GetThinpool."; |
| return false; |
| } |
| |
| // Prefer using thinpool's volume group as thinpool is passed into creating |
| // the logical volumes. |
| lvmd::LogicalVolume lv; |
| for (const auto& lv_config : lv_configs) { |
| auto lv_name = lv_config.name(); |
| if (GetLogicalVolume(thinpool.volume_group(), lv_name, &lv)) { |
| if (!ToggleLogicalVolumeActivation(lv, /*activate=*/true)) { |
| LOG(ERROR) << "Failed to ToggleLogicalVolumeActivation name=" |
| << lv_name; |
| return false; |
| } |
| LOG(INFO) << "Activated name=" << lv_name; |
| } else if (!CreateLogicalVolume(thinpool, lv_config, &lv)) { |
| LOG(ERROR) << "Failed to CreateLogicalVolume name=" << lv_name; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool LvmdProxyWrapper::RemoveLogicalVolumes( |
| const std::vector<std::string>& lv_names) { |
| auto stateful_path = |
| SystemState::Get()->boot_slot()->GetStatefulPartitionPath(); |
| |
| lvmd::PhysicalVolume pv; |
| if (!GetPhysicalVolume(stateful_path.value(), &pv)) { |
| LOG(ERROR) << "Failed to GetPhysicalVolume."; |
| return false; |
| } |
| |
| lvmd::VolumeGroup vg; |
| if (!GetVolumeGroup(pv, &vg)) { |
| LOG(ERROR) << "Failed to GetVolumeGroup."; |
| return false; |
| } |
| |
| brillo::ErrorPtr err; |
| lvmd::RemoveLogicalVolumesRequest request; |
| lvmd::RemoveLogicalVolumesResponse response; |
| |
| for (const auto& lv_name : lv_names) { |
| auto* lv = request.mutable_logical_volume_list()->add_logical_volume(); |
| lv->set_name(lv_name); |
| lv->mutable_volume_group()->CopyFrom(vg); |
| } |
| if (!lvmd_proxy_->RemoveLogicalVolumes(request, &response, &err)) { |
| LOG(WARNING) << "Failed to RemoveLogicalVolumes in lvmd: " |
| << Error::ToString(err); |
| return false; |
| } |
| return true; |
| } |
| |
| void LvmdProxyWrapper::RemoveLogicalVolumesAsync( |
| const std::vector<std::string>& lv_names, |
| base::OnceCallback<void(bool)> cb) { |
| lvmd::RemoveLogicalVolumesRequest request; |
| auto* lv_list = request.mutable_logical_volume_list(); |
| for (const auto& lv_name : lv_names) { |
| auto* lv = lv_list->add_logical_volume(); |
| lv->set_name(lv_name); |
| } |
| auto [cb1, cb2] = base::SplitOnceCallback(std::move(cb)); |
| lvmd_proxy_->RemoveLogicalVolumesAsync( |
| request, |
| base::BindOnce( |
| [](decltype(cb) cb, const lvmd::RemoveLogicalVolumesResponse&) { |
| std::move(cb).Run(true); |
| }, |
| std::move(cb1)), |
| base::BindOnce( |
| [](decltype(cb) cb, brillo::Error*) { std::move(cb).Run(false); }, |
| std::move(cb2))); |
| } |
| |
| bool LvmdProxyWrapper::ActivateLogicalVolume(const std::string& lv_name) { |
| auto stateful_path = |
| SystemState::Get()->boot_slot()->GetStatefulPartitionPath(); |
| |
| lvmd::PhysicalVolume pv; |
| if (!GetPhysicalVolume(stateful_path.value(), &pv)) { |
| LOG(ERROR) << "Failed to GetPhysicalVolume."; |
| return false; |
| } |
| |
| lvmd::VolumeGroup vg; |
| if (!GetVolumeGroup(pv, &vg)) { |
| LOG(ERROR) << "Failed to GetVolumeGroup."; |
| return false; |
| } |
| |
| lvmd::LogicalVolume lv; |
| if (!GetLogicalVolume(vg, lv_name, &lv)) { |
| LOG(ERROR) << "Failed to GetLogicalVolume."; |
| return false; |
| } |
| |
| if (!ToggleLogicalVolumeActivation(lv, /*activate=*/true)) { |
| LOG(ERROR) << "Failed to ToggleLogicalVolumeActivation from lvmd."; |
| return false; |
| } |
| return true; |
| } |
| |
| bool LvmdProxyWrapper::ListLogicalVolumes(lvmd::LogicalVolumeList* lvs) { |
| auto stateful_path = |
| SystemState::Get()->boot_slot()->GetStatefulPartitionPath(); |
| |
| lvmd::PhysicalVolume pv; |
| if (!GetPhysicalVolume(stateful_path.value(), &pv)) { |
| LOG(ERROR) << "Failed to GetPhysicalVolume."; |
| return false; |
| } |
| |
| lvmd::VolumeGroup vg; |
| if (!GetVolumeGroup(pv, &vg)) { |
| LOG(ERROR) << "Failed to GetVolumeGroup."; |
| return false; |
| } |
| |
| brillo::ErrorPtr err; |
| return lvmd_proxy_->ListLogicalVolumes(vg, lvs, &err); |
| } |
| |
| std::string LvmdProxyWrapper::GetLogicalVolumePath(const std::string& lv_name) { |
| lvmd::LogicalVolume lv; |
| return GetLogicalVolume(lv_name, &lv) ? lv.path() : ""; |
| } |
| |
| std::optional<int64_t> LvmdProxyWrapper::GetLogicalVolumeSize( |
| const std::string& lv_name) { |
| lvmd::LogicalVolume lv; |
| if (GetLogicalVolume(lv_name, &lv) && lv.size() >= 0) |
| return lv.size(); |
| return std::nullopt; |
| } |
| |
| bool LvmdProxyWrapper::ResizeLogicalVolumes( |
| const std::vector<lvmd::LogicalVolumeConfiguration>& lv_configs) { |
| auto stateful_path = |
| SystemState::Get()->boot_slot()->GetStatefulPartitionPath(); |
| |
| if (stateful_path.empty()) { |
| LOG(ERROR) << "Failed to GetStatefulPartitionPath."; |
| return false; |
| } |
| |
| lvmd::PhysicalVolume pv; |
| if (!GetPhysicalVolume(stateful_path.value(), &pv)) { |
| LOG(ERROR) << "Failed to GetPhysicalVolume."; |
| return false; |
| } |
| |
| lvmd::VolumeGroup vg; |
| if (!GetVolumeGroup(pv, &vg)) { |
| LOG(ERROR) << "Failed to GetVolumeGroup."; |
| return false; |
| } |
| |
| lvmd::LogicalVolume lv; |
| for (const auto& lv_config : lv_configs) { |
| auto lv_name = lv_config.name(); |
| if (!GetLogicalVolume(vg, lv_name, &lv)) { |
| LOG(ERROR) << "Failed to GetLogicalVolume " << lv_name; |
| return false; |
| } |
| |
| brillo::ErrorPtr err; |
| if (!lvmd_proxy_->ResizeLogicalVolume(lv, lv_config.size(), &err)) { |
| LOG(ERROR) << "Failed to ResizeLogicalVolume from lvmd: " |
| << Error::ToString(err); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| } // namespace dlcservice |