storage_isolated_driver_manager/
lib.rs1use anyhow::{Context, Result, anyhow};
6use fidl_fuchsia_io as fio;
7use fidl_fuchsia_storage_block::{BlockMarker, BlockProxy};
8use fidl_fuchsia_storage_partitions as fpartitions;
9use fs_management::filesystem::BlockConnector;
10use fs_management::format::{DiskFormat, detect_disk_format};
11use fuchsia_component::client::{ServiceInstanceStream, connect_to_protocol_at_path};
12use fuchsia_fs::directory::{WatchEvent, Watcher};
13use futures::TryStreamExt;
14use std::path::{Path, PathBuf};
15
16pub type Guid = [u8; 16];
17
18pub fn into_guid(guid: Guid) -> fidl_fuchsia_storage_block::Guid {
19 fidl_fuchsia_storage_block::Guid { value: guid }
20}
21
22pub fn create_random_guid() -> Guid {
23 *uuid::Uuid::new_v4().as_bytes()
24}
25
26async fn partition_type_guid_matches(guid: &Guid, partition: &BlockProxy) -> Result<bool> {
27 let (status, type_guid) =
28 partition.get_type_guid().await.context("Failed to get type guid (fidl error")?;
29 zx::ok(status).context("Failed to get type guid")?;
30 let type_guid = if let Some(guid) = type_guid { guid } else { return Ok(false) };
31 let matched = type_guid.value == *guid;
32 log::info!(matched, type_guid:?, target_guid:?=guid; "matching type guid");
33 Ok(matched)
34}
35
36async fn partition_instance_guid_matches(guid: &Guid, partition: &BlockProxy) -> Result<bool> {
37 let (status, instance_guid) =
38 partition.get_instance_guid().await.context("Failed to get instance guid (fidl error")?;
39 zx::ok(status).context("Failed to get instance guid")?;
40 let instance_guid = if let Some(guid) = instance_guid { guid } else { return Ok(false) };
41 let matched = instance_guid.value == *guid;
42 log::info!(matched, instance_guid:?, target_guid:?=guid; "matching instance guid");
43 Ok(matched)
44}
45
46async fn partition_name_matches(name: &str, partition: &BlockProxy) -> Result<bool> {
47 let (status, partition_name) =
48 partition.get_name().await.context("Failed to get partition name (fidl error")?;
49 zx::ok(status).context("Failed to get partition name")?;
50 let partition_name = if let Some(name) = partition_name { name } else { return Ok(false) };
51 let matched = partition_name == name;
52 log::info!(matched, partition_name = partition_name.as_str(), target_name = name; "matching name");
53 Ok(matched)
54}
55
56async fn block_contents_match(format: DiskFormat, block: &BlockProxy) -> Result<bool> {
57 let content_format = detect_disk_format(block).await;
58 Ok(format == content_format)
59}
60
61#[derive(Debug)]
63pub enum BlockDeviceMatcher<'a> {
64 TypeGuid(&'a Guid),
66
67 InstanceGuid(&'a Guid),
69
70 Name(&'a str),
72
73 ContentsMatch(DiskFormat),
75}
76
77impl BlockDeviceMatcher<'_> {
78 async fn matches(&self, partition: &BlockProxy) -> Result<bool> {
79 match self {
80 Self::TypeGuid(guid) => partition_type_guid_matches(guid, partition).await,
81 Self::InstanceGuid(guid) => partition_instance_guid_matches(guid, partition).await,
82 Self::Name(name) => partition_name_matches(name, partition).await,
83 Self::ContentsMatch(format) => block_contents_match(*format, partition).await,
84 }
85 }
86}
87
88async fn matches_all(partition: &BlockProxy, matchers: &[BlockDeviceMatcher<'_>]) -> bool {
89 for matcher in matchers {
90 if !matcher.matches(partition).await.unwrap_or(false) {
91 return false;
92 }
93 }
94 true
95}
96
97pub async fn wait_for_block_device_devfs(matchers: &[BlockDeviceMatcher<'_>]) -> Result<PathBuf> {
102 const DEV_CLASS_BLOCK: &str = "/dev/class/block";
103 assert!(!matchers.is_empty());
104 let block_dev_dir =
105 fuchsia_fs::directory::open_in_namespace(DEV_CLASS_BLOCK, fio::PERM_READABLE)?;
106 let mut watcher = Watcher::new(&block_dev_dir).await?;
107 while let Some(msg) = watcher.try_next().await? {
108 if msg.event != WatchEvent::ADD_FILE && msg.event != WatchEvent::EXISTING {
109 continue;
110 }
111 if msg.filename.to_str() == Some(".") {
112 continue;
113 }
114 let path = Path::new(DEV_CLASS_BLOCK).join(msg.filename);
115 let partition = connect_to_protocol_at_path::<BlockMarker>(path.to_str().unwrap())?;
116 if matches_all(&partition, matchers).await {
117 return Ok(path);
118 }
119 }
120 Err(anyhow!("Failed to wait for block device"))
121}
122
123pub async fn wait_for_block_device(
128 matchers: &[BlockDeviceMatcher<'_>],
129 mut stream: ServiceInstanceStream<fpartitions::PartitionServiceMarker>,
130) -> Result<fpartitions::PartitionServiceProxy> {
131 while let Some(proxy) = stream.try_next().await? {
132 let partition = proxy.connect_block()?.into_proxy();
133 if matches_all(&partition, matchers).await {
134 return Ok(proxy);
135 }
136 }
137 unreachable!()
138}
139
140pub async fn find_block_device_devfs(matchers: &[BlockDeviceMatcher<'_>]) -> Result<PathBuf> {
144 const DEV_CLASS_BLOCK: &str = "/dev/class/block";
145 assert!(!matchers.is_empty());
146 let block_dev_dir =
147 fuchsia_fs::directory::open_in_namespace(DEV_CLASS_BLOCK, fio::PERM_READABLE)?;
148 let entries = fuchsia_fs::directory::readdir(&block_dev_dir)
149 .await
150 .context("Failed to readdir /dev/class/block")?;
151 for entry in entries {
152 let path = Path::new(DEV_CLASS_BLOCK).join(entry.name);
153 let partition = connect_to_protocol_at_path::<BlockMarker>(path.to_str().unwrap())?;
154 if matches_all(&partition, matchers).await {
155 return Ok(path);
156 }
157 }
158 Err(anyhow!("Failed to find matching block device"))
159}
160
161pub async fn find_block_device<C, Iter>(
164 matchers: &[BlockDeviceMatcher<'_>],
165 partitions: Iter,
166) -> Result<Option<C>>
167where
168 C: BlockConnector,
169 Iter: Iterator<Item = C>,
170{
171 for connector in partitions {
172 let partition = connector.connect_block()?.into_proxy();
173 if matches_all(&partition, matchers).await {
174 return Ok(Some(connector));
175 }
176 }
177 Ok(None)
178}