1use fidl_fuchsia_component_sandbox as fsandbox;
11use fidl_fuchsia_io as fio;
12use fuchsia_component::client;
13use std::future::Future;
14use std::sync::Arc;
15use std::sync::atomic::{AtomicU64, Ordering};
16use thiserror::Error;
17
18pub trait Importable<'a>: Sized + Into<Capability> + TryFrom<Capability, Error = Error> {
21 type Ref: CapabilityRef<'a>;
23}
24
25#[derive(Error, Clone, Debug)]
27pub enum Error {
28 #[error("failed to connect to fuchsia.component.sandbox/CapabilityStore: {0}")]
30 FailedConnect(zx::Status),
31 #[error("wrong Capability type; got \"{got}\", want \"{want}\"")]
33 WrongType { got: &'static str, want: &'static str },
34 #[error("FIDL error {0}")]
36 Fidl(#[from] fidl::Error),
37 #[error("CapabilityStoreError {0:?}")]
39 CapabilityStore(fsandbox::CapabilityStoreError),
40}
41
42impl From<fsandbox::CapabilityStoreError> for Error {
43 fn from(value: fsandbox::CapabilityStoreError) -> Self {
44 Self::CapabilityStore(value)
45 }
46}
47
48#[derive(Clone, Default, Debug)]
53pub struct CapabilityIdGenerator {
54 next_id: Arc<AtomicU64>,
55}
56
57impl CapabilityIdGenerator {
58 pub fn new() -> Self {
60 Self::default()
61 }
62
63 pub fn next(&self) -> u64 {
65 self.range(1)
66 }
67
68 pub fn range(&self, size: u64) -> u64 {
71 self.next_id.fetch_add(size, Ordering::Relaxed)
72 }
73}
74
75#[derive(Clone, Debug)]
77pub struct CapabilityStore {
78 proxy: fsandbox::CapabilityStoreProxy,
79 id_gen: CapabilityIdGenerator,
80}
81
82impl<'a> CapabilityStore {
83 pub fn connect() -> Result<Self, Error> {
85 let proxy = client::connect_to_protocol::<fsandbox::CapabilityStoreMarker>().map_err(
86 |e| match e.downcast_ref() {
87 Some(status) => Error::FailedConnect(*status),
88 None => Error::FailedConnect(zx::Status::INTERNAL),
89 },
90 )?;
91 Ok(Self { proxy, id_gen: CapabilityIdGenerator::default() })
92 }
93
94 pub async fn import<T>(&'a self, value: T) -> Result<T::Ref, Error>
96 where
97 T: Importable<'a>,
98 {
99 let id = self.id_gen.next();
100 self.proxy.import(id, value.into().0).await??;
101 Ok(T::Ref::from_store(self, id))
102 }
103
104 pub async fn create_dictionary(&'a self) -> Result<Dictionary<'a>, Error> {
106 let id = self.id_gen.next();
107 self.proxy.dictionary_create(id).await??;
108 Ok(Dictionary { store: self, id })
109 }
110
111 pub async fn create_connector(
113 &'a self,
114 client_end: fidl::endpoints::ClientEnd<fsandbox::ReceiverMarker>,
115 ) -> Result<Connector<'a>, Error> {
116 let id = self.id_gen.next();
117 self.proxy.connector_create(id, client_end).await??;
118 Ok(Connector { store: self, id })
119 }
120
121 pub async fn create_dir_connector(
123 &'a self,
124 client_end: fidl::endpoints::ClientEnd<fsandbox::DirReceiverMarker>,
125 ) -> Result<DirConnector<'a>, Error> {
126 let id = self.id_gen.next();
127 self.proxy.dir_connector_create(id, client_end).await??;
128 Ok(DirConnector { store: self, id })
129 }
130}
131
132pub trait CapabilityRef<'a>: Sized {
138 fn from_store(store: &'a CapabilityStore, id: fsandbox::CapabilityId) -> Self;
144
145 fn store(&self) -> &'a CapabilityStore;
147
148 fn id(&self) -> fsandbox::CapabilityId;
150
151 fn duplicate(&'a self) -> impl Future<Output = Result<Self, Error>> {
154 async move {
155 let dup_id = self.store().id_gen.next();
156 self.store().proxy.duplicate(self.id(), dup_id).await??;
157 Ok(Self::from_store(self.store(), dup_id))
158 }
159 }
160
161 fn drop(self) -> impl Future<Output = Result<(), Error>> {
163 async move { Ok(self.store().proxy.drop(self.id()).await??) }
164 }
165
166 fn export<T>(self) -> impl Future<Output = Result<T, Error>>
169 where
170 T: Importable<'a, Ref = Self>,
171 {
172 async move {
173 let cap = self.store().proxy.export(self.id()).await??;
174 T::try_from(cap.into())
175 }
176 }
177}
178
179#[derive(Debug)]
185pub struct Capability(fsandbox::Capability);
186
187impl From<Capability> for fsandbox::Capability {
188 fn from(value: Capability) -> Self {
189 value.0
190 }
191}
192
193impl From<fsandbox::Capability> for Capability {
194 fn from(value: fsandbox::Capability) -> Self {
195 Self(value)
196 }
197}
198
199fn capability_type_name(cap: fsandbox::Capability) -> &'static str {
201 match cap {
202 fsandbox::Capability::Unit(_) => "Unit",
203 fsandbox::Capability::Handle(_) => "Handle",
204 fsandbox::Capability::Data(data) => match data {
205 fsandbox::Data::Bytes(_) => "Data::Bytes",
206 fsandbox::Data::String(_) => "Data::String",
207 fsandbox::Data::Int64(_) => "Data::Int64",
208 fsandbox::Data::Uint64(_) => "Data::Uint64",
209 _ => "Data::Unknown",
210 },
211 fsandbox::Capability::Dictionary(_) => "Dictionary",
212 fsandbox::Capability::Connector(_) => "Connector",
213 fsandbox::Capability::DirConnector(_) => "DirConnector",
214 fsandbox::Capability::Directory(_) => "Directory",
215 fsandbox::Capability::DirEntry(_) => "DirEntry",
216 fsandbox::Capability::ConnectorRouter(_) => "ConnectorRouter",
217 fsandbox::Capability::DictionaryRouter(_) => "DictionaryRouter",
218 fsandbox::Capability::DirEntryRouter(_) => "DirEntryRouter",
219 fsandbox::Capability::DataRouter(_) => "DataRouter",
220 fsandbox::Capability::DirConnectorRouter(_) => "DirConnectorRouter",
221 _ => "Unknown",
222 }
223}
224
225macro_rules! impl_capability_ref {
228 (
230 $(#[$attr:meta])*
231 $ref:ident, $t:ty, $variant:path
232 ) => {
233 impl_capability_ref!(
234 $(#[$attr])*
235 $ref
236 );
237
238 impl<'a> Importable<'a> for $t {
239 type Ref = $ref<'a>;
240 }
241
242 impl From<$t> for Capability {
243 fn from(value: $t) -> Self {
244 Self($variant(value))
245 }
246 }
247
248 impl TryFrom<Capability> for $t {
249 type Error = Error;
250 fn try_from(value: Capability) -> Result<Self, Self::Error> {
251 match value.0 {
252 $variant(inner) => Ok(inner),
253 capability => Err(Error::WrongType {
254 got: capability_type_name(capability),
255 want: stringify!($t),
256 }),
257 }
258 }
259 }
260 };
261 (
263 $(#[$attr:meta])*
264 $ref:ident
265 ) => {
266 $(#[$attr])*
267 #[derive(Debug)]
268 pub struct $ref<'a> {
269 store: &'a CapabilityStore,
270 id: fsandbox::CapabilityId,
271 }
272
273 impl<'a> CapabilityRef<'a> for $ref<'a> {
274 fn from_store(store: &'a CapabilityStore, id: fsandbox::CapabilityId) -> Self {
275 Self { store, id }
276 }
277
278 fn store(&self) -> &'a CapabilityStore {
279 self.store
280 }
281
282 fn id(&self) -> fsandbox::CapabilityId {
283 self.id
284 }
285 }
286 };
287}
288
289impl_capability_ref!(
290 Unit, fsandbox::Unit, fsandbox::Capability::Unit
292);
293
294impl_capability_ref!(
295 Handle, fidl::NullableHandle, fsandbox::Capability::Handle
297);
298
299impl_capability_ref!(
300 Data
305);
306
307impl_capability_ref!(
308 Dictionary, fsandbox::DictionaryRef, fsandbox::Capability::Dictionary
313);
314
315impl<'a> Dictionary<'a> {
316 pub async fn insert<V>(&self, key: impl Into<String>, value: V) -> Result<(), Error>
318 where
319 V: CapabilityRef<'a>,
320 {
321 self.store
322 .proxy
323 .dictionary_insert(
324 self.id,
325 &fsandbox::DictionaryItem { key: key.into(), value: value.id() },
326 )
327 .await??;
328 Ok(())
329 }
330
331 pub async fn get<T>(&self, key: impl AsRef<str>) -> Result<T, Error>
333 where
334 T: CapabilityRef<'a>,
335 {
336 let id = self.store.id_gen.next();
337 self.store.proxy.dictionary_get(self.id, key.as_ref(), id).await??;
338 Ok(T::from_store(self.store, id))
339 }
340}
341
342impl_capability_ref!(
343 Connector, fsandbox::Connector, fsandbox::Capability::Connector
347);
348
349impl<'a> Connector<'a> {
350 pub async fn open(
352 &self,
353 server_end: fidl::endpoints::ServerEnd<fsandbox::ReceiverMarker>,
354 ) -> Result<(), Error> {
355 Ok(self.store.proxy.connector_open(self.id, server_end.into_channel()).await??)
356 }
357}
358
359impl_capability_ref!(
360 DirConnector, fsandbox::DirConnector, fsandbox::Capability::DirConnector
362);
363
364impl<'a> DirConnector<'a> {
365 pub async fn open(
367 &self,
368 server_end: fidl::endpoints::ServerEnd<fio::DirectoryMarker>,
369 ) -> Result<(), Error> {
370 Ok(self
371 .store
372 .proxy
373 .dir_connector_open(fsandbox::CapabilityStoreDirConnectorOpenRequest {
374 id: Some(self.id),
375 server_end: Some(server_end),
376 flags: None,
377 path: None,
378 ..Default::default()
379 })
380 .await??)
381 }
382}
383
384impl_capability_ref!(
385 Directory, fidl::endpoints::ClientEnd<fio::DirectoryMarker>, fsandbox::Capability::Directory
387);
388
389impl_capability_ref!(
390 DirEntry, fsandbox::DirEntry, fsandbox::Capability::DirEntry
392);
393
394impl_capability_ref!(
395 ConnectorRouter, fidl::endpoints::ClientEnd<fsandbox::ConnectorRouterMarker>, fsandbox::Capability::ConnectorRouter
397);
398
399impl_capability_ref!(
400 DictionaryRouter, fidl::endpoints::ClientEnd<fsandbox::DictionaryRouterMarker>, fsandbox::Capability::DictionaryRouter
402);
403
404impl_capability_ref!(
405 DirEntryRouter, fidl::endpoints::ClientEnd<fsandbox::DirEntryRouterMarker>, fsandbox::Capability::DirEntryRouter
407);
408
409impl_capability_ref!(
410 DataRouter, fidl::endpoints::ClientEnd<fsandbox::DataRouterMarker>, fsandbox::Capability::DataRouter
412);
413
414impl_capability_ref!(
415 DirConnectorRouter, fidl::endpoints::ClientEnd<fsandbox::DirConnectorRouterMarker>, fsandbox::Capability::DirConnectorRouter
417);
418
419macro_rules! impl_data_capability {
421 ($t:ty, $variant:path, $variant_type_name:expr) => {
422 impl<'a> Importable<'a> for $t {
423 type Ref = Data<'a>;
424 }
425
426 impl From<$t> for Capability {
427 fn from(value: $t) -> Self {
428 Self(fsandbox::Capability::Data($variant(value)))
429 }
430 }
431
432 impl TryFrom<Capability> for $t {
433 type Error = Error;
434 fn try_from(value: Capability) -> Result<Self, Self::Error> {
435 match value.0 {
436 fsandbox::Capability::Data($variant(inner)) => Ok(inner),
437 capability => Err(Error::WrongType {
438 got: capability_type_name(capability),
439 want: $variant_type_name,
440 }),
441 }
442 }
443 }
444 };
445}
446
447impl_data_capability!(Vec<u8>, fsandbox::Data::Bytes, "Data::Bytes");
448impl_data_capability!(String, fsandbox::Data::String, "Data::String");
449impl_data_capability!(i64, fsandbox::Data::Int64, "Data::Int64");
450impl_data_capability!(u64, fsandbox::Data::Uint64, "Data::Uint64");
451
452#[cfg(test)]
453mod tests {
454 use super::*;
455 use assert_matches::assert_matches;
456 use fidl::endpoints::create_proxy_and_stream;
457 use fuchsia_async as fasync;
458 use futures::future::FutureExt;
459 use futures::prelude::*;
460 use futures::task::Poll;
461 use std::pin::Pin;
462 use test_case::test_case;
463 use zx::AsHandleRef;
464
465 fn setup_store() -> (CapabilityStore, fsandbox::CapabilityStoreRequestStream) {
466 let (proxy, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
467 let store = CapabilityStore { proxy, id_gen: CapabilityIdGenerator::default() };
468 (store, stream)
469 }
470
471 async fn wait_for_request<F, T>(f: &mut Pin<Box<F>>)
472 where
473 F: Future<Output = T> + ?Sized,
474 T: std::fmt::Debug,
475 {
476 assert_matches!(fasync::TestExecutor::poll_until_stalled(f.as_mut()).await, Poll::Pending);
477 }
478
479 #[test]
480 fn capability_id_generator() {
481 let id_gen = CapabilityIdGenerator::new();
482 assert_eq!(id_gen.next(), 0);
483 assert_eq!(id_gen.next(), 1);
484 assert_eq!(id_gen.range(5), 2);
485 assert_eq!(id_gen.next(), 7);
486 }
487
488 #[test_case("hello".to_string(); "string")]
489 #[test_case(123u64; "u64")]
490 #[test_case(-123i64; "i64")]
491 #[test_case(vec![1, 2, 3]; "bytes")]
492 fn data_capability_conversions<T>(val: T)
493 where
494 T: for<'a> Importable<'a, Ref = Data<'a>> + Clone + PartialEq + std::fmt::Debug,
495 {
496 let cap: Capability = val.clone().into();
497 let val_again: T = cap.try_into().unwrap();
498 assert_eq!(val_again, val);
499 }
500
501 #[test]
502 fn data_capability_conversions_wrong_type() {
503 let cap: Capability = "hello".to_string().into();
504 let res: Result<u64, _> = cap.try_into();
505 assert_matches!(res, Err(Error::WrongType { got: "Data::String", want: "Data::Uint64" }));
506 }
507
508 #[test_case("hello".to_string(); "string")]
509 #[test_case(123u64; "u64")]
510 #[test_case(-123i64; "i64")]
511 #[test_case(vec![1, 2, 3]; "bytes")]
512 #[fuchsia::test(allow_stalls = false)]
513 async fn capability_store_import_export<T>(val: T)
514 where
515 T: for<'a> Importable<'a, Ref: std::fmt::Debug> + Clone + PartialEq + std::fmt::Debug,
516 {
517 let (store, mut stream) = setup_store();
518
519 let mut import_fut = store.import(val.clone()).boxed_local();
521 wait_for_request(&mut import_fut).await;
522
523 let (capability, responder) = assert_matches!(
524 stream.next().await.unwrap().unwrap(),
525 fsandbox::CapabilityStoreRequest::Import { id, capability, responder } if id == 0 => (capability, responder)
526 );
527
528 let imported_val: T = Capability(capability).try_into().unwrap();
530 assert_eq!(imported_val, val);
531
532 responder.send(Ok(())).unwrap();
533 let cap = import_fut.await.unwrap();
534 assert_eq!(cap.id(), 0);
535
536 let mut export_fut = cap.export::<T>().boxed_local();
538 wait_for_request(&mut export_fut).await;
539
540 let responder = assert_matches!(
541 stream.next().await.unwrap().unwrap(),
542 fsandbox::CapabilityStoreRequest::Export { id, responder } if id == 0 => responder
543 );
544
545 let cap_to_return: Capability = val.clone().into();
546 responder.send(Ok(cap_to_return.into())).unwrap();
547
548 let result = export_fut.await.unwrap();
549 assert_eq!(result, val);
550 }
551
552 #[fuchsia::test(allow_stalls = false)]
553 async fn capability_ref_duplicate() {
554 let (store, mut stream) = setup_store();
555
556 let data = Data { id: 100, store: &store };
558
559 let mut duplicate_fut = data.duplicate().boxed_local();
560 wait_for_request(&mut duplicate_fut).await;
561
562 let responder = assert_matches!(
563 stream.next().await.unwrap().unwrap(),
564 fsandbox::CapabilityStoreRequest::Duplicate { id, dest_id, responder } if id == 100 && dest_id == 0 => responder
565 );
566
567 responder.send(Ok(())).unwrap();
568 let new_data: Data<'_> = duplicate_fut.await.unwrap();
569 assert_eq!(new_data.id, 0);
570 assert_eq!(store.id_gen.next(), 1); }
572
573 #[fuchsia::test(allow_stalls = false)]
574 async fn capability_ref_drop() {
575 let (store, mut stream) = setup_store();
576 let data = Data { id: 100, store: &store };
577
578 let mut drop_fut = data.drop().boxed_local();
579 wait_for_request(&mut drop_fut).await;
580
581 let responder = assert_matches!(
582 stream.next().await.unwrap().unwrap(),
583 fsandbox::CapabilityStoreRequest::Drop { id, responder } if id == 100 => responder
584 );
585
586 responder.send(Ok(())).unwrap();
587 drop_fut.await.unwrap();
588 }
589
590 #[fuchsia::test(allow_stalls = false)]
591 async fn dictionary_create() {
592 let (store, mut stream) = setup_store();
593
594 let mut create_fut = store.create_dictionary().boxed_local();
595 wait_for_request(&mut create_fut).await;
596
597 let responder = assert_matches!(
598 stream.next().await.unwrap().unwrap(),
599 fsandbox::CapabilityStoreRequest::DictionaryCreate { id, responder } if id == 0 => responder
600 );
601
602 responder.send(Ok(())).unwrap();
603 let dict = create_fut.await.unwrap();
604 assert_eq!(dict.id, 0);
605 }
606
607 #[fuchsia::test(allow_stalls = false)]
608 async fn dictionary_insert() {
609 let (store, mut stream) = setup_store();
610 let dict = Dictionary { id: 10, store: &store };
611 let data = Data { id: 20, store: &store };
612
613 let mut insert_fut = dict.insert("key", data).boxed_local();
614 wait_for_request(&mut insert_fut).await;
615
616 let responder = assert_matches!(
617 stream.next().await.unwrap().unwrap(),
618 fsandbox::CapabilityStoreRequest::DictionaryInsert { id, item, responder } if id == 10 && item.key == "key" && item.value == 20 => responder
619 );
620
621 responder.send(Ok(())).unwrap();
622 insert_fut.await.unwrap();
623 }
624
625 #[fuchsia::test(allow_stalls = false)]
626 async fn dictionary_get() {
627 let (store, mut stream) = setup_store();
628 let dict = Dictionary { id: 10, store: &store };
629
630 let mut get_fut = dict.get::<Data<'_>>("key").boxed_local();
631 wait_for_request(&mut get_fut).await;
632
633 let responder = assert_matches!(
634 stream.next().await.unwrap().unwrap(),
635 fsandbox::CapabilityStoreRequest::DictionaryGet { id, key, dest_id, responder } if id == 10 && key == "key" && dest_id == 0 => responder
636 );
637
638 responder.send(Ok(())).unwrap();
639 let data = get_fut.await.unwrap();
640 assert_eq!(data.id, 0);
641 }
642
643 #[fuchsia::test(allow_stalls = false)]
644 async fn dictionary_export() {
645 let (store, mut stream) = setup_store();
646 let dict = Dictionary { id: 10, store: &store };
647
648 let mut export_fut = dict.export().boxed_local();
649 wait_for_request(&mut export_fut).await;
650
651 let responder = assert_matches!(
652 stream.next().await.unwrap().unwrap(),
653 fsandbox::CapabilityStoreRequest::Export { id, responder } if id == 10 => responder
654 );
655
656 let (client, _server) = zx::EventPair::create();
657 let cap = fsandbox::Capability::Dictionary(fsandbox::DictionaryRef { token: client });
658 responder.send(Ok(cap)).unwrap();
659
660 let result: fsandbox::DictionaryRef = export_fut.await.unwrap();
661 assert_eq!(result.token.as_handle_ref().is_invalid(), false);
662 }
663
664 #[fuchsia::test(allow_stalls = false)]
665 async fn connector_create() {
666 let (store, mut stream) = setup_store();
667 let (client_end, _server_end) =
668 fidl::endpoints::create_endpoints::<fsandbox::ReceiverMarker>();
669
670 let mut create_fut = store.create_connector(client_end).boxed_local();
671 wait_for_request(&mut create_fut).await;
672
673 let responder = assert_matches!(
674 stream.next().await.unwrap().unwrap(),
675 fsandbox::CapabilityStoreRequest::ConnectorCreate { id, receiver, responder } if id == 0 && receiver.as_handle_ref().is_invalid() == false => responder
676 );
677
678 responder.send(Ok(())).unwrap();
679 let connector = create_fut.await.unwrap();
680 assert_eq!(connector.id, 0);
681 }
682
683 #[fuchsia::test(allow_stalls = false)]
684 async fn connector_open() {
685 let (store, mut stream) = setup_store();
686 let connector = Connector { id: 10, store: &store };
687
688 let (client_end, server_end) =
689 fidl::endpoints::create_endpoints::<fsandbox::ReceiverMarker>();
690 let mut open_fut = connector.open(server_end).boxed_local();
691 wait_for_request(&mut open_fut).await;
692
693 let responder = assert_matches!(
694 stream.next().await.unwrap().unwrap(),
695 fsandbox::CapabilityStoreRequest::ConnectorOpen { id, server_end, responder } if id == 10 && server_end.as_handle_ref().is_invalid() == false => responder
696 );
697
698 responder.send(Ok(())).unwrap();
699 open_fut.await.unwrap();
700 assert_eq!(client_end.as_handle_ref().is_invalid(), false);
701 }
702
703 #[fuchsia::test(allow_stalls = false)]
704 async fn dir_connector_create() {
705 let (store, mut stream) = setup_store();
706 let (client_end, _server_end) =
707 fidl::endpoints::create_endpoints::<fsandbox::DirReceiverMarker>();
708
709 let mut create_fut = store.create_dir_connector(client_end).boxed_local();
710 wait_for_request(&mut create_fut).await;
711
712 let responder = assert_matches!(
713 stream.next().await.unwrap().unwrap(),
714 fsandbox::CapabilityStoreRequest::DirConnectorCreate { id, receiver, responder } if id == 0 && receiver.as_handle_ref().is_invalid() == false => responder
715 );
716
717 responder.send(Ok(())).unwrap();
718 let connector = create_fut.await.unwrap();
719 assert_eq!(connector.id, 0);
720 }
721
722 #[fuchsia::test(allow_stalls = false)]
723 async fn dir_connector_open() {
724 let (store, mut stream) = setup_store();
725 let connector = DirConnector { id: 10, store: &store };
726
727 let (client_end, server_end) = fidl::endpoints::create_endpoints::<fio::DirectoryMarker>();
728 let mut open_fut = connector.open(server_end).boxed_local();
729 wait_for_request(&mut open_fut).await;
730
731 let responder = assert_matches!(
732 stream.next().await.unwrap().unwrap(),
733 fsandbox::CapabilityStoreRequest::DirConnectorOpen { payload, responder } if payload.id == Some(10) && payload.server_end.as_ref().expect("server_end").as_handle_ref().is_invalid() == false => responder
734 );
735
736 responder.send(Ok(())).unwrap();
737 open_fut.await.unwrap();
738 assert_eq!(client_end.as_handle_ref().is_invalid(), false);
739 }
740}