use crate::devices::{format_device_busy_error, DeviceError};
use crate::platform::traits::{DeviceSafety, VolumeOps};
use std::process::Command;
use super::LinuxPlatform;
pub type LinuxVolumeGuard = ();
impl VolumeOps for LinuxPlatform {
type Guard = LinuxVolumeGuard;
fn prepare_for_io(device_path: &str, auto_unmount: bool) -> Result<Self::Guard, DeviceError> {
let mounts = LinuxPlatform::list_mounts(device_path)?;
if mounts.is_empty() {
return Ok(());
}
if !auto_unmount {
return Err(DeviceError::busy(format_device_busy_error(
device_path, &mounts,
)));
}
Self::unmount_volumes(device_path)?;
let remaining = LinuxPlatform::list_mounts(device_path)?;
if !remaining.is_empty() {
return Err(DeviceError::query(format!(
"Could not unmount all volumes on {device_path}: {}",
remaining
.iter()
.map(|m| format!("{} on {}", m.source, m.mount_point))
.collect::<Vec<_>>()
.join(", ")
)));
}
Ok(())
}
fn unmount_volumes(device_path: &str) -> Result<Vec<String>, DeviceError> {
let mounts = LinuxPlatform::list_mounts(device_path)?;
if mounts.is_empty() {
return Ok(Vec::new());
}
let mut unmounted = Vec::new();
let mut failures = Vec::new();
for mount in mounts {
match umount_partition(&mount.source) {
Ok(()) => unmounted.push(format!("{} on {}", mount.source, mount.mount_point)),
Err(err) => failures.push(format!("{}: {err}", mount.source)),
}
}
if !failures.is_empty() {
let mut message = format!("Could not unmount all volumes on {device_path}");
if !unmounted.is_empty() {
message.push_str(&format!(" (unmounted: {})", unmounted.join(", ")));
}
message.push_str(&format!(". Still busy: {}", failures.join("; ")));
return Err(DeviceError::unmount(message));
}
Ok(unmounted)
}
}
fn umount_partition(source: &str) -> Result<(), DeviceError> {
let output = Command::new("umount")
.arg(source)
.output()
.map_err(|e| DeviceError::query(format!("Failed to run umount: {e}")))?;
if output.status.success() {
return Ok(());
}
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
Err(DeviceError::unmount(if stderr.is_empty() {
format!("umount {source} failed (exit {:?})", output.status.code())
} else {
format!("umount {source}: {stderr}")
}))
}