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