Skip to main content

runtime_capabilities/fidl/
router.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::fidl::{IntoFsandboxCapability, RemotableCapability};
6use crate::{
7    Capability, CapabilityBound, Dictionary, Request, Router, RouterResponse, WeakInstanceToken,
8};
9use fidl_fuchsia_component_sandbox as fsandbox;
10use fidl_fuchsia_io as fio;
11use router_error::{Explain, RouterError};
12use std::sync::Arc;
13use vfs::directory::entry::{self, DirectoryEntry, DirectoryEntryAsync, EntryInfo, GetEntryInfo};
14use vfs::execution_scope::ExecutionScope;
15use zx;
16
17impl Request {
18    pub fn into_fsandbox_request(self, token: WeakInstanceToken) -> fsandbox::RouteRequest {
19        let (token_event_pair, server) = zx::EventPair::create();
20        token.clone().register(token_event_pair.koid().unwrap(), server);
21        let fsandbox::Capability::Dictionary(dictionary_ref) =
22            self.metadata.into_fsandbox_capability(token)
23        else {
24            panic!("invalid type");
25        };
26        fsandbox::RouteRequest {
27            requesting: Some(fsandbox::InstanceToken { token: token_event_pair }),
28            metadata: Some(dictionary_ref),
29            ..Default::default()
30        }
31    }
32}
33
34impl TryFrom<fsandbox::DictionaryRouterRouteResponse> for RouterResponse<Dictionary> {
35    type Error = crate::RemoteError;
36
37    fn try_from(resp: fsandbox::DictionaryRouterRouteResponse) -> Result<Self, Self::Error> {
38        Ok(match resp {
39            fsandbox::DictionaryRouterRouteResponse::Dictionary(dict) => {
40                RouterResponse::<Dictionary>::Capability(dict.try_into()?)
41            }
42            fsandbox::DictionaryRouterRouteResponse::Unavailable(_) => RouterResponse::Unavailable,
43        })
44    }
45}
46
47/// Binds a Route request from fidl to the Rust [Router::Route] API. Shared by
48/// [Router] server implementations.
49pub(crate) async fn route_from_fidl<T, R>(
50    router: &Router<T>,
51    payload: fsandbox::RouteRequest,
52    token: WeakInstanceToken,
53) -> Result<R, fsandbox::RouterError>
54where
55    T: CapabilityBound,
56    R: TryFrom<RouterResponse<T>, Error = fsandbox::RouterError>,
57{
58    let resp = match (payload.requesting, payload.metadata) {
59        (Some(token), Some(metadata)) => {
60            let capability =
61                crate::fidl::registry::get(token.token.as_handle_ref().koid().unwrap());
62            let component = match capability {
63                Some(crate::Capability::Instance(c)) => c,
64                Some(_) => return Err(fsandbox::RouterError::InvalidArgs),
65                None => return Err(fsandbox::RouterError::InvalidArgs),
66            };
67            let Capability::Dictionary(metadata) =
68                Capability::try_from(fsandbox::Capability::Dictionary(metadata)).unwrap()
69            else {
70                return Err(fsandbox::RouterError::InvalidArgs);
71            };
72            let request = Request { metadata };
73            router.route(Some(request), false, component).await?
74        }
75        (None, None) => router.route(None, false, token).await?,
76        _ => {
77            return Err(fsandbox::RouterError::InvalidArgs);
78        }
79    };
80    resp.try_into()
81}
82
83impl<T: CapabilityBound + Clone> Router<T>
84where
85    Capability: From<T>,
86{
87    pub(crate) fn into_directory_entry(
88        self,
89        entry_type: fio::DirentType,
90        scope: ExecutionScope,
91        token: WeakInstanceToken,
92    ) -> Arc<dyn DirectoryEntry> {
93        struct RouterEntry<T: CapabilityBound> {
94            router: Router<T>,
95            entry_type: fio::DirentType,
96            scope: ExecutionScope,
97            token: WeakInstanceToken,
98        }
99
100        impl<T: CapabilityBound + Clone> DirectoryEntry for RouterEntry<T>
101        where
102            Capability: From<T>,
103        {
104            fn open_entry(
105                self: Arc<Self>,
106                mut request: entry::OpenRequest<'_>,
107            ) -> Result<(), zx::Status> {
108                request.set_scope(self.scope.clone());
109                request.spawn(self);
110                Ok(())
111            }
112        }
113
114        impl<T: CapabilityBound> GetEntryInfo for RouterEntry<T> {
115            fn entry_info(&self) -> EntryInfo {
116                EntryInfo::new(fio::INO_UNKNOWN, self.entry_type)
117            }
118        }
119
120        impl<T: CapabilityBound + Clone> DirectoryEntryAsync for RouterEntry<T>
121        where
122            Capability: From<T>,
123        {
124            async fn open_entry_async(
125                self: Arc<Self>,
126                open_request: entry::OpenRequest<'_>,
127            ) -> Result<(), zx::Status> {
128                // Hold a guard to prevent this task from being dropped during component
129                // destruction.  This task is tied to the target component.
130                let Some(_guard) = open_request.scope().try_active_guard() else {
131                    return Err(zx::Status::PEER_CLOSED);
132                };
133
134                // Request a capability from the `router`.
135                let result = match self.router.route(None, false, self.token.clone()).await {
136                    Ok(RouterResponse::<T>::Capability(c)) => Ok(Capability::from(c)),
137                    Ok(RouterResponse::<T>::Unavailable) => {
138                        return Err(zx::Status::NOT_FOUND);
139                    }
140                    Ok(RouterResponse::<T>::Debug(_)) => {
141                        // This shouldn't happen.
142                        return Err(zx::Status::INTERNAL);
143                    }
144                    Err(e) => Err(e),
145                };
146                let error = match result {
147                    Ok(capability) => {
148                        match capability
149                            .try_into_directory_entry(self.scope.clone(), self.token.clone())
150                        {
151                            Ok(open) => return open.open_entry(open_request),
152                            Err(_) => RouterError::NotSupported,
153                        }
154                    }
155                    Err(error) => error, // Routing failed (e.g. broken route).
156                };
157                Err(error.as_zx_status())
158            }
159        }
160
161        Arc::new(RouterEntry { router: self, entry_type, scope, token })
162    }
163}