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