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