driver_manager_driver_host/
runtime_dir.rs1use async_lock::OnceCell;
6use fidl_fuchsia_driver_host as fdh;
7use fidl_fuchsia_io as fio;
8use std::sync::Arc;
9use vfs::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
10use vfs::directory::simple::Simple;
11use vfs::execution_scope::ExecutionScope;
12use vfs::file::{FidlIoConnection, File, FileIo, FileLike, FileOptions, SyncMode, read_only};
13use vfs::node::Node;
14use vfs::{ObjectRequestRef, immutable_attributes, pseudo_directory};
15use zx::Status;
16
17#[derive(Clone)]
18pub struct ProcessInfo {
19 pub job_koid: zx::Koid,
20 pub process_koid: zx::Koid,
21 pub main_thread_koid: zx::Koid,
22 pub threads: Vec<fdh::ThreadInfo>,
23 pub dispatchers: Vec<fdh::DispatcherInfo>,
24}
25
26pub(crate) struct CachedProcessInfo {
27 cell: OnceCell<ProcessInfo>,
28 driver_host: fdh::DriverHostProxy,
29}
30
31impl CachedProcessInfo {
32 pub(crate) fn new(driver_host: fdh::DriverHostProxy) -> Self {
33 Self { cell: OnceCell::new(), driver_host }
34 }
35
36 pub(crate) async fn get(&self) -> Result<&ProcessInfo, zx::Status> {
37 self.cell
38 .get_or_try_init(|| async {
39 match self.driver_host.get_process_info().await {
40 Ok(Ok((job_koid, process_koid, main_thread_koid, threads, dispatchers))) => {
41 Ok(ProcessInfo {
42 job_koid: zx::Koid::from_raw(job_koid),
43 process_koid: zx::Koid::from_raw(process_koid),
44 main_thread_koid: zx::Koid::from_raw(main_thread_koid),
45 threads,
46 dispatchers,
47 })
48 }
49 Ok(Err(e)) => Err(zx::Status::from_raw(e)),
50 Err(e) => {
51 log::error!("FIDL error GetProcessInfo: {:?}", e);
52 Err(zx::Status::INTERNAL)
53 }
54 }
55 })
56 .await
57 }
58}
59
60struct ElfFile {
62 process_info: Arc<CachedProcessInfo>,
63 info_extractor: fn(&ProcessInfo) -> String,
64}
65
66impl DirectoryEntry for ElfFile {
67 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
68 request.open_file(self)
69 }
70}
71
72impl GetEntryInfo for ElfFile {
73 fn entry_info(&self) -> EntryInfo {
74 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)
75 }
76}
77
78impl Node for ElfFile {
79 async fn get_attributes(
80 &self,
81 requested_attributes: fio::NodeAttributesQuery,
82 ) -> Result<fio::NodeAttributes2, Status> {
83 let info = self.process_info.get().await?;
84 let content = (self.info_extractor)(info);
85 let content_size = content.len() as u64;
86 Ok(immutable_attributes!(
87 requested_attributes,
88 Immutable {
89 protocols: fio::NodeProtocolKinds::FILE,
90 abilities: fio::Operations::GET_ATTRIBUTES | fio::Operations::READ_BYTES,
91 content_size: content_size,
92 storage_size: content_size,
93 }
94 ))
95 }
96}
97
98impl FileIo for ElfFile {
99 async fn read_at(&self, offset: u64, buffer: &mut [u8]) -> Result<u64, Status> {
100 let info = self.process_info.get().await?;
101 let content = (self.info_extractor)(info);
102 let bytes = content.as_bytes();
103 let content_size = bytes.len() as u64;
104
105 if offset >= content_size {
106 return Ok(0u64);
107 }
108
109 let start = offset as usize;
110 let read_len = std::cmp::min(bytes.len() - start, buffer.len());
111 buffer[..read_len].copy_from_slice(&bytes[start..][..read_len]);
112 Ok(read_len as u64)
113 }
114
115 async fn write_at(&self, _offset: u64, _content: &[u8]) -> Result<u64, Status> {
116 Err(Status::NOT_SUPPORTED)
117 }
118
119 async fn append(&self, _content: &[u8]) -> Result<(u64, u64), Status> {
120 Err(Status::NOT_SUPPORTED)
121 }
122}
123
124impl File for ElfFile {
125 fn readable(&self) -> bool {
126 true
127 }
128
129 fn writable(&self) -> bool {
130 false
131 }
132
133 fn executable(&self) -> bool {
134 false
135 }
136
137 async fn open_file(&self, _options: &FileOptions) -> Result<(), Status> {
138 Ok(())
139 }
140
141 async fn truncate(&self, _length: u64) -> Result<(), Status> {
142 Err(Status::NOT_SUPPORTED)
143 }
144
145 async fn get_size(&self) -> Result<u64, Status> {
146 let info = self.process_info.get().await?;
147 let content = (self.info_extractor)(info);
148 Ok(content.len() as u64)
149 }
150
151 async fn update_attributes(
152 &self,
153 _attributes: fio::MutableNodeAttributes,
154 ) -> Result<(), Status> {
155 Err(Status::NOT_SUPPORTED)
156 }
157
158 async fn sync(&self, _mode: SyncMode) -> Result<(), Status> {
159 Ok(())
160 }
161}
162
163impl FileLike for ElfFile {
164 fn open(
165 self: Arc<Self>,
166 scope: ExecutionScope,
167 options: FileOptions,
168 object_request: ObjectRequestRef<'_>,
169 ) -> Result<(), Status> {
170 FidlIoConnection::create_sync(scope, self, options, object_request.take());
171 Ok(())
172 }
173}
174
175pub(crate) fn create_runtime_dir(process_info: Arc<CachedProcessInfo>) -> Arc<Simple> {
179 let now = zx::MonotonicInstant::get().into_nanos().to_string();
180 let process_start_time = read_only(now.into_bytes());
181
182 let job_id = Arc::new(ElfFile {
183 process_info: process_info.clone(),
184 info_extractor: |info| info.job_koid.raw_koid().to_string(),
185 });
186
187 let process_id = Arc::new(ElfFile {
188 process_info,
189 info_extractor: |info| info.process_koid.raw_koid().to_string(),
190 });
191
192 pseudo_directory! {
193 "elf" => pseudo_directory! {
194 "process_start_time" => process_start_time,
195 "job_id" => job_id,
196 "process_id" => process_id,
197 }
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204 use fidl::endpoints::{create_proxy, create_proxy_and_stream};
205 use futures::stream::StreamExt;
206
207 #[fuchsia::test]
208 async fn test_runtime_dir_creation() {
209 let (proxy, mut stream) = create_proxy_and_stream::<fdh::DriverHostMarker>();
210 let process_info = Arc::new(CachedProcessInfo::new(proxy));
211
212 fuchsia_async::Task::spawn(async move {
214 if let Some(Ok(fdh::DriverHostRequest::GetProcessInfo { responder })) =
215 stream.next().await
216 {
217 responder.send(Ok((123, 456, 789, &[], &[]))).unwrap();
218 }
219 })
220 .detach();
221
222 let dir = create_runtime_dir(process_info);
223
224 let scope = ExecutionScope::new();
225 let (root_proxy, root_server) = create_proxy::<fio::DirectoryMarker>();
226 vfs::directory::serve_on(dir, fio::PERM_READABLE, scope, root_server);
227
228 let entries = fuchsia_fs::directory::readdir(&root_proxy).await.unwrap();
229 assert!(entries.iter().any(|e| e.name == "elf"));
231
232 let elf_proxy =
233 fuchsia_fs::directory::open_directory(&root_proxy, "elf", fio::PERM_READABLE)
234 .await
235 .unwrap();
236 let elf_entries = fuchsia_fs::directory::readdir(&elf_proxy).await.unwrap();
237 assert!(elf_entries.iter().any(|e| e.name == "process_start_time"));
238 assert!(elf_entries.iter().any(|e| e.name == "job_id"));
239 assert!(elf_entries.iter().any(|e| e.name == "process_id"));
240
241 let job_id_file =
242 fuchsia_fs::directory::open_file(&elf_proxy, "job_id", fio::Flags::PERM_READ_BYTES)
243 .await
244 .unwrap();
245 let job_id = fuchsia_fs::file::read_to_string(&job_id_file).await.unwrap();
246 assert_eq!(job_id, "123");
247
248 let process_id_file =
249 fuchsia_fs::directory::open_file(&elf_proxy, "process_id", fio::Flags::PERM_READ_BYTES)
250 .await
251 .unwrap();
252 let process_id = fuchsia_fs::file::read_to_string(&process_id_file).await.unwrap();
253 assert_eq!(process_id, "456");
254 }
255}