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