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