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