Skip to main content

storage_device/
lib.rs

1// Copyright 2021 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//! `storage_device` provides a higher-level portable API ([`Device`]) for interacting with block
6//! devices.  This library also provides the [`Buffer`] type which is a contiguous, splittable
7//! transfer buffer allocated out of a shared pool which can be used for I/O.
8//!
9//! The two main implementations are
10//!   - [`block_device::BlockDevice`], which is backed by a [`block_client::Device`] and used on
11//!     Fuchsia devices, and
12//!   - [`file_backed_device::FileBackedDevice`], which is backed by a regular file and is portable.
13
14use crate::buffer::{BufferFuture, BufferRef, MutableBufferRef};
15use anyhow::{Error, bail};
16use async_trait::async_trait;
17// pub so `Device` trait implementations don't need to depend on the `block_protocol` crate
18pub use block_protocol::{InlineCryptoOptions, ReadOptions, WriteFlags, WriteOptions};
19use futures::channel::oneshot::{Sender, channel};
20use std::future::Future;
21use std::mem::ManuallyDrop;
22use std::ops::{Deref, Range};
23use std::pin::Pin;
24use std::sync::{Arc, OnceLock};
25
26pub mod buffer;
27pub mod buffer_allocator;
28pub mod splittable_buffer;
29
30pub use splittable_buffer::{SplittableBuffer, SubHandle};
31
32#[cfg(target_os = "fuchsia")]
33pub mod block_device;
34#[cfg(target_os = "fuchsia")]
35pub mod pinned_buffer_allocator;
36
37#[cfg(target_family = "unix")]
38pub mod file_backed_device;
39
40pub mod fake_device;
41
42pub mod ranged_device;
43
44#[async_trait]
45/// Device is an abstract representation of an underlying block device.
46pub trait Device: Send + Sync {
47    /// Allocates a transfer buffer of at least `size` bytes for doing I/O with the device.
48    /// The actual size of the buffer will be rounded up to a block-aligned size.
49    /// Blocks until enough capacity is available in the buffer.
50    fn allocate_buffer(&self, size: usize) -> BufferFuture<'_>;
51
52    /// Cleans up any transfer buffers that are no longer in use.
53    fn clean_transfer_buffer(&self) {}
54
55    /// Returns the block size of the device. Buffers are aligned to block-aligned chunks.
56    fn block_size(&self) -> u32;
57
58    /// Returns the number of blocks of the device.
59    fn block_count(&self) -> u64;
60
61    /// Returns the size in bytes of the device.
62    fn size(&self) -> u64 {
63        self.block_size() as u64 * self.block_count()
64    }
65
66    /// Fills `buffer` with blocks read from `offset`.
67    fn read<'a>(
68        &'a self,
69        offset: u64,
70        buffer: MutableBufferRef<'a>,
71    ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'a>> {
72        self.read_with_opts(offset, buffer, ReadOptions::default())
73    }
74
75    /// Fills `buffer` with blocks read from `offset`.
76    async fn read_with_opts(
77        &self,
78        offset: u64,
79        buffer: MutableBufferRef<'_>,
80        read_opts: ReadOptions,
81    ) -> Result<(), Error>;
82
83    /// Writes the contents of `buffer` to the device at `offset`.
84    fn write<'a>(
85        &'a self,
86        offset: u64,
87        buffer: BufferRef<'a>,
88    ) -> Pin<Box<dyn Future<Output = Result<(), Error>> + Send + 'a>> {
89        self.write_with_opts(offset, buffer, WriteOptions::default())
90    }
91
92    /// Writes the contents of `buffer` to the device at `offset`.
93    async fn write_with_opts(
94        &self,
95        offset: u64,
96        buffer: BufferRef<'_>,
97        write_opts: WriteOptions,
98    ) -> Result<(), Error>;
99
100    /// Trims the given device `range`.
101    async fn trim(&self, range: Range<u64>) -> Result<(), Error>;
102
103    /// Closes the block device. It is an error to continue using the device after this, but close
104    /// itself is idempotent.
105    async fn close(&self) -> Result<(), Error>;
106
107    /// Flush the device.
108    async fn flush(&self) -> Result<(), Error>;
109
110    /// Reopens the device, making it usable again. (Only implemented for testing devices.)
111    fn reopen(&self, _read_only: bool) {
112        unreachable!();
113    }
114    /// Returns whether the device is read-only.
115    fn is_read_only(&self) -> bool;
116
117    /// Returns whether the device supports trim.
118    fn supports_trim(&self) -> bool;
119
120    /// Returns a snapshot of the device.
121    fn snapshot(&self) -> Result<DeviceHolder, Error> {
122        bail!("Not supported");
123    }
124
125    /// Discards random blocks since the last flush.
126    fn discard_random_since_last_flush(&self) -> Result<(), Error> {
127        bail!("Not supported");
128    }
129
130    /// Poisons a device to panic on drop. Used to find hanging references.
131    fn poison(&self) -> Result<(), Error> {
132        bail!("Not supported");
133    }
134
135    /// Connects to the device's Mapper protocol.
136    #[cfg(target_os = "fuchsia")]
137    async fn connect_mapper(
138        &self,
139        server_end: fidl::endpoints::ServerEnd<fidl_fuchsia_storage_block::MapperMarker>,
140    ) -> Result<(), zx::Status> {
141        let _ = server_end.close_with_epitaph(zx::Status::NOT_SUPPORTED);
142        Err(zx::Status::NOT_SUPPORTED)
143    }
144}
145
146// Arc<dyn Device> can easily be cloned and supports concurrent access, but sometimes exclusive
147// access is required, in which case APIs should accept DeviceHolder.  It doesn't guarantee there
148// aren't some users that hold an Arc<dyn Device> somewhere, but it does mean that something that
149// accepts a DeviceHolder won't be sharing the device with something else that accepts a
150// DeviceHolder.  For example, FxFilesystem accepts a DeviceHolder which means that you cannot
151// create two FxFilesystem instances that are both sharing the same device.
152pub struct DeviceHolder {
153    device: ManuallyDrop<Arc<dyn Device>>,
154    on_drop: OnceLock<Sender<DeviceHolder>>,
155}
156
157impl DeviceHolder {
158    pub fn new(device: impl Device + 'static) -> Self {
159        DeviceHolder { device: ManuallyDrop::new(Arc::new(device)), on_drop: OnceLock::new() }
160    }
161
162    // Ensures there are no dangling references to the device. Useful for tests to ensure orderly
163    // shutdown.
164    pub fn ensure_unique(&self) {
165        assert_eq!(Arc::strong_count(&self.device), 1);
166    }
167
168    pub fn take_when_dropped(&self) -> impl Future<Output = DeviceHolder> + use<> {
169        let (sender, receiver) = channel::<DeviceHolder>();
170        self.on_drop
171            .set(sender)
172            .unwrap_or_else(|_| panic!("take_when_dropped should only be called once"));
173        async { receiver.await.unwrap() }
174    }
175}
176
177impl Drop for DeviceHolder {
178    fn drop(&mut self) {
179        if let Some(sender) = self.on_drop.take() {
180            // SAFETY: `device` is not used again.
181            let device = ManuallyDrop::new(unsafe { ManuallyDrop::take(&mut self.device) });
182            // We don't care if this fails to send.
183            let _ = sender.send(DeviceHolder { device, on_drop: OnceLock::new() });
184        } else {
185            // SAFETY: `device` is not used again.
186            unsafe { ManuallyDrop::drop(&mut self.device) }
187        }
188    }
189}
190
191impl Deref for DeviceHolder {
192    type Target = Arc<dyn Device>;
193
194    fn deref(&self) -> &Self::Target {
195        &self.device
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::DeviceHolder;
202    use crate::fake_device::FakeDevice;
203
204    #[fuchsia::test]
205    async fn test_take_when_dropped() {
206        let holder = DeviceHolder::new(FakeDevice::new(1, 512));
207        let fut = holder.take_when_dropped();
208        std::mem::drop(holder);
209        fut.await.ensure_unique();
210    }
211}