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