sandbox/fidl/
dir_entry.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::connector::Connectable;
6use crate::fidl::registry;
7use crate::{Connector, ConversionError, DirEntry, RemotableCapability, WeakInstanceToken};
8use fidl::handle::Status;
9use std::sync::Arc;
10use vfs::ToObjectRequest;
11use vfs::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
12use vfs::execution_scope::ExecutionScope;
13use {fidl_fuchsia_component_sandbox as fsandbox, fidl_fuchsia_io as fio};
14
15impl RemotableCapability for DirEntry {
16    fn try_into_directory_entry(
17        self,
18        _scope: ExecutionScope,
19        _token: WeakInstanceToken,
20    ) -> Result<Arc<dyn DirectoryEntry>, ConversionError> {
21        Ok(self.to_directory_entry())
22    }
23}
24
25impl From<DirEntry> for fsandbox::DirEntry {
26    fn from(value: DirEntry) -> Self {
27        fsandbox::DirEntry { token: registry::insert_token(value.into()) }
28    }
29}
30
31impl crate::fidl::IntoFsandboxCapability for DirEntry {
32    fn into_fsandbox_capability(self, _token: WeakInstanceToken) -> fsandbox::Capability {
33        fsandbox::Capability::DirEntry(self.into())
34    }
35}
36
37impl Connectable for DirEntry {
38    fn send(&self, message: crate::Message) -> Result<(), ()> {
39        const FLAGS: fio::Flags = fio::Flags::PROTOCOL_SERVICE;
40        FLAGS.to_object_request(message.channel).handle(|request| {
41            self.open_entry(OpenRequest::new(
42                ExecutionScope::new(),
43                FLAGS,
44                vfs::Path::dot(),
45                request,
46            ))
47        });
48        Ok(())
49    }
50}
51
52impl GetEntryInfo for DirEntry {
53    fn entry_info(&self) -> EntryInfo {
54        self.entry.entry_info()
55    }
56}
57
58impl DirectoryEntry for DirEntry {
59    fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
60        self.entry.clone().open_entry(request)
61    }
62}
63
64impl DirEntry {
65    /// Creates a [DirEntry] capability from a [vfs::directory::entry::DirectoryEntry].
66    pub fn new(entry: Arc<dyn DirectoryEntry>) -> Self {
67        Self { entry }
68    }
69
70    pub fn to_directory_entry(self) -> Arc<dyn DirectoryEntry> {
71        self.entry
72    }
73
74    /// Opens the corresponding entry by forwarding `open_request`.
75    pub fn open_entry(&self, open_request: OpenRequest<'_>) -> Result<(), Status> {
76        self.entry.clone().open_entry(open_request)
77    }
78
79    pub fn dirent_type(&self) -> fio::DirentType {
80        self.entry.entry_info().type_()
81    }
82}
83
84impl From<Connector> for DirEntry {
85    fn from(connector: Connector) -> Self {
86        Self::new(vfs::service::endpoint(move |_scope, server_end| {
87            let _ = connector.send_channel(server_end.into_zx_channel().into());
88        }))
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95    use crate::Capability;
96    use crate::fidl::IntoFsandboxCapability;
97    use fidl::endpoints;
98    use fidl_fuchsia_io as fio;
99    use test_util::Counter;
100    use vfs::directory::entry::{EntryInfo, GetEntryInfo, OpenRequest};
101    use vfs::execution_scope::ExecutionScope;
102    use vfs::path::Path;
103    use vfs::remote::RemoteLike;
104    use vfs::{ObjectRequestRef, ToObjectRequest};
105
106    struct MockDir(Counter);
107    impl DirectoryEntry for MockDir {
108        fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), zx::Status> {
109            request.open_remote(self)
110        }
111    }
112    impl GetEntryInfo for MockDir {
113        fn entry_info(&self) -> EntryInfo {
114            EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
115        }
116    }
117    impl RemoteLike for MockDir {
118        fn open(
119            self: Arc<Self>,
120            _scope: ExecutionScope,
121            _relative_path: Path,
122            _flags: fio::Flags,
123            _object_request: ObjectRequestRef<'_>,
124        ) -> Result<(), Status> {
125            self.0.inc();
126            Ok(())
127        }
128    }
129
130    #[fuchsia::test]
131    async fn into_fidl() {
132        let mock_dir = Arc::new(MockDir(Counter::new(0)));
133        let dir_entry = Capability::DirEntry(DirEntry::new(mock_dir.clone()));
134
135        // Round-trip to fidl and back. The fidl representation is just a token, so we need to
136        // convert it back to internal to do anything useful with it.
137        let cap = dir_entry.into_fsandbox_capability(WeakInstanceToken::new_invalid());
138        let cap = Capability::try_from(cap).unwrap();
139        let Capability::DirEntry(dir_entry) = cap else {
140            panic!();
141        };
142
143        assert_eq!(mock_dir.0.get(), 0);
144        let scope = ExecutionScope::new();
145        const FLAGS: fio::Flags = fio::PERM_READABLE;
146        let (_client, server) = endpoints::create_endpoints::<fio::DirectoryMarker>();
147        let mut object_request = FLAGS.to_object_request(server);
148        let dir_entry = dir_entry
149            .clone()
150            .try_into_directory_entry(scope.clone(), WeakInstanceToken::new_invalid())
151            .unwrap();
152        dir_entry
153            .open_entry(OpenRequest::new(scope.clone(), FLAGS, Path::dot(), &mut object_request))
154            .unwrap();
155        assert_eq!(mock_dir.0.get(), 1);
156    }
157}