use std::{
borrow::Borrow,
collections::{HashMap, HashSet},
ffi::OsStr,
fmt::{self, Debug, Display},
hash::Hash,
iter::once,
ops::Deref,
path::{Path, PathBuf},
};
use libudev::EventType;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use uuid::Uuid;
pub use crate::engine::{
engine::{Engine, StateDiff},
structures::Lockable,
types::{
actions::{
Clevis, CreateAction, DeleteAction, EngineAction, GrowAction, Key, MappingCreateAction,
MappingDeleteAction, PropChangeAction, RegenAction, RenameAction, SetCreateAction,
SetDeleteAction, SetUnlockAction, StartAction, StopAction, ToDisplay,
},
diff::{
Compare, Diff, PoolDiff, StratBlockDevDiff, StratFilesystemDiff, StratPoolDiff,
ThinPoolDiff,
},
keys::{EncryptionInfo, KeyDescription, PoolEncryptionInfo, SizedKeyMemory},
},
};
use crate::stratis::{StratisError, StratisResult};
mod actions;
mod diff;
mod keys;
macro_rules! uuid {
($vis:vis $ident:ident) => {
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Deserialize, Serialize)]
$vis struct $ident(pub uuid::Uuid);
impl $ident {
pub fn new_v4() -> Self {
$ident(uuid::Uuid::new_v4())
}
pub fn parse_str(s: &str) -> $crate::stratis::StratisResult<Self> {
Ok($ident(uuid::Uuid::parse_str(s)?))
}
pub fn nil() -> Self {
$ident(uuid::Uuid::nil())
}
}
impl std::ops::Deref for $ident {
type Target = uuid::Uuid;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl std::fmt::Display for $ident {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl $crate::engine::types::AsUuid for $ident {}
}
}
pub type ClevisInfo = (String, Value);
pub trait AsUuid:
Copy
+ Clone
+ Debug
+ Hash
+ Eq
+ PartialEq
+ for<'a> Deserialize<'a>
+ Serialize
+ Deref<Target = Uuid>
+ Display
{
}
uuid!(pub DevUuid);
uuid!(pub FilesystemUuid);
uuid!(pub PoolUuid);
#[derive(Debug, Eq, PartialEq)]
pub enum StratisUuid {
Dev(DevUuid),
Fs(FilesystemUuid),
Pool(PoolUuid),
}
impl Deref for StratisUuid {
type Target = Uuid;
fn deref(&self) -> &Self::Target {
match self {
StratisUuid::Dev(d) => d,
StratisUuid::Fs(f) => f,
StratisUuid::Pool(p) => p,
}
}
}
impl Display for StratisUuid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StratisUuid::Dev(d) => Display::fmt(d, f),
StratisUuid::Fs(fs) => Display::fmt(fs, f),
StratisUuid::Pool(p) => Display::fmt(p, f),
}
}
}
#[derive(Serialize, Deserialize, Clone, Copy, Eq, PartialEq)]
pub enum UnlockMethod {
Clevis,
Keyring,
}
impl<'a> TryFrom<&'a str> for UnlockMethod {
type Error = StratisError;
fn try_from(s: &str) -> StratisResult<UnlockMethod> {
match s {
"keyring" => Ok(UnlockMethod::Keyring),
"clevis" => Ok(UnlockMethod::Clevis),
_ => Err(StratisError::Msg(format!(
"{} is an invalid unlock method",
s
))),
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BlockDevTier {
Data = 0,
Cache = 1,
}
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize)]
pub struct Name(String);
impl Name {
pub fn new(name: String) -> Name {
Name(name)
}
pub fn to_owned(&self) -> String {
self.0.clone()
}
}
impl AsRef<str> for Name {
fn as_ref(&self) -> &str {
&self.0
}
}
impl Deref for Name {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
impl Borrow<str> for Name {
fn borrow(&self) -> &str {
&self.0
}
}
impl fmt::Display for Name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
pub enum ReportType {
StoppedPools,
}
impl<'a> TryFrom<&'a str> for ReportType {
type Error = StratisError;
fn try_from(name: &str) -> StratisResult<ReportType> {
match name {
"stopped_pools" => Ok(ReportType::StoppedPools),
_ => Err(StratisError::Msg(format!(
"Report name {} not understood",
name
))),
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct PoolDevice {
pub devnode: PathBuf,
pub uuid: DevUuid,
}
#[derive(Debug, Eq, PartialEq)]
pub struct LockedPoolInfo {
pub info: PoolEncryptionInfo,
pub devices: Vec<PoolDevice>,
}
#[derive(Default, Debug, Eq, PartialEq)]
pub struct LockedPoolsInfo {
pub locked: HashMap<PoolUuid, LockedPoolInfo>,
pub name_to_uuid: HashMap<Name, PoolUuid>,
pub uuid_to_name: HashMap<PoolUuid, Name>,
}
#[derive(Debug, Eq, PartialEq)]
pub struct StoppedPoolInfo {
pub info: Option<PoolEncryptionInfo>,
pub devices: Vec<PoolDevice>,
}
#[derive(Default, Debug, Eq, PartialEq)]
pub struct StoppedPoolsInfo {
pub stopped: HashMap<PoolUuid, StoppedPoolInfo>,
pub name_to_uuid: HashMap<Name, PoolUuid>,
pub uuid_to_name: HashMap<PoolUuid, Name>,
}
pub struct UdevEngineEvent {
event_type: EventType,
device: UdevEngineDevice,
}
impl UdevEngineEvent {
#[cfg(test)]
pub fn new(event_type: EventType, device: UdevEngineDevice) -> Self {
UdevEngineEvent { event_type, device }
}
pub fn event_type(&self) -> EventType {
self.event_type
}
pub fn device(&self) -> &UdevEngineDevice {
&self.device
}
}
impl From<&libudev::Event> for UdevEngineEvent {
fn from(e: &libudev::Event) -> UdevEngineEvent {
UdevEngineEvent {
event_type: e.event_type(),
device: UdevEngineDevice::from(e.device()),
}
}
}
pub struct UdevEngineDevice {
is_initialized: bool,
devnode: Option<PathBuf>,
devnum: Option<libc::dev_t>,
properties: HashMap<Box<OsStr>, Box<OsStr>>,
}
impl UdevEngineDevice {
#[cfg(test)]
pub fn new(
is_initialized: bool,
devnode: Option<PathBuf>,
devnum: Option<libc::dev_t>,
properties: HashMap<Box<OsStr>, Box<OsStr>>,
) -> Self {
UdevEngineDevice {
is_initialized,
devnode,
devnum,
properties,
}
}
pub fn is_initialized(&self) -> bool {
self.is_initialized
}
pub fn devnode(&self) -> Option<&Path> {
self.devnode.as_deref()
}
pub fn devnum(&self) -> Option<libc::dev_t> {
self.devnum
}
pub fn property_value<T>(&self, property_name: T) -> Option<StratisResult<&str>>
where
T: AsRef<OsStr> + Display,
{
self.properties
.get(property_name.as_ref())
.map(|value| {
value.to_str()
.ok_or_else(|| {
StratisError::Msg(
format!(
"Unable to convert udev property value with key {} to a string, lossy value is {}",
property_name,
value.to_string_lossy()
),
)
})
})
}
}
impl From<&libudev::Device> for UdevEngineDevice {
fn from(d: &libudev::Device) -> UdevEngineDevice {
UdevEngineDevice {
is_initialized: d.is_initialized(),
devnode: d.devnode().map(|p| p.to_owned()),
devnum: d.devnum(),
properties: d
.properties()
.map(|prop| (Box::from(prop.name()), Box::from(prop.value())))
.collect(),
}
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct DevicePath(PathBuf);
impl DevicePath {
pub fn new(path: &Path) -> StratisResult<Self> {
Ok(DevicePath(path.canonicalize()?))
}
}
impl Deref for DevicePath {
type Target = Path;
fn deref(&self) -> &Self::Target {
self.0.as_path()
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub enum ActionAvailability {
Full = 0,
NoRequests = 1,
NoPoolChanges = 2,
}
impl Display for ActionAvailability {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
ActionAvailability::Full => "fully_operational",
ActionAvailability::NoRequests => "no_ipc_requests",
ActionAvailability::NoPoolChanges => "no_pool_changes",
}
)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum MaybeInconsistent<T> {
Yes,
No(T),
}
impl<T> MaybeInconsistent<Option<T>> {
pub fn as_ref(&self) -> MaybeInconsistent<Option<&T>> {
match self {
MaybeInconsistent::Yes => MaybeInconsistent::Yes,
MaybeInconsistent::No(opt) => MaybeInconsistent::No(opt.as_ref()),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum PoolIdentifier<U> {
Name(Name),
Uuid(U),
}
impl<U> Display for PoolIdentifier<U>
where
U: Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PoolIdentifier::Name(n) => write!(f, "name {}", n),
PoolIdentifier::Uuid(u) => write!(f, "UUID {}", u),
}
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum UuidOrConflict {
Uuid(PoolUuid),
Conflict(HashSet<PoolUuid>),
}
impl UuidOrConflict {
fn invariant(&self) -> bool {
if let UuidOrConflict::Conflict(set) = self {
set.len() > 1
} else {
true
}
}
pub fn to_result(&self) -> StratisResult<PoolUuid> {
assert!(self.invariant());
match self {
UuidOrConflict::Uuid(u) => Ok(*u),
UuidOrConflict::Conflict(set) => Err(StratisError::Msg(format!(
"Found conflicting UUIDs for the same pool name: {}; please use UUID for operation",
set.iter()
.map(|u| u.to_string())
.collect::<Vec<_>>()
.join(", ")
))),
}
}
pub fn add(&mut self, uuid: PoolUuid) {
assert!(self.invariant());
match self {
UuidOrConflict::Uuid(u) => {
if *u != uuid {
*self = UuidOrConflict::Conflict(
once(*u).chain(once(uuid)).collect::<HashSet<_>>(),
);
}
}
UuidOrConflict::Conflict(set) => {
set.insert(uuid);
}
}
}
pub fn remove(&mut self, uuid: &PoolUuid) -> bool {
assert!(self.invariant());
match self {
UuidOrConflict::Uuid(u) => u == uuid,
UuidOrConflict::Conflict(set) => {
set.remove(uuid);
if set.len() == 1 {
let last_elem = set.drain().next().expect("Some(_) checked above");
*self = UuidOrConflict::Uuid(last_elem);
}
false
}
}
}
}