use std::{
collections::BTreeMap,
fs, io,
path::{Path, PathBuf},
};
use tracing::{debug, error, info};
pub type Map = BTreeMap<PathBuf, Record>;
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct Record {
pub name: Option<PathBuf>,
pub diskseq: Option<PathBuf>,
pub ids: Vec<PathBuf>,
pub label: Option<PathBuf>,
pub partlabel: Option<PathBuf>,
pub partuuid: Option<PathBuf>,
pub path: Option<PathBuf>,
pub uuid: Option<PathBuf>,
}
pub fn collect() -> io::Result<Map> {
let mut out = BTreeMap::new();
by_id(&mut out)?;
by_diskseq(&mut out)?;
by_label(&mut out)?;
by_partlabel(&mut out)?;
by_partuuid(&mut out)?;
by_path(&mut out)?;
by_uuid(&mut out)?;
Ok(out)
}
fn by_uuid(data: &mut Map) -> io::Result<()> {
for (uuid, name) in lookup("/dev/disk/by-uuid")? {
if let Some(rec) = data.get_mut(&name) {
rec.uuid = Some(uuid);
continue;
}
if let Some(rec) = data.insert(
name.clone(),
Record {
name: Some(name),
uuid: Some(uuid),
..Default::default()
},
) {
error!(?rec, "double insert");
}
}
Ok(())
}
fn by_path(data: &mut Map) -> io::Result<()> {
for (path, name) in lookup("/dev/disk/by-path")? {
if let Some(rec) = data.get_mut(&name) {
rec.path = Some(path);
continue;
}
if let Some(rec) = data.insert(
name.clone(),
Record {
name: Some(name),
path: Some(path),
..Default::default()
},
) {
error!(?rec, "double insert");
}
}
Ok(())
}
fn by_partuuid(data: &mut Map) -> io::Result<()> {
for (uuid, name) in lookup("/dev/disk/by-partuuid")? {
if let Some(rec) = data.get_mut(&name) {
rec.partuuid = Some(uuid);
continue;
}
if let Some(rec) = data.insert(
name.clone(),
Record {
name: Some(name),
partuuid: Some(uuid),
..Default::default()
},
) {
error!(?rec, "double insert");
}
}
Ok(())
}
fn by_partlabel(data: &mut Map) -> io::Result<()> {
for (label, name) in lookup("/dev/disk/by-partlabel")? {
if let Some(rec) = data.get_mut(&name) {
rec.partlabel = Some(label);
continue;
}
if let Some(rec) = data.insert(
name.clone(),
Record {
name: Some(name),
partlabel: Some(label),
..Default::default()
},
) {
error!(?rec, "double insert");
}
}
Ok(())
}
pub fn by_id(data: &mut Map) -> io::Result<()> {
for (id, name) in lookup("/dev/disk/by-id")? {
if let Some(rec) = data.get_mut(&name) {
rec.ids.push(id);
continue;
}
if let Some(rec) = data.insert(
name.clone(),
Record {
name: Some(name),
ids: vec![id],
..Default::default()
},
) {
error!(?rec, "double insert");
}
}
Ok(())
}
pub fn by_diskseq(data: &mut Map) -> io::Result<()> {
for (seq, name) in lookup("/dev/disk/by-diskseq")? {
if let Some(rec) = data.get_mut(&name) {
rec.diskseq = Some(seq);
continue;
}
if let Some(rec) = data.insert(
name.clone(),
Record {
name: Some(name),
diskseq: Some(seq),
..Default::default()
},
) {
error!(?rec, "double insert");
}
}
Ok(())
}
pub fn by_label(data: &mut Map) -> io::Result<()> {
for (label, name) in lookup("/dev/disk/by-label")? {
if let Some(rec) = data.get_mut(&name) {
rec.label = Some(label);
continue;
}
if let Some(rec) = data.insert(
name.clone(),
Record {
name: Some(name),
label: Some(label),
..Default::default()
},
) {
error!(?rec, "double insert");
}
}
Ok(())
}
fn lookup<T: AsRef<Path>>(root: T) -> io::Result<Vec<(PathBuf, PathBuf)>> {
let mut out = vec![];
let root = root.as_ref();
debug!(?root, "reading directory");
for entry in fs::read_dir(root)? {
let entry = entry?;
debug!(?entry);
let path = entry.path();
debug!(?path);
if !entry.file_type()?.is_symlink() {
info!(
?path,
"found entry that wasn't a symlink; is this expected?"
);
continue;
}
let link = fs::read_link(&path)?;
debug!(?link, "resolved link");
let link = root.join(link).canonicalize()?;
debug!(?link, "canonicalized link");
debug!(src = ?path, dest = ?link, "inserting src/dest pair into map");
out.push((path, link));
}
Ok(out)
}