1use crate::component_id_index::make_index_file;
6use crate::{
7 CheckUse, ExpectedResult, RoutingTestModel, RoutingTestModelBuilder, generate_storage_path,
8};
9use cm_config::{CapabilityAllowlistKey, CapabilityAllowlistSource};
10use cm_rust::offer::*;
11use cm_rust::*;
12use cm_rust_testing::*;
13use component_id_index::InstanceId;
14use fidl_fuchsia_io as fio;
15use moniker::{ExtendedMoniker, Moniker};
16use std::collections::HashSet;
17use std::marker::PhantomData;
18use zx_status;
19
20pub struct CommonStorageTest<T: RoutingTestModelBuilder> {
21 builder: PhantomData<T>,
22}
23
24impl<T: RoutingTestModelBuilder> CommonStorageTest<T> {
25 pub fn new() -> Self {
26 Self { builder: PhantomData }
27 }
28
29 pub async fn test_storage_dir_from_cm_namespace(&self) {
39 let components = vec![
40 (
41 "a",
42 ComponentDeclBuilder::new()
43 .offer(
44 OfferBuilder::storage()
45 .name("cache")
46 .source(OfferSource::Self_)
47 .target_static_child("b"),
48 )
49 .child_default("b")
50 .capability(
51 CapabilityBuilder::storage()
52 .name("cache")
53 .backing_dir("tmp")
54 .source(StorageDirectorySource::Parent)
55 .subdir("cache"),
56 )
57 .build(),
58 ),
59 (
60 "b",
61 ComponentDeclBuilder::new()
62 .use_(UseBuilder::storage().name("cache").path("/storage"))
63 .build(),
64 ),
65 ];
66 let namespace_capabilities = vec![
67 CapabilityBuilder::directory()
68 .name("tmp")
69 .path("/tmp")
70 .rights(fio::RW_STAR_DIR)
71 .build(),
72 ];
73 let mut builder = T::new("a", components);
74 builder.set_namespace_capabilities(namespace_capabilities);
75 let model = builder.build().await;
76
77 model
78 .check_use(
79 ["b"].try_into().unwrap(),
80 CheckUse::Storage {
81 path: "/storage".parse().unwrap(),
82 storage_relation: Some(Moniker::try_from(["b"]).unwrap()),
83 from_cm_namespace: true,
84 storage_subdir: Some("cache".to_string()),
85 expected_res: ExpectedResult::Ok,
86 },
87 )
88 .await;
89
90 model.check_namespace_subdir_contents("/tmp/cache", vec!["b:0".to_string()]).await;
91 }
92
93 pub async fn test_storage_from_void(&self) {
100 let components = vec![
101 (
102 "a",
103 ComponentDeclBuilder::new()
104 .offer(
105 OfferBuilder::storage()
106 .name("cache")
107 .source(OfferSource::Void)
108 .target_static_child("b")
109 .availability(Availability::Optional),
110 )
111 .child_default("b")
112 .build(),
113 ),
114 (
115 "b",
116 ComponentDeclBuilder::new()
117 .use_(UseBuilder::storage().name("cache").path("/storage"))
118 .build(),
119 ),
120 ];
121 let model = T::new("a", components).build().await;
122 model
123 .check_use(
124 ["b"].try_into().unwrap(),
125 CheckUse::Storage {
126 path: "/storage".parse().unwrap(),
127 storage_relation: Some(Moniker::try_from(["b"]).unwrap()),
128 from_cm_namespace: false,
129 storage_subdir: None,
130 expected_res: ExpectedResult::Err(zx_status::Status::NOT_FOUND),
131 },
132 )
133 .await;
134 }
135
136 pub async fn test_storage_and_dir_from_parent(&self) {
144 let components = vec![
145 (
146 "a",
147 ComponentDeclBuilder::new()
148 .capability(
149 CapabilityBuilder::directory()
150 .name("data")
151 .path("/data")
152 .rights(fio::RW_STAR_DIR),
153 )
154 .offer(
155 OfferBuilder::storage()
156 .name("cache")
157 .source(OfferSource::Self_)
158 .target_static_child("b"),
159 )
160 .child_default("b")
161 .capability(
162 CapabilityBuilder::storage()
163 .name("cache")
164 .backing_dir("data")
165 .source(StorageDirectorySource::Self_),
166 )
167 .build(),
168 ),
169 (
170 "b",
171 ComponentDeclBuilder::new()
172 .use_(UseBuilder::storage().name("cache").path("/storage"))
173 .build(),
174 ),
175 ];
176 let model = T::new("a", components).build().await;
177 model
178 .check_use(
179 ["b"].try_into().unwrap(),
180 CheckUse::Storage {
181 path: "/storage".parse().unwrap(),
182 storage_relation: Some(Moniker::try_from(["b"]).unwrap()),
183 from_cm_namespace: false,
184 storage_subdir: None,
185 expected_res: ExpectedResult::Ok,
186 },
187 )
188 .await;
189 model.check_test_subdir_contents(".", vec!["b:0".to_string(), "foo".to_string()]).await;
190 }
191
192 pub async fn test_storage_and_dir_from_parent_with_subdir(&self) {
201 let components = vec![
202 (
203 "a",
204 ComponentDeclBuilder::new()
205 .capability(
206 CapabilityBuilder::directory()
207 .name("data")
208 .path("/data")
209 .rights(fio::RW_STAR_DIR),
210 )
211 .offer(
212 OfferBuilder::storage()
213 .name("cache")
214 .source(OfferSource::Self_)
215 .target_static_child("b"),
216 )
217 .child_default("b")
218 .capability(
219 CapabilityBuilder::storage()
220 .name("cache")
221 .backing_dir("data")
222 .source(StorageDirectorySource::Self_)
223 .subdir("cache"),
224 )
225 .build(),
226 ),
227 (
228 "b",
229 ComponentDeclBuilder::new()
230 .use_(UseBuilder::storage().name("cache").path("/storage"))
231 .build(),
232 ),
233 ];
234 let model = T::new("a", components).build().await;
235 model
236 .check_use(
237 ["b"].try_into().unwrap(),
238 CheckUse::Storage {
239 path: "/storage".parse().unwrap(),
240 storage_relation: Some(Moniker::try_from(["b"]).unwrap()),
241 from_cm_namespace: false,
242 storage_subdir: Some("cache".to_string()),
243 expected_res: ExpectedResult::Ok,
244 },
245 )
246 .await;
247 model.check_test_subdir_contents(".", vec!["cache".to_string(), "foo".to_string()]).await;
248 }
249
250 pub async fn test_storage_and_dir_from_parent_rights_invalid(&self) {
259 let components = vec![
260 (
261 "a",
262 ComponentDeclBuilder::new()
263 .capability(CapabilityBuilder::directory().name("data").path("/data"))
264 .offer(
265 OfferBuilder::storage()
266 .name("cache")
267 .source(OfferSource::Self_)
268 .target_static_child("b"),
269 )
270 .child_default("b")
271 .capability(
272 CapabilityBuilder::storage()
273 .name("cache")
274 .backing_dir("data")
275 .source(StorageDirectorySource::Self_),
276 )
277 .build(),
278 ),
279 (
280 "b",
281 ComponentDeclBuilder::new()
282 .use_(UseBuilder::storage().name("cache").path("/storage"))
283 .build(),
284 ),
285 ];
286 let model = T::new("a", components).build().await;
287 model
288 .check_use(
289 ["b"].try_into().unwrap(),
290 CheckUse::Storage {
291 path: "/storage".parse().unwrap(),
292 storage_relation: None,
293 from_cm_namespace: false,
294 storage_subdir: None,
295 expected_res: ExpectedResult::Err(zx_status::Status::ACCESS_DENIED),
296 },
297 )
298 .await;
299 }
300
301 pub async fn test_storage_from_parent_dir_from_grandparent(&self) {
312 let components = vec![
313 (
314 "a",
315 ComponentDeclBuilder::new()
316 .capability(
317 CapabilityBuilder::directory()
318 .name("data")
319 .path("/data")
320 .rights(fio::RW_STAR_DIR),
321 )
322 .offer(
323 OfferBuilder::directory()
324 .name("data")
325 .target_name("minfs")
326 .source(OfferSource::Self_)
327 .target_static_child("b")
328 .rights(fio::RW_STAR_DIR),
329 )
330 .child_default("b")
331 .build(),
332 ),
333 (
334 "b",
335 ComponentDeclBuilder::new()
336 .offer(
337 OfferBuilder::storage()
338 .name("data")
339 .source(OfferSource::Self_)
340 .target_static_child("c"),
341 )
342 .child_default("c")
343 .capability(
344 CapabilityBuilder::storage()
345 .name("data")
346 .backing_dir("minfs")
347 .source(StorageDirectorySource::Parent),
348 )
349 .build(),
350 ),
351 (
352 "c",
353 ComponentDeclBuilder::new()
354 .use_(UseBuilder::storage().name("data").path("/storage"))
355 .build(),
356 ),
357 ];
358 let model = T::new("a", components).build().await;
359 model
360 .check_use(
361 ["b", "c"].try_into().unwrap(),
362 CheckUse::Storage {
363 path: "/storage".parse().unwrap(),
364 storage_relation: Some(Moniker::try_from(["c"]).unwrap()),
365 from_cm_namespace: false,
366 storage_subdir: None,
367 expected_res: ExpectedResult::Ok,
368 },
369 )
370 .await;
371 }
372
373 pub async fn test_storage_from_parent_dir_from_grandparent_with_subdirs(&self) {
385 let components = vec![
386 (
387 "a",
388 ComponentDeclBuilder::new()
389 .capability(
390 CapabilityBuilder::directory()
391 .name("data")
392 .path("/data")
393 .rights(fio::RW_STAR_DIR),
394 )
395 .offer(
396 OfferBuilder::directory()
397 .name("data")
398 .target_name("minfs")
399 .source(OfferSource::Self_)
400 .target_static_child("b")
401 .rights(fio::RW_STAR_DIR)
402 .subdir("subdir_1"),
403 )
404 .child_default("b")
405 .build(),
406 ),
407 (
408 "b",
409 ComponentDeclBuilder::new()
410 .offer(
411 OfferBuilder::storage()
412 .name("data")
413 .source(OfferSource::Self_)
414 .target_static_child("c"),
415 )
416 .child_default("c")
417 .capability(
418 CapabilityBuilder::storage()
419 .name("data")
420 .backing_dir("minfs")
421 .source(StorageDirectorySource::Parent)
422 .subdir("subdir_2"),
423 )
424 .build(),
425 ),
426 (
427 "c",
428 ComponentDeclBuilder::new()
429 .use_(UseBuilder::storage().name("data").path("/storage"))
430 .build(),
431 ),
432 ];
433 let model = T::new("a", components).build().await;
434 model.add_subdir_to_data_directory("subdir_1");
435 model
436 .check_use(
437 ["b", "c"].try_into().unwrap(),
438 CheckUse::Storage {
439 path: "/storage".parse().unwrap(),
440 storage_relation: Some(Moniker::try_from(["c"]).unwrap()),
441 from_cm_namespace: false,
442 storage_subdir: Some("subdir_1/subdir_2".to_string()),
443 expected_res: ExpectedResult::Ok,
444 },
445 )
446 .await;
447
448 model
449 .check_test_subdir_contents(".", vec!["foo".to_string(), "subdir_1".to_string()])
450 .await;
451 model.check_test_subdir_contents("subdir_1", vec!["subdir_2".to_string()]).await;
452 model.check_test_subdir_contents("subdir_1/subdir_2", vec!["c:0".to_string()]).await;
453 }
454
455 pub async fn test_storage_from_parent_dir_from_grandparent_with_subdir(&self) {
466 let components = vec![
467 (
468 "a",
469 ComponentDeclBuilder::new()
470 .capability(
471 CapabilityBuilder::directory()
472 .name("data")
473 .path("/data")
474 .rights(fio::RW_STAR_DIR)
475 .build(),
476 )
477 .offer(
478 OfferBuilder::directory()
479 .name("data")
480 .target_name("minfs")
481 .source(OfferSource::Self_)
482 .target_static_child("b")
483 .rights(fio::RW_STAR_DIR),
484 )
485 .child_default("b")
486 .build(),
487 ),
488 (
489 "b",
490 ComponentDeclBuilder::new()
491 .offer(
492 OfferBuilder::storage()
493 .name("data")
494 .source(OfferSource::Self_)
495 .target_static_child("c"),
496 )
497 .child_default("c")
498 .capability(
499 CapabilityBuilder::storage()
500 .name("data")
501 .backing_dir("minfs")
502 .source(StorageDirectorySource::Parent)
503 .subdir("bar"),
504 )
505 .build(),
506 ),
507 (
508 "c",
509 ComponentDeclBuilder::new()
510 .use_(UseBuilder::storage().name("data").path("/storage"))
511 .build(),
512 ),
513 ];
514 let model = T::new("a", components).build().await;
515 model
516 .check_use(
517 ["b", "c"].try_into().unwrap(),
518 CheckUse::Storage {
519 path: "/storage".parse().unwrap(),
520 storage_relation: Some(Moniker::try_from(["c"]).unwrap()),
521 from_cm_namespace: false,
522 storage_subdir: Some("bar".to_string()),
523 expected_res: ExpectedResult::Ok,
524 },
525 )
526 .await;
527 model.check_test_subdir_contents(".", vec!["bar".to_string(), "foo".to_string()]).await;
528 }
529
530 pub async fn test_storage_and_dir_from_grandparent(&self) {
541 let components = vec![
542 (
543 "a",
544 ComponentDeclBuilder::new()
545 .capability(
546 CapabilityBuilder::directory()
547 .name("data-root")
548 .path("/data")
549 .rights(fio::RW_STAR_DIR),
550 )
551 .offer(
552 OfferBuilder::storage()
553 .name("data")
554 .source(OfferSource::Self_)
555 .target_static_child("b"),
556 )
557 .child_default("b")
558 .capability(
559 CapabilityBuilder::storage()
560 .name("data")
561 .backing_dir("data-root")
562 .source(StorageDirectorySource::Self_),
563 )
564 .build(),
565 ),
566 (
567 "b",
568 ComponentDeclBuilder::new()
569 .offer(
570 OfferBuilder::storage()
571 .name("data")
572 .source(OfferSource::Parent)
573 .target_static_child("c"),
574 )
575 .child_default("c")
576 .build(),
577 ),
578 (
579 "c",
580 ComponentDeclBuilder::new()
581 .use_(UseBuilder::storage().name("data").path("/storage"))
582 .build(),
583 ),
584 ];
585 let model = T::new("a", components).build().await;
586 model
587 .check_use(
588 ["b", "c"].try_into().unwrap(),
589 CheckUse::Storage {
590 path: "/storage".parse().unwrap(),
591 storage_relation: Some(Moniker::try_from(["b", "c"]).unwrap()),
592 from_cm_namespace: false,
593 storage_subdir: None,
594 expected_res: ExpectedResult::Ok,
595 },
596 )
597 .await;
598 }
599
600 pub async fn test_storage_from_parent_dir_from_sibling(&self) {
609 let components = vec![
610 (
611 "a",
612 ComponentDeclBuilder::new()
613 .capability(
614 CapabilityBuilder::storage()
615 .name("cache")
616 .backing_dir("minfs")
617 .source(StorageDirectorySource::Child("b".into())),
618 )
619 .offer(
620 OfferBuilder::storage()
621 .name("cache")
622 .source(OfferSource::Self_)
623 .target_static_child("c"),
624 )
625 .child_default("b")
626 .child_default("c")
627 .build(),
628 ),
629 (
630 "b",
631 ComponentDeclBuilder::new()
632 .capability(
633 CapabilityBuilder::directory()
634 .name("data")
635 .path("/data")
636 .rights(fio::RW_STAR_DIR),
637 )
638 .expose(
639 ExposeBuilder::directory()
640 .name("data")
641 .source(ExposeSource::Self_)
642 .target_name("minfs")
643 .rights(fio::RW_STAR_DIR),
644 )
645 .build(),
646 ),
647 (
648 "c",
649 ComponentDeclBuilder::new()
650 .use_(UseBuilder::storage().name("cache").path("/storage"))
651 .build(),
652 ),
653 ];
654 let model = T::new("a", components).build().await;
655 model
656 .check_use(
657 ["c"].try_into().unwrap(),
658 CheckUse::Storage {
659 path: "/storage".parse().unwrap(),
660 storage_relation: Some(Moniker::try_from(["c"]).unwrap()),
661 from_cm_namespace: false,
662 storage_subdir: None,
663 expected_res: ExpectedResult::Ok,
664 },
665 )
666 .await;
667 }
668
669 pub async fn test_storage_from_parent_dir_from_sibling_with_subdir(&self) {
679 let components = vec![
680 (
681 "a",
682 ComponentDeclBuilder::new()
683 .capability(
684 CapabilityBuilder::storage()
685 .name("cache")
686 .backing_dir("minfs")
687 .source(StorageDirectorySource::Child("b".into()))
688 .subdir("subdir_2"),
689 )
690 .offer(
691 OfferBuilder::storage()
692 .name("cache")
693 .source(OfferSource::Self_)
694 .target_static_child("c"),
695 )
696 .child_default("b")
697 .child_default("c")
698 .build(),
699 ),
700 (
701 "b",
702 ComponentDeclBuilder::new()
703 .capability(
704 CapabilityBuilder::directory()
705 .name("data")
706 .path("/data")
707 .rights(fio::RW_STAR_DIR),
708 )
709 .expose(
710 ExposeBuilder::directory()
711 .name("data")
712 .source(ExposeSource::Self_)
713 .target_name("minfs")
714 .rights(fio::RW_STAR_DIR)
715 .subdir("subdir_1"),
716 )
717 .build(),
718 ),
719 (
720 "c",
721 ComponentDeclBuilder::new()
722 .use_(UseBuilder::storage().name("cache").path("/storage"))
723 .build(),
724 ),
725 ];
726 let model = T::new("a", components).build().await;
727 model.add_subdir_to_data_directory("subdir_1");
728 model
729 .check_use(
730 ["c"].try_into().unwrap(),
731 CheckUse::Storage {
732 path: "/storage".parse().unwrap(),
733 storage_relation: Some(Moniker::try_from(["c"]).unwrap()),
734 from_cm_namespace: false,
735 storage_subdir: Some("subdir_1/subdir_2".to_string()),
736 expected_res: ExpectedResult::Ok,
737 },
738 )
739 .await;
740 model
741 .check_test_subdir_contents(".", vec!["foo".to_string(), "subdir_1".to_string()])
742 .await;
743 model.check_test_subdir_contents("subdir_1", vec!["subdir_2".to_string()]).await;
744 model.check_test_subdir_contents("subdir_1/subdir_2", vec!["c:0".to_string()]).await;
745 }
746
747 pub async fn test_storage_multiple_types(&self) {
760 let components = vec![
761 (
762 "a",
763 ComponentDeclBuilder::new()
764 .capability(
765 CapabilityBuilder::storage()
766 .name("data")
767 .backing_dir("minfs")
768 .source(StorageDirectorySource::Child("b".into()))
769 .subdir("data"),
770 )
771 .capability(
772 CapabilityBuilder::storage()
773 .name("cache")
774 .backing_dir("minfs")
775 .source(StorageDirectorySource::Child("b".into()))
776 .subdir("cache"),
777 )
778 .offer(
779 OfferBuilder::storage()
780 .name("cache")
781 .source(OfferSource::Self_)
782 .target_static_child("c"),
783 )
784 .offer(
785 OfferBuilder::storage()
786 .name("data")
787 .source(OfferSource::Self_)
788 .target_static_child("c"),
789 )
790 .child_default("b")
791 .child_default("c")
792 .build(),
793 ),
794 (
795 "b",
796 ComponentDeclBuilder::new()
797 .capability(
798 CapabilityBuilder::directory()
799 .name("data")
800 .path("/data")
801 .rights(fio::RW_STAR_DIR),
802 )
803 .expose(
804 ExposeBuilder::directory()
805 .name("data")
806 .source(ExposeSource::Self_)
807 .target_name("minfs")
808 .rights(fio::RW_STAR_DIR),
809 )
810 .build(),
811 ),
812 (
813 "c",
814 ComponentDeclBuilder::new()
815 .offer(
816 OfferBuilder::storage()
817 .name("data")
818 .source(OfferSource::Parent)
819 .target_static_child("d"),
820 )
821 .offer(
822 OfferBuilder::storage()
823 .name("cache")
824 .source(OfferSource::Parent)
825 .target_static_child("d"),
826 )
827 .use_(UseBuilder::storage().name("data").path("/storage"))
828 .use_(UseBuilder::storage().name("cache").path("/cache"))
829 .child_default("d")
830 .build(),
831 ),
832 (
833 "d",
834 ComponentDeclBuilder::new()
835 .use_(UseBuilder::storage().name("data").path("/storage"))
836 .use_(UseBuilder::storage().name("cache").path("/cache"))
837 .build(),
838 ),
839 ];
840 let model = T::new("a", components).build().await;
841 model
842 .check_use(
843 ["c"].try_into().unwrap(),
844 CheckUse::Storage {
845 path: "/storage".parse().unwrap(),
846 storage_relation: Some(Moniker::try_from(["c"]).unwrap()),
847 from_cm_namespace: false,
848 storage_subdir: Some("data".to_string()),
849 expected_res: ExpectedResult::Ok,
850 },
851 )
852 .await;
853 model
854 .check_use(
855 ["c"].try_into().unwrap(),
856 CheckUse::Storage {
857 path: "/cache".parse().unwrap(),
858 storage_relation: Some(Moniker::try_from(["c"]).unwrap()),
859 from_cm_namespace: false,
860 storage_subdir: Some("cache".to_string()),
861 expected_res: ExpectedResult::Ok,
862 },
863 )
864 .await;
865 model
866 .check_use(
867 ["c", "d"].try_into().unwrap(),
868 CheckUse::Storage {
869 path: "/storage".parse().unwrap(),
870 storage_relation: Some(Moniker::try_from(["c", "d"]).unwrap()),
871 from_cm_namespace: false,
872 storage_subdir: Some("data".to_string()),
873 expected_res: ExpectedResult::Ok,
874 },
875 )
876 .await;
877 model
878 .check_use(
879 ["c", "d"].try_into().unwrap(),
880 CheckUse::Storage {
881 path: "/cache".parse().unwrap(),
882 storage_relation: Some(Moniker::try_from(["c", "d"]).unwrap()),
883 from_cm_namespace: false,
884 storage_subdir: Some("cache".to_string()),
885 expected_res: ExpectedResult::Ok,
886 },
887 )
888 .await;
889 }
890
891 pub async fn test_use_the_wrong_type_of_storage(&self) {
900 let components = vec![
901 (
902 "a",
903 ComponentDeclBuilder::new()
904 .capability(
905 CapabilityBuilder::directory()
906 .name("data")
907 .path("/data")
908 .rights(fio::RW_STAR_DIR),
909 )
910 .offer(
911 OfferBuilder::storage()
912 .name("cache")
913 .source(OfferSource::Self_)
914 .target_static_child("b"),
915 )
916 .child_default("b")
917 .capability(
918 CapabilityBuilder::storage()
919 .name("cache")
920 .backing_dir("minfs")
921 .source(StorageDirectorySource::Self_),
922 )
923 .build(),
924 ),
925 (
926 "b",
927 ComponentDeclBuilder::new()
928 .use_(UseBuilder::storage().name("data").path("/storage"))
929 .build(),
930 ),
931 ];
932 let model = T::new("a", components).build().await;
933 model
934 .check_use(
935 ["b"].try_into().unwrap(),
936 CheckUse::Storage {
937 path: "/storage".parse().unwrap(),
938 storage_relation: None,
939 from_cm_namespace: false,
940 storage_subdir: None,
941 expected_res: ExpectedResult::Err(zx_status::Status::NOT_FOUND),
942 },
943 )
944 .await;
945 }
946
947 pub async fn test_directories_are_not_storage(&self) {
954 let components = vec![
955 (
956 "a",
957 ComponentDeclBuilder::new()
958 .capability(
959 CapabilityBuilder::directory()
960 .name("data")
961 .path("/data")
962 .rights(fio::RW_STAR_DIR),
963 )
964 .offer(
965 OfferBuilder::directory()
966 .name("data")
967 .source(OfferSource::Self_)
968 .target_static_child("b")
969 .rights(fio::RW_STAR_DIR),
970 )
971 .child_default("b")
972 .build(),
973 ),
974 (
975 "b",
976 ComponentDeclBuilder::new()
977 .use_(UseBuilder::storage().name("data").path("/storage"))
978 .build(),
979 ),
980 ];
981 let model = T::new("a", components).build().await;
982 model
983 .check_use(
984 ["b"].try_into().unwrap(),
985 CheckUse::Storage {
986 path: "/storage".parse().unwrap(),
987 storage_relation: None,
988 from_cm_namespace: false,
989 storage_subdir: None,
990 expected_res: ExpectedResult::Err(zx_status::Status::NOT_FOUND),
991 },
992 )
993 .await;
994 }
995
996 pub async fn test_use_storage_when_not_offered(&self) {
1004 let components = vec![
1005 (
1006 "a",
1007 ComponentDeclBuilder::new()
1008 .child_default("b")
1009 .capability(
1010 CapabilityBuilder::directory()
1011 .name("minfs")
1012 .path("/data")
1013 .rights(fio::RW_STAR_DIR),
1014 )
1015 .capability(
1016 CapabilityBuilder::storage()
1017 .name("data")
1018 .backing_dir("minfs")
1019 .source(StorageDirectorySource::Self_),
1020 )
1021 .build(),
1022 ),
1023 (
1024 "b",
1025 ComponentDeclBuilder::new()
1026 .use_(UseBuilder::storage().name("data").path("/storage"))
1027 .build(),
1028 ),
1029 ];
1030 let model = T::new("a", components).build().await;
1031 model
1032 .check_use(
1033 ["b"].try_into().unwrap(),
1034 CheckUse::Storage {
1035 path: "/storage".parse().unwrap(),
1036 storage_relation: None,
1037 from_cm_namespace: false,
1038 storage_subdir: None,
1039 expected_res: ExpectedResult::Err(zx_status::Status::NOT_FOUND),
1040 },
1041 )
1042 .await;
1043 }
1044
1045 pub async fn test_dir_offered_from_nonexecutable(&self) {
1056 let components = vec![
1057 (
1058 "a",
1059 ComponentDeclBuilder::new_empty_component()
1060 .capability(
1061 CapabilityBuilder::directory()
1062 .name("data")
1063 .path("/data")
1064 .rights(fio::RW_STAR_DIR),
1065 )
1066 .offer(
1067 OfferBuilder::directory()
1068 .name("data")
1069 .target_name("minfs")
1070 .source(OfferSource::Self_)
1071 .target_static_child("b")
1072 .rights(fio::RW_STAR_DIR),
1073 )
1074 .child_default("b")
1075 .build(),
1076 ),
1077 (
1078 "b",
1079 ComponentDeclBuilder::new()
1080 .offer(
1081 OfferBuilder::storage()
1082 .name("data")
1083 .source(OfferSource::Self_)
1084 .target_static_child("c"),
1085 )
1086 .child_default("c")
1087 .capability(
1088 CapabilityBuilder::storage()
1089 .name("data")
1090 .backing_dir("minfs")
1091 .source(StorageDirectorySource::Parent),
1092 )
1093 .build(),
1094 ),
1095 (
1096 "c",
1097 ComponentDeclBuilder::new()
1098 .use_(UseBuilder::storage().name("data").path("/storage"))
1099 .build(),
1100 ),
1101 ];
1102 let model = T::new("a", components).build().await;
1103 model
1104 .check_use(
1105 ["b", "c"].try_into().unwrap(),
1106 CheckUse::Storage {
1107 path: "/storage".parse().unwrap(),
1108 storage_relation: None,
1109 from_cm_namespace: false,
1110 storage_subdir: None,
1111 expected_res: ExpectedResult::Err(zx_status::Status::NOT_FOUND),
1112 },
1113 )
1114 .await;
1115 }
1116
1117 pub async fn test_storage_dir_from_cm_namespace_prevented_by_policy(&self) {
1128 let components = vec![
1129 (
1130 "a",
1131 ComponentDeclBuilder::new()
1132 .offer(
1133 OfferBuilder::storage()
1134 .name("cache")
1135 .source(OfferSource::Self_)
1136 .target_static_child("b"),
1137 )
1138 .child_default("b")
1139 .capability(
1140 CapabilityBuilder::storage()
1141 .name("cache")
1142 .backing_dir("tmp")
1143 .source(StorageDirectorySource::Parent)
1144 .subdir("cache"),
1145 )
1146 .build(),
1147 ),
1148 (
1149 "b",
1150 ComponentDeclBuilder::new()
1151 .use_(UseBuilder::storage().name("cache").path("/storage"))
1152 .build(),
1153 ),
1154 ];
1155 let namespace_capabilities = vec![
1156 CapabilityBuilder::directory()
1157 .name("tmp")
1158 .path("/tmp")
1159 .rights(fio::RW_STAR_DIR)
1160 .build(),
1161 ];
1162 let mut builder = T::new("a", components);
1163 builder.set_namespace_capabilities(namespace_capabilities);
1164 builder.add_capability_policy(
1165 CapabilityAllowlistKey {
1166 source_moniker: ExtendedMoniker::ComponentInstance(Moniker::root()),
1167 source_name: "cache".parse().unwrap(),
1168 source: CapabilityAllowlistSource::Self_,
1169 capability: CapabilityTypeName::Storage,
1170 },
1171 HashSet::new(),
1172 );
1173 let model = builder.build().await;
1174
1175 model
1176 .check_use(
1177 ["b"].try_into().unwrap(),
1178 CheckUse::Storage {
1179 path: "/storage".parse().unwrap(),
1180 storage_relation: Some(Moniker::try_from(["b"]).unwrap()),
1181 from_cm_namespace: true,
1182 storage_subdir: Some("cache".to_string()),
1183 expected_res: ExpectedResult::Err(zx_status::Status::ACCESS_DENIED),
1184 },
1185 )
1186 .await;
1187 }
1188
1189 pub async fn test_instance_id_from_index(&self) {
1200 let b_instance_id = InstanceId::new_random(&mut rand::rng());
1201 let component_id_index = {
1202 let mut index = component_id_index::Index::default();
1203 index
1204 .insert(component_id_index::IndexEntry {
1205 moniker: Moniker::parse_str("/b").unwrap(),
1206 instance_id: b_instance_id.clone(),
1207 ignore_duplicate_id: false,
1208 })
1209 .unwrap();
1210 index
1211 };
1212 let component_id_index_path = make_index_file(component_id_index).unwrap();
1213 let components = vec![
1214 (
1215 "a",
1216 ComponentDeclBuilder::new()
1217 .capability(
1218 CapabilityBuilder::directory()
1219 .name("data")
1220 .path("/data")
1221 .rights(fio::RW_STAR_DIR),
1222 )
1223 .offer(
1224 OfferBuilder::storage()
1225 .name("cache")
1226 .source(OfferSource::Self_)
1227 .target_static_child("b"),
1228 )
1229 .child_default("b")
1230 .capability(
1231 CapabilityBuilder::storage()
1232 .name("cache")
1233 .backing_dir("data")
1234 .source(StorageDirectorySource::Self_),
1235 )
1236 .build(),
1237 ),
1238 (
1239 "b",
1240 ComponentDeclBuilder::new()
1241 .use_(UseBuilder::storage().name("cache").path("/storage"))
1242 .offer(
1243 OfferBuilder::storage()
1244 .name("cache")
1245 .source(OfferSource::Parent)
1246 .target_static_child("c"),
1247 )
1248 .child_default("c")
1249 .build(),
1250 ),
1251 (
1252 "c",
1253 ComponentDeclBuilder::new()
1254 .use_(UseBuilder::storage().name("cache").path("/storage"))
1255 .build(),
1256 ),
1257 ];
1258 let mut builder = T::new("a", components);
1259 builder.set_component_id_index_path(
1260 component_id_index_path.path().to_owned().try_into().unwrap(),
1261 );
1262 let model = builder.build().await;
1263
1264 model
1266 .check_use(
1267 ["b"].try_into().unwrap(),
1268 CheckUse::Storage {
1269 path: "/storage".parse().unwrap(),
1270 storage_relation: Some(Moniker::try_from(["b"]).unwrap()),
1271 from_cm_namespace: false,
1272 storage_subdir: None,
1273 expected_res: ExpectedResult::Ok,
1274 },
1275 )
1276 .await;
1277 model.check_test_subdir_contains(".", b_instance_id.to_string()).await;
1278
1279 let storage_relation = Moniker::try_from(["b", "c"]).unwrap();
1281 model
1282 .check_use(
1283 ["b", "c"].try_into().unwrap(),
1284 CheckUse::Storage {
1285 path: "/storage".parse().unwrap(),
1286 storage_relation: Some(storage_relation.clone()),
1287 from_cm_namespace: false,
1288 storage_subdir: None,
1289 expected_res: ExpectedResult::Ok,
1290 },
1291 )
1292 .await;
1293
1294 let expected_storage_path =
1295 generate_storage_path(None, &storage_relation, None).to_str().unwrap().to_string();
1296 model.check_test_dir_tree_contains(expected_storage_path).await;
1297 }
1298}