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