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