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, 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 {fidl_fuchsia_component_sandbox as fsandbox, fuchsia_async as fasync};
17
18type Store = sync::Mutex<HashMap<u64, Capability>>;
19
20pub async fn serve_capability_store(
21    mut stream: fsandbox::CapabilityStoreRequestStream,
22    // Receiver tasks launched on behalf of a *Connector may need to outlive this
23    // `CapabilityStore`. For example, if a client creates a Connector, Export()s it right away,
24    // then drops the `CapabilityStore`, dropping the task at that point deliver a `PEER_CLOSED`
25    // to the client and make the Connector they just created unusable.
26    //
27    // We could simply detach() the Task instead, but fuchsia_async considers that holding the
28    // handle is considered better practice.
29    receiver_scope: &fasync::Scope,
30    token: WeakInstanceToken,
31) -> Result<(), fidl::Error> {
32    let outer_store: Arc<Store> = Arc::new(Store::new(Default::default()));
33    while let Some(request) = stream.try_next().await? {
34        let mut store = outer_store.lock().unwrap();
35        match request {
36            fsandbox::CapabilityStoreRequest::Duplicate { id, dest_id, responder } => {
37                let result = (|| {
38                    let cap = store.get(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
39                    let cap = cap
40                        .try_clone()
41                        .map_err(|_| fsandbox::CapabilityStoreError::NotDuplicatable)?;
42                    insert_capability(&mut store, dest_id, cap)
43                })();
44                responder.send(result)?;
45            }
46            fsandbox::CapabilityStoreRequest::Drop { id, responder } => {
47                let result =
48                    store.remove(&id).map(|_| ()).ok_or(fsandbox::CapabilityStoreError::IdNotFound);
49                responder.send(result)?;
50            }
51            fsandbox::CapabilityStoreRequest::Export { id, responder } => {
52                let result = (|| {
53                    let cap =
54                        store.remove(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
55                    Ok(cap.into_fsandbox_capability(token.clone()))
56                })();
57                responder.send(result)?;
58            }
59            fsandbox::CapabilityStoreRequest::Import { id, capability, responder } => {
60                let result = (|| {
61                    let capability = capability
62                        .try_into()
63                        .map_err(|_| fsandbox::CapabilityStoreError::BadCapability)?;
64                    insert_capability(&mut store, id, capability)
65                })();
66                responder.send(result)?;
67            }
68            fsandbox::CapabilityStoreRequest::ConnectorCreate { id, receiver, responder } => {
69                let result = (|| {
70                    let connector = Connector::new_with_fidl_receiver(receiver, receiver_scope);
71                    insert_capability(&mut store, id, Capability::Connector(connector))
72                })();
73                responder.send(result)?;
74            }
75            fsandbox::CapabilityStoreRequest::ConnectorOpen { id, server_end, responder } => {
76                let result = (|| {
77                    let this = get_connector(&store, id)?;
78                    let _ = this.send(Message { channel: server_end });
79                    Ok(())
80                })();
81                responder.send(result)?;
82            }
83            fsandbox::CapabilityStoreRequest::DirConnectorCreate { id, receiver, responder } => {
84                let result = (|| {
85                    let connector = DirConnector::new_with_fidl_receiver(receiver, receiver_scope);
86                    insert_capability(&mut store, id, Capability::DirConnector(connector))
87                })();
88                responder.send(result)?;
89            }
90            fsandbox::CapabilityStoreRequest::DirConnectorOpen { payload, responder } => {
91                let result = (|| {
92                    let Some(id) = payload.id else {
93                        return Err(fsandbox::CapabilityStoreError::InvalidArgs);
94                    };
95                    let Some(server_end) = payload.server_end else {
96                        return Err(fsandbox::CapabilityStoreError::InvalidArgs);
97                    };
98                    let this = get_dir_connector(&store, id)?;
99                    let path = payload
100                        .path
101                        .map(RelativePath::new)
102                        .transpose()
103                        .map_err(|_| fsandbox::CapabilityStoreError::InvalidArgs)?
104                        .unwrap_or_else(|| RelativePath::dot());
105                    let _ = this.send(server_end, path, payload.flags);
106                    Ok(())
107                })();
108                responder.send(result)?;
109            }
110            fsandbox::CapabilityStoreRequest::DictionaryCreate { id, responder } => {
111                let result = insert_capability(&mut store, id, Capability::Dictionary(Dict::new()));
112                responder.send(result)?;
113            }
114            fsandbox::CapabilityStoreRequest::DictionaryLegacyImport {
115                id,
116                client_end,
117                responder,
118            } => {
119                let result = (|| {
120                    let capability = Dict::try_from(client_end)
121                        .map_err(|_| fsandbox::CapabilityStoreError::BadCapability)?
122                        .into();
123                    insert_capability(&mut store, id, capability)
124                })();
125                responder.send(result)?;
126            }
127            fsandbox::CapabilityStoreRequest::DictionaryLegacyExport {
128                id,
129                server_end,
130                responder,
131            } => {
132                let result = (|| {
133                    let cap =
134                        store.remove(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
135                    let Capability::Dictionary(_) = &cap else {
136                        return Err(fsandbox::CapabilityStoreError::WrongType);
137                    };
138                    let koid = server_end.basic_info().unwrap().related_koid;
139                    registry::insert(
140                        cap,
141                        koid,
142                        fasync::OnSignals::new(server_end, Signals::OBJECT_PEER_CLOSED).map(|_| ()),
143                    );
144                    Ok(())
145                })();
146                responder.send(result)?
147            }
148            fsandbox::CapabilityStoreRequest::DictionaryInsert { id, item, responder } => {
149                let result = (|| {
150                    let this = get_dictionary(&store, id)?;
151                    let this = this.clone();
152                    let key =
153                        item.key.parse().map_err(|_| fsandbox::CapabilityStoreError::InvalidKey)?;
154                    let value = store
155                        .remove(&item.value)
156                        .ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
157                    this.insert(key, value)
158                })();
159                responder.send(result)?;
160            }
161            fsandbox::CapabilityStoreRequest::DictionaryGet { id, key, dest_id, responder } => {
162                let result = (|| {
163                    let this = get_dictionary(&store, id)?;
164                    let key =
165                        Key::new(key).map_err(|_| fsandbox::CapabilityStoreError::InvalidKey)?;
166                    let cap = match this.get(&key) {
167                        Ok(Some(cap)) => Ok(cap),
168                        Ok(None) => {
169                            this.not_found(key.as_str());
170                            Err(fsandbox::CapabilityStoreError::ItemNotFound)
171                        }
172                        Err(()) => Err(fsandbox::CapabilityStoreError::NotDuplicatable),
173                    }?;
174                    insert_capability(&mut store, dest_id, cap)
175                })();
176                responder.send(result)?;
177            }
178            fsandbox::CapabilityStoreRequest::DictionaryRemove { id, key, dest_id, responder } => {
179                let result = (|| {
180                    let this = get_dictionary(&store, id)?;
181                    let key =
182                        Key::new(key).map_err(|_| fsandbox::CapabilityStoreError::InvalidKey)?;
183                    // Check this before removing from the dictionary.
184                    if let Some(dest_id) = dest_id.as_ref() {
185                        if store.contains_key(&dest_id.id) {
186                            return Err(fsandbox::CapabilityStoreError::IdAlreadyExists);
187                        }
188                    }
189                    let cap = match this.remove(&key) {
190                        Some(cap) => Ok(cap.into()),
191                        None => {
192                            this.not_found(key.as_str());
193                            Err(fsandbox::CapabilityStoreError::ItemNotFound)
194                        }
195                    }?;
196                    if let Some(dest_id) = dest_id.as_ref() {
197                        store.insert(dest_id.id, cap);
198                    }
199                    Ok(())
200                })();
201                responder.send(result)?;
202            }
203            fsandbox::CapabilityStoreRequest::DictionaryCopy { id, dest_id, responder } => {
204                let result = (|| {
205                    let this = get_dictionary(&store, id)?;
206                    let dict = this
207                        .shallow_copy()
208                        .map_err(|_| fsandbox::CapabilityStoreError::NotDuplicatable)?;
209                    insert_capability(&mut store, dest_id, Capability::Dictionary(dict))
210                })();
211                responder.send(result)?
212            }
213            fsandbox::CapabilityStoreRequest::DictionaryKeys {
214                id,
215                iterator: server_end,
216                responder,
217            } => {
218                let result = (|| {
219                    let this = get_dictionary(&store, id)?;
220                    let keys = this.keys();
221                    let stream = server_end.into_stream();
222                    let mut this = this.lock();
223                    this.tasks().spawn(serve_dictionary_keys_iterator(keys, stream));
224                    Ok(())
225                })();
226                responder.send(result)?
227            }
228            fsandbox::CapabilityStoreRequest::DictionaryEnumerate {
229                id,
230                iterator: server_end,
231                responder,
232            } => {
233                let result = (|| {
234                    let this = get_dictionary(&store, id)?;
235                    let items = this.enumerate();
236                    let stream = server_end.into_stream();
237                    let mut this = this.lock();
238                    this.tasks().spawn(serve_dictionary_enumerate_iterator(
239                        Arc::downgrade(&outer_store),
240                        items,
241                        stream,
242                    ));
243                    Ok(())
244                })();
245                responder.send(result)?
246            }
247            fsandbox::CapabilityStoreRequest::DictionaryDrain {
248                id,
249                iterator: server_end,
250                responder,
251            } => {
252                let result = (|| {
253                    let this = get_dictionary(&store, id)?;
254                    // Take out entries, replacing with an empty BTreeMap.
255                    // They are dropped if the caller does not request an iterator.
256                    let items = this.drain();
257                    if let Some(server_end) = server_end {
258                        let stream = server_end.into_stream();
259                        let mut this = this.lock();
260                        this.tasks().spawn(serve_dictionary_drain_iterator(
261                            Arc::downgrade(&outer_store),
262                            items,
263                            stream,
264                        ));
265                    }
266                    Ok(())
267                })();
268                responder.send(result)?
269            }
270            fsandbox::CapabilityStoreRequest::_UnknownMethod { ordinal, .. } => {
271                warn!("Received unknown CapabilityStore request with ordinal {ordinal}");
272            }
273        }
274    }
275    Ok(())
276}
277
278async fn serve_dictionary_keys_iterator(
279    mut keys: impl Iterator<Item = Key>,
280    mut stream: fsandbox::DictionaryKeysIteratorRequestStream,
281) {
282    while let Ok(Some(request)) = stream.try_next().await {
283        match request {
284            fsandbox::DictionaryKeysIteratorRequest::GetNext { responder } => {
285                let mut chunk = vec![];
286                for _ in 0..fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK {
287                    match keys.next() {
288                        Some(key) => {
289                            chunk.push(key.into());
290                        }
291                        None => break,
292                    }
293                }
294                let _ = responder.send(&chunk);
295            }
296            fsandbox::DictionaryKeysIteratorRequest::_UnknownMethod { ordinal, .. } => {
297                warn!(ordinal:%; "Unknown DictionaryKeysIterator request");
298            }
299        }
300    }
301}
302
303async fn serve_dictionary_enumerate_iterator(
304    store: Weak<Store>,
305    mut items: impl Iterator<Item = (Key, Result<Capability, ()>)>,
306    mut stream: fsandbox::DictionaryEnumerateIteratorRequestStream,
307) {
308    while let Ok(Some(request)) = stream.try_next().await {
309        let Some(store) = store.upgrade() else {
310            return;
311        };
312        let mut store = store.lock().unwrap();
313        match request {
314            fsandbox::DictionaryEnumerateIteratorRequest::GetNext {
315                start_id,
316                limit,
317                responder,
318            } => {
319                let result = (|| {
320                    let mut next_id = start_id;
321                    let chunk = get_next_chunk(&*store, &mut items, &mut next_id, limit)?;
322                    let end_id = next_id;
323
324                    let chunk: Vec<_> = chunk
325                        .into_iter()
326                        .map(|(key, value)| {
327                            if let Some((capability, id)) = value {
328                                store.insert(id, capability);
329                                fsandbox::DictionaryOptionalItem {
330                                    key: key.into(),
331                                    value: Some(Box::new(fsandbox::WrappedCapabilityId { id })),
332                                }
333                            } else {
334                                fsandbox::DictionaryOptionalItem { key: key.into(), value: None }
335                            }
336                        })
337                        .collect();
338                    Ok((chunk, end_id))
339                })();
340                let err = result.is_err();
341                let _ = responder.send(result);
342                if err {
343                    return;
344                }
345            }
346            fsandbox::DictionaryEnumerateIteratorRequest::_UnknownMethod { ordinal, .. } => {
347                warn!(ordinal:%; "Unknown DictionaryEnumerateIterator request");
348            }
349        }
350    }
351}
352
353async fn serve_dictionary_drain_iterator(
354    store: Weak<Store>,
355    items: impl Iterator<Item = (Key, Capability)>,
356    mut stream: fsandbox::DictionaryDrainIteratorRequestStream,
357) {
358    // Transform iterator to be compatible with get_next_chunk()
359    let mut items = items.map(|(key, capability)| (key, Ok(capability)));
360    while let Ok(Some(request)) = stream.try_next().await {
361        let Some(store) = store.upgrade() else {
362            return;
363        };
364        let mut store = store.lock().unwrap();
365        match request {
366            fsandbox::DictionaryDrainIteratorRequest::GetNext { start_id, limit, responder } => {
367                let result = (|| {
368                    let mut next_id = start_id;
369                    let chunk = get_next_chunk(&*store, &mut items, &mut next_id, limit)?;
370                    let end_id = next_id;
371
372                    let chunk: Vec<_> = chunk
373                        .into_iter()
374                        .map(|(key, value)| {
375                            let value = value.expect("unreachable: all values are present");
376                            let (capability, id) = value;
377                            store.insert(id, capability);
378                            fsandbox::DictionaryItem { key: key.into(), value: id }
379                        })
380                        .collect();
381                    Ok((chunk, end_id))
382                })();
383                match result {
384                    Ok((chunk, id)) => {
385                        let _ = responder.send(Ok((&chunk[..], id)));
386                    }
387                    Err(e) => {
388                        let _ = responder.send(Err(e));
389                        return;
390                    }
391                }
392            }
393            fsandbox::DictionaryDrainIteratorRequest::_UnknownMethod { ordinal, .. } => {
394                warn!(ordinal:%; "Unknown DictionaryDrainIterator request");
395            }
396        }
397    }
398}
399
400fn get_next_chunk(
401    store: &HashMap<u64, Capability>,
402    items: &mut impl Iterator<Item = (Key, Result<Capability, ()>)>,
403    next_id: &mut u64,
404    limit: u32,
405) -> Result<Vec<(Key, Option<(Capability, fsandbox::CapabilityId)>)>, fsandbox::CapabilityStoreError>
406{
407    if limit == 0 || limit > fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK {
408        return Err(fsandbox::CapabilityStoreError::InvalidArgs);
409    }
410
411    let mut chunk = vec![];
412    for _ in 0..limit {
413        match items.next() {
414            Some((key, value)) => {
415                let value = match value {
416                    Ok(value) => {
417                        let id = *next_id;
418                        // Pre-flight check: if an id is unavailable, return early
419                        // and don't make any changes to the store.
420                        if store.contains_key(&id) {
421                            return Err(fsandbox::CapabilityStoreError::IdAlreadyExists);
422                        }
423                        *next_id += 1;
424                        Some((value, id))
425                    }
426                    Err(_) => None,
427                };
428                chunk.push((key, value));
429            }
430            None => break,
431        }
432    }
433    Ok(chunk)
434}
435
436fn get_connector(
437    store: &HashMap<u64, Capability>,
438    id: u64,
439) -> Result<&Connector, fsandbox::CapabilityStoreError> {
440    let conn = store.get(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
441    if let Capability::Connector(conn) = conn {
442        Ok(conn)
443    } else {
444        Err(fsandbox::CapabilityStoreError::WrongType)
445    }
446}
447
448fn get_dir_connector(
449    store: &HashMap<u64, Capability>,
450    id: u64,
451) -> Result<&DirConnector, fsandbox::CapabilityStoreError> {
452    let conn = store.get(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
453    if let Capability::DirConnector(conn) = conn {
454        Ok(conn)
455    } else {
456        Err(fsandbox::CapabilityStoreError::WrongType)
457    }
458}
459
460fn get_dictionary(
461    store: &HashMap<u64, Capability>,
462    id: u64,
463) -> Result<&Dict, fsandbox::CapabilityStoreError> {
464    let dict = store.get(&id).ok_or(fsandbox::CapabilityStoreError::IdNotFound)?;
465    if let Capability::Dictionary(dict) = dict {
466        Ok(dict)
467    } else {
468        Err(fsandbox::CapabilityStoreError::WrongType)
469    }
470}
471
472fn insert_capability(
473    store: &mut HashMap<u64, Capability>,
474    id: u64,
475    cap: Capability,
476) -> Result<(), fsandbox::CapabilityStoreError> {
477    match store.entry(id) {
478        Entry::Occupied(_) => Err(fsandbox::CapabilityStoreError::IdAlreadyExists),
479        Entry::Vacant(entry) => {
480            entry.insert(cap);
481            Ok(())
482        }
483    }
484}
485
486#[cfg(test)]
487mod tests {
488    use super::*;
489    use crate::Data;
490    use assert_matches::assert_matches;
491    use fidl::{HandleBased, endpoints};
492
493    #[fuchsia::test]
494    async fn import_export() {
495        let (store, stream) =
496            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
497        let _server = fasync::Task::spawn(async move {
498            let receiver_scope = fasync::Scope::new();
499            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
500        });
501
502        let (ch, _) = fidl::Channel::create();
503        let handle = ch.into_handle();
504        let handle_koid = handle.get_koid().unwrap();
505        let cap1 = Capability::Handle(handle.into());
506        let cap2 = Capability::Data(Data::Int64(42));
507        store
508            .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
509            .await
510            .unwrap()
511            .unwrap();
512        store
513            .import(2, cap2.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
514            .await
515            .unwrap()
516            .unwrap();
517
518        let cap1 = store.export(1).await.unwrap().unwrap();
519        let cap2 = store.export(2).await.unwrap().unwrap();
520        assert_matches!(
521            cap1,
522            fsandbox::Capability::Handle(h) if h.get_koid().unwrap() == handle_koid
523        );
524        assert_matches!(
525            cap2,
526            fsandbox::Capability::Data(fsandbox::Data::Int64(i)) if i == 42
527        );
528    }
529
530    #[fuchsia::test]
531    async fn import_error() {
532        let (store, stream) =
533            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
534        let _server = fasync::Task::spawn(async move {
535            let receiver_scope = fasync::Scope::new();
536            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
537        });
538
539        let cap1 = Capability::Data(Data::Int64(42));
540        store
541            .import(
542                1,
543                cap1.try_clone()
544                    .unwrap()
545                    .into_fsandbox_capability(WeakInstanceToken::new_invalid()),
546            )
547            .await
548            .unwrap()
549            .unwrap();
550        assert_matches!(
551            store
552                .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
553                .await
554                .unwrap(),
555            Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
556        );
557
558        let (token, _) = fidl::EventPair::create();
559        let bad_connector = fsandbox::Capability::Connector(fsandbox::Connector { token });
560        assert_matches!(
561            store.import(2, bad_connector).await.unwrap(),
562            Err(fsandbox::CapabilityStoreError::BadCapability)
563        );
564    }
565
566    #[fuchsia::test]
567    async fn export_error() {
568        let (store, stream) =
569            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
570        let _server = fasync::Task::spawn(async move {
571            let receiver_scope = fasync::Scope::new();
572            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
573        });
574
575        let cap1 = Capability::Data(Data::Int64(42));
576        store
577            .import(
578                1,
579                cap1.try_clone()
580                    .unwrap()
581                    .into_fsandbox_capability(WeakInstanceToken::new_invalid()),
582            )
583            .await
584            .unwrap()
585            .unwrap();
586
587        assert_matches!(
588            store.export(2).await.unwrap(),
589            Err(fsandbox::CapabilityStoreError::IdNotFound)
590        );
591    }
592
593    #[fuchsia::test]
594    async fn drop() {
595        let (store, stream) =
596            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
597        let _server = fasync::Task::spawn(async move {
598            let receiver_scope = fasync::Scope::new();
599            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
600        });
601
602        let (ch, _) = fidl::Channel::create();
603        let handle = ch.into_handle();
604        let handle_koid = handle.get_koid().unwrap();
605        let cap1 = Capability::Handle(handle.into());
606        let cap2 = Capability::Data(Data::Int64(42));
607        store
608            .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
609            .await
610            .unwrap()
611            .unwrap();
612        store
613            .import(2, cap2.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
614            .await
615            .unwrap()
616            .unwrap();
617
618        // Drop capability 2. It's no longer in the store.
619        store.drop(2).await.unwrap().unwrap();
620        assert_matches!(
621            store.export(1).await.unwrap(),
622            Ok(fsandbox::Capability::Handle(h)) if h.get_koid().unwrap() == handle_koid
623        );
624        assert_matches!(
625            store.export(2).await.unwrap(),
626            Err(fsandbox::CapabilityStoreError::IdNotFound)
627        );
628
629        // Id 2 can be reused.
630        let cap2 = Capability::Data(Data::Int64(84));
631        store
632            .import(2, cap2.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
633            .await
634            .unwrap()
635            .unwrap();
636        assert_matches!(
637            store.export(2).await.unwrap(),
638            Ok(fsandbox::Capability::Data(fsandbox::Data::Int64(i))) if i == 84
639        );
640    }
641
642    #[fuchsia::test]
643    async fn drop_error() {
644        let (store, stream) =
645            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
646        let _server = fasync::Task::spawn(async move {
647            let receiver_scope = fasync::Scope::new();
648            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
649        });
650
651        let cap1 = Capability::Data(Data::Int64(42));
652        store
653            .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
654            .await
655            .unwrap()
656            .unwrap();
657
658        assert_matches!(
659            store.drop(2).await.unwrap(),
660            Err(fsandbox::CapabilityStoreError::IdNotFound)
661        );
662    }
663
664    #[fuchsia::test]
665    async fn duplicate() {
666        let (store, stream) =
667            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
668        let _server = fasync::Task::spawn(async move {
669            let receiver_scope = fasync::Scope::new();
670            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
671        });
672
673        let (event, _) = fidl::EventPair::create();
674        let handle = event.into_handle();
675        let handle_koid = handle.get_koid().unwrap();
676        let cap1 = Capability::Handle(handle.into());
677        store
678            .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
679            .await
680            .unwrap()
681            .unwrap();
682        store.duplicate(1, 2).await.unwrap().unwrap();
683        store.drop(1).await.unwrap().unwrap();
684
685        let cap1 = store.export(2).await.unwrap().unwrap();
686        assert_matches!(
687            cap1,
688            fsandbox::Capability::Handle(h) if h.get_koid().unwrap() == handle_koid
689        );
690    }
691
692    #[fuchsia::test]
693    async fn duplicate_error() {
694        let (store, stream) =
695            endpoints::create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
696        let _server = fasync::Task::spawn(async move {
697            let receiver_scope = fasync::Scope::new();
698            serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
699        });
700
701        assert_matches!(
702            store.duplicate(1, 2).await.unwrap(),
703            Err(fsandbox::CapabilityStoreError::IdNotFound)
704        );
705
706        let cap1 = Capability::Data(Data::Int64(42));
707        let cap2 = Capability::Data(Data::Int64(84));
708        store
709            .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
710            .await
711            .unwrap()
712            .unwrap();
713        store
714            .import(2, cap2.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
715            .await
716            .unwrap()
717            .unwrap();
718        assert_matches!(
719            store.duplicate(1, 2).await.unwrap(),
720            Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
721        );
722
723        // Channels do not support duplication.
724        let (ch, _) = fidl::Channel::create();
725        let handle = ch.into_handle();
726        let cap1 = Capability::Handle(handle.into());
727        store
728            .import(3, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
729            .await
730            .unwrap()
731            .unwrap();
732        assert_matches!(
733            store.duplicate(3, 4).await.unwrap(),
734            Err(fsandbox::CapabilityStoreError::NotDuplicatable)
735        );
736    }
737}