1use anyhow::Error;
6use block_server::async_interface::{Interface, SessionManager};
7use block_server::{BlockServer, DeviceInfo, PartitionInfo, WriteOptions};
8use fidl::endpoints::{Proxy as _, ServerEnd};
9use std::borrow::Cow;
10use std::num::NonZero;
11use std::sync::Arc;
12use {
13 fidl_fuchsia_hardware_block as fblock, fidl_fuchsia_hardware_block_volume as fvolume,
14 fuchsia_async as fasync,
15};
16
17pub const TYPE_GUID: [u8; 16] = [1; 16];
18pub const INSTANCE_GUID: [u8; 16] = [2; 16];
19pub const PARTITION_NAME: &str = "fake-server";
20
21pub enum WriteAction {
23 Write,
24 Discard,
25 Fail,
26}
27
28pub trait Observer: Send + Sync {
29 fn read(
30 &self,
31 _device_block_offset: u64,
32 _block_count: u32,
33 _vmo: &Arc<zx::Vmo>,
34 _vmo_offset: u64,
35 ) {
36 }
37
38 fn write(
39 &self,
40 _device_block_offset: u64,
41 _block_count: u32,
42 _vmo: &Arc<zx::Vmo>,
43 _vmo_offset: u64,
44 _opts: WriteOptions,
45 ) -> WriteAction {
46 WriteAction::Write
47 }
48
49 fn flush(&self) {}
50
51 fn trim(&self, _device_block_offset: u64, _block_count: u32) {}
52}
53
54pub struct FakeServer {
55 server: BlockServer<SessionManager<Data>>,
56}
57
58pub struct FakeServerOptions<'a> {
59 pub flags: fblock::Flag,
60 pub block_count: Option<u64>,
61 pub block_size: u32,
62 pub initial_content: Option<&'a [u8]>,
63 pub vmo: Option<zx::Vmo>,
64 pub observer: Option<Box<dyn Observer>>,
65}
66
67impl Default for FakeServerOptions<'_> {
68 fn default() -> Self {
69 FakeServerOptions {
70 flags: fblock::Flag::empty(),
71 block_count: None,
72 block_size: 512,
73 initial_content: None,
74 vmo: None,
75 observer: None,
76 }
77 }
78}
79
80impl From<FakeServerOptions<'_>> for FakeServer {
81 fn from(options: FakeServerOptions<'_>) -> Self {
82 let (vmo, block_count) = if let Some(vmo) = options.vmo {
83 let size = vmo.get_size().unwrap();
84 debug_assert!(size % options.block_size as u64 == 0);
85 let block_count = size / options.block_size as u64;
86 if let Some(bc) = options.block_count {
87 assert_eq!(block_count, bc);
88 }
89 (vmo, block_count)
90 } else {
91 let block_count = options.block_count.unwrap();
92 (zx::Vmo::create(block_count * options.block_size as u64).unwrap(), block_count)
93 };
94
95 if let Some(initial_content) = options.initial_content {
96 vmo.write(initial_content, 0).unwrap();
97 }
98
99 Self {
100 server: BlockServer::new(
101 options.block_size,
102 Arc::new(Data {
103 flags: options.flags,
104 block_size: options.block_size,
105 block_count: block_count,
106 data: vmo,
107 observer: options.observer,
108 }),
109 ),
110 }
111 }
112}
113
114impl FakeServer {
115 pub fn new(block_count: u64, block_size: u32, initial_content: &[u8]) -> Self {
116 FakeServerOptions {
117 block_count: Some(block_count),
118 block_size,
119 initial_content: Some(initial_content),
120 ..Default::default()
121 }
122 .into()
123 }
124
125 pub fn from_vmo(block_size: u32, vmo: zx::Vmo) -> Self {
126 FakeServerOptions { block_size, vmo: Some(vmo), ..Default::default() }.into()
127 }
128
129 pub async fn serve(&self, requests: fvolume::VolumeRequestStream) -> Result<(), Error> {
130 self.server.handle_requests(requests).await
131 }
132
133 pub fn volume_proxy(self: &Arc<Self>) -> fvolume::VolumeProxy {
134 let (client, server) = fidl::endpoints::create_endpoints();
135 self.connect(server);
136 client.into_proxy()
137 }
138
139 pub fn connect(self: &Arc<Self>, server: ServerEnd<fvolume::VolumeMarker>) {
140 let this = self.clone();
141 fasync::Task::spawn(async move {
142 let _ = this.serve(server.into_stream()).await;
143 })
144 .detach();
145 }
146
147 pub fn block_proxy(self: &Arc<Self>) -> fblock::BlockProxy {
148 fblock::BlockProxy::from_channel(self.volume_proxy().into_channel().unwrap())
149 }
150}
151
152struct Data {
153 flags: fblock::Flag,
154 block_size: u32,
155 block_count: u64,
156 data: zx::Vmo,
157 observer: Option<Box<dyn Observer>>,
158}
159
160impl Interface for Data {
161 async fn get_info(&self) -> Result<Cow<'_, DeviceInfo>, zx::Status> {
162 Ok(Cow::Owned(DeviceInfo::Partition(PartitionInfo {
163 device_flags: self.flags,
164 block_range: Some(0..self.block_count),
165 type_guid: TYPE_GUID.clone(),
166 instance_guid: INSTANCE_GUID.clone(),
167 name: PARTITION_NAME.to_string(),
168 flags: 0u64,
169 })))
170 }
171
172 async fn read(
173 &self,
174 device_block_offset: u64,
175 block_count: u32,
176 vmo: &Arc<zx::Vmo>,
177 vmo_offset: u64,
178 _trace_flow_id: Option<NonZero<u64>>,
179 ) -> Result<(), zx::Status> {
180 if let Some(observer) = self.observer.as_ref() {
181 observer.read(device_block_offset, block_count, vmo, vmo_offset);
182 }
183 vmo.write(
184 &self.data.read_to_vec(
185 device_block_offset * self.block_size as u64,
186 block_count as u64 * self.block_size as u64,
187 )?,
188 vmo_offset,
189 )
190 }
191
192 async fn write(
193 &self,
194 device_block_offset: u64,
195 block_count: u32,
196 vmo: &Arc<zx::Vmo>,
197 vmo_offset: u64,
198 opts: WriteOptions,
199 _trace_flow_id: Option<NonZero<u64>>,
200 ) -> Result<(), zx::Status> {
201 if let Some(observer) = self.observer.as_ref() {
202 match observer.write(device_block_offset, block_count, vmo, vmo_offset, opts) {
203 WriteAction::Write => {}
204 WriteAction::Discard => return Ok(()),
205 WriteAction::Fail => return Err(zx::Status::IO),
206 }
207 }
208 self.data.write(
209 &vmo.read_to_vec(vmo_offset, block_count as u64 * self.block_size as u64)?,
210 device_block_offset * self.block_size as u64,
211 )
212 }
213
214 async fn flush(&self, _trace_flow_id: Option<NonZero<u64>>) -> Result<(), zx::Status> {
215 if let Some(observer) = self.observer.as_ref() {
216 observer.flush();
217 }
218 Ok(())
219 }
220
221 async fn trim(
222 &self,
223 device_block_offset: u64,
224 block_count: u32,
225 _trace_flow_id: Option<NonZero<u64>>,
226 ) -> Result<(), zx::Status> {
227 if let Some(observer) = self.observer.as_ref() {
228 observer.trim(device_block_offset, block_count);
229 }
230 Ok(())
231 }
232}