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