fake_block_server/
fake_server.rs

1// Copyright 2024 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 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
21/// The Observer can silently discard writes, or fail them explicitly (zx::Status::IO is returned).
22pub 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}