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, Message, WeakInstanceToken};
8use cm_types::RelativePath;
9use fidl::handle::Signals;
10use fidl_fuchsia_component_decl as fdecl;
11use fidl_fuchsia_component_sandbox as fsandbox;
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        let mut store = outer_store.lock().unwrap();
42        match request {
43            fsandbox::CapabilityStoreRequest::Duplicate { id, dest_id, responder } => {
44                let result = (|| {
45                    let cap =
46                        store.get(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?.clone();
47                    insert_capability(&mut store, dest_id, cap)
48                })();
49                responder.send(result)?;
50            }
51            fsandbox::CapabilityStoreRequest::Drop { id, responder } => {
52                let result =
53                    store.remove(&id).map(|_| ()).ok_or(fsandbox::CapabilityStoreError::IdNotFound);
54                responder.send(result)?;
55            }
56            fsandbox::CapabilityStoreRequest::Export { id, responder } => {
57                let result = (|| {
58                    let cap =
59                        store.remove(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
60                    Ok(cap.into_fsandbox_capability(token.clone()))
61                })();
62                responder.send(result)?;
63            }
64            fsandbox::CapabilityStoreRequest::Import { id, capability, responder } => {
65                let result = (|| {
66                    let capability = capability
67                        .try_into()
68                        .map_err(|_| fsandbox::CapabilityStoreError::BadCapability)?;
69                    insert_capability(&mut store, id, capability)
70                })();
71                responder.send(result)?;
72            }
73            fsandbox::CapabilityStoreRequest::ConnectorCreate { id, receiver, responder } => {
74                let result = (|| {
75                    let connector = Connector::new_with_fidl_receiver(receiver, receiver_scope);
76                    insert_capability(&mut store, id, Capability::Connector(connector))
77                })();
78                responder.send(result)?;
79            }
80            fsandbox::CapabilityStoreRequest::ConnectorOpen { id, server_end, responder } => {
81                let result = (|| {
82                    let this = get_connector(&store, id)?;
83                    let _ = this.send(Message { channel: server_end });
84                    Ok(())
85                })();
86                responder.send(result)?;
87            }
88            fsandbox::CapabilityStoreRequest::DirConnectorCreate { id, receiver, responder } => {
89                let result = (|| {
90                    let connector = DirConnector::new_with_fidl_receiver(receiver, receiver_scope);
91                    insert_capability(&mut store, id, Capability::DirConnector(connector))
92                })();
93                responder.send(result)?;
94            }
95            fsandbox::CapabilityStoreRequest::DirConnectorOpen { payload, responder } => {
96                let result = (|| {
97                    let Some(id) = payload.id else {
98                        return Err(fsandbox::CapabilityStoreError::InvalidArgs);
99                    };
100                    let Some(server_end) = payload.server_end else {
101                        return Err(fsandbox::CapabilityStoreError::InvalidArgs);
102                    };
103                    let this = get_dir_connector(&store, id)?;
104                    let path = payload
105                        .path
106                        .map(RelativePath::new)
107                        .transpose()
108                        .map_err(|_| fsandbox::CapabilityStoreError::InvalidArgs)?
109                        .unwrap_or_else(|| RelativePath::dot());
110                    let _ = this.send(server_end, path, payload.flags);
111                    Ok(())
112                })();
113                responder.send(result)?;
114            }
115            fsandbox::CapabilityStoreRequest::DictionaryCreate { id, responder } => {
116                let result =
117                    insert_capability(&mut store, id, Capability::Dictionary(Dictionary::new()));
118                responder.send(result)?;
119            }
120            fsandbox::CapabilityStoreRequest::DictionaryLegacyImport {
121                id,
122                client_end,
123                responder,
124            } => {
125                let result = (|| {
126                    let capability = Dictionary::try_from(client_end)
127                        .map_err(|_| fsandbox::CapabilityStoreError::BadCapability)?
128                        .into();
129                    insert_capability(&mut store, id, capability)
130                })();
131                responder.send(result)?;
132            }
133            fsandbox::CapabilityStoreRequest::DictionaryLegacyExport {
134                id,
135                server_end,
136                responder,
137            } => {
138                let result = (|| {
139                    let cap =
140                        store.remove(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
141                    let Capability::Dictionary(_) = &cap else {
142                        return Err(fsandbox::CapabilityStoreError::WrongType);
143                    };
144                    let koid = server_end.as_handle_ref().basic_info().unwrap().related_koid;
145                    registry::insert(
146                        cap,
147                        koid,
148                        fasync::OnSignals::new(server_end, Signals::OBJECT_PEER_CLOSED).map(|_| ()),
149                    );
150                    Ok(())
151                })();
152                responder.send(result)?
153            }
154            fsandbox::CapabilityStoreRequest::DictionaryInsert { id, item, responder } => {
155                let result = (|| {
156                    let this = get_dictionary(&store, id)?;
157                    let this = this.clone();
158                    let key =
159                        item.key.parse().map_err(|_| fsandbox::CapabilityStoreError::InvalidKey)?;
160                    let value = store
161                        .remove(&item.value)
162                        .ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
163                    this.insert(key, value)
164                })();
165                responder.send(result)?;
166            }
167            fsandbox::CapabilityStoreRequest::DictionaryGet { id, key, dest_id, responder } => {
168                let result = (|| {
169                    let this = get_dictionary(&store, id)?;
170                    let key =
171                        Key::new(key).map_err(|_| fsandbox::CapabilityStoreError::InvalidKey)?;
172                    let cap = match this.get(&key) {
173                        Some(cap) => Ok(cap),
174                        None => {
175                            this.not_found(key.as_str());
176                            Err(fsandbox::CapabilityStoreError::ItemNotFound)
177                        }
178                    }?;
179                    insert_capability(&mut store, dest_id, cap)
180                })();
181                responder.send(result)?;
182            }
183            fsandbox::CapabilityStoreRequest::DictionaryRemove { id, key, dest_id, responder } => {
184                let result = (|| {
185                    let this = get_dictionary(&store, id)?;
186                    let key =
187                        Key::new(key).map_err(|_| fsandbox::CapabilityStoreError::InvalidKey)?;
188                    // Check this before removing from the dictionary.
189                    if let Some(dest_id) = dest_id.as_ref() {
190                        if store.contains_key(&dest_id.id) {
191                            return Err(fsandbox::CapabilityStoreError::IdAlreadyExists);
192                        }
193                    }
194                    let cap = match this.remove(&key) {
195                        Some(cap) => Ok(cap.into()),
196                        None => {
197                            this.not_found(key.as_str());
198                            Err(fsandbox::CapabilityStoreError::ItemNotFound)
199                        }
200                    }?;
201                    if let Some(dest_id) = dest_id.as_ref() {
202                        store.insert(dest_id.id, cap);
203                    }
204                    Ok(())
205                })();
206                responder.send(result)?;
207            }
208            fsandbox::CapabilityStoreRequest::DictionaryCopy { id, dest_id, responder } => {
209                let result = (|| {
210                    let this = get_dictionary(&store, id)?;
211                    let dict = this
212                        .shallow_copy()
213                        .map_err(|_| fsandbox::CapabilityStoreError::NotDuplicatable)?;
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    }
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, HandleBased, 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}