fshost_test_fixture/
mocks.rs1use crate::disk_builder::{DataSpec, DiskBuilder, VolumesSpec};
6use anyhow::Error;
7use fake_keymint::FakeKeymint;
8use ffeedback::FileReportResults;
9use fidl::prelude::*;
10use fs_management::filesystem::DirBasedBlockConnector;
11use fuchsia_component::client::connect::connect_to_named_protocol_at_dir_root;
12use fuchsia_component_test::LocalComponentHandles;
13use futures::channel::mpsc::{self};
14use futures::future::BoxFuture;
15use futures::{FutureExt as _, SinkExt as _, StreamExt as _};
16use std::sync::{Arc, LazyLock};
17use vfs::execution_scope::ExecutionScope;
18
19use {
20 fidl_fuchsia_boot as fboot, fidl_fuchsia_feedback as ffeedback,
21 fidl_fuchsia_fshost_fxfsprovisioner as ffxfsprovisioner, fidl_fuchsia_io as fio,
22 fidl_fuchsia_security_keymint as fkeymint, fidl_fuchsia_storage_partitions as fpartitions,
23};
24
25const ZBI_TYPE_STORAGE_RAMDISK: u32 = 0x4b534452;
27
28pub fn new_mocks(
29 vmo: Option<zx::Vmo>,
30 crash_reports_sink: mpsc::Sender<ffeedback::CrashReport>,
31 force_fxfs_provisioner_failure: bool,
32) -> impl Fn(LocalComponentHandles) -> BoxFuture<'static, Result<(), Error>> + Sync + Send + 'static
33{
34 let vmo = vmo.map(Arc::new);
35 let mock = move |handles: LocalComponentHandles| {
36 let vmo_clone = vmo.clone();
37 run_mocks(handles, vmo_clone, crash_reports_sink.clone(), force_fxfs_provisioner_failure)
38 .boxed()
39 };
40
41 mock
42}
43
44async fn run_mocks(
45 handles: LocalComponentHandles,
46 vmo: Option<Arc<zx::Vmo>>,
47 crash_reports_sink: mpsc::Sender<ffeedback::CrashReport>,
48 force_fxfs_provisioner_failure: bool,
49) -> Result<(), Error> {
50 let export = vfs::pseudo_directory! {
51 "svc" => vfs::pseudo_directory! {
52 fkeymint::SealingKeysMarker::PROTOCOL_NAME => vfs::service::host(move |stream| {
53 run_keymint(stream)
54 }),
55 fboot::ItemsMarker::PROTOCOL_NAME => vfs::service::host(move |stream| {
56 let vmo_clone = vmo.clone();
57 run_boot_items(stream, vmo_clone)
58 }),
59 ffeedback::CrashReporterMarker::PROTOCOL_NAME => vfs::service::host(move |stream| {
60 run_crash_reporter(stream, crash_reports_sink.clone())
61 }),
62 ffxfsprovisioner::FxfsProvisionerMarker::PROTOCOL_NAME => vfs::service::host(
63 move |stream| { run_fxfs_provisioner(stream, force_fxfs_provisioner_failure) }
64 ),
65 },
66 };
67
68 let scope = ExecutionScope::new();
69 vfs::directory::serve_on(export, fio::PERM_READABLE, scope.clone(), handles.outgoing_dir);
70 scope.wait().await;
71
72 Ok(())
73}
74
75async fn run_boot_items(mut stream: fboot::ItemsRequestStream, vmo: Option<Arc<zx::Vmo>>) {
79 while let Some(request) = stream.next().await {
80 match request.unwrap() {
81 fboot::ItemsRequest::Get { type_, extra, responder } => {
82 assert_eq!(type_, ZBI_TYPE_STORAGE_RAMDISK);
83 assert_eq!(extra, 0);
84 let response_vmo = vmo.as_ref().map(|vmo| {
85 vmo.create_child(zx::VmoChildOptions::SLICE, 0, vmo.get_size().unwrap())
86 .unwrap()
87 });
88 responder.send(response_vmo, 0).unwrap();
89 }
90 fboot::ItemsRequest::Get2 { type_, extra, responder } => {
91 assert_eq!(type_, ZBI_TYPE_STORAGE_RAMDISK);
92 assert_eq!((*extra.unwrap()).n, 0);
93 responder.send(Ok(Vec::new())).unwrap();
94 }
95 fboot::ItemsRequest::GetBootloaderFile { .. } => {
96 panic!(
97 "unexpectedly called GetBootloaderFile on {}",
98 fboot::ItemsMarker::PROTOCOL_NAME
99 );
100 }
101 }
102 }
103}
104
105async fn run_crash_reporter(
106 mut stream: ffeedback::CrashReporterRequestStream,
107 mut crash_reports_sink: mpsc::Sender<ffeedback::CrashReport>,
108) {
109 while let Some(request) = stream.next().await {
110 match request.unwrap() {
111 ffeedback::CrashReporterRequest::FileReport { report, responder } => {
112 crash_reports_sink.send(report).await.unwrap();
113 responder.send(Ok(&FileReportResults::default())).unwrap();
114 }
115 }
116 }
117}
118
119async fn run_fxfs_provisioner(
120 mut stream: ffxfsprovisioner::FxfsProvisionerRequestStream,
121 force_failure: bool,
122) {
123 while let Some(request) = stream.next().await {
124 match request.unwrap() {
125 ffxfsprovisioner::FxfsProvisionerRequest::Provision {
126 partition_service,
127 responder,
128 } => {
129 if force_failure {
130 responder.send(Err(zx::Status::INTERNAL.into_raw())).unwrap();
131 return;
132 }
133 let partition_service = partition_service.into_proxy();
134
135 let overlay = connect_to_named_protocol_at_dir_root::<
136 fpartitions::OverlayPartitionProxy,
137 >(&partition_service, "overlay")
138 .expect("failed to connect to OverlayPartition protocol");
139 let partitions_info = overlay
140 .get_partitions()
141 .await
142 .expect("get_partitions FIDL call failed")
143 .expect("get_partitions failed");
144 assert_eq!(partitions_info.len(), 2);
145 assert!(partitions_info.iter().any(|info| info.name == "super"));
146 assert!(partitions_info.iter().any(|info| info.name == "userdata"));
147
148 let connector =
149 Box::new(DirBasedBlockConnector::new(partition_service, "/volume".to_string()));
150
151 let mut disk_builder = DiskBuilder::new();
152 disk_builder
153 .format_volumes(VolumesSpec { fxfs_blob: true, create_data_partition: true })
154 .format_data(DataSpec { format: Some("fxfs"), ..Default::default() });
155 disk_builder.build_fxfs_as_volume_manager(connector).await;
156
157 responder.send(Ok(())).unwrap();
158 }
159 _ => {
160 unreachable!()
161 }
162 }
163 }
164}
165
166async fn run_keymint(stream: fkeymint::SealingKeysRequestStream) {
167 static KEYMINT: LazyLock<FakeKeymint> = LazyLock::new(FakeKeymint::default);
169 let keymint = &*KEYMINT;
170 keymint.run_sealing_keys_service(stream).await.unwrap();
171}