1#![deny(missing_docs)]
8use anyhow::Error;
9use fidl::endpoints::DiscoverableProtocolMarker as _;
10use fidl_fuchsia_hardware_ramdisk as framdisk;
11use fidl_fuchsia_hardware_ramdisk::Guid;
12use fidl_fuchsia_io as fio;
13use fidl_fuchsia_storage_block as fblock;
14use fs_management::filesystem::{BlockConnector, DirBasedBlockConnector};
15use fuchsia_component_client::Service;
16
17const GUID_LEN: usize = 16;
18
19pub struct RamdiskClientBuilder {
21 ramdisk_source: RamdiskSource,
22 block_size: u64,
23 max_transfer_blocks: Option<u32>,
24 guid: Option<[u8; GUID_LEN]>,
25 ramdisk_service: Option<fio::DirectoryProxy>,
26 device_flags: Option<fblock::DeviceFlag>,
27
28 publish: bool,
30}
31
32enum RamdiskSource {
33 Vmo { vmo: zx::Vmo },
34 Size { block_count: u64 },
35}
36
37impl RamdiskClientBuilder {
38 pub fn new(block_size: u64, block_count: u64) -> Self {
40 Self {
41 ramdisk_source: RamdiskSource::Size { block_count },
42 block_size,
43 max_transfer_blocks: None,
44 guid: None,
45 ramdisk_service: None,
46 publish: false,
47 device_flags: None,
48 }
49 }
50
51 pub fn new_with_vmo(vmo: zx::Vmo, block_size: Option<u64>) -> Self {
53 Self {
54 ramdisk_source: RamdiskSource::Vmo { vmo },
55 block_size: block_size.unwrap_or(0),
56 max_transfer_blocks: None,
57 guid: None,
58 ramdisk_service: None,
59 publish: false,
60 device_flags: None,
61 }
62 }
63
64 pub fn guid(mut self, guid: [u8; GUID_LEN]) -> Self {
66 self.guid = Some(guid);
67 self
68 }
69
70 pub fn max_transfer_blocks(mut self, value: u32) -> Self {
72 self.max_transfer_blocks = Some(value);
73 self
74 }
75
76 pub fn ramdisk_service(mut self, service: fio::DirectoryProxy) -> Self {
78 self.ramdisk_service = Some(service);
79 self
80 }
81
82 pub fn publish(mut self) -> Self {
84 self.publish = true;
85 self
86 }
87
88 pub fn device_flags(mut self, device_flags: fblock::DeviceFlag) -> Self {
90 self.device_flags = Some(device_flags);
91 self
92 }
93
94 pub async fn build(self) -> Result<RamdiskClient, Error> {
96 let Self {
97 ramdisk_source,
98 block_size,
99 max_transfer_blocks,
100 guid,
101 ramdisk_service,
102 publish,
103 device_flags,
104 } = self;
105
106 let service = match ramdisk_service {
108 Some(s) => {
109 Service::from_service_dir_proxy(s, fidl_fuchsia_hardware_ramdisk::ServiceMarker)
110 }
111 None => Service::open(fidl_fuchsia_hardware_ramdisk::ServiceMarker)?,
112 };
113 let ramdisk_controller = service.watch_for_any().await?.connect_to_controller()?;
114
115 let type_guid = guid.map(|guid| Guid { value: guid });
116
117 let options = match ramdisk_source {
118 RamdiskSource::Vmo { vmo } => framdisk::Options {
119 vmo: Some(vmo),
120 block_size: if block_size == 0 {
121 None
122 } else {
123 Some(block_size.try_into().unwrap())
124 },
125 type_guid,
126 publish: Some(publish),
127 max_transfer_blocks,
128 device_flags,
129 ..Default::default()
130 },
131 RamdiskSource::Size { block_count } => framdisk::Options {
132 block_count: Some(block_count),
133 block_size: Some(block_size.try_into().unwrap()),
134 type_guid,
135 publish: Some(publish),
136 max_transfer_blocks,
137 device_flags,
138 ..Default::default()
139 },
140 };
141
142 let (outgoing, event) =
143 ramdisk_controller.create(options).await?.map_err(|s| zx::Status::from_raw(s))?;
144
145 let outgoing = outgoing.into_proxy();
146
147 RamdiskClient::new(outgoing, event)
148 }
149}
150
151pub struct RamdiskClient {
155 outgoing: fio::DirectoryProxy,
157
158 _event: zx::EventPair,
160}
161
162impl RamdiskClient {
163 fn new(outgoing: fio::DirectoryProxy, event: zx::EventPair) -> Result<Self, Error> {
164 Ok(Self { outgoing, _event: event })
165 }
166
167 pub fn builder(block_size: u64, block_count: u64) -> RamdiskClientBuilder {
169 RamdiskClientBuilder::new(block_size, block_count)
170 }
171
172 pub async fn create(block_size: u64, block_count: u64) -> Result<Self, Error> {
174 Self::builder(block_size, block_count).build().await
175 }
176
177 pub fn outgoing(&self) -> &fio::DirectoryProxy {
179 &self.outgoing
180 }
181
182 pub fn open(&self) -> Result<fidl::endpoints::ClientEnd<fblock::BlockMarker>, Error> {
184 let (client, server_end) = fidl::endpoints::create_endpoints();
185 self.connect(server_end)?;
186 Ok(client)
187 }
188
189 pub fn connector(&self) -> Result<Box<dyn BlockConnector>, Error> {
191 let block_dir = fuchsia_fs::directory::clone(&self.outgoing)?;
192 Ok(Box::new(DirBasedBlockConnector::new(
193 block_dir,
194 format!("svc/{}", fblock::BlockMarker::PROTOCOL_NAME),
195 )))
196 }
197
198 pub fn connect(
200 &self,
201 server_end: fidl::endpoints::ServerEnd<fblock::BlockMarker>,
202 ) -> Result<(), Error> {
203 Ok(self.outgoing.open(
204 &format!("svc/{}", fblock::BlockMarker::PROTOCOL_NAME),
205 fio::Flags::empty(),
206 &fio::Options::default(),
207 server_end.into_channel(),
208 )?)
209 }
210
211 pub fn open_ramdisk(&self) -> Result<framdisk::RamdiskProxy, Error> {
213 let (client, server) = fidl::endpoints::create_proxy::<framdisk::RamdiskMarker>();
214 self.outgoing.open(
215 &format!("svc/{}", framdisk::RamdiskMarker::PROTOCOL_NAME),
216 fio::Flags::empty(),
217 &fio::Options::default(),
218 server.into_channel(),
219 )?;
220 Ok(client)
221 }
222
223 pub fn into_event(self) -> zx::EventPair {
225 self._event
226 }
227}
228
229impl BlockConnector for RamdiskClient {
230 fn connect_channel_to_block(
231 &self,
232 server_end: fidl::endpoints::ServerEnd<fidl_fuchsia_storage_block::BlockMarker>,
233 ) -> Result<(), Error> {
234 self.connect(server_end.into_channel().into())
235 }
236}
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241
242 const TEST_GUID: [u8; GUID_LEN] = [
245 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
246 0x10,
247 ];
248
249 #[fuchsia::test]
250 async fn create_get_dir_proxy_destroy() {
251 let ramdisk =
253 RamdiskClient::builder(512, 2048).build().await.expect("failed to create ramdisk");
254 let ramdisk_dir = &ramdisk.outgoing;
255 fuchsia_fs::directory::readdir(ramdisk_dir).await.expect("failed to readdir");
256 }
257
258 #[fuchsia::test]
259 async fn create_with_guid_get_dir_proxy_destroy() {
260 let ramdisk = RamdiskClient::builder(512, 2048)
261 .guid(TEST_GUID)
262 .build()
263 .await
264 .expect("failed to create ramdisk");
265 let ramdisk_dir = &ramdisk.outgoing;
266 fuchsia_fs::directory::readdir(ramdisk_dir).await.expect("failed to readdir");
267 }
268
269 #[fuchsia::test]
270 async fn create_open_destroy() {
271 let ramdisk = RamdiskClient::create(512, 2048).await.unwrap();
272 let client = ramdisk.open().unwrap().into_proxy();
273 client.get_info().await.expect("get_info failed").unwrap();
274 }
276
277 #[fuchsia::test]
278 async fn create_open_into_event() {
279 let ramdisk = RamdiskClient::create(512, 2048).await.unwrap();
280 let client = ramdisk.open().unwrap().into_proxy();
281 client.get_info().await.expect("get_info failed").unwrap();
282 let _event = ramdisk.into_event();
283 client.get_info().await.expect("get_info failed").unwrap();
285 }
286}