use anyhow::Result;
use std::fs::OpenOptions;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;
use super::super::gpt::{map_partitions, open_gpt};
use super::super::types::DiskInfo;
pub fn info(disk: &Path, json: bool) -> Result<()> {
let disk_size = std::fs::metadata(disk)?.len();
let partitions = match open_gpt(disk, false) {
Ok(gdisk) => map_partitions(&gdisk)?,
Err(_) => Vec::new(),
};
if json {
let info = DiskInfo {
disk: disk.display().to_string(),
size_bytes: disk_size,
partitions,
};
println!("{}", serde_json::to_string_pretty(&info)?);
return Ok(());
}
println!(
"Disk: {} ({} M, {} bytes)",
disk.display(),
format_mib(disk_size),
disk_size
);
if partitions.is_empty() {
println!("No GPT partitions found.");
let fs_type = detect_fs_type(disk)?;
println!(
"Filesystem: {}",
fs_type.as_deref().unwrap_or("unknown")
);
return Ok(());
}
for p in partitions {
println!(
"{:>3} {:<16} start={} M size={} M",
p.index,
p.name,
format_mib(p.start_bytes),
format_mib(p.size_bytes)
);
}
Ok(())
}
fn format_mib(bytes: u64) -> String {
const MIB: u64 = 1024 * 1024;
if bytes.is_multiple_of(MIB) {
format!("{}", bytes / MIB)
} else {
format!("{:.1}", bytes as f64 / MIB as f64)
}
}
fn detect_fs_type(disk: &Path) -> Result<Option<String>> {
let mut file = OpenOptions::new().read(true).open(disk)?;
let mut ext_magic = [0u8; 2];
if file.seek(SeekFrom::Start(1024 + 56)).is_ok()
&& file.read_exact(&mut ext_magic).is_ok()
&& u16::from_le_bytes(ext_magic) == 0xEF53
{
return Ok(Some("ext4".to_string()));
}
let mut boot = [0u8; 512];
let read = file.read(&mut boot)?;
if read >= 512 && boot[510] == 0x55 && boot[511] == 0xAA {
if boot.get(82..87) == Some(b"FAT32") {
return Ok(Some("fat32".to_string()));
}
if boot.get(54..59) == Some(b"FAT16") {
return Ok(Some("fat16".to_string()));
}
if boot.get(54..59) == Some(b"FAT12") {
return Ok(Some("fat12".to_string()));
}
}
Ok(None)
}