1use anyhow::{Context, Result};
6use fidl_fuchsia_storage_block::BlockProxy;
7use fidl_fuchsia_storage_partitions as fpartitions;
8use fs_management::filesystem::BlockConnector;
9use fs_management::format::{DiskFormat, detect_disk_format};
10use fuchsia_component::client::ServiceInstanceStream;
11use futures::TryStreamExt;
12
13pub type Guid = [u8; 16];
14
15pub fn into_guid(guid: Guid) -> fidl_fuchsia_storage_block::Guid {
16 fidl_fuchsia_storage_block::Guid { value: guid }
17}
18
19pub fn create_random_guid() -> Guid {
20 *uuid::Uuid::new_v4().as_bytes()
21}
22
23async fn partition_type_guid_matches(guid: &Guid, partition: &BlockProxy) -> Result<bool> {
24 let (status, type_guid) =
25 partition.get_type_guid().await.context("Failed to get type guid (fidl error")?;
26 zx::ok(status).context("Failed to get type guid")?;
27 let type_guid = if let Some(guid) = type_guid { guid } else { return Ok(false) };
28 let matched = type_guid.value == *guid;
29 log::info!(matched, type_guid:?, target_guid:?=guid; "matching type guid");
30 Ok(matched)
31}
32
33async fn partition_instance_guid_matches(guid: &Guid, partition: &BlockProxy) -> Result<bool> {
34 let (status, instance_guid) =
35 partition.get_instance_guid().await.context("Failed to get instance guid (fidl error")?;
36 zx::ok(status).context("Failed to get instance guid")?;
37 let instance_guid = if let Some(guid) = instance_guid { guid } else { return Ok(false) };
38 let matched = instance_guid.value == *guid;
39 log::info!(matched, instance_guid:?, target_guid:?=guid; "matching instance guid");
40 Ok(matched)
41}
42
43async fn partition_name_matches(name: &str, partition: &BlockProxy) -> Result<bool> {
44 let (status, partition_name) =
45 partition.get_name().await.context("Failed to get partition name (fidl error")?;
46 zx::ok(status).context("Failed to get partition name")?;
47 let partition_name = if let Some(name) = partition_name { name } else { return Ok(false) };
48 let matched = partition_name == name;
49 log::info!(matched, partition_name = partition_name.as_str(), target_name = name; "matching name");
50 Ok(matched)
51}
52
53async fn block_contents_match(format: DiskFormat, block: &BlockProxy) -> Result<bool> {
54 let content_format = detect_disk_format(block).await;
55 Ok(format == content_format)
56}
57
58#[derive(Debug)]
60pub enum BlockDeviceMatcher<'a> {
61 TypeGuid(&'a Guid),
63
64 InstanceGuid(&'a Guid),
66
67 Name(&'a str),
69
70 ContentsMatch(DiskFormat),
72}
73
74impl BlockDeviceMatcher<'_> {
75 async fn matches(&self, partition: &BlockProxy) -> Result<bool> {
76 match self {
77 Self::TypeGuid(guid) => partition_type_guid_matches(guid, partition).await,
78 Self::InstanceGuid(guid) => partition_instance_guid_matches(guid, partition).await,
79 Self::Name(name) => partition_name_matches(name, partition).await,
80 Self::ContentsMatch(format) => block_contents_match(*format, partition).await,
81 }
82 }
83}
84
85async fn matches_all(partition: &BlockProxy, matchers: &[BlockDeviceMatcher<'_>]) -> bool {
86 for matcher in matchers {
87 if !matcher.matches(partition).await.unwrap_or(false) {
88 return false;
89 }
90 }
91 true
92}
93
94pub async fn wait_for_block_device(
97 matchers: &[BlockDeviceMatcher<'_>],
98 mut stream: ServiceInstanceStream<fpartitions::PartitionServiceMarker>,
99) -> Result<fpartitions::PartitionServiceProxy> {
100 while let Some(proxy) = stream.try_next().await? {
101 let partition = proxy.connect_block()?.into_proxy();
102 if matches_all(&partition, matchers).await {
103 return Ok(proxy);
104 }
105 }
106 unreachable!()
107}
108
109pub async fn find_block_device<C, Iter>(
112 matchers: &[BlockDeviceMatcher<'_>],
113 partitions: Iter,
114) -> Result<Option<C>>
115where
116 C: BlockConnector,
117 Iter: Iterator<Item = C>,
118{
119 for connector in partitions {
120 let partition = connector.connect_block()?.into_proxy();
121 if matches_all(&partition, matchers).await {
122 return Ok(Some(connector));
123 }
124 }
125 Ok(None)
126}
127
128pub const DEFAULT_BENCHMARK_FVM_SIZE_BYTES: u64 = 160 * 1024 * 1024;
129
130pub async fn find_or_create_test_partition(
132 service: fuchsia_component::client::Service<fpartitions::PartitionServiceMarker>,
133 manager: fpartitions::PartitionsManagerProxy,
134 partition_size_bytes: u64,
135) -> Result<fpartitions::PartitionServiceProxy> {
136 if let Some(connector) = find_block_device(
137 &[
138 BlockDeviceMatcher::Name(fs_management::format::constants::BENCHMARK_FVM_VOLUME_NAME),
139 BlockDeviceMatcher::TypeGuid(
140 &fs_management::format::constants::BENCHMARK_FVM_TYPE_GUID,
141 ),
142 ],
143 service.clone().enumerate().await.context("Failed to enumerate partitions")?.into_iter(),
144 )
145 .await
146 .context("Error while searching for benchmark-fvm")?
147 {
148 return Ok(connector);
149 }
150
151 if let Some(connector) = find_block_device(
152 &[BlockDeviceMatcher::Name(fs_management::format::constants::PAD_RW_PARTITION_LABEL)],
153 service.clone().enumerate().await.context("Failed to enumerate partitions")?.into_iter(),
154 )
155 .await
156 .context("Error while searching for pad_rw")?
157 {
158 return Ok(connector);
159 }
160
161 let info = manager
163 .get_block_info()
164 .await
165 .context("FIDL error on get_block_info")?
166 .map_err(zx::Status::err_from_raw)
167 .context("get_block_info failed")?;
168 let transaction = manager
169 .create_transaction()
170 .await
171 .context("FIDL error on create_transaction")?
172 .map_err(zx::Status::err_from_raw)
173 .context("create_transaction failed")?;
174 let request = fpartitions::PartitionsManagerAddPartitionRequest {
175 transaction: Some(transaction.duplicate_handle(zx::Rights::SAME_RIGHTS)?),
176 name: Some(fs_management::format::constants::BENCHMARK_FVM_VOLUME_NAME.to_string()),
177 type_guid: Some(fidl_fuchsia_storage_block::Guid {
178 value: fs_management::format::constants::BENCHMARK_FVM_TYPE_GUID,
179 }),
180 num_blocks: Some(partition_size_bytes / info.1 as u64),
181 ..Default::default()
182 };
183 manager
184 .add_partition(request)
185 .await
186 .context("FIDL error on add_partition")?
187 .map_err(zx::Status::err_from_raw)
188 .context("add_partition failed")?;
189 manager
190 .commit_transaction(transaction)
191 .await
192 .context("FIDL error on commit_transaction")?
193 .map_err(zx::Status::err_from_raw)
194 .context("commit_transaction failed")?;
195
196 let service_instances = service.enumerate().await.context("Failed to enumerate partitions")?;
197 find_block_device(
198 &[
199 BlockDeviceMatcher::Name(fs_management::format::constants::BENCHMARK_FVM_VOLUME_NAME),
200 BlockDeviceMatcher::TypeGuid(
201 &fs_management::format::constants::BENCHMARK_FVM_TYPE_GUID,
202 ),
203 ],
204 service_instances.into_iter(),
205 )
206 .await?
207 .ok_or_else(|| anyhow::anyhow!("Failed to find newly created test partition"))
208}