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