test_vmo_backed_block_server/
data_for_testing.rs1use block_server::async_interface::Interface;
6use block_server::{DeviceInfo, ReadOptions, WriteFlags, WriteOptions};
7use fuchsia_sync::{Mutex, MutexGuard};
8use fxfs_crypto::{FscryptSoftwareInoLblk32FileCipher, UnwrappedKey};
9use rand::Rng as _;
10use std::borrow::Cow;
11use std::collections::BTreeMap;
12use std::num::NonZero;
13use std::sync::Arc;
14use std::time::Duration;
15
16pub struct Data {
17 pub info: DeviceInfo,
18 pub block_size: u32,
19 pub data: zx::Vmo,
20 pub write_cache: Option<Mutex<WriteCache>>,
21 pub max_jitter_usec: Option<u64>,
22 pub observer: Option<Box<dyn Observer>>,
23 pub fscrypt_keys: Mutex<FscryptKeys>,
25}
26
27impl Data {
28 async fn add_jitter(&self) {
29 if let Some(max) = self.max_jitter_usec {
30 fuchsia_async::Timer::new(Duration::from_micros(rand::random_range(0..max))).await
31 }
32 }
33
34 fn write_cache(&self) -> Option<MutexGuard<'_, WriteCache>> {
35 self.write_cache.as_ref().map(Mutex::lock)
36 }
37
38 pub fn client_closed(&self) -> Result<(), zx::Status> {
39 if let Some(mut cache) = self.write_cache() {
40 if let Some(observer) = self.observer.as_ref() {
41 observer.close(Some(&mut *cache));
42 }
43 cache.apply(&self.data)
44 } else {
45 if let Some(observer) = self.observer.as_ref() {
46 observer.close(None);
47 }
48 Ok(())
49 }
50 }
51
52 pub fn fscrypt_keys(&self) -> MutexGuard<'_, FscryptKeys> {
53 self.fscrypt_keys.lock()
54 }
55}
56
57impl Interface for Data {
58 fn get_info(&self) -> Cow<'_, DeviceInfo> {
59 Cow::Borrowed(&self.info)
60 }
61
62 async fn read(
63 &self,
64 device_block_offset: u64,
65 block_count: u32,
66 vmo: &Arc<zx::Vmo>,
67 vmo_offset: u64,
68 opts: ReadOptions,
69 _trace_flow_id: Option<NonZero<u64>>,
70 ) -> Result<(), zx::Status> {
71 self.add_jitter().await;
72 if let Some(observer) = self.observer.as_ref() {
73 observer.read(device_block_offset, block_count, vmo, vmo_offset);
74 }
75
76 if let Some(max) = self.info.max_transfer_blocks().as_ref() {
77 assert!(block_count <= max.get());
79 }
80 if device_block_offset + block_count as u64 > self.info.block_count().unwrap() {
81 Err(zx::Status::OUT_OF_RANGE)
82 } else {
83 let mut data = if let Some(cache) = self.write_cache() {
84 let mut data = vec![0u8; block_count as usize * self.block_size as usize];
85 cache.read(&self.data, device_block_offset, &mut data[..])?;
86 data
87 } else {
88 self.data.read_to_vec(
89 device_block_offset * self.block_size as u64,
90 block_count as u64 * self.block_size as u64,
91 )?
92 };
93 if opts.inline_crypto.is_enabled {
94 self.fscrypt_keys()
95 .get_key(opts.inline_crypto.slot)?
96 .decrypt(&mut data, opts.inline_crypto.dun as u128)
97 .map_err(|_| zx::Status::IO)?;
98 }
99 vmo.write(&data[..], vmo_offset)
100 }
101 }
102
103 async fn write(
104 &self,
105 device_block_offset: u64,
106 block_count: u32,
107 vmo: &Arc<zx::Vmo>,
108 vmo_offset: u64,
109 opts: WriteOptions,
110 _trace_flow_id: Option<NonZero<u64>>,
111 ) -> Result<(), zx::Status> {
112 self.add_jitter().await;
113 if let Some(observer) = self.observer.as_ref() {
114 match observer.write(device_block_offset, block_count, vmo, vmo_offset, opts) {
115 WriteAction::Write => {}
116 WriteAction::Discard => return Ok(()),
117 WriteAction::Fail => return Err(zx::Status::IO),
118 }
119 }
120
121 if opts.flags.contains(WriteFlags::PRE_BARRIER) {
122 if let Some(mut cache) = self.write_cache() {
123 cache.apply(&self.data)?;
124 }
125 }
126 if let Some(max) = self.info.max_transfer_blocks().as_ref() {
127 assert!(block_count <= max.get());
129 }
130 if device_block_offset + block_count as u64 > self.info.block_count().unwrap() {
131 Err(zx::Status::OUT_OF_RANGE)
132 } else {
133 let mut data =
134 vmo.read_to_vec(vmo_offset, block_count as u64 * self.block_size as u64)?;
135 if !opts.flags.contains(WriteFlags::FORCE_ACCESS)
136 && let Some(mut cache) = self.write_cache()
137 {
138 cache.insert(device_block_offset, &data[..]);
139 }
140 if opts.inline_crypto.is_enabled {
141 self.fscrypt_keys()
142 .get_key(opts.inline_crypto.slot)?
143 .encrypt(&mut data, opts.inline_crypto.dun as u128)
144 .map_err(|_| zx::Status::IO)?;
145 }
146 self.data.write(&data[..], device_block_offset * self.block_size as u64)
147 }
148 }
149
150 async fn flush(&self, _trace_flow_id: Option<NonZero<u64>>) -> Result<(), zx::Status> {
151 self.add_jitter().await;
152
153 let mut cache = self.write_cache();
154 if let Some(observer) = self.observer.as_ref() {
155 match cache.as_mut() {
156 Some(w) => observer.flush(Some(&mut *w)),
157 None => observer.flush(None),
158 }
159 }
160 if let Some(w) = cache.as_mut() { w.apply(&self.data) } else { Ok(()) }
161 }
162
163 async fn trim(
164 &self,
165 device_block_offset: u64,
166 block_count: u32,
167 _trace_flow_id: Option<NonZero<u64>>,
168 ) -> Result<(), zx::Status> {
169 self.add_jitter().await;
170
171 if let Some(observer) = self.observer.as_ref() {
172 observer.trim(device_block_offset, block_count);
173 }
174 if device_block_offset + block_count as u64 > self.info.block_count().unwrap() {
175 Err(zx::Status::OUT_OF_RANGE)
176 } else {
177 Ok(())
178 }
179 }
180}
181
182pub struct WriteCache {
185 block_size: u64,
186 block_offsets: Vec<u64>,
187 buffer: Vec<u8>,
188}
189
190impl WriteCache {
191 pub(crate) fn new(block_size: u64) -> Self {
192 Self { block_size, block_offsets: vec![], buffer: vec![] }
193 }
194
195 fn insert(&mut self, block_offset: u64, contents: &[u8]) {
196 let block_count = contents.len() as u64 / self.block_size;
197 let mut buf_offset = 0;
198 for offset in block_offset..block_offset + block_count {
199 self.block_offsets.push(offset);
200 self.buffer
201 .extend_from_slice(&contents[buf_offset..buf_offset + self.block_size as usize]);
202 buf_offset += self.block_size as usize;
203 }
204 }
205
206 fn read(
208 &self,
209 data: &zx::Vmo,
210 block_offset: u64,
211 contents: &mut [u8],
212 ) -> Result<(), zx::Status> {
213 let block_count = contents.len() as u64 / self.block_size;
214 let max_offset = block_offset + block_count;
215 data.read(contents, block_offset * self.block_size)?;
216 for (idx, offset) in self.block_offsets.iter().enumerate() {
220 if *offset >= block_offset && *offset < max_offset {
221 let in_offset = idx * self.block_size as usize;
222 let out_offset = ((*offset - block_offset) * self.block_size) as usize;
223 contents[out_offset..out_offset + self.block_size as usize]
224 .copy_from_slice(&self.buffer[in_offset..in_offset + self.block_size as usize]);
225 }
226 }
227 Ok(())
228 }
229
230 fn apply(&mut self, data: &zx::Vmo) -> Result<(), zx::Status> {
232 let mut buf_offset = 0;
233 for offset in self.block_offsets.drain(..) {
234 data.write(
235 &self.buffer[buf_offset..buf_offset + self.block_size as usize],
236 offset * self.block_size,
237 )?;
238 buf_offset += self.block_size as usize;
239 }
240 self.buffer.clear();
241 Ok(())
242 }
243
244 pub fn len(&self) -> usize {
246 self.block_offsets.len()
247 }
248
249 pub fn iter(&self) -> impl Iterator<Item = (&u64, &[u8])> {
251 self.block_offsets.iter().zip(self.buffer.windows(self.block_size as usize))
252 }
253
254 fn swap_writes(&mut self, i: usize, j: usize) {
255 self.block_offsets.swap(i, j);
256 let bs = self.block_size as usize;
257 let mut buf = vec![0u8; bs];
258 buf.copy_from_slice(&self.buffer[i * bs..(i + 1) * bs]);
259 self.buffer.copy_within(j * bs..(j + 1) * bs, i * bs);
260 self.buffer[j * bs..(j + 1) * bs].copy_from_slice(&buf[..]);
261 }
262
263 pub fn shuffle(&mut self) {
265 let mut rng = rand::rng();
267 for i in 0..self.block_offsets.len() {
268 let j = rng.random_range(0..=i);
269 if i != j {
270 self.swap_writes(i, j);
271 }
272 }
273 }
274
275 pub fn discard_some(&mut self) {
277 let mut rng = rand::rng();
278 let idx = rng.random_range(0..=self.block_offsets.len());
279 for i in idx..self.block_offsets.len() {
280 self.buffer[i * self.block_size as usize..(i + 1) * self.block_size as usize]
281 .fill(0xab);
282 }
283 }
284}
285
286pub enum WriteAction {
288 Write,
289 Discard,
290 Fail,
291}
292
293pub trait Observer: Send + Sync {
294 fn read(
295 &self,
296 _device_block_offset: u64,
297 _block_count: u32,
298 _vmo: &Arc<zx::Vmo>,
299 _vmo_offset: u64,
300 ) {
301 }
302
303 fn write(
304 &self,
305 _device_block_offset: u64,
306 _block_count: u32,
307 _vmo: &Arc<zx::Vmo>,
308 _vmo_offset: u64,
309 _opts: WriteOptions,
310 ) -> WriteAction {
311 WriteAction::Write
312 }
313
314 fn flush(&self, _writes: Option<&mut WriteCache>) {}
317
318 fn close(&self, _writes: Option<&mut WriteCache>) {}
321
322 fn trim(&self, _device_block_offset: u64, _block_count: u32) {}
323}
324
325pub struct FscryptKeys(BTreeMap<u8, FscryptSoftwareInoLblk32FileCipher>);
326
327impl FscryptKeys {
328 pub fn new() -> Self {
329 Self(BTreeMap::new())
330 }
331
332 pub fn evict_key(&mut self, slot: u8) -> Result<(), zx::Status> {
333 match self.0.remove(&slot) {
334 Some(_) => Ok(()),
335 None => Err(zx::Status::INVALID_ARGS),
336 }
337 }
338
339 pub fn program_key(&mut self, xts_key: &[u8; 64]) -> Result<u8, zx::Status> {
340 let unwrapped_key = UnwrappedKey::new(xts_key.to_vec());
341 let cipher = FscryptSoftwareInoLblk32FileCipher::new(&unwrapped_key);
342 for slot in 0..=u8::MAX {
344 if self.0.contains_key(&slot) {
345 continue;
346 }
347 self.0.insert(slot, cipher);
348 return Ok(slot);
349 }
350 Err(zx::Status::NO_RESOURCES)
351 }
352
353 pub fn get_key(&self, slot: u8) -> Result<&FscryptSoftwareInoLblk32FileCipher, zx::Status> {
354 self.0.get(&slot).ok_or(zx::Status::IO)
355 }
356}