Skip to main content

block_server/
lib.rs

1// Copyright 2025 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.
4use anyhow::Error;
5use block_protocol::{BlockFifoRequest, BlockFifoResponse};
6use fblock::{BlockIoFlag, BlockOpcode, MAX_TRANSFER_UNBOUNDED};
7use fidl_fuchsia_storage_block as fblock;
8use fuchsia_async as fasync;
9use fuchsia_async::epoch::{Epoch, EpochGuard};
10use fuchsia_sync::{MappedMutexGuard, Mutex, MutexGuard};
11use futures::{Future, FutureExt as _, TryStreamExt as _};
12use slab::Slab;
13use std::borrow::{Borrow, Cow};
14use std::collections::BTreeMap;
15use std::num::NonZero;
16use std::ops::Range;
17use std::sync::Arc;
18use std::sync::atomic::AtomicU64;
19use storage_device::buffer::Buffer;
20
21pub mod async_interface;
22pub mod c_interface;
23pub mod callback_interface;
24
25#[cfg(test)]
26mod decompression_tests;
27
28pub(crate) const FIFO_MAX_REQUESTS: usize = 64;
29
30type TraceFlowId = Option<NonZero<u64>>;
31
32#[derive(Clone, Debug)]
33pub enum DeviceInfo {
34    /// A raw non-partition block device.
35    Block(BlockInfo),
36    /// A static partition with fixed physical mappings.
37    Partition(PartitionInfo),
38    /// A dynamic volume whose slice/block count is queried via get_volume_info.
39    Volume(VolumeInfo),
40}
41
42impl DeviceInfo {
43    pub fn label(&self) -> &str {
44        match self {
45            Self::Block(BlockInfo { .. }) => "",
46            Self::Partition(PartitionInfo { name, .. }) => name,
47            Self::Volume(VolumeInfo { name, .. }) => name,
48        }
49    }
50    pub fn device_flags(&self) -> fblock::DeviceFlag {
51        match self {
52            Self::Block(BlockInfo { device_flags, .. }) => *device_flags,
53            Self::Partition(PartitionInfo { device_flags, .. }) => *device_flags,
54            Self::Volume(VolumeInfo { device_flags, .. }) => *device_flags,
55        }
56    }
57
58    /// Returns the block count of the device or partition.
59    /// Returns None for dynamic volumes (whose size needs to be queried from the volume manager).
60    pub fn block_count(&self) -> Option<u64> {
61        match self {
62            Self::Block(BlockInfo { block_count, .. }) => Some(*block_count),
63            Self::Partition(PartitionInfo { block_count, .. }) => Some(*block_count),
64            Self::Volume(VolumeInfo { .. }) => None,
65        }
66    }
67
68    pub fn max_transfer_blocks(&self) -> Option<NonZero<u32>> {
69        match self {
70            Self::Block(BlockInfo { max_transfer_blocks, .. }) => max_transfer_blocks.clone(),
71            Self::Partition(PartitionInfo { max_transfer_blocks, .. }) => {
72                max_transfer_blocks.clone()
73            }
74            Self::Volume(VolumeInfo { max_transfer_blocks, .. }) => max_transfer_blocks.clone(),
75        }
76    }
77
78    fn max_transfer_size(&self, block_size: u32) -> u32 {
79        if let Some(max_blocks) = self.max_transfer_blocks() {
80            max_blocks.get() * block_size
81        } else {
82            MAX_TRANSFER_UNBOUNDED
83        }
84    }
85
86    pub fn type_guid(&self) -> Option<[u8; 16]> {
87        match self {
88            Self::Partition(PartitionInfo { type_guid, .. }) => Some(*type_guid),
89            Self::Volume(VolumeInfo { type_guid, .. }) => Some(*type_guid),
90            Self::Block(_) => None,
91        }
92    }
93
94    pub fn instance_guid(&self) -> Option<[u8; 16]> {
95        match self {
96            Self::Partition(PartitionInfo { instance_guid, .. }) => Some(*instance_guid),
97            Self::Volume(VolumeInfo { instance_guid, .. }) => Some(*instance_guid),
98            Self::Block(_) => None,
99        }
100    }
101}
102
103/// Information associated with non-partition block devices.
104#[derive(Clone, Default, Debug)]
105pub struct BlockInfo {
106    pub device_flags: fblock::DeviceFlag,
107    pub block_count: u64,
108    pub max_transfer_blocks: Option<NonZero<u32>>,
109}
110
111/// Information associated with a block device that is also a partition.
112#[derive(Clone, Default, Debug)]
113pub struct PartitionInfo {
114    /// The device flags reported by the underlying device.
115    pub device_flags: fblock::DeviceFlag,
116    pub max_transfer_blocks: Option<NonZero<u32>>,
117    /// This is None for partitions which have multiple logical extents, in which case the
118    /// start_block_offset is not meaningful and potentially confusing.
119    pub start_block_offset: Option<u64>,
120    pub block_count: u64,
121    pub type_guid: [u8; 16],
122    pub instance_guid: [u8; 16],
123    pub name: String,
124    /// This can be None for partitions which are composed of multiple partitions (e.g. an
125    /// overlay partition in GPT).
126    pub flags: Option<u64>,
127}
128
129/// Information associated with a dynamic volume (such as FVM volumes).
130#[derive(Clone, Default, Debug)]
131pub struct VolumeInfo {
132    /// The device flags reported by the underlying device.
133    pub device_flags: fblock::DeviceFlag,
134    pub max_transfer_blocks: Option<NonZero<u32>>,
135    pub type_guid: [u8; 16],
136    pub instance_guid: [u8; 16],
137    pub name: String,
138    pub flags: u64,
139}
140
141/// We internally keep track of active requests, so that when the server is torn down, we can
142/// deallocate all of the resources for pending requests.
143struct ActiveRequest<S> {
144    session: S,
145    group_or_request: GroupOrRequest,
146    trace_flow_id: TraceFlowId,
147    _epoch_guard: EpochGuard<'static>,
148    status: zx::Status,
149    count: u32,
150    req_id: Option<u32>,
151    decompression_info: Option<DecompressionInfo>,
152}
153
154struct DecompressionInfo {
155    // This is the range of compressed bytes in receiving buffer.
156    compressed_range: Range<usize>,
157
158    // This is the range in the target VMO where we will write uncompressed bytes.
159    uncompressed_range: Range<u64>,
160
161    bytes_so_far: u64,
162    mapping: Arc<VmoMapping>,
163    buffer: Option<Buffer<'static>>,
164}
165
166impl DecompressionInfo {
167    /// Returns the uncompressed slice.
168    fn uncompressed_slice(&self) -> *mut [u8] {
169        std::ptr::slice_from_raw_parts_mut(
170            (self.mapping.base + self.uncompressed_range.start as usize) as *mut u8,
171            (self.uncompressed_range.end - self.uncompressed_range.start) as usize,
172        )
173    }
174}
175
176pub struct ActiveRequests<S>(Mutex<ActiveRequestsInner<S>>);
177
178impl<S> Default for ActiveRequests<S> {
179    fn default() -> Self {
180        Self(Mutex::new(ActiveRequestsInner { requests: Slab::default() }))
181    }
182}
183
184impl<S> ActiveRequests<S> {
185    fn complete_and_take_response(
186        &self,
187        request_id: RequestId,
188        status: zx::Status,
189    ) -> Option<(S, BlockFifoResponse)> {
190        self.0.lock().complete_and_take_response(request_id, status)
191    }
192
193    fn request(&self, request_id: RequestId) -> MappedMutexGuard<'_, ActiveRequest<S>> {
194        MutexGuard::map(self.0.lock(), |i| &mut i.requests[request_id.0])
195    }
196}
197
198struct ActiveRequestsInner<S> {
199    requests: Slab<ActiveRequest<S>>,
200}
201
202// Keeps track of all the requests that are currently being processed
203impl<S> ActiveRequestsInner<S> {
204    /// Completes a request.
205    fn complete(&mut self, request_id: RequestId, status: zx::Status) {
206        let group = &mut self.requests[request_id.0];
207
208        group.count = group.count.checked_sub(1).unwrap();
209        if status != zx::Status::OK && group.status == zx::Status::OK {
210            group.status = status
211        }
212
213        fuchsia_trace::duration!(
214            "storage",
215            "block_server::finish_transaction",
216            "request_id" => request_id.0,
217            "group_completed" => group.count == 0,
218            "status" => status.into_raw());
219        if let Some(trace_flow_id) = group.trace_flow_id {
220            fuchsia_trace::flow_step!(
221                "storage",
222                "block_server::finish_request",
223                trace_flow_id.get().into()
224            );
225        }
226
227        if group.count == 0
228            && group.status == zx::Status::OK
229            && let Some(info) = &mut group.decompression_info
230        {
231            struct RawDCtx(std::ptr::NonNull<zstd::zstd_safe::zstd_sys::ZSTD_DCtx>);
232
233            thread_local! {
234                static RAW_DECOMPRESSOR: std::cell::RefCell<RawDCtx> = {
235                    // SAFETY: Creating a new ZSTD decompression context does not borrow or capture
236                    // any external memory.
237                    let raw_ptr = unsafe { zstd::zstd_safe::zstd_sys::ZSTD_createDCtx() };
238                    let ptr = std::ptr::NonNull::new(raw_ptr).expect("ZSTD_createDCtx failed");
239                    std::cell::RefCell::new(RawDCtx(ptr))
240                };
241            }
242
243            impl Drop for RawDCtx {
244                fn drop(&mut self) {
245                    // SAFETY: `self.0` is non-null and was allocated via `ZSTD_createDCtx`.
246                    unsafe {
247                        zstd::zstd_safe::zstd_sys::ZSTD_freeDCtx(self.0.as_ptr());
248                    }
249                }
250            }
251
252            RAW_DECOMPRESSOR.with_borrow_mut(|decompressor| {
253                let dctx = decompressor.0.as_ptr();
254                let target = info.uncompressed_slice();
255                let buffer = info.buffer.take().unwrap();
256                let source = buffer.subslice(info.compressed_range.clone());
257
258                // SAFETY: `target` points to valid uncompressed destination memory in the VMO
259                // mapping. `source` points to valid compressed memory within `buffer`.
260                unsafe {
261                    let result = zstd::zstd_safe::zstd_sys::ZSTD_decompressDCtx(
262                        dctx,
263                        target as *mut u8 as *mut std::os::raw::c_void,
264                        target.len(),
265                        source.as_ptr() as *const std::os::raw::c_void,
266                        source.len(),
267                    );
268                    if zstd::zstd_safe::zstd_sys::ZSTD_isError(result) != 0 {
269                        let error = zstd::zstd_safe::get_error_name(result);
270                        log::warn!(error:?; "Decompression error");
271                        group.status = zx::Status::IO_DATA_INTEGRITY;
272                    }
273                }
274            });
275        }
276    }
277
278    /// Takes the response if all requests are finished.
279    fn take_response(&mut self, request_id: RequestId) -> Option<(S, BlockFifoResponse)> {
280        let group = &self.requests[request_id.0];
281        match group.req_id {
282            Some(reqid) if group.count == 0 => {
283                let group = self.requests.remove(request_id.0);
284                Some((
285                    group.session,
286                    BlockFifoResponse {
287                        status: group.status.into_raw(),
288                        reqid,
289                        group: group.group_or_request.group_id().unwrap_or(0),
290                        ..Default::default()
291                    },
292                ))
293            }
294            _ => None,
295        }
296    }
297
298    /// Competes the request and returns a response if the request group is finished.
299    fn complete_and_take_response(
300        &mut self,
301        request_id: RequestId,
302        status: zx::Status,
303    ) -> Option<(S, BlockFifoResponse)> {
304        self.complete(request_id, status);
305        self.take_response(request_id)
306    }
307}
308
309/// BlockServer is an implementation of fuchsia.hardware.block.partition.Partition.
310/// cbindgen:no-export
311pub struct BlockServer<SM: SessionManager> {
312    block_size: u32,
313    orchestrator: Arc<SM::Orchestrator>,
314}
315
316#[derive(Clone, Debug, PartialEq, Eq)]
317pub struct BlockOffsetMapping {
318    pub target_block_offset: u64,
319    pub length: u64,
320}
321
322/// Merges physically contiguous mappings into single logical mappings.  This is useful because it
323/// prevents requests from being unnecessarily split if they span the two mappings.
324pub fn coalesce_mappings(raw_mappings: Vec<BlockOffsetMapping>) -> Vec<BlockOffsetMapping> {
325    let mut mappings: Vec<BlockOffsetMapping> = Vec::with_capacity(raw_mappings.len());
326    for m in raw_mappings {
327        if let Some(last) = mappings.last_mut()
328            && last.target_block_offset + last.length == m.target_block_offset
329        {
330            last.length += m.length;
331        } else {
332            mappings.push(m);
333        }
334    }
335    mappings
336}
337
338/// Remaps the offset of block requests based on an internal map of contiguous logical extents.
339#[derive(Clone, Debug, Default, PartialEq, Eq)]
340pub struct OffsetMap {
341    mappings: Vec<BlockOffsetMapping>,
342}
343
344impl OffsetMap {
345    /// Creates a new `OffsetMap` from a list of `BlockOffsetMapping`s.
346    /// Returns `INVALID_ARGS` if any mapping length is zero, or if logical/target block offsets
347    /// overflow `u64`.
348    pub fn new(mappings: Vec<BlockOffsetMapping>) -> Result<Self, zx::Status> {
349        let mut total: u64 = 0;
350        for m in &mappings {
351            if m.length == 0 {
352                return Err(zx::Status::INVALID_ARGS);
353            }
354            m.target_block_offset.checked_add(m.length).ok_or(zx::Status::INVALID_ARGS)?;
355            total = total.checked_add(m.length).ok_or(zx::Status::INVALID_ARGS)?;
356        }
357        Ok(Self { mappings })
358    }
359
360    /// Creates an empty `OffsetMap`.
361    pub fn empty() -> Self {
362        Self { mappings: Vec::new() }
363    }
364
365    /// Maps `logical_offset` to `(target_block_offset, len)`, where `len` is the total number of
366    /// blocks which can be addressed from `target_block_offset` onwards.
367    ///
368    /// For example: if you have logical offset 0 pointing to physical extent 1000..1010, and you
369    /// search for offset 5, the return value will be `Some((1005, 5))`.
370    pub fn map(&self, logical_offset: u64) -> Option<(u64, u32)> {
371        let mut current_logical_start = 0;
372        for mapping in &self.mappings {
373            let current_logical_end = current_logical_start + mapping.length;
374            if logical_offset < current_logical_end {
375                let delta = logical_offset - current_logical_start;
376                let dev_offset = mapping.target_block_offset + delta;
377                let len = u32::try_from(current_logical_end - logical_offset).unwrap_or(u32::MAX);
378                return Some((dev_offset, len));
379            }
380            current_logical_start = current_logical_end;
381        }
382        None
383    }
384
385    pub fn is_empty(&self) -> bool {
386        self.mappings.is_empty()
387    }
388
389    pub fn total_blocks(&self) -> u64 {
390        self.mappings.iter().map(|m| m.length).sum()
391    }
392
393    /// Returns true if the block range `[offset, offset + length)` falls entirely within this
394    /// map's total blocks. If the map is empty, returns true (no range restriction).
395    pub fn are_blocks_within_source_range(&self, (offset, length): (u64, u32)) -> bool {
396        if self.is_empty() {
397            return true;
398        }
399        let total = self.total_blocks();
400        offset <= total && total - offset >= length as u64
401    }
402
403    pub fn mappings(&self) -> &[BlockOffsetMapping] {
404        &self.mappings
405    }
406}
407
408impl TryFrom<&fblock::BlockOffsetMapping> for BlockOffsetMapping {
409    type Error = zx::Status;
410
411    fn try_from(wire: &fblock::BlockOffsetMapping) -> Result<Self, Self::Error> {
412        if wire.length == 0 {
413            return Err(zx::Status::INVALID_ARGS);
414        }
415        wire.target_block_offset.checked_add(wire.length).ok_or(zx::Status::OUT_OF_RANGE)?;
416        Ok(BlockOffsetMapping {
417            target_block_offset: wire.target_block_offset,
418            length: wire.length,
419        })
420    }
421}
422
423impl TryFrom<fblock::BlockOffsetMapping> for BlockOffsetMapping {
424    type Error = zx::Status;
425
426    fn try_from(wire: fblock::BlockOffsetMapping) -> Result<Self, Self::Error> {
427        BlockOffsetMapping::try_from(&wire)
428    }
429}
430
431impl From<&BlockOffsetMapping> for fblock::BlockOffsetMapping {
432    fn from(m: &BlockOffsetMapping) -> Self {
433        fblock::BlockOffsetMapping { target_block_offset: m.target_block_offset, length: m.length }
434    }
435}
436
437impl From<BlockOffsetMapping> for fblock::BlockOffsetMapping {
438    fn from(m: BlockOffsetMapping) -> Self {
439        fblock::BlockOffsetMapping::from(&m)
440    }
441}
442
443impl TryFrom<&[fblock::BlockOffsetMapping]> for OffsetMap {
444    type Error = zx::Status;
445
446    fn try_from(wire: &[fblock::BlockOffsetMapping]) -> Result<Self, Self::Error> {
447        let raw_mappings: Vec<BlockOffsetMapping> =
448            wire.iter().map(BlockOffsetMapping::try_from).collect::<Result<_, _>>()?;
449        OffsetMap::new(raw_mappings)
450    }
451}
452
453impl TryFrom<Vec<fblock::BlockOffsetMapping>> for OffsetMap {
454    type Error = zx::Status;
455
456    fn try_from(wire: Vec<fblock::BlockOffsetMapping>) -> Result<Self, Self::Error> {
457        OffsetMap::try_from(wire.as_slice())
458    }
459}
460
461impl From<&OffsetMap> for Vec<fblock::BlockOffsetMapping> {
462    fn from(offset_map: &OffsetMap) -> Self {
463        offset_map.mappings.iter().map(fblock::BlockOffsetMapping::from).collect()
464    }
465}
466
467// Methods take Arc<Self> rather than &self because of
468// https://github.com/rust-lang/rust/issues/42940.
469pub trait SessionManager: 'static {
470    /// The Orchestrator is an object that holds the `SessionManager` and any other state that needs
471    /// to be shared between sessions.  It is responsible for keeping the `SessionManager` alive.
472    /// We use this type instead of directly holding an Arc<SessionManager> in BlockServer, to avoid
473    /// nested Arcs in concrete implementations which need to keep additional state.
474    type Orchestrator: Borrow<Self> + Send + Sync;
475
476    const SUPPORTS_DECOMPRESSION: bool;
477
478    type Session;
479
480    /// Returns true iff `a` and `b` identify the same session.  Used to scope
481    /// group-ID lookups in the shared `active_requests` slab to the originating
482    /// session.
483    fn session_eq(a: &Self::Session, b: &Self::Session) -> bool;
484
485    fn on_attach_vmo(
486        orchestrator: Arc<Self::Orchestrator>,
487        vmo: &Arc<zx::Vmo>,
488    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
489
490    /// Creates a new session to handle `stream`.
491    ///
492    /// The returned future should run until the session completes, for example when the client end
493    /// closes.
494    ///
495    /// `offset_map` is an optional client-provided map to adjust the offset/length of FIFO
496    /// requests.  If the implementation supports mapping requests, it must forward this back to
497    /// [`SessionHelper::new`].
498    fn open_session(
499        orchestrator: Arc<Self::Orchestrator>,
500        stream: fblock::SessionRequestStream,
501        offset_map: OffsetMap,
502        block_size: u32,
503    ) -> impl Future<Output = Result<(), Error>> + Send;
504
505    /// Called to get block/partition information for Block::GetInfo, Partition::GetTypeGuid, etc.
506    fn get_info(&self) -> Cow<'_, DeviceInfo>;
507
508    /// Called to handle the GetVolumeInfo FIDL call.
509    fn get_volume_info(
510        &self,
511    ) -> impl Future<Output = Result<(fblock::VolumeManagerInfo, fblock::VolumeInfo), zx::Status>> + Send
512    {
513        async { Err(zx::Status::NOT_SUPPORTED) }
514    }
515
516    /// Called to handle the QuerySlices FIDL call.
517    fn query_slices(
518        &self,
519        _start_slices: &[u64],
520    ) -> impl Future<Output = Result<Vec<fblock::VsliceRange>, zx::Status>> + Send {
521        async { Err(zx::Status::NOT_SUPPORTED) }
522    }
523
524    /// Called to handle the Shrink FIDL call.
525    fn extend(
526        &self,
527        _start_slice: u64,
528        _slice_count: u64,
529    ) -> impl Future<Output = Result<(), zx::Status>> + Send {
530        async { Err(zx::Status::NOT_SUPPORTED) }
531    }
532
533    /// Called to handle the Shrink FIDL call.
534    fn shrink(
535        &self,
536        _start_slice: u64,
537        _slice_count: u64,
538    ) -> impl Future<Output = Result<(), zx::Status>> + Send {
539        async { Err(zx::Status::NOT_SUPPORTED) }
540    }
541
542    /// Returns the active requests.
543    fn active_requests(&self) -> &ActiveRequests<Self::Session>;
544}
545
546/// A helper trait for converting various types into an `Orchestrator`.
547///
548/// This exists to simplify [`BlockServer::new`].
549pub trait IntoOrchestrator {
550    type SM: SessionManager;
551
552    fn into_orchestrator(self) -> Arc<<Self::SM as SessionManager>::Orchestrator>;
553}
554
555impl<SM: SessionManager> BlockServer<SM> {
556    pub fn new(block_size: u32, orchestrator: impl IntoOrchestrator<SM = SM>) -> Self {
557        Self { block_size, orchestrator: orchestrator.into_orchestrator() }
558    }
559
560    pub fn session_manager(&self) -> &SM {
561        self.orchestrator.as_ref().borrow()
562    }
563
564    /// Called to process requests for fuchsia.storage.block.Block.
565    pub async fn handle_requests(
566        &self,
567        mut requests: fblock::BlockRequestStream,
568    ) -> Result<(), Error> {
569        let scope = fasync::Scope::new();
570        loop {
571            match requests.try_next().await {
572                Ok(Some(request)) => {
573                    if let Some(session) = self.handle_request(request).await? {
574                        scope.spawn(session.map(|_| ()));
575                    }
576                }
577                Ok(None) => break,
578                Err(error) => log::warn!(error:?; "Invalid request"),
579            }
580        }
581        scope.await;
582        Ok(())
583    }
584
585    /// Processes a Block request.  If a new session task is created in response to the request,
586    /// it is returned.
587    async fn handle_request(
588        &self,
589        request: fblock::BlockRequest,
590    ) -> Result<Option<impl Future<Output = Result<(), Error>> + Send + use<SM>>, Error> {
591        match request {
592            fblock::BlockRequest::GetInfo { responder } => {
593                let info = self.device_info();
594                let max_transfer_size = info.max_transfer_size(self.block_size);
595                let (block_count, mut flags) = match info.as_ref() {
596                    DeviceInfo::Block(BlockInfo { block_count, device_flags, .. }) => {
597                        (*block_count, *device_flags)
598                    }
599                    DeviceInfo::Partition(partition_info) => {
600                        (partition_info.block_count, partition_info.device_flags)
601                    }
602                    DeviceInfo::Volume(volume_info) => {
603                        let volume_info_fidl = self.session_manager().get_volume_info().await?;
604                        let block_count = volume_info_fidl.0.slice_size
605                            * volume_info_fidl.1.partition_slice_count
606                            / self.block_size as u64;
607                        (block_count, volume_info.device_flags)
608                    }
609                };
610                if SM::SUPPORTS_DECOMPRESSION {
611                    flags |= fblock::DeviceFlag::ZSTD_DECOMPRESSION_SUPPORT;
612                }
613                responder.send(Ok(&fblock::BlockInfo {
614                    block_count,
615                    block_size: self.block_size,
616                    max_transfer_size,
617                    flags,
618                }))?;
619            }
620            fblock::BlockRequest::OpenSession { session, control_handle: _ } => {
621                return Ok(Some(SM::open_session(
622                    self.orchestrator.clone(),
623                    session.into_stream(),
624                    OffsetMap::empty(),
625                    self.block_size,
626                )));
627            }
628            fblock::BlockRequest::OpenSessionWithOptions {
629                session,
630                mappings,
631                control_handle: _,
632            } => {
633                let info = self.device_info();
634                let offset_map: OffsetMap = match mappings.as_slice().try_into() {
635                    Ok(map) => map,
636                    Err(status) => {
637                        session.close_with_epitaph(status)?;
638                        return Ok(None);
639                    }
640                };
641                if let Some(max) = info.block_count() {
642                    for m in offset_map.mappings() {
643                        if m.target_block_offset.checked_add(m.length).unwrap_or(u64::MAX) > max {
644                            log::warn!("Invalid mapping for session: {m:?} (max blocks {max})");
645                            session.close_with_epitaph(zx::Status::OUT_OF_RANGE)?;
646                            return Ok(None);
647                        }
648                    }
649                }
650                return Ok(Some(SM::open_session(
651                    self.orchestrator.clone(),
652                    session.into_stream(),
653                    offset_map,
654                    self.block_size,
655                )));
656            }
657            fblock::BlockRequest::GetTypeGuid { responder } => {
658                match self.device_info().type_guid() {
659                    Some(guid) => {
660                        responder.send(zx::sys::ZX_OK, Some(&fblock::Guid { value: guid }))?
661                    }
662                    None => responder.send(zx::sys::ZX_ERR_NOT_SUPPORTED, None)?,
663                }
664            }
665            fblock::BlockRequest::GetInstanceGuid { responder } => {
666                match self.device_info().instance_guid() {
667                    Some(guid) => {
668                        responder.send(zx::sys::ZX_OK, Some(&fblock::Guid { value: guid }))?
669                    }
670                    None => responder.send(zx::sys::ZX_ERR_NOT_SUPPORTED, None)?,
671                }
672            }
673            fblock::BlockRequest::GetName { responder } => {
674                let info = self.device_info();
675                match info.as_ref() {
676                    DeviceInfo::Partition(_) | DeviceInfo::Volume(_) => {
677                        responder.send(zx::sys::ZX_OK, Some(info.label()))?;
678                    }
679                    _ => responder.send(zx::sys::ZX_ERR_NOT_SUPPORTED, None)?,
680                }
681            }
682            fblock::BlockRequest::GetMetadata { responder } => {
683                let device_info = self.device_info();
684                match device_info.as_ref() {
685                    DeviceInfo::Partition(info) => {
686                        let mut type_guid =
687                            fblock::Guid { value: [0u8; fblock::GUID_LENGTH as usize] };
688                        type_guid.value.copy_from_slice(&info.type_guid);
689                        let mut instance_guid =
690                            fblock::Guid { value: [0u8; fblock::GUID_LENGTH as usize] };
691                        instance_guid.value.copy_from_slice(&info.instance_guid);
692                        let start_block_offset = info.start_block_offset;
693                        let flags = info.flags;
694                        responder.send(Ok(&fblock::PartitionInfo {
695                            name: Some(info.name.clone()),
696                            type_guid: Some(type_guid),
697                            instance_guid: Some(instance_guid),
698                            start_block_offset,
699                            num_blocks: device_info.block_count(),
700                            flags,
701                            ..Default::default()
702                        }))?;
703                    }
704                    DeviceInfo::Volume(info) => {
705                        let mut type_guid =
706                            fblock::Guid { value: [0u8; fblock::GUID_LENGTH as usize] };
707                        type_guid.value.copy_from_slice(&info.type_guid);
708                        let mut instance_guid =
709                            fblock::Guid { value: [0u8; fblock::GUID_LENGTH as usize] };
710                        instance_guid.value.copy_from_slice(&info.instance_guid);
711                        responder.send(Ok(&fblock::PartitionInfo {
712                            name: Some(info.name.clone()),
713                            type_guid: Some(type_guid),
714                            instance_guid: Some(instance_guid),
715                            start_block_offset: None,
716                            num_blocks: device_info.block_count(),
717                            flags: Some(info.flags),
718                            ..Default::default()
719                        }))?;
720                    }
721                    _ => responder.send(Err(zx::sys::ZX_ERR_NOT_SUPPORTED))?,
722                }
723            }
724            fblock::BlockRequest::QuerySlices { responder, start_slices } => {
725                match self.session_manager().query_slices(&start_slices).await {
726                    Ok(mut results) => {
727                        let results_len = results.len();
728                        assert!(results_len <= 16);
729                        results.resize(16, fblock::VsliceRange { allocated: false, count: 0 });
730                        responder.send(
731                            zx::sys::ZX_OK,
732                            &results.try_into().unwrap(),
733                            results_len as u64,
734                        )?;
735                    }
736                    Err(s) => {
737                        responder.send(
738                            s.into_raw(),
739                            &[fblock::VsliceRange { allocated: false, count: 0 }; 16],
740                            0,
741                        )?;
742                    }
743                }
744            }
745            fblock::BlockRequest::GetVolumeInfo { responder, .. } => {
746                match self.session_manager().get_volume_info().await {
747                    Ok((manager_info, volume_info)) => {
748                        responder.send(zx::sys::ZX_OK, Some(&manager_info), Some(&volume_info))?
749                    }
750                    Err(s) => responder.send(s.into_raw(), None, None)?,
751                }
752            }
753            fblock::BlockRequest::Extend { responder, start_slice, slice_count } => {
754                responder.send(
755                    zx::Status::from(self.session_manager().extend(start_slice, slice_count).await)
756                        .into_raw(),
757                )?;
758            }
759            fblock::BlockRequest::Shrink { responder, start_slice, slice_count } => {
760                responder.send(
761                    zx::Status::from(self.session_manager().shrink(start_slice, slice_count).await)
762                        .into_raw(),
763                )?;
764            }
765            fblock::BlockRequest::Destroy { responder, .. } => {
766                responder.send(zx::sys::ZX_ERR_NOT_SUPPORTED)?;
767            }
768        }
769        Ok(None)
770    }
771
772    fn device_info(&self) -> Cow<'_, DeviceInfo> {
773        self.session_manager().get_info()
774    }
775}
776
777pub(crate) struct RegisteredVmo {
778    pub vmo: Arc<zx::Vmo>,
779    pub size: u64,
780    pub mapping: Option<Arc<VmoMapping>>,
781}
782
783impl RegisteredVmo {
784    /// Validates that a request range (`vmo_offset` to `vmo_offset + length`, in bytes) falls
785    /// within the bounds of this VMO.
786    fn validate_request(&self, vmo_offset: u64, length: u64) -> Result<(), zx::Status> {
787        if vmo_offset > self.size || self.size - vmo_offset < length {
788            Err(zx::Status::OUT_OF_RANGE)
789        } else {
790            Ok(())
791        }
792    }
793
794    /// Returns the cached VMO mapping if available, or creates and caches a new mapping.
795    fn get_or_create_mapping(&mut self) -> Result<Arc<VmoMapping>, zx::Status> {
796        match &self.mapping {
797            Some(mapping) => Ok(mapping.clone()),
798            None => {
799                let mapping = VmoMapping::new(&self.vmo, self.size as usize)?;
800                self.mapping = Some(mapping.clone());
801                Ok(mapping)
802            }
803        }
804    }
805}
806
807struct SessionHelper<SM: SessionManager> {
808    orchestrator: Arc<SM::Orchestrator>,
809    offset_map: OffsetMap,
810    max_transfer_blocks: Option<NonZero<u32>>,
811    block_size: u32,
812    peer_fifo: zx::Fifo<BlockFifoResponse, BlockFifoRequest>,
813    vmos: Mutex<BTreeMap<u16, RegisteredVmo>>,
814}
815
816struct VmoMapping {
817    base: usize,
818    size: usize,
819}
820
821impl VmoMapping {
822    fn new(vmo: &zx::Vmo, size: usize) -> Result<Arc<Self>, zx::Status> {
823        Ok(Arc::new(Self {
824            base: fuchsia_runtime::vmar_root_self()
825                .map(0, vmo, 0, size, zx::VmarFlags::PERM_WRITE | zx::VmarFlags::PERM_READ)
826                .inspect_err(|error| {
827                    log::warn!(error:?, size; "VmoMapping: unable to map VMO");
828                })?,
829            size,
830        }))
831    }
832}
833
834impl Drop for VmoMapping {
835    fn drop(&mut self) {
836        // SAFETY: We mapped this in `VmoMapping::new`.
837        unsafe {
838            let _ = fuchsia_runtime::vmar_root_self().unmap(self.base, self.size);
839        }
840    }
841}
842
843enum HandleRequestResult {
844    /// The request was handled successfully.
845    Ok,
846    /// The request closed the stream.  The caller must shut down the session, and must call the
847    /// provided callback after the session is completely shut down.  The caller should assume that
848    /// no further requests need to be handled once this is received.
849    Closed(Box<dyn FnOnce() + Send + 'static>),
850}
851
852impl<SM: SessionManager> SessionHelper<SM> {
853    fn new(
854        orchestrator: Arc<SM::Orchestrator>,
855        offset_map: OffsetMap,
856        max_transfer_blocks: Option<NonZero<u32>>,
857        block_size: u32,
858    ) -> Result<(Self, zx::Fifo<BlockFifoRequest, BlockFifoResponse>), zx::Status> {
859        let (peer_fifo, fifo) = zx::Fifo::create(16)?;
860        Ok((
861            Self {
862                orchestrator,
863                offset_map,
864                max_transfer_blocks,
865                block_size,
866                peer_fifo,
867                vmos: Mutex::default(),
868            },
869            fifo,
870        ))
871    }
872
873    fn session_manager(&self) -> &SM {
874        self.orchestrator.as_ref().borrow()
875    }
876
877    async fn handle_request(
878        &self,
879        request: fblock::SessionRequest,
880    ) -> Result<HandleRequestResult, Error> {
881        match request {
882            fblock::SessionRequest::GetFifo { responder } => {
883                let rights = zx::Rights::TRANSFER
884                    | zx::Rights::READ
885                    | zx::Rights::WRITE
886                    | zx::Rights::SIGNAL
887                    | zx::Rights::WAIT;
888                match self.peer_fifo.duplicate_handle(rights) {
889                    Ok(fifo) => responder.send(Ok(fifo.downcast()))?,
890                    Err(s) => responder.send(Err(s.into_raw()))?,
891                }
892                Ok(HandleRequestResult::Ok)
893            }
894            fblock::SessionRequest::AttachVmo { vmo, responder } => {
895                let info = vmo.info().map_err(Error::from)?;
896                if info.flags.contains(zx::VmoInfoFlags::RESIZABLE) {
897                    responder.send(Err(zx::Status::INVALID_ARGS.into_raw()))?;
898                    return Ok(HandleRequestResult::Ok);
899                }
900                let size = info.size_bytes;
901                let vmo = Arc::new(vmo);
902                let vmo_id = {
903                    let mut vmos = self.vmos.lock();
904                    if vmos.len() == u16::MAX as usize {
905                        responder.send(Err(zx::Status::NO_RESOURCES.into_raw()))?;
906                        return Ok(HandleRequestResult::Ok);
907                    } else {
908                        let vmo_id = match vmos.last_entry() {
909                            None => 1,
910                            Some(o) => {
911                                o.key().checked_add(1).unwrap_or_else(|| {
912                                    let mut vmo_id = 1;
913                                    // Find the first gap...
914                                    for (&id, _) in &*vmos {
915                                        if id > vmo_id {
916                                            break;
917                                        }
918                                        vmo_id = id + 1;
919                                    }
920                                    vmo_id
921                                })
922                            }
923                        };
924                        vmos.insert(
925                            vmo_id,
926                            RegisteredVmo { vmo: vmo.clone(), size, mapping: None },
927                        );
928                        vmo_id
929                    }
930                };
931                SM::on_attach_vmo(self.orchestrator.clone(), &vmo).await?;
932                responder.send(Ok(&fblock::VmoId { id: vmo_id }))?;
933                Ok(HandleRequestResult::Ok)
934            }
935            fblock::SessionRequest::Close { responder } => {
936                Ok(HandleRequestResult::Closed(Box::new(move || {
937                    if let Err(error) = responder.send(Ok(())) {
938                        log::warn!(error:?; "Error sending close response");
939                    }
940                })))
941            }
942        }
943    }
944
945    /// Decodes `request`.
946    fn decode_fifo_request(
947        &self,
948        session: SM::Session,
949        request: &BlockFifoRequest,
950    ) -> Result<DecodedRequest, Option<BlockFifoResponse>> {
951        let flags = BlockIoFlag::from_bits_truncate(request.command.flags);
952
953        let request_bytes = request.length as u64 * self.block_size as u64;
954
955        let mut operation = BlockOpcode::from_primitive(request.command.opcode)
956            .ok_or(zx::Status::INVALID_ARGS)
957            .and_then(|code| {
958                if flags.contains(BlockIoFlag::DECOMPRESS_WITH_ZSTD) {
959                    if code != BlockOpcode::Read {
960                        return Err(zx::Status::INVALID_ARGS);
961                    }
962                    if !SM::SUPPORTS_DECOMPRESSION {
963                        return Err(zx::Status::NOT_SUPPORTED);
964                    }
965                }
966                if matches!(code, BlockOpcode::Read | BlockOpcode::Write | BlockOpcode::Trim) {
967                    if request.length == 0 {
968                        return Err(zx::Status::INVALID_ARGS);
969                    }
970                    // Make sure the end offset won't wrap.
971                    if request.dev_offset.checked_add(request.length as u64).is_none() {
972                        return Err(zx::Status::OUT_OF_RANGE);
973                    }
974                }
975                if matches!(code, BlockOpcode::Read | BlockOpcode::Write) {
976                    let vmo_byte_offset = request
977                        .vmo_offset
978                        .checked_mul(self.block_size as u64)
979                        .ok_or(zx::Status::OUT_OF_RANGE)?;
980                    if request_bytes.checked_add(vmo_byte_offset).is_none() {
981                        return Err(zx::Status::OUT_OF_RANGE);
982                    }
983                }
984                Ok(match code {
985                    BlockOpcode::Read => Operation::Read {
986                        device_block_offset: request.dev_offset,
987                        block_count: request.length,
988                        _unused: 0,
989                        vmo_offset: request
990                            .vmo_offset
991                            .checked_mul(self.block_size as u64)
992                            .ok_or(zx::Status::OUT_OF_RANGE)?,
993                        options: ReadOptions {
994                            inline_crypto: InlineCryptoOptions {
995                                is_enabled: flags.contains(BlockIoFlag::INLINE_ENCRYPTION_ENABLED),
996                                dun: request.dun,
997                                slot: request.slot,
998                            },
999                        },
1000                    },
1001                    BlockOpcode::Write => {
1002                        let mut options = WriteOptions {
1003                            inline_crypto: InlineCryptoOptions {
1004                                is_enabled: flags.contains(BlockIoFlag::INLINE_ENCRYPTION_ENABLED),
1005                                dun: request.dun,
1006                                slot: request.slot,
1007                            },
1008                            ..WriteOptions::default()
1009                        };
1010                        if flags.contains(BlockIoFlag::FORCE_ACCESS) {
1011                            options.flags |= WriteFlags::FORCE_ACCESS;
1012                        }
1013                        if flags.contains(BlockIoFlag::PRE_BARRIER) {
1014                            options.flags |= WriteFlags::PRE_BARRIER;
1015                        }
1016                        Operation::Write {
1017                            device_block_offset: request.dev_offset,
1018                            block_count: request.length,
1019                            _unused: 0,
1020                            options,
1021                            vmo_offset: request
1022                                .vmo_offset
1023                                .checked_mul(self.block_size as u64)
1024                                .ok_or(zx::Status::OUT_OF_RANGE)?,
1025                        }
1026                    }
1027                    BlockOpcode::Flush => Operation::Flush,
1028                    BlockOpcode::Trim => Operation::Trim {
1029                        device_block_offset: request.dev_offset,
1030                        block_count: request.length,
1031                    },
1032                    BlockOpcode::CloseVmo => Operation::CloseVmo,
1033                })
1034            });
1035
1036        let group_or_request = if flags.contains(BlockIoFlag::GROUP_ITEM) {
1037            GroupOrRequest::Group(request.group)
1038        } else {
1039            GroupOrRequest::Request(request.reqid)
1040        };
1041
1042        let mut active_requests = self.session_manager().active_requests().0.lock();
1043        let mut request_id = None;
1044
1045        // Multiple Block I/O request may be sent as a group.
1046        // Notes:
1047        // - the group is identified by the group id in the request
1048        // - if using groups, a response will not be sent unless `BlockIoFlag::GROUP_LAST`
1049        //   flag is set.
1050        // - when processing a request of a group fails, subsequent requests of that
1051        //   group will not be processed.
1052        // - decompression is a special case, see block-fifo.h for semantics.
1053        //
1054        // Refer to sdk/fidl/fuchsia.hardware.block.driver/block.fidl for details.
1055        if group_or_request.is_group() {
1056            // Search for an existing entry that matches this group.  NOTE: This is a potentially
1057            // expensive way to find a group (it's iterating over all slots in the active-requests
1058            // slab).  This can be optimised easily should we need to.
1059            for (key, group) in &mut active_requests.requests {
1060                if group.group_or_request == group_or_request
1061                    && SM::session_eq(&group.session, &session)
1062                {
1063                    if group.req_id.is_some() {
1064                        // We have already received a request tagged as last.
1065                        if group.status == zx::Status::OK {
1066                            group.status = zx::Status::INVALID_ARGS;
1067                        }
1068                        // Ignore this request.
1069                        return Err(None);
1070                    }
1071                    // See if this is a continuation of a decompressed read.
1072                    if group.status == zx::Status::OK
1073                        && let Some(info) = &mut group.decompression_info
1074                    {
1075                        if let Ok(Operation::Read {
1076                            device_block_offset,
1077                            mut block_count,
1078                            options,
1079                            vmo_offset: 0,
1080                            ..
1081                        }) = operation
1082                        {
1083                            let remaining_bytes = info
1084                                .compressed_range
1085                                .end
1086                                .next_multiple_of(self.block_size as usize)
1087                                as u64
1088                                - info.bytes_so_far;
1089                            if !flags.contains(BlockIoFlag::DECOMPRESS_WITH_ZSTD)
1090                                || request.total_compressed_bytes != 0
1091                                || request.uncompressed_bytes != 0
1092                                || request.compressed_prefix_bytes != 0
1093                                || (flags.contains(BlockIoFlag::GROUP_LAST)
1094                                    && info.bytes_so_far + request_bytes
1095                                        < info.compressed_range.end as u64)
1096                                || (!flags.contains(BlockIoFlag::GROUP_LAST)
1097                                    && request_bytes >= remaining_bytes)
1098                            {
1099                                group.status = zx::Status::INVALID_ARGS;
1100                            } else {
1101                                // We are tolerant of `block_count` being more than we actually
1102                                // need.  This can happen if the client is working with a larger
1103                                // block size than the device block size.  For example, if Blobfs
1104                                // has a 8192 byte block size, but the device might has a 512 byte
1105                                // block size, it can ask for a multiple of 16 blocks, when fewer
1106                                // than that might actually be required to hold the compressed data.
1107                                // It is easier for us to tolerate this here than to get Blobfs to
1108                                // change to pass only the blocks that are required.
1109                                if request_bytes > remaining_bytes {
1110                                    block_count = (remaining_bytes / self.block_size as u64) as u32;
1111                                }
1112
1113                                operation = Ok(Operation::ContinueDecompressedRead {
1114                                    offset: info.bytes_so_far,
1115                                    device_block_offset,
1116                                    block_count,
1117                                    options,
1118                                });
1119
1120                                info.bytes_so_far += block_count as u64 * self.block_size as u64;
1121                            }
1122                        } else {
1123                            group.status = zx::Status::INVALID_ARGS;
1124                        }
1125                    }
1126                    if flags.contains(BlockIoFlag::GROUP_LAST) {
1127                        group.req_id = Some(request.reqid);
1128                        // If the group has had an error, there is no point trying to issue this
1129                        // request.
1130                        if group.status != zx::Status::OK {
1131                            operation = Err(group.status);
1132                        }
1133                    } else if group.status != zx::Status::OK {
1134                        // The group has already encountered an error, so there is no point trying
1135                        // to issue this request.
1136                        return Err(None);
1137                    }
1138                    request_id = Some(RequestId(key));
1139                    group.count += 1;
1140                    break;
1141                }
1142            }
1143        }
1144
1145        let is_single_request =
1146            !flags.contains(BlockIoFlag::GROUP_ITEM) || flags.contains(BlockIoFlag::GROUP_LAST);
1147
1148        let mut decompression_info = None;
1149        let vmo = match operation {
1150            Ok(Operation::Read {
1151                device_block_offset,
1152                mut block_count,
1153                options,
1154                vmo_offset,
1155                ..
1156            }) => match self.vmos.lock().get_mut(&request.vmoid) {
1157                Some(registered_vmo) => {
1158                    if flags.contains(BlockIoFlag::DECOMPRESS_WITH_ZSTD) {
1159                        let compressed_range = request.compressed_prefix_bytes as usize
1160                            ..request.compressed_prefix_bytes as usize
1161                                + request.total_compressed_bytes as usize;
1162                        let required_buffer_size =
1163                            compressed_range.end.next_multiple_of(self.block_size as usize);
1164
1165                        // Validate the initial decompression request.
1166                        if compressed_range.start >= compressed_range.end
1167                            || vmo_offset.checked_add(request.uncompressed_bytes as u64).is_none()
1168                            || (is_single_request && request_bytes < compressed_range.end as u64)
1169                            || (!is_single_request && request_bytes >= required_buffer_size as u64)
1170                        {
1171                            Err(zx::Status::INVALID_ARGS)
1172                        } else {
1173                            // We are tolerant of `block_count` being more than we actually need.
1174                            // This can happen if the client is working in a larger block size than
1175                            // the device block size.  For example, Blobfs has a 8192 byte block
1176                            // size, but the device might have a 512 byte block size.  It is easier
1177                            // for us to tolerate this here than to get Blobfs to change to pass
1178                            // only the blocks that are required.
1179                            let bytes_so_far = if request_bytes > required_buffer_size as u64 {
1180                                block_count =
1181                                    (required_buffer_size / self.block_size as usize) as u32;
1182                                required_buffer_size as u64
1183                            } else {
1184                                request_bytes
1185                            };
1186
1187                            // To decompress, we need to have the target VMO mapped (cached).
1188                            registered_vmo
1189                                .get_or_create_mapping()
1190                                .and_then(|mapping| {
1191                                    // Make sure the `vmo_offset` and `uncompressed_bytes` are within
1192                                    // range.
1193                                    if vmo_offset
1194                                        .checked_add(request.uncompressed_bytes as u64)
1195                                        .is_some_and(|end| end <= mapping.size as u64)
1196                                    {
1197                                        Ok(mapping)
1198                                    } else {
1199                                        Err(zx::Status::OUT_OF_RANGE)
1200                                    }
1201                                })
1202                                .map(|mapping| {
1203                                    // Convert the operation into a `StartDecompressedRead`
1204                                    // operation. For non-fragmented requests, this will be the only
1205                                    // operation, but if it's a fragmented read,
1206                                    // `ContinueDecompressedRead` operations will follow.
1207                                    operation = Ok(Operation::StartDecompressedRead {
1208                                        required_buffer_size,
1209                                        device_block_offset,
1210                                        block_count,
1211                                        options,
1212                                    });
1213                                    // Record sufficient information so that we can decompress when
1214                                    // all the requests complete.
1215                                    decompression_info = Some(DecompressionInfo {
1216                                        compressed_range,
1217                                        bytes_so_far,
1218                                        mapping,
1219                                        uncompressed_range: vmo_offset
1220                                            ..vmo_offset + request.uncompressed_bytes as u64,
1221                                        buffer: None,
1222                                    });
1223                                    None
1224                                })
1225                        }
1226                    } else {
1227                        registered_vmo
1228                            .validate_request(vmo_offset, request_bytes)
1229                            .map(|()| Some(registered_vmo.vmo.clone()))
1230                    }
1231                }
1232                None => Err(zx::Status::IO),
1233            },
1234            Ok(Operation::Write { vmo_offset, .. }) => {
1235                self.vmos.lock().get(&request.vmoid).map_or(Err(zx::Status::IO), |registered_vmo| {
1236                    registered_vmo
1237                        .validate_request(vmo_offset, request_bytes)
1238                        .map(|()| Some(registered_vmo.vmo.clone()))
1239                })
1240            }
1241            Ok(Operation::CloseVmo) => {
1242                self.vmos.lock().remove(&request.vmoid).map_or(
1243                    Err(zx::Status::IO),
1244                    |registered_vmo| {
1245                        let vmo_clone = registered_vmo.vmo.clone();
1246                        // Make sure the VMO is dropped after all current Epoch guards have been
1247                        // dropped.
1248                        Epoch::global().defer(move || drop(vmo_clone));
1249                        Ok(Some(registered_vmo.vmo))
1250                    },
1251                )
1252            }
1253            _ => Ok(None),
1254        }
1255        .unwrap_or_else(|e| {
1256            operation = Err(e);
1257            None
1258        });
1259
1260        let trace_flow_id = NonZero::new(request.trace_flow_id);
1261        let request_id = request_id.unwrap_or_else(|| {
1262            RequestId(active_requests.requests.insert(ActiveRequest {
1263                session,
1264                group_or_request,
1265                trace_flow_id,
1266                _epoch_guard: Epoch::global().guard(),
1267                status: zx::Status::OK,
1268                count: 1,
1269                req_id: is_single_request.then_some(request.reqid),
1270                decompression_info,
1271            }))
1272        });
1273
1274        Ok(DecodedRequest {
1275            request_id,
1276            trace_flow_id,
1277            operation: operation.map_err(|status| {
1278                active_requests.complete_and_take_response(request_id, status).map(|(_, r)| r)
1279            })?,
1280            vmo,
1281        })
1282    }
1283
1284    fn take_vmos(&self) -> BTreeMap<u16, RegisteredVmo> {
1285        std::mem::take(&mut *self.vmos.lock())
1286    }
1287
1288    /// Maps the request and returns the mapped request with an optional remainder.
1289    fn map_request(
1290        &self,
1291        mut request: DecodedRequest,
1292        active_request: &mut ActiveRequest<SM::Session>,
1293    ) -> Result<(DecodedRequest, Option<DecodedRequest>), zx::Status> {
1294        if active_request.status != zx::Status::OK {
1295            return Err(zx::Status::BAD_STATE);
1296        }
1297        if let Some(blocks) = request.operation.blocks() {
1298            if !self.offset_map.are_blocks_within_source_range(blocks) {
1299                return Err(zx::Status::OUT_OF_RANGE);
1300            }
1301        }
1302        let remainder =
1303            request.operation.map(&self.offset_map, self.max_transfer_blocks, self.block_size)?;
1304        if remainder.is_some() {
1305            active_request.count += 1;
1306        }
1307        static CACHE: AtomicU64 = AtomicU64::new(0);
1308        if let Some(context) =
1309            fuchsia_trace::TraceCategoryContext::acquire_cached("storage", &CACHE)
1310        {
1311            use fuchsia_trace::ArgValue;
1312            let trace_args = [
1313                ArgValue::of("request_id", request.request_id.0),
1314                ArgValue::of("opcode", request.operation.trace_label()),
1315            ];
1316            let _scope =
1317                fuchsia_trace::duration("storage", "block_server::start_transaction", &trace_args);
1318            if let Some(trace_flow_id) = active_request.trace_flow_id {
1319                fuchsia_trace::flow_step(
1320                    &context,
1321                    "block_server::start_transaction",
1322                    trace_flow_id.get().into(),
1323                    &[],
1324                );
1325            }
1326        }
1327        let remainder = remainder.map(|operation| DecodedRequest { operation, ..request.clone() });
1328        Ok((request, remainder))
1329    }
1330
1331    /// Drops all requests for which `pred` is true.
1332    ///
1333    /// NOTE: This should only be called once we are certain that the requests will not be
1334    /// completed asynchronously  Otherwise, requests might be completed twice.
1335    fn drop_active_requests(&self, pred: impl Fn(&SM::Session) -> bool) {
1336        self.session_manager().active_requests().0.lock().requests.retain(|_, r| !pred(&r.session));
1337    }
1338
1339    /// Closes all grouped requests for which `pred` is true and which are held open pending the
1340    /// completion of their group.
1341    ///
1342    /// Normally, a request is dropped from ActiveRequests when it is completed.  However, if a
1343    /// request is part of a group, it will not be dropped until a request with GROUP_LAST arrives.
1344    /// If we're shutting down a session, the client may not ever send the GROUP_LAST, so we need to
1345    /// be sure to close these grouped requests.
1346    ///
1347    /// This is called during session shutdown in situations where [`Self::drop_active_requests`]
1348    /// cannot be used (e.g. for the callback interface, which hands off the responsibility of
1349    /// completing requests to its concrete implementation and cannot control when requests are
1350    /// completed relative to session shutdown).
1351    fn close_active_groups(&self, pred: impl Fn(&SM::Session) -> bool) {
1352        self.session_manager().active_requests().0.lock().requests.retain(|_, request| {
1353            if !pred(&request.session) || request.req_id.is_some() {
1354                return true;
1355            }
1356            // Mark the group as completed, and immediately drop any which have no outstanding
1357            // requests (since they will otherwise never be dropped).
1358            request.req_id = Some(u32::MAX);
1359            request.count > 0
1360        });
1361    }
1362}
1363
1364#[repr(transparent)]
1365#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
1366pub struct RequestId(usize);
1367
1368#[derive(Clone, Debug)]
1369struct DecodedRequest {
1370    request_id: RequestId,
1371    trace_flow_id: TraceFlowId,
1372    operation: Operation,
1373    vmo: Option<Arc<zx::Vmo>>,
1374}
1375
1376/// cbindgen:no-export
1377pub type WriteFlags = block_protocol::WriteFlags;
1378pub type WriteOptions = block_protocol::WriteOptions;
1379pub type ReadOptions = block_protocol::ReadOptions;
1380pub type InlineCryptoOptions = block_protocol::InlineCryptoOptions;
1381
1382#[repr(C)]
1383#[derive(Clone, Debug, PartialEq, Eq)]
1384pub enum Operation {
1385    // NOTE: On the C++ side, this ends up as a union and, for efficiency reasons, there is code
1386    // that assumes that some fields for reads and writes (and possibly trim) line-up (e.g. common
1387    // code can read `device_block_offset` from the read variant and then assume it's valid for the
1388    // write variant).
1389    Read {
1390        device_block_offset: u64,
1391        block_count: u32,
1392        _unused: u32,
1393        vmo_offset: u64,
1394        options: ReadOptions,
1395    },
1396    Write {
1397        device_block_offset: u64,
1398        block_count: u32,
1399        _unused: u32,
1400        vmo_offset: u64,
1401        options: WriteOptions,
1402    },
1403    Flush,
1404    Trim {
1405        device_block_offset: u64,
1406        block_count: u32,
1407    },
1408    /// This will never be seen by the C interface.
1409    CloseVmo,
1410    /// This will never be seen by the C interface.
1411    StartDecompressedRead {
1412        required_buffer_size: usize,
1413        device_block_offset: u64,
1414        block_count: u32,
1415        options: ReadOptions,
1416    },
1417    /// This will never be seen by the C interface.
1418    ContinueDecompressedRead {
1419        offset: u64,
1420        device_block_offset: u64,
1421        block_count: u32,
1422        options: ReadOptions,
1423    },
1424}
1425
1426impl Operation {
1427    fn trace_label(&self) -> &'static str {
1428        match self {
1429            Operation::Read { .. } => "read",
1430            Operation::Write { .. } => "write",
1431            Operation::Flush { .. } => "flush",
1432            Operation::Trim { .. } => "trim",
1433            Operation::CloseVmo { .. } => "close_vmo",
1434            Operation::StartDecompressedRead { .. } => "start_decompressed_read",
1435            Operation::ContinueDecompressedRead { .. } => "continue_decompressed_read",
1436        }
1437    }
1438
1439    /// Returns (offset, length).
1440    pub fn blocks(&self) -> Option<(u64, u32)> {
1441        match self {
1442            Operation::Read { device_block_offset, block_count, .. }
1443            | Operation::Write { device_block_offset, block_count, .. }
1444            | Operation::Trim { device_block_offset, block_count, .. } => {
1445                Some((*device_block_offset, *block_count))
1446            }
1447            _ => None,
1448        }
1449    }
1450
1451    /// Returns mutable references to (offset, length).
1452    fn blocks_mut(&mut self) -> Option<(&mut u64, &mut u32)> {
1453        match self {
1454            Operation::Read { device_block_offset, block_count, .. }
1455            | Operation::Write { device_block_offset, block_count, .. }
1456            | Operation::Trim { device_block_offset, block_count, .. } => {
1457                Some((device_block_offset, block_count))
1458            }
1459            _ => None,
1460        }
1461    }
1462
1463    /// Maps the operation using `offset_map` and returns the remainder if the request was split
1464    /// due to `max_transfer_blocks` or crossing mapping boundaries.
1465    fn map(
1466        &mut self,
1467        offset_map: &OffsetMap,
1468        max_transfer_blocks: Option<NonZero<u32>>,
1469        block_size: u32,
1470    ) -> Result<Option<Self>, zx::Status> {
1471        let mut max = match self {
1472            Operation::Read { .. } | Operation::Write { .. } => max_transfer_blocks.map(u32::from),
1473            _ => None,
1474        };
1475        let (offset, length) = match self.blocks_mut() {
1476            Some(b) => b,
1477            None => return Ok(None),
1478        };
1479        let orig_offset = *offset;
1480        if !offset_map.is_empty() {
1481            let (dev_offset, len) = offset_map.map(*offset).ok_or(zx::Status::OUT_OF_RANGE)?;
1482            *offset = dev_offset;
1483            max = match max {
1484                None => Some(len),
1485                Some(m) => Some(std::cmp::min(m, len)),
1486            };
1487        }
1488        if let Some(max) = max {
1489            if *length as u64 > max as u64 {
1490                let rem = *length - max;
1491                *length = max;
1492                return Ok(Some(match self {
1493                    Operation::Read {
1494                        device_block_offset: _,
1495                        block_count: _,
1496                        vmo_offset,
1497                        _unused,
1498                        options,
1499                    } => {
1500                        let mut options = *options;
1501                        options.inline_crypto.dun += max;
1502                        Operation::Read {
1503                            device_block_offset: orig_offset + max as u64,
1504                            block_count: rem,
1505                            vmo_offset: *vmo_offset + max as u64 * block_size as u64,
1506                            _unused: *_unused,
1507                            options: options,
1508                        }
1509                    }
1510                    Operation::Write {
1511                        device_block_offset: _,
1512                        block_count: _,
1513                        _unused,
1514                        vmo_offset,
1515                        options,
1516                    } => {
1517                        let mut options = *options;
1518                        options.inline_crypto.dun += max;
1519                        Operation::Write {
1520                            device_block_offset: orig_offset + max as u64,
1521                            block_count: rem,
1522                            _unused: *_unused,
1523                            vmo_offset: *vmo_offset + max as u64 * block_size as u64,
1524                            options: options,
1525                        }
1526                    }
1527                    Operation::Trim { device_block_offset: _, block_count: _ } => Operation::Trim {
1528                        device_block_offset: orig_offset + max as u64,
1529                        block_count: rem,
1530                    },
1531                    _ => unreachable!(),
1532                }));
1533            }
1534        }
1535        Ok(None)
1536    }
1537
1538    /// Returns true if the specified write flags are set.
1539    pub fn has_write_flag(&self, value: WriteFlags) -> bool {
1540        if let Operation::Write { options, .. } = self {
1541            options.flags.contains(value)
1542        } else {
1543            false
1544        }
1545    }
1546
1547    /// Removes `value` from the request's write flags and returns true if the flag was set.
1548    pub fn take_write_flag(&mut self, value: WriteFlags) -> bool {
1549        if let Operation::Write { options, .. } = self {
1550            let result = options.flags.contains(value);
1551            options.flags.remove(value);
1552            result
1553        } else {
1554            false
1555        }
1556    }
1557}
1558
1559#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
1560pub enum GroupOrRequest {
1561    Group(u16),
1562    Request(u32),
1563}
1564
1565impl GroupOrRequest {
1566    fn is_group(&self) -> bool {
1567        matches!(self, Self::Group(_))
1568    }
1569
1570    fn group_id(&self) -> Option<u16> {
1571        match self {
1572            Self::Group(id) => Some(*id),
1573            Self::Request(_) => None,
1574        }
1575    }
1576}
1577
1578#[cfg(test)]
1579mod tests {
1580    use super::{
1581        BlockOffsetMapping, BlockServer, DeviceInfo, FIFO_MAX_REQUESTS, OffsetMap, Operation,
1582        PartitionInfo, TraceFlowId,
1583    };
1584    use assert_matches::assert_matches;
1585    use block_protocol::{
1586        BlockFifoCommand, BlockFifoRequest, BlockFifoResponse, InlineCryptoOptions, ReadOptions,
1587        WriteFlags, WriteOptions,
1588    };
1589    use fidl_fuchsia_storage_block as fblock;
1590    use fidl_fuchsia_storage_block::{BlockIoFlag, BlockOpcode};
1591    use fuchsia_async as fasync;
1592    use fuchsia_sync::Mutex;
1593    use futures::FutureExt as _;
1594    use futures::channel::oneshot;
1595    use futures::future::BoxFuture;
1596    use std::borrow::Cow;
1597    use std::future::poll_fn;
1598    use std::num::NonZero;
1599    use std::pin::pin;
1600    use std::sync::Arc;
1601    use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
1602    use std::task::{Context, Poll};
1603
1604    #[derive(Default)]
1605    struct MockInterface {
1606        info: Option<DeviceInfo>,
1607        read_hook: Option<
1608            Box<
1609                dyn Fn(u64, u32, &Arc<zx::Vmo>, u64) -> BoxFuture<'static, Result<(), zx::Status>>
1610                    + Send
1611                    + Sync,
1612            >,
1613        >,
1614        write_hook:
1615            Option<Box<dyn Fn(u64) -> BoxFuture<'static, Result<(), zx::Status>> + Send + Sync>>,
1616        barrier_hook: Option<Box<dyn Fn() -> Result<(), zx::Status> + Send + Sync>>,
1617    }
1618
1619    impl super::async_interface::Interface for MockInterface {
1620        async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
1621            Ok(())
1622        }
1623
1624        fn get_info(&self) -> Cow<'_, DeviceInfo> {
1625            match &self.info {
1626                Some(info) => Cow::Borrowed(info),
1627                None => Cow::Owned(test_device_info()),
1628            }
1629        }
1630
1631        async fn read(
1632            &self,
1633            device_block_offset: u64,
1634            block_count: u32,
1635            vmo: &Arc<zx::Vmo>,
1636            vmo_offset: u64,
1637            _opts: ReadOptions,
1638            _trace_flow_id: TraceFlowId,
1639        ) -> Result<(), zx::Status> {
1640            if let Some(total) = self.get_info().block_count() {
1641                if device_block_offset >= total || total - device_block_offset < block_count as u64
1642                {
1643                    return Err(zx::Status::OUT_OF_RANGE);
1644                }
1645            }
1646            if let Some(read_hook) = &self.read_hook {
1647                read_hook(device_block_offset, block_count, vmo, vmo_offset).await
1648            } else {
1649                unimplemented!();
1650            }
1651        }
1652
1653        async fn write(
1654            &self,
1655            device_block_offset: u64,
1656            _block_count: u32,
1657            _vmo: &Arc<zx::Vmo>,
1658            _vmo_offset: u64,
1659            opts: WriteOptions,
1660            _trace_flow_id: TraceFlowId,
1661        ) -> Result<(), zx::Status> {
1662            if opts.flags.contains(WriteFlags::PRE_BARRIER)
1663                && let Some(barrier_hook) = &self.barrier_hook
1664            {
1665                barrier_hook()?;
1666            }
1667            if let Some(write_hook) = &self.write_hook {
1668                write_hook(device_block_offset).await
1669            } else {
1670                unimplemented!();
1671            }
1672        }
1673
1674        async fn flush(&self, _trace_flow_id: TraceFlowId) -> Result<(), zx::Status> {
1675            Ok(())
1676        }
1677
1678        async fn trim(
1679            &self,
1680            _device_block_offset: u64,
1681            _block_count: u32,
1682            _trace_flow_id: TraceFlowId,
1683        ) -> Result<(), zx::Status> {
1684            unreachable!();
1685        }
1686
1687        async fn get_volume_info(
1688            &self,
1689        ) -> Result<(fblock::VolumeManagerInfo, fblock::VolumeInfo), zx::Status> {
1690            // Hang forever for the test_requests_dont_block_sessions test.
1691            let () = std::future::pending().await;
1692            unreachable!();
1693        }
1694    }
1695
1696    const BLOCK_SIZE: u32 = 512;
1697    const MAX_TRANSFER_BLOCKS: u32 = 10;
1698
1699    fn test_device_info() -> DeviceInfo {
1700        DeviceInfo::Partition(PartitionInfo {
1701            device_flags: fblock::DeviceFlag::READONLY
1702                | fblock::DeviceFlag::BARRIER_SUPPORT
1703                | fblock::DeviceFlag::FUA_SUPPORT,
1704            max_transfer_blocks: NonZero::new(MAX_TRANSFER_BLOCKS),
1705            start_block_offset: Some(0),
1706            block_count: 100,
1707            type_guid: [1; 16],
1708            instance_guid: [2; 16],
1709            name: "foo".to_string(),
1710            flags: Some(0xabcd),
1711        })
1712    }
1713
1714    #[fuchsia::test]
1715    async fn test_barriers_ordering() {
1716        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1717        let vmo = zx::Vmo::create(2 * zx::system_get_page_size() as u64).unwrap();
1718        let barrier_called = Arc::new(AtomicBool::new(false));
1719
1720        futures::join!(
1721            async move {
1722                let barrier_called_clone = barrier_called.clone();
1723                let block_server = BlockServer::new(
1724                    BLOCK_SIZE,
1725                    Arc::new(MockInterface {
1726                        barrier_hook: Some(Box::new(move || {
1727                            barrier_called.store(true, Ordering::Relaxed);
1728                            Ok(())
1729                        })),
1730                        write_hook: Some(Box::new(move |device_block_offset| {
1731                            let barrier_called = barrier_called_clone.clone();
1732                            Box::pin(async move {
1733                                // The sleep allows the server to reorder the fifo requests.
1734                                if device_block_offset % 2 == 0 {
1735                                    fasync::Timer::new(fasync::MonotonicInstant::after(
1736                                        zx::MonotonicDuration::from_millis(200),
1737                                    ))
1738                                    .await;
1739                                }
1740                                assert!(barrier_called.load(Ordering::Relaxed));
1741                                Ok(())
1742                            })
1743                        })),
1744                        ..MockInterface::default()
1745                    }),
1746                );
1747                block_server.handle_requests(stream).await.unwrap();
1748            },
1749            async move {
1750                let (session_proxy, server) = fidl::endpoints::create_proxy();
1751
1752                proxy.open_session(server).unwrap();
1753
1754                let vmo_id = session_proxy
1755                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1756                    .await
1757                    .unwrap()
1758                    .unwrap();
1759                assert_ne!(vmo_id.id, 0);
1760
1761                let mut fifo =
1762                    fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
1763                let (mut reader, mut writer) = fifo.async_io();
1764
1765                writer
1766                    .write_entries(&BlockFifoRequest {
1767                        command: BlockFifoCommand {
1768                            opcode: BlockOpcode::Write.into_primitive(),
1769                            flags: BlockIoFlag::PRE_BARRIER.bits(),
1770                            ..Default::default()
1771                        },
1772                        vmoid: vmo_id.id,
1773                        dev_offset: 0,
1774                        length: 5,
1775                        vmo_offset: 6,
1776                        ..Default::default()
1777                    })
1778                    .await
1779                    .unwrap();
1780
1781                for i in 0..10 {
1782                    writer
1783                        .write_entries(&BlockFifoRequest {
1784                            command: BlockFifoCommand {
1785                                opcode: BlockOpcode::Write.into_primitive(),
1786                                ..Default::default()
1787                            },
1788                            vmoid: vmo_id.id,
1789                            dev_offset: i + 1,
1790                            length: 5,
1791                            vmo_offset: 6,
1792                            ..Default::default()
1793                        })
1794                        .await
1795                        .unwrap();
1796                }
1797                for _ in 0..11 {
1798                    let mut response = BlockFifoResponse::default();
1799                    reader.read_entries(&mut response).await.unwrap();
1800                    assert_eq!(response.status, zx::sys::ZX_OK);
1801                }
1802
1803                std::mem::drop(proxy);
1804            }
1805        );
1806    }
1807
1808    #[fuchsia::test]
1809    async fn test_info() {
1810        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1811
1812        futures::join!(
1813            async {
1814                let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(MockInterface::default()));
1815                block_server.handle_requests(stream).await.unwrap();
1816            },
1817            async {
1818                let expected_info = test_device_info();
1819                let partition_info = if let DeviceInfo::Partition(info) = &expected_info {
1820                    info
1821                } else {
1822                    unreachable!()
1823                };
1824
1825                let block_info = proxy.get_info().await.unwrap().unwrap();
1826                assert_eq!(block_info.block_count, expected_info.block_count().unwrap());
1827                assert_eq!(
1828                    block_info.flags,
1829                    fblock::DeviceFlag::READONLY
1830                        | fblock::DeviceFlag::ZSTD_DECOMPRESSION_SUPPORT
1831                        | fblock::DeviceFlag::BARRIER_SUPPORT
1832                        | fblock::DeviceFlag::FUA_SUPPORT
1833                );
1834
1835                assert_eq!(block_info.max_transfer_size, MAX_TRANSFER_BLOCKS * BLOCK_SIZE);
1836
1837                let (status, type_guid) = proxy.get_type_guid().await.unwrap();
1838                assert_eq!(status, zx::sys::ZX_OK);
1839                assert_eq!(&type_guid.as_ref().unwrap().value, &partition_info.type_guid);
1840
1841                let (status, instance_guid) = proxy.get_instance_guid().await.unwrap();
1842                assert_eq!(status, zx::sys::ZX_OK);
1843                assert_eq!(&instance_guid.as_ref().unwrap().value, &partition_info.instance_guid);
1844
1845                let (status, name) = proxy.get_name().await.unwrap();
1846                assert_eq!(status, zx::sys::ZX_OK);
1847                assert_eq!(name.as_ref(), Some(&partition_info.name));
1848
1849                let metadata = proxy.get_metadata().await.unwrap().expect("get_flags failed");
1850                assert_eq!(metadata.name, name);
1851                assert_eq!(metadata.type_guid.as_ref(), type_guid.as_deref());
1852                assert_eq!(metadata.instance_guid.as_ref(), instance_guid.as_deref());
1853                let expected_start = partition_info.start_block_offset.or(Some(0));
1854                assert_eq!(metadata.start_block_offset, expected_start);
1855                assert_eq!(metadata.num_blocks, Some(partition_info.block_count));
1856                assert_eq!(metadata.flags, partition_info.flags);
1857
1858                std::mem::drop(proxy);
1859            }
1860        );
1861    }
1862
1863    #[fuchsia::test]
1864    async fn test_attach_vmo() {
1865        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1866
1867        let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
1868        let koid = vmo.koid().unwrap();
1869
1870        futures::join!(
1871            async {
1872                let block_server = BlockServer::new(
1873                    BLOCK_SIZE,
1874                    Arc::new(MockInterface {
1875                        read_hook: Some(Box::new(move |_, _, vmo, _| {
1876                            assert_eq!(vmo.koid().unwrap(), koid);
1877                            Box::pin(async { Ok(()) })
1878                        })),
1879                        ..MockInterface::default()
1880                    }),
1881                );
1882                block_server.handle_requests(stream).await.unwrap();
1883            },
1884            async move {
1885                let (session_proxy, server) = fidl::endpoints::create_proxy();
1886
1887                proxy.open_session(server).unwrap();
1888
1889                let vmo_id = session_proxy
1890                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1891                    .await
1892                    .unwrap()
1893                    .unwrap();
1894                assert_ne!(vmo_id.id, 0);
1895
1896                let mut fifo =
1897                    fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
1898                let (mut reader, mut writer) = fifo.async_io();
1899
1900                // Keep attaching VMOs until we eventually hit the maximum.
1901                let mut count = 1;
1902                loop {
1903                    match session_proxy
1904                        .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1905                        .await
1906                        .unwrap()
1907                    {
1908                        Ok(vmo_id) => assert_ne!(vmo_id.id, 0),
1909                        Err(e) => {
1910                            assert_eq!(e, zx::sys::ZX_ERR_NO_RESOURCES);
1911                            break;
1912                        }
1913                    }
1914
1915                    // Only test every 10 to keep test time down.
1916                    if count % 10 == 0 {
1917                        writer
1918                            .write_entries(&BlockFifoRequest {
1919                                command: BlockFifoCommand {
1920                                    opcode: BlockOpcode::Read.into_primitive(),
1921                                    ..Default::default()
1922                                },
1923                                vmoid: vmo_id.id,
1924                                length: 1,
1925                                ..Default::default()
1926                            })
1927                            .await
1928                            .unwrap();
1929
1930                        let mut response = BlockFifoResponse::default();
1931                        reader.read_entries(&mut response).await.unwrap();
1932                        assert_eq!(response.status, zx::sys::ZX_OK);
1933                    }
1934
1935                    count += 1;
1936                }
1937
1938                assert_eq!(count, u16::MAX as u64);
1939
1940                // Detach the original VMO, and make sure we can then attach another one.
1941                writer
1942                    .write_entries(&BlockFifoRequest {
1943                        command: BlockFifoCommand {
1944                            opcode: BlockOpcode::CloseVmo.into_primitive(),
1945                            ..Default::default()
1946                        },
1947                        vmoid: vmo_id.id,
1948                        ..Default::default()
1949                    })
1950                    .await
1951                    .unwrap();
1952
1953                let mut response = BlockFifoResponse::default();
1954                reader.read_entries(&mut response).await.unwrap();
1955                assert_eq!(response.status, zx::sys::ZX_OK);
1956
1957                let new_vmo_id = session_proxy
1958                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
1959                    .await
1960                    .unwrap()
1961                    .unwrap();
1962                // It should reuse the same ID.
1963                assert_eq!(new_vmo_id.id, vmo_id.id);
1964
1965                std::mem::drop(proxy);
1966            }
1967        );
1968    }
1969
1970    #[fuchsia::test]
1971    async fn test_attach_resizable_vmo_fails() {
1972        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1973        let resizable_vmo = zx::Vmo::create_with_opts(zx::VmoOptions::RESIZABLE, 4096).unwrap();
1974
1975        futures::join!(
1976            async {
1977                let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(MockInterface::default()));
1978                block_server.handle_requests(stream).await.unwrap();
1979            },
1980            async move {
1981                let (session_proxy, server) = fidl::endpoints::create_proxy();
1982                proxy.open_session(server).unwrap();
1983                let res = session_proxy.attach_vmo(resizable_vmo).await.unwrap();
1984                assert_eq!(res, Err(zx::sys::ZX_ERR_INVALID_ARGS));
1985                std::mem::drop(proxy);
1986            }
1987        );
1988    }
1989
1990    #[fuchsia::test]
1991    async fn test_close() {
1992        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
1993
1994        let mut server = std::pin::pin!(
1995            async {
1996                let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(MockInterface::default()));
1997                block_server.handle_requests(stream).await.unwrap();
1998            }
1999            .fuse()
2000        );
2001
2002        let mut client = std::pin::pin!(
2003            async {
2004                let (session_proxy, server) = fidl::endpoints::create_proxy();
2005
2006                proxy.open_session(server).unwrap();
2007
2008                // Dropping the proxy should not cause the session to terminate because the session
2009                // is still live.
2010                std::mem::drop(proxy);
2011
2012                session_proxy.close().await.unwrap().unwrap();
2013
2014                // Keep the session alive.  Calling `close` should cause the server to terminate.
2015                let _: () = std::future::pending().await;
2016            }
2017            .fuse()
2018        );
2019
2020        futures::select!(
2021            _ = server => {}
2022            _ = client => unreachable!(),
2023        );
2024    }
2025
2026    #[derive(Default)]
2027    struct IoMockInterface {
2028        do_checks: bool,
2029        expected_op: Arc<Mutex<Option<ExpectedOp>>>,
2030        return_errors: bool,
2031    }
2032
2033    #[derive(Debug)]
2034    enum ExpectedOp {
2035        Read(u64, u32, u64),
2036        Write(u64, u32, u64),
2037        Trim(u64, u32),
2038        Flush,
2039    }
2040
2041    impl super::async_interface::Interface for IoMockInterface {
2042        async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
2043            Ok(())
2044        }
2045
2046        fn get_info(&self) -> Cow<'_, DeviceInfo> {
2047            Cow::Owned(DeviceInfo::Block(crate::BlockInfo {
2048                block_count: 100,
2049                ..Default::default()
2050            }))
2051        }
2052
2053        async fn read(
2054            &self,
2055            device_block_offset: u64,
2056            block_count: u32,
2057            _vmo: &Arc<zx::Vmo>,
2058            vmo_offset: u64,
2059            _opts: ReadOptions,
2060            _trace_flow_id: TraceFlowId,
2061        ) -> Result<(), zx::Status> {
2062            if self.return_errors {
2063                Err(zx::Status::INTERNAL)
2064            } else {
2065                if self.do_checks {
2066                    assert_matches!(
2067                        self.expected_op.lock().take(),
2068                        Some(ExpectedOp::Read(a, b, c)) if device_block_offset == a &&
2069                            block_count == b && vmo_offset / BLOCK_SIZE as u64 == c,
2070                        "Read {device_block_offset} {block_count} {vmo_offset}"
2071                    );
2072                }
2073                Ok(())
2074            }
2075        }
2076
2077        async fn write(
2078            &self,
2079            device_block_offset: u64,
2080            block_count: u32,
2081            _vmo: &Arc<zx::Vmo>,
2082            vmo_offset: u64,
2083            _write_opts: WriteOptions,
2084            _trace_flow_id: TraceFlowId,
2085        ) -> Result<(), zx::Status> {
2086            if self.return_errors {
2087                Err(zx::Status::NOT_SUPPORTED)
2088            } else {
2089                if self.do_checks {
2090                    assert_matches!(
2091                        self.expected_op.lock().take(),
2092                        Some(ExpectedOp::Write(a, b, c)) if device_block_offset == a &&
2093                            block_count == b && vmo_offset / BLOCK_SIZE as u64 == c,
2094                        "Write {device_block_offset} {block_count} {vmo_offset}"
2095                    );
2096                }
2097                Ok(())
2098            }
2099        }
2100
2101        async fn flush(&self, _trace_flow_id: TraceFlowId) -> Result<(), zx::Status> {
2102            if self.return_errors {
2103                Err(zx::Status::NO_RESOURCES)
2104            } else {
2105                if self.do_checks {
2106                    assert_matches!(self.expected_op.lock().take(), Some(ExpectedOp::Flush));
2107                }
2108                Ok(())
2109            }
2110        }
2111
2112        async fn trim(
2113            &self,
2114            device_block_offset: u64,
2115            block_count: u32,
2116            _trace_flow_id: TraceFlowId,
2117        ) -> Result<(), zx::Status> {
2118            if self.return_errors {
2119                Err(zx::Status::NO_MEMORY)
2120            } else {
2121                if self.do_checks {
2122                    assert_matches!(
2123                        self.expected_op.lock().take(),
2124                        Some(ExpectedOp::Trim(a, b)) if device_block_offset == a &&
2125                            block_count == b,
2126                        "Trim {device_block_offset} {block_count}"
2127                    );
2128                }
2129                Ok(())
2130            }
2131        }
2132    }
2133
2134    #[fuchsia::test]
2135    async fn test_io() {
2136        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2137
2138        let expected_op = Arc::new(Mutex::new(None));
2139        let expected_op_clone = expected_op.clone();
2140
2141        let server = async {
2142            let block_server = BlockServer::new(
2143                BLOCK_SIZE,
2144                Arc::new(IoMockInterface {
2145                    return_errors: false,
2146                    do_checks: true,
2147                    expected_op: expected_op_clone,
2148                }),
2149            );
2150            block_server.handle_requests(stream).await.unwrap();
2151        };
2152
2153        let client = async move {
2154            let (session_proxy, server) = fidl::endpoints::create_proxy();
2155
2156            proxy.open_session(server).unwrap();
2157
2158            let vmo = zx::Vmo::create(2 * zx::system_get_page_size() as u64).unwrap();
2159            let vmo_id = session_proxy
2160                .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2161                .await
2162                .unwrap()
2163                .unwrap();
2164
2165            let mut fifo =
2166                fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2167            let (mut reader, mut writer) = fifo.async_io();
2168
2169            // READ
2170            *expected_op.lock() = Some(ExpectedOp::Read(1, 2, 3));
2171            writer
2172                .write_entries(&BlockFifoRequest {
2173                    command: BlockFifoCommand {
2174                        opcode: BlockOpcode::Read.into_primitive(),
2175                        ..Default::default()
2176                    },
2177                    vmoid: vmo_id.id,
2178                    dev_offset: 1,
2179                    length: 2,
2180                    vmo_offset: 3,
2181                    ..Default::default()
2182                })
2183                .await
2184                .unwrap();
2185
2186            let mut response = BlockFifoResponse::default();
2187            reader.read_entries(&mut response).await.unwrap();
2188            assert_eq!(response.status, zx::sys::ZX_OK);
2189
2190            // WRITE
2191            *expected_op.lock() = Some(ExpectedOp::Write(4, 5, 6));
2192            writer
2193                .write_entries(&BlockFifoRequest {
2194                    command: BlockFifoCommand {
2195                        opcode: BlockOpcode::Write.into_primitive(),
2196                        ..Default::default()
2197                    },
2198                    vmoid: vmo_id.id,
2199                    dev_offset: 4,
2200                    length: 5,
2201                    vmo_offset: 6,
2202                    ..Default::default()
2203                })
2204                .await
2205                .unwrap();
2206
2207            let mut response = BlockFifoResponse::default();
2208            reader.read_entries(&mut response).await.unwrap();
2209            assert_eq!(response.status, zx::sys::ZX_OK);
2210
2211            // FLUSH
2212            *expected_op.lock() = Some(ExpectedOp::Flush);
2213            writer
2214                .write_entries(&BlockFifoRequest {
2215                    command: BlockFifoCommand {
2216                        opcode: BlockOpcode::Flush.into_primitive(),
2217                        ..Default::default()
2218                    },
2219                    ..Default::default()
2220                })
2221                .await
2222                .unwrap();
2223
2224            reader.read_entries(&mut response).await.unwrap();
2225            assert_eq!(response.status, zx::sys::ZX_OK);
2226
2227            // TRIM
2228            *expected_op.lock() = Some(ExpectedOp::Trim(7, 8));
2229            writer
2230                .write_entries(&BlockFifoRequest {
2231                    command: BlockFifoCommand {
2232                        opcode: BlockOpcode::Trim.into_primitive(),
2233                        ..Default::default()
2234                    },
2235                    dev_offset: 7,
2236                    length: 8,
2237                    ..Default::default()
2238                })
2239                .await
2240                .unwrap();
2241
2242            reader.read_entries(&mut response).await.unwrap();
2243            assert_eq!(response.status, zx::sys::ZX_OK);
2244
2245            std::mem::drop(proxy);
2246        };
2247
2248        futures::join!(server, client);
2249    }
2250
2251    #[fuchsia::test]
2252    async fn test_io_errors() {
2253        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2254
2255        futures::join!(
2256            async {
2257                let block_server = BlockServer::new(
2258                    BLOCK_SIZE,
2259                    Arc::new(IoMockInterface {
2260                        return_errors: true,
2261                        do_checks: false,
2262                        expected_op: Arc::new(Mutex::new(None)),
2263                    }),
2264                );
2265                block_server.handle_requests(stream).await.unwrap();
2266            },
2267            async move {
2268                let (session_proxy, server) = fidl::endpoints::create_proxy();
2269
2270                proxy.open_session(server).unwrap();
2271
2272                let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2273                let vmo_id = session_proxy
2274                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2275                    .await
2276                    .unwrap()
2277                    .unwrap();
2278
2279                let mut fifo =
2280                    fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2281                let (mut reader, mut writer) = fifo.async_io();
2282
2283                // READ
2284                writer
2285                    .write_entries(&BlockFifoRequest {
2286                        command: BlockFifoCommand {
2287                            opcode: BlockOpcode::Read.into_primitive(),
2288                            ..Default::default()
2289                        },
2290                        vmoid: vmo_id.id,
2291                        length: 1,
2292                        reqid: 1,
2293                        ..Default::default()
2294                    })
2295                    .await
2296                    .unwrap();
2297
2298                let mut response = BlockFifoResponse::default();
2299                reader.read_entries(&mut response).await.unwrap();
2300                assert_eq!(response.status, zx::sys::ZX_ERR_INTERNAL);
2301
2302                // WRITE
2303                writer
2304                    .write_entries(&BlockFifoRequest {
2305                        command: BlockFifoCommand {
2306                            opcode: BlockOpcode::Write.into_primitive(),
2307                            ..Default::default()
2308                        },
2309                        vmoid: vmo_id.id,
2310                        length: 1,
2311                        reqid: 2,
2312                        ..Default::default()
2313                    })
2314                    .await
2315                    .unwrap();
2316
2317                reader.read_entries(&mut response).await.unwrap();
2318                assert_eq!(response.status, zx::sys::ZX_ERR_NOT_SUPPORTED);
2319
2320                // FLUSH
2321                writer
2322                    .write_entries(&BlockFifoRequest {
2323                        command: BlockFifoCommand {
2324                            opcode: BlockOpcode::Flush.into_primitive(),
2325                            ..Default::default()
2326                        },
2327                        reqid: 3,
2328                        ..Default::default()
2329                    })
2330                    .await
2331                    .unwrap();
2332
2333                reader.read_entries(&mut response).await.unwrap();
2334                assert_eq!(response.status, zx::sys::ZX_ERR_NO_RESOURCES);
2335
2336                // TRIM
2337                writer
2338                    .write_entries(&BlockFifoRequest {
2339                        command: BlockFifoCommand {
2340                            opcode: BlockOpcode::Trim.into_primitive(),
2341                            ..Default::default()
2342                        },
2343                        reqid: 4,
2344                        length: 1,
2345                        ..Default::default()
2346                    })
2347                    .await
2348                    .unwrap();
2349
2350                reader.read_entries(&mut response).await.unwrap();
2351                assert_eq!(response.status, zx::sys::ZX_ERR_NO_MEMORY);
2352
2353                std::mem::drop(proxy);
2354            }
2355        );
2356    }
2357
2358    #[fuchsia::test]
2359    async fn test_invalid_args() {
2360        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2361
2362        futures::join!(
2363            async {
2364                let block_server = BlockServer::new(
2365                    BLOCK_SIZE,
2366                    Arc::new(IoMockInterface {
2367                        return_errors: false,
2368                        do_checks: false,
2369                        expected_op: Arc::new(Mutex::new(None)),
2370                    }),
2371                );
2372                block_server.handle_requests(stream).await.unwrap();
2373            },
2374            async move {
2375                let (session_proxy, server) = fidl::endpoints::create_proxy();
2376
2377                proxy.open_session(server).unwrap();
2378
2379                let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2380                let vmo_id = session_proxy
2381                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2382                    .await
2383                    .unwrap()
2384                    .unwrap();
2385
2386                let mut fifo =
2387                    fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2388
2389                async fn test(
2390                    fifo: &mut fasync::Fifo<BlockFifoResponse, BlockFifoRequest>,
2391                    request: BlockFifoRequest,
2392                ) -> Result<(), zx::Status> {
2393                    let (mut reader, mut writer) = fifo.async_io();
2394                    writer.write_entries(&request).await.unwrap();
2395                    let mut response = BlockFifoResponse::default();
2396                    reader.read_entries(&mut response).await.unwrap();
2397                    zx::Status::ok(response.status)
2398                }
2399
2400                // READ
2401
2402                let good_read_request = || BlockFifoRequest {
2403                    command: BlockFifoCommand {
2404                        opcode: BlockOpcode::Read.into_primitive(),
2405                        ..Default::default()
2406                    },
2407                    length: 1,
2408                    vmoid: vmo_id.id,
2409                    ..Default::default()
2410                };
2411
2412                assert_eq!(
2413                    test(
2414                        &mut fifo,
2415                        BlockFifoRequest { vmoid: vmo_id.id + 1, ..good_read_request() }
2416                    )
2417                    .await,
2418                    Err(zx::Status::IO)
2419                );
2420
2421                assert_eq!(
2422                    test(
2423                        &mut fifo,
2424                        BlockFifoRequest {
2425                            vmo_offset: 0xffff_ffff_ffff_ffff,
2426                            ..good_read_request()
2427                        }
2428                    )
2429                    .await,
2430                    Err(zx::Status::OUT_OF_RANGE)
2431                );
2432
2433                assert_eq!(
2434                    test(
2435                        &mut fifo,
2436                        BlockFifoRequest {
2437                            vmo_offset: 0x007f_ffff_ffff_ffff,
2438                            length: 2,
2439                            ..good_read_request()
2440                        }
2441                    )
2442                    .await,
2443                    Err(zx::Status::OUT_OF_RANGE)
2444                );
2445
2446                assert_eq!(
2447                    test(&mut fifo, BlockFifoRequest { length: 0, ..good_read_request() }).await,
2448                    Err(zx::Status::INVALID_ARGS)
2449                );
2450
2451                assert_eq!(
2452                    test(
2453                        &mut fifo,
2454                        BlockFifoRequest { vmo_offset: 8, length: 1, ..good_read_request() }
2455                    )
2456                    .await,
2457                    Err(zx::Status::OUT_OF_RANGE)
2458                );
2459
2460                // WRITE
2461
2462                let good_write_request = || BlockFifoRequest {
2463                    command: BlockFifoCommand {
2464                        opcode: BlockOpcode::Write.into_primitive(),
2465                        ..Default::default()
2466                    },
2467                    length: 1,
2468                    vmoid: vmo_id.id,
2469                    ..Default::default()
2470                };
2471
2472                assert_eq!(
2473                    test(
2474                        &mut fifo,
2475                        BlockFifoRequest { vmoid: vmo_id.id + 1, ..good_write_request() }
2476                    )
2477                    .await,
2478                    Err(zx::Status::IO)
2479                );
2480
2481                assert_eq!(
2482                    test(
2483                        &mut fifo,
2484                        BlockFifoRequest {
2485                            vmo_offset: 0xffff_ffff_ffff_ffff,
2486                            ..good_write_request()
2487                        }
2488                    )
2489                    .await,
2490                    Err(zx::Status::OUT_OF_RANGE)
2491                );
2492
2493                assert_eq!(
2494                    test(
2495                        &mut fifo,
2496                        BlockFifoRequest {
2497                            vmo_offset: 0x007f_ffff_ffff_ffff,
2498                            length: 2,
2499                            ..good_write_request()
2500                        }
2501                    )
2502                    .await,
2503                    Err(zx::Status::OUT_OF_RANGE)
2504                );
2505
2506                assert_eq!(
2507                    test(&mut fifo, BlockFifoRequest { length: 0, ..good_write_request() }).await,
2508                    Err(zx::Status::INVALID_ARGS)
2509                );
2510
2511                assert_eq!(
2512                    test(
2513                        &mut fifo,
2514                        BlockFifoRequest { vmo_offset: 8, length: 1, ..good_write_request() }
2515                    )
2516                    .await,
2517                    Err(zx::Status::OUT_OF_RANGE)
2518                );
2519
2520                // CLOSE VMO
2521
2522                assert_eq!(
2523                    test(
2524                        &mut fifo,
2525                        BlockFifoRequest {
2526                            command: BlockFifoCommand {
2527                                opcode: BlockOpcode::CloseVmo.into_primitive(),
2528                                ..Default::default()
2529                            },
2530                            vmoid: vmo_id.id + 1,
2531                            ..Default::default()
2532                        }
2533                    )
2534                    .await,
2535                    Err(zx::Status::IO)
2536                );
2537
2538                std::mem::drop(proxy);
2539            }
2540        );
2541    }
2542
2543    #[fuchsia::test]
2544    async fn test_concurrent_requests() {
2545        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2546
2547        let waiting_readers = Arc::new(Mutex::new(Vec::new()));
2548        let waiting_readers_clone = waiting_readers.clone();
2549
2550        futures::join!(
2551            async move {
2552                let block_server = BlockServer::new(
2553                    BLOCK_SIZE,
2554                    Arc::new(MockInterface {
2555                        read_hook: Some(Box::new(move |dev_block_offset, _, _, _| {
2556                            let (tx, rx) = oneshot::channel();
2557                            waiting_readers_clone.lock().push((dev_block_offset as u32, tx));
2558                            Box::pin(async move {
2559                                let _ = rx.await;
2560                                Ok(())
2561                            })
2562                        })),
2563                        ..MockInterface::default()
2564                    }),
2565                );
2566                block_server.handle_requests(stream).await.unwrap();
2567            },
2568            async move {
2569                let (session_proxy, server) = fidl::endpoints::create_proxy();
2570
2571                proxy.open_session(server).unwrap();
2572
2573                let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2574                let vmo_id = session_proxy
2575                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2576                    .await
2577                    .unwrap()
2578                    .unwrap();
2579
2580                let mut fifo =
2581                    fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2582                let (mut reader, mut writer) = fifo.async_io();
2583
2584                writer
2585                    .write_entries(&BlockFifoRequest {
2586                        command: BlockFifoCommand {
2587                            opcode: BlockOpcode::Read.into_primitive(),
2588                            ..Default::default()
2589                        },
2590                        reqid: 1,
2591                        dev_offset: 1, // Intentionally use the same as `reqid`.
2592                        vmoid: vmo_id.id,
2593                        length: 1,
2594                        ..Default::default()
2595                    })
2596                    .await
2597                    .unwrap();
2598
2599                writer
2600                    .write_entries(&BlockFifoRequest {
2601                        command: BlockFifoCommand {
2602                            opcode: BlockOpcode::Read.into_primitive(),
2603                            ..Default::default()
2604                        },
2605                        reqid: 2,
2606                        dev_offset: 2,
2607                        vmoid: vmo_id.id,
2608                        length: 1,
2609                        ..Default::default()
2610                    })
2611                    .await
2612                    .unwrap();
2613
2614                // Wait till both those entries are pending.
2615                poll_fn(|cx: &mut Context<'_>| {
2616                    if waiting_readers.lock().len() == 2 {
2617                        Poll::Ready(())
2618                    } else {
2619                        // Yield to the executor.
2620                        cx.waker().wake_by_ref();
2621                        Poll::Pending
2622                    }
2623                })
2624                .await;
2625
2626                let mut response = BlockFifoResponse::default();
2627                assert!(futures::poll!(pin!(reader.read_entries(&mut response))).is_pending());
2628
2629                let (id, tx) = waiting_readers.lock().pop().unwrap();
2630                tx.send(()).unwrap();
2631
2632                reader.read_entries(&mut response).await.unwrap();
2633                assert_eq!(response.status, zx::sys::ZX_OK);
2634                assert_eq!(response.reqid, id);
2635
2636                assert!(futures::poll!(pin!(reader.read_entries(&mut response))).is_pending());
2637
2638                let (id, tx) = waiting_readers.lock().pop().unwrap();
2639                tx.send(()).unwrap();
2640
2641                reader.read_entries(&mut response).await.unwrap();
2642                assert_eq!(response.status, zx::sys::ZX_OK);
2643                assert_eq!(response.reqid, id);
2644            }
2645        );
2646    }
2647
2648    #[fuchsia::test]
2649    async fn test_session_close_is_synchronous() {
2650        use futures::{FutureExt as _, StreamExt as _};
2651
2652        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2653
2654        let (start_tx, mut start_rx) = futures::channel::mpsc::channel(1);
2655        let (finish_tx, finish_rx) = futures::channel::oneshot::channel();
2656        let finish_rx = Arc::new(Mutex::new(Some(finish_rx)));
2657
2658        futures::join!(
2659            async move {
2660                let block_server = BlockServer::new(
2661                    BLOCK_SIZE,
2662                    Arc::new(MockInterface {
2663                        read_hook: Some(Box::new(move |_, _, _, _| {
2664                            let mut start_tx = start_tx.clone();
2665                            let finish_rx = finish_rx.lock().take().unwrap();
2666                            Box::pin(async move {
2667                                start_tx.try_send(()).unwrap();
2668                                let _ = finish_rx.await;
2669                                Ok(())
2670                            })
2671                        })),
2672                        ..MockInterface::default()
2673                    }),
2674                );
2675                block_server.handle_requests(stream).await.unwrap();
2676            },
2677            async move {
2678                let (session_proxy, server) = fidl::endpoints::create_proxy();
2679                proxy.open_session(server).unwrap();
2680
2681                let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2682                let vmo_id = session_proxy
2683                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2684                    .await
2685                    .unwrap()
2686                    .unwrap();
2687
2688                let mut fifo = fasync::Fifo::<BlockFifoResponse, BlockFifoRequest>::from_fifo(
2689                    session_proxy.get_fifo().await.unwrap().unwrap(),
2690                );
2691                let (_reader, mut writer) = fifo.async_io();
2692
2693                writer
2694                    .write_entries(&BlockFifoRequest {
2695                        command: BlockFifoCommand {
2696                            opcode: BlockOpcode::Read.into_primitive(),
2697                            ..Default::default()
2698                        },
2699                        reqid: 1,
2700                        vmoid: vmo_id.id,
2701                        length: 1,
2702                        ..Default::default()
2703                    })
2704                    .await
2705                    .unwrap();
2706
2707                // Wait for the read to actually start.
2708                start_rx.next().await.unwrap();
2709
2710                // The close request shouldn't complete yet because the read is still hanging.
2711                let mut close_fut = std::pin::pin!(session_proxy.close().fuse());
2712                let mut timer_fut = std::pin::pin!(
2713                    fasync::Timer::new(std::time::Duration::from_millis(100)).fuse()
2714                );
2715                futures::select! {
2716                    res = close_fut => panic!("close completed too early: {:?}", res),
2717                    _ = timer_fut => {}
2718                }
2719
2720                // Finish the pending request.
2721                finish_tx.send(()).unwrap();
2722
2723                // Verify that close() now completes.
2724                close_fut.await.unwrap().unwrap();
2725
2726                std::mem::drop(proxy);
2727            }
2728        );
2729    }
2730
2731    #[fuchsia::test]
2732    async fn test_groups() {
2733        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2734
2735        futures::join!(
2736            async move {
2737                let block_server = BlockServer::new(
2738                    BLOCK_SIZE,
2739                    Arc::new(MockInterface {
2740                        read_hook: Some(Box::new(move |_, _, _, _| Box::pin(async { Ok(()) }))),
2741                        ..MockInterface::default()
2742                    }),
2743                );
2744                block_server.handle_requests(stream).await.unwrap();
2745            },
2746            async move {
2747                let (session_proxy, server) = fidl::endpoints::create_proxy();
2748
2749                proxy.open_session(server).unwrap();
2750
2751                let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2752                let vmo_id = session_proxy
2753                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2754                    .await
2755                    .unwrap()
2756                    .unwrap();
2757
2758                let mut fifo =
2759                    fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2760                let (mut reader, mut writer) = fifo.async_io();
2761
2762                writer
2763                    .write_entries(&BlockFifoRequest {
2764                        command: BlockFifoCommand {
2765                            opcode: BlockOpcode::Read.into_primitive(),
2766                            flags: BlockIoFlag::GROUP_ITEM.bits(),
2767                            ..Default::default()
2768                        },
2769                        group: 1,
2770                        vmoid: vmo_id.id,
2771                        length: 1,
2772                        ..Default::default()
2773                    })
2774                    .await
2775                    .unwrap();
2776
2777                writer
2778                    .write_entries(&BlockFifoRequest {
2779                        command: BlockFifoCommand {
2780                            opcode: BlockOpcode::Read.into_primitive(),
2781                            flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
2782                            ..Default::default()
2783                        },
2784                        reqid: 2,
2785                        group: 1,
2786                        vmoid: vmo_id.id,
2787                        length: 1,
2788                        ..Default::default()
2789                    })
2790                    .await
2791                    .unwrap();
2792
2793                let mut response = BlockFifoResponse::default();
2794                reader.read_entries(&mut response).await.unwrap();
2795                assert_eq!(response.status, zx::sys::ZX_OK);
2796                assert_eq!(response.reqid, 2);
2797                assert_eq!(response.group, 1);
2798            }
2799        );
2800    }
2801
2802    #[fuchsia::test]
2803    async fn test_group_error() {
2804        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2805
2806        let counter = Arc::new(AtomicU64::new(0));
2807        let counter_clone = counter.clone();
2808
2809        futures::join!(
2810            async move {
2811                let block_server = BlockServer::new(
2812                    BLOCK_SIZE,
2813                    Arc::new(MockInterface {
2814                        read_hook: Some(Box::new(move |_, _, _, _| {
2815                            counter_clone.fetch_add(1, Ordering::Relaxed);
2816                            Box::pin(async { Err(zx::Status::BAD_STATE) })
2817                        })),
2818                        ..MockInterface::default()
2819                    }),
2820                );
2821                block_server.handle_requests(stream).await.unwrap();
2822            },
2823            async move {
2824                let (session_proxy, server) = fidl::endpoints::create_proxy();
2825
2826                proxy.open_session(server).unwrap();
2827
2828                let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2829                let vmo_id = session_proxy
2830                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2831                    .await
2832                    .unwrap()
2833                    .unwrap();
2834
2835                let mut fifo =
2836                    fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2837                let (mut reader, mut writer) = fifo.async_io();
2838
2839                writer
2840                    .write_entries(&BlockFifoRequest {
2841                        command: BlockFifoCommand {
2842                            opcode: BlockOpcode::Read.into_primitive(),
2843                            flags: BlockIoFlag::GROUP_ITEM.bits(),
2844                            ..Default::default()
2845                        },
2846                        group: 1,
2847                        vmoid: vmo_id.id,
2848                        length: 1,
2849                        ..Default::default()
2850                    })
2851                    .await
2852                    .unwrap();
2853
2854                // Wait until processed.
2855                poll_fn(|cx: &mut Context<'_>| {
2856                    if counter.load(Ordering::Relaxed) == 1 {
2857                        Poll::Ready(())
2858                    } else {
2859                        // Yield to the executor.
2860                        cx.waker().wake_by_ref();
2861                        Poll::Pending
2862                    }
2863                })
2864                .await;
2865
2866                let mut response = BlockFifoResponse::default();
2867                assert!(futures::poll!(pin!(reader.read_entries(&mut response))).is_pending());
2868
2869                writer
2870                    .write_entries(&BlockFifoRequest {
2871                        command: BlockFifoCommand {
2872                            opcode: BlockOpcode::Read.into_primitive(),
2873                            flags: BlockIoFlag::GROUP_ITEM.bits(),
2874                            ..Default::default()
2875                        },
2876                        group: 1,
2877                        vmoid: vmo_id.id,
2878                        length: 1,
2879                        ..Default::default()
2880                    })
2881                    .await
2882                    .unwrap();
2883
2884                writer
2885                    .write_entries(&BlockFifoRequest {
2886                        command: BlockFifoCommand {
2887                            opcode: BlockOpcode::Read.into_primitive(),
2888                            flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
2889                            ..Default::default()
2890                        },
2891                        reqid: 2,
2892                        group: 1,
2893                        vmoid: vmo_id.id,
2894                        length: 1,
2895                        ..Default::default()
2896                    })
2897                    .await
2898                    .unwrap();
2899
2900                reader.read_entries(&mut response).await.unwrap();
2901                assert_eq!(response.status, zx::sys::ZX_ERR_BAD_STATE);
2902                assert_eq!(response.reqid, 2);
2903                assert_eq!(response.group, 1);
2904
2905                assert!(futures::poll!(pin!(reader.read_entries(&mut response))).is_pending());
2906
2907                // Only the first request should have been processed.
2908                assert_eq!(counter.load(Ordering::Relaxed), 1);
2909            }
2910        );
2911    }
2912
2913    #[fuchsia::test]
2914    async fn test_group_with_two_lasts() {
2915        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
2916
2917        let (tx, rx) = oneshot::channel();
2918
2919        futures::join!(
2920            async move {
2921                let rx = Mutex::new(Some(rx));
2922                let block_server = BlockServer::new(
2923                    BLOCK_SIZE,
2924                    Arc::new(MockInterface {
2925                        read_hook: Some(Box::new(move |_, _, _, _| {
2926                            let rx = rx.lock().take().unwrap();
2927                            Box::pin(async {
2928                                let _ = rx.await;
2929                                Ok(())
2930                            })
2931                        })),
2932                        ..MockInterface::default()
2933                    }),
2934                );
2935                block_server.handle_requests(stream).await.unwrap();
2936            },
2937            async move {
2938                let (session_proxy, server) = fidl::endpoints::create_proxy();
2939
2940                proxy.open_session(server).unwrap();
2941
2942                let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
2943                let vmo_id = session_proxy
2944                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
2945                    .await
2946                    .unwrap()
2947                    .unwrap();
2948
2949                let mut fifo =
2950                    fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
2951                let (mut reader, mut writer) = fifo.async_io();
2952
2953                writer
2954                    .write_entries(&BlockFifoRequest {
2955                        command: BlockFifoCommand {
2956                            opcode: BlockOpcode::Read.into_primitive(),
2957                            flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
2958                            ..Default::default()
2959                        },
2960                        reqid: 1,
2961                        group: 1,
2962                        vmoid: vmo_id.id,
2963                        length: 1,
2964                        ..Default::default()
2965                    })
2966                    .await
2967                    .unwrap();
2968
2969                writer
2970                    .write_entries(&BlockFifoRequest {
2971                        command: BlockFifoCommand {
2972                            opcode: BlockOpcode::Read.into_primitive(),
2973                            flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
2974                            ..Default::default()
2975                        },
2976                        reqid: 2,
2977                        group: 1,
2978                        vmoid: vmo_id.id,
2979                        length: 1,
2980                        ..Default::default()
2981                    })
2982                    .await
2983                    .unwrap();
2984
2985                // Send an independent request to flush through the fifo.
2986                writer
2987                    .write_entries(&BlockFifoRequest {
2988                        command: BlockFifoCommand {
2989                            opcode: BlockOpcode::CloseVmo.into_primitive(),
2990                            ..Default::default()
2991                        },
2992                        reqid: 3,
2993                        vmoid: vmo_id.id,
2994                        ..Default::default()
2995                    })
2996                    .await
2997                    .unwrap();
2998
2999                // It should succeed.
3000                let mut response = BlockFifoResponse::default();
3001                reader.read_entries(&mut response).await.unwrap();
3002                assert_eq!(response.status, zx::sys::ZX_OK);
3003                assert_eq!(response.reqid, 3);
3004
3005                // Now release the original request.
3006                tx.send(()).unwrap();
3007
3008                // The response should be for the first message tagged as last, and it should be
3009                // an error because we sent two messages with the LAST marker.
3010                let mut response = BlockFifoResponse::default();
3011                reader.read_entries(&mut response).await.unwrap();
3012                assert_eq!(response.status, zx::sys::ZX_ERR_INVALID_ARGS);
3013                assert_eq!(response.reqid, 1);
3014                assert_eq!(response.group, 1);
3015            }
3016        );
3017    }
3018
3019    #[fuchsia::test(allow_stalls = false)]
3020    async fn test_requests_dont_block_sessions() {
3021        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3022
3023        let (tx, rx) = oneshot::channel();
3024
3025        fasync::Task::local(async move {
3026            let rx = Mutex::new(Some(rx));
3027            let block_server = BlockServer::new(
3028                BLOCK_SIZE,
3029                Arc::new(MockInterface {
3030                    read_hook: Some(Box::new(move |_, _, _, _| {
3031                        let rx = rx.lock().take().unwrap();
3032                        Box::pin(async {
3033                            let _ = rx.await;
3034                            Ok(())
3035                        })
3036                    })),
3037                    ..MockInterface::default()
3038                }),
3039            );
3040            block_server.handle_requests(stream).await.unwrap();
3041        })
3042        .detach();
3043
3044        let mut fut = pin!(async {
3045            let (session_proxy, server) = fidl::endpoints::create_proxy();
3046
3047            proxy.open_session(server).unwrap();
3048
3049            let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
3050            let vmo_id = session_proxy
3051                .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
3052                .await
3053                .unwrap()
3054                .unwrap();
3055
3056            let mut fifo =
3057                fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
3058            let (mut reader, mut writer) = fifo.async_io();
3059
3060            writer
3061                .write_entries(&BlockFifoRequest {
3062                    command: BlockFifoCommand {
3063                        opcode: BlockOpcode::Read.into_primitive(),
3064                        flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
3065                        ..Default::default()
3066                    },
3067                    reqid: 1,
3068                    group: 1,
3069                    vmoid: vmo_id.id,
3070                    length: 1,
3071                    ..Default::default()
3072                })
3073                .await
3074                .unwrap();
3075
3076            let mut response = BlockFifoResponse::default();
3077            reader.read_entries(&mut response).await.unwrap();
3078            assert_eq!(response.status, zx::sys::ZX_OK);
3079        });
3080
3081        // The response won't come back until we send on `tx`.
3082        assert!(fasync::TestExecutor::poll_until_stalled(&mut fut).await.is_pending());
3083
3084        let mut fut2 = pin!(proxy.get_volume_info());
3085
3086        // get_volume_info is set up to stall forever.
3087        assert!(fasync::TestExecutor::poll_until_stalled(&mut fut2).await.is_pending());
3088
3089        // If we now free up the first future, it should resolve; the stalled call to
3090        // get_volume_info should not block the fifo response.
3091        let _ = tx.send(());
3092
3093        assert!(fasync::TestExecutor::poll_until_stalled(&mut fut).await.is_ready());
3094    }
3095
3096    #[fuchsia::test]
3097    async fn test_request_flow_control() {
3098        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3099
3100        // The client will ensure that MAX_REQUESTS are queued up before firing `event`, and the
3101        // server will block until that happens.
3102        const MAX_REQUESTS: u64 = FIFO_MAX_REQUESTS as u64;
3103        let event = Arc::new((event_listener::Event::new(), AtomicBool::new(false)));
3104        let event_clone = event.clone();
3105        futures::join!(
3106            async move {
3107                let block_server = BlockServer::new(
3108                    BLOCK_SIZE,
3109                    Arc::new(MockInterface {
3110                        info: Some(DeviceInfo::Partition(PartitionInfo {
3111                            block_count: 1000,
3112                            ..Default::default()
3113                        })),
3114                        read_hook: Some(Box::new(move |_, _, _, _| {
3115                            let event_clone = event_clone.clone();
3116                            Box::pin(async move {
3117                                if !event_clone.1.load(Ordering::SeqCst) {
3118                                    event_clone.0.listen().await;
3119                                }
3120                                Ok(())
3121                            })
3122                        })),
3123                        ..MockInterface::default()
3124                    }),
3125                );
3126                block_server.handle_requests(stream).await.unwrap();
3127            },
3128            async move {
3129                let (session_proxy, server) = fidl::endpoints::create_proxy();
3130
3131                proxy.open_session(server).unwrap();
3132
3133                let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
3134                let vmo_id = session_proxy
3135                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
3136                    .await
3137                    .unwrap()
3138                    .unwrap();
3139
3140                let mut fifo =
3141                    fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
3142                let (mut reader, mut writer) = fifo.async_io();
3143
3144                for i in 0..MAX_REQUESTS {
3145                    writer
3146                        .write_entries(&BlockFifoRequest {
3147                            command: BlockFifoCommand {
3148                                opcode: BlockOpcode::Read.into_primitive(),
3149                                ..Default::default()
3150                            },
3151                            reqid: (i + 1) as u32,
3152                            dev_offset: i,
3153                            vmoid: vmo_id.id,
3154                            length: 1,
3155                            ..Default::default()
3156                        })
3157                        .await
3158                        .unwrap();
3159                }
3160                assert!(
3161                    futures::poll!(pin!(writer.write_entries(&BlockFifoRequest {
3162                        command: BlockFifoCommand {
3163                            opcode: BlockOpcode::Read.into_primitive(),
3164                            ..Default::default()
3165                        },
3166                        reqid: u32::MAX,
3167                        dev_offset: MAX_REQUESTS,
3168                        vmoid: vmo_id.id,
3169                        length: 1,
3170                        ..Default::default()
3171                    })))
3172                    .is_pending()
3173                );
3174                // OK, let the server start to process.
3175                event.1.store(true, Ordering::SeqCst);
3176                event.0.notify(usize::MAX);
3177                // For each entry we read, make sure we can write a new one in.
3178                let mut finished_reqids = vec![];
3179                for i in MAX_REQUESTS..2 * MAX_REQUESTS {
3180                    let mut response = BlockFifoResponse::default();
3181                    reader.read_entries(&mut response).await.unwrap();
3182                    assert_eq!(response.status, zx::sys::ZX_OK);
3183                    finished_reqids.push(response.reqid);
3184                    writer
3185                        .write_entries(&BlockFifoRequest {
3186                            command: BlockFifoCommand {
3187                                opcode: BlockOpcode::Read.into_primitive(),
3188                                ..Default::default()
3189                            },
3190                            reqid: (i + 1) as u32,
3191                            dev_offset: i,
3192                            vmoid: vmo_id.id,
3193                            length: 1,
3194                            ..Default::default()
3195                        })
3196                        .await
3197                        .unwrap();
3198                }
3199                let mut response = BlockFifoResponse::default();
3200                for _ in 0..MAX_REQUESTS {
3201                    reader.read_entries(&mut response).await.unwrap();
3202                    assert_eq!(response.status, zx::sys::ZX_OK);
3203                    finished_reqids.push(response.reqid);
3204                }
3205                // Verify that we got a response for each request.  Note that we can't assume FIFO
3206                // ordering.
3207                finished_reqids.sort();
3208                assert_eq!(finished_reqids.len(), 2 * MAX_REQUESTS as usize);
3209                let mut i = 1;
3210                for reqid in finished_reqids {
3211                    assert_eq!(reqid, i);
3212                    i += 1;
3213                }
3214            }
3215        );
3216    }
3217
3218    #[fuchsia::test]
3219    async fn test_passthrough_io_with_fixed_map() {
3220        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3221
3222        let expected_op = Arc::new(Mutex::new(None));
3223        let expected_op_clone = expected_op.clone();
3224        futures::join!(
3225            async {
3226                let block_server = BlockServer::new(
3227                    BLOCK_SIZE,
3228                    Arc::new(IoMockInterface {
3229                        return_errors: false,
3230                        do_checks: true,
3231                        expected_op: expected_op_clone,
3232                    }),
3233                );
3234                block_server.handle_requests(stream).await.unwrap();
3235            },
3236            async move {
3237                let (session_proxy, server) = fidl::endpoints::create_proxy();
3238
3239                let mapping = fblock::BlockOffsetMapping { target_block_offset: 10, length: 20 };
3240                proxy.open_session_with_options(server, &[mapping]).unwrap();
3241
3242                let vmo = zx::Vmo::create(2 * zx::system_get_page_size() as u64).unwrap();
3243                let vmo_id = session_proxy
3244                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
3245                    .await
3246                    .unwrap()
3247                    .unwrap();
3248
3249                let mut fifo =
3250                    fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
3251                let (mut reader, mut writer) = fifo.async_io();
3252
3253                // READ
3254                *expected_op.lock() = Some(ExpectedOp::Read(11, 2, 3));
3255                writer
3256                    .write_entries(&BlockFifoRequest {
3257                        command: BlockFifoCommand {
3258                            opcode: BlockOpcode::Read.into_primitive(),
3259                            ..Default::default()
3260                        },
3261                        vmoid: vmo_id.id,
3262                        dev_offset: 1,
3263                        length: 2,
3264                        vmo_offset: 3,
3265                        ..Default::default()
3266                    })
3267                    .await
3268                    .unwrap();
3269
3270                let mut response = BlockFifoResponse::default();
3271                reader.read_entries(&mut response).await.unwrap();
3272                assert_eq!(response.status, zx::sys::ZX_OK);
3273
3274                // WRITE
3275                *expected_op.lock() = Some(ExpectedOp::Write(14, 5, 6));
3276                writer
3277                    .write_entries(&BlockFifoRequest {
3278                        command: BlockFifoCommand {
3279                            opcode: BlockOpcode::Write.into_primitive(),
3280                            ..Default::default()
3281                        },
3282                        vmoid: vmo_id.id,
3283                        dev_offset: 4,
3284                        length: 5,
3285                        vmo_offset: 6,
3286                        ..Default::default()
3287                    })
3288                    .await
3289                    .unwrap();
3290
3291                reader.read_entries(&mut response).await.unwrap();
3292                assert_eq!(response.status, zx::sys::ZX_OK);
3293
3294                // FLUSH
3295                *expected_op.lock() = Some(ExpectedOp::Flush);
3296                writer
3297                    .write_entries(&BlockFifoRequest {
3298                        command: BlockFifoCommand {
3299                            opcode: BlockOpcode::Flush.into_primitive(),
3300                            ..Default::default()
3301                        },
3302                        ..Default::default()
3303                    })
3304                    .await
3305                    .unwrap();
3306
3307                reader.read_entries(&mut response).await.unwrap();
3308                assert_eq!(response.status, zx::sys::ZX_OK);
3309
3310                // TRIM
3311                *expected_op.lock() = Some(ExpectedOp::Trim(17, 3));
3312                writer
3313                    .write_entries(&BlockFifoRequest {
3314                        command: BlockFifoCommand {
3315                            opcode: BlockOpcode::Trim.into_primitive(),
3316                            ..Default::default()
3317                        },
3318                        dev_offset: 7,
3319                        length: 3,
3320                        ..Default::default()
3321                    })
3322                    .await
3323                    .unwrap();
3324
3325                reader.read_entries(&mut response).await.unwrap();
3326                assert_eq!(response.status, zx::sys::ZX_OK);
3327
3328                // READ past window
3329                *expected_op.lock() = None;
3330                writer
3331                    .write_entries(&BlockFifoRequest {
3332                        command: BlockFifoCommand {
3333                            opcode: BlockOpcode::Read.into_primitive(),
3334                            ..Default::default()
3335                        },
3336                        vmoid: vmo_id.id,
3337                        dev_offset: 19,
3338                        length: 2,
3339                        vmo_offset: 3,
3340                        ..Default::default()
3341                    })
3342                    .await
3343                    .unwrap();
3344
3345                reader.read_entries(&mut response).await.unwrap();
3346                assert_eq!(response.status, zx::sys::ZX_ERR_OUT_OF_RANGE);
3347
3348                std::mem::drop(proxy);
3349            }
3350        );
3351    }
3352
3353    #[fuchsia::test]
3354    fn operation_map() {
3355        const BLOCK_SIZE: u32 = 512;
3356
3357        #[track_caller]
3358        fn expect_map_result(
3359            mut operation: Operation,
3360            mapping: Option<fblock::BlockOffsetMapping>,
3361            max_blocks: Option<NonZero<u32>>,
3362            expected_operations: Vec<Operation>,
3363        ) {
3364            let offset_map = mapping
3365                .map(|m| {
3366                    let map: BlockOffsetMapping = (&m).try_into().unwrap();
3367                    OffsetMap::new(vec![map]).unwrap()
3368                })
3369                .unwrap_or_else(OffsetMap::empty);
3370            let mut ops = vec![];
3371            while let Some(remainder) = operation.map(&offset_map, max_blocks, BLOCK_SIZE).unwrap()
3372            {
3373                ops.push(operation);
3374                operation = remainder;
3375            }
3376            ops.push(operation);
3377            assert_eq!(ops, expected_operations);
3378        }
3379
3380        // No limits
3381        expect_map_result(
3382            Operation::Read {
3383                device_block_offset: 10,
3384                block_count: 200,
3385                _unused: 0,
3386                vmo_offset: 0,
3387                options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3388            },
3389            None,
3390            None,
3391            vec![Operation::Read {
3392                device_block_offset: 10,
3393                block_count: 200,
3394                _unused: 0,
3395                vmo_offset: 0,
3396                options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3397            }],
3398        );
3399
3400        // Max block count
3401        expect_map_result(
3402            Operation::Read {
3403                device_block_offset: 10,
3404                block_count: 200,
3405                _unused: 0,
3406                vmo_offset: 0,
3407                options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3408            },
3409            None,
3410            NonZero::new(120),
3411            vec![
3412                Operation::Read {
3413                    device_block_offset: 10,
3414                    block_count: 120,
3415                    _unused: 0,
3416                    vmo_offset: 0,
3417                    options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3418                },
3419                Operation::Read {
3420                    device_block_offset: 130,
3421                    block_count: 80,
3422                    _unused: 0,
3423                    vmo_offset: 120 * BLOCK_SIZE as u64,
3424                    options: ReadOptions {
3425                        // The DUN should be offset by the number of blocks in the first request.
3426                        inline_crypto: InlineCryptoOptions::enabled(1, 1000 + 120),
3427                    },
3428                },
3429            ],
3430        );
3431        expect_map_result(
3432            Operation::Trim { device_block_offset: 10, block_count: 200 },
3433            None,
3434            NonZero::new(120),
3435            vec![Operation::Trim { device_block_offset: 10, block_count: 200 }],
3436        );
3437
3438        // Remapping + Max block count
3439        expect_map_result(
3440            Operation::Read {
3441                device_block_offset: 0,
3442                block_count: 200,
3443                _unused: 0,
3444                vmo_offset: 0,
3445                options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3446            },
3447            Some(fblock::BlockOffsetMapping { target_block_offset: 100, length: 200 }),
3448            NonZero::new(120),
3449            vec![
3450                Operation::Read {
3451                    device_block_offset: 100,
3452                    block_count: 120,
3453                    _unused: 0,
3454                    vmo_offset: 0,
3455                    options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3456                },
3457                Operation::Read {
3458                    device_block_offset: 220,
3459                    block_count: 80,
3460                    _unused: 0,
3461                    vmo_offset: 120 * BLOCK_SIZE as u64,
3462                    options: ReadOptions {
3463                        inline_crypto: InlineCryptoOptions::enabled(1, 1000 + 120),
3464                    },
3465                },
3466            ],
3467        );
3468        expect_map_result(
3469            Operation::Trim { device_block_offset: 0, block_count: 200 },
3470            Some(fblock::BlockOffsetMapping { target_block_offset: 100, length: 200 }),
3471            NonZero::new(120),
3472            vec![Operation::Trim { device_block_offset: 100, block_count: 200 }],
3473        );
3474
3475        // Multi-extent remapping
3476        let multi_extent_map = OffsetMap::new(vec![
3477            BlockOffsetMapping { target_block_offset: 100, length: 10 },
3478            BlockOffsetMapping { target_block_offset: 200, length: 20 },
3479        ])
3480        .unwrap();
3481
3482        fn expect_multi_map_result(
3483            mut operation: Operation,
3484            offset_map: &OffsetMap,
3485            max_blocks: Option<NonZero<u32>>,
3486            expected_operations: Vec<Operation>,
3487        ) {
3488            let mut ops = vec![];
3489            while let Some(remainder) = operation.map(offset_map, max_blocks, BLOCK_SIZE).unwrap() {
3490                ops.push(operation);
3491                operation = remainder;
3492            }
3493            ops.push(operation);
3494            assert_eq!(ops, expected_operations);
3495        }
3496
3497        // Read spanning multi-extents (logical offset 5, count 15 -> 5 blocks in extent 0, 10 in
3498        // extent 1)
3499        expect_multi_map_result(
3500            Operation::Read {
3501                device_block_offset: 5,
3502                block_count: 15,
3503                _unused: 0,
3504                vmo_offset: 0,
3505                options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3506            },
3507            &multi_extent_map,
3508            None,
3509            vec![
3510                Operation::Read {
3511                    device_block_offset: 105,
3512                    block_count: 5,
3513                    _unused: 0,
3514                    vmo_offset: 0,
3515                    options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3516                },
3517                Operation::Read {
3518                    device_block_offset: 200,
3519                    block_count: 10,
3520                    _unused: 0,
3521                    vmo_offset: 5 * BLOCK_SIZE as u64,
3522                    options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1005) },
3523                },
3524            ],
3525        );
3526
3527        // Read spanning multi-extents with max_transfer_blocks limit
3528        expect_multi_map_result(
3529            Operation::Read {
3530                device_block_offset: 5,
3531                block_count: 15,
3532                _unused: 0,
3533                vmo_offset: 0,
3534                options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3535            },
3536            &multi_extent_map,
3537            NonZero::new(7),
3538            vec![
3539                Operation::Read {
3540                    device_block_offset: 105,
3541                    block_count: 5,
3542                    _unused: 0,
3543                    vmo_offset: 0,
3544                    options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1000) },
3545                },
3546                Operation::Read {
3547                    device_block_offset: 200,
3548                    block_count: 7,
3549                    _unused: 0,
3550                    vmo_offset: 5 * BLOCK_SIZE as u64,
3551                    options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1005) },
3552                },
3553                Operation::Read {
3554                    device_block_offset: 207,
3555                    block_count: 3,
3556                    _unused: 0,
3557                    vmo_offset: 12 * BLOCK_SIZE as u64,
3558                    options: ReadOptions { inline_crypto: InlineCryptoOptions::enabled(1, 1012) },
3559                },
3560            ],
3561        );
3562
3563        // Write spanning multi-extents
3564        expect_multi_map_result(
3565            Operation::Write {
3566                device_block_offset: 5,
3567                block_count: 15,
3568                _unused: 0,
3569                vmo_offset: 0,
3570                options: WriteOptions {
3571                    inline_crypto: InlineCryptoOptions::enabled(1, 2000),
3572                    flags: WriteFlags::empty(),
3573                },
3574            },
3575            &multi_extent_map,
3576            None,
3577            vec![
3578                Operation::Write {
3579                    device_block_offset: 105,
3580                    block_count: 5,
3581                    _unused: 0,
3582                    vmo_offset: 0,
3583                    options: WriteOptions {
3584                        inline_crypto: InlineCryptoOptions::enabled(1, 2000),
3585                        flags: WriteFlags::empty(),
3586                    },
3587                },
3588                Operation::Write {
3589                    device_block_offset: 200,
3590                    block_count: 10,
3591                    _unused: 0,
3592                    vmo_offset: 5 * BLOCK_SIZE as u64,
3593                    options: WriteOptions {
3594                        inline_crypto: InlineCryptoOptions::enabled(1, 2005),
3595                        flags: WriteFlags::empty(),
3596                    },
3597                },
3598            ],
3599        );
3600
3601        // Trim spanning multi-extents
3602        expect_multi_map_result(
3603            Operation::Trim { device_block_offset: 5, block_count: 15 },
3604            &multi_extent_map,
3605            None,
3606            vec![
3607                Operation::Trim { device_block_offset: 105, block_count: 5 },
3608                Operation::Trim { device_block_offset: 200, block_count: 10 },
3609            ],
3610        );
3611
3612        // Large extent test (length > u32::MAX)
3613        let large_extent_map = OffsetMap::new(vec![BlockOffsetMapping {
3614            target_block_offset: 100,
3615            length: (u32::MAX as u64) + 10,
3616        }])
3617        .unwrap();
3618
3619        assert_eq!(large_extent_map.map(0), Some((100, u32::MAX)));
3620    }
3621
3622    // Verifies that if the pre-flush (for a simulated barrier) fails, the write is not executed.
3623    #[fuchsia::test]
3624    async fn test_pre_barrier_flush_failure() {
3625        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3626
3627        struct NoBarrierInterface;
3628        impl super::async_interface::Interface for NoBarrierInterface {
3629            fn get_info(&self) -> Cow<'_, DeviceInfo> {
3630                Cow::Owned(DeviceInfo::Partition(PartitionInfo {
3631                    device_flags: fblock::DeviceFlag::empty(), // No BARRIER_SUPPORT
3632                    max_transfer_blocks: NonZero::new(100),
3633                    start_block_offset: Some(0),
3634                    block_count: 100,
3635                    type_guid: [0; 16],
3636                    instance_guid: [0; 16],
3637                    name: "test".to_string(),
3638                    flags: Some(0),
3639                }))
3640            }
3641            async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
3642                Ok(())
3643            }
3644            async fn read(
3645                &self,
3646                _: u64,
3647                _: u32,
3648                _: &Arc<zx::Vmo>,
3649                _: u64,
3650                _: ReadOptions,
3651                _: TraceFlowId,
3652            ) -> Result<(), zx::Status> {
3653                unreachable!()
3654            }
3655            async fn write(
3656                &self,
3657                _: u64,
3658                _: u32,
3659                _: &Arc<zx::Vmo>,
3660                _: u64,
3661                _: WriteOptions,
3662                _: TraceFlowId,
3663            ) -> Result<(), zx::Status> {
3664                panic!("Write should not be called");
3665            }
3666            async fn flush(&self, _: TraceFlowId) -> Result<(), zx::Status> {
3667                Err(zx::Status::IO)
3668            }
3669            async fn trim(&self, _: u64, _: u32, _: TraceFlowId) -> Result<(), zx::Status> {
3670                unreachable!()
3671            }
3672        }
3673
3674        futures::join!(
3675            async move {
3676                let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(NoBarrierInterface));
3677                block_server.handle_requests(stream).await.unwrap();
3678            },
3679            async move {
3680                let (session_proxy, server) = fidl::endpoints::create_proxy();
3681                proxy.open_session(server).unwrap();
3682                let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
3683                let vmo_id = session_proxy
3684                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
3685                    .await
3686                    .unwrap()
3687                    .unwrap();
3688
3689                let mut fifo =
3690                    fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
3691                let (mut reader, mut writer) = fifo.async_io();
3692
3693                writer
3694                    .write_entries(&BlockFifoRequest {
3695                        command: BlockFifoCommand {
3696                            opcode: BlockOpcode::Write.into_primitive(),
3697                            flags: BlockIoFlag::PRE_BARRIER.bits(),
3698                            ..Default::default()
3699                        },
3700                        vmoid: vmo_id.id,
3701                        length: 1,
3702                        ..Default::default()
3703                    })
3704                    .await
3705                    .unwrap();
3706
3707                let mut response = BlockFifoResponse::default();
3708                reader.read_entries(&mut response).await.unwrap();
3709                assert_eq!(response.status, zx::sys::ZX_ERR_IO);
3710            }
3711        );
3712    }
3713
3714    // Verifies that if the write fails when a post-flush is required (for a simulated FUA), the
3715    // post-flush is not executed.
3716    #[fuchsia::test]
3717    async fn test_post_barrier_write_failure() {
3718        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3719
3720        struct NoBarrierInterface;
3721        impl super::async_interface::Interface for NoBarrierInterface {
3722            fn get_info(&self) -> Cow<'_, DeviceInfo> {
3723                Cow::Owned(DeviceInfo::Partition(PartitionInfo {
3724                    device_flags: fblock::DeviceFlag::empty(), // No FUA_SUPPORT
3725                    max_transfer_blocks: NonZero::new(100),
3726                    start_block_offset: Some(0),
3727                    block_count: 100,
3728                    type_guid: [0; 16],
3729                    instance_guid: [0; 16],
3730                    name: "test".to_string(),
3731                    flags: Some(0),
3732                }))
3733            }
3734            async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
3735                Ok(())
3736            }
3737            async fn read(
3738                &self,
3739                _: u64,
3740                _: u32,
3741                _: &Arc<zx::Vmo>,
3742                _: u64,
3743                _: ReadOptions,
3744                _: TraceFlowId,
3745            ) -> Result<(), zx::Status> {
3746                unreachable!()
3747            }
3748            async fn write(
3749                &self,
3750                _: u64,
3751                _: u32,
3752                _: &Arc<zx::Vmo>,
3753                _: u64,
3754                _: WriteOptions,
3755                _: TraceFlowId,
3756            ) -> Result<(), zx::Status> {
3757                Err(zx::Status::IO)
3758            }
3759            async fn flush(&self, _: TraceFlowId) -> Result<(), zx::Status> {
3760                panic!("Flush should not be called")
3761            }
3762            async fn trim(&self, _: u64, _: u32, _: TraceFlowId) -> Result<(), zx::Status> {
3763                unreachable!()
3764            }
3765        }
3766
3767        futures::join!(
3768            async move {
3769                let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(NoBarrierInterface));
3770                block_server.handle_requests(stream).await.unwrap();
3771            },
3772            async move {
3773                let (session_proxy, server) = fidl::endpoints::create_proxy();
3774                proxy.open_session(server).unwrap();
3775                let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
3776                let vmo_id = session_proxy
3777                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
3778                    .await
3779                    .unwrap()
3780                    .unwrap();
3781
3782                let mut fifo =
3783                    fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
3784                let (mut reader, mut writer) = fifo.async_io();
3785
3786                writer
3787                    .write_entries(&BlockFifoRequest {
3788                        command: BlockFifoCommand {
3789                            opcode: BlockOpcode::Write.into_primitive(),
3790                            flags: BlockIoFlag::FORCE_ACCESS.bits(),
3791                            ..Default::default()
3792                        },
3793                        vmoid: vmo_id.id,
3794                        length: 1,
3795                        ..Default::default()
3796                    })
3797                    .await
3798                    .unwrap();
3799
3800                let mut response = BlockFifoResponse::default();
3801                reader.read_entries(&mut response).await.unwrap();
3802                assert_eq!(response.status, zx::sys::ZX_ERR_IO);
3803            }
3804        );
3805    }
3806
3807    /// Verifies that group IDs are isolated per session.
3808    ///
3809    /// Even if two independent sessions on the same BlockServer use the same group ID,
3810    /// their in-flight transaction groups must remain isolated.
3811    #[fuchsia::test]
3812    async fn test_group_ids_isolated_per_session() {
3813        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3814
3815        futures::join!(
3816            async {
3817                let block_server = BlockServer::new(
3818                    BLOCK_SIZE,
3819                    // MockInterface::flush() is a no-op that returns Ok(()).
3820                    Arc::new(MockInterface::default()),
3821                );
3822                block_server.handle_requests(stream).await.unwrap();
3823            },
3824            async move {
3825                async fn settle() {
3826                    // Let the single-threaded executor drain server work.
3827                    for _ in 0..32 {
3828                        fasync::yield_now().await;
3829                    }
3830                }
3831
3832                // --- Open session A. ---
3833                let (session_a, server_a) = fidl::endpoints::create_proxy();
3834                proxy.open_session(server_a).unwrap();
3835                let mut fifo_a =
3836                    fasync::Fifo::from_fifo(session_a.get_fifo().await.unwrap().unwrap());
3837
3838                // --- Open session B. ---
3839                let (session_b, server_b) = fidl::endpoints::create_proxy();
3840                proxy.open_session(server_b).unwrap();
3841                let mut fifo_b =
3842                    fasync::Fifo::from_fifo(session_b.get_fifo().await.unwrap().unwrap());
3843
3844                // ----------------------------------------------------------------
3845                // Control: with no interference, A's two-part Flush group is OK.
3846                // ----------------------------------------------------------------
3847                {
3848                    let (mut reader_a, mut writer_a) = fifo_a.async_io();
3849                    writer_a
3850                        .write_entries(&BlockFifoRequest {
3851                            command: BlockFifoCommand {
3852                                opcode: BlockOpcode::Flush.into_primitive(),
3853                                flags: BlockIoFlag::GROUP_ITEM.bits(),
3854                                ..Default::default()
3855                            },
3856                            group: 1,
3857                            reqid: 0xAAAA,
3858                            ..Default::default()
3859                        })
3860                        .await
3861                        .unwrap();
3862                    settle().await;
3863                    writer_a
3864                        .write_entries(&BlockFifoRequest {
3865                            command: BlockFifoCommand {
3866                                opcode: BlockOpcode::Flush.into_primitive(),
3867                                flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
3868                                ..Default::default()
3869                            },
3870                            group: 1,
3871                            reqid: 0xAAAA,
3872                            ..Default::default()
3873                        })
3874                        .await
3875                        .unwrap();
3876                    let mut response = BlockFifoResponse::default();
3877                    reader_a.read_entries(&mut response).await.unwrap();
3878                    assert_eq!(response.reqid, 0xAAAA);
3879                    assert_eq!(
3880                        zx::Status::from_raw(response.status),
3881                        zx::Status::OK,
3882                        "control: A's valid Flush group must succeed"
3883                    );
3884                }
3885
3886                // ----------------------------------------------------------------
3887                // Run concurrent group requests with the same group ID (7) on
3888                // both sessions, and verify they both succeed independently.
3889                // ----------------------------------------------------------------
3890
3891                // Step 1: Session A starts group 7.
3892                {
3893                    let (_reader_a, mut writer_a) = fifo_a.async_io();
3894                    writer_a
3895                        .write_entries(&BlockFifoRequest {
3896                            command: BlockFifoCommand {
3897                                opcode: BlockOpcode::Flush.into_primitive(),
3898                                flags: BlockIoFlag::GROUP_ITEM.bits(),
3899                                ..Default::default()
3900                            },
3901                            group: 7,
3902                            reqid: 100,
3903                            ..Default::default()
3904                        })
3905                        .await
3906                        .unwrap();
3907                }
3908                settle().await;
3909
3910                // Step 2: Session B starts group 7.
3911                {
3912                    let (_reader_b, mut writer_b) = fifo_b.async_io();
3913                    writer_b
3914                        .write_entries(&BlockFifoRequest {
3915                            command: BlockFifoCommand {
3916                                opcode: BlockOpcode::Flush.into_primitive(),
3917                                flags: BlockIoFlag::GROUP_ITEM.bits(),
3918                                ..Default::default()
3919                            },
3920                            group: 7,
3921                            reqid: 200,
3922                            ..Default::default()
3923                        })
3924                        .await
3925                        .unwrap();
3926                }
3927                settle().await;
3928
3929                // Step 3: Session A finishes group 7.
3930                {
3931                    let (_reader_a, mut writer_a) = fifo_a.async_io();
3932                    writer_a
3933                        .write_entries(&BlockFifoRequest {
3934                            command: BlockFifoCommand {
3935                                opcode: BlockOpcode::Flush.into_primitive(),
3936                                flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
3937                                ..Default::default()
3938                            },
3939                            group: 7,
3940                            reqid: 100,
3941                            ..Default::default()
3942                        })
3943                        .await
3944                        .unwrap();
3945                }
3946                settle().await;
3947
3948                // Step 4: Session B finishes group 7.
3949                {
3950                    let (_reader_b, mut writer_b) = fifo_b.async_io();
3951                    writer_b
3952                        .write_entries(&BlockFifoRequest {
3953                            command: BlockFifoCommand {
3954                                opcode: BlockOpcode::Flush.into_primitive(),
3955                                flags: (BlockIoFlag::GROUP_ITEM | BlockIoFlag::GROUP_LAST).bits(),
3956                                ..Default::default()
3957                            },
3958                            group: 7,
3959                            reqid: 200,
3960                            ..Default::default()
3961                        })
3962                        .await
3963                        .unwrap();
3964                }
3965                settle().await;
3966
3967                // Verify Session A's response.
3968                {
3969                    let (mut reader_a, _writer_a) = fifo_a.async_io();
3970                    let mut response_a = BlockFifoResponse::default();
3971                    reader_a.read_entries(&mut response_a).await.unwrap();
3972                    assert_eq!(response_a.reqid, 100);
3973                    assert_eq!(response_a.group, 7);
3974                    assert_eq!(zx::Status::from_raw(response_a.status), zx::Status::OK);
3975                }
3976
3977                // Verify Session B's response.
3978                {
3979                    let (mut reader_b, _writer_b) = fifo_b.async_io();
3980                    let mut response_b = BlockFifoResponse::default();
3981                    reader_b.read_entries(&mut response_b).await.unwrap();
3982                    assert_eq!(response_b.reqid, 200);
3983                    assert_eq!(response_b.group, 7);
3984                    assert_eq!(zx::Status::from_raw(response_b.status), zx::Status::OK);
3985                }
3986
3987                std::mem::drop(session_a);
3988                std::mem::drop(session_b);
3989                std::mem::drop(proxy);
3990            }
3991        );
3992    }
3993
3994    #[fuchsia::test]
3995    async fn test_unmapped_request_out_of_range() {
3996        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
3997        let _server = fasync::Task::spawn(async move {
3998            let block_server = BlockServer::new(4096, Arc::new(MockInterface::default()));
3999            let _ = block_server.handle_requests(stream).await;
4000        });
4001
4002        let (session_proxy, server) = fidl::endpoints::create_proxy();
4003        proxy.open_session(server).unwrap();
4004
4005        let vmo = zx::Vmo::create(zx::system_get_page_size() as u64).unwrap();
4006        let vmo_id = session_proxy
4007            .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
4008            .await
4009            .unwrap()
4010            .unwrap();
4011
4012        let mut fifo = fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
4013        let (mut reader, mut writer) = fifo.async_io();
4014
4015        // Attempting to read at offset 100 on a 100-block unmapped partition should fail with
4016        // OUT_OF_RANGE.
4017        writer
4018            .write_entries(&BlockFifoRequest {
4019                command: BlockFifoCommand {
4020                    opcode: BlockOpcode::Read.into_primitive(),
4021                    ..Default::default()
4022                },
4023                reqid: 1,
4024                vmoid: vmo_id.id,
4025                length: 1,
4026                dev_offset: 100,
4027                ..Default::default()
4028            })
4029            .await
4030            .unwrap();
4031
4032        let mut response = BlockFifoResponse::default();
4033        reader.read_entries(&mut response).await.unwrap();
4034        assert_eq!(zx::Status::from_raw(response.status), zx::Status::OUT_OF_RANGE);
4035    }
4036
4037    #[test]
4038    fn test_offset_map_coalescing() {
4039        use crate::{BlockOffsetMapping, OffsetMap};
4040
4041        // Mappings 0 and 1 are contiguous in device offset (100..150 + 150..180 = 100..180).
4042        // Mapping 2 is not contiguous with 1 (target 250 instead of 180).
4043        let mappings = vec![
4044            BlockOffsetMapping { target_block_offset: 100, length: 50 },
4045            BlockOffsetMapping { target_block_offset: 150, length: 30 },
4046            BlockOffsetMapping { target_block_offset: 250, length: 20 },
4047        ];
4048        let map = OffsetMap::new(crate::coalesce_mappings(mappings)).unwrap();
4049
4050        assert_eq!(map.mappings().len(), 2);
4051        assert_eq!(map.mappings()[0].target_block_offset, 100);
4052        assert_eq!(map.mappings()[0].length, 80);
4053        assert_eq!(map.mappings()[1].target_block_offset, 250);
4054        assert_eq!(map.mappings()[1].length, 20);
4055
4056        // Logical offset 10 falls in the first extent, but since it coalesces with the second,
4057        // extent_remaining_blocks extends to logical offset 80 (80 - 10 = 70).
4058        assert_eq!(map.map(10), Some((110, 70)));
4059        // Logical offset 55 falls in the second extent, remaining blocks up to logical 80
4060        // (80 - 55 = 25).
4061        assert_eq!(map.map(55), Some((155, 25)));
4062        // Logical offset 85 falls in the third extent, which is not coalesced with the second
4063        // (100 - 85 = 15).
4064        assert_eq!(map.map(85), Some((255, 15)));
4065    }
4066    #[fuchsia::test]
4067    async fn test_open_session_with_options_errors() {
4068        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
4069
4070        futures::join!(
4071            async {
4072                let block_server = BlockServer::new(BLOCK_SIZE, Arc::new(MockInterface::default()));
4073                let _ = block_server.handle_requests(stream).await;
4074            },
4075            async move {
4076                // Test 1: Mapping with zero length -> should close session with
4077                // INVALID_ARGS epitaph.
4078                {
4079                    let (session_proxy, server) = fidl::endpoints::create_proxy();
4080                    proxy
4081                        .open_session_with_options(
4082                            server,
4083                            &[fblock::BlockOffsetMapping { target_block_offset: 0, length: 0 }],
4084                        )
4085                        .unwrap();
4086                    let res = session_proxy.get_fifo().await;
4087                    assert_matches!(
4088                        res,
4089                        Err(fidl::Error::ClientChannelClosed { epitaph, .. })
4090                            if epitaph == zx::Status::INVALID_ARGS
4091                    );
4092                }
4093
4094                // Test 2: Mappings out of range (exceeding block_count) -> should close with
4095                // OUT_OF_RANGE epitaph.
4096                {
4097                    let (session_proxy, server) = fidl::endpoints::create_proxy();
4098                    let mapping = fblock::BlockOffsetMapping {
4099                        target_block_offset: u64::MAX - 10, // Way past block_count
4100                        length: 10,
4101                    };
4102                    proxy.open_session_with_options(server, &[mapping]).unwrap();
4103                    let res = session_proxy.get_fifo().await;
4104                    assert_matches!(
4105                        res,
4106                        Err(fidl::Error::ClientChannelClosed { epitaph, .. })
4107                            if epitaph == zx::Status::OUT_OF_RANGE
4108                    );
4109                }
4110            }
4111        );
4112    }
4113
4114    #[fuchsia::test]
4115    async fn test_split_request_failure_aborts_subsequent_chunks() {
4116        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<fblock::BlockMarker>();
4117
4118        let read_calls = Arc::new(AtomicU64::new(0));
4119        let read_calls_clone = read_calls.clone();
4120
4121        struct MockSplitInterface {
4122            read_calls: Arc<AtomicU64>,
4123        }
4124
4125        impl super::async_interface::Interface for MockSplitInterface {
4126            fn get_info(&self) -> Cow<'_, DeviceInfo> {
4127                Cow::Owned(DeviceInfo::Partition(PartitionInfo {
4128                    device_flags: fblock::DeviceFlag::READONLY,
4129                    max_transfer_blocks: NonZero::new(5),
4130                    start_block_offset: Some(0),
4131                    block_count: 100,
4132                    type_guid: [1; 16],
4133                    instance_guid: [2; 16],
4134                    name: "foo".to_string(),
4135                    flags: Some(0),
4136                }))
4137            }
4138
4139            async fn on_attach_vmo(&self, _vmo: &zx::Vmo) -> Result<(), zx::Status> {
4140                Ok(())
4141            }
4142
4143            async fn read(
4144                &self,
4145                _device_block_offset: u64,
4146                _block_count: u32,
4147                _vmo: &Arc<zx::Vmo>,
4148                _vmo_offset: u64,
4149                _opts: ReadOptions,
4150                _trace_flow_id: TraceFlowId,
4151            ) -> Result<(), zx::Status> {
4152                let call_num = self.read_calls.fetch_add(1, Ordering::Relaxed);
4153                if call_num == 0 { Err(zx::Status::IO) } else { Ok(()) }
4154            }
4155
4156            async fn write(
4157                &self,
4158                _device_block_offset: u64,
4159                _block_count: u32,
4160                _vmo: &Arc<zx::Vmo>,
4161                _vmo_offset: u64,
4162                _opts: WriteOptions,
4163                _trace_flow_id: TraceFlowId,
4164            ) -> Result<(), zx::Status> {
4165                unreachable!()
4166            }
4167
4168            async fn flush(&self, _trace_flow_id: TraceFlowId) -> Result<(), zx::Status> {
4169                Ok(())
4170            }
4171
4172            async fn trim(
4173                &self,
4174                _device_block_offset: u64,
4175                _block_count: u32,
4176                _trace_flow_id: TraceFlowId,
4177            ) -> Result<(), zx::Status> {
4178                unreachable!()
4179            }
4180        }
4181
4182        futures::join!(
4183            async move {
4184                let block_server = BlockServer::new(
4185                    BLOCK_SIZE,
4186                    Arc::new(MockSplitInterface { read_calls: read_calls_clone }),
4187                );
4188                let _ = block_server.handle_requests(stream).await;
4189            },
4190            async move {
4191                let (session_proxy, server) = fidl::endpoints::create_proxy();
4192                proxy.open_session(server).unwrap();
4193
4194                let vmo = zx::Vmo::create(2 * zx::system_get_page_size() as u64).unwrap();
4195                let vmo_id = session_proxy
4196                    .attach_vmo(vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).unwrap())
4197                    .await
4198                    .unwrap()
4199                    .unwrap();
4200
4201                let mut fifo =
4202                    fasync::Fifo::from_fifo(session_proxy.get_fifo().await.unwrap().unwrap());
4203                let (mut reader, mut writer) = fifo.async_io();
4204
4205                // Send a request for 10 blocks. With max_transfer_blocks = 5, this will be split
4206                // into 2 chunks of 5 blocks each.
4207                writer
4208                    .write_entries(&BlockFifoRequest {
4209                        command: BlockFifoCommand {
4210                            opcode: BlockOpcode::Read.into_primitive(),
4211                            ..Default::default()
4212                        },
4213                        vmoid: vmo_id.id,
4214                        length: 10,
4215                        dev_offset: 0,
4216                        reqid: 1,
4217                        ..Default::default()
4218                    })
4219                    .await
4220                    .unwrap();
4221
4222                let mut response = BlockFifoResponse::default();
4223                reader.read_entries(&mut response).await.unwrap();
4224                assert_ne!(response.status, zx::sys::ZX_OK);
4225
4226                // Verify that only the first chunk was submitted to `read`. The second chunk
4227                // should have been aborted when `map_request` saw active_request.status != OK.
4228                assert_eq!(read_calls.load(Ordering::Relaxed), 1);
4229
4230                std::mem::drop(proxy);
4231            }
4232        );
4233    }
4234}