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