Skip to main content

runtime_capabilities/fidl/
dir_connector.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::dir_connector::DirConnectable;
6use crate::fidl::registry;
7use crate::fidl::registry::try_from_handle_in_registry;
8use crate::{Capability, DirConnector, DirReceiver, RemoteError, WeakInstanceToken};
9use cm_types::{Name, RelativePath};
10use fidl::endpoints::{ClientEnd, ServerEnd};
11use fidl_fuchsia_component_sandbox as fsandbox;
12use fidl_fuchsia_io as fio;
13use fuchsia_async as fasync;
14use futures::channel::mpsc;
15use std::fmt;
16use std::sync::Arc;
17use vfs::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
18use vfs::execution_scope::ExecutionScope;
19use vfs::object_request::{ObjectRequest, ObjectRequestRef};
20use vfs::remote::RemoteLike;
21
22impl DirConnector {
23    pub(crate) fn new_with_fidl_receiver(
24        receiver_client: ClientEnd<fsandbox::DirReceiverMarker>,
25        scope: &fasync::Scope,
26    ) -> Arc<Self> {
27        let (sender, receiver) = mpsc::unbounded();
28        let receiver = DirReceiver::new(receiver);
29        // Exits when ServerEnd<DirReceiver> is closed
30        scope.spawn(receiver.handle_receiver(receiver_client.into_proxy()));
31        Self::new_sendable(sender)
32    }
33
34    pub fn from_directory_entry(
35        directory_entry: Arc<dyn DirectoryEntry>,
36        flags: fio::Flags,
37    ) -> Arc<Self> {
38        assert_eq!(directory_entry.entry_info().type_(), fio::DirentType::Directory);
39        DirConnector::new_sendable(DirectoryEntryDirConnector {
40            directory_entry,
41            scope: ExecutionScope::new(),
42            flags,
43        })
44    }
45
46    pub(crate) fn try_from_fsandbox(
47        dir_connector: fsandbox::DirConnector,
48    ) -> Result<Arc<Self>, RemoteError> {
49        let any = try_from_handle_in_registry(dir_connector.token.as_handle_ref())?;
50        let Capability::DirConnector(dir_connector) = any else {
51            panic!("BUG: registry has a non-dir-connector capability under a dir-connector koid");
52        };
53        Ok(dir_connector)
54    }
55
56    pub(crate) fn to_fsandbox(self: Arc<Self>) -> fsandbox::DirConnector {
57        fsandbox::DirConnector { token: registry::insert_token(self.into()) }
58    }
59}
60
61struct DirectoryEntryDirConnector {
62    directory_entry: Arc<dyn DirectoryEntry>,
63    scope: ExecutionScope,
64    flags: fio::Flags,
65}
66
67// We can't derive Debug on DirectoryEntryDirConnector because of `Arc<dyn DirectoryEntry>`
68impl fmt::Debug for DirectoryEntryDirConnector {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
70        #[allow(dead_code)]
71        #[derive(Debug)]
72        struct DirectoryEntryDirConnector<'a> {
73            scope: &'a ExecutionScope,
74            flags: &'a fio::Flags,
75        }
76        fmt::Debug::fmt(&DirectoryEntryDirConnector { scope: &self.scope, flags: &self.flags }, f)
77    }
78}
79
80impl DirConnectable for DirectoryEntryDirConnector {
81    fn maximum_flags(&self) -> fio::Flags {
82        self.flags
83    }
84
85    fn send(
86        &self,
87        channel: ServerEnd<fio::DirectoryMarker>,
88        subdir: RelativePath,
89        flags: Option<fio::Flags>,
90    ) -> Result<(), ()> {
91        let flags = flags.unwrap_or(self.flags);
92        let mut object_request =
93            ObjectRequest::new(flags, &fio::Options::default(), channel.into_channel());
94        let path = vfs::path::Path::validate_and_split(format!("{}", subdir))
95            .expect("relative path is invalid vfs path");
96        let open_request = OpenRequest::new(self.scope.clone(), flags, path, &mut object_request);
97        self.directory_entry.clone().open_entry(open_request).map_err(|_| ())
98    }
99}
100
101pub struct DirConnectorDirectoryEntry {
102    pub dir_connector: Arc<DirConnector>,
103}
104
105impl RemoteLike for DirConnectorDirectoryEntry {
106    fn open(
107        self: Arc<Self>,
108        _scope: ExecutionScope,
109        mut path: vfs::path::Path,
110        flags: fio::Flags,
111        object_request: ObjectRequestRef<'_>,
112    ) -> Result<(), zx::Status> {
113        let mut relative_path = RelativePath::dot();
114        while let Some(segment) = path.next() {
115            let name = Name::new(segment).map_err(|_e|
116                // The VFS path isn't valid according to RelativePath.
117                zx::Status::INVALID_ARGS)?;
118            let success = relative_path.push(name);
119            if !success {
120                // The path is too long
121                return Err(zx::Status::INVALID_ARGS);
122            }
123        }
124        self.dir_connector
125            .send(object_request.take().into_server_end(), relative_path, Some(flags))
126            .map_err(|_| zx::Status::INTERNAL)
127    }
128}
129
130impl DirectoryEntry for DirConnectorDirectoryEntry {
131    fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), zx::Status> {
132        request.open_remote(self)
133    }
134}
135
136impl GetEntryInfo for DirConnectorDirectoryEntry {
137    fn entry_info(&self) -> EntryInfo {
138        EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
139    }
140}
141
142impl crate::fidl::IntoFsandboxCapability for Arc<DirConnector> {
143    fn into_fsandbox_capability(self, _token: Arc<WeakInstanceToken>) -> fsandbox::Capability {
144        fsandbox::Capability::DirConnector(fsandbox::DirConnector {
145            token: registry::insert_token(self.into()),
146        })
147    }
148}