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