routing_test_helpers/
storage.rs

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