1use crate::execution_scope::ExecutionScope;
8use crate::node::Node;
9use crate::object_request::ObjectRequestRef;
10#[cfg(test)]
11use crate::object_request::ToObjectRequest;
12use crate::protocols::ProtocolsExt;
13use flex_fuchsia_io as fio;
14use std::future::{Future, ready};
15use std::sync::Arc;
16use zx_status::Status;
17
18#[cfg(not(feature = "fdomain"))]
20pub mod vmo;
21
22#[cfg(feature = "fdomain")]
23pub mod simple;
24
25mod common;
26
27pub mod connection;
28
29pub use connection::{FidlIoConnection, RawIoConnection};
30
31#[cfg(not(feature = "fdomain"))]
32pub use connection::{GetVmo, StreamIoConnection};
33
34#[cfg(feature = "fdomain")]
46pub fn read_only(content: impl AsRef<[u8]>) -> Arc<simple::SimpleFile> {
47 simple::SimpleFile::read_only(content)
48}
49
50#[cfg(not(feature = "fdomain"))]
51pub use vmo::read_only;
52
53#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
56pub struct FileOptions {
57 pub rights: fio::Operations,
58 pub is_append: bool,
59 #[cfg(fuchsia_api_level_at_least = "HEAD")]
60 pub is_linkable: bool,
61}
62
63impl FileOptions {
64 #[cfg(target_os = "fuchsia")]
66 pub fn to_stream_options(&self) -> zx::StreamOptions {
67 let mut options = zx::StreamOptions::empty();
68 if self.rights.contains(fio::Operations::READ_BYTES) {
69 options |= zx::StreamOptions::MODE_READ;
70 }
71 if self.rights.contains(fio::Operations::WRITE_BYTES) {
72 options |= zx::StreamOptions::MODE_WRITE;
73 }
74 if self.is_append {
75 options |= zx::StreamOptions::MODE_APPEND;
76 }
77 options
78 }
79
80 pub(crate) fn to_io1(&self) -> fio::OpenFlags {
81 let mut flags = fio::OpenFlags::empty();
82 if self.rights.contains(fio::Operations::READ_BYTES) {
83 flags |= fio::OpenFlags::RIGHT_READABLE;
84 }
85 if self.rights.contains(fio::Operations::WRITE_BYTES) {
86 flags |= fio::OpenFlags::RIGHT_WRITABLE;
87 }
88 if self.rights.contains(fio::Operations::EXECUTE) {
89 flags |= fio::OpenFlags::RIGHT_EXECUTABLE;
90 }
91 if self.is_append {
92 flags |= fio::OpenFlags::APPEND;
93 }
94 flags
95 }
96}
97
98impl From<&FileOptions> for fio::Flags {
99 fn from(options: &FileOptions) -> Self {
100 let mut flags = fio::Flags::from_bits_truncate(options.rights.bits());
102 if options.is_append {
103 flags |= fio::Flags::FILE_APPEND;
104 }
105 flags | fio::Flags::PROTOCOL_FILE
106 }
107}
108
109#[derive(Default, PartialEq)]
110pub enum SyncMode {
111 #[default]
113 Normal,
114
115 PreClose,
126}
127
128pub trait File: Node {
130 fn readable(&self) -> bool {
132 true
133 }
134 fn writable(&self) -> bool {
135 false
136 }
137 fn executable(&self) -> bool {
138 false
139 }
140
141 fn open_file(&self, options: &FileOptions) -> impl Future<Output = Result<(), Status>> + Send;
149
150 fn truncate(&self, length: u64) -> impl Future<Output = Result<(), Status>> + Send;
154
155 #[cfg(target_os = "fuchsia")]
158 fn get_backing_memory(
159 &self,
160 _flags: fio::VmoFlags,
161 ) -> impl Future<Output = Result<zx::Vmo, Status>> + Send {
162 ready(Err(Status::NOT_SUPPORTED))
163 }
164
165 fn get_size(&self) -> impl Future<Output = Result<u64, Status>> + Send;
168
169 fn update_attributes(
173 &self,
174 attributes: fio::MutableNodeAttributes,
175 ) -> impl Future<Output = Result<(), Status>> + Send;
176
177 #[cfg(fuchsia_api_level_at_least = "HEAD")]
179 fn allocate(
180 &self,
181 _offset: u64,
182 _length: u64,
183 _mode: fio::AllocateMode,
184 ) -> impl Future<Output = Result<(), Status>> + Send {
185 ready(Err(Status::NOT_SUPPORTED))
186 }
187
188 #[cfg(fuchsia_api_level_at_least = "HEAD")]
190 fn enable_verity(
191 &self,
192 _options: fio::VerificationOptions,
193 ) -> impl Future<Output = Result<(), Status>> + Send {
194 ready(Err(Status::NOT_SUPPORTED))
195 }
196
197 fn sync(&self, mode: SyncMode) -> impl Future<Output = Result<(), Status>> + Send;
202}
203
204pub trait FileIo: Send + Sync {
207 fn read_at(
211 &self,
212 offset: u64,
213 buffer: &mut [u8],
214 ) -> impl Future<Output = Result<u64, Status>> + Send;
215
216 fn write_at(
223 &self,
224 offset: u64,
225 content: &[u8],
226 ) -> impl Future<Output = Result<u64, Status>> + Send;
227
228 fn append(&self, content: &[u8]) -> impl Future<Output = Result<(u64, u64), Status>> + Send;
236}
237
238pub trait RawFileIoConnection: Send + Sync {
244 fn read(&self, count: u64) -> impl Future<Output = Result<Vec<u8>, Status>> + Send;
247
248 fn read_at(
250 &self,
251 offset: u64,
252 count: u64,
253 ) -> impl Future<Output = Result<Vec<u8>, Status>> + Send;
254
255 fn write(&self, data: &[u8]) -> impl Future<Output = Result<u64, Status>> + Send;
259
260 fn write_at(
262 &self,
263 offset: u64,
264 data: &[u8],
265 ) -> impl Future<Output = Result<u64, Status>> + Send;
266
267 fn seek(
269 &self,
270 offset: i64,
271 origin: fio::SeekOrigin,
272 ) -> impl Future<Output = Result<u64, Status>> + Send;
273
274 fn set_flags(&self, flags: fio::Flags) -> Result<(), Status>;
276}
277
278pub trait FileLike: Node {
279 fn open(
280 self: Arc<Self>,
281 scope: ExecutionScope,
282 options: FileOptions,
283 object_request: ObjectRequestRef<'_>,
284 ) -> Result<(), Status>;
285}
286
287pub fn serve(
289 file: Arc<impl FileLike>,
290 scope: ExecutionScope,
291 protocols: &impl ProtocolsExt,
292 object_request: ObjectRequestRef<'_>,
293) -> Result<(), Status> {
294 if protocols.is_node() {
295 let options = protocols.to_node_options(file.entry_info().type_())?;
296 file.open_as_node(scope, options, object_request)
297 } else {
298 file.open(scope, protocols.to_file_options()?, object_request)
299 }
300}
301
302#[cfg(test)]
304#[cfg(not(feature = "fdomain"))]
305pub fn serve_proxy(file: Arc<impl FileLike>, flags: fio::Flags) -> fio::FileProxy {
306 #[cfg(feature = "fdomain")]
307 let scope = crate::execution_scope::ExecutionScope::new(flex_local::local_client_empty());
308 #[cfg(not(feature = "fdomain"))]
309 let scope = crate::execution_scope::ExecutionScope::new();
310
311 #[cfg(feature = "fdomain")]
312 let (proxy, server) = {
313 let client = scope.domain();
314 client.create_proxy::<fio::FileMarker>()
315 };
316 #[cfg(not(feature = "fdomain"))]
317 let (proxy, server) = fidl::endpoints::create_proxy::<fio::FileMarker>();
318
319 let request = flags.to_object_request(server);
320
321 request.handle(|request| serve(file, scope, &flags, request));
322 proxy
323}
324
325#[cfg(test)]
327#[cfg(feature = "fdomain")]
328pub fn serve_proxy(file: Arc<impl FileLike>, flags: fio::Flags) -> fio::FileProxy {
329 let client = fdomain_local::local_client_empty();
330 let (proxy, server) = client.create_proxy::<fio::FileMarker>();
331 let request = flags.to_object_request(server);
332 request.handle(|request| serve(file, ExecutionScope::new(client.clone()), &flags, request));
333 proxy
334}
335
336#[cfg(test)]
337pub fn serve_proxy_with_scope(
338 file: Arc<impl FileLike>,
339 flags: fio::Flags,
340 scope: &ExecutionScope,
341) -> fio::FileProxy {
342 #[cfg(feature = "fdomain")]
343 let client = scope.domain();
344 #[cfg(feature = "fdomain")]
345 let (proxy, server) = client.create_proxy::<fio::FileMarker>();
346 #[cfg(not(feature = "fdomain"))]
347 let (proxy, server) = fidl::endpoints::create_proxy::<fio::FileMarker>();
348
349 let request = flags.to_object_request(server);
350 request.handle(|request| serve(file, scope.clone(), &flags, request));
351 proxy
352}