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