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