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