1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use {
    crate::object_store::{PosixAttributes, Timestamp},
    anyhow::{bail, Error},
    async_trait::async_trait,
    storage_device::buffer::{Buffer, BufferRef, MutableBufferRef},
};

// Some places use Default and assume that zero is an invalid object ID, so this cannot be changed
// easily.
pub const INVALID_OBJECT_ID: u64 = 0;

/// A handle for a generic object.  For objects with a data payload, use the ReadObjectHandle or
/// WriteObjectHandle traits.
pub trait ObjectHandle: Send + Sync + 'static {
    /// Returns the object identifier for this object which will be unique for the store that the
    /// object is contained in, but not necessarily unique within the entire system.
    fn object_id(&self) -> u64;

    // Returns the size of the object.
    fn get_size(&self) -> u64;

    /// Returns the filesystem block size, which should be at least as big as the device block size,
    /// but not necessarily the same.
    fn block_size(&self) -> u64;

    /// Allocates a buffer for doing I/O (read and write) for the object.
    fn allocate_buffer(&self, size: usize) -> Buffer<'_>;

    /// Sets tracing for this object.
    fn set_trace(&self, _v: bool) {}
}

#[derive(Clone, Debug, PartialEq)]
pub struct ObjectProperties {
    /// The number of references to this object.
    pub refs: u64,
    /// The number of bytes allocated to all extents across all attributes for this object.
    pub allocated_size: u64,
    /// The logical content size for the default data attribute of this object, i.e. the size of a
    /// file.  (Objects with no data attribute have size 0.)
    pub data_attribute_size: u64,
    /// The timestamp at which the object was created (i.e. crtime).
    pub creation_time: Timestamp,
    /// The timestamp at which the objects's data was last modified (i.e. mtime).
    pub modification_time: Timestamp,
    /// The number of sub-directories.
    pub sub_dirs: u64,
    // The POSIX attributes: mode, uid, gid, rdev
    pub posix_attributes: Option<PosixAttributes>,
}

#[async_trait]
pub trait GetProperties {
    /// Gets the object's properties.
    async fn get_properties(&self) -> Result<ObjectProperties, Error>;
}

#[async_trait]
pub trait ReadObjectHandle: ObjectHandle {
    /// Fills |buf| with up to |buf.len()| bytes read from |offset| on the underlying device.
    /// |offset| and |buf| must both be block-aligned.
    async fn read(&self, offset: u64, buf: MutableBufferRef<'_>) -> Result<usize, Error>;
}

#[async_trait]
pub trait WriteObjectHandle: ObjectHandle {
    /// Writes |buf.len())| bytes at |offset| (or the end of the file), returning the object size
    /// after writing.
    /// The writes may be cached, in which case a later call to |flush| is necessary to persist the
    /// writes.
    async fn write_or_append(&self, offset: Option<u64>, buf: BufferRef<'_>) -> Result<u64, Error>;

    /// Truncates the object to |size| bytes.
    /// The truncate may be cached, in which case a later call to |flush| is necessary to persist
    /// the truncate.
    async fn truncate(&self, size: u64) -> Result<(), Error>;

    /// Updates the timestamps for the object.
    /// The truncate may be cached, in which case a later call to |flush| is necessary to persist
    /// the truncate.
    async fn write_timestamps<'a>(
        &'a self,
        crtime: Option<Timestamp>,
        mtime: Option<Timestamp>,
    ) -> Result<(), Error>;

    /// Flushes all pending data and metadata updates for the object.
    async fn flush(&self) -> Result<(), Error>;
}

#[async_trait]
pub trait ObjectHandleExt: ReadObjectHandle {
    // Returns the contents of the object. The object must be < |limit| bytes in size.
    async fn contents(&self, limit: usize) -> Result<Box<[u8]>, Error> {
        let size = self.get_size();
        if size > limit as u64 {
            bail!("Object too big ({} > {})", size, limit);
        }
        let mut buf = self.allocate_buffer(size as usize);
        self.read(0u64, buf.as_mut()).await?;
        let mut vec = vec![0; size as usize];
        vec.copy_from_slice(&buf.as_slice());
        Ok(vec.into())
    }
}

#[async_trait]
impl<T: ReadObjectHandle + ?Sized> ObjectHandleExt for T {}

#[async_trait]
/// This trait is an asynchronous streaming writer.
pub trait WriteBytes {
    fn handle(&self) -> &dyn WriteObjectHandle;

    /// Buffers writes to be written to the underlying handle. This may flush bytes immediately
    /// or when buffers are full.
    async fn write_bytes(&mut self, buf: &[u8]) -> Result<(), Error>;

    /// Called to flush to the handle.  Named to avoid confluct with the flush method above.
    async fn complete(&mut self) -> Result<(), Error>;

    /// Moves the offset forward by `amount`, which will result in zeroes in the output stream, even
    /// if no other data is appended to it.
    async fn skip(&mut self, amount: u64) -> Result<(), Error>;
}