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