blob: 01fda8107e9cb0964f20853fa74e184ff0d3358b [file] [log] [blame] [edit]
// 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/dlc_lvm.h"
#include <algorithm>
#include <string>
#include <utility>
#include <base/files/file_util.h>
#include <base/strings/stringprintf.h>
#include <lvmd/proto_bindings/lvmd.pb.h>
#include <lvmd/dbus-proxies.h>
#include "dlcservice/lvm/lvm_utils.h"
#include "dlcservice/system_state.h"
#include "dlcservice/utils.h"
namespace dlcservice {
DlcLvm::DlcLvm(DlcId id) : DlcBase(std::move(id)) {}
bool DlcLvm::CreateDlc(brillo::ErrorPtr* err) {
if (!UseLogicalVolume()) {
LOG(INFO) << "Skipping creation of logical volumes for DLC=" << id_;
return DlcBase::CreateDlc(err);
}
LOG(INFO) << "Creating logical volumes for DLC=" << id_;
if (!CreateDlcLogicalVolumes()) {
LOG(ERROR) << "Failed to create logical volumes for DLC=" << id_;
// TODO(b/236007986): Report metrics.
*err = Error::Create(
FROM_HERE, kErrorInternal,
base::StringPrintf("Failed to create DLC=%s logical volumes.",
id_.c_str()));
return false;
}
// TODO(b/236007986): Fix cros deploy'ing by migrating existing DLCs
// pre-logical volume here.
return true;
}
bool DlcLvm::CreateDlcLogicalVolumes() {
lvmd::LogicalVolumeConfiguration lv_config_a, lv_config_b;
lv_config_a.set_name(LogicalVolumeName(id_, BootSlotInterface::Slot::A));
lv_config_b.set_name(LogicalVolumeName(id_, BootSlotInterface::Slot::B));
auto size = manifest_->preallocated_size();
// Convert to MiB from bytes.
size /= 1024 * 1024;
// Cannot pass in a value of 0, so set the lower bound to 1MiB.
size = std::max<int64_t>(1, size);
lv_config_a.set_size(size);
lv_config_b.set_size(size);
if (!SystemState::Get()->lvmd_wrapper()->CreateLogicalVolumes({
lv_config_a,
lv_config_b,
})) {
LOG(ERROR) << "Failed to create logical volumes for DLC=" << id_;
return false;
}
return true;
}
bool DlcLvm::DeleteInternal(brillo::ErrorPtr* err) {
if (!UseLogicalVolume()) {
LOG(INFO) << "Skipping deletion of logical volumes for DLC=" << id_;
return DlcBase::DeleteInternal(err);
}
LOG(INFO) << "Deleting logical volumes for DLC=" << id_;
bool ret = true;
if (!DeleteInternalLogicalVolumes()) {
*err = Error::CreateInternal(
FROM_HERE, error::kFailedInternal,
base::StringPrintf("Failed to delete logical volumes for DLC=%s",
id_.c_str()));
ret = false;
}
// Still run base `DeleteInternal()`.
// This allows migration onto newer release to cleanup old paths.
return DlcBase::DeleteInternal(err) && ret;
}
bool DlcLvm::DeleteInternalLogicalVolumes() {
return SystemState::Get()->lvmd_wrapper()->RemoveLogicalVolumes({
LogicalVolumeName(id_, BootSlotInterface::Slot::A),
LogicalVolumeName(id_, BootSlotInterface::Slot::B),
});
}
bool DlcLvm::MountInternal(std::string* mount_point, brillo::ErrorPtr* err) {
if (!UseLogicalVolume()) {
return DlcBase::MountInternal(mount_point, err);
}
imageloader::LoadDlcRequest request;
request.set_id(id_);
request.set_path(
GetImagePath(SystemState::Get()->active_boot_slot()).value());
request.set_package(package_);
if (!SystemState::Get()->image_loader()->LoadDlc(request, mount_point,
nullptr,
/*timeout_ms=*/60 * 1000)) {
*err = Error::CreateInternal(FROM_HERE, error::kFailedToMountImage,
"Imageloader is unavailable for LoadDlc().");
state_.set_last_error_code(Error::GetErrorCode(*err));
return false;
}
if (mount_point->empty()) {
*err = Error::CreateInternal(FROM_HERE, error::kFailedToMountImage,
"Imageloader LoadDlc() call failed.");
state_.set_last_error_code(Error::GetErrorCode(*err));
return false;
}
return true;
}
bool DlcLvm::MakeReadyForUpdateInternal() const {
if (!UseLogicalVolume()) {
LOG(INFO) << "Skipping update ready marking of logical volume for DLC="
<< id_;
return DlcBase::MakeReadyForUpdateInternal();
}
auto inactive_lv_name =
LogicalVolumeName(id_, SystemState::Get()->inactive_boot_slot());
if (!SystemState::Get()->lvmd_wrapper()->ActivateLogicalVolume(
inactive_lv_name)) {
LOG(ERROR) << "Failed to activate inactive logical volumes for DLC=" << id_;
return false;
}
return true;
}
bool DlcLvm::VerifyInternal(const base::FilePath& image_path,
std::vector<uint8_t>* image_sha256) {
if (!UseLogicalVolume()) {
LOG(INFO) << "Skipping verification of logical voluems for DLC=" << id_;
return DlcBase::VerifyInternal(image_path, image_sha256);
}
if (!HashFile(image_path, manifest_->size(), image_sha256,
/*skip_size_check=*/true)) {
LOG(ERROR) << "Failed to hash logical volume: " << image_path.value();
return false;
}
return true;
}
base::FilePath DlcLvm::GetImagePath(BootSlot::Slot slot) const {
if (!UseLogicalVolume()) {
return DlcBase::GetImagePath(slot);
}
auto lv_name = LogicalVolumeName(id_, slot);
return base::FilePath(
SystemState::Get()->lvmd_wrapper()->GetLogicalVolumePath(lv_name));
}
bool DlcLvm::UseLogicalVolume() const {
return manifest_->use_logical_volume() &&
SystemState::Get()->IsLvmStackEnabled();
}
} // namespace dlcservice