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 fkeymint::AdminMarker::PROTOCOL_NAME => vfs::service::host(move |stream| {
56 run_keymint_admin(stream)
57 }),
58 fboot::ItemsMarker::PROTOCOL_NAME => vfs::service::host(move |stream| {
59 let vmo_clone = vmo.clone();
60 run_boot_items(stream, vmo_clone)
61 }),
62 ffeedback::CrashReporterMarker::PROTOCOL_NAME => vfs::service::host(move |stream| {
63 run_crash_reporter(stream, crash_reports_sink.clone())
64 }),
65 ffxfsprovisioner::FxfsProvisionerMarker::PROTOCOL_NAME => vfs::service::host(
66 move |stream| { run_fxfs_provisioner(stream, force_fxfs_provisioner_failure) }
67 ),
68 },
69 };
70
71 let scope = ExecutionScope::new();
72 vfs::directory::serve_on(export, fio::PERM_READABLE, scope.clone(), handles.outgoing_dir);
73 scope.wait().await;
74
75 Ok(())
76}
77
78async fn run_boot_items(mut stream: fboot::ItemsRequestStream, vmo: Option<Arc<zx::Vmo>>) {
82 while let Some(request) = stream.next().await {
83 match request.unwrap() {
84 fboot::ItemsRequest::Get { type_, extra, responder } => {
85 assert_eq!(type_, ZBI_TYPE_STORAGE_RAMDISK);
86 assert_eq!(extra, 0);
87 let response_vmo = vmo.as_ref().map(|vmo| {
88 vmo.create_child(zx::VmoChildOptions::SLICE, 0, vmo.get_size().unwrap())
89 .unwrap()
90 });
91 responder.send(response_vmo, 0).unwrap();
92 }
93 fboot::ItemsRequest::Get2 { type_, extra, responder } => {
94 assert_eq!(type_, ZBI_TYPE_STORAGE_RAMDISK);
95 assert_eq!((*extra.unwrap()).n, 0);
96 responder.send(Ok(Vec::new())).unwrap();
97 }
98 fboot::ItemsRequest::GetBootloaderFile { .. } => {
99 panic!(
100 "unexpectedly called GetBootloaderFile on {}",
101 fboot::ItemsMarker::PROTOCOL_NAME
102 );
103 }
104 }
105 }
106}
107
108async fn run_crash_reporter(
109 mut stream: ffeedback::CrashReporterRequestStream,
110 mut crash_reports_sink: mpsc::Sender<ffeedback::CrashReport>,
111) {
112 while let Some(request) = stream.next().await {
113 match request.unwrap() {
114 ffeedback::CrashReporterRequest::FileReport { report, responder } => {
115 crash_reports_sink.send(report).await.unwrap();
116 responder.send(Ok(&FileReportResults::default())).unwrap();
117 }
118 }
119 }
120}
121
122async fn run_fxfs_provisioner(
123 mut stream: ffxfsprovisioner::FxfsProvisionerRequestStream,
124 force_failure: bool,
125) {
126 while let Some(request) = stream.next().await {
127 match request.unwrap() {
128 ffxfsprovisioner::FxfsProvisionerRequest::Provision {
129 partition_service,
130 responder,
131 } => {
132 if force_failure {
133 responder.send(Err(zx::Status::INTERNAL.into_raw())).unwrap();
134 return;
135 }
136 let partition_service = partition_service.into_proxy();
137
138 let overlay = connect_to_named_protocol_at_dir_root::<
139 fpartitions::OverlayPartitionProxy,
140 >(&partition_service, "overlay")
141 .expect("failed to connect to OverlayPartition protocol");
142 let partitions_info = overlay
143 .get_partitions()
144 .await
145 .expect("get_partitions FIDL call failed")
146 .expect("get_partitions failed");
147 assert_eq!(partitions_info.len(), 2);
148 assert!(partitions_info.iter().any(|info| info.name == "super"));
149 assert!(partitions_info.iter().any(|info| info.name == "userdata"));
150
151 let connector =
152 Box::new(DirBasedBlockConnector::new(partition_service, "/volume".to_string()));
153
154 let mut disk_builder = DiskBuilder::new();
155 disk_builder
156 .format_volumes(VolumesSpec { fxfs_blob: true, create_data_partition: true })
157 .format_data(DataSpec { format: Some("fxfs"), ..Default::default() });
158 disk_builder.build_fxfs_as_volume_manager(connector).await;
159
160 responder.send(Ok(())).unwrap();
161 }
162 _ => {
163 unreachable!()
164 }
165 }
166 }
167}
168
169static KEYMINT: LazyLock<FakeKeymint> = LazyLock::new(FakeKeymint::default);
170
171async fn run_keymint(stream: fkeymint::SealingKeysRequestStream) {
172 let keymint = &*KEYMINT;
173 keymint.run_sealing_keys_service(stream).await.unwrap();
174}
175
176async fn run_keymint_admin(stream: fkeymint::AdminRequestStream) {
177 let keymint = &*KEYMINT;
178 keymint.run_admin_service(stream).await.unwrap();
179}