use super::cmd;
use super::detect::{BlockDevice, has_holders, has_partitions, is_mounted};
use super::error::StorageError;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EmptyCheckResult {
Empty,
HasPartitionTable,
HasFilesystem,
HasPartitions,
IsMounted,
HasHolders,
}
impl EmptyCheckResult {
pub fn is_empty(&self) -> bool {
matches!(self, EmptyCheckResult::Empty)
}
}
pub fn verify_empty(device: &BlockDevice) -> Result<EmptyCheckResult, StorageError> {
let dev_path = device.path.to_string_lossy();
log::debug!("verifying device is empty: {}", dev_path);
if has_partitions(device) {
log::debug!("{}: has partitions in sysfs", dev_path);
return Ok(EmptyCheckResult::HasPartitions);
}
if is_mounted(device) {
log::debug!("{}: is mounted", dev_path);
return Ok(EmptyCheckResult::IsMounted);
}
if has_holders(device) {
log::debug!("{}: has holders", dev_path);
return Ok(EmptyCheckResult::HasHolders);
}
if has_blkid_signature(device)? {
log::debug!("{}: has blkid signature", dev_path);
return Ok(EmptyCheckResult::HasFilesystem);
}
if has_sgdisk_partitions(device)? {
log::debug!("{}: has sgdisk partitions", dev_path);
return Ok(EmptyCheckResult::HasPartitionTable);
}
log::info!("{}: verified empty", dev_path);
Ok(EmptyCheckResult::Empty)
}
fn has_blkid_signature(device: &BlockDevice) -> Result<bool, StorageError> {
let dev_path = device.path.to_string_lossy();
let output = cmd::run_allow_fail("blkid", &["-p", &dev_path]);
match output {
Some(o) => {
let has_output = !o.stdout.is_empty();
Ok(has_output)
}
None => {
log::warn!("blkid command failed, assuming no signature");
Ok(false)
}
}
}
fn has_sgdisk_partitions(device: &BlockDevice) -> Result<bool, StorageError> {
let dev_path = device.path.to_string_lossy();
let output = cmd::run_allow_fail("sgdisk", &["-p", &dev_path]);
match output {
Some(o) => {
let stdout = String::from_utf8_lossy(&o.stdout);
if stdout.contains("Creating new GPT entries") {
return Ok(false);
}
for line in stdout.lines() {
let trimmed = line.trim();
if let Some(first_char) = trimmed.chars().next()
&& first_char.is_ascii_digit()
&& trimmed.contains(" ")
{
return Ok(true);
}
}
Ok(false)
}
None => {
Ok(false)
}
}
}
pub fn find_first_empty(devices: &[BlockDevice]) -> Result<Option<&BlockDevice>, StorageError> {
for device in devices {
match verify_empty(device)? {
EmptyCheckResult::Empty => {
log::info!("found empty disk: {}", device.name);
return Ok(Some(device));
}
reason => {
log::debug!("skipping {}: {:?}", device.name, reason);
}
}
}
log::info!("no empty disks found");
Ok(None)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_check_result_is_empty() {
assert!(EmptyCheckResult::Empty.is_empty());
assert!(!EmptyCheckResult::HasPartitions.is_empty());
assert!(!EmptyCheckResult::HasFilesystem.is_empty());
}
}