1use anyhow::Error;
12use block_client::{BlockClient as _, BufferSlice, MutableBufferSlice, RemoteBlockClient};
13use fidl::endpoints::create_endpoints;
14use std::sync::Arc;
15use vfs::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
16use vfs::execution_scope::ExecutionScope;
17use vfs::file::{FidlIoConnection, File, FileIo, FileLike, FileOptions, SyncMode};
18use vfs::node::Node;
19use vfs::{ObjectRequestRef, immutable_attributes, pseudo_directory};
20use {fidl_fuchsia_io as fio, fidl_fuchsia_storage_block as fblock, fuchsia_async as fasync};
21
22struct BlockFile {
23 block_client: RemoteBlockClient,
24}
25
26impl DirectoryEntry for BlockFile {
27 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), zx::Status> {
28 request.open_file(self)
29 }
30}
31
32impl GetEntryInfo for BlockFile {
33 fn entry_info(&self) -> EntryInfo {
34 EntryInfo::new(0, fio::DirentType::File)
35 }
36}
37
38impl Node for BlockFile {
39 async fn get_attributes(
40 &self,
41 requested_attributes: fio::NodeAttributesQuery,
42 ) -> Result<fio::NodeAttributes2, zx::Status> {
43 let block_size = self.block_client.block_size();
44 let block_count = self.block_client.block_count();
45 let device_size = block_count.checked_mul(block_size.into()).unwrap();
46 Ok(immutable_attributes!(
47 requested_attributes,
48 Immutable {
49 protocols: fio::NodeProtocolKinds::FILE,
50 abilities: fio::Operations::GET_ATTRIBUTES
51 | fio::Operations::UPDATE_ATTRIBUTES
52 | fio::Operations::READ_BYTES
53 | fio::Operations::WRITE_BYTES,
54 content_size: device_size,
55 storage_size: device_size,
56 }
57 ))
58 }
59}
60
61impl File for BlockFile {
62 fn writable(&self) -> bool {
63 true
64 }
65
66 async fn open_file(&self, _options: &FileOptions) -> Result<(), zx::Status> {
67 Ok(())
68 }
69
70 async fn truncate(&self, _length: u64) -> Result<(), zx::Status> {
71 Err(zx::Status::NOT_SUPPORTED)
72 }
73
74 async fn get_size(&self) -> Result<u64, zx::Status> {
75 let block_size = self.block_client.block_size();
76 let block_count = self.block_client.block_count();
77 Ok(block_count.checked_mul(block_size.into()).unwrap())
78 }
79
80 async fn update_attributes(
81 &self,
82 _attributes: fio::MutableNodeAttributes,
83 ) -> Result<(), zx::Status> {
84 Err(zx::Status::NOT_SUPPORTED)
85 }
86
87 async fn sync(&self, _mode: SyncMode) -> Result<(), zx::Status> {
88 self.block_client.flush().await
89 }
90}
91
92impl FileIo for BlockFile {
93 async fn read_at(&self, offset: u64, buffer: &mut [u8]) -> Result<u64, zx::Status> {
94 let () = self.block_client.read_at(MutableBufferSlice::Memory(buffer), offset).await?;
95 Ok(buffer.len().try_into().unwrap())
96 }
97
98 async fn write_at(&self, offset: u64, content: &[u8]) -> Result<u64, zx::Status> {
99 let () = self.block_client.write_at(BufferSlice::Memory(content), offset).await?;
100 Ok(content.len().try_into().unwrap())
101 }
102
103 async fn append(&self, _content: &[u8]) -> Result<(u64, u64), zx::Status> {
104 Err(zx::Status::NOT_SUPPORTED)
105 }
106}
107
108impl FileLike for BlockFile {
109 fn open(
110 self: Arc<Self>,
111 scope: ExecutionScope,
112 options: FileOptions,
113 object_request: ObjectRequestRef<'_>,
114 ) -> Result<(), zx::Status> {
115 FidlIoConnection::create_sync(scope, self, options, object_request.take());
116 Ok(())
117 }
118}
119
120pub async fn run(
125 block_proxy: fblock::BlockProxy,
126 binary: &str,
127 args: impl Iterator<Item = String>,
128) -> Result<i64, Error> {
129 let (client, server) = create_endpoints::<fio::DirectoryMarker>();
130
131 let server_fut = {
132 let block_client = RemoteBlockClient::new(block_proxy).await?;
133 let dir = pseudo_directory! {
134 "block" => Arc::new(BlockFile {
135 block_client,
136 }),
137 };
138
139 let scope = ExecutionScope::new();
140 vfs::directory::serve_on(
141 dir,
142 fio::PERM_READABLE | fio::PERM_WRITABLE,
143 scope.clone(),
144 server,
145 );
146 async move { scope.wait().await }
147 };
148
149 let client_fut = {
150 let mut builder = fdio::SpawnBuilder::new()
151 .options(fdio::SpawnOptions::CLONE_ALL)
152 .add_directory_to_namespace("/device", client)?
153 .arg(binary)?;
154 for arg in args {
155 builder = builder.arg(arg)?;
156 }
157 builder = builder.arg("/device/block")?;
158 let process = builder.spawn_from_path(binary, &fuchsia_runtime::job_default())?;
159
160 async move {
161 let _: zx::Signals =
162 fasync::OnSignals::new(&process, zx::Signals::PROCESS_TERMINATED).await?;
163 let info = process.info()?;
164 Ok::<_, Error>(info.return_code)
165 }
166 };
167
168 futures::future::join(server_fut, client_fut).await.1
169}