use anyhow::{anyhow, Context as _, Error};
use fidl_fuchsia_device::ControllerMarker;
use fidl_fuchsia_hardware_block::BlockMarker;
#[derive(Debug, PartialEq, Clone)]
pub struct BlockDevice {
pub topo_path: String,
pub class_path: String,
pub size: u64,
}
impl BlockDevice {
pub fn is_disk(&self) -> bool {
!self.topo_path.contains("/block/part-")
}
}
pub async fn get_block_device(class_path: &str) -> Result<Option<BlockDevice>, Error> {
let controller = fuchsia_component::client::connect_to_protocol_at_path::<ControllerMarker>(
format!("{class_path}/device_controller").as_str(),
)?;
let topo_path = controller
.get_topological_path()
.await
.context("FIDL: get_topological_path()")?
.map_err(zx::Status::from_raw)
.context("response: get_topological_path()")?;
if topo_path.contains("/ramdisk-") {
Ok(None)
} else {
let block =
fuchsia_component::client::connect_to_protocol_at_path::<BlockMarker>(class_path)?;
let info = block
.get_info()
.await
.context("FIDL: get_info()")?
.map_err(zx::Status::from_raw)
.context("response: get_info()")?;
let block_count = info.block_count;
let block_size = info.block_size;
let size = block_count.checked_mul(block_size.into()).ok_or_else(|| {
anyhow!("device size overflow: block_count={} block_size={}", block_count, block_size)
})?;
let class_path = class_path.to_owned();
Ok(Some(BlockDevice { topo_path, class_path, size }))
}
}
pub async fn get_block_devices() -> Result<Vec<BlockDevice>, Error> {
const BLOCK_DIR: &str = "/dev/class/block";
let entries = std::fs::read_dir(BLOCK_DIR)?;
let futures = entries.map(|entry| async {
let entry = entry?;
let path = entry.path();
let class_path =
path.to_str().ok_or_else(|| anyhow!("path contains non-UTF8: {}", path.display()))?;
get_block_device(class_path).await
});
let options = futures::future::try_join_all(futures).await?;
let devices = options.into_iter().filter_map(std::convert::identity).collect();
Ok(devices)
}