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