1#![warn(missing_docs)]
8
9use crate::common::IntoAny;
10use crate::directory::entry_container::Directory;
11use crate::execution_scope::ExecutionScope;
12use crate::file::{self, FileLike};
13use crate::object_request::ObjectRequestSend;
14use crate::path::Path;
15use crate::service::{self, ServiceLike};
16use crate::symlink::{self, Symlink};
17use crate::{ObjectRequestRef, ToObjectRequest};
18
19use fidl::endpoints::{create_endpoints, ClientEnd};
20use fidl_fuchsia_io as fio;
21use std::fmt;
22use std::future::Future;
23use std::sync::Arc;
24use zx_status::Status;
25
26#[derive(PartialEq, Eq, Clone)]
30pub struct EntryInfo(u64, fio::DirentType);
31
32impl EntryInfo {
33 pub fn new(inode: u64, type_: fio::DirentType) -> Self {
35 Self(inode, type_)
36 }
37
38 pub fn inode(&self) -> u64 {
40 let Self(inode, _type) = self;
41 *inode
42 }
43
44 pub fn type_(&self) -> fio::DirentType {
46 let Self(_inode, type_) = self;
47 *type_
48 }
49}
50
51impl fmt::Debug for EntryInfo {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 let Self(inode, type_) = self;
54 if *inode == fio::INO_UNKNOWN {
55 write!(f, "{:?}(fio::INO_UNKNOWN)", type_)
56 } else {
57 write!(f, "{:?}({})", type_, inode)
58 }
59 }
60}
61
62pub trait GetEntryInfo {
64 fn entry_info(&self) -> EntryInfo;
66}
67
68pub trait DirectoryEntry: GetEntryInfo + IntoAny + Sync + Send + 'static {
74 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status>;
76
77 fn scope(&self) -> Option<ExecutionScope> {
83 None
84 }
85}
86
87pub trait DirectoryEntryAsync: DirectoryEntry {
89 fn open_entry_async(
91 self: Arc<Self>,
92 request: OpenRequest<'_>,
93 ) -> impl Future<Output = Result<(), Status>> + Send;
94}
95
96#[derive(Debug)]
98pub struct OpenRequest<'a> {
99 scope: ExecutionScope,
100 request_flags: RequestFlags,
101 path: Path,
102 object_request: ObjectRequestRef<'a>,
103}
104
105#[derive(Debug)]
108pub enum RequestFlags {
109 Open1(fio::OpenFlags),
111 Open3(fio::Flags),
113}
114
115impl From<fio::OpenFlags> for RequestFlags {
116 fn from(value: fio::OpenFlags) -> Self {
117 RequestFlags::Open1(value)
118 }
119}
120
121impl From<fio::Flags> for RequestFlags {
122 fn from(value: fio::Flags) -> Self {
123 RequestFlags::Open3(value)
124 }
125}
126
127impl<'a> OpenRequest<'a> {
128 pub fn new(
130 scope: ExecutionScope,
131 request_flags: impl Into<RequestFlags>,
132 path: Path,
133 object_request: ObjectRequestRef<'a>,
134 ) -> Self {
135 Self { scope, request_flags: request_flags.into(), path, object_request }
136 }
137
138 pub fn path(&self) -> &Path {
140 &self.path
141 }
142
143 pub fn prepend_path(&mut self, prefix: &Path) {
145 self.path = self.path.with_prefix(prefix);
146 }
147
148 pub fn set_path(&mut self, path: Path) {
150 self.path = path;
151 }
152
153 pub async fn wait_till_ready(&self) -> bool {
157 self.object_request.wait_till_ready().await
158 }
159
160 pub fn requires_event(&self) -> bool {
166 self.object_request.what_to_send() != ObjectRequestSend::Nothing
167 }
168
169 pub fn open_dir(self, dir: Arc<impl Directory>) -> Result<(), Status> {
171 match self {
172 OpenRequest {
173 scope,
174 request_flags: RequestFlags::Open1(flags),
175 path,
176 object_request,
177 } => {
178 dir.deprecated_open(scope, flags, path, object_request.take().into_server_end());
179 Ok(())
182 }
183 OpenRequest {
184 scope,
185 request_flags: RequestFlags::Open3(flags),
186 path,
187 object_request,
188 } => dir.open(scope, path, flags, object_request),
189 }
190 }
191
192 pub fn open_file(self, file: Arc<impl FileLike>) -> Result<(), Status> {
194 match self {
195 OpenRequest {
196 scope,
197 request_flags: RequestFlags::Open1(flags),
198 path,
199 object_request,
200 } => {
201 if !path.is_empty() {
202 return Err(Status::NOT_DIR);
203 }
204 file::serve(file, scope, &flags, object_request)
205 }
206 OpenRequest {
207 scope,
208 request_flags: RequestFlags::Open3(flags),
209 path,
210 object_request,
211 } => {
212 if !path.is_empty() {
213 return Err(Status::NOT_DIR);
214 }
215 file::serve(file, scope, &flags, object_request)
216 }
217 }
218 }
219
220 pub fn open_symlink(self, service: Arc<impl Symlink>) -> Result<(), Status> {
222 match self {
223 OpenRequest {
224 scope,
225 request_flags: RequestFlags::Open1(flags),
226 path,
227 object_request,
228 } => {
229 if !path.is_empty() {
230 return Err(Status::NOT_DIR);
231 }
232 symlink::serve(service, scope, flags, object_request)
233 }
234 OpenRequest {
235 scope,
236 request_flags: RequestFlags::Open3(flags),
237 path,
238 object_request,
239 } => {
240 if !path.is_empty() {
241 return Err(Status::NOT_DIR);
242 }
243 symlink::serve(service, scope, flags, object_request)
244 }
245 }
246 }
247
248 pub fn open_service(self, service: Arc<impl ServiceLike>) -> Result<(), Status> {
250 match self {
251 OpenRequest {
252 scope,
253 request_flags: RequestFlags::Open1(flags),
254 path,
255 object_request,
256 } => {
257 if !path.is_empty() {
258 return Err(Status::NOT_DIR);
259 }
260 service::serve(service, scope, &flags, object_request)
261 }
262 OpenRequest {
263 scope,
264 request_flags: RequestFlags::Open3(flags),
265 path,
266 object_request,
267 } => {
268 if !path.is_empty() {
269 return Err(Status::NOT_DIR);
270 }
271 service::serve(service, scope, &flags, object_request)
272 }
273 }
274 }
275
276 pub fn open_remote(
278 self,
279 remote: Arc<impl crate::remote::RemoteLike + Send + Sync + 'static>,
280 ) -> Result<(), Status> {
281 match self {
282 OpenRequest {
283 scope,
284 request_flags: RequestFlags::Open1(flags),
285 path,
286 object_request,
287 } => {
288 if object_request.what_to_send() == ObjectRequestSend::Nothing && remote.lazy(&path)
289 {
290 let object_request = object_request.take();
291 scope.clone().spawn(async move {
292 if object_request.wait_till_ready().await {
293 remote.deprecated_open(
294 scope,
295 flags,
296 path,
297 object_request.into_server_end(),
298 );
299 }
300 });
301 } else {
302 remote.deprecated_open(
303 scope,
304 flags,
305 path,
306 object_request.take().into_server_end(),
307 );
308 }
309 Ok(())
310 }
311 OpenRequest {
312 scope,
313 request_flags: RequestFlags::Open3(flags),
314 path,
315 object_request,
316 } => {
317 if object_request.what_to_send() == ObjectRequestSend::Nothing && remote.lazy(&path)
318 {
319 let object_request = object_request.take();
320 scope.clone().spawn(async move {
321 if object_request.wait_till_ready().await {
322 object_request.handle(|object_request| {
323 remote.open(scope, path, flags, object_request)
324 });
325 }
326 });
327 Ok(())
328 } else {
329 remote.open(scope, path, flags, object_request)
330 }
331 }
332 }
333 }
334
335 pub fn spawn(self, entry: Arc<impl DirectoryEntryAsync>) {
337 let OpenRequest { scope, request_flags, path, object_request } = self;
338 let mut object_request = object_request.take();
339 match request_flags {
340 RequestFlags::Open1(flags) => {
341 scope.clone().spawn(async move {
342 match entry
343 .open_entry_async(OpenRequest::new(
344 scope,
345 RequestFlags::Open1(flags),
346 path,
347 &mut object_request,
348 ))
349 .await
350 {
351 Ok(()) => {}
352 Err(s) => object_request.shutdown(s),
353 }
354 });
355 }
356 RequestFlags::Open3(flags) => {
357 scope.clone().spawn(async move {
358 match entry
359 .open_entry_async(OpenRequest::new(
360 scope,
361 RequestFlags::Open3(flags),
362 path,
363 &mut object_request,
364 ))
365 .await
366 {
367 Ok(()) => {}
368 Err(s) => object_request.shutdown(s),
369 }
370 });
371 }
372 }
373 }
374
375 pub fn scope(&self) -> &ExecutionScope {
377 &self.scope
378 }
379
380 pub fn set_scope(&mut self, scope: ExecutionScope) {
383 self.scope = scope;
384 }
385}
386
387pub struct SubNode<T: ?Sized> {
390 parent: Arc<T>,
391 path: Path,
392 entry_type: fio::DirentType,
393}
394
395impl<T: DirectoryEntry + ?Sized> SubNode<T> {
396 pub fn new(parent: Arc<T>, path: Path, entry_type: fio::DirentType) -> SubNode<T> {
399 assert_eq!(parent.entry_info().type_(), fio::DirentType::Directory);
400 Self { parent, path, entry_type }
401 }
402}
403
404impl<T: DirectoryEntry + ?Sized> GetEntryInfo for SubNode<T> {
405 fn entry_info(&self) -> EntryInfo {
406 EntryInfo::new(fio::INO_UNKNOWN, self.entry_type)
407 }
408}
409
410impl<T: DirectoryEntry + ?Sized> DirectoryEntry for SubNode<T> {
411 fn open_entry(self: Arc<Self>, mut request: OpenRequest<'_>) -> Result<(), Status> {
412 request.path = request.path.with_prefix(&self.path);
413 self.parent.clone().open_entry(request)
414 }
415}
416
417pub fn serve_directory(
420 dir: Arc<impl DirectoryEntry + ?Sized>,
421 scope: &ExecutionScope,
422 flags: fio::Flags,
423) -> Result<ClientEnd<fio::DirectoryMarker>, Status> {
424 assert_eq!(dir.entry_info().type_(), fio::DirentType::Directory);
425 let (client, server) = create_endpoints::<fio::DirectoryMarker>();
426 flags
427 .to_object_request(server)
428 .handle(|object_request| {
429 Ok(dir.open_entry(OpenRequest::new(scope.clone(), flags, Path::dot(), object_request)))
430 })
431 .unwrap()?;
432 Ok(client)
433}
434
435#[cfg(test)]
436mod tests {
437 use super::{
438 DirectoryEntry, DirectoryEntryAsync, EntryInfo, OpenRequest, RequestFlags, SubNode,
439 };
440 use crate::directory::entry::GetEntryInfo;
441 use crate::execution_scope::ExecutionScope;
442 use crate::file::read_only;
443 use crate::path::Path;
444 use crate::{assert_read, pseudo_directory, ObjectRequest};
445 use assert_matches::assert_matches;
446 use fidl::endpoints::create_proxy;
447 use fidl_fuchsia_io as fio;
448 use futures::StreamExt;
449 use std::sync::Arc;
450 use zx_status::Status;
451
452 #[fuchsia::test]
453 async fn sub_node() {
454 let root = pseudo_directory!(
455 "a" => pseudo_directory!(
456 "b" => pseudo_directory!(
457 "c" => pseudo_directory!(
458 "d" => read_only(b"foo")
459 )
460 )
461 )
462 );
463 let sub_node = Arc::new(SubNode::new(
464 root,
465 Path::validate_and_split("a/b").unwrap(),
466 fio::DirentType::Directory,
467 ));
468
469 let root2 = pseudo_directory!(
470 "e" => sub_node
471 );
472
473 let file_proxy = crate::serve_file(
474 root2,
475 Path::validate_and_split("e/c/d").unwrap(),
476 fio::PERM_READABLE,
477 );
478 assert_read!(file_proxy, "foo");
479 }
480
481 #[fuchsia::test]
482 async fn object_request_spawn() {
483 struct MockNode<F: Send + Sync + 'static>
484 where
485 for<'a> F: Fn(OpenRequest<'a>) -> Status,
486 {
487 callback: F,
488 }
489 impl<F: Send + Sync + 'static> DirectoryEntry for MockNode<F>
490 where
491 for<'a> F: Fn(OpenRequest<'a>) -> Status,
492 {
493 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
494 request.spawn(self);
495 Ok(())
496 }
497 }
498 impl<F: Send + Sync + 'static> GetEntryInfo for MockNode<F>
499 where
500 for<'a> F: Fn(OpenRequest<'a>) -> Status,
501 {
502 fn entry_info(&self) -> EntryInfo {
503 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Unknown)
504 }
505 }
506 impl<F: Send + Sync + 'static> DirectoryEntryAsync for MockNode<F>
507 where
508 for<'a> F: Fn(OpenRequest<'a>) -> Status,
509 {
510 async fn open_entry_async(
511 self: Arc<Self>,
512 request: OpenRequest<'_>,
513 ) -> Result<(), Status> {
514 Err((self.callback)(request))
515 }
516 }
517
518 let scope = ExecutionScope::new();
519
520 let (proxy, server) = create_proxy::<fio::NodeMarker>();
521 let flags = fio::Flags::PROTOCOL_FILE | fio::Flags::FILE_APPEND;
522 let mut object_request =
523 ObjectRequest::new(flags, &Default::default(), server.into_channel());
524
525 Arc::new(MockNode {
526 callback: move |request| {
527 assert_matches!(
528 request,
529 OpenRequest {
530 request_flags: RequestFlags::Open3(f),
531 path,
532 ..
533 } if f == flags && path.as_ref() == "a/b/c"
534 );
535 Status::BAD_STATE
536 },
537 })
538 .open_entry(OpenRequest::new(
539 scope.clone(),
540 flags,
541 "a/b/c".try_into().unwrap(),
542 &mut object_request,
543 ))
544 .unwrap();
545
546 assert_matches!(
547 proxy.take_event_stream().next().await,
548 Some(Err(fidl::Error::ClientChannelClosed { status, .. }))
549 if status == Status::BAD_STATE
550 );
551 }
552}