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