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