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