blob: 2e9d1494b31e868432a8bbc0758b970c4d87dda7 [file] [log] [blame] [edit]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! Implements an API for managing device mapper (DM) devices
use std::ffi::OsStr;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::str;
use anyhow::Context;
use anyhow::Result;
use crate::hiberutil::checked_command;
use crate::hiberutil::checked_command_output;
/// Path of the dmsetup utility.
const DMSETUP_PATH: &str = "/sbin/dmsetup";
pub struct DeviceMapper {}
impl DeviceMapper {
/// Create a new DM device.
pub fn create_device(name: &str, table: &str) -> Result<()> {
let args = vec![
String::from("create"),
name.to_string(),
String::from("--table"),
table.to_string(),
];
Self::run_dmsetup(args).context(format!("Failed to create DM device '{name}'"))?;
Ok(())
}
/// Remove an existing DM device.
pub fn remove_device(name: &str) -> Result<()> {
Self::run_dmsetup(["remove", name])
.context(format!("Failed to remove DM device '{name}'"))?;
Ok(())
}
/// Rename a DM device.
pub fn rename_device(old_name: &str, new_name: &str) -> Result<()> {
Self::run_dmsetup(["rename", old_name, new_name]).context(format!(
"Failed to rename DM device '{old_name}' to '{new_name}"
))?;
Ok(())
}
/// Set the UUID of a DM device.
pub fn set_device_uuid(name: &str, uuid: &str) -> Result<()> {
Self::run_dmsetup(["rename", name, "--setuuid", uuid])
.context(format!("Failed to set UUID of DM device '{name}'"))?;
Ok(())
}
/// Suspend a DM device.
pub fn suspend_device(name: &str) -> Result<()> {
Self::run_dmsetup(["suspend", name])
.context(format!("Failed to suspend DM device '{name}'"))?;
Ok(())
}
/// Resume a suspended DM device.
pub fn resume_device(name: &str) -> Result<()> {
Self::run_dmsetup(["resume", name])
.context(format!("Failed to suspend DM device '{name}'"))?;
Ok(())
}
/// Reload the device table of a DM device.
pub fn reload_device_table(name: &str, table: &str) -> Result<()> {
Self::run_dmsetup(["reload", name, "--table", table]).context(format!(
"Failed to reload DM table for device \
'{name}' (table: '{table}')"
))?;
Ok(())
}
/// Get the table of a DM device.
pub fn get_device_table(name: &str) -> Result<String> {
let out = checked_command_output(Command::new(DMSETUP_PATH).args(["table", name]))
.context(format!("Failed to get DM table for device '{name}'"))?;
let table = str::from_utf8(&out.stdout)
.context(format!(
"Table of DM device '{name}' contains non-UTF8 characters"
))?
.trim()
.to_string();
Ok(table)
}
/// Check whether a DM device exists.
pub fn device_exists(name: &str) -> bool {
Self::run_dmsetup(["status", name]).is_ok()
}
/// Get the path of a DM device.
pub fn device_path(name: &str) -> Result<PathBuf> {
let symlink_path = Path::new("/dev/mapper").join(name);
fs::canonicalize(symlink_path).map_err(anyhow::Error::from)
}
fn run_dmsetup<I, S>(args: I) -> Result<()>
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
checked_command(Command::new(DMSETUP_PATH).args(args))
}
}