1use crate::dict::{EntryUpdate, UpdateNotifierRetention};
6use crate::fidl::registry::{self, try_from_handle_in_registry};
7use crate::{
8 Capability, ConversionError, Dict, RemotableCapability, RemoteError, WeakInstanceToken,
9};
10use fidl::AsHandleRef;
11use fidl_fuchsia_component_sandbox as fsandbox;
12use fuchsia_sync::Mutex;
13use futures::FutureExt;
14use futures::channel::oneshot;
15use log::warn;
16use std::sync::{Arc, Weak};
17use vfs::directory::entry::DirectoryEntry;
18use vfs::directory::helper::DirectlyMutable;
19use vfs::directory::immutable::simple as pfs;
20use vfs::execution_scope::ExecutionScope;
21use vfs::name::Name;
22
23impl From<Dict> for fsandbox::DictionaryRef {
24 fn from(dict: Dict) -> Self {
25 fsandbox::DictionaryRef { token: registry::insert_token(dict.into()) }
26 }
27}
28
29impl crate::fidl::IntoFsandboxCapability for Dict {
30 fn into_fsandbox_capability(self, _token: WeakInstanceToken) -> fsandbox::Capability {
31 fsandbox::Capability::Dictionary(self.into())
32 }
33}
34
35impl TryFrom<fsandbox::DictionaryRef> for Dict {
36 type Error = RemoteError;
37
38 fn try_from(dict: fsandbox::DictionaryRef) -> Result<Self, Self::Error> {
39 let any = try_from_handle_in_registry(dict.token.as_handle_ref())?;
40 let Capability::Dictionary(dict) = any else {
41 panic!("BUG: registry has a non-Dict capability under a Dict koid");
42 };
43 Ok(dict)
44 }
45}
46
47impl TryFrom<fidl::Channel> for Dict {
49 type Error = RemoteError;
50
51 fn try_from(dict: fidl::Channel) -> Result<Self, Self::Error> {
52 let any = try_from_handle_in_registry(dict.as_handle_ref())?;
53 let Capability::Dictionary(dict) = any else {
54 panic!("BUG: registry has a non-Dict capability under a Dict koid");
55 };
56 Ok(dict)
57 }
58}
59
60impl Dict {
61 pub fn try_into_directory_entry_oneshot(
71 self,
72 scope: ExecutionScope,
73 token: WeakInstanceToken,
74 ) -> Result<Arc<dyn DirectoryEntry>, ConversionError> {
75 let directory = pfs::simple();
76 for (key, value) in self.drain() {
77 let dir_entry = match value {
78 Capability::Dictionary(value) => {
79 value.try_into_directory_entry_oneshot(scope.clone(), token.clone())?
80 }
81 value => value.try_into_directory_entry(scope.clone(), token.clone())?,
82 };
83 let key =
84 Name::try_from(key.to_string()).expect("cm_types::Name is always a valid vfs Name");
85 directory
86 .add_entry_impl(key, dir_entry, false)
87 .expect("dictionary values must be unique")
88 }
89
90 let not_found = self.lock().not_found.take();
91 directory.clone().set_not_found_handler(Box::new(move |path| {
92 if let Some(not_found) = not_found.as_ref() {
93 not_found(path);
94 }
95 }));
96
97 Ok(directory)
98 }
99}
100
101impl RemotableCapability for Dict {
102 fn try_into_directory_entry(
103 self,
104 scope: ExecutionScope,
105 token: WeakInstanceToken,
106 ) -> Result<Arc<dyn DirectoryEntry>, ConversionError> {
107 let directory = pfs::simple();
108 let weak_dir: Weak<pfs::Simple> = Arc::downgrade(&directory);
109 let (error_sender, error_receiver) = oneshot::channel();
110 let error_sender = Mutex::new(Some(error_sender));
111 self.register_update_notifier(Box::new(move |update: EntryUpdate<'_>| {
115 let Some(directory) = weak_dir.upgrade() else {
116 return UpdateNotifierRetention::Drop_;
117 };
118 match update {
119 EntryUpdate::Add(key, value) => {
120 let value = match value.try_clone() {
121 Ok(value) => value,
122 Err(_err) => {
123 if let Some(error_sender) = error_sender.lock().take() {
124 let _ = error_sender.send(ConversionError::NotSupported);
125 } else {
126 warn!(
127 "unable to add uncloneable capability type from dictionary to \
128 directory"
129 );
130 }
131 return UpdateNotifierRetention::Retain;
132 }
133 };
134 let dir_entry = match value
135 .try_into_directory_entry(scope.clone(), token.clone())
136 {
137 Ok(dir_entry) => dir_entry,
138 Err(err) => {
139 if let Some(error_sender) = error_sender.lock().take() {
140 let _ = error_sender.send(err);
141 } else {
142 warn!(
143 "value in dictionary cannot be converted to directory entry: \
144 {err:?}"
145 )
146 }
147 return UpdateNotifierRetention::Retain;
148 }
149 };
150 let name = Name::try_from(key.to_string())
151 .expect("cm_types::Name is always a valid vfs Name");
152 directory
153 .add_entry_impl(name, dir_entry, false)
154 .expect("dictionary values must be unique")
155 }
156 EntryUpdate::Remove(key) => {
157 let name = Name::try_from(key.to_string())
158 .expect("cm_types::Name is always a valid vfs Name");
159 let _ = directory.remove_entry_impl(name, false);
160 }
161 EntryUpdate::Idle => (),
162 }
163 UpdateNotifierRetention::Retain
164 }));
165 directory.clone().set_not_found_handler(Box::new(move |path| {
166 self.not_found(path);
181 }));
182 if let Some(Ok(error)) = error_receiver.now_or_never() {
183 return Err(error);
186 }
187 Ok(directory)
188 }
189}
190
191#[cfg(test)]
193mod tests {
194 use super::*;
195 use crate::dict::{
196 BorrowedKey, HYBRID_SWITCH_INSERTION_LEN, HYBRID_SWITCH_REMOVAL_LEN, HybridMap, Key,
197 };
198 use crate::fidl::IntoFsandboxCapability;
199 use crate::{Data, Dict, DirEntry, Handle, Unit, serve_capability_store};
200 use assert_matches::assert_matches;
201 use fidl::endpoints::{Proxy, create_proxy, create_proxy_and_stream};
202 use fidl::handle::{Channel, HandleBased, Status};
203 use fuchsia_fs::directory;
204 use futures::StreamExt;
205 use std::sync::LazyLock;
206 use std::{fmt, iter};
207 use test_case::test_case;
208 use test_util::Counter;
209 use vfs::directory::entry::{
210 DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest, serve_directory,
211 };
212 use vfs::execution_scope::ExecutionScope;
213 use vfs::path::Path;
214 use vfs::remote::RemoteLike;
215 use vfs::{ObjectRequestRef, pseudo_directory};
216 use {fidl_fuchsia_io as fio, fuchsia_async as fasync};
217
218 static CAP_KEY: LazyLock<Key> = LazyLock::new(|| "Cap".parse().unwrap());
219
220 #[derive(Debug, Clone, Copy)]
221 enum TestType {
222 Small,
224 Big,
226 }
227
228 #[fuchsia::test]
229 async fn create() {
230 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
231 let _server = fasync::Task::spawn(async move {
232 let receiver_scope = fasync::Scope::new();
233 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
234 });
235 let id_gen = sandbox::CapabilityIdGenerator::new();
236
237 let dict_id = id_gen.next();
238 assert_matches!(store.dictionary_create(dict_id).await.unwrap(), Ok(()));
239 assert_matches!(
240 store.dictionary_create(dict_id).await.unwrap(),
241 Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
242 );
243
244 let value = 10;
245 store.import(value, Unit::default().into()).await.unwrap().unwrap();
246 store
247 .dictionary_insert(dict_id, &fsandbox::DictionaryItem { key: "k".into(), value })
248 .await
249 .unwrap()
250 .unwrap();
251
252 let (iterator, server_end) = create_proxy();
254 store.dictionary_keys(dict_id, server_end).await.unwrap().unwrap();
255 let keys = iterator.get_next().await.unwrap();
256 assert!(iterator.get_next().await.unwrap().is_empty());
257 assert_eq!(keys, ["k"]);
258 }
259
260 #[fuchsia::test]
261 async fn create_error() {
262 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
263 let _server = fasync::Task::spawn(async move {
264 let receiver_scope = fasync::Scope::new();
265 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
266 });
267
268 let cap = Capability::Data(Data::Int64(42));
269 assert_matches!(
270 store
271 .import(1, cap.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
272 .await
273 .unwrap(),
274 Ok(())
275 );
276 assert_matches!(
277 store.dictionary_create(1).await.unwrap(),
278 Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
279 );
280 }
281
282 #[fuchsia::test]
283 async fn legacy_import() {
284 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
285 let _server = fasync::Task::spawn(async move {
286 let receiver_scope = fasync::Scope::new();
287 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
288 });
289
290 let dict_id = 1;
291 assert_matches!(store.dictionary_create(dict_id).await.unwrap(), Ok(()));
292 assert_matches!(
293 store.dictionary_create(dict_id).await.unwrap(),
294 Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
295 );
296
297 let value = 10;
298 store.import(value, Unit::default().into()).await.unwrap().unwrap();
299 store
300 .dictionary_insert(dict_id, &fsandbox::DictionaryItem { key: "k".into(), value })
301 .await
302 .unwrap()
303 .unwrap();
304
305 let (client, server) = fidl::Channel::create();
307 store.dictionary_legacy_export(dict_id, server).await.unwrap().unwrap();
308 let dict_id = 2;
309 store.dictionary_legacy_import(dict_id, client).await.unwrap().unwrap();
310
311 let (iterator, server_end) = create_proxy();
313 store.dictionary_keys(dict_id, server_end).await.unwrap().unwrap();
314 let keys = iterator.get_next().await.unwrap();
315 assert!(iterator.get_next().await.unwrap().is_empty());
316 assert_eq!(keys, ["k"]);
317 }
318
319 #[fuchsia::test]
320 async fn legacy_import_error() {
321 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
322 let _server = fasync::Task::spawn(async move {
323 let receiver_scope = fasync::Scope::new();
324 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
325 });
326
327 store.dictionary_create(10).await.unwrap().unwrap();
328 let (dict_ch, server) = fidl::Channel::create();
329 store.dictionary_legacy_export(10, server).await.unwrap().unwrap();
330
331 let cap1 = Capability::Data(Data::Int64(42));
332 store
333 .import(1, cap1.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
334 .await
335 .unwrap()
336 .unwrap();
337 assert_matches!(
338 store.dictionary_legacy_import(1, dict_ch).await.unwrap(),
339 Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
340 );
341
342 let (ch, _) = fidl::Channel::create();
343 assert_matches!(
344 store.dictionary_legacy_import(2, ch.into()).await.unwrap(),
345 Err(fsandbox::CapabilityStoreError::BadCapability)
346 );
347 }
348
349 #[fuchsia::test]
350 async fn legacy_export_error() {
351 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
352 let _server = fasync::Task::spawn(async move {
353 let receiver_scope = fasync::Scope::new();
354 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
355 });
356
357 let (_dict_ch, server) = fidl::Channel::create();
358 assert_matches!(
359 store.dictionary_legacy_export(1, server).await.unwrap(),
360 Err(fsandbox::CapabilityStoreError::IdNotFound)
361 );
362 }
363
364 #[test_case(TestType::Small)]
365 #[test_case(TestType::Big)]
366 #[fuchsia::test]
367 async fn insert(test_type: TestType) {
368 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
369 let _server = fasync::Task::spawn(async move {
370 let receiver_scope = fasync::Scope::new();
371 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
372 });
373
374 let dict = new_dict(test_type);
375 let dict_ref = Capability::Dictionary(dict.clone())
376 .into_fsandbox_capability(WeakInstanceToken::new_invalid());
377 let dict_id = 1;
378 store.import(dict_id, dict_ref).await.unwrap().unwrap();
379
380 let unit = Unit::default().into();
381 let value = 2;
382 store.import(value, unit).await.unwrap().unwrap();
383 store
384 .dictionary_insert(
385 dict_id,
386 &fsandbox::DictionaryItem { key: CAP_KEY.to_string(), value },
387 )
388 .await
389 .unwrap()
390 .unwrap();
391
392 assert_eq!(adjusted_len(&dict, test_type), 1);
394
395 let cap = dict.remove(&*CAP_KEY).expect("not in entries after insert");
397 let Capability::Unit(unit) = cap else { panic!("Bad capability type: {:#?}", cap) };
398 assert_eq!(unit, Unit::default());
399 }
400
401 #[fuchsia::test]
402 async fn insert_error() {
403 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
404 let _server = fasync::Task::spawn(async move {
405 let receiver_scope = fasync::Scope::new();
406 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
407 });
408
409 let unit = Unit::default().into();
410 let value = 2;
411 store.import(value, unit).await.unwrap().unwrap();
412
413 assert_matches!(
414 store
415 .dictionary_insert(1, &fsandbox::DictionaryItem { key: "k".into(), value })
416 .await
417 .unwrap(),
418 Err(fsandbox::CapabilityStoreError::IdNotFound)
419 );
420 assert_matches!(
421 store
422 .dictionary_insert(2, &fsandbox::DictionaryItem { key: "k".into(), value })
423 .await
424 .unwrap(),
425 Err(fsandbox::CapabilityStoreError::WrongType)
426 );
427
428 store.dictionary_create(1).await.unwrap().unwrap();
429 assert_matches!(
430 store
431 .dictionary_insert(1, &fsandbox::DictionaryItem { key: "^bad".into(), value })
432 .await
433 .unwrap(),
434 Err(fsandbox::CapabilityStoreError::InvalidKey)
435 );
436
437 assert_matches!(
438 store
439 .dictionary_insert(1, &fsandbox::DictionaryItem { key: "k".into(), value })
440 .await
441 .unwrap(),
442 Ok(())
443 );
444
445 let unit = Unit::default().into();
446 let value = 3;
447 store.import(value, unit).await.unwrap().unwrap();
448 assert_matches!(
449 store
450 .dictionary_insert(1, &fsandbox::DictionaryItem { key: "k".into(), value })
451 .await
452 .unwrap(),
453 Err(fsandbox::CapabilityStoreError::ItemAlreadyExists)
454 );
455 }
456
457 #[test_case(TestType::Small)]
458 #[test_case(TestType::Big)]
459 #[fuchsia::test]
460 async fn remove(test_type: TestType) {
461 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
462 let _server = fasync::Task::spawn(async move {
463 let receiver_scope = fasync::Scope::new();
464 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
465 });
466
467 let dict = new_dict(test_type);
468
469 dict.insert(CAP_KEY.clone(), Capability::Unit(Unit::default())).unwrap();
471 assert_eq!(adjusted_len(&dict, test_type), 1);
472
473 let dict_ref = Capability::Dictionary(dict.clone())
474 .into_fsandbox_capability(WeakInstanceToken::new_invalid());
475 let dict_id = 1;
476 store.import(dict_id, dict_ref).await.unwrap().unwrap();
477
478 let dest_id = 2;
479 store
480 .dictionary_remove(
481 dict_id,
482 &CAP_KEY.to_string(),
483 Some(&fsandbox::WrappedNewCapabilityId { id: dest_id }),
484 )
485 .await
486 .unwrap()
487 .unwrap();
488 let cap = store.export(dest_id).await.unwrap().unwrap();
489 assert_eq!(cap, Unit::default().into());
491
492 assert_eq!(adjusted_len(&dict, test_type), 0);
494 }
495
496 #[fuchsia::test]
497 async fn remove_error() {
498 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
499 let _server = fasync::Task::spawn(async move {
500 let receiver_scope = fasync::Scope::new();
501 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
502 });
503
504 assert_matches!(
505 store.dictionary_remove(1, "k".into(), None).await.unwrap(),
506 Err(fsandbox::CapabilityStoreError::IdNotFound)
507 );
508
509 store.dictionary_create(1).await.unwrap().unwrap();
510
511 let unit = Unit::default().into();
512 store.import(2, unit).await.unwrap().unwrap();
513
514 assert_matches!(
515 store.dictionary_remove(2, "k".into(), None).await.unwrap(),
516 Err(fsandbox::CapabilityStoreError::WrongType)
517 );
518 store
519 .dictionary_insert(1, &fsandbox::DictionaryItem { key: "k".into(), value: 2 })
520 .await
521 .unwrap()
522 .unwrap();
523 assert_matches!(
524 store
525 .dictionary_remove(1, "k".into(), Some(&fsandbox::WrappedNewCapabilityId { id: 1 }))
526 .await
527 .unwrap(),
528 Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
529 );
530 assert_matches!(
531 store.dictionary_remove(1, "^bad".into(), None).await.unwrap(),
532 Err(fsandbox::CapabilityStoreError::InvalidKey)
533 );
534 assert_matches!(
535 store.dictionary_remove(1, "not_found".into(), None).await.unwrap(),
536 Err(fsandbox::CapabilityStoreError::ItemNotFound)
537 );
538 }
539
540 #[test_case(TestType::Small)]
541 #[test_case(TestType::Big)]
542 #[fuchsia::test]
543 async fn get(test_type: TestType) {
544 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
545 let _server = fasync::Task::spawn(async move {
546 let receiver_scope = fasync::Scope::new();
547 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
548 });
549
550 let dict = new_dict(test_type);
551
552 dict.insert(CAP_KEY.clone(), Capability::Unit(Unit::default())).unwrap();
553 assert_eq!(adjusted_len(&dict, test_type), 1);
554 let (ch, _) = fidl::Channel::create();
555 let handle = Handle::from(ch.into_handle());
556 dict.insert("h".parse().unwrap(), Capability::Handle(handle)).unwrap();
557
558 let dict_ref = Capability::Dictionary(dict.clone())
559 .into_fsandbox_capability(WeakInstanceToken::new_invalid());
560 let dict_id = 1;
561 store.import(dict_id, dict_ref).await.unwrap().unwrap();
562
563 let dest_id = 2;
564 store.dictionary_get(dict_id, CAP_KEY.as_str(), dest_id).await.unwrap().unwrap();
565 let cap = store.export(dest_id).await.unwrap().unwrap();
566 assert_eq!(cap, Unit::default().into());
567 }
568
569 #[fuchsia::test]
570 async fn get_error() {
571 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
572 let _server = fasync::Task::spawn(async move {
573 let receiver_scope = fasync::Scope::new();
574 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
575 });
576
577 assert_matches!(
578 store.dictionary_get(1, "k".into(), 2).await.unwrap(),
579 Err(fsandbox::CapabilityStoreError::IdNotFound)
580 );
581
582 store.dictionary_create(1).await.unwrap().unwrap();
583
584 store.import(2, Unit::default().into()).await.unwrap().unwrap();
585
586 assert_matches!(
587 store.dictionary_get(2, "k".into(), 3).await.unwrap(),
588 Err(fsandbox::CapabilityStoreError::WrongType)
589 );
590 store
591 .dictionary_insert(1, &fsandbox::DictionaryItem { key: "k".into(), value: 2 })
592 .await
593 .unwrap()
594 .unwrap();
595
596 store.import(2, Unit::default().into()).await.unwrap().unwrap();
597 assert_matches!(
598 store.dictionary_get(1, "k".into(), 2).await.unwrap(),
599 Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
600 );
601 assert_matches!(
602 store.dictionary_get(1, "^bad".into(), 3).await.unwrap(),
603 Err(fsandbox::CapabilityStoreError::InvalidKey)
604 );
605 assert_matches!(
606 store.dictionary_get(1, "not_found".into(), 3).await.unwrap(),
607 Err(fsandbox::CapabilityStoreError::ItemNotFound)
608 );
609
610 let (ch, _) = fidl::Channel::create();
612 let handle = Handle::from(ch.into_handle());
613 store
614 .import(3, handle.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
615 .await
616 .unwrap()
617 .unwrap();
618 store
619 .dictionary_insert(1, &fsandbox::DictionaryItem { key: "h".into(), value: 3 })
620 .await
621 .unwrap()
622 .unwrap();
623 assert_matches!(
624 store.dictionary_get(1, "h".into(), 3).await.unwrap(),
625 Err(fsandbox::CapabilityStoreError::NotDuplicatable)
626 );
627 }
628
629 #[test_case(TestType::Small)]
630 #[test_case(TestType::Big)]
631 #[fuchsia::test]
632 async fn copy(test_type: TestType) {
633 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
634 let _server = fasync::Task::spawn(async move {
635 let receiver_scope = fasync::Scope::new();
636 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
637 });
638
639 let dict = new_dict(test_type);
641 dict.insert("unit1".parse().unwrap(), Capability::Unit(Unit::default())).unwrap();
642 store
643 .import(
644 1,
645 dict.clone().into_fsandbox_capability(WeakInstanceToken::new_invalid()).into(),
646 )
647 .await
648 .unwrap()
649 .unwrap();
650 store.dictionary_copy(1, 2).await.unwrap().unwrap();
651
652 store.import(3, Unit::default().into()).await.unwrap().unwrap();
654 store
655 .dictionary_insert(2, &fsandbox::DictionaryItem { key: "k".into(), value: 3 })
656 .await
657 .unwrap()
658 .unwrap();
659
660 let copy = store.export(2).await.unwrap().unwrap();
662 let copy = Capability::try_from(copy).unwrap();
663 let Capability::Dictionary(copy) = copy else { panic!() };
664 {
665 assert_eq!(adjusted_len(©, test_type), 2);
666 let copy = copy.lock();
667 assert!(copy.entries.iter().all(|(_, value)| matches!(value, Capability::Unit(_))));
668 }
669
670 {
672 assert_eq!(adjusted_len(&dict, test_type), 1);
673 let dict = dict.lock();
674 assert!(dict.entries.iter().all(|(_, value)| matches!(value, Capability::Unit(_))));
675 }
676 }
677
678 #[fuchsia::test]
679 async fn copy_error() {
680 let (store, stream) = 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 assert_matches!(
687 store.dictionary_copy(1, 2).await.unwrap(),
688 Err(fsandbox::CapabilityStoreError::IdNotFound)
689 );
690
691 store.dictionary_create(1).await.unwrap().unwrap();
692 store.import(2, Unit::default().into()).await.unwrap().unwrap();
693 assert_matches!(
694 store.dictionary_copy(1, 2).await.unwrap(),
695 Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
696 );
697
698 assert_matches!(
699 store.dictionary_copy(2, 3).await.unwrap(),
700 Err(fsandbox::CapabilityStoreError::WrongType)
701 );
702
703 let (ch, _) = fidl::Channel::create();
705 let handle = Handle::from(ch.into_handle());
706 store
707 .import(3, handle.into_fsandbox_capability(WeakInstanceToken::new_invalid()))
708 .await
709 .unwrap()
710 .unwrap();
711 store
712 .dictionary_insert(1, &fsandbox::DictionaryItem { key: "h".into(), value: 3 })
713 .await
714 .unwrap()
715 .unwrap();
716 assert_matches!(
717 store.dictionary_copy(1, 3).await.unwrap(),
718 Err(fsandbox::CapabilityStoreError::NotDuplicatable)
719 );
720 }
721
722 #[test_case(TestType::Small)]
723 #[test_case(TestType::Big)]
724 #[fuchsia::test]
725 async fn duplicate(test_type: TestType) {
726 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
727 let _server = fasync::Task::spawn(async move {
728 let receiver_scope = fasync::Scope::new();
729 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
730 });
731
732 let dict = new_dict(test_type);
733 store
734 .import(1, dict.clone().into_fsandbox_capability(WeakInstanceToken::new_invalid()))
735 .await
736 .unwrap()
737 .unwrap();
738 store.duplicate(1, 2).await.unwrap().unwrap();
739
740 store.import(3, Unit::default().into()).await.unwrap().unwrap();
742 store
743 .dictionary_insert(2, &fsandbox::DictionaryItem { key: "k".into(), value: 3 })
744 .await
745 .unwrap()
746 .unwrap();
747 let dict_dup = store.export(2).await.unwrap().unwrap();
748 let dict_dup = Capability::try_from(dict_dup).unwrap();
749 let Capability::Dictionary(dict_dup) = dict_dup else { panic!() };
750 assert_eq!(adjusted_len(&dict_dup, test_type), 1);
751
752 assert_eq!(adjusted_len(&dict_dup, test_type), 1);
754 }
755
756 #[test_case(TestType::Small)]
758 #[test_case(TestType::Big)]
759 #[fuchsia::test]
760 async fn read(test_type: TestType) {
761 let dict = new_dict(test_type);
762 let id_gen = sandbox::CapabilityIdGenerator::new();
763
764 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
765 let _server = fasync::Task::spawn(async move {
766 let receiver_scope = fasync::Scope::new();
767 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
768 });
769 let dict_ref =
770 Capability::Dictionary(dict).into_fsandbox_capability(WeakInstanceToken::new_invalid());
771 let dict_id = id_gen.next();
772 store.import(dict_id, dict_ref).await.unwrap().unwrap();
773
774 let mut data_caps = vec![];
776 for i in 1..3 {
777 let id = id_gen.next();
778 store.import(id, Data::Int64(i.try_into().unwrap()).into()).await.unwrap().unwrap();
779 data_caps.push(id);
780 }
781
782 store
784 .dictionary_insert(
785 dict_id,
786 &fsandbox::DictionaryItem { key: "Cap1".into(), value: data_caps.remove(0) },
787 )
788 .await
789 .unwrap()
790 .unwrap();
791 store
792 .dictionary_insert(
793 dict_id,
794 &fsandbox::DictionaryItem { key: "Cap2".into(), value: data_caps.remove(0) },
795 )
796 .await
797 .unwrap()
798 .unwrap();
799 let (ch, _) = fidl::Channel::create();
800 let handle = ch.into_handle();
801 let id = id_gen.next();
802 store.import(id, fsandbox::Capability::Handle(handle)).await.unwrap().unwrap();
803 store
804 .dictionary_insert(dict_id, &fsandbox::DictionaryItem { key: "Cap3".into(), value: id })
805 .await
806 .unwrap()
807 .unwrap();
808
809 {
811 let (iterator, server_end) = create_proxy();
812 store.dictionary_keys(dict_id, server_end).await.unwrap().unwrap();
813 let keys = iterator.get_next().await.unwrap();
814 assert!(iterator.get_next().await.unwrap().is_empty());
815 match test_type {
816 TestType::Small => assert_eq!(keys, ["Cap1", "Cap2", "Cap3"]),
817 TestType::Big => {
818 assert_eq!(keys[0..3], ["Cap1", "Cap2", "Cap3"]);
819 assert_eq!(keys.len(), 3 + HYBRID_SWITCH_INSERTION_LEN);
820 }
821 }
822 }
823 {
825 let (iterator, server_end) = create_proxy();
826 store.dictionary_enumerate(dict_id, server_end).await.unwrap().unwrap();
827 let start_id = 100;
828 let ofs: u32 = match test_type {
829 TestType::Small => 0,
830 TestType::Big => HYBRID_SWITCH_INSERTION_LEN as u32,
831 };
832 let limit = 4 + ofs;
833 let (mut items, end_id) = iterator.get_next(start_id, limit).await.unwrap().unwrap();
834 assert_eq!(end_id, 102 + ofs as u64);
835 let (last, end_id) = iterator.get_next(end_id, limit).await.unwrap().unwrap();
836 assert!(last.is_empty());
837 assert_eq!(end_id, 102 + ofs as u64);
838
839 assert_matches!(
840 items.remove(0),
841 fsandbox::DictionaryOptionalItem {
842 key,
843 value: Some(value)
844 }
845 if key == "Cap1" && value.id == 100
846 );
847 assert_matches!(
848 store.export(100).await.unwrap().unwrap(),
849 fsandbox::Capability::Data(fsandbox::Data::Int64(1))
850 );
851 assert_matches!(
852 items.remove(0),
853 fsandbox::DictionaryOptionalItem {
854 key,
855 value: Some(value)
856 }
857 if key == "Cap2" && value.id == 101
858 );
859 assert_matches!(
860 store.export(101).await.unwrap().unwrap(),
861 fsandbox::Capability::Data(fsandbox::Data::Int64(2))
862 );
863 assert_matches!(
864 items.remove(0),
865 fsandbox::DictionaryOptionalItem {
866 key,
867 value: None
868 }
869 if key == "Cap3"
870 );
871 match test_type {
872 TestType::Small => {}
873 TestType::Big => {
874 assert_eq!(items.len(), HYBRID_SWITCH_INSERTION_LEN);
875 }
876 }
877 }
878 }
879
880 #[test_case(TestType::Small)]
881 #[test_case(TestType::Big)]
882 #[fuchsia::test]
883 async fn drain(test_type: TestType) {
884 let dict = new_dict(test_type);
885
886 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
887 let _server = fasync::Task::spawn(async move {
888 let receiver_scope = fasync::Scope::new();
889 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
890 });
891 let dict_ref = Capability::Dictionary(dict.clone())
892 .into_fsandbox_capability(WeakInstanceToken::new_invalid());
893 let dict_id = 1;
894 store.import(dict_id, dict_ref).await.unwrap().unwrap();
895
896 let mut data_caps = vec![];
898 for i in 1..3 {
899 let value = 10 + i;
900 store.import(value, Data::Int64(i.try_into().unwrap()).into()).await.unwrap().unwrap();
901 data_caps.push(value);
902 }
903
904 store
906 .dictionary_insert(
907 dict_id,
908 &fsandbox::DictionaryItem { key: "Cap1".into(), value: data_caps.remove(0) },
909 )
910 .await
911 .unwrap()
912 .unwrap();
913 store
914 .dictionary_insert(
915 dict_id,
916 &fsandbox::DictionaryItem { key: "Cap2".into(), value: data_caps.remove(0) },
917 )
918 .await
919 .unwrap()
920 .unwrap();
921 let (ch, _) = fidl::Channel::create();
922 let handle = ch.into_handle();
923 let handle_koid = handle.get_koid().unwrap();
924 let value = 20;
925 store.import(value, fsandbox::Capability::Handle(handle)).await.unwrap().unwrap();
926 store
927 .dictionary_insert(dict_id, &fsandbox::DictionaryItem { key: "Cap3".into(), value })
928 .await
929 .unwrap()
930 .unwrap();
931
932 let (iterator, server_end) = create_proxy();
933 store.dictionary_drain(dict_id, Some(server_end)).await.unwrap().unwrap();
934 let ofs: u32 = match test_type {
935 TestType::Small => 0,
936 TestType::Big => HYBRID_SWITCH_INSERTION_LEN as u32,
937 };
938 let start_id = 100;
939 let limit = 4 + ofs;
940 let (mut items, end_id) = iterator.get_next(start_id, limit).await.unwrap().unwrap();
941 assert_eq!(end_id, 103 + ofs as u64);
942 let (last, end_id) = iterator.get_next(end_id, limit).await.unwrap().unwrap();
943 assert!(last.is_empty());
944 assert_eq!(end_id, 103 + ofs as u64);
945
946 assert_matches!(
947 items.remove(0),
948 fsandbox::DictionaryItem {
949 key,
950 value: 100
951 }
952 if key == "Cap1"
953 );
954 assert_matches!(
955 store.export(100).await.unwrap().unwrap(),
956 fsandbox::Capability::Data(fsandbox::Data::Int64(1))
957 );
958 assert_matches!(
959 items.remove(0),
960 fsandbox::DictionaryItem {
961 key,
962 value: 101
963 }
964 if key == "Cap2"
965 );
966 assert_matches!(
967 store.export(101).await.unwrap().unwrap(),
968 fsandbox::Capability::Data(fsandbox::Data::Int64(2))
969 );
970 assert_matches!(
971 items.remove(0),
972 fsandbox::DictionaryItem {
973 key,
974 value: 102
975 }
976 if key == "Cap3"
977 );
978 assert_matches!(
979 store.export(102).await.unwrap().unwrap(),
980 fsandbox::Capability::Handle(handle)
981 if handle.get_koid().unwrap() == handle_koid
982 );
983
984 assert_eq!(dict.keys().count(), 0);
986 }
987
988 #[fuchsia::test]
990 async fn read_batches() {
991 const NUM_ENTRIES: u32 = fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK * 2 + 1;
995
996 const EXPECTED_CHUNK_LENGTHS: &[u32] =
998 &[fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK, fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK, 1];
999
1000 let id_gen = sandbox::CapabilityIdGenerator::new();
1001
1002 let dict = Dict::new();
1004 for i in 0..NUM_ENTRIES {
1005 dict.insert(format!("{}", i).parse().unwrap(), Capability::Unit(Unit::default()))
1006 .unwrap();
1007 }
1008
1009 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
1010 let _server = fasync::Task::spawn(async move {
1011 let receiver_scope = fasync::Scope::new();
1012 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
1013 });
1014 let dict_ref = Capability::Dictionary(dict.clone())
1015 .into_fsandbox_capability(WeakInstanceToken::new_invalid());
1016 let dict_id = id_gen.next();
1017 store.import(dict_id, dict_ref).await.unwrap().unwrap();
1018
1019 let (key_iterator, server_end) = create_proxy();
1020 store.dictionary_keys(dict_id, server_end).await.unwrap().unwrap();
1021 let (item_iterator, server_end) = create_proxy();
1022 store.dictionary_enumerate(dict_id, server_end).await.unwrap().unwrap();
1023
1024 let mut num_got_items: u32 = 0;
1026 let mut start_id = 100;
1027 let limit = fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK;
1028 for expected_len in EXPECTED_CHUNK_LENGTHS {
1029 let keys = key_iterator.get_next().await.unwrap();
1030 let (items, end_id) = item_iterator.get_next(start_id, limit).await.unwrap().unwrap();
1031 if keys.is_empty() && items.is_empty() {
1032 break;
1033 }
1034 assert_eq!(*expected_len, keys.len() as u32);
1035 assert_eq!(*expected_len, items.len() as u32);
1036 assert_eq!(u64::from(*expected_len), end_id - start_id);
1037 start_id = end_id;
1038 num_got_items += *expected_len;
1039 }
1040
1041 let (items, _) = item_iterator.get_next(start_id, limit).await.unwrap().unwrap();
1043 assert!(items.is_empty());
1044 assert!(key_iterator.get_next().await.unwrap().is_empty());
1045
1046 assert_eq!(num_got_items, NUM_ENTRIES);
1047 }
1048
1049 #[fuchsia::test]
1050 async fn drain_batches() {
1051 const NUM_ENTRIES: u32 = fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK * 2 + 1;
1055
1056 const EXPECTED_CHUNK_LENGTHS: &[u32] =
1058 &[fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK, fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK, 1];
1059
1060 let dict = Dict::new();
1062 for i in 0..NUM_ENTRIES {
1063 dict.insert(format!("{}", i).parse().unwrap(), Capability::Unit(Unit::default()))
1064 .unwrap();
1065 }
1066
1067 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
1068 let _server = fasync::Task::spawn(async move {
1069 let receiver_scope = fasync::Scope::new();
1070 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
1071 });
1072 let dict_ref = Capability::Dictionary(dict.clone())
1073 .into_fsandbox_capability(WeakInstanceToken::new_invalid());
1074 let dict_id = 1;
1075 store.import(dict_id, dict_ref).await.unwrap().unwrap();
1076
1077 let (item_iterator, server_end) = create_proxy();
1078 store.dictionary_drain(dict_id, Some(server_end)).await.unwrap().unwrap();
1079
1080 let mut num_got_items: u32 = 0;
1082 let mut start_id = 100;
1083 let limit = fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK;
1084 for expected_len in EXPECTED_CHUNK_LENGTHS {
1085 let (items, end_id) = item_iterator.get_next(start_id, limit).await.unwrap().unwrap();
1086 if items.is_empty() {
1087 break;
1088 }
1089 assert_eq!(*expected_len, items.len() as u32);
1090 assert_eq!(u64::from(*expected_len), end_id - start_id);
1091 start_id = end_id;
1092 num_got_items += *expected_len;
1093 }
1094
1095 let (items, _) = item_iterator.get_next(start_id, limit).await.unwrap().unwrap();
1097 assert!(items.is_empty());
1098
1099 assert_eq!(num_got_items, NUM_ENTRIES);
1100
1101 assert_eq!(dict.keys().count(), 0);
1103 }
1104
1105 #[fuchsia::test]
1106 async fn read_error() {
1107 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
1108 let _server = fasync::Task::spawn(async move {
1109 let receiver_scope = fasync::Scope::new();
1110 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
1111 });
1112
1113 store.import(2, Unit::default().into()).await.unwrap().unwrap();
1114
1115 let (_, server_end) = create_proxy();
1116 assert_matches!(
1117 store.dictionary_keys(1, server_end).await.unwrap(),
1118 Err(fsandbox::CapabilityStoreError::IdNotFound)
1119 );
1120 let (_, server_end) = create_proxy();
1121 assert_matches!(
1122 store.dictionary_enumerate(1, server_end).await.unwrap(),
1123 Err(fsandbox::CapabilityStoreError::IdNotFound)
1124 );
1125 assert_matches!(
1126 store.dictionary_drain(1, None).await.unwrap(),
1127 Err(fsandbox::CapabilityStoreError::IdNotFound)
1128 );
1129
1130 let (_, server_end) = create_proxy();
1131 assert_matches!(
1132 store.dictionary_keys(2, server_end).await.unwrap(),
1133 Err(fsandbox::CapabilityStoreError::WrongType)
1134 );
1135 let (_, server_end) = create_proxy();
1136 assert_matches!(
1137 store.dictionary_enumerate(2, server_end).await.unwrap(),
1138 Err(fsandbox::CapabilityStoreError::WrongType)
1139 );
1140 assert_matches!(
1141 store.dictionary_drain(2, None).await.unwrap(),
1142 Err(fsandbox::CapabilityStoreError::WrongType)
1143 );
1144 }
1145
1146 #[fuchsia::test]
1147 async fn read_iterator_error() {
1148 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
1149 let _server = fasync::Task::spawn(async move {
1150 let receiver_scope = fasync::Scope::new();
1151 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
1152 });
1153
1154 store.dictionary_create(1).await.unwrap().unwrap();
1155
1156 {
1157 let (iterator, server_end) = create_proxy();
1158 store.dictionary_enumerate(1, server_end).await.unwrap().unwrap();
1159 assert_matches!(
1160 iterator.get_next(2, fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK + 1).await.unwrap(),
1161 Err(fsandbox::CapabilityStoreError::InvalidArgs)
1162 );
1163 let (iterator, server_end) = create_proxy();
1164 store.dictionary_enumerate(1, server_end).await.unwrap().unwrap();
1165 assert_matches!(
1166 iterator.get_next(2, 0).await.unwrap(),
1167 Err(fsandbox::CapabilityStoreError::InvalidArgs)
1168 );
1169
1170 let (iterator, server_end) = create_proxy();
1171 store.dictionary_drain(1, Some(server_end)).await.unwrap().unwrap();
1172 assert_matches!(
1173 iterator.get_next(2, fsandbox::MAX_DICTIONARY_ITERATOR_CHUNK + 1).await.unwrap(),
1174 Err(fsandbox::CapabilityStoreError::InvalidArgs)
1175 );
1176 let (iterator, server_end) = create_proxy();
1177 store.dictionary_drain(1, Some(server_end)).await.unwrap().unwrap();
1178 assert_matches!(
1179 iterator.get_next(2, 0).await.unwrap(),
1180 Err(fsandbox::CapabilityStoreError::InvalidArgs)
1181 );
1182 }
1183
1184 store.import(4, Unit::default().into()).await.unwrap().unwrap();
1185 for i in 0..3 {
1186 store.import(2, Unit::default().into()).await.unwrap().unwrap();
1187 store
1188 .dictionary_insert(1, &fsandbox::DictionaryItem { key: format!("k{i}"), value: 2 })
1189 .await
1190 .unwrap()
1191 .unwrap();
1192 }
1193
1194 {
1196 let (iterator, server_end) = create_proxy();
1197 store.dictionary_enumerate(1, server_end).await.unwrap().unwrap();
1198 assert_matches!(
1199 iterator.get_next(2, 3).await.unwrap(),
1200 Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
1201 );
1202
1203 let (iterator, server_end) = create_proxy();
1204 store.dictionary_drain(1, Some(server_end)).await.unwrap().unwrap();
1205 assert_matches!(
1206 iterator.get_next(2, 3).await.unwrap(),
1207 Err(fsandbox::CapabilityStoreError::IdAlreadyExists)
1208 );
1209 }
1210 }
1211
1212 #[fuchsia::test]
1213 async fn try_into_open_error_not_supported() {
1214 let dict = Dict::new();
1215 dict.insert(CAP_KEY.clone(), Capability::Unit(Unit::default()))
1216 .expect("dict entry already exists");
1217 let scope = ExecutionScope::new();
1218 assert_matches!(
1219 dict.try_into_directory_entry(scope, WeakInstanceToken::new_invalid()).err(),
1220 Some(ConversionError::NotSupported)
1221 );
1222 }
1223
1224 struct MockDir(Counter);
1225 impl DirectoryEntry for MockDir {
1226 fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
1227 request.open_remote(self)
1228 }
1229 }
1230 impl GetEntryInfo for MockDir {
1231 fn entry_info(&self) -> EntryInfo {
1232 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::Directory)
1233 }
1234 }
1235 impl RemoteLike for MockDir {
1236 fn open(
1237 self: Arc<Self>,
1238 _scope: ExecutionScope,
1239 relative_path: Path,
1240 _flags: fio::Flags,
1241 _object_request: ObjectRequestRef<'_>,
1242 ) -> Result<(), Status> {
1243 assert_eq!(relative_path.as_ref(), "bar");
1244 self.0.inc();
1245 Ok(())
1246 }
1247 }
1248
1249 #[fuchsia::test]
1250 async fn try_into_open_success() {
1251 let dict = Dict::new();
1252 let mock_dir = Arc::new(MockDir(Counter::new(0)));
1253 dict.insert(CAP_KEY.clone(), Capability::DirEntry(DirEntry::new(mock_dir.clone())))
1254 .expect("dict entry already exists");
1255 let scope = ExecutionScope::new();
1256 let remote =
1257 dict.try_into_directory_entry(scope.clone(), WeakInstanceToken::new_invalid()).unwrap();
1258
1259 let dir_client_end = serve_directory(remote.clone(), &scope, fio::PERM_READABLE).unwrap();
1260
1261 assert_eq!(mock_dir.0.get(), 0);
1262 let (client_end, server_end) = Channel::create();
1263 let dir = dir_client_end.channel();
1264 fdio::service_connect_at(dir, &format!("{}/bar", *CAP_KEY), server_end).unwrap();
1265 fasync::Channel::from_channel(client_end).on_closed().await.unwrap();
1266 assert_eq!(mock_dir.0.get(), 1);
1267 }
1268
1269 #[fuchsia::test]
1270 async fn try_into_open_success_nested() {
1271 let inner_dict = Dict::new();
1272 let mock_dir = Arc::new(MockDir(Counter::new(0)));
1273 inner_dict
1274 .insert(CAP_KEY.clone(), Capability::DirEntry(DirEntry::new(mock_dir.clone())))
1275 .expect("dict entry already exists");
1276 let dict = Dict::new();
1277 dict.insert(CAP_KEY.clone(), Capability::Dictionary(inner_dict)).unwrap();
1278
1279 let scope = ExecutionScope::new();
1280 let remote = dict
1281 .try_into_directory_entry(scope.clone(), WeakInstanceToken::new_invalid())
1282 .expect("convert dict into Open capability");
1283
1284 let dir_client_end = serve_directory(remote.clone(), &scope, fio::PERM_READABLE).unwrap();
1285
1286 let dir = dir_client_end.into_proxy();
1288 assert_eq!(
1289 fuchsia_fs::directory::readdir(&dir).await.unwrap(),
1290 vec![directory::DirEntry {
1291 name: CAP_KEY.to_string(),
1292 kind: fio::DirentType::Directory
1293 },]
1294 );
1295
1296 assert_eq!(mock_dir.0.get(), 0);
1298 let (client_end, server_end) = Channel::create();
1299 let dir = dir.into_channel().unwrap().into_zx_channel();
1300 fdio::service_connect_at(&dir, &format!("{}/{}/bar", *CAP_KEY, *CAP_KEY), server_end)
1301 .unwrap();
1302 fasync::Channel::from_channel(client_end).on_closed().await.unwrap();
1303 assert_eq!(mock_dir.0.get(), 1)
1304 }
1305
1306 #[fuchsia::test]
1307 async fn switch_between_vec_and_map() {
1308 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
1309 let _server = fasync::Task::spawn(async move {
1310 let receiver_scope = fasync::Scope::new();
1311 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
1312 });
1313
1314 let dict = Dict::new();
1315 let dict_ref = Capability::Dictionary(dict.clone())
1316 .into_fsandbox_capability(WeakInstanceToken::new_invalid());
1317 let dict_id = 1;
1318 store.import(dict_id, dict_ref).await.unwrap().unwrap();
1319
1320 {
1322 for i in (1..=HYBRID_SWITCH_INSERTION_LEN - 1).rev() {
1323 let unit = Unit::default().into();
1324 let value = (i + 10) as u64;
1325 store.import(value, unit).await.unwrap().unwrap();
1326 store
1327 .dictionary_insert(
1328 dict_id,
1329 &fsandbox::DictionaryItem { key: key_for(i).into(), value },
1330 )
1331 .await
1332 .unwrap()
1333 .unwrap();
1334 }
1335
1336 let entries = &dict.lock().entries;
1337 let HybridMap::Vec(v) = entries else { panic!() };
1338 v.iter().for_each(|(_, v)| assert_matches!(v, Capability::Unit(_)));
1339 let actual_keys: Vec<Key> = v.iter().map(|(k, _)| k.clone()).collect();
1340 let expected: Vec<Key> =
1341 (1..=HYBRID_SWITCH_INSERTION_LEN - 1).map(|i| key_for(i)).collect();
1342 assert_eq!(actual_keys, expected);
1343 }
1344
1345 {
1347 let i = HYBRID_SWITCH_INSERTION_LEN;
1348 let unit = Unit::default().into();
1349 let value = (i + 10) as u64;
1350 store.import(value, unit).await.unwrap().unwrap();
1351 store
1352 .dictionary_insert(
1353 dict_id,
1354 &fsandbox::DictionaryItem { key: key_for(i).into(), value },
1355 )
1356 .await
1357 .unwrap()
1358 .unwrap();
1359
1360 let entries = &dict.lock().entries;
1361 let HybridMap::Map(m) = entries else { panic!() };
1362 m.iter().for_each(|(_, m)| assert_matches!(m, Capability::Unit(_)));
1363 let actual_keys: Vec<Key> = m.iter().map(|(k, _)| k.clone()).collect();
1364 let expected: Vec<Key> =
1365 (1..=HYBRID_SWITCH_INSERTION_LEN).map(|i| key_for(i)).collect();
1366 assert_eq!(actual_keys, expected);
1367 }
1368
1369 {
1371 for i in (HYBRID_SWITCH_INSERTION_LEN - HYBRID_SWITCH_REMOVAL_LEN + 1
1372 ..=HYBRID_SWITCH_INSERTION_LEN)
1373 .rev()
1374 {
1375 store.dictionary_remove(dict_id, key_for(i).as_str(), None).await.unwrap().unwrap();
1376 }
1377
1378 let entries = &dict.lock().entries;
1379 let HybridMap::Map(m) = entries else { panic!() };
1380 m.iter().for_each(|(_, v)| assert_matches!(v, Capability::Unit(_)));
1381 let actual_keys: Vec<Key> = m.iter().map(|(k, _)| k.clone()).collect();
1382 let expected: Vec<Key> =
1383 (1..=HYBRID_SWITCH_REMOVAL_LEN + 1).map(|i| key_for(i)).collect();
1384 assert_eq!(actual_keys, expected);
1385 }
1386
1387 {
1389 let i = HYBRID_SWITCH_REMOVAL_LEN + 1;
1390 store.dictionary_remove(dict_id, key_for(i).as_str(), None).await.unwrap().unwrap();
1391
1392 let entries = &dict.lock().entries;
1393 let HybridMap::Vec(v) = entries else { panic!() };
1394 v.iter().for_each(|(_, v)| assert_matches!(v, Capability::Unit(_)));
1395 let actual_keys: Vec<Key> = v.iter().map(|(k, _)| k.clone()).collect();
1396 let expected: Vec<Key> = (1..=HYBRID_SWITCH_REMOVAL_LEN).map(|i| key_for(i)).collect();
1397 assert_eq!(actual_keys, expected);
1398 }
1399 }
1400
1401 #[test_case(TestType::Small)]
1402 #[test_case(TestType::Big)]
1403 #[fuchsia::test]
1404 async fn register_update_notifier(test_type: TestType) {
1405 use std::sync::mpsc;
1410
1411 let (store, stream) = create_proxy_and_stream::<fsandbox::CapabilityStoreMarker>();
1412 let _server = fasync::Task::spawn(async move {
1413 let receiver_scope = fasync::Scope::new();
1414 serve_capability_store(stream, &receiver_scope, WeakInstanceToken::new_invalid()).await
1415 });
1416
1417 #[derive(PartialEq)]
1418 enum Update {
1419 Add(Key),
1420 Remove(Key),
1421 Idle,
1422 }
1423 impl fmt::Debug for Update {
1424 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1425 match self {
1426 Self::Add(k) => write!(f, "Add({k})"),
1427 Self::Remove(k) => write!(f, "Remove({k})"),
1428 Self::Idle => write!(f, "Idle"),
1429 }
1430 }
1431 }
1432
1433 let dict = new_dict(test_type);
1434 let (update_tx, update_rx) = mpsc::channel();
1435 let subscribed = Arc::new(Mutex::new(true));
1436 let subscribed2 = subscribed.clone();
1437 dict.register_update_notifier(Box::new(move |update: EntryUpdate<'_>| {
1438 let u = match update {
1439 EntryUpdate::Add(k, v) => {
1440 assert_matches!(v, Capability::Unit(_));
1441 Update::Add(k.into())
1442 }
1443 EntryUpdate::Remove(k) => Update::Remove(k.into()),
1444 EntryUpdate::Idle => Update::Idle,
1445 };
1446 update_tx.send(u).unwrap();
1447 if *subscribed2.lock() {
1448 UpdateNotifierRetention::Retain
1449 } else {
1450 UpdateNotifierRetention::Drop_
1451 }
1452 }));
1453 let dict_ref = Capability::Dictionary(dict.clone())
1454 .into_fsandbox_capability(WeakInstanceToken::new_invalid());
1455 let dict_id = 1;
1456 store.import(dict_id, dict_ref).await.unwrap().unwrap();
1457
1458 let i = 1;
1460 let unit = Unit::default().into();
1461 let value = (i + 10) as u64;
1462 store.import(value, unit).await.unwrap().unwrap();
1463 store
1464 .dictionary_insert(dict_id, &fsandbox::DictionaryItem { key: key_for(i).into(), value })
1465 .await
1466 .unwrap()
1467 .unwrap();
1468
1469 for expected_result in [Ok(()), Err(fsandbox::CapabilityStoreError::ItemAlreadyExists)] {
1470 let i = 2;
1471 let unit = Unit::default().into();
1472 let value = (i + 10) as u64;
1473 store.import(value, unit).await.unwrap().unwrap();
1474 let result = store
1475 .dictionary_insert(
1476 dict_id,
1477 &fsandbox::DictionaryItem { key: key_for(i).into(), value },
1478 )
1479 .await
1480 .unwrap();
1481 assert_eq!(result, expected_result);
1482 }
1483
1484 for expected_result in [Ok(()), Err(fsandbox::CapabilityStoreError::ItemNotFound)] {
1486 let i = 1;
1487 let result =
1488 store.dictionary_remove(dict_id, &key_for(i).as_str(), None).await.unwrap();
1489 assert_eq!(result, expected_result);
1490 }
1491
1492 let i = 3;
1494 let unit = Unit::default().into();
1495 let value = (i + 10) as u64;
1496 store.import(value, unit).await.unwrap().unwrap();
1497 store
1498 .dictionary_insert(dict_id, &fsandbox::DictionaryItem { key: key_for(i).into(), value })
1499 .await
1500 .unwrap()
1501 .unwrap();
1502 store.dictionary_drain(dict_id, None).await.unwrap().unwrap();
1503
1504 *subscribed.lock() = false;
1506 let i = 4;
1507 let unit = Unit::default().into();
1508 let value = (i + 10) as u64;
1509 store.import(value, unit).await.unwrap().unwrap();
1510 store
1511 .dictionary_insert(dict_id, &fsandbox::DictionaryItem { key: key_for(i).into(), value })
1512 .await
1513 .unwrap()
1514 .unwrap();
1515 store.dictionary_remove(dict_id, key_for(i).as_str(), None).await.unwrap().unwrap();
1517
1518 let updates: Vec<_> = iter::from_fn(move || match update_rx.try_recv() {
1520 Ok(e) => Some(e),
1521 Err(mpsc::TryRecvError::Disconnected) => None,
1522 Err(mpsc::TryRecvError::Empty) => unreachable!(),
1524 })
1525 .collect();
1526 let expected_updates = [
1527 Update::Idle,
1528 Update::Add(key_for(1)),
1530 Update::Add(key_for(2)),
1531 Update::Remove(key_for(2)),
1532 Update::Add(key_for(2)),
1533 Update::Remove(key_for(1)),
1535 Update::Add(key_for(3)),
1537 Update::Remove(key_for(2)),
1538 Update::Remove(key_for(3)),
1539 Update::Add(key_for(4)),
1541 ];
1542 match test_type {
1543 TestType::Small => {
1544 assert_eq!(updates, expected_updates);
1545 }
1546 TestType::Big => {
1547 let updates = &updates[HYBRID_SWITCH_INSERTION_LEN..];
1549 let nexpected = expected_updates.len() - 1;
1550 assert_eq!(updates[..nexpected], expected_updates[..nexpected]);
1551
1552 let expected_updates = &expected_updates[nexpected..];
1554 let updates = &updates[nexpected + HYBRID_SWITCH_INSERTION_LEN..];
1555 assert_eq!(updates, expected_updates);
1556 }
1557 }
1558 }
1559
1560 #[fuchsia::test]
1561 async fn live_update_add_nodes() {
1562 let dict = Dict::new();
1563 let scope = ExecutionScope::new();
1564 let remote = dict
1565 .clone()
1566 .try_into_directory_entry(scope.clone(), WeakInstanceToken::new_invalid())
1567 .unwrap();
1568 let dir_proxy =
1569 serve_directory(remote.clone(), &scope, fio::PERM_READABLE).unwrap().into_proxy();
1570 let mut watcher = fuchsia_fs::directory::Watcher::new(&dir_proxy)
1571 .await
1572 .expect("failed to create watcher");
1573
1574 assert_eq!(fuchsia_fs::directory::readdir(&dir_proxy).await.unwrap(), vec![]);
1576 assert_eq!(
1577 watcher.next().await,
1578 Some(Ok(fuchsia_fs::directory::WatchMessage {
1579 event: fuchsia_fs::directory::WatchEvent::EXISTING,
1580 filename: ".".into(),
1581 })),
1582 );
1583 assert_eq!(
1584 watcher.next().await,
1585 Some(Ok(fuchsia_fs::directory::WatchMessage {
1586 event: fuchsia_fs::directory::WatchEvent::IDLE,
1587 filename: "".into(),
1588 })),
1589 );
1590
1591 let fs = pseudo_directory! {};
1594 let dir_entry = DirEntry::new(fs);
1595 dict.insert("a".parse().unwrap(), Capability::DirEntry(dir_entry.clone()))
1596 .expect("dict entry already exists");
1597
1598 assert_eq!(
1599 fuchsia_fs::directory::readdir(&dir_proxy).await.unwrap(),
1600 vec![directory::DirEntry { name: "a".to_string(), kind: fio::DirentType::Directory },]
1601 );
1602 assert_eq!(
1603 watcher.next().await,
1604 Some(Ok(fuchsia_fs::directory::WatchMessage {
1605 event: fuchsia_fs::directory::WatchEvent::ADD_FILE,
1606 filename: "a".into(),
1607 })),
1608 );
1609
1610 dict.insert("b".parse().unwrap(), Capability::DirEntry(dir_entry))
1613 .expect("dict entry already exists");
1614 let mut readdir_results = fuchsia_fs::directory::readdir(&dir_proxy).await.unwrap();
1615 readdir_results.sort_by(|entry_1, entry_2| entry_1.name.cmp(&entry_2.name));
1616 assert_eq!(
1617 readdir_results,
1618 vec![
1619 directory::DirEntry { name: "a".to_string(), kind: fio::DirentType::Directory },
1620 directory::DirEntry { name: "b".to_string(), kind: fio::DirentType::Directory },
1621 ]
1622 );
1623 assert_eq!(
1624 watcher.next().await,
1625 Some(Ok(fuchsia_fs::directory::WatchMessage {
1626 event: fuchsia_fs::directory::WatchEvent::ADD_FILE,
1627 filename: "b".into(),
1628 })),
1629 );
1630 }
1631
1632 #[fuchsia::test]
1633 async fn live_update_remove_nodes() {
1634 let dict = Dict::new();
1635 let fs = pseudo_directory! {};
1636 let dir_entry = DirEntry::new(fs);
1637 dict.insert("a".parse().unwrap(), Capability::DirEntry(dir_entry.clone()))
1638 .expect("dict entry already exists");
1639 dict.insert("b".parse().unwrap(), Capability::DirEntry(dir_entry.clone()))
1640 .expect("dict entry already exists");
1641 dict.insert("c".parse().unwrap(), Capability::DirEntry(dir_entry.clone()))
1642 .expect("dict entry already exists");
1643
1644 let scope = ExecutionScope::new();
1645 let remote = dict
1646 .clone()
1647 .try_into_directory_entry(scope.clone(), WeakInstanceToken::new_invalid())
1648 .unwrap();
1649 let dir_proxy =
1650 serve_directory(remote.clone(), &scope, fio::PERM_READABLE).unwrap().into_proxy();
1651 let mut watcher = fuchsia_fs::directory::Watcher::new(&dir_proxy)
1652 .await
1653 .expect("failed to create watcher");
1654
1655 let mut readdir_results = fuchsia_fs::directory::readdir(&dir_proxy).await.unwrap();
1658 readdir_results.sort_by(|entry_1, entry_2| entry_1.name.cmp(&entry_2.name));
1659 assert_eq!(
1660 readdir_results,
1661 vec![
1662 directory::DirEntry { name: "a".to_string(), kind: fio::DirentType::Directory },
1663 directory::DirEntry { name: "b".to_string(), kind: fio::DirentType::Directory },
1664 directory::DirEntry { name: "c".to_string(), kind: fio::DirentType::Directory },
1665 ]
1666 );
1667 let mut existing_files = vec![];
1668 loop {
1669 match watcher.next().await {
1670 Some(Ok(fuchsia_fs::directory::WatchMessage { event, filename }))
1671 if event == fuchsia_fs::directory::WatchEvent::EXISTING =>
1672 {
1673 existing_files.push(filename)
1674 }
1675 Some(Ok(fuchsia_fs::directory::WatchMessage { event, filename: _ }))
1676 if event == fuchsia_fs::directory::WatchEvent::IDLE =>
1677 {
1678 break;
1679 }
1680 other_message => panic!("unexpected message: {:?}", other_message),
1681 }
1682 }
1683 existing_files.sort();
1684 let expected_files: Vec<std::path::PathBuf> =
1685 vec![".".into(), "a".into(), "b".into(), "c".into()];
1686 assert_eq!(existing_files, expected_files,);
1687
1688 let _ =
1691 dict.remove(&BorrowedKey::new("a").unwrap()).expect("capability was not in dictionary");
1692 assert_eq!(
1693 watcher.next().await,
1694 Some(Ok(fuchsia_fs::directory::WatchMessage {
1695 event: fuchsia_fs::directory::WatchEvent::REMOVE_FILE,
1696 filename: "a".into(),
1697 })),
1698 );
1699
1700 let _ =
1701 dict.remove(&BorrowedKey::new("b").unwrap()).expect("capability was not in dictionary");
1702 assert_eq!(
1703 watcher.next().await,
1704 Some(Ok(fuchsia_fs::directory::WatchMessage {
1705 event: fuchsia_fs::directory::WatchEvent::REMOVE_FILE,
1706 filename: "b".into(),
1707 })),
1708 );
1709
1710 let _ =
1711 dict.remove(&BorrowedKey::new("c").unwrap()).expect("capability was not in dictionary");
1712 assert_eq!(
1713 watcher.next().await,
1714 Some(Ok(fuchsia_fs::directory::WatchMessage {
1715 event: fuchsia_fs::directory::WatchEvent::REMOVE_FILE,
1716 filename: "c".into(),
1717 })),
1718 );
1719
1720 assert_eq!(fuchsia_fs::directory::readdir(&dir_proxy).await.unwrap(), vec![],);
1723 }
1724
1725 #[fuchsia::test]
1726 async fn into_directory_oneshot() {
1727 let dict = Dict::new();
1728 let inner_dict = Dict::new();
1729 let fs = pseudo_directory! {};
1730 let dir_entry = DirEntry::new(fs);
1731 inner_dict.insert("x".parse().unwrap(), Capability::DirEntry(dir_entry.clone())).unwrap();
1732 dict.insert("a".parse().unwrap(), Capability::DirEntry(dir_entry.clone())).unwrap();
1733 dict.insert("b".parse().unwrap(), Capability::DirEntry(dir_entry.clone())).unwrap();
1734 dict.insert("c".parse().unwrap(), Capability::Dictionary(inner_dict.clone())).unwrap();
1735
1736 let scope = ExecutionScope::new();
1737 let remote = dict
1738 .clone()
1739 .try_into_directory_entry_oneshot(scope.clone(), WeakInstanceToken::new_invalid())
1740 .unwrap();
1741 let dir_proxy =
1742 serve_directory(remote.clone(), &scope, fio::PERM_READABLE).unwrap().into_proxy();
1743
1744 let mut readdir_results = fuchsia_fs::directory::readdir(&dir_proxy).await.unwrap();
1747 readdir_results.sort_by(|entry_1, entry_2| entry_1.name.cmp(&entry_2.name));
1748 assert_eq!(
1749 readdir_results,
1750 vec![
1751 directory::DirEntry { name: "a".to_string(), kind: fio::DirentType::Directory },
1752 directory::DirEntry { name: "b".to_string(), kind: fio::DirentType::Directory },
1753 directory::DirEntry { name: "c".to_string(), kind: fio::DirentType::Directory },
1754 ]
1755 );
1756
1757 let (inner_proxy, server) = create_proxy::<fio::DirectoryMarker>();
1758 dir_proxy.open("c", Default::default(), &Default::default(), server.into()).unwrap();
1759 let readdir_results = fuchsia_fs::directory::readdir(&inner_proxy).await.unwrap();
1760 assert_eq!(
1761 readdir_results,
1762 vec![directory::DirEntry { name: "x".to_string(), kind: fio::DirentType::Directory }]
1763 );
1764
1765 assert_matches!(dict.keys().next(), None);
1767 assert_matches!(inner_dict.keys().next(), None);
1768
1769 dict.insert("z".parse().unwrap(), Capability::DirEntry(dir_entry.clone())).unwrap();
1771 let mut readdir_results = fuchsia_fs::directory::readdir(&dir_proxy).await.unwrap();
1772 readdir_results.sort_by(|entry_1, entry_2| entry_1.name.cmp(&entry_2.name));
1773 assert_eq!(
1774 readdir_results,
1775 vec![
1776 directory::DirEntry { name: "a".to_string(), kind: fio::DirentType::Directory },
1777 directory::DirEntry { name: "b".to_string(), kind: fio::DirentType::Directory },
1778 directory::DirEntry { name: "c".to_string(), kind: fio::DirentType::Directory },
1779 ]
1780 );
1781 }
1782
1783 fn key_for(i: usize) -> Key {
1787 iter::repeat("A").take(i).collect::<String>().parse().unwrap()
1788 }
1789
1790 fn new_dict(test_type: TestType) -> Dict {
1791 let dict = Dict::new();
1792 match test_type {
1793 TestType::Small => {}
1794 TestType::Big => {
1795 for i in 1..=HYBRID_SWITCH_INSERTION_LEN {
1796 dict.insert(
1799 format!("_{i}").parse().unwrap(),
1800 Capability::Unit(Unit::default()),
1801 )
1802 .unwrap();
1803 }
1804 }
1805 }
1806 dict
1807 }
1808
1809 fn adjusted_len(dict: &Dict, test_type: TestType) -> usize {
1810 let ofs = match test_type {
1811 TestType::Small => 0,
1812 TestType::Big => HYBRID_SWITCH_INSERTION_LEN,
1813 };
1814 dict.lock().entries.len() - ofs
1815 }
1816}