fuchsia_component_directory/
lib.rs1#![deny(missing_docs)]
11
12use anyhow::{Context, Error};
13use fidl::endpoints::ClientEnd;
14use fidl_fuchsia_io as fio;
15use std::sync::Arc;
16
17pub trait Directory {
19 fn open(&self, path: &str, flags: fio::Flags, server_end: zx::Channel) -> Result<(), Error>;
21}
22
23impl Directory for fio::DirectoryProxy {
24 fn open(&self, path: &str, flags: fio::Flags, server_end: zx::Channel) -> Result<(), Error> {
25 #[cfg(fuchsia_api_level_at_least = "27")]
26 let () = self.open(path, flags, &fio::Options::default(), server_end.into())?;
27 #[cfg(not(fuchsia_api_level_at_least = "27"))]
28 let () = self.open3(path, flags, &fio::Options::default(), server_end.into())?;
29 Ok(())
30 }
31}
32
33impl Directory for fio::DirectorySynchronousProxy {
34 fn open(&self, path: &str, flags: fio::Flags, server_end: zx::Channel) -> Result<(), Error> {
35 #[cfg(fuchsia_api_level_at_least = "27")]
36 let () = self.open(path, flags, &fio::Options::default(), server_end.into())?;
37 #[cfg(not(fuchsia_api_level_at_least = "27"))]
38 let () = self.open3(path, flags, &fio::Options::default(), server_end.into())?;
39 Ok(())
40 }
41}
42
43impl Directory for ClientEnd<fio::DirectoryMarker> {
44 fn open(&self, path: &str, flags: fio::Flags, server_end: zx::Channel) -> Result<(), Error> {
45 let raw_handle = self.channel().as_handle_ref().raw_handle();
46 unsafe {
48 let borrowed: zx::Channel = zx::NullableHandle::from_raw(raw_handle).into();
49 let proxy = fio::DirectorySynchronousProxy::new(borrowed);
50 #[cfg(fuchsia_api_level_at_least = "27")]
51 proxy.open(path, flags, &fio::Options::default(), server_end.into())?;
52 #[cfg(not(fuchsia_api_level_at_least = "27"))]
53 proxy.open3(path, flags, &fio::Options::default(), server_end.into())?;
54 std::mem::forget(proxy.into_channel());
55 }
56 Ok(())
57 }
58}
59
60pub trait AsRefDirectory {
66 fn as_ref_directory(&self) -> &dyn Directory;
68}
69
70impl AsRefDirectory for fio::DirectoryProxy {
71 fn as_ref_directory(&self) -> &dyn Directory {
72 self
73 }
74}
75
76impl AsRefDirectory for fio::DirectorySynchronousProxy {
77 fn as_ref_directory(&self) -> &dyn Directory {
78 self
79 }
80}
81
82impl AsRefDirectory for ClientEnd<fio::DirectoryMarker> {
83 fn as_ref_directory(&self) -> &dyn Directory {
84 self
85 }
86}
87
88impl<T: Directory> AsRefDirectory for Box<T> {
89 fn as_ref_directory(&self) -> &dyn Directory {
90 &**self
91 }
92}
93
94impl<T: Directory> AsRefDirectory for Arc<T> {
95 fn as_ref_directory(&self) -> &dyn Directory {
96 &**self
97 }
98}
99
100impl<T: Directory> AsRefDirectory for &T {
101 fn as_ref_directory(&self) -> &dyn Directory {
102 *self
103 }
104}
105
106pub fn open_directory_async(
108 parent: &impl AsRefDirectory,
109 path: &str,
110 rights: fio::Rights,
111) -> Result<fio::DirectoryProxy, Error> {
112 let (dir, server_end) = fidl::endpoints::create_proxy::<fio::DirectoryMarker>();
113
114 let flags = fio::Flags::PROTOCOL_DIRECTORY | fio::Flags::from_bits_truncate(rights.bits());
115 let () = parent
116 .as_ref_directory()
117 .open(path, flags, server_end.into_channel())
118 .context("opening directory without describe")?;
119
120 Ok(dir)
121}
122
123pub fn open_file_async(
125 parent: &impl AsRefDirectory,
126 path: &str,
127 rights: fio::Rights,
128) -> Result<fio::FileProxy, Error> {
129 let (file, server_end) = fidl::endpoints::create_proxy::<fio::FileMarker>();
130
131 let flags = fio::Flags::PROTOCOL_FILE | fio::Flags::from_bits_truncate(rights.bits());
132 let () = parent
133 .as_ref_directory()
134 .open(path, flags, server_end.into_channel())
135 .context("opening file without describe")?;
136
137 Ok(file)
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use assert_matches::assert_matches;
144 use fuchsia_async as fasync;
145 use vfs::directory::immutable::simple;
146 use vfs::file::vmo::read_only;
147 use vfs::pseudo_directory;
148
149 #[fasync::run_singlethreaded(test)]
150 async fn open_directory_async_real() {
151 let dir = pseudo_directory! {
152 "dir" => simple(),
153 };
154 let dir = vfs::directory::serve_read_only(dir);
155 let dir = open_directory_async(&dir, "dir", fio::Rights::empty()).unwrap();
156 fuchsia_fs::directory::close(dir).await.unwrap();
157 }
158
159 #[fasync::run_singlethreaded(test)]
160 async fn open_directory_async_fake() {
161 let dir = pseudo_directory! {
162 "dir" => simple(),
163 };
164 let dir = vfs::directory::serve_read_only(dir);
165 let dir = open_directory_async(&dir, "fake", fio::Rights::empty()).unwrap();
166 assert_matches!(fuchsia_fs::directory::close(dir).await, Err(_));
168 }
169
170 #[fasync::run_singlethreaded(test)]
171 async fn open_file_async_real() {
172 let dir = pseudo_directory! {
173 "file" => read_only("read_only"),
174 };
175 let dir = vfs::directory::serve_read_only(dir);
176 let file = open_file_async(&dir, "file", fio::Rights::READ_BYTES).unwrap();
177 fuchsia_fs::file::close(file).await.unwrap();
178 }
179
180 #[fasync::run_singlethreaded(test)]
181 async fn open_file_async_fake() {
182 let dir = pseudo_directory! {
183 "file" => read_only("read_only"),
184 };
185 let dir = vfs::directory::serve_read_only(dir);
186 let fake = open_file_async(&dir, "fake", fio::Rights::READ_BYTES).unwrap();
187 assert_matches!(fuchsia_fs::file::close(fake).await, Err(_));
189 }
190}