Skip to main content

routing/bedrock/
structured_dict.rs

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.
4
5use crate::DictExt;
6use cm_types::{BorrowedName, IterablePath, Name};
7use fidl_fuchsia_component_sandbox as fsandbox;
8use sandbox::{Capability, Data, Dict};
9use std::fmt;
10use std::marker::PhantomData;
11use std::sync::LazyLock;
12
13/// 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.
26    fn from_dict(dict: Dict) -> Self;
27}
28
29/// 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}
43
44impl<T: StructuredDict> StructuredDict for StructuredDictMap<T> {
45    fn from_dict(dict: Dict) -> Self {
46        Self { inner: dict, phantom: Default::default() }
47    }
48}
49
50#[allow(private_bounds)]
51impl<T: StructuredDict> StructuredDictMap<T> {
52    pub fn insert(&self, key: Name, value: T) -> Result<(), fsandbox::CapabilityStoreError> {
53        let dict: Dict = value.into();
54        self.inner.insert(key, dict.into())
55    }
56
57    pub fn get(&self, key: &BorrowedName) -> Option<T> {
58        self.inner.get(key).expect("structured map entry must be cloneable").map(|cap| {
59            let Capability::Dictionary(dict) = cap else {
60                unreachable!("structured map entry must be a dict: {cap:?}");
61            };
62            T::from_dict(dict)
63        })
64    }
65
66    pub fn remove(&self, key: &Name) -> Option<T> {
67        self.inner.remove(&*key).map(|cap| {
68            let Capability::Dictionary(dict) = cap else {
69                unreachable!("structured map entry must be a dict: {cap:?}");
70            };
71            T::from_dict(dict)
72        })
73    }
74
75    pub fn append(&self, other: &Self) -> Result<(), ()> {
76        self.inner.append(&other.inner)
77    }
78
79    pub fn enumerate(&self) -> impl Iterator<Item = (Name, T)> {
80        self.inner.enumerate().map(|(key, capability_res)| match capability_res {
81            Ok(Capability::Dictionary(dict)) => (key, T::from_dict(dict)),
82            Ok(cap) => unreachable!("structured map entry must be a dict: {cap:?}"),
83            Err(_) => panic!("structured map entry must be cloneable"),
84        })
85    }
86}
87
88impl<T: StructuredDict> From<StructuredDictMap<T>> for Dict {
89    fn from(m: StructuredDictMap<T>) -> Self {
90        m.inner
91    }
92}
93
94// Dictionary keys for different kinds of sandboxes.
95/// Dictionary of capabilities from or to the parent.
96static PARENT: LazyLock<Name> = LazyLock::new(|| "parent".parse().unwrap());
97
98/// Dictionary of capabilities from a component's environment.
99static ENVIRONMENT: LazyLock<Name> = LazyLock::new(|| "environment".parse().unwrap());
100
101/// Dictionary of debug capabilities in a component's environment.
102static DEBUG: LazyLock<Name> = LazyLock::new(|| "debug".parse().unwrap());
103
104/// Dictionary of runner capabilities in a component's environment.
105static RUNNERS: LazyLock<Name> = LazyLock::new(|| "runners".parse().unwrap());
106
107/// Dictionary of resolver capabilities in a component's environment.
108static RESOLVERS: LazyLock<Name> = LazyLock::new(|| "resolvers".parse().unwrap());
109
110/// Dictionary of capabilities the component exposes to the framework.
111static FRAMEWORK: LazyLock<Name> = LazyLock::new(|| "framework".parse().unwrap());
112
113/// The stop timeout for a component environment.
114static STOP_TIMEOUT: LazyLock<Name> = LazyLock::new(|| "stop_timeout".parse().unwrap());
115
116/// The name of a component environment.
117static NAME: LazyLock<Name> = LazyLock::new(|| "name".parse().unwrap());
118
119/// Contains the capabilities component receives from its parent and environment. Stored as a
120/// [Dict] containing two nested [Dict]s for the parent and environment.
121#[derive(Clone, Debug)]
122pub struct ComponentInput(Dict);
123
124impl Default for ComponentInput {
125    fn default() -> Self {
126        Self::new(ComponentEnvironment::new())
127    }
128}
129
130impl StructuredDict for ComponentInput {
131    fn from_dict(dict: Dict) -> Self {
132        Self(dict)
133    }
134}
135
136impl ComponentInput {
137    pub fn new(environment: ComponentEnvironment) -> Self {
138        let dict = Dict::new();
139
140        if !environment.0.is_empty() {
141            dict.insert(ENVIRONMENT.clone(), Dict::from(environment).into()).unwrap();
142        }
143        Self(dict)
144    }
145
146    /// Creates a new ComponentInput with entries cloned from this ComponentInput.
147    ///
148    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
149    /// underlying data.
150    pub fn shallow_copy(&self) -> Result<Self, ()> {
151        // Note: We call [Dict::copy] on the nested [Dict]s, not the root [Dict], because
152        // [Dict::copy] only goes one level deep and we want to copy the contents of the
153        // inner sandboxes.
154        let dest = Dict::new();
155        shallow_copy(&self.0, &dest, &*PARENT)?;
156        shallow_copy(&self.0, &dest, &*ENVIRONMENT)?;
157        Ok(Self(dest))
158    }
159
160    /// Returns the sub-dictionary containing capabilities routed by the component's parent.
161    pub fn capabilities(&self) -> Dict {
162        get_or_insert(&self.0, &*PARENT)
163    }
164
165    /// Returns the sub-dictionary containing capabilities routed by the component's environment.
166    pub fn environment(&self) -> ComponentEnvironment {
167        ComponentEnvironment(get_or_insert(&self.0, &*ENVIRONMENT))
168    }
169
170    pub fn insert_capability(
171        &self,
172        path: &impl IterablePath,
173        capability: Capability,
174    ) -> Result<(), fsandbox::CapabilityStoreError> {
175        self.capabilities().insert_capability(path, capability.into())
176    }
177}
178
179impl From<ComponentInput> for Dict {
180    fn from(e: ComponentInput) -> Self {
181        e.0
182    }
183}
184
185/// The capabilities a component has in its environment. Stored as a [Dict] containing a nested
186/// [Dict] holding the environment's debug capabilities.
187#[derive(Clone, Debug)]
188pub struct ComponentEnvironment(Dict);
189
190impl Default for ComponentEnvironment {
191    fn default() -> Self {
192        Self(Dict::new())
193    }
194}
195
196impl StructuredDict for ComponentEnvironment {
197    fn from_dict(dict: Dict) -> Self {
198        Self(dict)
199    }
200}
201
202impl ComponentEnvironment {
203    pub fn new() -> Self {
204        Self::default()
205    }
206
207    /// Capabilities listed in the `debug_capabilities` portion of its environment.
208    pub fn debug(&self) -> Dict {
209        get_or_insert(&self.0, &*DEBUG)
210    }
211
212    /// Capabilities listed in the `runners` portion of its environment.
213    pub fn runners(&self) -> Dict {
214        get_or_insert(&self.0, &*RUNNERS)
215    }
216
217    /// Capabilities listed in the `resolvers` portion of its environment.
218    pub fn resolvers(&self) -> Dict {
219        get_or_insert(&self.0, &*RESOLVERS)
220    }
221
222    /// Sets the stop timeout (in milliseconds) for this environment.
223    pub fn set_stop_timeout(&self, timeout: i64) {
224        let _ = self.0.insert(STOP_TIMEOUT.clone(), Data::Int64(timeout).into());
225    }
226
227    /// Returns the stop timeout (in milliseconds) for this environment.
228    pub fn stop_timeout(&self) -> Option<i64> {
229        let Ok(Some(Capability::Data(Data::Int64(timeout)))) = self.0.get(&*STOP_TIMEOUT) else {
230            return None;
231        };
232        Some(timeout)
233    }
234
235    /// Sets the name for this environment.
236    pub fn set_name(&self, name: &Name) {
237        let _ = self.0.insert(NAME.clone(), Data::String(name.as_str().into()).into());
238    }
239
240    /// Returns the name for this environment.
241    pub fn name(&self) -> Option<Name> {
242        let Ok(Some(Capability::Data(Data::String(name)))) = self.0.get(&*NAME) else {
243            return None;
244        };
245        Some(Name::new(name).unwrap())
246    }
247
248    pub fn shallow_copy(&self) -> Result<Self, ()> {
249        // Note: We call [Dict::shallow_copy] on the nested [Dict]s, not the root [Dict], because
250        // [Dict::shallow_copy] only goes one level deep and we want to copy the contents of the
251        // inner sandboxes.
252        let dest = Dict::new();
253        shallow_copy(&self.0, &dest, &*DEBUG)?;
254        shallow_copy(&self.0, &dest, &*RUNNERS)?;
255        shallow_copy(&self.0, &dest, &*RESOLVERS)?;
256        Ok(Self(dest))
257    }
258}
259
260impl From<ComponentEnvironment> for Dict {
261    fn from(e: ComponentEnvironment) -> Self {
262        e.0
263    }
264}
265
266/// Contains the capabilities a component makes available to its parent or the framework. Stored as
267/// a [Dict] containing two nested [Dict]s for the capabilities made available to the parent and to
268/// the framework.
269#[derive(Clone, Debug)]
270pub struct ComponentOutput(Dict);
271
272impl Default for ComponentOutput {
273    fn default() -> Self {
274        Self::new()
275    }
276}
277
278impl StructuredDict for ComponentOutput {
279    fn from_dict(dict: Dict) -> Self {
280        Self(dict)
281    }
282}
283
284impl ComponentOutput {
285    pub fn new() -> Self {
286        Self(Dict::new())
287    }
288
289    /// Creates a new ComponentOutput with entries cloned from this ComponentOutput.
290    ///
291    /// This is a shallow copy. Values are cloned, not copied, so are new references to the same
292    /// underlying data.
293    pub fn shallow_copy(&self) -> Result<Self, ()> {
294        // Note: We call [Dict::copy] on the nested [Dict]s, not the root [Dict], because
295        // [Dict::copy] only goes one level deep and we want to copy the contents of the
296        // inner sandboxes.
297        let dest = Dict::new();
298        shallow_copy(&self.0, &dest, &*PARENT)?;
299        shallow_copy(&self.0, &dest, &*FRAMEWORK)?;
300        Ok(Self(dest))
301    }
302
303    /// Returns the sub-dictionary containing capabilities routed to the component's parent.
304    /// framework. Lazily adds the dictionary if it does not exist yet.
305    pub fn capabilities(&self) -> Dict {
306        get_or_insert(&self.0, &*PARENT)
307    }
308
309    /// Returns the sub-dictionary containing capabilities exposed by the component to the
310    /// framework. Lazily adds the dictionary if it does not exist yet.
311    pub fn framework(&self) -> Dict {
312        get_or_insert(&self.0, &*FRAMEWORK)
313    }
314}
315
316impl From<ComponentOutput> for Dict {
317    fn from(e: ComponentOutput) -> Self {
318        e.0
319    }
320}
321
322fn shallow_copy(src: &Dict, dest: &Dict, key: &Name) -> Result<(), ()> {
323    if let Some(d) = src.get(key).expect("must be cloneable") {
324        let Capability::Dictionary(d) = d else {
325            unreachable!("{key} entry must be a dict: {d:?}");
326        };
327        dest.insert(key.clone(), d.shallow_copy()?.into()).ok();
328    }
329    Ok(())
330}
331
332fn get_or_insert(this: &Dict, key: &Name) -> Dict {
333    let cap = this
334        .get_or_insert(&key, || Capability::Dictionary(Dict::new()))
335        .expect("capabilities must be cloneable");
336    let Capability::Dictionary(dict) = cap else {
337        unreachable!("{key} entry must be a dict: {cap:?}");
338    };
339    dict
340}
341
342#[cfg(test)]
343mod tests {
344    use super::*;
345    use assert_matches::assert_matches;
346    use sandbox::DictKey;
347
348    impl StructuredDict for Dict {
349        fn from_dict(dict: Dict) -> Self {
350            dict
351        }
352    }
353
354    #[fuchsia::test]
355    async fn structured_dict_map() {
356        let dict1 = {
357            let dict = Dict::new();
358            dict.insert("a".parse().unwrap(), Dict::new().into())
359                .expect("dict entry already exists");
360            dict
361        };
362        let dict2 = {
363            let dict = Dict::new();
364            dict.insert("b".parse().unwrap(), Dict::new().into())
365                .expect("dict entry already exists");
366            dict
367        };
368        let dict2_alt = {
369            let dict = Dict::new();
370            dict.insert("c".parse().unwrap(), Dict::new().into())
371                .expect("dict entry already exists");
372            dict
373        };
374        let name1 = Name::new("1").unwrap();
375        let name2 = Name::new("2").unwrap();
376
377        let map: StructuredDictMap<Dict> = Default::default();
378        assert_matches!(map.get(&name1), None);
379        assert!(map.insert(name1.clone(), dict1).is_ok());
380        let d = map.get(&name1).unwrap();
381        let key = DictKey::new("a").unwrap();
382        assert_matches!(d.get(&key), Ok(Some(_)));
383
384        assert!(map.insert(name2.clone(), dict2).is_ok());
385        let d = map.remove(&name2).unwrap();
386        assert_matches!(map.remove(&name2), None);
387        let key = DictKey::new("b").unwrap();
388        assert_matches!(d.get(&key), Ok(Some(_)));
389
390        assert!(map.insert(name2.clone(), dict2_alt).is_ok());
391        let d = map.get(&name2).unwrap();
392        let key = DictKey::new("c").unwrap();
393        assert_matches!(d.get(&key), Ok(Some(_)));
394    }
395}