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