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