Skip to main content

fxfs/
object_handle.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
5use crate::object_store::{DirType, PosixAttributes, Timestamp};
6use anyhow::Error;
7use async_trait::async_trait;
8use std::future::Future;
9use std::sync::Arc;
10use storage_device::buffer::{BufferFuture, BufferRef, MutableBufferRef};
11use storage_units::BlockSize;
12
13// Some places use Default and assume that zero is an invalid object ID, so this cannot be changed
14// easily.
15pub const INVALID_OBJECT_ID: u64 = 0;
16
17/// A handle for a generic object.  For objects with a data payload, use the ReadObjectHandle or
18/// WriteObjectHandle traits.
19pub trait ObjectHandle: Send + Sync + 'static {
20    /// Returns the object identifier for this object which will be unique for the store that the
21    /// object is contained in, but not necessarily unique within the entire system.
22    fn object_id(&self) -> u64;
23
24    /// Returns the filesystem block size, which should be at least as big as the device block size,
25    /// but not necessarily the same.
26    fn block_size(&self) -> BlockSize;
27
28    /// Allocates a buffer for doing I/O (read and write) for the object.
29    fn allocate_buffer(&self, size: usize) -> BufferFuture<'_>;
30
31    /// Sets tracing for this object.
32    fn set_trace(&self, _v: bool) {}
33}
34
35#[derive(Clone, Debug, PartialEq)]
36pub struct ObjectProperties {
37    /// The number of references to this object.
38    pub refs: u64,
39    /// The number of bytes allocated to all extents across all attributes for this object.
40    pub allocated_size: u64,
41    /// The logical content size for the default data attribute of this object, i.e. the size of a
42    /// file.  (Objects with no data attribute have size 0.)
43    pub data_attribute_size: u64,
44    /// The timestamp at which the object was created (i.e. crtime).
45    pub creation_time: Timestamp,
46    /// The timestamp at which the objects's data was last modified (i.e. mtime).
47    pub modification_time: Timestamp,
48    /// The timestamp at which the object was last read (i.e. atime).
49    pub access_time: Timestamp,
50    /// The timestamp at which the object's status was last modified (i.e. ctime).
51    pub change_time: Timestamp,
52    /// The number of sub-directories.
53    pub sub_dirs: u64,
54    /// POSIX attributes: mode, uid, gid, rdev
55    pub posix_attributes: Option<PosixAttributes>,
56    /// The type of directory (encryption, casefolding, etc.)
57    pub dir_type: DirType,
58}
59
60#[async_trait]
61pub trait ReadObjectHandle: ObjectHandle {
62    /// Fills `buf` with bytes read from `offset` on the underlying device.
63    ///
64    /// Both `offset` and `buf.len()` must be aligned to the object's `block_size()`.
65    ///
66    /// Returns the number of bytes read. If `offset >= size`, returns 0. Holes/sparse extents
67    /// within the read range are zero-filled. Callers should not make any assumptions about the
68    /// contents of the buffer past the returned read amount.
69    async fn read_aligned(&self, offset: u64, buf: MutableBufferRef<'_>) -> Result<usize, Error>;
70
71    /// Returns the size of the object.
72    fn get_size(&self) -> u64;
73}
74
75pub trait WriteObjectHandle: ObjectHandle {
76    /// Writes |buf.len())| bytes at |offset| (or the end of the file), returning the object size
77    /// after writing.
78    /// The writes may be cached, in which case a later call to |flush| is necessary to persist the
79    /// writes.
80    fn write_or_append(
81        &self,
82        offset: Option<u64>,
83        buf: BufferRef<'_>,
84    ) -> impl Future<Output = Result<u64, Error>> + Send;
85
86    /// Truncates the object to |size| bytes.
87    /// The truncate may be cached, in which case a later call to |flush| is necessary to persist
88    /// the truncate.
89    fn truncate(&self, size: u64) -> impl Future<Output = Result<(), Error>> + Send;
90
91    /// Flushes all pending data and metadata updates for the object.
92    fn flush(&self) -> impl Future<Output = Result<(), Error>> + Send;
93}
94
95/// This trait is an asynchronous streaming writer.
96pub trait WriteBytes: Sized {
97    fn block_size(&self) -> BlockSize;
98
99    /// Buffers writes to be written to the underlying handle. This may flush bytes immediately
100    /// or when buffers are full.
101    fn write_bytes(&mut self, buf: &[u8]) -> impl Future<Output = Result<(), Error>> + Send;
102
103    /// Called to flush to the handle. The total number of bytes written is returned.
104    fn complete(self) -> impl Future<Output = Result<u64, Error>> + Send;
105
106    /// Moves the offset forward by `amount`, which will result in zeroes in the output stream, even
107    /// if no other data is appended to it.
108    fn skip(&mut self, amount: u64) -> impl Future<Output = Result<(), Error>> + Send;
109}
110
111impl LayerObject for dyn ReadObjectHandle + '_ {}
112
113/// A handle for reading layer objects.
114#[async_trait]
115pub trait LayerObject: ReadObjectHandle {
116    /// Returns a memory-mapped slice of the entire layer file if supported (e.g. when backed by a
117    /// pager-managed VMO).
118    fn as_slice(&self) -> Option<&[u8]> {
119        None
120    }
121
122    /// Returns true if an underlying I/O error occurred while paging in data for this object.
123    fn has_io_error(&self) -> bool {
124        false
125    }
126
127    /// Requests that cached data (such as paged-in pages) for this object be purged.
128    fn purge_cached_data(&self) {}
129
130    /// Called when the layer is closed to release any external resources (such as pager
131    /// registrations).
132    async fn close(&self) {}
133}
134
135impl<T: ObjectHandle + ?Sized> ObjectHandle for Arc<T> {
136    fn object_id(&self) -> u64 {
137        (**self).object_id()
138    }
139
140    fn block_size(&self) -> BlockSize {
141        (**self).block_size()
142    }
143
144    fn allocate_buffer(&self, size: usize) -> BufferFuture<'_> {
145        (**self).allocate_buffer(size)
146    }
147
148    fn set_trace(&self, v: bool) {
149        (**self).set_trace(v)
150    }
151}
152
153#[async_trait]
154impl<T: ReadObjectHandle + ?Sized> ReadObjectHandle for Arc<T> {
155    async fn read_aligned(&self, offset: u64, buf: MutableBufferRef<'_>) -> Result<usize, Error> {
156        (**self).read_aligned(offset, buf).await
157    }
158
159    fn get_size(&self) -> u64 {
160        (**self).get_size()
161    }
162}
163
164#[async_trait]
165impl<T: LayerObject + ?Sized> LayerObject for Arc<T> {
166    fn as_slice(&self) -> Option<&[u8]> {
167        (**self).as_slice()
168    }
169
170    fn has_io_error(&self) -> bool {
171        (**self).has_io_error()
172    }
173
174    fn purge_cached_data(&self) {
175        (**self).purge_cached_data()
176    }
177
178    async fn close(&self) {
179        (**self).close().await
180    }
181}
182
183impl<T: ObjectHandle + ?Sized> ObjectHandle for Box<T> {
184    fn object_id(&self) -> u64 {
185        (**self).object_id()
186    }
187
188    fn block_size(&self) -> BlockSize {
189        (**self).block_size()
190    }
191
192    fn allocate_buffer(&self, size: usize) -> BufferFuture<'_> {
193        (**self).allocate_buffer(size)
194    }
195
196    fn set_trace(&self, v: bool) {
197        (**self).set_trace(v)
198    }
199}
200
201#[async_trait]
202impl<T: ReadObjectHandle + ?Sized> ReadObjectHandle for Box<T> {
203    async fn read_aligned(&self, offset: u64, buf: MutableBufferRef<'_>) -> Result<usize, Error> {
204        (**self).read_aligned(offset, buf).await
205    }
206
207    fn get_size(&self) -> u64 {
208        (**self).get_size()
209    }
210}
211
212#[async_trait]
213impl<T: LayerObject + ?Sized> LayerObject for Box<T> {
214    fn as_slice(&self) -> Option<&[u8]> {
215        (**self).as_slice()
216    }
217
218    fn has_io_error(&self) -> bool {
219        (**self).has_io_error()
220    }
221
222    fn purge_cached_data(&self) {
223        (**self).purge_cached_data()
224    }
225
226    async fn close(&self) {
227        (**self).close().await
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234    use crate::testing::fake_object::{FakeObject, FakeObjectHandle};
235
236    #[fuchsia::test]
237    async fn test_object_handle_and_layer_object_defaults() {
238        let object = Arc::new(FakeObject::new());
239        let handle = FakeObjectHandle::new(object);
240
241        handle.set_trace(true);
242        assert_eq!(handle.as_slice(), None);
243        assert!(!handle.has_io_error());
244        handle.purge_cached_data();
245        handle.close().await;
246
247        let dyn_read: &dyn ReadObjectHandle = &handle;
248        assert_eq!(dyn_read.as_slice(), None);
249        assert!(!dyn_read.has_io_error());
250        dyn_read.purge_cached_data();
251        dyn_read.close().await;
252    }
253
254    #[fuchsia::test]
255    async fn test_arc_forwarding_impls() {
256        let object = Arc::new(FakeObject::new());
257        let arc_handle: Arc<FakeObjectHandle> = Arc::new(FakeObjectHandle::new(object));
258
259        assert_eq!(arc_handle.object_id(), 0);
260        assert_eq!(arc_handle.block_size(), BlockSize::SIZE_512B);
261        arc_handle.set_trace(false);
262
263        let mut buf = arc_handle.allocate_buffer(512).await;
264        assert_eq!(arc_handle.get_size(), 0);
265        assert_eq!(arc_handle.read_aligned(0, buf.as_mut()).await.unwrap(), 0);
266
267        assert_eq!(arc_handle.as_slice(), None);
268        assert!(!arc_handle.has_io_error());
269        arc_handle.purge_cached_data();
270        arc_handle.close().await;
271    }
272
273    #[fuchsia::test]
274    async fn test_box_forwarding_impls() {
275        let object = Arc::new(FakeObject::new());
276        let box_handle: Box<FakeObjectHandle> = Box::new(FakeObjectHandle::new(object));
277
278        assert_eq!(box_handle.object_id(), 0);
279        assert_eq!(box_handle.block_size(), BlockSize::SIZE_512B);
280        box_handle.set_trace(true);
281
282        let mut buf = box_handle.allocate_buffer(512).await;
283        assert_eq!(box_handle.get_size(), 0);
284        assert_eq!(box_handle.read_aligned(0, buf.as_mut()).await.unwrap(), 0);
285
286        assert_eq!(box_handle.as_slice(), None);
287        assert!(!box_handle.has_io_error());
288        box_handle.purge_cached_data();
289        box_handle.close().await;
290    }
291}