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