Skip to main content

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    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    ///   component manager's namespace
30    ///    |
31    ///    a
32    ///    |
33    ///    b
34    ///
35    /// a: has storage decl with name "mystorage" with a source of realm at path /data
36    /// a: offers cache storage to b from "mystorage"
37    /// b: uses cache storage as /storage.
38    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    ///   a
94    ///    \
95    ///     b
96    ///
97    /// a: offers cache storage to b from "void"
98    /// b: uses cache storage as /storage
99    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    ///   a
137    ///    \
138    ///     b
139    ///
140    /// a: has storage decl with name "mystorage" with a source of self at path /data
141    /// a: offers cache storage to b from "mystorage"
142    /// b: uses cache storage as /storage
143    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    ///   a
193    ///    \
194    ///     b
195    ///
196    /// a: has storage decl with name "mystorage" with a source of self at path /data, with subdir
197    ///    "cache"
198    /// a: offers cache storage to b from "mystorage"
199    /// b: uses cache storage as /storage
200    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    ///   a
251    ///    \
252    ///     b
253    ///
254    /// a: has storage decl with name "mystorage" with a source of self at path /data, but /data
255    ///    has only read rights
256    /// a: offers cache storage to b from "mystorage"
257    /// b: uses cache storage as /storage
258    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    ///   a
302    ///    \
303    ///     b
304    ///      \
305    ///       c
306    ///
307    /// a: offers directory /data to b as /minfs
308    /// b: has storage decl with name "mystorage" with a source of realm at path /minfs
309    /// b: offers data storage to c from "mystorage"
310    /// c: uses data storage as /storage
311    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    ///   a
374    ///    \
375    ///     b
376    ///      \
377    ///       c
378    ///
379    /// a: offers directory /data to b as /minfs with subdir "subdir_1"
380    /// b: has storage decl with name "mystorage" with a source of realm at path /minfs with subdir
381    ///    "subdir_2"
382    /// b: offers data storage to c from "mystorage"
383    /// c: uses data storage as /storage
384    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    ///   a
456    ///    \
457    ///     b
458    ///      \
459    ///       c
460    ///
461    /// a: offers directory /data to b as /minfs
462    /// b: has storage decl with name "mystorage" with a source of realm at path /minfs, subdir "bar"
463    /// b: offers data storage to c from "mystorage"
464    /// c: uses data storage as /storage
465    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    ///   a
531    ///    \
532    ///     b
533    ///      \
534    ///       c
535    ///
536    /// a: has storage decl with name "mystorage" with a source of self at path /data
537    /// a: offers data storage to b from "mystorage"
538    /// b: offers data storage to c from realm
539    /// c: uses data storage as /storage
540    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    ///   a
601    ///  / \
602    /// b   c
603    ///
604    /// b: exposes directory /data as /minfs
605    /// a: has storage decl with name "mystorage" with a source of child b at path /minfs
606    /// a: offers cache storage to c from "mystorage"
607    /// c: uses cache storage as /storage
608    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    ///   a
670    ///  / \
671    /// b   c
672    ///
673    /// b: exposes directory /data as /minfs with subdir "subdir_1"
674    /// a: has storage decl with name "mystorage" with a source of child b at path /minfs and subdir
675    ///    "subdir_2"
676    /// a: offers cache storage to c from "mystorage"
677    /// c: uses cache storage as /storage
678    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    ///   a
748    ///  / \
749    /// b   c
750    ///      \
751    ///       d
752    ///
753    /// b: exposes directory /data as /minfs
754    /// a: has storage decl with name "mystorage" with a source of child b at path /minfs
755    /// a: offers data, cache, and meta storage to c from "mystorage"
756    /// c: uses cache and meta storage as /storage
757    /// c: offers data and meta storage to d
758    /// d: uses data and meta storage
759    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    ///   a
892    ///    \
893    ///     b
894    ///
895    /// a: has storage decl with name "mystorage" with a source of self at path /storage
896    /// a: offers cache storage to b from "mystorage"
897    /// b: uses data storage as /storage, fails to since data != cache
898    /// b: uses meta storage, fails to since meta != cache
899    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    ///   a
948    ///    \
949    ///     b
950    ///
951    /// a: offers directory from self at path "/data"
952    /// b: uses data storage as /storage, fails to since data storage != "/data" directories
953    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    ///   a
997    ///    \
998    ///     b
999    ///
1000    /// a: has storage decl with name "mystorage" with a source of self at path /data
1001    /// a: does not offer any storage to b
1002    /// b: uses meta storage and data storage as /storage, fails to since it was not offered either
1003    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    ///   a
1046    ///    \
1047    ///     b
1048    ///      \
1049    ///       c
1050    ///
1051    /// a: offers directory /data to b as /minfs, but a is non-executable
1052    /// b: has storage decl with name "mystorage" with a source of realm at path /minfs
1053    /// b: offers data and meta storage to b from "mystorage"
1054    /// c: uses meta and data storage as /storage, fails to since a is non-executable
1055    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    ///   component manager's namespace
1118    ///    |
1119    ///    a
1120    ///    |
1121    ///    b
1122    ///
1123    /// a: has storage decl with name "mystorage" with a source of parent at path /data
1124    /// a: offers cache storage to b from "mystorage"
1125    /// b: uses cache storage as /storage.
1126    /// Policy prevents b from using storage.
1127    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    ///   component manager's namespace
1190    ///    |
1191    ///    a
1192    ///    |
1193    ///    b
1194    ///    |
1195    ///    c
1196    ///
1197    /// Instance IDs defined only for `b` in the component ID index.
1198    /// Check that the correct storage layout is used when a component has an instance ID.
1199    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        // instance `b` uses instance-id based paths.
1265        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        // instance `c` uses moniker-based paths.
1280        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}