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