Skip to main content

block_client/
lib.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! A Rust client library for the Fuchsia block protocol.
6//!
7//! This crate provides a low-level client for interacting with block devices over the
8//! `fuchsia.hardware.block` FIDL protocol and the FIFO interface for block I/O.
9//!
10//! See the [`BlockClient`] trait.
11
12use fidl_fuchsia_storage_block as block;
13use fidl_fuchsia_storage_block::{MAX_TRANSFER_UNBOUNDED, VMOID_INVALID};
14use fuchsia_async as fasync;
15use fuchsia_sync::Mutex;
16use futures::channel::oneshot;
17use futures::executor::block_on;
18use std::borrow::Borrow;
19use std::collections::HashMap;
20use std::future::Future;
21use std::hash::{Hash, Hasher};
22use std::mem::MaybeUninit;
23use std::num::NonZero;
24use std::ops::{DerefMut, Range};
25use std::pin::Pin;
26use std::sync::atomic::{AtomicU16, Ordering};
27use std::sync::{Arc, LazyLock};
28use std::task::{Context, Poll, Waker};
29use storage_trace as trace;
30use zx::sys::zx_handle_t;
31
32pub use cache::Cache;
33
34pub use block::DeviceFlag as BlockDeviceFlag;
35
36pub use block_protocol::*;
37
38pub mod cache;
39
40const TEMP_VMO_SIZE: usize = 65536;
41
42/// If a trace flow ID isn't specified for requests, one will be generated.
43pub const NO_TRACE_ID: u64 = 0;
44
45pub use fidl_fuchsia_storage_block::{BlockIoFlag, BlockOpcode};
46
47fn fidl_to_status(error: fidl::Error) -> zx::Status {
48    match error {
49        fidl::Error::ClientChannelClosed { status, .. } => status,
50        _ => zx::Status::INTERNAL,
51    }
52}
53
54fn opcode_str(opcode: u8) -> &'static str {
55    match BlockOpcode::from_primitive(opcode) {
56        Some(BlockOpcode::Read) => "read",
57        Some(BlockOpcode::Write) => "write",
58        Some(BlockOpcode::Flush) => "flush",
59        Some(BlockOpcode::Trim) => "trim",
60        Some(BlockOpcode::CloseVmo) => "close_vmo",
61        None => "unknown",
62    }
63}
64
65// Generates a trace ID that will be unique across the system (as long as |request_id| isn't
66// reused within this process).
67fn generate_trace_flow_id(request_id: u32) -> u64 {
68    static SELF_HANDLE: LazyLock<zx_handle_t> =
69        LazyLock::new(|| fuchsia_runtime::process_self().raw_handle());
70    *SELF_HANDLE as u64 + (request_id as u64) << 32
71}
72
73pub enum BufferSlice<'a> {
74    VmoId { vmo_id: &'a VmoId, offset: u64, length: u64 },
75    Memory(&'a [u8]),
76}
77
78impl<'a> BufferSlice<'a> {
79    pub fn new_with_vmo_id(vmo_id: &'a VmoId, offset: u64, length: u64) -> Self {
80        BufferSlice::VmoId { vmo_id, offset, length }
81    }
82}
83
84impl<'a> From<&'a [u8]> for BufferSlice<'a> {
85    fn from(buf: &'a [u8]) -> Self {
86        BufferSlice::Memory(buf)
87    }
88}
89
90pub enum MutableBufferSlice<'a> {
91    VmoId { vmo_id: &'a VmoId, offset: u64, length: u64 },
92    Memory(&'a mut [u8]),
93}
94
95impl<'a> MutableBufferSlice<'a> {
96    pub fn new_with_vmo_id(vmo_id: &'a VmoId, offset: u64, length: u64) -> Self {
97        MutableBufferSlice::VmoId { vmo_id, offset, length }
98    }
99}
100
101impl<'a> From<&'a mut [u8]> for MutableBufferSlice<'a> {
102    fn from(buf: &'a mut [u8]) -> Self {
103        MutableBufferSlice::Memory(buf)
104    }
105}
106
107#[derive(Default)]
108struct RequestState {
109    result: Option<zx::Status>,
110    waker: Option<Waker>,
111}
112
113#[derive(Default)]
114struct FifoState {
115    // The fifo.
116    fifo: Option<fasync::Fifo<BlockFifoResponse, BlockFifoRequest>>,
117
118    // The next request ID to be used.
119    next_request_id: u32,
120
121    // A queue of messages to be sent on the fifo.
122    queue: std::collections::VecDeque<BlockFifoRequest>,
123
124    // Map from request ID to RequestState.
125    map: HashMap<u32, RequestState>,
126
127    // The waker for the FifoPoller.
128    poller_waker: Option<Waker>,
129
130    // If set, attach a barrier to the next write request
131    attach_barrier: bool,
132}
133
134impl FifoState {
135    fn terminate(&mut self) {
136        self.fifo.take();
137        for (_, request_state) in self.map.iter_mut() {
138            request_state.result.get_or_insert(zx::Status::CANCELED);
139            if let Some(waker) = request_state.waker.take() {
140                waker.wake();
141            }
142        }
143        if let Some(waker) = self.poller_waker.take() {
144            waker.wake();
145        }
146    }
147
148    // Returns true if polling should be terminated.
149    fn poll_send_requests(&mut self, context: &mut Context<'_>) -> bool {
150        let fifo = if let Some(fifo) = self.fifo.as_ref() {
151            fifo
152        } else {
153            return true;
154        };
155
156        loop {
157            let slice = self.queue.as_slices().0;
158            if slice.is_empty() {
159                return false;
160            }
161            match fifo.try_write(context, slice) {
162                Poll::Ready(Ok(sent)) => {
163                    self.queue.drain(0..sent);
164                }
165                Poll::Ready(Err(_)) => {
166                    self.terminate();
167                    return true;
168                }
169                Poll::Pending => {
170                    return false;
171                }
172            }
173        }
174    }
175}
176
177type FifoStateRef = Arc<Mutex<FifoState>>;
178
179// A future used for fifo responses.
180struct ResponseFuture {
181    request_id: u32,
182    fifo_state: FifoStateRef,
183}
184
185impl ResponseFuture {
186    fn new(fifo_state: FifoStateRef, request_id: u32) -> Self {
187        ResponseFuture { request_id, fifo_state }
188    }
189}
190
191impl Future for ResponseFuture {
192    type Output = Result<(), zx::Status>;
193
194    fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
195        let mut state = self.fifo_state.lock();
196        let request_state = state.map.get_mut(&self.request_id).unwrap();
197        if let Some(result) = request_state.result {
198            Poll::Ready(result.into())
199        } else {
200            request_state.waker.replace(context.waker().clone());
201            Poll::Pending
202        }
203    }
204}
205
206impl Drop for ResponseFuture {
207    fn drop(&mut self) {
208        let mut state = self.fifo_state.lock();
209        if let Some(request_state) = state.map.remove(&self.request_id) {
210            if request_state.result.is_none() {
211                // Request was still pending!  This is a cancellation which we do not support.  We
212                // do not know the disposition of any VMO that the far end might still be writing
213                // to.  To avoid potential corruption (e.g. a client reuses a buffer that might
214                // still be being used by the driver), terminate the connection to prevent any
215                // future use.
216                state.terminate();
217            }
218        }
219        update_outstanding_requests_counter(state.map.len());
220    }
221}
222
223/// Wraps a vmo-id. Will panic if you forget to detach.
224#[derive(Debug)]
225#[must_use]
226pub struct VmoId(AtomicU16);
227
228impl VmoId {
229    /// VmoIds will normally be vended by attach_vmo, but this might be used in some tests
230    pub fn new(id: u16) -> Self {
231        Self(AtomicU16::new(id))
232    }
233
234    /// Invalidates self and returns a new VmoId with the same underlying ID.
235    pub fn take(&self) -> Self {
236        Self(AtomicU16::new(self.0.swap(VMOID_INVALID, Ordering::Relaxed)))
237    }
238
239    pub fn is_valid(&self) -> bool {
240        self.id() != VMOID_INVALID
241    }
242
243    /// Takes the ID.  The caller assumes responsibility for detaching.
244    #[must_use]
245    pub fn into_id(self) -> u16 {
246        self.0.swap(VMOID_INVALID, Ordering::Relaxed)
247    }
248
249    pub fn id(&self) -> u16 {
250        self.0.load(Ordering::Relaxed)
251    }
252}
253
254impl PartialEq for VmoId {
255    fn eq(&self, other: &Self) -> bool {
256        self.id() == other.id()
257    }
258}
259
260impl Eq for VmoId {}
261
262impl Drop for VmoId {
263    fn drop(&mut self) {
264        assert_eq!(self.0.load(Ordering::Relaxed), VMOID_INVALID, "Did you forget to detach?");
265    }
266}
267
268impl Hash for VmoId {
269    fn hash<H: Hasher>(&self, state: &mut H) {
270        self.id().hash(state);
271    }
272}
273
274/// Represents a client connection to a block device. This is a simplified version of the block.fidl
275/// interface.
276/// Most users will use the RemoteBlockClient instantiation of this trait.
277pub trait BlockClient: Send + Sync {
278    /// Wraps AttachVmo from fuchsia.hardware.block::Block.
279    ///
280    /// # Safety
281    ///
282    /// The caller must ensure that no references are held during I/O as this would be
283    /// undefined behavior.  The caller may hold pointers, which does not lead to undefined
284    /// behavior; Rust does not make the same assumptions as references for pointers.
285    ///
286    /// Whilst a connection failure or client-side cancellation does not immediately lead to
287    /// Rust undefined behavior, the caller must thereafter assume the VMO is poisoned.  No
288    /// assumptions can be made regarding what might be written to the VMO by the far end, or
289    /// anything the far end might have delegated access to.  The only safe thing to do is
290    /// discard the VMO and not attempt to use or re-attach it.
291    ///
292    /// Attaching the VMO once for its lifetime is safe, if strictly more than necessary.
293    unsafe fn attach_vmo(
294        &self,
295        vmo: &zx::Vmo,
296    ) -> impl Future<Output = Result<VmoId, zx::Status>> + Send;
297
298    /// Detaches the given vmo-id from the device.
299    fn detach_vmo(&self, vmo_id: VmoId) -> impl Future<Output = Result<(), zx::Status>> + Send;
300
301    /// Reads from the device at |device_offset| into the given buffer slice.
302    fn read_at(
303        &self,
304        buffer_slice: MutableBufferSlice<'_>,
305        device_offset: u64,
306    ) -> impl Future<Output = Result<(), zx::Status>> + Send {
307        self.read_at_with_opts_traced(buffer_slice, device_offset, ReadOptions::default(), 0)
308    }
309
310    fn read_at_with_opts(
311        &self,
312        buffer_slice: MutableBufferSlice<'_>,
313        device_offset: u64,
314        opts: ReadOptions,
315    ) -> impl Future<Output = Result<(), zx::Status>> + Send {
316        self.read_at_with_opts_traced(buffer_slice, device_offset, opts, 0)
317    }
318
319    fn read_at_with_opts_traced(
320        &self,
321        buffer_slice: MutableBufferSlice<'_>,
322        device_offset: u64,
323        opts: ReadOptions,
324        trace_flow_id: u64,
325    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
326
327    /// Writes the data in |buffer_slice| to the device.
328    fn write_at(
329        &self,
330        buffer_slice: BufferSlice<'_>,
331        device_offset: u64,
332    ) -> impl Future<Output = Result<(), zx::Status>> + Send {
333        self.write_at_with_opts_traced(
334            buffer_slice,
335            device_offset,
336            WriteOptions::default(),
337            NO_TRACE_ID,
338        )
339    }
340
341    fn write_at_with_opts(
342        &self,
343        buffer_slice: BufferSlice<'_>,
344        device_offset: u64,
345        opts: WriteOptions,
346    ) -> impl Future<Output = Result<(), zx::Status>> + Send {
347        self.write_at_with_opts_traced(buffer_slice, device_offset, opts, NO_TRACE_ID)
348    }
349
350    fn write_at_with_opts_traced(
351        &self,
352        buffer_slice: BufferSlice<'_>,
353        device_offset: u64,
354        opts: WriteOptions,
355        trace_flow_id: u64,
356    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
357
358    /// Trims the given range on the block device.
359    fn trim(
360        &self,
361        device_range: Range<u64>,
362    ) -> impl Future<Output = Result<(), zx::Status>> + Send {
363        self.trim_traced(device_range, NO_TRACE_ID)
364    }
365
366    fn trim_traced(
367        &self,
368        device_range: Range<u64>,
369        trace_flow_id: u64,
370    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
371
372    /// Attaches a barrier to the next write sent to the underlying block device. This barrier
373    /// method is an alternative to setting the WriteOption::PRE_BARRIER on `write_at_with_opts`.
374    /// This method makes it easier to guarantee that the barrier is attached to the correct
375    /// write operation when subsequent write operations can get reordered.
376    fn barrier(&self);
377
378    fn flush(&self) -> impl Future<Output = Result<(), zx::Status>> + Send {
379        self.flush_traced(NO_TRACE_ID)
380    }
381
382    /// Sends a flush request to the underlying block device.
383    fn flush_traced(
384        &self,
385        trace_flow_id: u64,
386    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
387
388    /// Closes the fifo.
389    fn close(&self) -> impl Future<Output = Result<(), zx::Status>> + Send;
390
391    /// Returns the block size of the device.
392    fn block_size(&self) -> u32;
393
394    /// Returns the size, in blocks, of the device.
395    fn block_count(&self) -> u64;
396
397    /// Returns the maximum number of blocks which can be transferred in a single request.
398    fn max_transfer_blocks(&self) -> Option<NonZero<u32>>;
399
400    /// Returns the block flags reported by the device.
401    fn block_flags(&self) -> BlockDeviceFlag;
402
403    /// Returns true if the remote fifo is still connected.
404    fn is_connected(&self) -> bool;
405}
406
407struct Common {
408    block_size: u32,
409    block_count: u64,
410    max_transfer_blocks: Option<NonZero<u32>>,
411    block_flags: BlockDeviceFlag,
412    fifo_state: FifoStateRef,
413    temp_vmo: futures::lock::Mutex<zx::Vmo>,
414    temp_vmo_id: VmoId,
415}
416
417impl Common {
418    fn new(
419        fifo: fasync::Fifo<BlockFifoResponse, BlockFifoRequest>,
420        info: &block::BlockInfo,
421        temp_vmo: zx::Vmo,
422        temp_vmo_id: VmoId,
423    ) -> Self {
424        let fifo_state = Arc::new(Mutex::new(FifoState { fifo: Some(fifo), ..Default::default() }));
425        fasync::Task::spawn(FifoPoller { fifo_state: fifo_state.clone() }).detach();
426        Self {
427            block_size: info.block_size,
428            block_count: info.block_count,
429            max_transfer_blocks: if info.max_transfer_size != MAX_TRANSFER_UNBOUNDED {
430                NonZero::new(info.max_transfer_size / info.block_size)
431            } else {
432                None
433            },
434            block_flags: info.flags,
435            fifo_state,
436            temp_vmo: futures::lock::Mutex::new(temp_vmo),
437            temp_vmo_id,
438        }
439    }
440
441    fn to_blocks(&self, bytes: u64) -> Result<u64, zx::Status> {
442        if bytes % self.block_size as u64 != 0 {
443            Err(zx::Status::INVALID_ARGS)
444        } else {
445            Ok(bytes / self.block_size as u64)
446        }
447    }
448
449    // Sends the request and waits for the response.
450    async fn send(&self, mut request: BlockFifoRequest) -> Result<(), zx::Status> {
451        let (request_id, trace_flow_id) = {
452            let mut state = self.fifo_state.lock();
453
454            let mut flags = BlockIoFlag::from_bits_retain(request.command.flags);
455            if BlockOpcode::from_primitive(request.command.opcode) == Some(BlockOpcode::Write)
456                && state.attach_barrier
457            {
458                flags |= BlockIoFlag::PRE_BARRIER;
459                request.command.flags = flags.bits();
460                state.attach_barrier = false;
461            }
462
463            if state.fifo.is_none() {
464                // Fifo has been closed.
465                return Err(zx::Status::CANCELED);
466            }
467            trace::duration!(
468                "storage",
469                "block_client::send::start",
470                "op" => opcode_str(request.command.opcode),
471                "len" => request.length * self.block_size
472            );
473            let request_id = state.next_request_id;
474            state.next_request_id = state.next_request_id.overflowing_add(1).0;
475            assert!(
476                state.map.insert(request_id, RequestState::default()).is_none(),
477                "request id in use!"
478            );
479            update_outstanding_requests_counter(state.map.len());
480            request.reqid = request_id;
481            if request.trace_flow_id == NO_TRACE_ID {
482                request.trace_flow_id = generate_trace_flow_id(request_id);
483            }
484            let trace_flow_id = request.trace_flow_id;
485            trace::flow_begin!("storage", "block_client::send", trace_flow_id.into());
486            state.queue.push_back(request);
487            if let Some(waker) = state.poller_waker.clone() {
488                state.poll_send_requests(&mut Context::from_waker(&waker));
489            }
490            (request_id, trace_flow_id)
491        };
492        ResponseFuture::new(self.fifo_state.clone(), request_id).await?;
493        trace::duration!("storage", "block_client::send::end");
494        trace::flow_end!("storage", "block_client::send", trace_flow_id.into());
495        Ok(())
496    }
497
498    async fn detach_vmo(&self, vmo_id: VmoId) -> Result<(), zx::Status> {
499        self.send(BlockFifoRequest {
500            command: BlockFifoCommand {
501                opcode: BlockOpcode::CloseVmo.into_primitive(),
502                flags: 0,
503                ..Default::default()
504            },
505            vmoid: vmo_id.into_id(),
506            ..Default::default()
507        })
508        .await
509    }
510
511    async fn read_at(
512        &self,
513        buffer_slice: MutableBufferSlice<'_>,
514        device_offset: u64,
515        opts: ReadOptions,
516        trace_flow_id: u64,
517    ) -> Result<(), zx::Status> {
518        let mut flags = BlockIoFlag::empty();
519
520        if opts.inline_crypto.is_enabled {
521            flags |= BlockIoFlag::INLINE_ENCRYPTION_ENABLED;
522        }
523
524        match buffer_slice {
525            MutableBufferSlice::VmoId { vmo_id, offset, length } => {
526                self.send(BlockFifoRequest {
527                    command: BlockFifoCommand {
528                        opcode: BlockOpcode::Read.into_primitive(),
529                        flags: flags.bits(),
530                        ..Default::default()
531                    },
532                    vmoid: vmo_id.id(),
533                    length: self
534                        .to_blocks(length)?
535                        .try_into()
536                        .map_err(|_| zx::Status::INVALID_ARGS)?,
537                    vmo_offset: self.to_blocks(offset)?,
538                    dev_offset: self.to_blocks(device_offset)?,
539                    trace_flow_id,
540                    dun: opts.inline_crypto.dun,
541                    slot: opts.inline_crypto.slot,
542                    ..Default::default()
543                })
544                .await?
545            }
546            MutableBufferSlice::Memory(mut slice) => {
547                let temp_vmo = self.temp_vmo.lock().await;
548                let mut device_block = self.to_blocks(device_offset)?;
549                loop {
550                    let to_do = std::cmp::min(TEMP_VMO_SIZE, slice.len());
551                    let block_count = self.to_blocks(to_do as u64)? as u32;
552                    self.send(BlockFifoRequest {
553                        command: BlockFifoCommand {
554                            opcode: BlockOpcode::Read.into_primitive(),
555                            flags: flags.bits(),
556                            ..Default::default()
557                        },
558                        vmoid: self.temp_vmo_id.id(),
559                        length: block_count,
560                        vmo_offset: 0,
561                        dev_offset: device_block,
562                        trace_flow_id,
563                        dun: opts.inline_crypto.dun,
564                        slot: opts.inline_crypto.slot,
565                        ..Default::default()
566                    })
567                    .await?;
568                    temp_vmo.read(&mut slice[..to_do], 0)?;
569                    if to_do == slice.len() {
570                        break;
571                    }
572                    device_block += block_count as u64;
573                    slice = &mut slice[to_do..];
574                }
575            }
576        }
577        Ok(())
578    }
579
580    async fn write_at(
581        &self,
582        buffer_slice: BufferSlice<'_>,
583        device_offset: u64,
584        opts: WriteOptions,
585        trace_flow_id: u64,
586    ) -> Result<(), zx::Status> {
587        let mut flags = BlockIoFlag::empty();
588
589        if opts.flags.contains(WriteFlags::FORCE_ACCESS) {
590            flags |= BlockIoFlag::FORCE_ACCESS;
591        }
592
593        if opts.flags.contains(WriteFlags::PRE_BARRIER) {
594            flags |= BlockIoFlag::PRE_BARRIER;
595        }
596
597        if opts.inline_crypto.is_enabled {
598            flags |= BlockIoFlag::INLINE_ENCRYPTION_ENABLED;
599        }
600
601        match buffer_slice {
602            BufferSlice::VmoId { vmo_id, offset, length } => {
603                self.send(BlockFifoRequest {
604                    command: BlockFifoCommand {
605                        opcode: BlockOpcode::Write.into_primitive(),
606                        flags: flags.bits(),
607                        ..Default::default()
608                    },
609                    vmoid: vmo_id.id(),
610                    length: self
611                        .to_blocks(length)?
612                        .try_into()
613                        .map_err(|_| zx::Status::INVALID_ARGS)?,
614                    vmo_offset: self.to_blocks(offset)?,
615                    dev_offset: self.to_blocks(device_offset)?,
616                    trace_flow_id,
617                    dun: opts.inline_crypto.dun,
618                    slot: opts.inline_crypto.slot,
619                    ..Default::default()
620                })
621                .await?;
622            }
623            BufferSlice::Memory(mut slice) => {
624                let temp_vmo = self.temp_vmo.lock().await;
625                let mut device_block = self.to_blocks(device_offset)?;
626                loop {
627                    let to_do = std::cmp::min(TEMP_VMO_SIZE, slice.len());
628                    let block_count = self.to_blocks(to_do as u64)? as u32;
629                    temp_vmo.write(&slice[..to_do], 0)?;
630                    self.send(BlockFifoRequest {
631                        command: BlockFifoCommand {
632                            opcode: BlockOpcode::Write.into_primitive(),
633                            flags: flags.bits(),
634                            ..Default::default()
635                        },
636                        vmoid: self.temp_vmo_id.id(),
637                        length: block_count,
638                        vmo_offset: 0,
639                        dev_offset: device_block,
640                        trace_flow_id,
641                        dun: opts.inline_crypto.dun,
642                        slot: opts.inline_crypto.slot,
643                        ..Default::default()
644                    })
645                    .await?;
646                    if to_do == slice.len() {
647                        break;
648                    }
649                    device_block += block_count as u64;
650                    slice = &slice[to_do..];
651                }
652            }
653        }
654        Ok(())
655    }
656
657    async fn trim(&self, device_range: Range<u64>, trace_flow_id: u64) -> Result<(), zx::Status> {
658        let length = self.to_blocks(device_range.end - device_range.start)? as u32;
659        let dev_offset = self.to_blocks(device_range.start)?;
660        self.send(BlockFifoRequest {
661            command: BlockFifoCommand {
662                opcode: BlockOpcode::Trim.into_primitive(),
663                flags: 0,
664                ..Default::default()
665            },
666            vmoid: VMOID_INVALID,
667            length,
668            dev_offset,
669            trace_flow_id,
670            ..Default::default()
671        })
672        .await
673    }
674
675    async fn flush(&self, trace_flow_id: u64) -> Result<(), zx::Status> {
676        self.send(BlockFifoRequest {
677            command: BlockFifoCommand {
678                opcode: BlockOpcode::Flush.into_primitive(),
679                flags: 0,
680                ..Default::default()
681            },
682            vmoid: VMOID_INVALID,
683            trace_flow_id,
684            ..Default::default()
685        })
686        .await
687    }
688
689    fn barrier(&self) {
690        self.fifo_state.lock().attach_barrier = true;
691    }
692
693    fn block_size(&self) -> u32 {
694        self.block_size
695    }
696
697    fn block_count(&self) -> u64 {
698        self.block_count
699    }
700
701    fn max_transfer_blocks(&self) -> Option<NonZero<u32>> {
702        self.max_transfer_blocks.clone()
703    }
704
705    fn block_flags(&self) -> BlockDeviceFlag {
706        self.block_flags
707    }
708
709    fn is_connected(&self) -> bool {
710        self.fifo_state.lock().fifo.is_some()
711    }
712}
713
714impl Drop for Common {
715    fn drop(&mut self) {
716        // It's OK to leak the VMO id because the server will dump all VMOs when the fifo is torn
717        // down.
718        let _ = self.temp_vmo_id.take().into_id();
719        self.fifo_state.lock().terminate();
720    }
721}
722
723// RemoteBlockClient is a BlockClient that communicates with a real block device over FIDL.
724pub struct RemoteBlockClient {
725    session: block::SessionProxy,
726    common: Common,
727}
728
729impl RemoteBlockClient {
730    /// Returns a connection to a remote block device via the given channel.
731    pub async fn new(remote: impl Borrow<block::BlockProxy>) -> Result<Self, zx::Status> {
732        let remote = remote.borrow();
733        let info =
734            remote.get_info().await.map_err(fidl_to_status)?.map_err(zx::Status::from_raw)?;
735        let (session, server) = fidl::endpoints::create_proxy();
736        let () = remote.open_session(server).map_err(fidl_to_status)?;
737        Self::from_session(info, session).await
738    }
739
740    pub async fn from_session(
741        info: block::BlockInfo,
742        session: block::SessionProxy,
743    ) -> Result<Self, zx::Status> {
744        const SCRATCH_VMO_NAME: zx::Name = zx::Name::new_lossy("block-client-scratch-vmo");
745        let fifo =
746            session.get_fifo().await.map_err(fidl_to_status)?.map_err(zx::Status::from_raw)?;
747        let fifo = fasync::Fifo::from_fifo(fifo);
748        let temp_vmo = zx::Vmo::create(TEMP_VMO_SIZE as u64)?;
749        temp_vmo.set_name(&SCRATCH_VMO_NAME)?;
750        let dup = temp_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
751        let vmo_id =
752            session.attach_vmo(dup).await.map_err(fidl_to_status)?.map_err(zx::Status::from_raw)?;
753        let vmo_id = VmoId::new(vmo_id.id);
754        Ok(RemoteBlockClient { session, common: Common::new(fifo, &info, temp_vmo, vmo_id) })
755    }
756}
757
758impl BlockClient for RemoteBlockClient {
759    async unsafe fn attach_vmo(&self, vmo: &zx::Vmo) -> Result<VmoId, zx::Status> {
760        let dup = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
761        let vmo_id = self
762            .session
763            .attach_vmo(dup)
764            .await
765            .map_err(fidl_to_status)?
766            .map_err(zx::Status::from_raw)?;
767        Ok(VmoId::new(vmo_id.id))
768    }
769
770    async fn detach_vmo(&self, vmo_id: VmoId) -> Result<(), zx::Status> {
771        self.common.detach_vmo(vmo_id).await
772    }
773
774    async fn read_at_with_opts_traced(
775        &self,
776        buffer_slice: MutableBufferSlice<'_>,
777        device_offset: u64,
778        opts: ReadOptions,
779        trace_flow_id: u64,
780    ) -> Result<(), zx::Status> {
781        self.common.read_at(buffer_slice, device_offset, opts, trace_flow_id).await
782    }
783
784    async fn write_at_with_opts_traced(
785        &self,
786        buffer_slice: BufferSlice<'_>,
787        device_offset: u64,
788        opts: WriteOptions,
789        trace_flow_id: u64,
790    ) -> Result<(), zx::Status> {
791        self.common.write_at(buffer_slice, device_offset, opts, trace_flow_id).await
792    }
793
794    async fn trim_traced(&self, range: Range<u64>, trace_flow_id: u64) -> Result<(), zx::Status> {
795        self.common.trim(range, trace_flow_id).await
796    }
797
798    async fn flush_traced(&self, trace_flow_id: u64) -> Result<(), zx::Status> {
799        self.common.flush(trace_flow_id).await
800    }
801
802    fn barrier(&self) {
803        self.common.barrier()
804    }
805
806    async fn close(&self) -> Result<(), zx::Status> {
807        let () =
808            self.session.close().await.map_err(fidl_to_status)?.map_err(zx::Status::from_raw)?;
809        Ok(())
810    }
811
812    fn block_size(&self) -> u32 {
813        self.common.block_size()
814    }
815
816    fn block_count(&self) -> u64 {
817        self.common.block_count()
818    }
819
820    fn max_transfer_blocks(&self) -> Option<NonZero<u32>> {
821        self.common.max_transfer_blocks()
822    }
823
824    fn block_flags(&self) -> BlockDeviceFlag {
825        self.common.block_flags()
826    }
827
828    fn is_connected(&self) -> bool {
829        self.common.is_connected()
830    }
831}
832
833pub struct RemoteBlockClientSync {
834    session: block::SessionSynchronousProxy,
835    common: Common,
836}
837
838impl RemoteBlockClientSync {
839    /// Returns a connection to a remote block device via the given channel, but spawns a separate
840    /// thread for polling the fifo which makes it work in cases where no executor is configured for
841    /// the calling thread.
842    pub fn new(
843        client_end: fidl::endpoints::ClientEnd<block::BlockMarker>,
844    ) -> Result<Self, zx::Status> {
845        let remote = block::BlockSynchronousProxy::new(client_end.into_channel());
846        let info = remote
847            .get_info(zx::MonotonicInstant::INFINITE)
848            .map_err(fidl_to_status)?
849            .map_err(zx::Status::from_raw)?;
850        let (client, server) = fidl::endpoints::create_endpoints();
851        let () = remote.open_session(server).map_err(fidl_to_status)?;
852        let session = block::SessionSynchronousProxy::new(client.into_channel());
853        let fifo = session
854            .get_fifo(zx::MonotonicInstant::INFINITE)
855            .map_err(fidl_to_status)?
856            .map_err(zx::Status::from_raw)?;
857        let temp_vmo = zx::Vmo::create(TEMP_VMO_SIZE as u64)?;
858        let dup = temp_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
859        let vmo_id = session
860            .attach_vmo(dup, zx::MonotonicInstant::INFINITE)
861            .map_err(fidl_to_status)?
862            .map_err(zx::Status::from_raw)?;
863        let vmo_id = VmoId::new(vmo_id.id);
864
865        // The fifo needs to be instantiated from the thread that has the executor as that's where
866        // the fifo registers for notifications to be delivered.
867        let (sender, receiver) = oneshot::channel::<Result<Self, zx::Status>>();
868        std::thread::spawn(move || {
869            let mut executor = fasync::LocalExecutor::default();
870            let fifo = fasync::Fifo::from_fifo(fifo);
871            let common = Common::new(fifo, &info, temp_vmo, vmo_id);
872            let fifo_state = common.fifo_state.clone();
873            let _ = sender.send(Ok(RemoteBlockClientSync { session, common }));
874            executor.run_singlethreaded(FifoPoller { fifo_state });
875        });
876        block_on(receiver).map_err(|_| zx::Status::CANCELED)?
877    }
878
879    /// Wraps AttachVmo from fuchsia.hardware.block::Block.
880    ///
881    /// # Safety
882    ///
883    /// See `BlockClient::attach_vmo`.
884    pub unsafe fn attach_vmo(&self, vmo: &zx::Vmo) -> Result<VmoId, zx::Status> {
885        let dup = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
886        let vmo_id = self
887            .session
888            .attach_vmo(dup, zx::MonotonicInstant::INFINITE)
889            .map_err(fidl_to_status)?
890            .map_err(zx::Status::from_raw)?;
891        Ok(VmoId::new(vmo_id.id))
892    }
893
894    pub fn detach_vmo(&self, vmo_id: VmoId) -> Result<(), zx::Status> {
895        block_on(self.common.detach_vmo(vmo_id))
896    }
897
898    pub fn read_at(
899        &self,
900        buffer_slice: MutableBufferSlice<'_>,
901        device_offset: u64,
902    ) -> Result<(), zx::Status> {
903        block_on(self.common.read_at(
904            buffer_slice,
905            device_offset,
906            ReadOptions::default(),
907            NO_TRACE_ID,
908        ))
909    }
910
911    pub fn write_at(
912        &self,
913        buffer_slice: BufferSlice<'_>,
914        device_offset: u64,
915    ) -> Result<(), zx::Status> {
916        block_on(self.common.write_at(
917            buffer_slice,
918            device_offset,
919            WriteOptions::default(),
920            NO_TRACE_ID,
921        ))
922    }
923
924    pub fn flush(&self) -> Result<(), zx::Status> {
925        block_on(self.common.flush(NO_TRACE_ID))
926    }
927
928    pub fn close(&self) -> Result<(), zx::Status> {
929        let () = self
930            .session
931            .close(zx::MonotonicInstant::INFINITE)
932            .map_err(fidl_to_status)?
933            .map_err(zx::Status::from_raw)?;
934        Ok(())
935    }
936
937    pub fn block_size(&self) -> u32 {
938        self.common.block_size()
939    }
940
941    pub fn block_count(&self) -> u64 {
942        self.common.block_count()
943    }
944
945    pub fn is_connected(&self) -> bool {
946        self.common.is_connected()
947    }
948}
949
950impl Drop for RemoteBlockClientSync {
951    fn drop(&mut self) {
952        // Ignore errors here as there is not much we can do about it.
953        let _ = self.close();
954    }
955}
956
957// FifoPoller is a future responsible for sending and receiving from the fifo.
958struct FifoPoller {
959    fifo_state: FifoStateRef,
960}
961
962impl Future for FifoPoller {
963    type Output = ();
964
965    fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
966        let mut state_lock = self.fifo_state.lock();
967        let state = state_lock.deref_mut(); // So that we can split the borrow.
968
969        // Send requests.
970        if state.poll_send_requests(context) {
971            return Poll::Ready(());
972        }
973
974        // Receive responses.
975        let fifo = state.fifo.as_ref().unwrap(); // Safe because poll_send_requests checks.
976        loop {
977            let mut response = MaybeUninit::uninit();
978            match fifo.try_read(context, &mut response) {
979                Poll::Pending => {
980                    state.poller_waker = Some(context.waker().clone());
981                    return Poll::Pending;
982                }
983                Poll::Ready(Ok(_)) => {
984                    let response = unsafe { response.assume_init() };
985                    let request_id = response.reqid;
986                    // If the request isn't in the map, assume that it's a cancelled read.
987                    if let Some(request_state) = state.map.get_mut(&request_id) {
988                        request_state.result.replace(zx::Status::from_raw(response.status));
989                        if let Some(waker) = request_state.waker.take() {
990                            waker.wake();
991                        }
992                    }
993                }
994                Poll::Ready(Err(_)) => {
995                    state.terminate();
996                    return Poll::Ready(());
997                }
998            }
999        }
1000    }
1001}
1002
1003fn update_outstanding_requests_counter(outstanding: usize) {
1004    trace::counter!("storage", "block-requests", 0, "outstanding" => outstanding);
1005}
1006
1007#[cfg(test)]
1008mod tests {
1009    use super::{
1010        BlockClient, BlockFifoRequest, BlockFifoResponse, BufferSlice, MutableBufferSlice,
1011        RemoteBlockClient, RemoteBlockClientSync, WriteOptions,
1012    };
1013    use block_protocol::ReadOptions;
1014    use block_server::{BlockServer, DeviceInfo, PartitionInfo};
1015    use fidl::endpoints::RequestStream as _;
1016    use fidl_fuchsia_storage_block as block;
1017    use fuchsia_async as fasync;
1018    use futures::future::{AbortHandle, Abortable, TryFutureExt as _};
1019    use futures::join;
1020    use futures::stream::StreamExt as _;
1021    use futures::stream::futures_unordered::FuturesUnordered;
1022    use ramdevice_client::RamdiskClient;
1023    use std::borrow::Cow;
1024    use std::num::NonZero;
1025    use std::sync::Arc;
1026    use std::sync::atomic::{AtomicBool, Ordering};
1027
1028    const RAMDISK_BLOCK_SIZE: u64 = 1024;
1029    const RAMDISK_BLOCK_COUNT: u64 = 1024;
1030
1031    pub async fn make_ramdisk() -> (RamdiskClient, block::BlockProxy, RemoteBlockClient) {
1032        let ramdisk = RamdiskClient::create(RAMDISK_BLOCK_SIZE, RAMDISK_BLOCK_COUNT)
1033            .await
1034            .expect("RamdiskClient::create failed");
1035        let client_end = ramdisk.open().expect("ramdisk.open failed");
1036        let proxy = client_end.into_proxy();
1037        let block_client = RemoteBlockClient::new(proxy).await.expect("new failed");
1038        assert_eq!(block_client.block_size(), 1024);
1039        let client_end = ramdisk.open().expect("ramdisk.open failed");
1040        let proxy = client_end.into_proxy();
1041        (ramdisk, proxy, block_client)
1042    }
1043
1044    #[fuchsia::test]
1045    async fn test_against_ram_disk() {
1046        let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1047
1048        let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1049        vmo.write(b"hello", 5).expect("vmo.write failed");
1050        // SAFETY: Test code, only attach once, no other mappings.
1051        let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1052        block_client
1053            .write_at(BufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0)
1054            .await
1055            .expect("write_at failed");
1056        block_client
1057            .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 1024, 2048), 0)
1058            .await
1059            .expect("read_at failed");
1060        let mut buf: [u8; 5] = Default::default();
1061        vmo.read(&mut buf, 1029).expect("vmo.read failed");
1062        assert_eq!(&buf, b"hello");
1063        block_client.detach_vmo(vmo_id).await.expect("detach_vmo failed");
1064    }
1065
1066    #[fuchsia::test]
1067    async fn test_alignment() {
1068        let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1069        let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1070        // SAFETY: Test code, only attach once, no other mappings.
1071        let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1072        block_client
1073            .write_at(BufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 1)
1074            .await
1075            .expect_err("expected failure due to bad alignment");
1076        block_client.detach_vmo(vmo_id).await.expect("detach_vmo failed");
1077    }
1078
1079    #[fuchsia::test]
1080    async fn test_parallel_io() {
1081        let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1082        let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1083        // SAFETY: Test code, only attach once, no other mappings.
1084        let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1085        let mut reads = Vec::new();
1086        for _ in 0..1024 {
1087            reads.push(
1088                block_client
1089                    .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0)
1090                    .inspect_err(|e| panic!("read should have succeeded: {}", e)),
1091            );
1092        }
1093        futures::future::join_all(reads).await;
1094        block_client.detach_vmo(vmo_id).await.expect("detach_vmo failed");
1095    }
1096
1097    #[fuchsia::test]
1098    async fn test_closed_device() {
1099        let (ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1100        let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1101        // SAFETY: Test code, only attach once, no other mappings.
1102        let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1103        let mut reads = Vec::new();
1104        for _ in 0..1024 {
1105            reads.push(
1106                block_client.read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0),
1107            );
1108        }
1109        assert!(block_client.is_connected());
1110        let _ = futures::join!(futures::future::join_all(reads), async {
1111            std::mem::drop(ramdisk);
1112        });
1113        // Destroying the ramdisk is asynchronous. Keep issuing reads until they start failing.
1114        while block_client
1115            .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0)
1116            .await
1117            .is_ok()
1118        {}
1119
1120        // Sometimes the FIFO will start rejecting requests before FIFO is actually closed, so we
1121        // get false-positives from is_connected.
1122        while block_client.is_connected() {
1123            // Sleep for a bit to minimise lock contention.
1124            fasync::Timer::new(fasync::MonotonicInstant::after(
1125                zx::MonotonicDuration::from_millis(500),
1126            ))
1127            .await;
1128        }
1129
1130        // But once is_connected goes negative, it should stay negative.
1131        assert_eq!(block_client.is_connected(), false);
1132        let _ = block_client.detach_vmo(vmo_id).await;
1133    }
1134
1135    #[fuchsia::test]
1136    async fn test_cancelled_reads() {
1137        let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1138        let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1139        // SAFETY: Test code, only attach once, no other mappings.
1140        let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1141        {
1142            let mut reads = FuturesUnordered::new();
1143            for _ in 0..1024 {
1144                reads.push(
1145                    block_client.read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0),
1146                );
1147            }
1148            // Read the first 500 results and then dump the rest.
1149            for _ in 0..500 {
1150                reads.next().await;
1151            }
1152        }
1153
1154        // Since we dropped pending futures (cancellation), the client must forcibly
1155        // terminate the connection to prevent any future unsafe I/O. Thus, further
1156        // calls like `detach_vmo` must fail with CANCELED.
1157        assert_eq!(
1158            block_client.read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0).await,
1159            Err(zx::Status::CANCELED)
1160        );
1161        assert_eq!(block_client.detach_vmo(vmo_id).await, Err(zx::Status::CANCELED));
1162    }
1163
1164    #[fuchsia::test]
1165    async fn test_parallel_large_read_and_write_with_memory_succeds() {
1166        let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1167        let block_client_ref = &block_client;
1168        let test_one = |offset, len, fill| async move {
1169            let buf = vec![fill; len];
1170            block_client_ref.write_at(buf[..].into(), offset).await.expect("write_at failed");
1171            // Read back an extra block either side.
1172            let mut read_buf = vec![0u8; len + 2 * RAMDISK_BLOCK_SIZE as usize];
1173            block_client_ref
1174                .read_at(read_buf.as_mut_slice().into(), offset - RAMDISK_BLOCK_SIZE)
1175                .await
1176                .expect("read_at failed");
1177            assert_eq!(
1178                &read_buf[0..RAMDISK_BLOCK_SIZE as usize],
1179                &[0; RAMDISK_BLOCK_SIZE as usize][..]
1180            );
1181            assert_eq!(
1182                &read_buf[RAMDISK_BLOCK_SIZE as usize..RAMDISK_BLOCK_SIZE as usize + len],
1183                &buf[..]
1184            );
1185            assert_eq!(
1186                &read_buf[RAMDISK_BLOCK_SIZE as usize + len..],
1187                &[0; RAMDISK_BLOCK_SIZE as usize][..]
1188            );
1189        };
1190        const WRITE_LEN: usize = super::TEMP_VMO_SIZE * 3 + RAMDISK_BLOCK_SIZE as usize;
1191        join!(
1192            test_one(RAMDISK_BLOCK_SIZE, WRITE_LEN, 0xa3u8),
1193            test_one(2 * RAMDISK_BLOCK_SIZE + WRITE_LEN as u64, WRITE_LEN, 0x7fu8)
1194        );
1195    }
1196
1197    // Implements dummy server which can be used by test cases to verify whether
1198    // channel messages and fifo operations are being received - by using set_channel_handler or
1199    // set_fifo_hander respectively
1200    struct FakeBlockServer<'a> {
1201        server_channel: Option<fidl::endpoints::ServerEnd<block::BlockMarker>>,
1202        channel_handler: Box<dyn Fn(&block::SessionRequest) -> bool + 'a>,
1203        fifo_handler: Box<dyn Fn(BlockFifoRequest) -> BlockFifoResponse + 'a>,
1204    }
1205
1206    impl<'a> FakeBlockServer<'a> {
1207        // Creates a new FakeBlockServer given a channel to listen on.
1208        //
1209        // 'channel_handler' and 'fifo_handler' closures allow for customizing the way how the server
1210        // handles requests received from channel or the fifo respectfully.
1211        //
1212        // 'channel_handler' receives a message before it is handled by the default implementation
1213        // and can return 'true' to indicate all processing is done and no further processing of
1214        // that message is required
1215        //
1216        // 'fifo_handler' takes as input a BlockFifoRequest and produces a response which the
1217        // FakeBlockServer will send over the fifo.
1218        fn new(
1219            server_channel: fidl::endpoints::ServerEnd<block::BlockMarker>,
1220            channel_handler: impl Fn(&block::SessionRequest) -> bool + 'a,
1221            fifo_handler: impl Fn(BlockFifoRequest) -> BlockFifoResponse + 'a,
1222        ) -> FakeBlockServer<'a> {
1223            FakeBlockServer {
1224                server_channel: Some(server_channel),
1225                channel_handler: Box::new(channel_handler),
1226                fifo_handler: Box::new(fifo_handler),
1227            }
1228        }
1229
1230        // Runs the server
1231        async fn run(&mut self) {
1232            let server = self.server_channel.take().unwrap();
1233
1234            // Set up a mock server.
1235            let (server_fifo, client_fifo) =
1236                zx::Fifo::<BlockFifoRequest, BlockFifoResponse>::create(16)
1237                    .expect("Fifo::create failed");
1238            let maybe_server_fifo = fuchsia_sync::Mutex::new(Some(client_fifo));
1239
1240            let (fifo_future_abort, fifo_future_abort_registration) = AbortHandle::new_pair();
1241            let fifo_future = Abortable::new(
1242                async {
1243                    let mut fifo = fasync::Fifo::from_fifo(server_fifo);
1244                    let (mut reader, mut writer) = fifo.async_io();
1245                    let mut request = BlockFifoRequest::default();
1246                    loop {
1247                        match reader.read_entries(&mut request).await {
1248                            Ok(1) => {}
1249                            Err(zx::Status::PEER_CLOSED) => break,
1250                            Err(e) => panic!("read_entry failed {:?}", e),
1251                            _ => unreachable!(),
1252                        };
1253
1254                        let response = self.fifo_handler.as_ref()(request);
1255                        writer
1256                            .write_entries(std::slice::from_ref(&response))
1257                            .await
1258                            .expect("write_entries failed");
1259                    }
1260                },
1261                fifo_future_abort_registration,
1262            );
1263
1264            let channel_future = async {
1265                server
1266                    .into_stream()
1267                    .for_each_concurrent(None, |request| async {
1268                        let request = request.expect("unexpected fidl error");
1269
1270                        match request {
1271                            block::BlockRequest::GetInfo { responder } => {
1272                                responder
1273                                    .send(Ok(&block::BlockInfo {
1274                                        block_count: 1024,
1275                                        block_size: 512,
1276                                        max_transfer_size: 1024 * 1024,
1277                                        flags: block::DeviceFlag::empty(),
1278                                    }))
1279                                    .expect("send failed");
1280                            }
1281                            block::BlockRequest::OpenSession { session, control_handle: _ } => {
1282                                let stream = session.into_stream();
1283                                stream
1284                                    .for_each(|request| async {
1285                                        let request = request.expect("unexpected fidl error");
1286                                        // Give a chance for the test to register and potentially
1287                                        // handle the event
1288                                        if self.channel_handler.as_ref()(&request) {
1289                                            return;
1290                                        }
1291                                        match request {
1292                                            block::SessionRequest::GetFifo { responder } => {
1293                                                match maybe_server_fifo.lock().take() {
1294                                                    Some(fifo) => {
1295                                                        responder.send(Ok(fifo.downcast()))
1296                                                    }
1297                                                    None => responder.send(Err(
1298                                                        zx::Status::NO_RESOURCES.into_raw(),
1299                                                    )),
1300                                                }
1301                                                .expect("send failed")
1302                                            }
1303                                            block::SessionRequest::AttachVmo {
1304                                                vmo: _,
1305                                                responder,
1306                                            } => responder
1307                                                .send(Ok(&block::VmoId { id: 1 }))
1308                                                .expect("send failed"),
1309                                            block::SessionRequest::Close { responder } => {
1310                                                fifo_future_abort.abort();
1311                                                responder.send(Ok(())).expect("send failed")
1312                                            }
1313                                        }
1314                                    })
1315                                    .await
1316                            }
1317                            _ => panic!("Unexpected message"),
1318                        }
1319                    })
1320                    .await;
1321            };
1322
1323            let _result = join!(fifo_future, channel_future);
1324            //_result can be Err(Aborted) since FifoClose calls .abort but that's expected
1325        }
1326    }
1327
1328    #[fuchsia::test]
1329    async fn test_block_close_is_called() {
1330        let close_called = fuchsia_sync::Mutex::new(false);
1331        let (client_end, server) = fidl::endpoints::create_endpoints::<block::BlockMarker>();
1332
1333        std::thread::spawn(move || {
1334            let _block_client =
1335                RemoteBlockClientSync::new(client_end).expect("RemoteBlockClientSync::new failed");
1336            // The drop here should cause Close to be sent.
1337        });
1338
1339        let channel_handler = |request: &block::SessionRequest| -> bool {
1340            if let block::SessionRequest::Close { .. } = request {
1341                *close_called.lock() = true;
1342            }
1343            false
1344        };
1345        FakeBlockServer::new(server, channel_handler, |_| unreachable!()).run().await;
1346
1347        // After the server has finished running, we can check to see that close was called.
1348        assert!(*close_called.lock());
1349    }
1350
1351    #[fuchsia::test]
1352    async fn test_block_flush_is_called() {
1353        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<block::BlockMarker>();
1354
1355        struct Interface {
1356            flush_called: Arc<AtomicBool>,
1357        }
1358        impl block_server::async_interface::Interface for Interface {
1359            fn get_info(&self) -> Cow<'_, DeviceInfo> {
1360                Cow::Owned(DeviceInfo::Partition(PartitionInfo {
1361                    device_flags: fidl_fuchsia_storage_block::DeviceFlag::empty(),
1362                    max_transfer_blocks: None,
1363                    block_range: Some(0..1000),
1364                    type_guid: [0; 16],
1365                    instance_guid: [0; 16],
1366                    name: "foo".to_string(),
1367                    flags: 0,
1368                }))
1369            }
1370
1371            async fn read(
1372                &self,
1373                _device_block_offset: u64,
1374                _block_count: u32,
1375                _vmo: &Arc<zx::Vmo>,
1376                _vmo_offset: u64,
1377                _opts: ReadOptions,
1378                _trace_flow_id: Option<NonZero<u64>>,
1379            ) -> Result<(), zx::Status> {
1380                unreachable!();
1381            }
1382
1383            async fn write(
1384                &self,
1385                _device_block_offset: u64,
1386                _block_count: u32,
1387                _vmo: &Arc<zx::Vmo>,
1388                _vmo_offset: u64,
1389                _opts: WriteOptions,
1390                _trace_flow_id: Option<NonZero<u64>>,
1391            ) -> Result<(), zx::Status> {
1392                unreachable!();
1393            }
1394
1395            async fn flush(&self, _trace_flow_id: Option<NonZero<u64>>) -> Result<(), zx::Status> {
1396                self.flush_called.store(true, Ordering::Relaxed);
1397                Ok(())
1398            }
1399
1400            async fn trim(
1401                &self,
1402                _device_block_offset: u64,
1403                _block_count: u32,
1404                _trace_flow_id: Option<NonZero<u64>>,
1405            ) -> Result<(), zx::Status> {
1406                unreachable!();
1407            }
1408        }
1409
1410        let flush_called = Arc::new(AtomicBool::new(false));
1411
1412        futures::join!(
1413            async {
1414                let block_client = RemoteBlockClient::new(proxy).await.expect("new failed");
1415
1416                block_client.flush().await.expect("flush failed");
1417            },
1418            async {
1419                let block_server = BlockServer::new(
1420                    512,
1421                    Arc::new(Interface { flush_called: flush_called.clone() }),
1422                );
1423                block_server.handle_requests(stream.cast_stream()).await.unwrap();
1424            }
1425        );
1426
1427        assert!(flush_called.load(Ordering::Relaxed));
1428    }
1429
1430    #[fuchsia::test]
1431    async fn test_trace_flow_ids_set() {
1432        let (proxy, server) = fidl::endpoints::create_proxy();
1433
1434        futures::join!(
1435            async {
1436                let block_client = RemoteBlockClient::new(proxy).await.expect("new failed");
1437                block_client.flush().await.expect("flush failed");
1438            },
1439            async {
1440                let flow_id: fuchsia_sync::Mutex<Option<u64>> = fuchsia_sync::Mutex::new(None);
1441                let fifo_handler = |request: BlockFifoRequest| -> BlockFifoResponse {
1442                    if request.trace_flow_id > 0 {
1443                        *flow_id.lock() = Some(request.trace_flow_id);
1444                    }
1445                    BlockFifoResponse {
1446                        status: zx::Status::OK.into_raw(),
1447                        reqid: request.reqid,
1448                        ..Default::default()
1449                    }
1450                };
1451                FakeBlockServer::new(server, |_| false, fifo_handler).run().await;
1452                // After the server has finished running, verify the trace flow ID was set to some value.
1453                assert!(flow_id.lock().is_some());
1454            }
1455        );
1456    }
1457}