1use crate::bedrock::structured_dict::ComponentInput;
6use crate::bedrock::with_policy_check::WithPolicyCheck;
7use crate::component_instance::{
8 ComponentInstanceInterface, ExtendedInstanceInterface, WeakComponentInstanceInterface,
9};
10use crate::error::{RouteVerb, RoutingError};
11use crate::intermediate_router::{IntermediateRouter, RouteRequest, WeakDictionaryOrRouter};
12use crate::{DictExt, WeakInstanceTokenExt};
13use async_trait::async_trait;
14use capability_source::{CapabilitySource, ComponentCapability, ComponentSource};
15use cm_rust::{CapabilityTypeName, GenericRef, NativeIntoFidl};
16use cm_types::{Name, Path, RelativePath};
17use component_id_index::InstanceId;
18use fidl_fuchsia_component_decl as fdecl;
19use fidl_fuchsia_component_runtime as fruntime;
20use fidl_fuchsia_io as fio;
21use moniker::{ChildName, ExtendedMoniker, Moniker};
22use router_error::RouterError;
23use runtime_capabilities::{
24 Capability, Connector, Data, Dictionary, DirConnector, Routable, Router, WeakInstanceToken,
25};
26use std::collections::HashMap;
27use std::marker::PhantomData;
28use std::path::PathBuf;
29use std::sync::Arc;
30
31pub trait ProgramOutputGenerator<C: ComponentInstanceInterface + 'static> {
32 fn new_program_dictionary_router(
35 &self,
36 component: WeakComponentInstanceInterface<C>,
37 path: Path,
38 capability: ComponentCapability,
39 ) -> Arc<Router<Dictionary>>;
40
41 fn new_outgoing_dir_connector_router(
44 &self,
45 component: &Arc<C>,
46 decl: &cm_rust::ComponentDecl,
47 capability: &cm_rust::CapabilityDecl,
48 ) -> Arc<Router<Connector>>;
49
50 fn new_outgoing_dir_dir_connector_router(
53 &self,
54 component: &Arc<C>,
55 decl: &cm_rust::ComponentDecl,
56 capability: &cm_rust::CapabilityDecl,
57 ) -> Arc<Router<DirConnector>>;
58}
59
60pub fn build_program_output_dictionary<C: ComponentInstanceInterface + 'static>(
61 component: &Arc<C>,
62 decl: &cm_rust::ComponentDecl,
63 component_input: &ComponentInput,
64 child_outgoing_dictionary_routers: &HashMap<ChildName, Arc<Router<Dictionary>>>,
65 router_gen: &impl ProgramOutputGenerator<C>,
66) -> (Arc<Dictionary>, Arc<Dictionary>) {
67 let program_output_dict = Dictionary::new();
68 let declared_dictionaries = Dictionary::new();
69 for capability in &decl.capabilities {
70 extend_dict_with_capability(
71 component,
72 decl,
73 capability,
74 &program_output_dict,
75 &declared_dictionaries,
76 component_input,
77 child_outgoing_dictionary_routers,
78 router_gen,
79 );
80 }
81 (program_output_dict, declared_dictionaries)
82}
83
84fn extend_dict_with_capability<C: ComponentInstanceInterface + 'static>(
87 component: &Arc<C>,
88 decl: &cm_rust::ComponentDecl,
89 capability: &cm_rust::CapabilityDecl,
90 program_output_dict: &Arc<Dictionary>,
91 declared_dictionaries: &Arc<Dictionary>,
92 component_input: &ComponentInput,
93 child_outgoing_dictionary_routers: &HashMap<ChildName, Arc<Router<Dictionary>>>,
94 router_gen: &impl ProgramOutputGenerator<C>,
95) {
96 match capability {
97 cm_rust::CapabilityDecl::Service(_) => {
98 let router =
99 router_gen.new_outgoing_dir_dir_connector_router(component, decl, capability);
100 let router = router.with_policy_check::<C>(
101 CapabilitySource::Component(ComponentSource {
102 capability: ComponentCapability::from(capability.clone()),
103 moniker: component.moniker().clone(),
104 }),
105 component.policy_checker().clone(),
106 );
107 let prev = program_output_dict
108 .insert_capability(capability.name(), Capability::DirConnectorRouter(router));
109 assert!(prev.is_none(), "failed to insert {}: preexisting value", capability.name());
110 }
111 cm_rust::CapabilityDecl::Directory(_) => {
112 let router =
113 router_gen.new_outgoing_dir_dir_connector_router(component, decl, capability);
114 let router = router.with_policy_check::<C>(
115 CapabilitySource::Component(ComponentSource {
116 capability: ComponentCapability::from(capability.clone()),
117 moniker: component.moniker().clone(),
118 }),
119 component.policy_checker().clone(),
120 );
121 let prev = program_output_dict
122 .insert_capability(capability.name(), Capability::DirConnectorRouter(router));
123 assert!(prev.is_none(), "failed to insert {}: preexisting value", capability.name());
124 }
125 cm_rust::CapabilityDecl::Storage(cm_rust::StorageDecl {
126 name,
127 source,
128 backing_dir,
129 subdir,
130 storage_id,
131 }) => {
132 let (backing_source, source_ref): (WeakDictionaryOrRouter, GenericRef) = match source {
133 cm_rust::StorageDirectorySource::Parent => {
134 (Arc::downgrade(&component_input.capabilities()).into(), GenericRef::Parent)
135 }
136 cm_rust::StorageDirectorySource::Self_ => {
137 (Arc::downgrade(program_output_dict).into(), GenericRef::Self_)
138 }
139 cm_rust::StorageDirectorySource::Child(child_name) => {
140 let Some(child_component_output) =
141 child_outgoing_dictionary_routers.get(child_name.as_str())
142 else {
143 panic!(
144 "use declaration in manifest for component {} has a source of a nonexistent child {}, this should be prevented by manifest validation",
145 component.moniker(),
146 child_name
147 );
148 };
149 (
150 Arc::downgrade(child_component_output).into(),
151 GenericRef::Child(Name::new(child_name).unwrap()),
152 )
153 }
154 };
155 let weak_component_token: Arc<WeakInstanceToken> = component.as_weak().into();
156 let router: Arc<Router<DirConnector>> = IntermediateRouter::new(
157 backing_source,
158 vec![backing_dir.clone()].into(),
159 RouteRequest {
160 build_type_name: CapabilityTypeName::Directory,
161 availability: Some(cm_rust::Availability::Required),
162 directory_rights: Some(fio::PERM_READABLE | fio::PERM_WRITABLE),
163 sub_directory_path: Some(RelativePath::dot()),
164 ..Default::default()
165 },
166 weak_component_token.clone(),
167 component.moniker().clone(),
168 RouteVerb::Declare,
169 source_ref,
170 )
171 .try_into()
172 .expect("wrong type from intermediate router");
173
174 #[derive(Debug)]
175 struct StorageBackingDirRouter<C: ComponentInstanceInterface + 'static> {
176 subdir: RelativePath,
177 storage_id: fdecl::StorageId,
178 backing_dir_router: Arc<Router<DirConnector>>,
179 storage_source_moniker: Moniker,
180 backing_dir_target: Arc<WeakInstanceToken>,
181 _component_type: PhantomData<C>,
182 }
183
184 impl<C: ComponentInstanceInterface + 'static> StorageBackingDirRouter<C> {
185 fn prepare_route(
186 &self,
187 mut request: fruntime::RouteRequest,
188 target: Arc<WeakInstanceToken>,
189 ) -> Result<fruntime::RouteRequest, RouterError> {
190 fn generate_moniker_based_storage_path(
191 subdir: Option<String>,
192 moniker: &Moniker,
193 instance_id: Option<&InstanceId>,
194 ) -> PathBuf {
195 let mut dir_path = vec![];
196 if let Some(subdir) = subdir {
197 dir_path.push(subdir);
198 }
199
200 if let Some(id) = instance_id {
201 dir_path.push(id.to_string());
202 return dir_path.into_iter().collect();
203 }
204
205 let path = moniker.path();
206 let mut path = path.iter();
207 if let Some(p) = path.next() {
208 dir_path.push(format!("{p}:0"));
209 }
210 while let Some(p) = path.next() {
211 dir_path.push("children".to_string());
212 dir_path.push(format!("{p}:0"));
213 }
214
215 dir_path.push("data".to_string());
222 dir_path.into_iter().collect()
223 }
224 let StorageBackingDirRouter {
225 subdir,
226 storage_id,
227 backing_dir_router: _,
228 storage_source_moniker,
229 backing_dir_target: _,
230 _component_type: _,
231 } = self;
232 let instance: ExtendedInstanceInterface<C> = target.upgrade().unwrap();
233 let instance = match instance {
234 ExtendedInstanceInterface::Component(c) => c,
235 ExtendedInstanceInterface::AboveRoot(_) => {
236 panic!("unexpected component manager instance")
237 }
238 };
239 let index = instance.component_id_index();
240 let instance_id = index.id_for_moniker(instance.moniker());
241 match storage_id {
242 fdecl::StorageId::StaticInstanceId if instance_id.is_none() => {
243 return Err(RouterError::from(RoutingError::ComponentNotInIdIndex {
244 source_moniker: storage_source_moniker.clone(),
245 target_name: instance.moniker().leaf().map(Into::into),
246 }));
247 }
248 _ => (),
249 }
250 let moniker = match WeakInstanceTokenExt::<C>::moniker(&target) {
251 ExtendedMoniker::ComponentInstance(m) => m,
252 ExtendedMoniker::ComponentManager => {
253 panic!("component manager is the target of a storage capability")
254 }
255 };
256 let moniker = match moniker.strip_prefix(&storage_source_moniker) {
257 Ok(v) => v,
258 Err(_) => moniker,
259 };
260 let subdir_opt = if subdir.is_dot() { None } else { Some(subdir.to_string()) };
261 let isolated_storage_path =
262 generate_moniker_based_storage_path(subdir_opt, &moniker, instance_id);
263 request.isolated_storage_path =
264 Some(format!("{}", isolated_storage_path.display()));
265 request.build_type_name = Some(CapabilityTypeName::Directory.to_string());
266 request.directory_rights = Some(fio::PERM_READABLE | fio::PERM_WRITABLE);
267 request.inherit_rights = Some(false);
268 request.storage_sub_directory_path = Some(subdir.to_string());
269 request.storage_source_moniker = Some(storage_source_moniker.to_string());
270 Ok(request)
271 }
272 }
273
274 #[async_trait]
275 impl<C: ComponentInstanceInterface + 'static> Routable<DirConnector>
276 for StorageBackingDirRouter<C>
277 {
278 async fn route(
279 &self,
280 request: fruntime::RouteRequest,
281 target: Arc<WeakInstanceToken>,
282 ) -> Result<Option<Arc<DirConnector>>, RouterError> {
283 let request = self.prepare_route(request, target)?;
284 self.backing_dir_router.route(request, self.backing_dir_target.clone()).await
285 }
286
287 async fn route_debug(
288 &self,
289 request: fruntime::RouteRequest,
290 target: Arc<WeakInstanceToken>,
291 ) -> Result<CapabilitySource, RouterError> {
292 let request = self.prepare_route(request, target)?;
293 self.backing_dir_router
294 .route_debug(request, self.backing_dir_target.clone())
295 .await
296 }
297 }
298
299 let router = router.with_policy_check::<C>(
300 CapabilitySource::Component(ComponentSource {
301 capability: ComponentCapability::from(capability.clone()),
302 moniker: component.moniker().clone(),
303 }),
304 component.policy_checker().clone(),
305 );
306 let router = Router::new(StorageBackingDirRouter::<C> {
307 subdir: subdir.clone(),
308 storage_id: storage_id.clone(),
309 backing_dir_router: router,
310 storage_source_moniker: component.moniker().clone(),
311 backing_dir_target: weak_component_token,
312 _component_type: Default::default(),
313 });
314 let prev =
315 program_output_dict.insert_capability(name, Capability::DirConnectorRouter(router));
316 assert!(prev.is_none(), "failed to insert {}: preexisting value", capability.name());
317 }
318 cm_rust::CapabilityDecl::Protocol(_)
319 | cm_rust::CapabilityDecl::Runner(_)
320 | cm_rust::CapabilityDecl::Resolver(_) => {
321 let router = router_gen.new_outgoing_dir_connector_router(component, decl, capability);
322 let router = router.with_policy_check::<C>(
323 CapabilitySource::Component(ComponentSource {
324 capability: ComponentCapability::from(capability.clone()),
325 moniker: component.moniker().clone(),
326 }),
327 component.policy_checker().clone(),
328 );
329 let prev = program_output_dict
330 .insert_capability(capability.name(), Capability::ConnectorRouter(router));
331 assert!(prev.is_none(), "failed to insert {}: preexisting value", capability.name());
332 }
333 cm_rust::CapabilityDecl::Dictionary(d) => {
334 extend_dict_with_dictionary(
335 component,
336 d,
337 program_output_dict,
338 declared_dictionaries,
339 router_gen,
340 );
341 }
342 cm_rust::CapabilityDecl::Config(c) => {
343 let data = Arc::new(Data::Bytes(
344 fidl::persist(&c.value.clone().native_into_fidl()).unwrap().into(),
345 ));
346 struct ConfigRouter {
347 data: Arc<Data>,
348 source: CapabilitySource,
349 }
350 #[async_trait]
351 impl Routable<Data> for ConfigRouter {
352 async fn route(
353 &self,
354 _request: fruntime::RouteRequest,
355 _target: Arc<WeakInstanceToken>,
356 ) -> Result<Option<Arc<Data>>, RouterError> {
357 Ok(Some(self.data.clone()))
358 }
359 async fn route_debug(
360 &self,
361 _request: fruntime::RouteRequest,
362 _target: Arc<WeakInstanceToken>,
363 ) -> Result<CapabilitySource, RouterError> {
364 Ok(self.source.clone())
365 }
366 }
367 let source = CapabilitySource::Component(ComponentSource {
368 capability: ComponentCapability::Config(c.name.clone()),
369 moniker: component.moniker().clone(),
370 });
371 let router = Router::new(ConfigRouter { data, source: source.clone() });
372 let router = router.with_policy_check::<C>(source, component.policy_checker().clone());
373 let prev = program_output_dict
374 .insert_capability(capability.name(), Capability::DataRouter(router));
375 assert!(prev.is_none(), "failed to insert {}: preexisting value", capability.name());
376 }
377 cm_rust::CapabilityDecl::EventStream(_) => {
378 return;
380 }
381 }
382}
383
384fn extend_dict_with_dictionary<C: ComponentInstanceInterface + 'static>(
385 component: &Arc<C>,
386 decl: &cm_rust::DictionaryDecl,
387 program_output_dict: &Arc<Dictionary>,
388 declared_dictionaries: &Arc<Dictionary>,
389 router_gen: &impl ProgramOutputGenerator<C>,
390) {
391 let router;
392 let declared_dict;
393 if let Some(source_path) = decl.source_path.as_ref() {
394 router = router_gen.new_program_dictionary_router(
396 component.as_weak(),
397 source_path.clone(),
398 ComponentCapability::Dictionary(decl.clone()),
399 );
400 declared_dict = None;
401 } else {
402 let dict = Dictionary::new();
403 router = make_simple_dict_router(dict.clone(), component, decl);
404 declared_dict = Some(dict);
405 }
406 if let Some(dict) = declared_dict {
407 let prev = declared_dictionaries.insert_capability(&decl.name, dict.into());
408 assert!(prev.is_none(), "failed to insert {}: preexisting value", decl.name);
409 }
410 let prev = program_output_dict.insert_capability(&decl.name, router.into());
411 assert!(prev.is_none(), "failed to insert {}: preexisting value", decl.name);
412}
413
414fn make_simple_dict_router<C: ComponentInstanceInterface + 'static>(
416 dict: Arc<Dictionary>,
417 component: &Arc<C>,
418 decl: &cm_rust::DictionaryDecl,
419) -> Arc<Router<Dictionary>> {
420 struct DictRouter {
421 dict: Arc<Dictionary>,
422 source: CapabilitySource,
423 }
424 #[async_trait]
425 impl Routable<Dictionary> for DictRouter {
426 async fn route(
427 &self,
428 _request: fruntime::RouteRequest,
429 _target: Arc<WeakInstanceToken>,
430 ) -> Result<Option<Arc<Dictionary>>, RouterError> {
431 Ok(Some(self.dict.clone()))
432 }
433
434 async fn route_debug(
435 &self,
436 _request: fruntime::RouteRequest,
437 _target: Arc<WeakInstanceToken>,
438 ) -> Result<CapabilitySource, RouterError> {
439 Ok(self.source.clone())
440 }
441 }
442 let source = CapabilitySource::Component(ComponentSource {
443 capability: ComponentCapability::Dictionary(decl.clone()),
444 moniker: component.moniker().clone(),
445 });
446 Router::<Dictionary>::new(DictRouter { dict, source })
447}