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 { epitaph, .. } => match epitaph.into() {
50            Err(s) => s,
51            Ok(()) => zx::Status::PEER_CLOSED,
52        },
53        _ => zx::Status::INTERNAL,
54    }
55}
56
57fn opcode_str(opcode: u8) -> &'static str {
58    match BlockOpcode::from_primitive(opcode) {
59        Some(BlockOpcode::Read) => "read",
60        Some(BlockOpcode::Write) => "write",
61        Some(BlockOpcode::Flush) => "flush",
62        Some(BlockOpcode::Trim) => "trim",
63        Some(BlockOpcode::CloseVmo) => "close_vmo",
64        None => "unknown",
65    }
66}
67
68// Generates a trace ID that will be unique across the system (as long as |request_id| isn't
69// reused within this process).
70fn generate_trace_flow_id(request_id: u32) -> u64 {
71    static SELF_HANDLE: LazyLock<zx_handle_t> =
72        LazyLock::new(|| fuchsia_runtime::process_self().raw_handle());
73    *SELF_HANDLE as u64 + (request_id as u64) << 32
74}
75
76pub enum BufferSlice<'a> {
77    VmoId { vmo_id: &'a VmoId, offset: u64, length: u64 },
78    Memory(&'a [u8]),
79}
80
81impl<'a> BufferSlice<'a> {
82    pub fn new_with_vmo_id(vmo_id: &'a VmoId, offset: u64, length: u64) -> Self {
83        BufferSlice::VmoId { vmo_id, offset, length }
84    }
85}
86
87impl<'a> From<&'a [u8]> for BufferSlice<'a> {
88    fn from(buf: &'a [u8]) -> Self {
89        BufferSlice::Memory(buf)
90    }
91}
92
93pub enum MutableBufferSlice<'a> {
94    VmoId { vmo_id: &'a VmoId, offset: u64, length: u64 },
95    Memory(&'a mut [u8]),
96}
97
98impl<'a> MutableBufferSlice<'a> {
99    pub fn new_with_vmo_id(vmo_id: &'a VmoId, offset: u64, length: u64) -> Self {
100        MutableBufferSlice::VmoId { vmo_id, offset, length }
101    }
102}
103
104impl<'a> From<&'a mut [u8]> for MutableBufferSlice<'a> {
105    fn from(buf: &'a mut [u8]) -> Self {
106        MutableBufferSlice::Memory(buf)
107    }
108}
109
110#[derive(Default)]
111struct RequestState {
112    result: Option<Result<(), zx::Status>>,
113    waker: Option<Waker>,
114}
115
116#[derive(Default)]
117struct FifoState {
118    // The fifo.
119    fifo: Option<fasync::Fifo<BlockFifoResponse, BlockFifoRequest>>,
120
121    // The next request ID to be used.
122    next_request_id: u32,
123
124    // A queue of messages to be sent on the fifo.
125    queue: std::collections::VecDeque<BlockFifoRequest>,
126
127    // Map from request ID to RequestState.
128    map: HashMap<u32, RequestState>,
129
130    // The waker for the FifoPoller.
131    poller_waker: Option<Waker>,
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(Err(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.get());
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)
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    fn flush(&self) -> impl Future<Output = Result<(), zx::Status>> + Send {
373        self.flush_traced(NO_TRACE_ID)
374    }
375
376    /// Sends a flush request to the underlying block device.
377    fn flush_traced(
378        &self,
379        trace_flow_id: u64,
380    ) -> impl Future<Output = Result<(), zx::Status>> + Send;
381
382    /// Closes the fifo.
383    fn close(&self) -> impl Future<Output = Result<(), zx::Status>> + Send;
384
385    /// Returns the block size of the device.
386    fn block_size(&self) -> u32;
387
388    /// Returns the size, in blocks, of the device.
389    fn block_count(&self) -> u64;
390
391    /// Returns the maximum number of blocks which can be transferred in a single request.
392    fn max_transfer_blocks(&self) -> Option<NonZero<u32>>;
393
394    /// Returns the block flags reported by the device.
395    fn block_flags(&self) -> BlockDeviceFlag;
396
397    /// Returns true if the remote fifo is still connected.
398    fn is_connected(&self) -> bool;
399}
400
401struct Common {
402    block_size: u32,
403    block_count: u64,
404    max_transfer_blocks: Option<NonZero<u32>>,
405    block_flags: BlockDeviceFlag,
406    fifo_state: FifoStateRef,
407    temp_vmo: futures::lock::Mutex<zx::Vmo>,
408    temp_vmo_id: VmoId,
409}
410
411impl Common {
412    fn new(
413        fifo: fasync::Fifo<BlockFifoResponse, BlockFifoRequest>,
414        info: &block::BlockInfo,
415        temp_vmo: zx::Vmo,
416        temp_vmo_id: VmoId,
417    ) -> Self {
418        let fifo_state = Arc::new(Mutex::new(FifoState { fifo: Some(fifo), ..Default::default() }));
419        fasync::Task::spawn(FifoPoller { fifo_state: fifo_state.clone() }).detach();
420        Self {
421            block_size: info.block_size,
422            block_count: info.block_count,
423            max_transfer_blocks: if info.max_transfer_size != MAX_TRANSFER_UNBOUNDED {
424                NonZero::new(info.max_transfer_size / info.block_size)
425            } else {
426                None
427            },
428            block_flags: info.flags,
429            fifo_state,
430            temp_vmo: futures::lock::Mutex::new(temp_vmo),
431            temp_vmo_id,
432        }
433    }
434
435    fn to_blocks(&self, bytes: u64) -> Result<u64, zx::Status> {
436        if bytes % self.block_size as u64 != 0 {
437            Err(zx::Status::INVALID_ARGS)
438        } else {
439            Ok(bytes / self.block_size as u64)
440        }
441    }
442
443    // Sends the request and waits for the response.
444    async fn send(&self, mut request: BlockFifoRequest) -> Result<(), zx::Status> {
445        let (request_id, trace_flow_id) = {
446            let mut state = self.fifo_state.lock();
447
448            if state.fifo.is_none() {
449                // Fifo has been closed.
450                return Err(zx::Status::CANCELED);
451            }
452            trace::duration!(
453                "storage",
454                "block_client::send::start",
455                "op" => opcode_str(request.command.opcode),
456                "len" => request.length * self.block_size
457            );
458            let request_id = state.next_request_id;
459            state.next_request_id = state.next_request_id.overflowing_add(1).0;
460            assert!(
461                state.map.insert(request_id, RequestState::default()).is_none(),
462                "request id in use!"
463            );
464            update_outstanding_requests_counter(state.map.len());
465            request.reqid = request_id;
466            if request.trace_flow_id == NO_TRACE_ID {
467                request.trace_flow_id = generate_trace_flow_id(request_id);
468            }
469            let trace_flow_id = request.trace_flow_id;
470            trace::flow_begin!("storage", "block_client::send", trace_flow_id.into());
471            state.queue.push_back(request);
472            if let Some(waker) = state.poller_waker.clone() {
473                state.poll_send_requests(&mut Context::from_waker(&waker));
474            }
475            (request_id, trace_flow_id)
476        };
477        ResponseFuture::new(self.fifo_state.clone(), request_id).await?;
478        trace::duration!("storage", "block_client::send::end");
479        trace::flow_end!("storage", "block_client::send", trace_flow_id.into());
480        Ok(())
481    }
482
483    fn detach_vmo(&self, vmo_id: VmoId) -> impl Future<Output = Result<(), zx::Status>> {
484        self.send(BlockFifoRequest {
485            command: BlockFifoCommand {
486                opcode: BlockOpcode::CloseVmo.into_primitive(),
487                flags: 0,
488                ..Default::default()
489            },
490            vmoid: vmo_id.into_id(),
491            ..Default::default()
492        })
493    }
494
495    async fn read_at(
496        &self,
497        buffer_slice: MutableBufferSlice<'_>,
498        device_offset: u64,
499        opts: ReadOptions,
500        trace_flow_id: u64,
501    ) -> Result<(), zx::Status> {
502        let mut flags = BlockIoFlag::empty();
503
504        if opts.inline_crypto.is_enabled {
505            flags |= BlockIoFlag::INLINE_ENCRYPTION_ENABLED;
506        }
507
508        match buffer_slice {
509            MutableBufferSlice::VmoId { vmo_id, offset, length } => {
510                self.send(BlockFifoRequest {
511                    command: BlockFifoCommand {
512                        opcode: BlockOpcode::Read.into_primitive(),
513                        flags: flags.bits(),
514                        ..Default::default()
515                    },
516                    vmoid: vmo_id.id(),
517                    length: self
518                        .to_blocks(length)?
519                        .try_into()
520                        .map_err(|_| zx::Status::INVALID_ARGS)?,
521                    vmo_offset: self.to_blocks(offset)?,
522                    dev_offset: self.to_blocks(device_offset)?,
523                    trace_flow_id,
524                    dun: opts.inline_crypto.dun,
525                    slot: opts.inline_crypto.slot,
526                    ..Default::default()
527                })
528                .await?
529            }
530            MutableBufferSlice::Memory(mut slice) => {
531                let temp_vmo = self.temp_vmo.lock().await;
532                let mut device_block = self.to_blocks(device_offset)?;
533                loop {
534                    let to_do = std::cmp::min(TEMP_VMO_SIZE, slice.len());
535                    let block_count = self.to_blocks(to_do as u64)? as u32;
536                    self.send(BlockFifoRequest {
537                        command: BlockFifoCommand {
538                            opcode: BlockOpcode::Read.into_primitive(),
539                            flags: flags.bits(),
540                            ..Default::default()
541                        },
542                        vmoid: self.temp_vmo_id.id(),
543                        length: block_count,
544                        vmo_offset: 0,
545                        dev_offset: device_block,
546                        trace_flow_id,
547                        dun: opts.inline_crypto.dun,
548                        slot: opts.inline_crypto.slot,
549                        ..Default::default()
550                    })
551                    .await?;
552                    temp_vmo.read(&mut slice[..to_do], 0)?;
553                    if to_do == slice.len() {
554                        break;
555                    }
556                    device_block += block_count as u64;
557                    slice = &mut slice[to_do..];
558                }
559            }
560        }
561        Ok(())
562    }
563
564    async fn write_at(
565        &self,
566        buffer_slice: BufferSlice<'_>,
567        device_offset: u64,
568        opts: WriteOptions,
569        trace_flow_id: u64,
570    ) -> Result<(), zx::Status> {
571        let mut flags = BlockIoFlag::empty();
572
573        if opts.flags.contains(WriteFlags::FORCE_ACCESS) {
574            flags |= BlockIoFlag::FORCE_ACCESS;
575        }
576
577        if opts.flags.contains(WriteFlags::PRE_BARRIER) {
578            flags |= BlockIoFlag::PRE_BARRIER;
579        }
580
581        if opts.inline_crypto.is_enabled {
582            flags |= BlockIoFlag::INLINE_ENCRYPTION_ENABLED;
583        }
584
585        match buffer_slice {
586            BufferSlice::VmoId { vmo_id, offset, length } => {
587                self.send(BlockFifoRequest {
588                    command: BlockFifoCommand {
589                        opcode: BlockOpcode::Write.into_primitive(),
590                        flags: flags.bits(),
591                        ..Default::default()
592                    },
593                    vmoid: vmo_id.id(),
594                    length: self
595                        .to_blocks(length)?
596                        .try_into()
597                        .map_err(|_| zx::Status::INVALID_ARGS)?,
598                    vmo_offset: self.to_blocks(offset)?,
599                    dev_offset: self.to_blocks(device_offset)?,
600                    trace_flow_id,
601                    dun: opts.inline_crypto.dun,
602                    slot: opts.inline_crypto.slot,
603                    ..Default::default()
604                })
605                .await?;
606            }
607            BufferSlice::Memory(mut slice) => {
608                let temp_vmo = self.temp_vmo.lock().await;
609                let mut device_block = self.to_blocks(device_offset)?;
610                loop {
611                    let to_do = std::cmp::min(TEMP_VMO_SIZE, slice.len());
612                    let block_count = self.to_blocks(to_do as u64)? as u32;
613                    temp_vmo.write(&slice[..to_do], 0)?;
614                    self.send(BlockFifoRequest {
615                        command: BlockFifoCommand {
616                            opcode: BlockOpcode::Write.into_primitive(),
617                            flags: flags.bits(),
618                            ..Default::default()
619                        },
620                        vmoid: self.temp_vmo_id.id(),
621                        length: block_count,
622                        vmo_offset: 0,
623                        dev_offset: device_block,
624                        trace_flow_id,
625                        dun: opts.inline_crypto.dun,
626                        slot: opts.inline_crypto.slot,
627                        ..Default::default()
628                    })
629                    .await?;
630                    if to_do == slice.len() {
631                        break;
632                    }
633                    device_block += block_count as u64;
634                    slice = &slice[to_do..];
635                }
636            }
637        }
638        Ok(())
639    }
640
641    async fn trim(&self, device_range: Range<u64>, trace_flow_id: u64) -> Result<(), zx::Status> {
642        let length = self.to_blocks(device_range.end - device_range.start)? as u32;
643        let dev_offset = self.to_blocks(device_range.start)?;
644        self.send(BlockFifoRequest {
645            command: BlockFifoCommand {
646                opcode: BlockOpcode::Trim.into_primitive(),
647                flags: 0,
648                ..Default::default()
649            },
650            vmoid: VMOID_INVALID,
651            length,
652            dev_offset,
653            trace_flow_id,
654            ..Default::default()
655        })
656        .await
657    }
658
659    fn flush(&self, trace_flow_id: u64) -> impl Future<Output = Result<(), zx::Status>> {
660        self.send(BlockFifoRequest {
661            command: BlockFifoCommand {
662                opcode: BlockOpcode::Flush.into_primitive(),
663                flags: 0,
664                ..Default::default()
665            },
666            vmoid: VMOID_INVALID,
667            trace_flow_id,
668            ..Default::default()
669        })
670    }
671
672    fn block_size(&self) -> u32 {
673        self.block_size
674    }
675
676    fn block_count(&self) -> u64 {
677        self.block_count
678    }
679
680    fn max_transfer_blocks(&self) -> Option<NonZero<u32>> {
681        self.max_transfer_blocks.clone()
682    }
683
684    fn block_flags(&self) -> BlockDeviceFlag {
685        self.block_flags
686    }
687
688    fn is_connected(&self) -> bool {
689        self.fifo_state.lock().fifo.is_some()
690    }
691}
692
693impl Drop for Common {
694    fn drop(&mut self) {
695        // It's OK to leak the VMO id because the server will dump all VMOs when the fifo is torn
696        // down.
697        let _ = self.temp_vmo_id.take().into_id();
698        self.fifo_state.lock().terminate();
699    }
700}
701
702// RemoteBlockClient is a BlockClient that communicates with a real block device over FIDL.
703pub struct RemoteBlockClient {
704    session: block::SessionProxy,
705    common: Common,
706}
707
708impl RemoteBlockClient {
709    /// Returns a connection to a remote block device via the given channel.
710    pub async fn new(remote: impl Borrow<block::BlockProxy>) -> Result<Self, zx::Status> {
711        let remote = remote.borrow();
712        let info =
713            remote.get_info().await.map_err(fidl_to_status)?.map_err(zx::Status::err_from_raw)?;
714        let (session, server) = fidl::endpoints::create_proxy();
715        let () = remote.open_session(server).map_err(fidl_to_status)?;
716        Self::from_session(info, session).await
717    }
718
719    pub async fn from_session(
720        info: block::BlockInfo,
721        session: block::SessionProxy,
722    ) -> Result<Self, zx::Status> {
723        const SCRATCH_VMO_NAME: zx::Name = zx::Name::new_lossy("block-client-scratch-vmo");
724        let fifo =
725            session.get_fifo().await.map_err(fidl_to_status)?.map_err(zx::Status::err_from_raw)?;
726        let fifo = fasync::Fifo::from_fifo(fifo);
727        let temp_vmo = zx::Vmo::create(TEMP_VMO_SIZE as u64)?;
728        temp_vmo.set_name(&SCRATCH_VMO_NAME)?;
729        let dup = temp_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
730        let vmo_id = session
731            .attach_vmo(dup)
732            .await
733            .map_err(fidl_to_status)?
734            .map_err(zx::Status::err_from_raw)?;
735        let vmo_id = VmoId::new(vmo_id.id);
736        Ok(RemoteBlockClient { session, common: Common::new(fifo, &info, temp_vmo, vmo_id) })
737    }
738}
739
740impl BlockClient for RemoteBlockClient {
741    async unsafe fn attach_vmo(&self, vmo: &zx::Vmo) -> Result<VmoId, zx::Status> {
742        let dup = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
743        let vmo_id = self
744            .session
745            .attach_vmo(dup)
746            .await
747            .map_err(fidl_to_status)?
748            .map_err(zx::Status::err_from_raw)?;
749        Ok(VmoId::new(vmo_id.id))
750    }
751
752    fn detach_vmo(&self, vmo_id: VmoId) -> impl Future<Output = Result<(), zx::Status>> {
753        self.common.detach_vmo(vmo_id)
754    }
755
756    fn read_at_with_opts_traced(
757        &self,
758        buffer_slice: MutableBufferSlice<'_>,
759        device_offset: u64,
760        opts: ReadOptions,
761        trace_flow_id: u64,
762    ) -> impl Future<Output = Result<(), zx::Status>> {
763        self.common.read_at(buffer_slice, device_offset, opts, trace_flow_id)
764    }
765
766    fn write_at_with_opts_traced(
767        &self,
768        buffer_slice: BufferSlice<'_>,
769        device_offset: u64,
770        opts: WriteOptions,
771        trace_flow_id: u64,
772    ) -> impl Future<Output = Result<(), zx::Status>> {
773        self.common.write_at(buffer_slice, device_offset, opts, trace_flow_id)
774    }
775
776    fn trim_traced(
777        &self,
778        range: Range<u64>,
779        trace_flow_id: u64,
780    ) -> impl Future<Output = Result<(), zx::Status>> {
781        self.common.trim(range, trace_flow_id)
782    }
783
784    fn flush_traced(&self, trace_flow_id: u64) -> impl Future<Output = Result<(), zx::Status>> {
785        self.common.flush(trace_flow_id)
786    }
787
788    async fn close(&self) -> Result<(), zx::Status> {
789        let () = self
790            .session
791            .close()
792            .await
793            .map_err(fidl_to_status)?
794            .map_err(zx::Status::err_from_raw)?;
795        Ok(())
796    }
797
798    fn block_size(&self) -> u32 {
799        self.common.block_size()
800    }
801
802    fn block_count(&self) -> u64 {
803        self.common.block_count()
804    }
805
806    fn max_transfer_blocks(&self) -> Option<NonZero<u32>> {
807        self.common.max_transfer_blocks()
808    }
809
810    fn block_flags(&self) -> BlockDeviceFlag {
811        self.common.block_flags()
812    }
813
814    fn is_connected(&self) -> bool {
815        self.common.is_connected()
816    }
817}
818
819pub struct RemoteBlockClientSync {
820    session: block::SessionSynchronousProxy,
821    common: Common,
822}
823
824impl RemoteBlockClientSync {
825    /// Returns a connection to a remote block device via the given channel, but spawns a separate
826    /// thread for polling the fifo which makes it work in cases where no executor is configured for
827    /// the calling thread.
828    pub fn new(
829        client_end: fidl::endpoints::ClientEnd<block::BlockMarker>,
830    ) -> Result<Self, zx::Status> {
831        let remote = block::BlockSynchronousProxy::new(client_end.into_channel());
832        let info = remote
833            .get_info(zx::MonotonicInstant::INFINITE)
834            .map_err(fidl_to_status)?
835            .map_err(zx::Status::err_from_raw)?;
836        let (client, server) = fidl::endpoints::create_endpoints();
837        let () = remote.open_session(server).map_err(fidl_to_status)?;
838        let session = block::SessionSynchronousProxy::new(client.into_channel());
839        let fifo = session
840            .get_fifo(zx::MonotonicInstant::INFINITE)
841            .map_err(fidl_to_status)?
842            .map_err(zx::Status::err_from_raw)?;
843        let temp_vmo = zx::Vmo::create(TEMP_VMO_SIZE as u64)?;
844        let dup = temp_vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
845        let vmo_id = session
846            .attach_vmo(dup, zx::MonotonicInstant::INFINITE)
847            .map_err(fidl_to_status)?
848            .map_err(zx::Status::err_from_raw)?;
849        let vmo_id = VmoId::new(vmo_id.id);
850
851        // The fifo needs to be instantiated from the thread that has the executor as that's where
852        // the fifo registers for notifications to be delivered.
853        let (sender, receiver) = oneshot::channel::<Result<Self, zx::Status>>();
854        std::thread::spawn(move || {
855            let mut executor = fasync::LocalExecutor::default();
856            let fifo = fasync::Fifo::from_fifo(fifo);
857            let common = Common::new(fifo, &info, temp_vmo, vmo_id);
858            let fifo_state = common.fifo_state.clone();
859            let _ = sender.send(Ok(RemoteBlockClientSync { session, common }));
860            executor.run_singlethreaded(FifoPoller { fifo_state });
861        });
862        block_on(receiver).map_err(|_| zx::Status::CANCELED)?
863    }
864
865    /// Wraps AttachVmo from fuchsia.hardware.block::Block.
866    ///
867    /// # Safety
868    ///
869    /// See `BlockClient::attach_vmo`.
870    pub unsafe fn attach_vmo(&self, vmo: &zx::Vmo) -> Result<VmoId, zx::Status> {
871        let dup = vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
872        let vmo_id = self
873            .session
874            .attach_vmo(dup, zx::MonotonicInstant::INFINITE)
875            .map_err(fidl_to_status)?
876            .map_err(zx::Status::err_from_raw)?;
877        Ok(VmoId::new(vmo_id.id))
878    }
879
880    pub fn detach_vmo(&self, vmo_id: VmoId) -> Result<(), zx::Status> {
881        block_on(self.common.detach_vmo(vmo_id))
882    }
883
884    pub fn read_at(
885        &self,
886        buffer_slice: MutableBufferSlice<'_>,
887        device_offset: u64,
888    ) -> Result<(), zx::Status> {
889        block_on(self.common.read_at(
890            buffer_slice,
891            device_offset,
892            ReadOptions::default(),
893            NO_TRACE_ID,
894        ))
895    }
896
897    pub fn write_at(
898        &self,
899        buffer_slice: BufferSlice<'_>,
900        device_offset: u64,
901    ) -> Result<(), zx::Status> {
902        block_on(self.common.write_at(
903            buffer_slice,
904            device_offset,
905            WriteOptions::default(),
906            NO_TRACE_ID,
907        ))
908    }
909
910    pub fn flush(&self) -> Result<(), zx::Status> {
911        block_on(self.common.flush(NO_TRACE_ID))
912    }
913
914    pub fn close(&self) -> Result<(), zx::Status> {
915        let () = self
916            .session
917            .close(zx::MonotonicInstant::INFINITE)
918            .map_err(fidl_to_status)?
919            .map_err(zx::Status::err_from_raw)?;
920        Ok(())
921    }
922
923    pub fn block_size(&self) -> u32 {
924        self.common.block_size()
925    }
926
927    pub fn block_count(&self) -> u64 {
928        self.common.block_count()
929    }
930
931    pub fn is_connected(&self) -> bool {
932        self.common.is_connected()
933    }
934}
935
936impl Drop for RemoteBlockClientSync {
937    fn drop(&mut self) {
938        // Ignore errors here as there is not much we can do about it.
939        let _ = self.close();
940    }
941}
942
943// FifoPoller is a future responsible for sending and receiving from the fifo.
944struct FifoPoller {
945    fifo_state: FifoStateRef,
946}
947
948impl Future for FifoPoller {
949    type Output = ();
950
951    fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
952        let mut state_lock = self.fifo_state.lock();
953        let state = state_lock.deref_mut(); // So that we can split the borrow.
954
955        // Send requests.
956        if state.poll_send_requests(context) {
957            return Poll::Ready(());
958        }
959
960        // Receive responses.
961        let fifo = state.fifo.as_ref().unwrap(); // Safe because poll_send_requests checks.
962        loop {
963            let mut response = MaybeUninit::uninit();
964            match fifo.try_read(context, &mut response) {
965                Poll::Pending => {
966                    state.poller_waker = Some(context.waker().clone());
967                    return Poll::Pending;
968                }
969                Poll::Ready(Ok(_)) => {
970                    let response = unsafe { response.assume_init() };
971                    let request_id = response.reqid;
972                    // If the request isn't in the map, assume that it's a cancelled read.
973                    if let Some(request_state) = state.map.get_mut(&request_id) {
974                        request_state.result.replace(zx::Status::ok(response.status));
975                        if let Some(waker) = request_state.waker.take() {
976                            waker.wake();
977                        }
978                    }
979                }
980                Poll::Ready(Err(_)) => {
981                    state.terminate();
982                    return Poll::Ready(());
983                }
984            }
985        }
986    }
987}
988
989fn update_outstanding_requests_counter(outstanding: usize) {
990    trace::counter!("storage", "block-requests", 0, "outstanding" => outstanding);
991}
992
993#[cfg(test)]
994mod tests {
995    use super::{
996        BlockClient, BlockFifoRequest, BlockFifoResponse, BufferSlice, MutableBufferSlice,
997        RemoteBlockClient, RemoteBlockClientSync, WriteOptions,
998    };
999    use block_protocol::ReadOptions;
1000    use block_server::{BlockServer, DeviceInfo, PartitionInfo};
1001    use fidl::endpoints::RequestStream as _;
1002    use fidl_fuchsia_storage_block as block;
1003    use fuchsia_async as fasync;
1004    use futures::future::{AbortHandle, Abortable, TryFutureExt as _};
1005    use futures::join;
1006    use futures::stream::StreamExt as _;
1007    use futures::stream::futures_unordered::FuturesUnordered;
1008    use ramdevice_client::RamdiskClient;
1009    use std::borrow::Cow;
1010    use std::num::NonZero;
1011    use std::sync::Arc;
1012    use std::sync::atomic::{AtomicBool, Ordering};
1013
1014    const RAMDISK_BLOCK_SIZE: u64 = 1024;
1015    const RAMDISK_BLOCK_COUNT: u64 = 1024;
1016
1017    pub async fn make_ramdisk() -> (RamdiskClient, block::BlockProxy, RemoteBlockClient) {
1018        let ramdisk = RamdiskClient::create(RAMDISK_BLOCK_SIZE, RAMDISK_BLOCK_COUNT)
1019            .await
1020            .expect("RamdiskClient::create failed");
1021        let client_end = ramdisk.open().expect("ramdisk.open failed");
1022        let proxy = client_end.into_proxy();
1023        let block_client = RemoteBlockClient::new(proxy).await.expect("new failed");
1024        assert_eq!(block_client.block_size(), 1024);
1025        let client_end = ramdisk.open().expect("ramdisk.open failed");
1026        let proxy = client_end.into_proxy();
1027        (ramdisk, proxy, block_client)
1028    }
1029
1030    #[fuchsia::test]
1031    async fn test_against_ram_disk() {
1032        let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1033
1034        let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1035        vmo.write(b"hello", 5).expect("vmo.write failed");
1036        // SAFETY: Test code, only attach once, no other mappings.
1037        let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1038        block_client
1039            .write_at(BufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0)
1040            .await
1041            .expect("write_at failed");
1042        block_client
1043            .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 1024, 2048), 0)
1044            .await
1045            .expect("read_at failed");
1046        let mut buf: [u8; 5] = Default::default();
1047        vmo.read(&mut buf, 1029).expect("vmo.read failed");
1048        assert_eq!(&buf, b"hello");
1049        block_client.detach_vmo(vmo_id).await.expect("detach_vmo failed");
1050    }
1051
1052    #[fuchsia::test]
1053    async fn test_alignment() {
1054        let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1055        let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1056        // SAFETY: Test code, only attach once, no other mappings.
1057        let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1058        block_client
1059            .write_at(BufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 1)
1060            .await
1061            .expect_err("expected failure due to bad alignment");
1062        block_client.detach_vmo(vmo_id).await.expect("detach_vmo failed");
1063    }
1064
1065    #[fuchsia::test]
1066    async fn test_parallel_io() {
1067        let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1068        let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1069        // SAFETY: Test code, only attach once, no other mappings.
1070        let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1071        let mut reads = Vec::new();
1072        for _ in 0..1024 {
1073            reads.push(
1074                block_client
1075                    .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0)
1076                    .inspect_err(|e| panic!("read should have succeeded: {}", e)),
1077            );
1078        }
1079        futures::future::join_all(reads).await;
1080        block_client.detach_vmo(vmo_id).await.expect("detach_vmo failed");
1081    }
1082
1083    #[fuchsia::test]
1084    async fn test_closed_device() {
1085        let (ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1086        let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1087        // SAFETY: Test code, only attach once, no other mappings.
1088        let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1089        let mut reads = Vec::new();
1090        for _ in 0..1024 {
1091            reads.push(
1092                block_client.read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0),
1093            );
1094        }
1095        assert!(block_client.is_connected());
1096        let _ = futures::join!(futures::future::join_all(reads), async {
1097            std::mem::drop(ramdisk);
1098        });
1099        // Destroying the ramdisk is asynchronous. Keep issuing reads until they start failing.
1100        while block_client
1101            .read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0)
1102            .await
1103            .is_ok()
1104        {}
1105
1106        // Sometimes the FIFO will start rejecting requests before FIFO is actually closed, so we
1107        // get false-positives from is_connected.
1108        while block_client.is_connected() {
1109            // Sleep for a bit to minimise lock contention.
1110            fasync::Timer::new(fasync::MonotonicInstant::after(
1111                zx::MonotonicDuration::from_millis(500),
1112            ))
1113            .await;
1114        }
1115
1116        // But once is_connected goes negative, it should stay negative.
1117        assert_eq!(block_client.is_connected(), false);
1118        let _ = block_client.detach_vmo(vmo_id).await;
1119    }
1120
1121    #[fuchsia::test]
1122    async fn test_cancelled_reads() {
1123        let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1124        let vmo = zx::Vmo::create(131072).expect("Vmo::create failed");
1125        // SAFETY: Test code, only attach once, no other mappings.
1126        let vmo_id = unsafe { block_client.attach_vmo(&vmo) }.await.expect("attach_vmo failed");
1127        {
1128            let mut reads = FuturesUnordered::new();
1129            for _ in 0..1024 {
1130                reads.push(
1131                    block_client.read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0),
1132                );
1133            }
1134            // Read the first 500 results and then dump the rest.
1135            for _ in 0..500 {
1136                reads.next().await;
1137            }
1138        }
1139
1140        // Since we dropped pending futures (cancellation), the client must forcibly
1141        // terminate the connection to prevent any future unsafe I/O. Thus, further
1142        // calls like `detach_vmo` must fail with CANCELED.
1143        assert_eq!(
1144            block_client.read_at(MutableBufferSlice::new_with_vmo_id(&vmo_id, 0, 1024), 0).await,
1145            Err(zx::Status::CANCELED)
1146        );
1147        assert_eq!(block_client.detach_vmo(vmo_id).await, Err(zx::Status::CANCELED));
1148    }
1149
1150    #[fuchsia::test]
1151    async fn test_parallel_large_read_and_write_with_memory_succeds() {
1152        let (_ramdisk, _block_proxy, block_client) = make_ramdisk().await;
1153        let block_client_ref = &block_client;
1154        let test_one = |offset, len, fill| async move {
1155            let buf = vec![fill; len];
1156            block_client_ref.write_at(buf[..].into(), offset).await.expect("write_at failed");
1157            // Read back an extra block either side.
1158            let mut read_buf = vec![0u8; len + 2 * RAMDISK_BLOCK_SIZE as usize];
1159            block_client_ref
1160                .read_at(read_buf.as_mut_slice().into(), offset - RAMDISK_BLOCK_SIZE)
1161                .await
1162                .expect("read_at failed");
1163            assert_eq!(
1164                &read_buf[0..RAMDISK_BLOCK_SIZE as usize],
1165                &[0; RAMDISK_BLOCK_SIZE as usize][..]
1166            );
1167            assert_eq!(
1168                &read_buf[RAMDISK_BLOCK_SIZE as usize..RAMDISK_BLOCK_SIZE as usize + len],
1169                &buf[..]
1170            );
1171            assert_eq!(
1172                &read_buf[RAMDISK_BLOCK_SIZE as usize + len..],
1173                &[0; RAMDISK_BLOCK_SIZE as usize][..]
1174            );
1175        };
1176        const WRITE_LEN: usize = super::TEMP_VMO_SIZE * 3 + RAMDISK_BLOCK_SIZE as usize;
1177        join!(
1178            test_one(RAMDISK_BLOCK_SIZE, WRITE_LEN, 0xa3u8),
1179            test_one(2 * RAMDISK_BLOCK_SIZE + WRITE_LEN as u64, WRITE_LEN, 0x7fu8)
1180        );
1181    }
1182
1183    // Implements dummy server which can be used by test cases to verify whether
1184    // channel messages and fifo operations are being received - by using set_channel_handler or
1185    // set_fifo_hander respectively
1186    struct FakeBlockServer<'a> {
1187        server_channel: Option<fidl::endpoints::ServerEnd<block::BlockMarker>>,
1188        channel_handler: Box<dyn Fn(&block::SessionRequest) -> bool + 'a>,
1189        fifo_handler: Box<dyn Fn(BlockFifoRequest) -> BlockFifoResponse + 'a>,
1190    }
1191
1192    impl<'a> FakeBlockServer<'a> {
1193        // Creates a new FakeBlockServer given a channel to listen on.
1194        //
1195        // 'channel_handler' and 'fifo_handler' closures allow for customizing the way how the server
1196        // handles requests received from channel or the fifo respectfully.
1197        //
1198        // 'channel_handler' receives a message before it is handled by the default implementation
1199        // and can return 'true' to indicate all processing is done and no further processing of
1200        // that message is required
1201        //
1202        // 'fifo_handler' takes as input a BlockFifoRequest and produces a response which the
1203        // FakeBlockServer will send over the fifo.
1204        fn new(
1205            server_channel: fidl::endpoints::ServerEnd<block::BlockMarker>,
1206            channel_handler: impl Fn(&block::SessionRequest) -> bool + 'a,
1207            fifo_handler: impl Fn(BlockFifoRequest) -> BlockFifoResponse + 'a,
1208        ) -> FakeBlockServer<'a> {
1209            FakeBlockServer {
1210                server_channel: Some(server_channel),
1211                channel_handler: Box::new(channel_handler),
1212                fifo_handler: Box::new(fifo_handler),
1213            }
1214        }
1215
1216        // Runs the server
1217        async fn run(&mut self) {
1218            let server = self.server_channel.take().unwrap();
1219
1220            // Set up a mock server.
1221            let (server_fifo, client_fifo) =
1222                zx::Fifo::<BlockFifoRequest, BlockFifoResponse>::create(16)
1223                    .expect("Fifo::create failed");
1224            let maybe_server_fifo = fuchsia_sync::Mutex::new(Some(client_fifo));
1225
1226            let (fifo_future_abort, fifo_future_abort_registration) = AbortHandle::new_pair();
1227            let fifo_future = Abortable::new(
1228                async {
1229                    let mut fifo = fasync::Fifo::from_fifo(server_fifo);
1230                    let (mut reader, mut writer) = fifo.async_io();
1231                    let mut request = BlockFifoRequest::default();
1232                    loop {
1233                        match reader.read_entries(&mut request).await {
1234                            Ok(n) if n.get() == 1 => {}
1235                            Err(zx::Status::PEER_CLOSED) => break,
1236                            Err(e) => panic!("read_entry failed {:?}", e),
1237                            _ => unreachable!(),
1238                        };
1239
1240                        let response = self.fifo_handler.as_ref()(request);
1241                        writer
1242                            .write_entries(std::slice::from_ref(&response))
1243                            .await
1244                            .expect("write_entries failed");
1245                    }
1246                },
1247                fifo_future_abort_registration,
1248            );
1249
1250            let channel_future = async {
1251                server
1252                    .into_stream()
1253                    .for_each_concurrent(None, |request| async {
1254                        let request = request.expect("unexpected fidl error");
1255
1256                        match request {
1257                            block::BlockRequest::GetInfo { responder } => {
1258                                responder
1259                                    .send(Ok(&block::BlockInfo {
1260                                        block_count: 1024,
1261                                        block_size: 512,
1262                                        max_transfer_size: 1024 * 1024,
1263                                        flags: block::DeviceFlag::empty(),
1264                                    }))
1265                                    .expect("send failed");
1266                            }
1267                            block::BlockRequest::OpenSession { session, control_handle: _ } => {
1268                                let stream = session.into_stream();
1269                                stream
1270                                    .for_each(|request| async {
1271                                        let request = request.expect("unexpected fidl error");
1272                                        // Give a chance for the test to register and potentially
1273                                        // handle the event
1274                                        if self.channel_handler.as_ref()(&request) {
1275                                            return;
1276                                        }
1277                                        match request {
1278                                            block::SessionRequest::GetFifo { responder } => {
1279                                                match maybe_server_fifo.lock().take() {
1280                                                    Some(fifo) => {
1281                                                        responder.send(Ok(fifo.downcast()))
1282                                                    }
1283                                                    None => responder.send(Err(
1284                                                        zx::Status::NO_RESOURCES.into_raw(),
1285                                                    )),
1286                                                }
1287                                                .expect("send failed")
1288                                            }
1289                                            block::SessionRequest::AttachVmo {
1290                                                vmo: _,
1291                                                responder,
1292                                            } => responder
1293                                                .send(Ok(&block::VmoId { id: 1 }))
1294                                                .expect("send failed"),
1295                                            block::SessionRequest::Close { responder } => {
1296                                                fifo_future_abort.abort();
1297                                                responder.send(Ok(())).expect("send failed")
1298                                            }
1299                                        }
1300                                    })
1301                                    .await
1302                            }
1303                            _ => panic!("Unexpected message"),
1304                        }
1305                    })
1306                    .await;
1307            };
1308
1309            let _result = join!(fifo_future, channel_future);
1310            //_result can be Err(Aborted) since FifoClose calls .abort but that's expected
1311        }
1312    }
1313
1314    #[fuchsia::test]
1315    async fn test_block_close_is_called() {
1316        let close_called = fuchsia_sync::Mutex::new(false);
1317        let (client_end, server) = fidl::endpoints::create_endpoints::<block::BlockMarker>();
1318
1319        std::thread::spawn(move || {
1320            let _block_client =
1321                RemoteBlockClientSync::new(client_end).expect("RemoteBlockClientSync::new failed");
1322            // The drop here should cause Close to be sent.
1323        });
1324
1325        let channel_handler = |request: &block::SessionRequest| -> bool {
1326            if let block::SessionRequest::Close { .. } = request {
1327                *close_called.lock() = true;
1328            }
1329            false
1330        };
1331        FakeBlockServer::new(server, channel_handler, |_| unreachable!()).run().await;
1332
1333        // After the server has finished running, we can check to see that close was called.
1334        assert!(*close_called.lock());
1335    }
1336
1337    #[fuchsia::test]
1338    async fn test_block_flush_is_called() {
1339        let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<block::BlockMarker>();
1340
1341        struct Interface {
1342            flush_called: Arc<AtomicBool>,
1343        }
1344        impl block_server::async_interface::Interface for Interface {
1345            fn get_info(&self) -> Cow<'_, DeviceInfo> {
1346                Cow::Owned(DeviceInfo::Partition(PartitionInfo {
1347                    device_flags: fidl_fuchsia_storage_block::DeviceFlag::empty(),
1348                    max_transfer_blocks: None,
1349                    start_block_offset: None,
1350                    block_count: 1000,
1351                    type_guid: [0; 16],
1352                    instance_guid: [0; 16],
1353                    name: "foo".to_string(),
1354                    ..Default::default()
1355                }))
1356            }
1357
1358            async fn read(
1359                &self,
1360                _device_block_offset: u64,
1361                _block_count: u32,
1362                _vmo: &Arc<zx::Vmo>,
1363                _vmo_offset: u64,
1364                _opts: ReadOptions,
1365                _trace_flow_id: Option<NonZero<u64>>,
1366            ) -> Result<(), zx::Status> {
1367                unreachable!();
1368            }
1369
1370            async fn write(
1371                &self,
1372                _device_block_offset: u64,
1373                _block_count: u32,
1374                _vmo: &Arc<zx::Vmo>,
1375                _vmo_offset: u64,
1376                _opts: WriteOptions,
1377                _trace_flow_id: Option<NonZero<u64>>,
1378            ) -> Result<(), zx::Status> {
1379                unreachable!();
1380            }
1381
1382            async fn flush(&self, _trace_flow_id: Option<NonZero<u64>>) -> Result<(), zx::Status> {
1383                self.flush_called.store(true, Ordering::Relaxed);
1384                Ok(())
1385            }
1386
1387            async fn trim(
1388                &self,
1389                _device_block_offset: u64,
1390                _block_count: u32,
1391                _trace_flow_id: Option<NonZero<u64>>,
1392            ) -> Result<(), zx::Status> {
1393                unreachable!();
1394            }
1395        }
1396
1397        let flush_called = Arc::new(AtomicBool::new(false));
1398
1399        futures::join!(
1400            async {
1401                let block_client = RemoteBlockClient::new(proxy).await.expect("new failed");
1402
1403                block_client.flush().await.expect("flush failed");
1404            },
1405            async {
1406                let block_server = BlockServer::new(
1407                    512,
1408                    Arc::new(Interface { flush_called: flush_called.clone() }),
1409                );
1410                block_server.handle_requests(stream.cast_stream()).await.unwrap();
1411            }
1412        );
1413
1414        assert!(flush_called.load(Ordering::Relaxed));
1415    }
1416
1417    #[fuchsia::test]
1418    async fn test_trace_flow_ids_set() {
1419        let (proxy, server) = fidl::endpoints::create_proxy();
1420
1421        futures::join!(
1422            async {
1423                let block_client = RemoteBlockClient::new(proxy).await.expect("new failed");
1424                block_client.flush().await.expect("flush failed");
1425            },
1426            async {
1427                let flow_id: fuchsia_sync::Mutex<Option<u64>> = fuchsia_sync::Mutex::new(None);
1428                let fifo_handler = |request: BlockFifoRequest| -> BlockFifoResponse {
1429                    if request.trace_flow_id > 0 {
1430                        *flow_id.lock() = Some(request.trace_flow_id);
1431                    }
1432                    BlockFifoResponse {
1433                        status: zx::sys::ZX_OK,
1434                        reqid: request.reqid,
1435                        ..Default::default()
1436                    }
1437                };
1438                FakeBlockServer::new(server, |_| false, fifo_handler).run().await;
1439                // After the server has finished running, verify the trace flow ID was set to some value.
1440                assert!(flow_id.lock().is_some());
1441            }
1442        );
1443    }
1444}