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