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