1use fuchsia_component::client;
11use std::sync::Arc;
12use std::sync::atomic::{AtomicU64, Ordering};
13use thiserror::Error;
14use {fidl_fuchsia_component_sandbox as fsandbox, fidl_fuchsia_io as fio};
15
16pub trait Importable<'a>: Sized + Into<Capability> + TryFrom<Capability, Error = Error> {
20 type Handle: CapabilityHandle<'a>;
22}
23
24#[derive(Error, Clone, Debug)]
26pub enum Error {
27 #[error("failed to connect to fuchsia.component.sandbox/CapabilityStore: {0}")]
29 FailedConnect(zx::Status),
30 #[error("wrong Capability type; got \"{got}\", want \"{want}\"")]
32 WrongType { got: &'static str, want: &'static str },
33 #[error("FIDL error {0}")]
35 Fidl(#[from] fidl::Error),
36 #[error("CapabilityStoreError {0:?}")]
38 CapabilityStore(fsandbox::CapabilityStoreError),
39}
40
41impl From<fsandbox::CapabilityStoreError> for Error {
42 fn from(value: fsandbox::CapabilityStoreError) -> Self {
43 Self::CapabilityStore(value)
44 }
45}
46
47#[derive(Clone, Default, Debug)]
52pub struct CapabilityIdGenerator {
53 next_id: Arc<AtomicU64>,
54}
55
56impl CapabilityIdGenerator {
57 pub fn new() -> Self {
59 Self::default()
60 }
61
62 pub fn next(&self) -> u64 {
64 self.range(1)
65 }
66
67 pub fn range(&self, size: u64) -> u64 {
70 self.next_id.fetch_add(size, Ordering::Relaxed)
71 }
72}
73
74#[derive(Clone, Debug)]
76pub struct CapabilityStore {
77 proxy: fsandbox::CapabilityStoreProxy,
78 id_gen: CapabilityIdGenerator,
79}
80
81impl<'a> CapabilityStore {
82 pub fn connect() -> Result<Self, Error> {
84 let proxy = client::connect_to_protocol::<fsandbox::CapabilityStoreMarker>().map_err(
85 |e| match e.downcast_ref() {
86 Some(status) => Error::FailedConnect(*status),
87 None => Error::FailedConnect(zx::Status::INTERNAL),
88 },
89 )?;
90 Ok(Self { proxy, id_gen: CapabilityIdGenerator::default() })
91 }
92
93 pub async fn import<T>(&'a self, value: T) -> Result<T::Handle, Error>
95 where
96 T: Importable<'a>,
97 {
98 let id = self.id_gen.next();
99 self.proxy.import(id, value.into().0).await??;
100 Ok(T::Handle::from_store(self, id))
101 }
102
103 pub async fn create_dictionary(&'a self) -> Result<Dictionary<'a>, Error> {
105 let id = self.id_gen.next();
106 self.proxy.dictionary_create(id).await??;
107 Ok(Dictionary { store: self, id })
108 }
109
110 pub async fn create_connector(
112 &'a self,
113 client_end: fidl::endpoints::ClientEnd<fsandbox::ReceiverMarker>,
114 ) -> Result<Connector<'a>, Error> {
115 let id = self.id_gen.next();
116 self.proxy.connector_create(id, client_end).await??;
117 Ok(Connector { store: self, id })
118 }
119}
120
121#[allow(async_fn_in_trait)]
127pub trait CapabilityHandle<'a>: Sized {
128 fn from_store(store: &'a CapabilityStore, id: fsandbox::CapabilityId) -> Self;
129 fn store(&self) -> &'a CapabilityStore;
130 fn id(&self) -> fsandbox::CapabilityId;
131
132 async fn duplicate(&'a self) -> Result<Self, Error> {
135 let dup_id = self.store().id_gen.next();
136 self.store().proxy.duplicate(self.id(), dup_id).await??;
137 Ok(Self::from_store(self.store(), dup_id))
138 }
139
140 async fn drop(self) -> Result<(), Error> {
142 Ok(self.store().proxy.drop(self.id()).await??)
143 }
144
145 async fn export<T>(self) -> Result<T, Error>
148 where
149 T: Importable<'a, Handle = Self>,
150 {
151 let cap = self.store().proxy.export(self.id()).await??;
152 T::try_from(cap.into())
153 }
154}
155
156#[derive(Debug)]
162pub struct Capability(fsandbox::Capability);
163
164impl From<Capability> for fsandbox::Capability {
165 fn from(value: Capability) -> Self {
166 value.0
167 }
168}
169
170impl From<fsandbox::Capability> for Capability {
171 fn from(value: fsandbox::Capability) -> Self {
172 Self(value)
173 }
174}
175
176fn capability_type_name(cap: fsandbox::Capability) -> &'static str {
178 match cap {
179 fsandbox::Capability::Unit(_) => "Unit",
180 fsandbox::Capability::Handle(_) => "Handle",
181 fsandbox::Capability::Dictionary(_) => "Dictionary",
182 fsandbox::Capability::Connector(_) => "Connector",
183 fsandbox::Capability::Directory(_) => "Directory",
184 fsandbox::Capability::DirEntry(_) => "DirEntry",
185 fsandbox::Capability::ConnectorRouter(_) => "ConnectorRouter",
186 fsandbox::Capability::DictionaryRouter(_) => "DictionaryRouter",
187 fsandbox::Capability::DirEntryRouter(_) => "DirEntryRouter",
188 fsandbox::Capability::DataRouter(_) => "DataRouter",
189 fsandbox::Capability::DirConnectorRouter(_) => "DirConnectorRouter",
190 fsandbox::Capability::Data(data) => match data {
191 fsandbox::Data::Bytes(_) => "Data::Bytes",
192 fsandbox::Data::String(_) => "Data::String",
193 fsandbox::Data::Int64(_) => "Data::Int64",
194 fsandbox::Data::Uint64(_) => "Data::Uint64",
195 _ => "Data::Unknown",
196 },
197 _ => "Unknown",
198 }
199}
200
201macro_rules! impl_try_from_capability {
203 ($t:ty, $variant:path, $variant_type_name:expr) => {
204 impl TryFrom<Capability> for $t {
205 type Error = Error;
206 fn try_from(value: Capability) -> Result<Self, Self::Error> {
207 match value.0 {
208 $variant(inner) => Ok(inner),
209 capability => Err(Error::WrongType {
210 got: capability_type_name(capability),
211 want: $variant_type_name,
212 }),
213 }
214 }
215 }
216 };
217}
218
219impl_try_from_capability!(fsandbox::Unit, fsandbox::Capability::Unit, "Unit");
220impl_try_from_capability!(fidl::Handle, fsandbox::Capability::Handle, "Handle");
221impl_try_from_capability!(fsandbox::DictionaryRef, fsandbox::Capability::Dictionary, "Dictionary");
222impl_try_from_capability!(fsandbox::Connector, fsandbox::Capability::Connector, "Connector");
223impl_try_from_capability!(
224 fidl::endpoints::ClientEnd<fio::DirectoryMarker>,
225 fsandbox::Capability::Directory,
226 "Directory"
227);
228impl_try_from_capability!(fsandbox::DirEntry, fsandbox::Capability::DirEntry, "DirEntry");
229impl_try_from_capability!(
230 fidl::endpoints::ClientEnd<fsandbox::ConnectorRouterMarker>,
231 fsandbox::Capability::ConnectorRouter,
232 "ConnectorRouter"
233);
234impl_try_from_capability!(
235 fidl::endpoints::ClientEnd<fsandbox::DictionaryRouterMarker>,
236 fsandbox::Capability::DictionaryRouter,
237 "DictionaryRouter"
238);
239impl_try_from_capability!(
240 fidl::endpoints::ClientEnd<fsandbox::DirEntryRouterMarker>,
241 fsandbox::Capability::DirEntryRouter,
242 "DirEntryRouter"
243);
244impl_try_from_capability!(
245 fidl::endpoints::ClientEnd<fsandbox::DataRouterMarker>,
246 fsandbox::Capability::DataRouter,
247 "DataRouter"
248);
249impl_try_from_capability!(
250 fidl::endpoints::ClientEnd<fsandbox::DirConnectorRouterMarker>,
251 fsandbox::Capability::DirConnectorRouter,
252 "DirConnectorRouter"
253);
254
255macro_rules! impl_data_capability {
257 ($t:ty, $variant:path, $variant_type_name:expr) => {
258 impl<'a> Importable<'a> for $t {
259 type Handle = Data<'a>;
260 }
261
262 impl From<$t> for Capability {
263 fn from(value: $t) -> Self {
264 Self(fsandbox::Capability::Data($variant(value)))
265 }
266 }
267
268 impl TryFrom<Capability> for $t {
269 type Error = Error;
270 fn try_from(value: Capability) -> Result<Self, Self::Error> {
271 match value.0 {
272 fsandbox::Capability::Data($variant(inner)) => Ok(inner),
273 capability => Err(Error::WrongType {
274 got: capability_type_name(capability),
275 want: $variant_type_name,
276 }),
277 }
278 }
279 }
280 };
281}
282
283impl_data_capability!(Vec<u8>, fsandbox::Data::Bytes, "Data::Bytes");
284impl_data_capability!(String, fsandbox::Data::String, "Data::String");
285impl_data_capability!(i64, fsandbox::Data::Int64, "Data:Int64");
286impl_data_capability!(u64, fsandbox::Data::Uint64, "Data:Uint64");
287
288impl<'a> Importable<'a> for fsandbox::DictionaryRef {
289 type Handle = Dictionary<'a>;
290}
291
292impl From<fsandbox::DictionaryRef> for Capability {
293 fn from(value: fsandbox::DictionaryRef) -> Self {
294 Capability(fsandbox::Capability::Dictionary(value))
295 }
296}
297
298#[derive(Debug)]
303pub struct Dictionary<'a> {
304 store: &'a CapabilityStore,
305 id: fsandbox::CapabilityId,
306}
307
308impl<'a> CapabilityHandle<'a> for Dictionary<'a> {
309 fn from_store(store: &'a CapabilityStore, id: fsandbox::CapabilityId) -> Self {
310 Self { store, id }
311 }
312
313 fn store(&self) -> &'a CapabilityStore {
314 self.store
315 }
316
317 fn id(&self) -> fsandbox::CapabilityId {
318 self.id
319 }
320}
321
322impl<'a> Dictionary<'a> {
323 pub async fn insert<V>(&self, key: impl Into<String>, value: V) -> Result<(), Error>
325 where
326 V: CapabilityHandle<'a>,
327 {
328 self.store
329 .proxy
330 .dictionary_insert(
331 self.id,
332 &fsandbox::DictionaryItem { key: key.into(), value: value.id() },
333 )
334 .await??;
335 Ok(())
336 }
337
338 pub async fn get<T>(&self, key: impl AsRef<str>) -> Result<T, Error>
340 where
341 T: CapabilityHandle<'a>,
342 {
343 let id = self.store.id_gen.next();
344 self.store.proxy.dictionary_get(self.id, key.as_ref(), id).await??;
345 Ok(T::from_store(self.store, id))
346 }
347}
348
349#[derive(Debug)]
353pub struct Connector<'a> {
354 store: &'a CapabilityStore,
355 id: fsandbox::CapabilityId,
356}
357
358impl<'a> CapabilityHandle<'a> for Connector<'a> {
359 fn from_store(store: &'a CapabilityStore, id: fsandbox::CapabilityId) -> Self {
360 Self { store, id }
361 }
362
363 fn store(&self) -> &'a CapabilityStore {
364 self.store
365 }
366
367 fn id(&self) -> fsandbox::CapabilityId {
368 self.id
369 }
370}
371
372impl<'a> Connector<'a> {}
373
374#[derive(Debug)]
379pub struct Data<'a> {
380 store: &'a CapabilityStore,
381 id: fsandbox::CapabilityId,
382}
383
384impl<'a> CapabilityHandle<'a> for Data<'a> {
385 fn from_store(store: &'a CapabilityStore, id: fsandbox::CapabilityId) -> Self {
386 Self { store, id }
387 }
388
389 fn store(&self) -> &'a CapabilityStore {
390 self.store
391 }
392
393 fn id(&self) -> fsandbox::CapabilityId {
394 self.id
395 }
396}
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401 use assert_matches::assert_matches;
402 use fidl::endpoints::create_proxy_and_stream;
403 use fuchsia_async as fasync;
404 use futures::FutureExt;
405 use futures::prelude::*;
406 use futures::task::Poll;
407 use std::pin::Pin;
408 use zx::AsHandleRef as _;
409
410 fn setup_store() -> (CapabilityStore, fsandbox::CapabilityStoreRequestStream) {
411 let (proxy, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
412 let store = CapabilityStore { proxy, id_gen: CapabilityIdGenerator::default() };
413 (store, stream)
414 }
415
416 async fn wait_for_request<F, T>(f: &mut Pin<Box<F>>)
417 where
418 F: Future<Output = T> + ?Sized,
419 T: std::fmt::Debug,
420 {
421 assert_matches!(fasync::TestExecutor::poll_until_stalled(f.as_mut()).await, Poll::Pending);
422 }
423
424 #[test]
425 fn capability_id_generator() {
426 let id_gen = CapabilityIdGenerator::new();
427 assert_eq!(id_gen.next(), 0);
428 assert_eq!(id_gen.next(), 1);
429 assert_eq!(id_gen.range(5), 2);
430 assert_eq!(id_gen.next(), 7);
431 }
432
433 #[test]
434 fn data_capability_conversions() {
435 let cap: Capability = "hello".to_string().into();
437 let str_again: String = cap.try_into().unwrap();
438 assert_eq!(str_again, "hello");
439
440 let cap: Capability = 123u64.into();
442 let num_again: u64 = cap.try_into().unwrap();
443 assert_eq!(num_again, 123);
444
445 let cap: Capability = "hello".to_string().into();
447 let res: Result<u64, _> = cap.try_into();
448 assert_matches!(res, Err(Error::WrongType { got: "Data::String", want: _ }));
449 }
450
451 #[fuchsia::test(allow_stalls = false)]
452 async fn capability_store_import() {
453 let (store, mut stream) = setup_store();
454
455 let mut import_fut = store.import("hello".to_string()).boxed_local();
456 wait_for_request(&mut import_fut).await;
457
458 let (capability, responder) = assert_matches!(
459 stream.next().await.unwrap().unwrap(),
460 fsandbox::CapabilityStoreRequest::Import { id, capability, responder } if id == 0 => (capability, responder)
461 );
462
463 assert_matches!(capability, fsandbox::Capability::Data(fsandbox::Data::String(s)) if s == "hello");
464 responder.send(Ok(())).unwrap();
465 let handle = import_fut.await.unwrap();
466 assert_eq!(handle.id, 0);
467 }
468
469 #[fuchsia::test(allow_stalls = false)]
470 async fn capability_handle_duplicate() {
471 let (store, mut stream) = setup_store();
472
473 let handle = Data { id: 100, store: &store };
475
476 let mut duplicate_fut = handle.duplicate().boxed_local();
477 wait_for_request(&mut duplicate_fut).await;
478
479 let responder = assert_matches!(
480 stream.next().await.unwrap().unwrap(),
481 fsandbox::CapabilityStoreRequest::Duplicate { id, dest_id, responder } if id == 100 && dest_id == 0 => responder
482 );
483
484 responder.send(Ok(())).unwrap();
485 let new_handle: Data<'_> = duplicate_fut.await.unwrap();
486 assert_eq!(new_handle.id, 0);
487 assert_eq!(store.id_gen.next(), 1); }
489
490 #[fuchsia::test(allow_stalls = false)]
491 async fn capability_handle_drop() {
492 let (store, mut stream) = setup_store();
493 let handle = Data { id: 100, store: &store };
494
495 let mut drop_fut = handle.drop().boxed_local();
496 wait_for_request(&mut drop_fut).await;
497
498 let responder = assert_matches!(
499 stream.next().await.unwrap().unwrap(),
500 fsandbox::CapabilityStoreRequest::Drop { id, responder } if id == 100 => responder
501 );
502
503 responder.send(Ok(())).unwrap();
504 drop_fut.await.unwrap();
505 }
506
507 #[fuchsia::test(allow_stalls = false)]
508 async fn capability_handle_export() {
509 let (store, mut stream) = setup_store();
510 let handle = Data { id: 100, store: &store };
511
512 let mut export_fut = handle.export::<String>().boxed_local();
513 wait_for_request(&mut export_fut).await;
514
515 let responder = assert_matches!(
516 stream.next().await.unwrap().unwrap(),
517 fsandbox::CapabilityStoreRequest::Export { id, responder } if id == 100 => responder
518 );
519
520 let cap = fsandbox::Capability::Data(fsandbox::Data::String("exported".to_string()));
521 responder.send(Ok(cap)).unwrap();
522
523 let result = export_fut.await.unwrap();
524 assert_eq!(result, "exported");
525 }
526
527 #[fuchsia::test(allow_stalls = false)]
528 async fn dictionary_create() {
529 let (store, mut stream) = setup_store();
530
531 let mut create_fut = store.create_dictionary().boxed_local();
532 wait_for_request(&mut create_fut).await;
533
534 let responder = assert_matches!(
535 stream.next().await.unwrap().unwrap(),
536 fsandbox::CapabilityStoreRequest::DictionaryCreate { id, responder } if id == 0 => responder
537 );
538
539 responder.send(Ok(())).unwrap();
540 let dict = create_fut.await.unwrap();
541 assert_eq!(dict.id, 0);
542 }
543
544 #[fuchsia::test(allow_stalls = false)]
545 async fn dictionary_insert() {
546 let (store, mut stream) = setup_store();
547 let dict = Dictionary { id: 10, store: &store };
548 let data = Data { id: 20, store: &store };
549
550 let mut insert_fut = dict.insert("key", data).boxed_local();
551 wait_for_request(&mut insert_fut).await;
552
553 let responder = assert_matches!(
554 stream.next().await.unwrap().unwrap(),
555 fsandbox::CapabilityStoreRequest::DictionaryInsert { id, item, responder } if id == 10 && item.key == "key" && item.value == 20 => responder
556 );
557
558 responder.send(Ok(())).unwrap();
559 insert_fut.await.unwrap();
560 }
561
562 #[fuchsia::test(allow_stalls = false)]
563 async fn dictionary_get() {
564 let (store, mut stream) = setup_store();
565 let dict = Dictionary { id: 10, store: &store };
566
567 let mut get_fut = dict.get::<Data<'_>>("key").boxed_local();
568 wait_for_request(&mut get_fut).await;
569
570 let responder = assert_matches!(
571 stream.next().await.unwrap().unwrap(),
572 fsandbox::CapabilityStoreRequest::DictionaryGet { id, key, dest_id, responder } if id == 10 && key == "key" && dest_id == 0 => responder
573 );
574
575 responder.send(Ok(())).unwrap();
576 let handle = get_fut.await.unwrap();
577 assert_eq!(handle.id, 0);
578 }
579
580 #[fuchsia::test(allow_stalls = false)]
581 async fn dictionary_export() {
582 let (store, mut stream) = setup_store();
583 let dict = Dictionary { id: 10, store: &store };
584
585 let mut export_fut = dict.export().boxed_local();
586 wait_for_request(&mut export_fut).await;
587
588 let responder = assert_matches!(
589 stream.next().await.unwrap().unwrap(),
590 fsandbox::CapabilityStoreRequest::Export { id, responder } if id == 10 => responder
591 );
592
593 let (client, _server) = zx::EventPair::create();
594 let cap = fsandbox::Capability::Dictionary(fsandbox::DictionaryRef { token: client });
595 responder.send(Ok(cap)).unwrap();
596
597 let result: fsandbox::DictionaryRef = export_fut.await.unwrap();
598 assert_eq!(result.token.as_handle_ref().is_invalid(), false);
599 }
600
601 #[fuchsia::test(allow_stalls = false)]
602 async fn connector_create() {
603 let (store, mut stream) = setup_store();
604 let (client_end, _server_end) =
605 fidl::endpoints::create_endpoints::<fsandbox::ReceiverMarker>();
606
607 let mut create_fut = store.create_connector(client_end).boxed_local();
608 wait_for_request(&mut create_fut).await;
609
610 let responder = assert_matches!(
611 stream.next().await.unwrap().unwrap(),
612 fsandbox::CapabilityStoreRequest::ConnectorCreate { id, receiver, responder } if id == 0 && receiver.as_handle_ref().is_invalid() == false => responder
613 );
614
615 responder.send(Ok(())).unwrap();
616 let connector = create_fut.await.unwrap();
617 assert_eq!(connector.id, 0);
618 }
619
620 #[fuchsia::test(allow_stalls = false)]
621 async fn data_export() {
622 let (store, mut stream) = setup_store();
623 let data = Data { id: 10, store: &store };
624
625 let mut export_fut = data.export::<String>().boxed_local();
626 wait_for_request(&mut export_fut).await;
627
628 let responder = assert_matches!(
629 stream.next().await.unwrap().unwrap(),
630 fsandbox::CapabilityStoreRequest::Export { id, responder } if id == 10 => responder
631 );
632
633 let cap = fsandbox::Capability::Data(fsandbox::Data::String("exported data".to_string()));
634 responder.send(Ok(cap)).unwrap();
635
636 let result = export_fut.await.unwrap();
637 assert_eq!(result, "exported data");
638 }
639}