1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
45use crate::DictExt;
6use cm_types::{IterablePath, Name};
7use fidl_fuchsia_component_sandbox as fsandbox;
8use lazy_static::lazy_static;
9use sandbox::{Capability, Dict};
10use std::fmt;
11use std::marker::PhantomData;
1213/// This trait is implemented by types that wrap a [Dict] and wish to present an abstracted
14/// interface over the [Dict].
15///
16/// All such types are defined in this module, so this trait is private.
17///
18/// See also: [StructuredDictMap]
19trait StructuredDict: Into<Dict> + Default + Clone + fmt::Debug {
20/// Converts from [Dict] to `Self`.
21 ///
22 /// REQUIRES: [Dict] is a valid representation of `Self`.
23 ///
24 /// IMPORTANT: The caller should know that [Dict] is a valid representation of [Self]. This
25 /// function is not guaranteed to perform any validation.
26fn from_dict(dict: Dict) -> Self;
27}
2829/// A collection type for mapping [Name] to [StructuredDict], using [Dict] as the underlying
30/// representation.
31///
32/// For example, this can be used to store a map of child or collection names to [ComponentInput]s
33/// (where [ComponentInput] is the type that implements [StructuredDict]).
34///
35/// Because the representation of this type is [Dict], this type itself implements
36/// [StructuredDict].
37#[derive(Clone, Debug, Default)]
38#[allow(private_bounds)]
39pub struct StructuredDictMap<T: StructuredDict> {
40 inner: Dict,
41 phantom: PhantomData<T>,
42}
4344impl<T: StructuredDict> StructuredDict for StructuredDictMap<T> {
45fn from_dict(dict: Dict) -> Self {
46Self { inner: dict, phantom: Default::default() }
47 }
48}
4950#[allow(private_bounds)]
51impl<T: StructuredDict> StructuredDictMap<T> {
52pub fn insert(&self, key: Name, value: T) -> Result<(), fsandbox::CapabilityStoreError> {
53let dict: Dict = value.into();
54self.inner.insert(key, dict.into())
55 }
5657pub fn get(&self, key: &Name) -> Option<T> {
58self.inner.get(key).expect("structured map entry must be cloneable").map(|cap| {
59let Capability::Dictionary(dict) = cap else {
60unreachable!("structured map entry must be a dict: {cap:?}");
61 };
62 T::from_dict(dict)
63 })
64 }
6566pub fn remove(&self, key: &Name) -> Option<T> {
67self.inner.remove(key).map(|cap| {
68let Capability::Dictionary(dict) = cap else {
69unreachable!("structured map entry must be a dict: {cap:?}");
70 };
71 T::from_dict(dict)
72 })
73 }
7475pub fn enumerate(&self) -> impl Iterator<Item = (Name, T)> {
76self.inner.enumerate().map(|(key, capability_res)| match capability_res {
77Ok(Capability::Dictionary(dict)) => (key, T::from_dict(dict)),
78Ok(cap) => unreachable!("structured map entry must be a dict: {cap:?}"),
79Err(_) => panic!("structured map entry must be cloneable"),
80 })
81 }
82}
8384impl<T: StructuredDict> From<StructuredDictMap<T>> for Dict {
85fn from(m: StructuredDictMap<T>) -> Self {
86 m.inner
87 }
88}
8990// Dictionary keys for different kinds of sandboxes.
91lazy_static! {
92/// Dictionary of capabilities from or to the parent.
93static ref PARENT: Name = "parent".parse().unwrap();
9495/// Dictionary of capabilities from a component's environment.
96static ref ENVIRONMENT: Name = "environment".parse().unwrap();
9798/// Dictionary of debug capabilities in a component's environment.
99static ref DEBUG: Name = "debug".parse().unwrap();
100101/// Dictionary of runner capabilities in a component's environment.
102static ref RUNNERS: Name = "runners".parse().unwrap();
103104/// Dictionary of resolver capabilities in a component's environment.
105static ref RESOLVERS: Name = "resolvers".parse().unwrap();
106107/// Dictionary of capabilities available to the framework.
108static ref FRAMEWORK: Name = "framework".parse().unwrap();
109}
110111/// Contains the capabilities component receives from its parent and environment. Stored as a
112/// [Dict] containing two nested [Dict]s for the parent and environment.
113#[derive(Clone, Debug)]
114pub struct ComponentInput(Dict);
115116impl Default for ComponentInput {
117fn default() -> Self {
118Self::new(ComponentEnvironment::new())
119 }
120}
121122impl StructuredDict for ComponentInput {
123fn from_dict(dict: Dict) -> Self {
124Self(dict)
125 }
126}
127128impl ComponentInput {
129pub fn new(environment: ComponentEnvironment) -> Self {
130let dict = Dict::new();
131 dict.insert(PARENT.clone(), Dict::new().into()).ok();
132 dict.insert(ENVIRONMENT.clone(), Dict::from(environment).into()).ok();
133Self(dict)
134 }
135136/// Creates a new ComponentInput with entries cloned from this ComponentInput.
137 ///
138 /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
139 /// underlying data.
140pub fn shallow_copy(&self) -> Result<Self, ()> {
141// Note: We call [Dict::copy] on the nested [Dict]s, not the root [Dict], because
142 // [Dict::copy] only goes one level deep and we want to copy the contents of the
143 // inner sandboxes.
144let dict = Dict::new();
145 dict.insert(PARENT.clone(), self.capabilities().shallow_copy()?.into()).ok();
146 dict.insert(ENVIRONMENT.clone(), Dict::from(self.environment()).shallow_copy()?.into())
147 .ok();
148Ok(Self(dict))
149 }
150151/// Returns the sub-dictionary containing capabilities routed by the component's parent.
152pub fn capabilities(&self) -> Dict {
153let cap = self.0.get(&*PARENT).expect("capabilities must be cloneable").unwrap();
154let Capability::Dictionary(dict) = cap else {
155unreachable!("parent entry must be a dict: {cap:?}");
156 };
157 dict
158 }
159160/// Returns the sub-dictionary containing capabilities routed by the component's environment.
161pub fn environment(&self) -> ComponentEnvironment {
162let cap = self.0.get(&*ENVIRONMENT).expect("environment must be cloneable").unwrap();
163let Capability::Dictionary(dict) = cap else {
164unreachable!("environment entry must be a dict: {cap:?}");
165 };
166 ComponentEnvironment(dict)
167 }
168169pub fn insert_capability(
170&self,
171 path: &impl IterablePath,
172 capability: Capability,
173 ) -> Result<(), fsandbox::CapabilityStoreError> {
174self.capabilities().insert_capability(path, capability.into())
175 }
176}
177178impl From<ComponentInput> for Dict {
179fn from(e: ComponentInput) -> Self {
180 e.0
181}
182}
183184/// The capabilities a component has in its environment. Stored as a [Dict] containing a nested
185/// [Dict] holding the environment's debug capabilities.
186#[derive(Clone, Debug)]
187pub struct ComponentEnvironment(Dict);
188189impl Default for ComponentEnvironment {
190fn default() -> Self {
191let dict = Dict::new();
192 dict.insert(DEBUG.clone(), Dict::new().into()).ok();
193 dict.insert(RUNNERS.clone(), Dict::new().into()).ok();
194 dict.insert(RESOLVERS.clone(), Dict::new().into()).ok();
195Self(dict)
196 }
197}
198199impl StructuredDict for ComponentEnvironment {
200fn from_dict(dict: Dict) -> Self {
201Self(dict)
202 }
203}
204205impl ComponentEnvironment {
206pub fn new() -> Self {
207Self::default()
208 }
209210/// Capabilities listed in the `debug_capabilities` portion of its environment.
211pub fn debug(&self) -> Dict {
212let cap = self.0.get(&*DEBUG).expect("debug must be cloneable").unwrap();
213let Capability::Dictionary(dict) = cap else {
214unreachable!("debug entry must be a dict: {cap:?}");
215 };
216 dict
217 }
218219/// Capabilities listed in the `runners` portion of its environment.
220pub fn runners(&self) -> Dict {
221let cap = self.0.get(&*RUNNERS).expect("runners must be cloneable").unwrap();
222let Capability::Dictionary(dict) = cap else {
223unreachable!("runners entry must be a dict: {cap:?}");
224 };
225 dict
226 }
227228/// Capabilities listed in the `resolvers` portion of its environment.
229pub fn resolvers(&self) -> Dict {
230let cap = self.0.get(&*RESOLVERS).expect("resolvers must be cloneable").unwrap();
231let Capability::Dictionary(dict) = cap else {
232unreachable!("resolvers entry must be a dict: {cap:?}");
233 };
234 dict
235 }
236237pub fn shallow_copy(&self) -> Result<Self, ()> {
238// Note: We call [Dict::shallow_copy] on the nested [Dict]s, not the root [Dict], because
239 // [Dict::shallow_copy] only goes one level deep and we want to copy the contents of the
240 // inner sandboxes.
241let dict = Dict::new();
242 dict.insert(DEBUG.clone(), self.debug().shallow_copy()?.into()).ok();
243 dict.insert(RUNNERS.clone(), self.runners().shallow_copy()?.into()).ok();
244 dict.insert(RESOLVERS.clone(), self.resolvers().shallow_copy()?.into()).ok();
245Ok(Self(dict))
246 }
247}
248249impl From<ComponentEnvironment> for Dict {
250fn from(e: ComponentEnvironment) -> Self {
251 e.0
252}
253}
254255/// Contains the capabilities a component makes available to its parent or the framework. Stored as
256/// a [Dict] containing two nested [Dict]s for the capabilities made available to the parent and to
257/// the framework.
258#[derive(Clone, Debug)]
259pub struct ComponentOutput(Dict);
260261impl Default for ComponentOutput {
262fn default() -> Self {
263Self::new()
264 }
265}
266267impl StructuredDict for ComponentOutput {
268fn from_dict(dict: Dict) -> Self {
269Self(dict)
270 }
271}
272273impl ComponentOutput {
274pub fn new() -> Self {
275let dict = Dict::new();
276 dict.insert(PARENT.clone(), Dict::new().into()).ok();
277 dict.insert(FRAMEWORK.clone(), Dict::new().into()).ok();
278Self(dict)
279 }
280281/// Creates a new ComponentOutput with entries cloned from this ComponentOutput.
282 ///
283 /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
284 /// underlying data.
285pub fn shallow_copy(&self) -> Result<Self, ()> {
286// Note: We call [Dict::copy] on the nested [Dict]s, not the root [Dict], because
287 // [Dict::copy] only goes one level deep and we want to copy the contents of the
288 // inner sandboxes.
289let dict = Dict::new();
290 dict.insert(PARENT.clone(), self.capabilities().shallow_copy()?.into()).ok();
291 dict.insert(FRAMEWORK.clone(), self.framework().shallow_copy()?.into()).ok();
292Ok(Self(dict))
293 }
294295/// Returns the sub-dictionary containing capabilities routed by the component's parent.
296pub fn capabilities(&self) -> Dict {
297let cap = self.0.get(&*PARENT).expect("capabilities must be cloneable").unwrap();
298let Capability::Dictionary(dict) = cap else {
299unreachable!("parent entry must be a dict: {cap:?}");
300 };
301 dict
302 }
303304/// Returns the sub-dictionary containing capabilities routed to the component's framework.
305pub fn framework(&self) -> Dict {
306let cap = self.0.get(&*FRAMEWORK).expect("capabilities must be cloneable").unwrap();
307let Capability::Dictionary(dict) = cap else {
308unreachable!("framework entry must be a dict: {cap:?}");
309 };
310 dict
311 }
312}
313314impl From<ComponentOutput> for Dict {
315fn from(e: ComponentOutput) -> Self {
316 e.0
317}
318}
319320#[cfg(test)]
321mod tests {
322use super::*;
323use assert_matches::assert_matches;
324use sandbox::DictKey;
325326impl StructuredDict for Dict {
327fn from_dict(dict: Dict) -> Self {
328 dict
329 }
330 }
331332#[fuchsia::test]
333async fn structured_dict_map() {
334let dict1 = {
335let dict = Dict::new();
336 dict.insert("a".parse().unwrap(), Dict::new().into())
337 .expect("dict entry already exists");
338 dict
339 };
340let dict2 = {
341let dict = Dict::new();
342 dict.insert("b".parse().unwrap(), Dict::new().into())
343 .expect("dict entry already exists");
344 dict
345 };
346let dict2_alt = {
347let dict = Dict::new();
348 dict.insert("c".parse().unwrap(), Dict::new().into())
349 .expect("dict entry already exists");
350 dict
351 };
352let name1 = Name::new("1").unwrap();
353let name2 = Name::new("2").unwrap();
354355let map: StructuredDictMap<Dict> = Default::default();
356assert_matches!(map.get(&name1), None);
357assert!(map.insert(name1.clone(), dict1).is_ok());
358let d = map.get(&name1).unwrap();
359let key = DictKey::new("a").unwrap();
360assert_matches!(d.get(&key), Ok(Some(_)));
361362assert!(map.insert(name2.clone(), dict2).is_ok());
363let d = map.remove(&name2).unwrap();
364assert_matches!(map.remove(&name2), None);
365let key = DictKey::new("b").unwrap();
366assert_matches!(d.get(&key), Ok(Some(_)));
367368assert!(map.insert(name2.clone(), dict2_alt).is_ok());
369let d = map.get(&name2).unwrap();
370let key = DictKey::new("c").unwrap();
371assert_matches!(d.get(&key), Ok(Some(_)));
372 }
373}