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
78pub trait DirectoryEntryAsync: DirectoryEntry {
80 fn open_entry_async(
82 self: Arc<Self>,
83 request: OpenRequest<'_>,
84 ) -> impl Future<Output = Result<(), Status>> + Send;
85}
86
87#[derive(Debug)]
89pub struct OpenRequest<'a> {
90 scope: ExecutionScope,
91 request_flags: RequestFlags,
92 path: Path,
93 object_request: ObjectRequestRef<'a>,
94}
95
96#[derive(Debug)]
99pub enum RequestFlags {
100 Open1(fio::OpenFlags),
102 Open3(fio::Flags),
104}
105
106impl From<fio::OpenFlags> for RequestFlags {
107 fn from(value: fio::OpenFlags) -> Self {
108 RequestFlags::Open1(value)
109 }
110}
111
112impl From<fio::Flags> for RequestFlags {
113 fn from(value: fio::Flags) -> Self {
114 RequestFlags::Open3(value)
115 }
116}
117
118impl<'a> OpenRequest<'a> {
119 pub fn new(
121 scope: ExecutionScope,
122 request_flags: impl Into<RequestFlags>,
123 path: Path,
124 object_request: ObjectRequestRef<'a>,
125 ) -> Self {
126 Self { scope, request_flags: request_flags.into(), path, object_request }
127 }
128
129 pub fn path(&self) -> &Path {
131 &self.path
132 }
133
134 pub fn prepend_path(&mut self, prefix: &Path) {
136 self.path = self.path.with_prefix(prefix);
137 }
138
139 pub fn set_path(&mut self, path: Path) {
141 self.path = path;
142 }
143
144 pub async fn wait_till_ready(&self) -> bool {
148 self.object_request.wait_till_ready().await
149 }
150
151 pub fn requires_event(&self) -> bool {
157 self.object_request.what_to_send() != ObjectRequestSend::Nothing
158 }
159
160 pub fn open_dir(self, dir: Arc<impl Directory>) -> Result<(), Status> {
162 match self {
163 OpenRequest {
164 scope,
165 request_flags: RequestFlags::Open1(flags),
166 path,
167 object_request,
168 } => {
169 dir.deprecated_open(scope, flags, path, object_request.take().into_server_end());
170 Ok(())
173 }
174 OpenRequest {
175 scope,
176 request_flags: RequestFlags::Open3(flags),
177 path,
178 object_request,
179 } => dir.open(scope, path, flags, object_request),
180 }
181 }
182
183 pub fn open_file(self, file: Arc<impl FileLike>) -> Result<(), Status> {
185 match self {
186 OpenRequest {
187 scope,
188 request_flags: RequestFlags::Open1(flags),
189 path,
190 object_request,
191 } => {
192 if !path.is_empty() {
193 return Err(Status::NOT_DIR);
194 }
195 file::serve(file, scope, &flags, object_request)
196 }
197 OpenRequest {
198 scope,
199 request_flags: RequestFlags::Open3(flags),
200 path,
201 object_request,
202 } => {
203 if !path.is_empty() {
204 return Err(Status::NOT_DIR);
205 }
206 file::serve(file, scope, &flags, object_request)
207 }
208 }
209 }
210
211 pub fn open_symlink(self, service: Arc<impl Symlink>) -> Result<(), Status> {
213 match self {
214 OpenRequest {
215 scope,
216 request_flags: RequestFlags::Open1(flags),
217 path,
218 object_request,
219 } => {
220 if !path.is_empty() {
221 return Err(Status::NOT_DIR);
222 }
223 symlink::serve(service, scope, flags, object_request)
224 }
225 OpenRequest {
226 scope,
227 request_flags: RequestFlags::Open3(flags),
228 path,
229 object_request,
230 } => {
231 if !path.is_empty() {
232 return Err(Status::NOT_DIR);
233 }
234 symlink::serve(service, scope, flags, object_request)
235 }
236 }
237 }
238
239 pub fn open_service(self, service: Arc<impl ServiceLike>) -> Result<(), Status> {
241 match self {
242 OpenRequest {
243 scope,
244 request_flags: RequestFlags::Open1(flags),
245 path,
246 object_request,
247 } => {
248 if !path.is_empty() {
249 return Err(Status::NOT_DIR);
250 }
251 service::serve(service, scope, &flags, object_request)
252 }
253 OpenRequest {
254 scope,
255 request_flags: RequestFlags::Open3(flags),
256 path,
257 object_request,
258 } => {
259 if !path.is_empty() {
260 return Err(Status::NOT_DIR);
261 }
262 service::serve(service, scope, &flags, object_request)
263 }
264 }
265 }
266
267 pub fn open_remote(
269 self,
270 remote: Arc<impl crate::remote::RemoteLike + Send + Sync + 'static>,
271 ) -> Result<(), Status> {
272 match self {
273 OpenRequest {
274 scope,
275 request_flags: RequestFlags::Open1(flags),
276 path,
277 object_request,
278 } => {
279 if object_request.what_to_send() == ObjectRequestSend::Nothing && remote.lazy(&path)
280 {
281 let object_request = object_request.take();
282 scope.clone().spawn(async move {
283 if object_request.wait_till_ready().await {
284 remote.deprecated_open(
285 scope,
286 flags,
287 path,
288 object_request.into_server_end(),
289 );
290 }
291 });
292 } else {
293 remote.deprecated_open(
294 scope,
295 flags,
296 path,
297 object_request.take().into_server_end(),
298 );
299 }
300 Ok(())
301 }
302 OpenRequest {
303 scope,
304 request_flags: RequestFlags::Open3(flags),
305 path,
306 object_request,
307 } => {
308 if object_request.what_to_send() == ObjectRequestSend::Nothing && remote.lazy(&path)
309 {
310 let object_request = object_request.take();
311 scope.clone().spawn(async move {
312 if object_request.wait_till_ready().await {
313 object_request.handle(|object_request| {
314 remote.open(scope, path, flags, object_request)
315 });
316 }
317 });
318 Ok(())
319 } else {
320 remote.open(scope, path, flags, object_request)
321 }
322 }
323 }
324 }
325
326 pub fn spawn(self, entry: Arc<impl DirectoryEntryAsync>) {
328 let OpenRequest { scope, request_flags, path, object_request } = self;
329 let mut object_request = object_request.take();
330 match request_flags {
331 RequestFlags::Open1(flags) => {
332 scope.clone().spawn(async move {
333 match entry
334 .open_entry_async(OpenRequest::new(
335 scope,
336 RequestFlags::Open1(flags),
337 path,
338 &mut object_request,
339 ))
340 .await
341 {
342 Ok(()) => {}
343 Err(s) => object_request.shutdown(s),
344 }
345 });
346 }
347 RequestFlags::Open3(flags) => {
348 scope.clone().spawn(async move {
349 match entry
350 .open_entry_async(OpenRequest::new(
351 scope,
352 RequestFlags::Open3(flags),
353 path,
354 &mut object_request,
355 ))
356 .await
357 {
358 Ok(()) => {}
359 Err(s) => object_request.shutdown(s),
360 }
361 });
362 }
363 }
364 }
365
366 pub fn scope(&self) -> &ExecutionScope {
368 &self.scope
369 }
370
371 pub fn set_scope(&mut self, scope: ExecutionScope) {
374 self.scope = scope;
375 }
376}
377
378pub struct SubNode<T: ?Sized> {
381 parent: Arc<T>,
382 path: Path,
383 entry_type: fio::DirentType,
384}
385
386impl<T: DirectoryEntry + ?Sized> SubNode<T> {
387 pub fn new(parent: Arc<T>, path: Path, entry_type: fio::DirentType) -> SubNode<T> {
390 assert_eq!(parent.entry_info().type_(), fio::DirentType::Directory);
391 Self { parent, path, entry_type }
392 }
393}
394
395impl<T: DirectoryEntry + ?Sized> GetEntryInfo for SubNode<T> {
396 fn entry_info(&self) -> EntryInfo {
397 EntryInfo::new(fio::INO_UNKNOWN, self.entry_type)
398 }
399}
400
401impl<T: DirectoryEntry + ?Sized> DirectoryEntry for SubNode<T> {
402 fn open_entry(self: Arc<Self>, mut request: OpenRequest<'_>) -> Result<(), Status> {
403 request.path = request.path.with_prefix(&self.path);
404 self.parent.clone().open_entry(request)
405 }
406}
407
408pub fn serve_directory(
411 dir: Arc<impl DirectoryEntry + ?Sized>,
412 scope: &ExecutionScope,
413 flags: fio::Flags,
414) -> Result<ClientEnd<fio::DirectoryMarker>, Status> {
415 assert_eq!(dir.entry_info().type_(), fio::DirentType::Directory);
416 let (client, server) = create_endpoints::<fio::DirectoryMarker>();
417 flags
418 .to_object_request(server)
419 .handle(|object_request| {
420 Ok(dir.open_entry(OpenRequest::new(scope.clone(), flags, Path::dot(), object_request)))
421 })
422 .unwrap()?;
423 Ok(client)
424}
425
426#[cfg(test)]
427mod tests {
428 use super::{
429 DirectoryEntry, DirectoryEntryAsync, EntryInfo, OpenRequest, RequestFlags, SubNode,
430 };
431 use crate::directory::entry::GetEntryInfo;
432 use crate::directory::entry_container::Directory;
433 use crate::execution_scope::ExecutionScope;
434 use crate::file::read_only;
435 use crate::path::Path;
436 use crate::{assert_read, pseudo_directory, ObjectRequest, ToObjectRequest};
437 use assert_matches::assert_matches;
438 use fidl::endpoints::{create_endpoints, create_proxy, ClientEnd};
439 use fidl_fuchsia_io as fio;
440 use futures::StreamExt;
441 use std::sync::Arc;
442 use zx_status::Status;
443
444 #[fuchsia::test]
445 async fn sub_node() {
446 let root = pseudo_directory!(
447 "a" => pseudo_directory!(
448 "b" => pseudo_directory!(
449 "c" => pseudo_directory!(
450 "d" => read_only(b"foo")
451 )
452 )
453 )
454 );
455 let sub_node = Arc::new(SubNode::new(
456 root,
457 Path::validate_and_split("a/b").unwrap(),
458 fio::DirentType::Directory,
459 ));
460 let scope = ExecutionScope::new();
461 let (client, server) = create_endpoints();
462
463 let root2 = pseudo_directory!(
464 "e" => sub_node
465 );
466
467 root2.deprecated_open(
468 scope.clone(),
469 fio::OpenFlags::RIGHT_READABLE,
470 Path::validate_and_split("e/c/d").unwrap(),
471 server,
472 );
473 assert_read!(ClientEnd::<fio::FileMarker>::from(client.into_channel()).into_proxy(), "foo");
474 }
475
476 #[fuchsia::test]
477 async fn object_request_spawn() {
478 struct MockNode<F: Send + Sync + 'static>
479 where
480 for<'a> F: Fn(OpenRequest<'a>) -> Status,
481 {
482 callback: F,
483 }
484 impl<F: Send + Sync + 'static> DirectoryEntry for MockNode<F>
485 where
486 for<'a> F: Fn(OpenRequest<'a>) -> Status,
487 {
488 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
489 request.spawn(self);
490 Ok(())
491 }
492 }
493 impl<F: Send + Sync + 'static> GetEntryInfo for MockNode<F>
494 where
495 for<'a> F: Fn(OpenRequest<'a>) -> Status,
496 {
497 fn entry_info(&self) -> EntryInfo {
498 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Unknown)
499 }
500 }
501 impl<F: Send + Sync + 'static> DirectoryEntryAsync for MockNode<F>
502 where
503 for<'a> F: Fn(OpenRequest<'a>) -> Status,
504 {
505 async fn open_entry_async(
506 self: Arc<Self>,
507 request: OpenRequest<'_>,
508 ) -> Result<(), Status> {
509 Err((self.callback)(request))
510 }
511 }
512
513 let scope = ExecutionScope::new();
514 let (proxy, server) = create_proxy::<fio::NodeMarker>();
515 let flags = fio::OpenFlags::DIRECTORY | fio::OpenFlags::RIGHT_READABLE;
516 let mut object_request = flags.to_object_request(server);
517
518 let flags_copy = flags;
519 Arc::new(MockNode {
520 callback: move |request| {
521 assert_matches!(
522 request,
523 OpenRequest {
524 request_flags: RequestFlags::Open1(f),
525 path,
526 ..
527 } if f == flags_copy && path.as_ref() == "a/b/c"
528 );
529 Status::BAD_STATE
530 },
531 })
532 .open_entry(OpenRequest::new(
533 scope.clone(),
534 flags,
535 "a/b/c".try_into().unwrap(),
536 &mut object_request,
537 ))
538 .unwrap();
539
540 assert_matches!(
541 proxy.take_event_stream().next().await,
542 Some(Err(fidl::Error::ClientChannelClosed { status, .. }))
543 if status == Status::BAD_STATE
544 );
545
546 let (proxy, server) = create_proxy::<fio::NodeMarker>();
547 let flags = fio::Flags::PROTOCOL_FILE | fio::Flags::FILE_APPEND;
548 let mut object_request =
549 ObjectRequest::new(flags, &Default::default(), server.into_channel());
550
551 Arc::new(MockNode {
552 callback: move |request| {
553 assert_matches!(
554 request,
555 OpenRequest {
556 request_flags: RequestFlags::Open3(f),
557 path,
558 ..
559 } if f == flags && path.as_ref() == "a/b/c"
560 );
561 Status::BAD_STATE
562 },
563 })
564 .open_entry(OpenRequest::new(
565 scope.clone(),
566 flags,
567 "a/b/c".try_into().unwrap(),
568 &mut object_request,
569 ))
570 .unwrap();
571
572 assert_matches!(
573 proxy.take_event_stream().next().await,
574 Some(Err(fidl::Error::ClientChannelClosed { status, .. }))
575 if status == Status::BAD_STATE
576 );
577 }
578}