Skip to main content

runtime_capabilities/fidl/
store.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::dictionary::Key;
6use crate::fidl::{IntoFsandboxCapability, RemotableCapability, registry};
7use crate::{Capability, Connector, Dictionary, DirConnector, WeakInstanceToken};
8use cm_types::RelativePath;
9use fidl::handle::Signals;
10use fidl_fuchsia_component_decl as fdecl;
11use fidl_fuchsia_component_sandbox::{self as fsandbox, CapabilityStoreRequest};
12use fidl_fuchsia_io as fio;
13use fuchsia_async as fasync;
14use futures::{FutureExt, TryStreamExt};
15use log::warn;
16use std::collections::HashMap;
17use std::collections::hash_map::Entry;
18use std::sync::{self, Arc, Weak};
19use vfs::ExecutionScope;
20use vfs::directory::entry::SubNode;
21use vfs::directory::helper::DirectlyMutable;
22use vfs::directory::simple::Simple;
23use vfs::path::Path;
24
25type Store = sync::Mutex<HashMap<u64, Capability>>;
26
27pub async fn serve_capability_store(
28    mut stream: fsandbox::CapabilityStoreRequestStream,
29    // Receiver tasks launched on behalf of a *Connector may need to outlive this
30    // `CapabilityStore`. For example, if a client creates a Connector, Export()s it right away,
31    // then drops the `CapabilityStore`, dropping the task at that point deliver a `PEER_CLOSED`
32    // to the client and make the Connector they just created unusable.
33    //
34    // We could simply detach() the Task instead, but fuchsia_async considers that holding the
35    // handle is considered better practice.
36    receiver_scope: &fasync::Scope,
37    token: WeakInstanceToken,
38) -> Result<(), fidl::Error> {
39    let outer_store: Arc<Store> = Arc::new(Store::new(Default::default()));
40    while let Some(request) = stream.try_next().await? {
41        handle_capability_store_request(request, &outer_store, receiver_scope, &token)
42            .boxed()
43            .await?;
44    }
45    Ok(())
46}
47
48async fn handle_capability_store_request(
49    request: CapabilityStoreRequest,
50    outer_store: &Arc<Store>,
51    receiver_scope: &fasync::Scope,
52    token: &WeakInstanceToken,
53) -> Result<(), fidl::Error> {
54    let mut store = outer_store.lock().unwrap();
55    match request {
56        fsandbox::CapabilityStoreRequest::Duplicate { id, dest_id, responder } => {
57            let result = (|| {
58                let cap = store.get(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?.clone();
59                insert_capability(&mut store, dest_id, cap)
60            })();
61            responder.send(result)?;
62        }
63        fsandbox::CapabilityStoreRequest::Drop { id, responder } => {
64            let result =
65                store.remove(&id).map(|_| ()).ok_or(fsandbox::CapabilityStoreError::IdNotFound);
66            responder.send(result)?;
67        }
68        fsandbox::CapabilityStoreRequest::Export { id, responder } => {
69            let result = (|| {
70                let cap = store.remove(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
71                Ok(cap.into_fsandbox_capability(token.clone()))
72            })();
73            responder.send(result)?;
74        }
75        fsandbox::CapabilityStoreRequest::Import { id, capability, responder } => {
76            let result = (|| {
77                let capability = capability
78                    .try_into()
79                    .map_err(|_| fsandbox::CapabilityStoreError::BadCapability)?;
80                insert_capability(&mut store, id, capability)
81            })();
82            responder.send(result)?;
83        }
84        fsandbox::CapabilityStoreRequest::ConnectorCreate { id, receiver, responder } => {
85            let result = (|| {
86                let connector = Connector::new_with_fidl_receiver(receiver, receiver_scope);
87                insert_capability(&mut store, id, Capability::Connector(connector))
88            })();
89            responder.send(result)?;
90        }
91        fsandbox::CapabilityStoreRequest::ConnectorOpen { id, server_end, responder } => {
92            let result = (|| {
93                let this = get_connector(&store, id)?;
94                let _ = this.send(server_end);
95                Ok(())
96            })();
97            responder.send(result)?;
98        }
99        fsandbox::CapabilityStoreRequest::DirConnectorCreate { id, receiver, responder } => {
100            let result = (|| {
101                let connector = DirConnector::new_with_fidl_receiver(receiver, receiver_scope);
102                insert_capability(&mut store, id, Capability::DirConnector(connector))
103            })();
104            responder.send(result)?;
105        }
106        fsandbox::CapabilityStoreRequest::DirConnectorOpen { payload, responder } => {
107            let result = (|| {
108                let Some(id) = payload.id else {
109                    return Err(fsandbox::CapabilityStoreError::InvalidArgs);
110                };
111                let Some(server_end) = payload.server_end else {
112                    return Err(fsandbox::CapabilityStoreError::InvalidArgs);
113                };
114                let this = get_dir_connector(&store, id)?;
115                let path = payload
116                    .path
117                    .map(RelativePath::new)
118                    .transpose()
119                    .map_err(|_| fsandbox::CapabilityStoreError::InvalidArgs)?
120                    .unwrap_or_else(|| RelativePath::dot());
121                let _ = this.send(server_end, path, payload.flags);
122                Ok(())
123            })();
124            responder.send(result)?;
125        }
126        fsandbox::CapabilityStoreRequest::DictionaryCreate { id, responder } => {
127            let result =
128                insert_capability(&mut store, id, Capability::Dictionary(Dictionary::new()));
129            responder.send(result)?;
130        }
131        fsandbox::CapabilityStoreRequest::DictionaryLegacyImport { id, client_end, responder } => {
132            let result = (|| {
133                let capability = Dictionary::try_from(client_end)
134                    .map_err(|_| fsandbox::CapabilityStoreError::BadCapability)?
135                    .into();
136                insert_capability(&mut store, id, capability)
137            })();
138            responder.send(result)?;
139        }
140        fsandbox::CapabilityStoreRequest::DictionaryLegacyExport { id, server_end, responder } => {
141            let result = (|| {
142                let cap = store.remove(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
143                let Capability::Dictionary(_) = &cap else {
144                    return Err(fsandbox::CapabilityStoreError::WrongType);
145                };
146                let koid = server_end.as_handle_ref().basic_info().unwrap().related_koid;
147                registry::insert(
148                    cap,
149                    koid,
150                    fasync::OnSignals::new(server_end, Signals::OBJECT_PEER_CLOSED).map(|_| ()),
151                );
152                Ok(())
153            })();
154            responder.send(result)?
155        }
156        fsandbox::CapabilityStoreRequest::DictionaryInsert { id, item, responder } => {
157            let result = (|| {
158                let this = get_dictionary(&store, id)?;
159                let this = this.clone();
160                let key =
161                    item.key.parse().map_err(|_| fsandbox::CapabilityStoreError::InvalidKey)?;
162                let value =
163                    store.remove(&item.value).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
164                if this.insert(key, value).is_some() {
165                    Err(fsandbox::CapabilityStoreError::ItemAlreadyExists)
166                } else {
167                    Ok(())
168                }
169            })();
170            responder.send(result)?;
171        }
172        fsandbox::CapabilityStoreRequest::DictionaryGet { id, key, dest_id, responder } => {
173            let result = (|| {
174                let this = get_dictionary(&store, id)?;
175                let key = Key::new(key).map_err(|_| fsandbox::CapabilityStoreError::InvalidKey)?;
176                let cap = match this.get(&key) {
177                    Some(cap) => Ok(cap),
178                    None => {
179                        this.not_found(key.as_str());
180                        Err(fsandbox::CapabilityStoreError::ItemNotFound)
181                    }
182                }?;
183                insert_capability(&mut store, dest_id, cap)
184            })();
185            responder.send(result)?;
186        }
187        fsandbox::CapabilityStoreRequest::DictionaryRemove { id, key, dest_id, responder } => {
188            let result = (|| {
189                let this = get_dictionary(&store, id)?;
190                let key = Key::new(key).map_err(|_| fsandbox::CapabilityStoreError::InvalidKey)?;
191                // Check this before removing from the dictionary.
192                if let Some(dest_id) = dest_id.as_ref() {
193                    if store.contains_key(&dest_id.id) {
194                        return Err(fsandbox::CapabilityStoreError::IdAlreadyExists);
195                    }
196                }
197                let cap = match this.remove(&key) {
198                    Some(cap) => Ok(cap.into()),
199                    None => {
200                        this.not_found(key.as_str());
201                        Err(fsandbox::CapabilityStoreError::ItemNotFound)
202                    }
203                }?;
204                if let Some(dest_id) = dest_id.as_ref() {
205                    store.insert(dest_id.id, cap);
206                }
207                Ok(())
208            })();
209            responder.send(result)?;
210        }
211        fsandbox::CapabilityStoreRequest::DictionaryCopy { id, dest_id, responder } => {
212            let result = (|| {
213                let this = get_dictionary(&store, id)?;
214                let dict = this.shallow_copy();
215                insert_capability(&mut store, dest_id, Capability::Dictionary(dict))
216            })();
217            responder.send(result)?
218        }
219        fsandbox::CapabilityStoreRequest::DictionaryKeys {
220            id,
221            iterator: server_end,
222            responder,
223        } => {
224            let result = (|| {
225                let this = get_dictionary(&store, id)?;
226                let keys = this.snapshot_keys_as_strings().into_iter();
227                let stream = server_end.into_stream();
228                let mut this = this.lock();
229                this.tasks().spawn(serve_dictionary_keys_iterator(keys, stream));
230                Ok(())
231            })();
232            responder.send(result)?
233        }
234        fsandbox::CapabilityStoreRequest::DictionaryEnumerate {
235            id,
236            iterator: server_end,
237            responder,
238        } => {
239            let result = (|| {
240                let this = get_dictionary(&store, id)?;
241                let items = this.enumerate().map(|(k, v)| (k, Ok(v)));
242                let stream = server_end.into_stream();
243                let mut this = this.lock();
244                this.tasks().spawn(serve_dictionary_enumerate_iterator(
245                    Arc::downgrade(&outer_store),
246                    items,
247                    stream,
248                ));
249                Ok(())
250            })();
251            responder.send(result)?
252        }
253        fsandbox::CapabilityStoreRequest::DictionaryDrain {
254            id,
255            iterator: server_end,
256            responder,
257        } => {
258            let result = (|| {
259                let this = get_dictionary(&store, id)?;
260                // Take out entries, replacing with an empty BTreeMap.
261                // They are dropped if the caller does not request an iterator.
262                let items = this.drain();
263                if let Some(server_end) = server_end {
264                    let stream = server_end.into_stream();
265                    let mut this = this.lock();
266                    this.tasks().spawn(serve_dictionary_drain_iterator(
267                        Arc::downgrade(&outer_store),
268                        items,
269                        stream,
270                    ));
271                }
272                Ok(())
273            })();
274            responder.send(result)?
275        }
276        fsandbox::CapabilityStoreRequest::CreateServiceAggregate { sources, responder } => {
277            // Store does not use an async-compatible mutex, so we can't hold a MutexGuard for
278            // it across await boundaries. This means we must drop the store MutexGuard before
279            // calling await, or Rust yells at us that the futures are not Send.
280            drop(store);
281            responder.send(create_service_aggregate(token.clone(), sources).await)?;
282        }
283        fsandbox::CapabilityStoreRequest::_UnknownMethod { ordinal, .. } => {
284            warn!("Received unknown CapabilityStore request with ordinal {ordinal}");
285        }
286    }
287    Ok(())
288}
289
290async fn serve_dictionary_keys_iterator(
291    mut keys: impl Iterator<Item = String>,
292    mut stream: fsandbox::DictionaryKeysIteratorRequestStream,
293) {
294    while let Ok(Some(request)) = stream.try_next().await {
295        match request {
296            fsandbox::DictionaryKeysIteratorRequest::GetNext { responder } => {
297                let mut chunk = vec![];
298                for _ in 0..fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK {
299                    match keys.next() {
300                        Some(key) => {
301                            chunk.push(key.into());
302                        }
303                        None => break,
304                    }
305                }
306                let _ = responder.send(&chunk);
307            }
308            fsandbox::DictionaryKeysIteratorRequest::_UnknownMethod { ordinal, .. } => {
309                warn!(ordinal:%; "Unknown DictionaryKeysIterator request");
310            }
311        }
312    }
313}
314
315async fn serve_dictionary_enumerate_iterator(
316    store: Weak<Store>,
317    mut items: impl Iterator<Item = (Key, Result<Capability, ()>)>,
318    mut stream: fsandbox::DictionaryEnumerateIteratorRequestStream,
319) {
320    while let Ok(Some(request)) = stream.try_next().await {
321        let Some(store) = store.upgrade() else {
322            return;
323        };
324        let mut store = store.lock().unwrap();
325        match request {
326            fsandbox::DictionaryEnumerateIteratorRequest::GetNext {
327                start_id,
328                limit,
329                responder,
330            } => {
331                let result = (|| {
332                    let mut next_id = start_id;
333                    let chunk = get_next_chunk(&*store, &mut items, &mut next_id, limit)?;
334                    let end_id = next_id;
335
336                    let chunk: Vec<_> = chunk
337                        .into_iter()
338                        .map(|(key, value)| {
339                            if let Some((capability, id)) = value {
340                                store.insert(id, capability);
341                                fsandbox::DictionaryOptionalItem {
342                                    key: key.into(),
343                                    value: Some(Box::new(fsandbox::WrappedCapabilityId { id })),
344                                }
345                            } else {
346                                fsandbox::DictionaryOptionalItem { key: key.into(), value: None }
347                            }
348                        })
349                        .collect();
350                    Ok((chunk, end_id))
351                })();
352                let err = result.is_err();
353                let _ = responder.send(result);
354                if err {
355                    return;
356                }
357            }
358            fsandbox::DictionaryEnumerateIteratorRequest::_UnknownMethod { ordinal, .. } => {
359                warn!(ordinal:%; "Unknown DictionaryEnumerateIterator request");
360            }
361        }
362    }
363}
364
365async fn serve_dictionary_drain_iterator(
366    store: Weak<Store>,
367    items: impl Iterator<Item = (Key, Capability)>,
368    mut stream: fsandbox::DictionaryDrainIteratorRequestStream,
369) {
370    // Transform iterator to be compatible with get_next_chunk()
371    let mut items = items.map(|(key, capability)| (key, Ok(capability)));
372    while let Ok(Some(request)) = stream.try_next().await {
373        let Some(store) = store.upgrade() else {
374            return;
375        };
376        let mut store = store.lock().unwrap();
377        match request {
378            fsandbox::DictionaryDrainIteratorRequest::GetNext { start_id, limit, responder } => {
379                let result = (|| {
380                    let mut next_id = start_id;
381                    let chunk = get_next_chunk(&*store, &mut items, &mut next_id, limit)?;
382                    let end_id = next_id;
383
384                    let chunk: Vec<_> = chunk
385                        .into_iter()
386                        .map(|(key, value)| {
387                            let value = value.expect("unreachable: all values are present");
388                            let (capability, id) = value;
389                            store.insert(id, capability);
390                            fsandbox::DictionaryItem { key: key.into(), value: id }
391                        })
392                        .collect();
393                    Ok((chunk, end_id))
394                })();
395                match result {
396                    Ok((chunk, id)) => {
397                        let _ = responder.send(Ok((&chunk[..], id)));
398                    }
399                    Err(e) => {
400                        let _ = responder.send(Err(e));
401                        return;
402                    }
403                }
404            }
405            fsandbox::DictionaryDrainIteratorRequest::_UnknownMethod { ordinal, .. } => {
406                warn!(ordinal:%; "Unknown DictionaryDrainIterator request");
407            }
408        }
409    }
410}
411
412fn get_next_chunk(
413    store: &HashMap<u64, Capability>,
414    items: &mut impl Iterator<Item = (Key, Result<Capability, ()>)>,
415    next_id: &mut u64,
416    limit: u32,
417) -> Result<Vec<(Key, Option<(Capability, fsandbox::CapabilityId)>)>, fsandbox::CapabilityStoreError>
418{
419    if limit == 0 || limit > fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK {
420        return Err(fsandbox::CapabilityStoreError::InvalidArgs);
421    }
422
423    let mut chunk = vec![];
424    for _ in 0..limit {
425        match items.next() {
426            Some((key, value)) => {
427                let value = match value {
428                    Ok(value) => {
429                        let id = *next_id;
430                        // Pre-flight check: if an id is unavailable, return early
431                        // and don't make any changes to the store.
432                        if store.contains_key(&id) {
433                            return Err(fsandbox::CapabilityStoreError::IdAlreadyExists);
434                        }
435                        *next_id += 1;
436                        Some((value, id))
437                    }
438                    Err(_) => None,
439                };
440                chunk.push((key, value));
441            }
442            None => break,
443        }
444    }
445    Ok(chunk)
446}
447
448fn get_connector(
449    store: &HashMap<u64, Capability>,
450    id: u64,
451) -> Result<&Connector, fsandbox::CapabilityStoreError> {
452    let conn = store.get(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
453    if let Capability::Connector(conn) = conn {
454        Ok(conn)
455    } else {
456        Err(fsandbox::CapabilityStoreError::WrongType)
457    }
458}
459
460fn get_dir_connector(
461    store: &HashMap<u64, Capability>,
462    id: u64,
463) -> Result<&DirConnector, fsandbox::CapabilityStoreError> {
464    let conn = store.get(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
465    if let Capability::DirConnector(conn) = conn {
466        Ok(conn)
467    } else {
468        Err(fsandbox::CapabilityStoreError::WrongType)
469    }
470}
471
472fn get_dictionary(
473    store: &HashMap<u64, Capability>,
474    id: u64,
475) -> Result<&Dictionary, fsandbox::CapabilityStoreError> {
476    let dict = store.get(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
477    if let Capability::Dictionary(dict) = dict {
478        Ok(dict)
479    } else {
480        Err(fsandbox::CapabilityStoreError::WrongType)
481    }
482}
483
484fn insert_capability(
485    store: &mut HashMap<u64, Capability>,
486    id: u64,
487    cap: Capability,
488) -> Result<(), fsandbox::CapabilityStoreError> {
489    match store.entry(id) {
490        Entry::Occupied(_) => Err(fsandbox::CapabilityStoreError::IdAlreadyExists),
491        Entry::Vacant(entry) => {
492            entry.insert(cap);
493            Ok(())
494        }
495    }
496}
497
498async fn create_service_aggregate(
499    route_source: WeakInstanceToken,
500    sources: Vec<fsandbox::AggregateSource>,
501) -> Result<fsandbox::DirConnector, fsandbox::CapabilityStoreError> {
502    fn is_set<T>(val: &Option<Vec<T>>) -> bool {
503        val.as_ref().map(|v| !v.is_empty()).unwrap_or(false)
504    }
505    if sources.iter().any(|s| is_set(&s.source_instance_filter) || is_set(&s.renamed_instances)) {
506        // This is a renames aggregate
507        let dir_connectors_and_renames = sources
508            .into_iter()
509            .map(|s| {
510                let renames = process_renames(&s);
511                let sandbox_dir_connector =
512                    s.dir_connector.ok_or(fsandbox::CapabilityStoreError::InvalidArgs)?;
513                let dir_connector = DirConnector::try_from(sandbox_dir_connector)
514                    .map_err(|_| fsandbox::CapabilityStoreError::InvalidArgs)?;
515                Ok((dir_connector, renames))
516            })
517            .collect::<Result<Vec<_>, fsandbox::CapabilityStoreError>>()?;
518        let target_directory = Simple::new();
519        for (dir_connector, renames) in dir_connectors_and_renames.into_iter() {
520            for mapping in renames.into_iter() {
521                let source_path = Path::validate_and_split(mapping.source_name)
522                    .map_err(|_| fsandbox::CapabilityStoreError::InvalidArgs)?;
523                let target_path = Path::validate_and_split(mapping.target_name)
524                    .map_err(|_| fsandbox::CapabilityStoreError::InvalidArgs)?;
525                let dir_connector_as_dir_entry = dir_connector
526                    .clone()
527                    .try_into_directory_entry(ExecutionScope::new(), route_source.clone())
528                    .expect("this is infallible");
529                let sub_node = Arc::new(SubNode::new(
530                    dir_connector_as_dir_entry,
531                    source_path,
532                    fio::DirentType::Directory,
533                ));
534                let sub_dir_connector =
535                    DirConnector::from_directory_entry(sub_node, fio::PERM_READABLE);
536                let sub_dir_entry = sub_dir_connector
537                    .try_into_directory_entry(ExecutionScope::new(), route_source.clone())
538                    .expect("this is infallible");
539                target_directory
540                    .add_entry(target_path.as_str(), sub_dir_entry)
541                    .map_err(|_| fsandbox::CapabilityStoreError::InvalidArgs)?;
542            }
543        }
544        return Ok(DirConnector::from_directory_entry(target_directory, fio::PERM_READABLE).into());
545    }
546    // Anonymous aggregates are currently unsupported.
547    Err(fsandbox::CapabilityStoreError::InvalidArgs)
548}
549
550fn process_renames(source: &fsandbox::AggregateSource) -> Vec<fdecl::NameMapping> {
551    match (&source.source_instance_filter, &source.renamed_instances) {
552        (Some(filter), Some(renames)) if !renames.is_empty() && !filter.is_empty() => renames
553            .iter()
554            .filter(|mapping| filter.contains(&mapping.target_name))
555            .cloned()
556            .collect(),
557        (Some(filter), _) if !filter.is_empty() => filter
558            .iter()
559            .map(|name| fdecl::NameMapping { source_name: name.clone(), target_name: name.clone() })
560            .collect(),
561        (_, Some(renames)) if !renames.is_empty() => renames.clone(),
562        _ => vec![],
563    }
564}
565
566#[cfg(test)]
567mod tests {
568    use super::*;
569    use crate::{Data, DirConnectable, Handle};
570    use assert_matches::assert_matches;
571    use fidl::endpoints::{ServerEnd, create_endpoints};
572    use fidl::{AsHandleRef, endpoints};
573    use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender, unbounded};
574
575    #[fuchsia::test]
576    async fn import_export() {
577        let (store, stream) =
578            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
579        let _server = fasync::Task::spawn(async move {
580            let receiver_scope = fasync::Scope::new();
581            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
582        });
583
584        let (ch, _) = fidl::Channel::create();
585        let handle = ch.into_handle();
586        let handle_koid = handle.as_handle_ref().koid().unwrap();
587        let cap1 = Capability::Handle(Handle::new(handle));
588        let cap2 = Capability::Data(Data::Int64(42));
589        store
590            .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
591            .await
592            .unwrap()
593            .unwrap();
594        store
595            .import(2, cap2.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
596            .await
597            .unwrap()
598            .unwrap();
599
600        let cap1 = store.export(1).await.unwrap().unwrap();
601        let cap2 = store.export(2).await.unwrap().unwrap();
602        assert_matches!(
603            cap1,
604            fsandbox::Capability::Handle(h) if h.as_handle_ref().koid().unwrap() == handle_koid
605        );
606        assert_matches!(
607            cap2,
608            fsandbox::Capability::Data(fsandbox::Data::Int64(i)) if i == 42
609        );
610    }
611
612    #[fuchsia::test]
613    async fn import_error() {
614        let (store, stream) =
615            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
616        let _server = fasync::Task::spawn(async move {
617            let receiver_scope = fasync::Scope::new();
618            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
619        });
620
621        let cap1 = Capability::Data(Data::Int64(42));
622        store
623            .import(1, cap1.clone().into_fsandbox_capability(WeakInstanceToken::new_invalid()))
624            .await
625            .unwrap()
626            .unwrap();
627        assert_matches!(
628            store
629                .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
630                .await
631                .unwrap(),
632            Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
633        );
634
635        let (token, _) = fidl::EventPair::create();
636        let bad_connector = fsandbox::Capability::Connector(fsandbox::Connector { token });
637        assert_matches!(
638            store.import(2, bad_connector).await.unwrap(),
639            Err(fsandbox::CapabilityStoreError::BadCapability)
640        );
641    }
642
643    #[fuchsia::test]
644    async fn export_error() {
645        let (store, stream) =
646            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
647        let _server = fasync::Task::spawn(async move {
648            let receiver_scope = fasync::Scope::new();
649            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
650        });
651
652        let cap1 = Capability::Data(Data::Int64(42));
653        store
654            .import(1, cap1.clone().into_fsandbox_capability(WeakInstanceToken::new_invalid()))
655            .await
656            .unwrap()
657            .unwrap();
658
659        assert_matches!(
660            store.export(2).await.unwrap(),
661            Err(fsandbox::CapabilityStoreError::IdNotFound)
662        );
663    }
664
665    #[fuchsia::test]
666    async fn drop() {
667        let (store, stream) =
668            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
669        let _server = fasync::Task::spawn(async move {
670            let receiver_scope = fasync::Scope::new();
671            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
672        });
673
674        let (ch, _) = fidl::Channel::create();
675        let handle = ch.into_handle();
676        let handle_koid = handle.as_handle_ref().koid().unwrap();
677        let cap1 = Capability::Handle(Handle::new(handle));
678        let cap2 = Capability::Data(Data::Int64(42));
679        store
680            .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
681            .await
682            .unwrap()
683            .unwrap();
684        store
685            .import(2, cap2.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
686            .await
687            .unwrap()
688            .unwrap();
689
690        // Drop capability 2. It's no longer in the store.
691        store.drop(2).await.unwrap().unwrap();
692        assert_matches!(
693            store.export(1).await.unwrap(),
694            Ok(fsandbox::Capability::Handle(h)) if h.as_handle_ref().koid().unwrap() == handle_koid
695        );
696        assert_matches!(
697            store.export(2).await.unwrap(),
698            Err(fsandbox::CapabilityStoreError::IdNotFound)
699        );
700
701        // Id 2 can be reused.
702        let cap2 = Capability::Data(Data::Int64(84));
703        store
704            .import(2, cap2.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
705            .await
706            .unwrap()
707            .unwrap();
708        assert_matches!(
709            store.export(2).await.unwrap(),
710            Ok(fsandbox::Capability::Data(fsandbox::Data::Int64(i))) if i == 84
711        );
712    }
713
714    #[fuchsia::test]
715    async fn drop_error() {
716        let (store, stream) =
717            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
718        let _server = fasync::Task::spawn(async move {
719            let receiver_scope = fasync::Scope::new();
720            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
721        });
722
723        let cap1 = Capability::Data(Data::Int64(42));
724        store
725            .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
726            .await
727            .unwrap()
728            .unwrap();
729
730        assert_matches!(
731            store.drop(2).await.unwrap(),
732            Err(fsandbox::CapabilityStoreError::IdNotFound)
733        );
734    }
735
736    #[fuchsia::test]
737    async fn duplicate() {
738        let (store, stream) =
739            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
740        let _server = fasync::Task::spawn(async move {
741            let receiver_scope = fasync::Scope::new();
742            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
743        });
744
745        let (event, _) = fidl::EventPair::create();
746        let handle = event.into_handle();
747        let handle_koid = handle.as_handle_ref().koid().unwrap();
748        let cap1 = Capability::Handle(Handle::new(handle));
749        store
750            .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
751            .await
752            .unwrap()
753            .unwrap();
754        store.duplicate(1, 2).await.unwrap().unwrap();
755        store.drop(1).await.unwrap().unwrap();
756
757        let cap1 = store.export(2).await.unwrap().unwrap();
758        assert_matches!(
759            cap1,
760            fsandbox::Capability::Handle(h) if h.as_handle_ref().koid().unwrap() == handle_koid
761        );
762    }
763
764    #[derive(Debug)]
765    struct TestDirConnector {
766        sender:
767            UnboundedSender<(ServerEnd<fio::DirectoryMarker>, RelativePath, Option<fio::Flags>)>,
768    }
769
770    impl DirConnectable for TestDirConnector {
771        fn maximum_flags(&self) -> fio::Flags {
772            fio::PERM_READABLE
773        }
774
775        fn send(
776            &self,
777            dir: ServerEnd<fio::DirectoryMarker>,
778            subdir: RelativePath,
779            flags: Option<fio::Flags>,
780        ) -> Result<(), ()> {
781            self.sender.unbounded_send((dir, subdir, flags)).unwrap();
782            Ok(())
783        }
784    }
785
786    impl TestDirConnector {
787        fn new() -> (
788            DirConnector,
789            UnboundedReceiver<(ServerEnd<fio::DirectoryMarker>, RelativePath, Option<fio::Flags>)>,
790        ) {
791            let (sender, receiver) = unbounded();
792            (DirConnector::new_sendable(Self { sender }), receiver)
793        }
794    }
795
796    #[fuchsia::test]
797    async fn rename_aggregate_with_one_source() {
798        let (source_dir_connector, mut source_dir_receiver) = TestDirConnector::new();
799        let sources = vec![fsandbox::AggregateSource {
800            dir_connector: Some(source_dir_connector.into()),
801            renamed_instances: Some(vec![fdecl::NameMapping {
802                source_name: "foo".to_string(),
803                target_name: "bar".to_string(),
804            }]),
805            ..Default::default()
806        }];
807        let fidl_aggregate = create_service_aggregate(WeakInstanceToken::new_invalid(), sources)
808            .await
809            .expect("failed to create service aggregate");
810        let aggregate = DirConnector::try_from(fidl_aggregate).expect("invalid dir connector");
811
812        let (client_end, server_end) = create_endpoints::<fio::DirectoryMarker>();
813        aggregate.send(server_end, RelativePath::new("bar").unwrap(), None).unwrap();
814        let (received_server_end, path, flags) = source_dir_receiver.try_next().unwrap().unwrap();
815        assert_eq!(
816            client_end.as_handle_ref().basic_info().unwrap().koid,
817            received_server_end.as_handle_ref().basic_info().unwrap().related_koid
818        );
819        assert_eq!(path, RelativePath::new("foo").unwrap());
820        assert_eq!(flags, Some(fio::PERM_READABLE));
821    }
822
823    #[fuchsia::test]
824    async fn rename_aggregate_with_two_sources() {
825        let (source_dir_connector_1, source_dir_receiver_1) = TestDirConnector::new();
826        let (source_dir_connector_2, source_dir_receiver_2) = TestDirConnector::new();
827        let sources = vec![
828            fsandbox::AggregateSource {
829                dir_connector: Some(source_dir_connector_1.into()),
830                renamed_instances: Some(vec![fdecl::NameMapping {
831                    source_name: "foo".to_string(),
832                    target_name: "bar".to_string(),
833                }]),
834                ..Default::default()
835            },
836            fsandbox::AggregateSource {
837                dir_connector: Some(source_dir_connector_2.into()),
838                renamed_instances: Some(vec![fdecl::NameMapping {
839                    source_name: "foo".to_string(),
840                    target_name: "baz".to_string(),
841                }]),
842                ..Default::default()
843            },
844        ];
845        let fidl_aggregate = create_service_aggregate(WeakInstanceToken::new_invalid(), sources)
846            .await
847            .expect("failed to create service aggregate");
848        let aggregate = DirConnector::try_from(fidl_aggregate).expect("invalid dir connector");
849
850        for (mut receiver, name) in [(source_dir_receiver_1, "bar"), (source_dir_receiver_2, "baz")]
851        {
852            let (client_end, server_end) = create_endpoints::<fio::DirectoryMarker>();
853            aggregate.send(server_end, RelativePath::new(name).unwrap(), None).unwrap();
854            let (received_server_end, path, flags) = receiver.try_next().unwrap().unwrap();
855            assert_eq!(
856                client_end.as_handle_ref().basic_info().unwrap().koid,
857                received_server_end.as_handle_ref().basic_info().unwrap().related_koid
858            );
859            assert_eq!(path, RelativePath::new("foo").unwrap());
860            assert_eq!(flags, Some(fio::PERM_READABLE));
861        }
862    }
863
864    #[fuchsia::test]
865    async fn rename_and_filtering_aggregate() {
866        let (source_dir_connector_1, source_dir_receiver_1) = TestDirConnector::new();
867        let (source_dir_connector_2, source_dir_receiver_2) = TestDirConnector::new();
868        let sources = vec![
869            fsandbox::AggregateSource {
870                dir_connector: Some(source_dir_connector_1.into()),
871                renamed_instances: Some(vec![fdecl::NameMapping {
872                    source_name: "foo".to_string(),
873                    target_name: "bar".to_string(),
874                }]),
875                ..Default::default()
876            },
877            fsandbox::AggregateSource {
878                dir_connector: Some(source_dir_connector_2.into()),
879                source_instance_filter: Some(vec!["foo".to_string()]),
880                ..Default::default()
881            },
882        ];
883        let fidl_aggregate = create_service_aggregate(WeakInstanceToken::new_invalid(), sources)
884            .await
885            .expect("failed to create service aggregate");
886        let aggregate = DirConnector::try_from(fidl_aggregate).expect("invalid dir connector");
887
888        for (mut receiver, name) in [(source_dir_receiver_1, "bar"), (source_dir_receiver_2, "foo")]
889        {
890            let (client_end, server_end) = create_endpoints::<fio::DirectoryMarker>();
891            aggregate.send(server_end, RelativePath::new(name).unwrap(), None).unwrap();
892            let (received_server_end, path, flags) = receiver.try_next().unwrap().unwrap();
893            assert_eq!(
894                client_end.as_handle_ref().basic_info().unwrap().koid,
895                received_server_end.as_handle_ref().basic_info().unwrap().related_koid
896            );
897            assert_eq!(path, RelativePath::new("foo").unwrap());
898            assert_eq!(flags, Some(fio::PERM_READABLE));
899        }
900    }
901}