use std::{fs::File, io::Read, path::PathBuf, thread::sleep, time::Duration};
use nix::mount::{umount2, MntFlags};
use devicemapper::{DevId, DmNameBuf, DmOptions};
use crate::engine::strat_engine::{
cmd::udev_settle,
dm::{get_dm, get_dm_init},
};
mod cleanup_errors {
error_chain! {
foreign_links {
Ioe(std::io::Error);
Mnt(libmount::mountinfo::ParseError);
Nix(nix::Error);
}
}
}
use self::cleanup_errors::{Error, Result};
fn dm_stratis_devices_remove() -> Result<()> {
fn one_iteration() -> Result<(bool, Vec<DmNameBuf>)> {
let mut progress_made = false;
let mut remain = get_dm()
.list_devices()
.map_err(|e| {
let err_msg = "failed while listing DM devices, giving up";
Error::with_chain(e, err_msg)
})?
.iter()
.map(|d| &d.0)
.filter_map(|n| {
if !n.to_string().starts_with("stratis-1") {
None
} else {
match get_dm().device_remove(&DevId::Name(n), &DmOptions::new()) {
Ok(_) => {
progress_made = true;
None
}
Err(_) => Some(n.to_owned()),
}
}
})
.collect::<Vec<_>>();
if !remain.is_empty() && !progress_made {
remain = remain
.into_iter()
.filter(|name| {
for _ in 0..3 {
match get_dm().device_remove(&DevId::Name(name), &DmOptions::new()) {
Ok(_) => {
progress_made = true;
return false;
}
Err(e) => {
debug!(
"Failed to remove device {} on retry: {}",
name.to_string(),
e
);
sleep(Duration::from_secs(1));
}
}
}
true
})
.collect();
}
Ok((progress_made, remain))
}
fn do_while_progress() -> Result<Vec<DmNameBuf>> {
let mut result = one_iteration()?;
while result.0 {
result = one_iteration()?;
}
Ok(result.1)
}
|| -> Result<()> {
udev_settle().unwrap();
get_dm_init().map_err(|err| Error::with_chain(err, "Unable to initialize DM"))?;
do_while_progress().and_then(|remain| {
if !remain.is_empty() {
Err(format!("Some Stratis DM devices remaining: {:?}", remain).into())
} else {
Ok(())
}
})
}()
.map_err(|e| e.chain_err(|| "Failed to ensure removal of all Stratis DM devices"))
}
fn stratis_filesystems_unmount() -> Result<()> {
|| -> Result<()> {
let mut mount_data = String::new();
File::open("/proc/self/mountinfo")?.read_to_string(&mut mount_data)?;
let parser = libmount::mountinfo::Parser::new(mount_data.as_bytes());
for mount_point in parser
.filter_map(|x| x.ok())
.filter_map(|m| m.mount_point.into_owned().into_string().ok())
.filter(|mp| mp.contains("stratis"))
{
umount2(&PathBuf::from(mount_point), MntFlags::MNT_DETACH)?;
}
Ok(())
}()
.map_err(|e| e.chain_err(|| "Failed to ensure all Stratis filesystems were unmounted"))
}
pub fn clean_up() -> Result<()> {
stratis_filesystems_unmount().and_then(|_| dm_stratis_devices_remove())
}