sandbox/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::dict::Key;
6use crate::fidl::{IntoFsandboxCapability, RemotableCapability, registry};
7use crate::{Capability, Connector, Dict, DirConnector, Message, WeakInstanceToken};
8use cm_types::RelativePath;
9use fidl::handle::Signals;
10use futures::{FutureExt, TryStreamExt};
11use log::warn;
12use std::collections::HashMap;
13use std::collections::hash_map::Entry;
14use std::sync::{self, Arc, Weak};
15use vfs::ExecutionScope;
16use vfs::directory::entry::SubNode;
17use vfs::directory::helper::DirectlyMutable;
18use vfs::directory::simple::Simple;
19use vfs::path::Path;
20use {
21    fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_component_sandbox as fsandbox,
22    fidl_fuchsia_io as fio, fuchsia_async as fasync,
23};
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 = store.get(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
46                    let cap = cap
47                        .try_clone()
48                        .map_err(|_| fsandbox::CapabilityStoreError::NotDuplicatable)?;
49                    insert_capability(&mut store, dest_id, cap)
50                })();
51                responder.send(result)?;
52            }
53            fsandbox::CapabilityStoreRequest::Drop { id, responder } => {
54                let result =
55                    store.remove(&id).map(|_| ()).ok_or(fsandbox::CapabilityStoreError::IdNotFound);
56                responder.send(result)?;
57            }
58            fsandbox::CapabilityStoreRequest::Export { id, responder } => {
59                let result = (|| {
60                    let cap =
61                        store.remove(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
62                    Ok(cap.into_fsandbox_capability(token.clone()))
63                })();
64                responder.send(result)?;
65            }
66            fsandbox::CapabilityStoreRequest::Import { id, capability, responder } => {
67                let result = (|| {
68                    let capability = capability
69                        .try_into()
70                        .map_err(|_| fsandbox::CapabilityStoreError::BadCapability)?;
71                    insert_capability(&mut store, id, capability)
72                })();
73                responder.send(result)?;
74            }
75            fsandbox::CapabilityStoreRequest::ConnectorCreate { id, receiver, responder } => {
76                let result = (|| {
77                    let connector = Connector::new_with_fidl_receiver(receiver, receiver_scope);
78                    insert_capability(&mut store, id, Capability::Connector(connector))
79                })();
80                responder.send(result)?;
81            }
82            fsandbox::CapabilityStoreRequest::ConnectorOpen { id, server_end, responder } => {
83                let result = (|| {
84                    let this = get_connector(&store, id)?;
85                    let _ = this.send(Message { channel: server_end });
86                    Ok(())
87                })();
88                responder.send(result)?;
89            }
90            fsandbox::CapabilityStoreRequest::DirConnectorCreate { id, receiver, responder } => {
91                let result = (|| {
92                    let connector = DirConnector::new_with_fidl_receiver(receiver, receiver_scope);
93                    insert_capability(&mut store, id, Capability::DirConnector(connector))
94                })();
95                responder.send(result)?;
96            }
97            fsandbox::CapabilityStoreRequest::DirConnectorOpen { payload, responder } => {
98                let result = (|| {
99                    let Some(id) = payload.id else {
100                        return Err(fsandbox::CapabilityStoreError::InvalidArgs);
101                    };
102                    let Some(server_end) = payload.server_end else {
103                        return Err(fsandbox::CapabilityStoreError::InvalidArgs);
104                    };
105                    let this = get_dir_connector(&store, id)?;
106                    let path = payload
107                        .path
108                        .map(RelativePath::new)
109                        .transpose()
110                        .map_err(|_| fsandbox::CapabilityStoreError::InvalidArgs)?
111                        .unwrap_or_else(|| RelativePath::dot());
112                    let _ = this.send(server_end, path, payload.flags);
113                    Ok(())
114                })();
115                responder.send(result)?;
116            }
117            fsandbox::CapabilityStoreRequest::DictionaryCreate { id, responder } => {
118                let result = insert_capability(&mut store, id, Capability::Dictionary(Dict::new()));
119                responder.send(result)?;
120            }
121            fsandbox::CapabilityStoreRequest::DictionaryLegacyImport {
122                id,
123                client_end,
124                responder,
125            } => {
126                let result = (|| {
127                    let capability = Dict::try_from(client_end)
128                        .map_err(|_| fsandbox::CapabilityStoreError::BadCapability)?
129                        .into();
130                    insert_capability(&mut store, id, capability)
131                })();
132                responder.send(result)?;
133            }
134            fsandbox::CapabilityStoreRequest::DictionaryLegacyExport {
135                id,
136                server_end,
137                responder,
138            } => {
139                let result = (|| {
140                    let cap =
141                        store.remove(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
142                    let Capability::Dictionary(_) = &cap else {
143                        return Err(fsandbox::CapabilityStoreError::WrongType);
144                    };
145                    let koid = server_end.as_handle_ref().basic_info().unwrap().related_koid;
146                    registry::insert(
147                        cap,
148                        koid,
149                        fasync::OnSignals::new(server_end, Signals::OBJECT_PEER_CLOSED).map(|_| ()),
150                    );
151                    Ok(())
152                })();
153                responder.send(result)?
154            }
155            fsandbox::CapabilityStoreRequest::DictionaryInsert { id, item, responder } => {
156                let result = (|| {
157                    let this = get_dictionary(&store, id)?;
158                    let this = this.clone();
159                    let key =
160                        item.key.parse().map_err(|_| fsandbox::CapabilityStoreError::InvalidKey)?;
161                    let value = store
162                        .remove(&item.value)
163                        .ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
164                    this.insert(key, value)
165                })();
166                responder.send(result)?;
167            }
168            fsandbox::CapabilityStoreRequest::DictionaryGet { id, key, dest_id, responder } => {
169                let result = (|| {
170                    let this = get_dictionary(&store, id)?;
171                    let key =
172                        Key::new(key).map_err(|_| fsandbox::CapabilityStoreError::InvalidKey)?;
173                    let cap = match this.get(&key) {
174                        Ok(Some(cap)) => Ok(cap),
175                        Ok(None) => {
176                            this.not_found(key.as_str());
177                            Err(fsandbox::CapabilityStoreError::ItemNotFound)
178                        }
179                        Err(()) => Err(fsandbox::CapabilityStoreError::NotDuplicatable),
180                    }?;
181                    insert_capability(&mut store, dest_id, cap)
182                })();
183                responder.send(result)?;
184            }
185            fsandbox::CapabilityStoreRequest::DictionaryRemove { id, key, dest_id, responder } => {
186                let result = (|| {
187                    let this = get_dictionary(&store, id)?;
188                    let key =
189                        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
214                        .shallow_copy()
215                        .map_err(|_| fsandbox::CapabilityStoreError::NotDuplicatable)?;
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.keys();
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();
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 = Key>,
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<&Dict, 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};
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.into());
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(
626                1,
627                cap1.try_clone()
628                    .unwrap()
629                    .into_fsandbox_capability(WeakInstanceToken::new_invalid()),
630            )
631            .await
632            .unwrap()
633            .unwrap();
634        assert_matches!(
635            store
636                .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
637                .await
638                .unwrap(),
639            Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
640        );
641
642        let (token, _) = fidl::EventPair::create();
643        let bad_connector = fsandbox::Capability::Connector(fsandbox::Connector { token });
644        assert_matches!(
645            store.import(2, bad_connector).await.unwrap(),
646            Err(fsandbox::CapabilityStoreError::BadCapability)
647        );
648    }
649
650    #[fuchsia::test]
651    async fn export_error() {
652        let (store, stream) =
653            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
654        let _server = fasync::Task::spawn(async move {
655            let receiver_scope = fasync::Scope::new();
656            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
657        });
658
659        let cap1 = Capability::Data(Data::Int64(42));
660        store
661            .import(
662                1,
663                cap1.try_clone()
664                    .unwrap()
665                    .into_fsandbox_capability(WeakInstanceToken::new_invalid()),
666            )
667            .await
668            .unwrap()
669            .unwrap();
670
671        assert_matches!(
672            store.export(2).await.unwrap(),
673            Err(fsandbox::CapabilityStoreError::IdNotFound)
674        );
675    }
676
677    #[fuchsia::test]
678    async fn drop() {
679        let (store, stream) =
680            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
681        let _server = fasync::Task::spawn(async move {
682            let receiver_scope = fasync::Scope::new();
683            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
684        });
685
686        let (ch, _) = fidl::Channel::create();
687        let handle = ch.into_handle();
688        let handle_koid = handle.as_handle_ref().koid().unwrap();
689        let cap1 = Capability::Handle(handle.into());
690        let cap2 = Capability::Data(Data::Int64(42));
691        store
692            .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
693            .await
694            .unwrap()
695            .unwrap();
696        store
697            .import(2, cap2.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
698            .await
699            .unwrap()
700            .unwrap();
701
702        // Drop capability 2. It's no longer in the store.
703        store.drop(2).await.unwrap().unwrap();
704        assert_matches!(
705            store.export(1).await.unwrap(),
706            Ok(fsandbox::Capability::Handle(h)) if h.as_handle_ref().koid().unwrap() == handle_koid
707        );
708        assert_matches!(
709            store.export(2).await.unwrap(),
710            Err(fsandbox::CapabilityStoreError::IdNotFound)
711        );
712
713        // Id 2 can be reused.
714        let cap2 = Capability::Data(Data::Int64(84));
715        store
716            .import(2, cap2.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
717            .await
718            .unwrap()
719            .unwrap();
720        assert_matches!(
721            store.export(2).await.unwrap(),
722            Ok(fsandbox::Capability::Data(fsandbox::Data::Int64(i))) if i == 84
723        );
724    }
725
726    #[fuchsia::test]
727    async fn drop_error() {
728        let (store, stream) =
729            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
730        let _server = fasync::Task::spawn(async move {
731            let receiver_scope = fasync::Scope::new();
732            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
733        });
734
735        let cap1 = Capability::Data(Data::Int64(42));
736        store
737            .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
738            .await
739            .unwrap()
740            .unwrap();
741
742        assert_matches!(
743            store.drop(2).await.unwrap(),
744            Err(fsandbox::CapabilityStoreError::IdNotFound)
745        );
746    }
747
748    #[fuchsia::test]
749    async fn duplicate() {
750        let (store, stream) =
751            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
752        let _server = fasync::Task::spawn(async move {
753            let receiver_scope = fasync::Scope::new();
754            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
755        });
756
757        let (event, _) = fidl::EventPair::create();
758        let handle = event.into_handle();
759        let handle_koid = handle.as_handle_ref().koid().unwrap();
760        let cap1 = Capability::Handle(handle.into());
761        store
762            .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
763            .await
764            .unwrap()
765            .unwrap();
766        store.duplicate(1, 2).await.unwrap().unwrap();
767        store.drop(1).await.unwrap().unwrap();
768
769        let cap1 = store.export(2).await.unwrap().unwrap();
770        assert_matches!(
771            cap1,
772            fsandbox::Capability::Handle(h) if h.as_handle_ref().koid().unwrap() == handle_koid
773        );
774    }
775
776    #[fuchsia::test]
777    async fn duplicate_error() {
778        let (store, stream) =
779            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
780        let _server = fasync::Task::spawn(async move {
781            let receiver_scope = fasync::Scope::new();
782            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
783        });
784
785        assert_matches!(
786            store.duplicate(1, 2).await.unwrap(),
787            Err(fsandbox::CapabilityStoreError::IdNotFound)
788        );
789
790        let cap1 = Capability::Data(Data::Int64(42));
791        let cap2 = Capability::Data(Data::Int64(84));
792        store
793            .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
794            .await
795            .unwrap()
796            .unwrap();
797        store
798            .import(2, cap2.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
799            .await
800            .unwrap()
801            .unwrap();
802        assert_matches!(
803            store.duplicate(1, 2).await.unwrap(),
804            Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
805        );
806
807        // Channels do not support duplication.
808        let (ch, _) = fidl::Channel::create();
809        let handle = ch.into_handle();
810        let cap1 = Capability::Handle(handle.into());
811        store
812            .import(3, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
813            .await
814            .unwrap()
815            .unwrap();
816        assert_matches!(
817            store.duplicate(3, 4).await.unwrap(),
818            Err(fsandbox::CapabilityStoreError::NotDuplicatable)
819        );
820    }
821
822    #[derive(Debug)]
823    struct TestDirConnector {
824        sender:
825            UnboundedSender<(ServerEnd<fio::DirectoryMarker>, RelativePath, Option<fio::Flags>)>,
826    }
827
828    impl DirConnectable for TestDirConnector {
829        fn maximum_flags(&self) -> fio::Flags {
830            fio::PERM_READABLE
831        }
832
833        fn send(
834            &self,
835            dir: ServerEnd<fio::DirectoryMarker>,
836            subdir: RelativePath,
837            flags: Option<fio::Flags>,
838        ) -> Result<(), ()> {
839            self.sender.unbounded_send((dir, subdir, flags)).unwrap();
840            Ok(())
841        }
842    }
843
844    impl TestDirConnector {
845        fn new() -> (
846            DirConnector,
847            UnboundedReceiver<(ServerEnd<fio::DirectoryMarker>, RelativePath, Option<fio::Flags>)>,
848        ) {
849            let (sender, receiver) = unbounded();
850            (DirConnector::new_sendable(Self { sender }), receiver)
851        }
852    }
853
854    #[fuchsia::test]
855    async fn rename_aggregate_with_one_source() {
856        let (source_dir_connector, mut source_dir_receiver) = TestDirConnector::new();
857        let sources = vec![fsandbox::AggregateSource {
858            dir_connector: Some(source_dir_connector.into()),
859            renamed_instances: Some(vec![fdecl::NameMapping {
860                source_name: "foo".to_string(),
861                target_name: "bar".to_string(),
862            }]),
863            ..Default::default()
864        }];
865        let fidl_aggregate = create_service_aggregate(WeakInstanceToken::new_invalid(), sources)
866            .await
867            .expect("failed to create service aggregate");
868        let aggregate = DirConnector::try_from(fidl_aggregate).expect("invalid dir connector");
869
870        let (client_end, server_end) = create_endpoints::<fio::DirectoryMarker>();
871        aggregate.send(server_end, RelativePath::new("bar").unwrap(), None).unwrap();
872        let (received_server_end, path, flags) = source_dir_receiver.try_next().unwrap().unwrap();
873        assert_eq!(
874            client_end.as_handle_ref().basic_info().unwrap().koid,
875            received_server_end.as_handle_ref().basic_info().unwrap().related_koid
876        );
877        assert_eq!(path, RelativePath::new("foo").unwrap());
878        assert_eq!(flags, Some(fio::PERM_READABLE));
879    }
880
881    #[fuchsia::test]
882    async fn rename_aggregate_with_two_sources() {
883        let (source_dir_connector_1, source_dir_receiver_1) = TestDirConnector::new();
884        let (source_dir_connector_2, source_dir_receiver_2) = TestDirConnector::new();
885        let sources = vec![
886            fsandbox::AggregateSource {
887                dir_connector: Some(source_dir_connector_1.into()),
888                renamed_instances: Some(vec![fdecl::NameMapping {
889                    source_name: "foo".to_string(),
890                    target_name: "bar".to_string(),
891                }]),
892                ..Default::default()
893            },
894            fsandbox::AggregateSource {
895                dir_connector: Some(source_dir_connector_2.into()),
896                renamed_instances: Some(vec![fdecl::NameMapping {
897                    source_name: "foo".to_string(),
898                    target_name: "baz".to_string(),
899                }]),
900                ..Default::default()
901            },
902        ];
903        let fidl_aggregate = create_service_aggregate(WeakInstanceToken::new_invalid(), sources)
904            .await
905            .expect("failed to create service aggregate");
906        let aggregate = DirConnector::try_from(fidl_aggregate).expect("invalid dir connector");
907
908        for (mut receiver, name) in [(source_dir_receiver_1, "bar"), (source_dir_receiver_2, "baz")]
909        {
910            let (client_end, server_end) = create_endpoints::<fio::DirectoryMarker>();
911            aggregate.send(server_end, RelativePath::new(name).unwrap(), None).unwrap();
912            let (received_server_end, path, flags) = receiver.try_next().unwrap().unwrap();
913            assert_eq!(
914                client_end.as_handle_ref().basic_info().unwrap().koid,
915                received_server_end.as_handle_ref().basic_info().unwrap().related_koid
916            );
917            assert_eq!(path, RelativePath::new("foo").unwrap());
918            assert_eq!(flags, Some(fio::PERM_READABLE));
919        }
920    }
921
922    #[fuchsia::test]
923    async fn rename_and_filtering_aggregate() {
924        let (source_dir_connector_1, source_dir_receiver_1) = TestDirConnector::new();
925        let (source_dir_connector_2, source_dir_receiver_2) = TestDirConnector::new();
926        let sources = vec![
927            fsandbox::AggregateSource {
928                dir_connector: Some(source_dir_connector_1.into()),
929                renamed_instances: Some(vec![fdecl::NameMapping {
930                    source_name: "foo".to_string(),
931                    target_name: "bar".to_string(),
932                }]),
933                ..Default::default()
934            },
935            fsandbox::AggregateSource {
936                dir_connector: Some(source_dir_connector_2.into()),
937                source_instance_filter: Some(vec!["foo".to_string()]),
938                ..Default::default()
939            },
940        ];
941        let fidl_aggregate = create_service_aggregate(WeakInstanceToken::new_invalid(), sources)
942            .await
943            .expect("failed to create service aggregate");
944        let aggregate = DirConnector::try_from(fidl_aggregate).expect("invalid dir connector");
945
946        for (mut receiver, name) in [(source_dir_receiver_1, "bar"), (source_dir_receiver_2, "foo")]
947        {
948            let (client_end, server_end) = create_endpoints::<fio::DirectoryMarker>();
949            aggregate.send(server_end, RelativePath::new(name).unwrap(), None).unwrap();
950            let (received_server_end, path, flags) = receiver.try_next().unwrap().unwrap();
951            assert_eq!(
952                client_end.as_handle_ref().basic_info().unwrap().koid,
953                received_server_end.as_handle_ref().basic_info().unwrap().related_koid
954            );
955            assert_eq!(path, RelativePath::new("foo").unwrap());
956            assert_eq!(flags, Some(fio::PERM_READABLE));
957        }
958    }
959}