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