1pub mod availability;
6pub mod bedrock;
7pub mod component_instance;
8pub mod config;
9pub mod error;
10pub mod error_logging_router;
11pub mod intermediate_router;
12pub mod policy;
13pub mod resolving;
14pub mod rights;
15pub mod subdir;
16mod to_request;
17mod to_source;
18
19use crate::bedrock::request_metadata::directory_metadata;
20use crate::component_instance::{ComponentInstanceInterface, ResolvedInstanceInterface};
21use crate::error::RoutingError;
22use capability_source::CapabilitySource;
23use cm_rust::{
24 Availability, CapabilityTypeName, ExposeDecl, ExposeDeclCommon, ExposeTarget, OfferDecl,
25 OfferDeclCommon, OfferTarget, StorageDecl, StorageDirectorySource, UseDecl,
26};
27use cm_types::{IterablePath, Name, RelativePath};
28use fidl_fuchsia_component_decl as fdecl;
29use fidl_fuchsia_component_runtime::RouteRequest;
30use fidl_fuchsia_io::RW_STAR_DIR;
31use itertools::Itertools;
32use moniker::ChildName;
33use runtime_capabilities::{Capability, CapabilityBound, Dictionary, DirConnector, Router};
34use std::fmt::Debug;
35use std::sync::Arc;
36
37pub use bedrock::dict_ext::DictExt;
38pub use bedrock::weak_instance_token_ext::{WeakInstanceTokenExt, test_invalid_instance_token};
39
40#[derive(Clone)]
41pub struct SandboxPath {
42 path: String,
43}
44
45impl SandboxPath {
46 pub fn resolver(scheme: &str) -> Self {
47 Self { path: format!("component_input/environment/resolvers/{}", scheme) }
48 }
49
50 pub fn used_path(target_path: &impl IterablePath) -> Self {
51 let path: RelativePath = target_path.iter_segments().collect::<Vec<_>>().into();
52 Self { path: format!("program_input/namespace/{}", path) }
53 }
54}
55
56impl From<&UseDecl> for SandboxPath {
57 fn from(use_decl: &UseDecl) -> Self {
58 let path = match use_decl {
59 UseDecl::Config(u) => format!("program_input/config/{}", u.target_name),
60 UseDecl::Dictionary(u) => format!("program_input/namespace{}", u.target_path),
61 UseDecl::Directory(u) => format!("program_input/namespace{}", u.target_path),
62 UseDecl::EventStream(u) => format!("program_input/namespace{}", u.target_path),
63 UseDecl::Protocol(u) => match (&u.target_path, &u.numbered_handle) {
64 (Some(target_path), None) => format!("program_input/namespace{}", target_path),
65 (None, Some(numbered_handle)) => {
66 format!("program_input/numbered_handles/{}", Name::from(*numbered_handle))
67 }
68 _ => panic!("invalid use decl"),
69 },
70 UseDecl::Runner(_u) => "program_input/runner".to_string(),
71 UseDecl::Service(u) => format!("program_input/namespace{}", u.target_path),
72 UseDecl::Storage(u) => format!("program_input/namespace{}", u.target_path),
73 };
74 Self { path }
75 }
76}
77
78impl From<&OfferDecl> for SandboxPath {
79 fn from(offer_decl: &OfferDecl) -> Self {
80 let path = match offer_decl.target() {
81 OfferTarget::Child(child_ref) if child_ref.collection.is_some() => {
82 panic!("dynamic offers not supported")
83 }
84 OfferTarget::Child(child_ref) => {
85 format!("child_inputs/{}/parent/{}", child_ref.name, offer_decl.target_name())
86 }
87 OfferTarget::Collection(name) => {
88 format!("collection_inputs/{}/parent/{}", name, offer_decl.target_name())
89 }
90 OfferTarget::Capability(name) => {
91 format!("declared_dictionaries/{}/{}", name, offer_decl.target_name())
92 }
93 };
94 Self { path }
95 }
96}
97
98impl From<&ExposeDecl> for SandboxPath {
99 fn from(expose_decl: &ExposeDecl) -> Self {
100 let path = match expose_decl.target() {
101 ExposeTarget::Parent => {
102 format!("component_output/parent/{}", expose_decl.target_name())
103 }
104 ExposeTarget::Framework => {
105 format!("component_output/framework/{}", expose_decl.target_name())
106 }
107 };
108 Self { path }
109 }
110}
111
112impl From<SandboxPath> for RelativePath {
113 fn from(path: SandboxPath) -> Self {
114 RelativePath::new(&path.path).expect("invalid path string")
115 }
116}
117
118pub async fn debug_route_sandbox_path<C: ComponentInstanceInterface + 'static>(
121 component: &Arc<C>,
122 sandbox_path: impl Into<SandboxPath>,
123) -> Result<CapabilitySource, RoutingError> {
124 debug_route_sandbox_path_with_request(component, sandbox_path, RouteRequest::default()).await
125}
126
127pub async fn debug_route_sandbox_path_with_request<C: ComponentInstanceInterface + 'static>(
130 component: &Arc<C>,
131 sandbox_path: impl Into<SandboxPath>,
132 request: RouteRequest,
133) -> Result<CapabilitySource, RoutingError> {
134 let sandbox_path = sandbox_path.into();
135 let path: RelativePath = sandbox_path.clone().into();
136 let mut path_vec: Vec<Name> = path.iter_segments().map(|n| n.to_owned()).collect();
137 let last_name = path_vec.pop().expect("can't open empty path");
138
139 let sandbox = component.component_sandbox().await.map_err(RoutingError::from)?;
140 let mut dictionary: Arc<Dictionary> = sandbox.into();
141
142 for next_name in path_vec.iter() {
143 match dictionary.get(next_name) {
144 Some(Capability::Dictionary(sub_dictionary)) => dictionary = sub_dictionary,
145 Some(Capability::DictionaryRouter(router)) => {
146 let dictionary_request = RouteRequest {
147 build_type_name: Some(CapabilityTypeName::Dictionary.to_string()),
148 availability: Some(
149 request.availability.unwrap_or(fdecl::Availability::Required),
150 ),
151 ..request.clone()
152 };
153 match router.route(dictionary_request, component.as_weak().into()).await {
154 Ok(Some(sub_dictionary)) => dictionary = sub_dictionary,
155 Ok(None) => {
156 return Err(RoutingError::BedrockNotPresentInDictionary {
157 moniker: component.moniker().clone().into(),
158 name: path.iter_segments().join("/"),
159 });
160 }
161 Err(e) => return Err(e.try_into().unwrap()),
162 }
163 }
164 Some(other_capability) => {
165 return Err(RoutingError::BedrockWrongCapabilityType {
166 actual: other_capability.debug_typename().to_string(),
167 expected: "dictionary".to_string(),
168 moniker: component.moniker().clone().into(),
169 });
170 }
171 None => {
172 return Err(RoutingError::BedrockNotPresentInDictionary {
173 moniker: component.moniker().clone().into(),
174 name: path.iter_segments().join("/"),
175 });
176 }
177 }
178 }
179
180 match dictionary.get(&last_name) {
181 None => Err(RoutingError::BedrockNotPresentInDictionary {
182 moniker: component.moniker().clone().into(),
183 name: path.iter_segments().join("/"),
184 }),
185 Some(Capability::ConnectorRouter(router)) => router
186 .route_debug(request, component.as_weak().into())
187 .await
188 .map_err(|e| RoutingError::try_from(e).expect("invalid routing error")),
189 Some(Capability::DirConnectorRouter(router)) => router
190 .route_debug(request, component.as_weak().into())
191 .await
192 .map_err(|e| RoutingError::try_from(e).expect("invalid routing error")),
193 Some(Capability::DictionaryRouter(router)) => router
194 .route_debug(request, component.as_weak().into())
195 .await
196 .map_err(|e| RoutingError::try_from(e).expect("invalid routing error")),
197 Some(Capability::DataRouter(router)) => router
198 .route_debug(request, component.as_weak().into())
199 .await
200 .map_err(|e| RoutingError::try_from(e).expect("invalid routing error")),
201 Some(_other_type) => {
202 panic!("can't debug route a non-router type")
203 }
204 }
205}
206
207pub async fn debug_route_storage_backing_directory<C: ComponentInstanceInterface + 'static>(
209 component: &Arc<C>,
210 storage_decl: StorageDecl,
211) -> Result<CapabilitySource, RoutingError> {
212 let component_sandbox = component.component_sandbox().await?;
213 let source_dictionary = match storage_decl.source {
214 StorageDirectorySource::Parent => component_sandbox.component_input.capabilities(),
215 StorageDirectorySource::Self_ => component_sandbox.program_output_dict.clone(),
216 StorageDirectorySource::Child(static_name) => {
217 let child_name = ChildName::parse(static_name)
218 .expect("invalid child name, this should be prevented by manifest validation");
219 let child_component = component
220 .lock_resolved_state()
221 .await?
222 .get_child(&child_name)
223 .expect("resolver registration references nonexistent static child, this should be prevented by manifest validation");
224 let child_sandbox = child_component.component_sandbox().await?;
225 child_sandbox.component_output.capabilities().clone()
226 }
227 };
228 route_capability_inner::<DirConnector, _>(
229 &source_dictionary,
230 &storage_decl.backing_dir,
231 directory_metadata(Availability::Required, Some(RW_STAR_DIR.into()), None),
232 component,
233 )
234 .await
235}
236
237async fn route_capability_inner<T, C>(
238 dictionary: &Arc<Dictionary>,
239 path: &impl IterablePath,
240 request: RouteRequest,
241 target: &Arc<C>,
242) -> Result<CapabilitySource, RoutingError>
243where
244 C: ComponentInstanceInterface + 'static,
245 T: CapabilityBound + Debug,
246 Arc<T>: TryFrom<Capability>,
247 Router<T>: CapabilityBound,
248 Capability: From<Arc<T>>,
249 Capability: From<Arc<Router<T>>>,
250 Arc<Router<T>>: TryFrom<Capability>,
251{
252 let Some(capability) = dictionary.get_capability(path) else {
253 return Err(RoutingError::BedrockNotPresentInDictionary {
254 moniker: target.moniker().clone().into(),
255 name: path.iter_segments().join("/"),
256 });
257 };
258 let actual_type_name = capability.debug_typename();
259 let router: Arc<Router<T>> =
260 capability.try_into().map_err(|_| RoutingError::BedrockWrongCapabilityType {
261 actual: actual_type_name.to_string(),
262 expected: Router::<T>::debug_typename().to_string(),
263 moniker: target.moniker().clone().into(),
264 })?;
265 perform_route::<T, C>(router, request, target).await
266}
267
268async fn perform_route<T, C>(
269 router: Arc<Router<T>>,
270 request: RouteRequest,
271 target: &Arc<C>,
272) -> Result<CapabilitySource, RoutingError>
273where
274 C: ComponentInstanceInterface + 'static,
275 T: CapabilityBound + Debug,
276 Arc<Router<T>>: TryFrom<Capability>,
277{
278 router.route_debug(request, target.as_weak().into()).await.map_err(|e| {
279 RoutingError::try_from(e).expect("failed to convert RouterError to RoutingError")
280 })
281}