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