Skip to main content

routing/bedrock/
request_metadata.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 std::path::PathBuf;
6use std::str::FromStr;
7
8use crate::rights::Rights;
9use crate::subdir::SubDir;
10use cm_rust::{Availability, CapabilityTypeName};
11use cm_types::RelativePath;
12use fidl::{persist, unpersist};
13use moniker::Moniker;
14use sandbox::{Capability, Data, Dict, DictKey};
15use {
16    fidl_fuchsia_component_internal as finternal, fidl_fuchsia_component_sandbox as fsandbox,
17    fidl_fuchsia_io as fio,
18};
19
20/// A type which has accessors for route request metadata of type T.
21pub trait Metadata<T> {
22    /// A key string used for setting and getting the metadata.
23    const KEY: &'static str;
24
25    /// Infallibly assigns `value` to `self`.
26    fn set_metadata(&self, value: T);
27
28    /// Retrieves the subdir metadata from `self`, if present.
29    fn get_metadata(&self) -> Option<T>;
30}
31
32impl Metadata<CapabilityTypeName> for Dict {
33    const KEY: &'static str = "type";
34
35    fn set_metadata(&self, value: CapabilityTypeName) {
36        let key = DictKey::new(<Self as Metadata<CapabilityTypeName>>::KEY)
37            .expect("dict key creation failed unexpectedly");
38        match self.insert(key, Capability::Data(Data::String(value.to_string().into()))) {
39            // When an entry already exists for a key in a Dict, insert() will
40            // still replace that entry with the new value, even though it
41            // returns an ItemAlreadyExists error. As a result, we can treat
42            // ItemAlreadyExists as a success case.
43            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
44            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
45            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
46        }
47    }
48
49    fn get_metadata(&self) -> Option<CapabilityTypeName> {
50        let key = DictKey::new(<Self as Metadata<CapabilityTypeName>>::KEY)
51            .expect("dict key creation failed unexpectedly");
52        let capability = self.get(&key).ok()??;
53        match capability {
54            Capability::Data(Data::String(capability_type_name)) => {
55                CapabilityTypeName::from_str(&capability_type_name).ok()
56            }
57            _ => None,
58        }
59    }
60}
61
62impl Metadata<Availability> for Dict {
63    const KEY: &'static str = "availability";
64
65    fn set_metadata(&self, value: Availability) {
66        let key = DictKey::new(<Self as Metadata<Availability>>::KEY)
67            .expect("dict key creation failed unexpectedly");
68        match self.insert(key, Capability::Data(Data::String(value.to_string().into()))) {
69            // When an entry already exists for a key in a Dict, insert() will
70            // still replace that entry with the new value, even though it
71            // returns an ItemAlreadyExists error. As a result, we can treat
72            // ItemAlreadyExists as a success case.
73            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
74            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
75            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
76        }
77    }
78
79    fn get_metadata(&self) -> Option<Availability> {
80        let key = DictKey::new(<Self as Metadata<Availability>>::KEY)
81            .expect("dict key creation failed unexpectedly");
82        let capability = self.get(&key).ok()??;
83        match capability {
84            Capability::Data(Data::String(availability)) => match &*availability {
85                "Optional" => Some(Availability::Optional),
86                "Required" => Some(Availability::Required),
87                "SameAsTarget" => Some(Availability::SameAsTarget),
88                "Transitional" => Some(Availability::Transitional),
89                _ => None,
90            },
91            _ => None,
92        }
93    }
94}
95
96impl Metadata<Rights> for Dict {
97    const KEY: &'static str = "rights";
98
99    fn set_metadata(&self, value: Rights) {
100        let key = DictKey::new(<Self as Metadata<Rights>>::KEY)
101            .expect("dict key creation failed unexpectedly");
102        match self.insert(key, Capability::Data(Data::Uint64(value.into()))) {
103            // When an entry already exists for a key in a Dict, insert() will
104            // still replace that entry with the new value, even though it
105            // returns an ItemAlreadyExists error. As a result, we can treat
106            // ItemAlreadyExists as a success case.
107            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
108            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
109            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
110        }
111    }
112
113    fn get_metadata(&self) -> Option<Rights> {
114        let key = DictKey::new(<Self as Metadata<Rights>>::KEY)
115            .expect("dict key creation failed unexpectedly");
116        let capability = self.get(&key).ok()??;
117        let rights = match capability {
118            Capability::Data(Data::Uint64(rights)) => fio::Operations::from_bits(rights)?,
119            _ => None?,
120        };
121        Some(Rights::from(rights))
122    }
123}
124impl Metadata<finternal::EventStreamRouteMetadata> for Dict {
125    const KEY: &'static str = "event_stream_route_metadata";
126
127    fn set_metadata(&self, esrm: finternal::EventStreamRouteMetadata) {
128        let key = DictKey::new(<Self as Metadata<Rights>>::KEY)
129            .expect("dict key creation failed unexpectedly");
130        let value = persist(&esrm).expect("failed to persist event stream route metadata");
131        match self.insert(key, Data::Bytes(value.into()).into()) {
132            // When an entry already exists for a key in a Dict, insert() will
133            // still replace that entry with the new value, even though it
134            // returns an ItemAlreadyExists error. As a result, we can treat
135            // ItemAlreadyExists as a success case.
136            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
137            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
138            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
139        }
140    }
141
142    fn get_metadata(&self) -> Option<finternal::EventStreamRouteMetadata> {
143        let key = DictKey::new(<Self as Metadata<Rights>>::KEY)
144            .expect("dict key creation failed unexpectedly");
145        let capability = self.get(&key).ok()??;
146        match capability {
147            Capability::Data(Data::Bytes(bytes)) => Some(unpersist(&bytes).ok()?),
148            _ => None,
149        }
150    }
151}
152
153/// The directory rights associated with the previous declaration in a multi-step route.
154pub struct IntermediateRights(pub Rights);
155
156impl Metadata<IntermediateRights> for Dict {
157    const KEY: &'static str = "intermediate_rights";
158
159    fn set_metadata(&self, value: IntermediateRights) {
160        let key = DictKey::new(<Self as Metadata<IntermediateRights>>::KEY)
161            .expect("dict key creation failed unexpectedly");
162        let IntermediateRights(value) = value;
163        match self.insert(key, Capability::Data(Data::Uint64(value.into()))) {
164            // When an entry already exists for a key in a Dict, insert() will
165            // still replace that entry with the new value, even though it
166            // returns an ItemAlreadyExists error. As a result, we can treat
167            // ItemAlreadyExists as a success case.
168            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
169            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
170            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
171        }
172    }
173
174    fn get_metadata(&self) -> Option<IntermediateRights> {
175        let key = DictKey::new(<Self as Metadata<IntermediateRights>>::KEY)
176            .expect("dict key creation failed unexpectedly");
177        let capability = self.get(&key).ok()??;
178        let rights = match capability {
179            Capability::Data(Data::Uint64(rights)) => fio::Operations::from_bits(rights)?,
180            _ => None?,
181        };
182        Some(IntermediateRights(Rights::from(rights)))
183    }
184}
185
186/// A flag indicating that directory rights should be inherited from the capability declaration
187/// if they were not present in an expose or offer declaration.
188pub struct InheritRights(pub bool);
189
190impl Metadata<InheritRights> for Dict {
191    const KEY: &'static str = "inherit_rights";
192
193    fn set_metadata(&self, value: InheritRights) {
194        let key = DictKey::new(<Self as Metadata<InheritRights>>::KEY)
195            .expect("dict key creation failed unexpectedly");
196        let InheritRights(value) = value;
197        match self.insert(key, Capability::Data(Data::Uint64(value.into()))) {
198            // When an entry already exists for a key in a Dict, insert() will
199            // still replace that entry with the new value, even though it
200            // returns an ItemAlreadyExists error. As a result, we can treat
201            // ItemAlreadyExists as a success case.
202            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
203            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
204            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
205        }
206    }
207
208    fn get_metadata(&self) -> Option<InheritRights> {
209        let key = DictKey::new(<Self as Metadata<InheritRights>>::KEY)
210            .expect("dict key creation failed unexpectedly");
211        let capability = self.get(&key).ok()??;
212        let inherit = match capability {
213            Capability::Data(Data::Uint64(inherit)) => inherit != 0,
214            _ => None?,
215        };
216        Some(InheritRights(inherit))
217    }
218}
219
220impl Metadata<SubDir> for Dict {
221    const KEY: &'static str = "subdir";
222
223    fn set_metadata(&self, value: SubDir) {
224        let key = DictKey::new(<Self as Metadata<SubDir>>::KEY)
225            .expect("dict key creation failed unexpectedly");
226        match self.insert(key, Capability::Data(Data::String(value.to_string().into()))) {
227            // When an entry already exists for a key in a Dict, insert() will
228            // still replace that entry with the new value, even though it
229            // returns an ItemAlreadyExists error. As a result, we can treat
230            // ItemAlreadyExists as a success case.
231            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
232            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
233            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
234        }
235    }
236
237    fn get_metadata(&self) -> Option<SubDir> {
238        let key = DictKey::new(<Self as Metadata<SubDir>>::KEY)
239            .expect("dict key creation failed unexpectedly");
240        let capability = self.get(&key).ok()??;
241        match capability {
242            Capability::Data(Data::String(subdir)) => SubDir::new(subdir).ok(),
243            _ => None,
244        }
245    }
246}
247
248/// The isolated storage path in the backing directory of the providing
249/// component of a storage capability.
250pub struct IsolatedStoragePath(pub PathBuf);
251
252impl Metadata<IsolatedStoragePath> for Dict {
253    const KEY: &'static str = "isolated_storage_path";
254
255    fn set_metadata(&self, value: IsolatedStoragePath) {
256        let key = DictKey::new(<Self as Metadata<IsolatedStoragePath>>::KEY)
257            .expect("dict key creation failed unexpectedly");
258        match self.insert(
259            key,
260            Capability::Data(Data::String(value.0.to_string_lossy().into_owned().into())),
261        ) {
262            // When an entry already exists for a key in a Dict, insert() will
263            // still replace that entry with the new value, even though it
264            // returns an ItemAlreadyExists error. As a result, we can treat
265            // ItemAlreadyExists as a success case.
266            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
267            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
268            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
269        }
270    }
271
272    fn get_metadata(&self) -> Option<IsolatedStoragePath> {
273        let key = DictKey::new(<Self as Metadata<IsolatedStoragePath>>::KEY)
274            .expect("dict key creation failed unexpectedly");
275        let capability = self.get(&key).ok()??;
276        match capability {
277            Capability::Data(Data::String(isolated_storage_path)) => {
278                Some(IsolatedStoragePath(PathBuf::from(isolated_storage_path.to_string())))
279            }
280            _ => None,
281        }
282    }
283}
284
285/// The subdirectory inside of the storage backing directory's subdirectory to
286/// use, if any. The difference between this and SubDir is that a) the SubDir
287/// generically refers to the subdirectory of a directory capability, and b) the
288/// SubDir is appended to the IsolatedStoragePath first (which is a path into a
289/// backing directory), and component_manager will create the StorageSubdir if
290/// it doesn't exist but won't create SubDir. Accordingly, the complete path to
291/// a storage capability within the backing directory is
292/// {IsolatedStoragePath}/{SubDir}/{StorageSubdir}.
293pub struct StorageSubdir(pub RelativePath);
294
295impl Metadata<StorageSubdir> for Dict {
296    const KEY: &'static str = "storage_subdir";
297
298    fn set_metadata(&self, value: StorageSubdir) {
299        let key = DictKey::new(<Self as Metadata<StorageSubdir>>::KEY)
300            .expect("dict key creation failed unexpectedly");
301        match self.insert(key, Capability::Data(Data::String(value.0.to_string().into()))) {
302            // When an entry already exists for a key in a Dict, insert() will
303            // still replace that entry with the new value, even though it
304            // returns an ItemAlreadyExists error. As a result, we can treat
305            // ItemAlreadyExists as a success case.
306            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
307            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
308            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
309        }
310    }
311
312    fn get_metadata(&self) -> Option<StorageSubdir> {
313        let key = DictKey::new(<Self as Metadata<StorageSubdir>>::KEY)
314            .expect("dict key creation failed unexpectedly");
315        let capability = self.get(&key).ok()??;
316        match capability {
317            Capability::Data(Data::String(subdir)) => {
318                Some(StorageSubdir(RelativePath::new(subdir).unwrap()))
319            }
320            _ => None,
321        }
322    }
323}
324
325/// The moniker of the component that provides a Storage porcelain capability.
326pub struct StorageSourceMoniker(pub Moniker);
327
328impl Metadata<StorageSourceMoniker> for Dict {
329    const KEY: &'static str = "storage_source_moniker";
330
331    fn set_metadata(&self, value: StorageSourceMoniker) {
332        let key = DictKey::new(<Self as Metadata<StorageSourceMoniker>>::KEY)
333            .expect("dict key creation failed unexpectedly");
334        match self.insert(key, Capability::Data(Data::String(value.0.to_string().into()))) {
335            // When an entry already exists for a key in a Dict, insert() will
336            // still replace that entry with the new value, even though it
337            // returns an ItemAlreadyExists error. As a result, we can treat
338            // ItemAlreadyExists as a success case.
339            Ok(()) | Err(fsandbox::CapabilityStoreError::ItemAlreadyExists) => (),
340            // Dict::insert() only returns `CapabilityStoreError::ItemAlreadyExists` variant
341            Err(e) => panic!("unexpected error variant returned from Dict::insert(): {e:?}"),
342        }
343    }
344
345    fn get_metadata(&self) -> Option<StorageSourceMoniker> {
346        let key = DictKey::new(<Self as Metadata<StorageSourceMoniker>>::KEY)
347            .expect("dict key creation failed unexpectedly");
348        let capability = self.get(&key).ok()??;
349        match capability {
350            Capability::Data(Data::String(moniker)) => {
351                Some(StorageSourceMoniker(Moniker::parse_str(&moniker).unwrap()))
352            }
353            _ => None,
354        }
355    }
356}
357
358/// Returns a `Dict` containing Router Request metadata specifying a Protocol porcelain type.
359pub fn protocol_metadata(availability: cm_types::Availability) -> sandbox::Dict {
360    let metadata = sandbox::Dict::new();
361    metadata.set_metadata(CapabilityTypeName::Protocol);
362    metadata.set_metadata(availability);
363    metadata
364}
365
366/// Returns a `Dict` containing Router Request metadata specifying a Dictionary porcelain type.
367pub fn dictionary_metadata(availability: cm_types::Availability) -> sandbox::Dict {
368    let metadata = sandbox::Dict::new();
369    metadata.set_metadata(CapabilityTypeName::Dictionary);
370    metadata.set_metadata(availability);
371    metadata
372}
373
374/// Returns a `Dict` containing Router Request metadata specifying a Directory porcelain type.
375pub fn directory_metadata(
376    availability: cm_types::Availability,
377    rights: Option<Rights>,
378    subdir: Option<SubDir>,
379) -> sandbox::Dict {
380    let metadata = sandbox::Dict::new();
381    metadata.set_metadata(CapabilityTypeName::Directory);
382    if let Some(subdir) = subdir {
383        metadata.set_metadata(subdir);
384    }
385    metadata.set_metadata(availability);
386    match rights {
387        Some(rights) => {
388            metadata.set_metadata(rights);
389            metadata.set_metadata(InheritRights(false));
390        }
391        None => {
392            metadata.set_metadata(InheritRights(true));
393        }
394    }
395    metadata
396}
397
398/// Returns a `Dict` containing Router Request metadata specifying a Config porcelain type.
399pub fn config_metadata(availability: cm_types::Availability) -> sandbox::Dict {
400    let metadata = sandbox::Dict::new();
401    metadata.set_metadata(CapabilityTypeName::Config);
402    metadata.set_metadata(availability);
403    metadata
404}
405
406/// Returns a `Dict` containing Router Request metadata specifying a Runner porcelain type.
407pub fn runner_metadata(availability: cm_types::Availability) -> sandbox::Dict {
408    let metadata = sandbox::Dict::new();
409    metadata.set_metadata(CapabilityTypeName::Runner);
410    metadata.set_metadata(availability);
411    metadata
412}
413
414/// Returns a `Dict` Containing Router Request metadata specifying a Resolver porcelain type.
415pub fn resolver_metadata(availability: cm_types::Availability) -> sandbox::Dict {
416    let metadata = sandbox::Dict::new();
417    metadata.set_metadata(CapabilityTypeName::Resolver);
418    metadata.set_metadata(availability);
419    metadata
420}
421
422/// Returns a `Dict` Containing Router Request metadata specifying a Service porcelain type.
423pub fn service_metadata(availability: cm_types::Availability) -> sandbox::Dict {
424    let metadata = sandbox::Dict::new();
425    metadata.set_metadata(CapabilityTypeName::Service);
426    metadata.set_metadata(availability);
427    // Service capabilities are implemented as DirConnectors. When the Router<DirConnector> that
428    // connects to a component's outgoing directory wants to assemble a DirConnector, it pulls the
429    // set of rights that are allowed for that DirConnector from the route metadata. This gives us
430    // two choices: maintain a different Router<DirConnector> exclusively for connecting service
431    // capabilities to an outgoing directory that hard-codes R_STAR_DIR, or set R_STAR_DIR in the
432    // routing metadata and let the existing Router<DirConnector> use that information.
433    //
434    // It's less code duplication to do the latter, so we set the necessary bits to carry rights
435    // information in the routing metadata for service capability routing.
436    metadata.set_metadata(Rights::from(fio::R_STAR_DIR));
437    metadata.set_metadata(InheritRights(true));
438    metadata
439}
440
441pub fn event_stream_metadata(
442    availability: cm_types::Availability,
443    route_metadata: finternal::EventStreamRouteMetadata,
444) -> sandbox::Dict {
445    let metadata = sandbox::Dict::new();
446    metadata.set_metadata(CapabilityTypeName::EventStream);
447    metadata.set_metadata(availability);
448    metadata.set_metadata(route_metadata);
449    metadata
450}
451
452/// Returns a `Dict` containing Router Request metadata specifying a Storage porcelain type.
453pub fn storage_metadata(availability: cm_types::Availability) -> sandbox::Dict {
454    let metadata = sandbox::Dict::new();
455    metadata.set_metadata(CapabilityTypeName::Storage);
456    metadata.set_metadata(availability);
457    metadata.set_metadata(Rights::from(fio::RW_STAR_DIR));
458    metadata.set_metadata(InheritRights(false));
459    metadata
460}