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 max_transfer_blocks: Option<NonZero<u32>>,
63 pub initial_content: Option<&'a [u8]>,
64 pub vmo: Option<zx::Vmo>,
65 pub observer: Option<Box<dyn Observer>>,
66}
67
68impl Default for FakeServerOptions<'_> {
69 fn default() -> Self {
70 FakeServerOptions {
71 flags: fblock::Flag::empty(),
72 block_count: None,
73 block_size: 512,
74 max_transfer_blocks: None,
75 initial_content: None,
76 vmo: None,
77 observer: None,
78 }
79 }
80}
81
82impl From<FakeServerOptions<'_>> for FakeServer {
83 fn from(options: FakeServerOptions<'_>) -> Self {
84 let (vmo, block_count) = if let Some(vmo) = options.vmo {
85 let size = vmo.get_size().unwrap();
86 debug_assert!(size % options.block_size as u64 == 0);
87 let block_count = size / options.block_size as u64;
88 if let Some(bc) = options.block_count {
89 assert_eq!(block_count, bc);
90 }
91 (vmo, block_count)
92 } else {
93 let block_count = options.block_count.unwrap();
94 (zx::Vmo::create(block_count * options.block_size as u64).unwrap(), block_count)
95 };
96
97 if let Some(initial_content) = options.initial_content {
98 vmo.write(initial_content, 0).unwrap();
99 }
100
101 Self {
102 server: BlockServer::new(
103 options.block_size,
104 Arc::new(Data {
105 flags: options.flags,
106 block_size: options.block_size,
107 block_count: block_count,
108 max_transfer_blocks: options.max_transfer_blocks,
109 data: vmo,
110 observer: options.observer,
111 }),
112 ),
113 }
114 }
115}
116
117impl FakeServer {
118 pub fn new(block_count: u64, block_size: u32, initial_content: &[u8]) -> Self {
119 FakeServerOptions {
120 block_count: Some(block_count),
121 block_size,
122 initial_content: Some(initial_content),
123 ..Default::default()
124 }
125 .into()
126 }
127
128 pub fn from_vmo(block_size: u32, vmo: zx::Vmo) -> Self {
129 FakeServerOptions { block_size, vmo: Some(vmo), ..Default::default() }.into()
130 }
131
132 pub async fn serve(&self, requests: fvolume::VolumeRequestStream) -> Result<(), Error> {
133 self.server.handle_requests(requests).await
134 }
135
136 pub fn volume_proxy(self: &Arc<Self>) -> fvolume::VolumeProxy {
137 let (client, server) = fidl::endpoints::create_endpoints();
138 self.connect(server);
139 client.into_proxy()
140 }
141
142 pub fn connect(self: &Arc<Self>, server: ServerEnd<fvolume::VolumeMarker>) {
143 let this = self.clone();
144 fasync::Task::spawn(async move {
145 let _ = this.serve(server.into_stream()).await;
146 })
147 .detach();
148 }
149
150 pub fn block_proxy(self: &Arc<Self>) -> fblock::BlockProxy {
151 fblock::BlockProxy::from_channel(self.volume_proxy().into_channel().unwrap())
152 }
153}
154
155struct Data {
156 flags: fblock::Flag,
157 block_size: u32,
158 block_count: u64,
159 max_transfer_blocks: Option<NonZero<u32>>,
160 data: zx::Vmo,
161 observer: Option<Box<dyn Observer>>,
162}
163
164impl Interface for Data {
165 async fn get_info(&self) -> Result<Cow<'_, DeviceInfo>, zx::Status> {
166 Ok(Cow::Owned(DeviceInfo::Partition(PartitionInfo {
167 device_flags: self.flags,
168 max_transfer_blocks: self.max_transfer_blocks.clone(),
169 block_range: Some(0..self.block_count),
170 type_guid: TYPE_GUID.clone(),
171 instance_guid: INSTANCE_GUID.clone(),
172 name: PARTITION_NAME.to_string(),
173 flags: 0u64,
174 })))
175 }
176
177 async fn read(
178 &self,
179 device_block_offset: u64,
180 block_count: u32,
181 vmo: &Arc<zx::Vmo>,
182 vmo_offset: u64,
183 _trace_flow_id: Option<NonZero<u64>>,
184 ) -> Result<(), zx::Status> {
185 if let Some(observer) = self.observer.as_ref() {
186 observer.read(device_block_offset, block_count, vmo, vmo_offset);
187 }
188 if let Some(max) = self.max_transfer_blocks.as_ref() {
189 assert!(block_count <= max.get());
191 }
192 if device_block_offset + block_count as u64 > self.block_count {
193 Err(zx::Status::OUT_OF_RANGE)
194 } else {
195 vmo.write(
196 &self.data.read_to_vec(
197 device_block_offset * self.block_size as u64,
198 block_count as u64 * self.block_size as u64,
199 )?,
200 vmo_offset,
201 )
202 }
203 }
204
205 async fn write(
206 &self,
207 device_block_offset: u64,
208 block_count: u32,
209 vmo: &Arc<zx::Vmo>,
210 vmo_offset: u64,
211 opts: WriteOptions,
212 _trace_flow_id: Option<NonZero<u64>>,
213 ) -> Result<(), zx::Status> {
214 if let Some(observer) = self.observer.as_ref() {
215 match observer.write(device_block_offset, block_count, vmo, vmo_offset, opts) {
216 WriteAction::Write => {}
217 WriteAction::Discard => return Ok(()),
218 WriteAction::Fail => return Err(zx::Status::IO),
219 }
220 }
221 if let Some(max) = self.max_transfer_blocks.as_ref() {
222 assert!(block_count <= max.get());
224 }
225 if device_block_offset + block_count as u64 > self.block_count {
226 Err(zx::Status::OUT_OF_RANGE)
227 } else {
228 self.data.write(
229 &vmo.read_to_vec(vmo_offset, block_count as u64 * self.block_size as u64)?,
230 device_block_offset * self.block_size as u64,
231 )
232 }
233 }
234
235 async fn flush(&self, _trace_flow_id: Option<NonZero<u64>>) -> Result<(), zx::Status> {
236 if let Some(observer) = self.observer.as_ref() {
237 observer.flush();
238 }
239 Ok(())
240 }
241
242 async fn trim(
243 &self,
244 device_block_offset: u64,
245 block_count: u32,
246 _trace_flow_id: Option<NonZero<u64>>,
247 ) -> Result<(), zx::Status> {
248 if let Some(observer) = self.observer.as_ref() {
249 observer.trim(device_block_offset, block_count);
250 }
251 if device_block_offset + block_count as u64 > self.block_count {
252 Err(zx::Status::OUT_OF_RANGE)
253 } else {
254 Ok(())
255 }
256 }
257}