vfs/
file.rs

1// Copyright 2019 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//! Module holding different kinds of files and their building blocks.
6
7use crate::execution_scope::ExecutionScope;
8use crate::node::Node;
9use crate::object_request::{ObjectRequestRef, ToObjectRequest as _};
10use crate::protocols::ProtocolsExt;
11use fidl_fuchsia_io as fio;
12use std::future::{Future, ready};
13use std::sync::Arc;
14use zx_status::Status;
15
16/// File nodes backed by VMOs.
17#[cfg(target_os = "fuchsia")]
18pub mod vmo;
19
20#[cfg(not(target_os = "fuchsia"))]
21pub mod simple;
22
23mod common;
24
25pub mod connection;
26
27pub use connection::{FidlIoConnection, RawIoConnection};
28
29#[cfg(target_os = "fuchsia")]
30pub use connection::{GetVmo, StreamIoConnection};
31
32/// Creates a new read-only `SimpleFile` with the specified `content`.
33///
34/// ## Examples
35/// ```
36/// // Using static data:
37/// let from_str = read_only("str");
38/// let from_bytes = read_only(b"bytes");
39/// // Using owned data:
40/// let from_string = read_only(String::from("owned"));
41/// let from_vec = read_only(vec![0u8; 2]);
42/// ```
43#[cfg(not(target_os = "fuchsia"))]
44pub fn read_only(content: impl AsRef<[u8]>) -> Arc<simple::SimpleFile> {
45    simple::SimpleFile::read_only(content)
46}
47
48#[cfg(target_os = "fuchsia")]
49pub use vmo::read_only;
50
51/// FileOptions include options that are relevant after the file has been opened. Flags like
52/// `TRUNCATE`, which only applies during open time, are not included.
53#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
54pub struct FileOptions {
55    pub rights: fio::Operations,
56    pub is_append: bool,
57    #[cfg(fuchsia_api_level_at_least = "HEAD")]
58    pub is_linkable: bool,
59}
60
61impl FileOptions {
62    /// Converts to `StreamOptions`.
63    #[cfg(target_os = "fuchsia")]
64    pub fn to_stream_options(&self) -> zx::StreamOptions {
65        let mut options = zx::StreamOptions::empty();
66        if self.rights.contains(fio::Operations::READ_BYTES) {
67            options |= zx::StreamOptions::MODE_READ;
68        }
69        if self.rights.contains(fio::Operations::WRITE_BYTES) {
70            options |= zx::StreamOptions::MODE_WRITE;
71        }
72        if self.is_append {
73            options |= zx::StreamOptions::MODE_APPEND;
74        }
75        options
76    }
77
78    pub(crate) fn to_io1(&self) -> fio::OpenFlags {
79        let mut flags = fio::OpenFlags::empty();
80        if self.rights.contains(fio::Operations::READ_BYTES) {
81            flags |= fio::OpenFlags::RIGHT_READABLE;
82        }
83        if self.rights.contains(fio::Operations::WRITE_BYTES) {
84            flags |= fio::OpenFlags::RIGHT_WRITABLE;
85        }
86        if self.rights.contains(fio::Operations::EXECUTE) {
87            flags |= fio::OpenFlags::RIGHT_EXECUTABLE;
88        }
89        if self.is_append {
90            flags |= fio::OpenFlags::APPEND;
91        }
92        flags
93    }
94}
95
96impl From<&FileOptions> for fio::Flags {
97    fn from(options: &FileOptions) -> Self {
98        // There is 1:1 mapping between `fio::Operations` and `fio::Flags`.
99        let mut flags = fio::Flags::from_bits_truncate(options.rights.bits());
100        if options.is_append {
101            flags |= fio::Flags::FILE_APPEND;
102        }
103        flags | fio::Flags::PROTOCOL_FILE
104    }
105}
106
107#[derive(Default, PartialEq)]
108pub enum SyncMode {
109    /// Used when the Sync fuchsia.io method is used.
110    #[default]
111    Normal,
112
113    /// Used when the connection is about to be closed. Typically this will involve flushing data
114    /// from caches, but performance is a consideration, so it should only perform what might be
115    /// necessary for closing the file. If anything *must* happen when a file is closed, it must be
116    /// implemented in the `Node::close` function, not here; a call to sync with this mode is not
117    /// guaranteed and not implementing/supporting it should have no effect on correctness. If
118    /// `Node::close` needs to flush data in an async context, it has to spawn a task.  Supporting
119    /// this mode means that in most cases there's no need to spawn a task because there should be
120    /// nothing that needs to be flushed (but it must check). This will only be called if the
121    /// connection has write permissions; a connection that only has read permissions should not
122    /// have made any changes that need flushing.
123    PreClose,
124}
125
126/// Trait used for all files.
127pub trait File: Node {
128    /// Capabilities:
129    fn readable(&self) -> bool {
130        true
131    }
132    fn writable(&self) -> bool {
133        false
134    }
135    fn executable(&self) -> bool {
136        false
137    }
138
139    /// Called when the file is going to be accessed, typically by a new connection.
140    /// Flags is the same as the flags passed to `fidl_fuchsia_io.Node/Open`.
141    /// The following flags are handled by the connection and do not need to be handled inside
142    /// open():
143    /// * OPEN_FLAG_TRUNCATE - A call to truncate() will be made immediately after open().
144    /// * OPEN_FLAG_DESCRIBE - The OnOpen event is sent before any other requests are received from
145    /// the file's client.
146    fn open_file(&self, options: &FileOptions) -> impl Future<Output = Result<(), Status>> + Send;
147
148    /// Truncate the file to `length`.
149    /// If there are pending attributes to update (see `update_attributes`), they should also be
150    /// flushed at this time. Otherwise, no attributes should be updated, other than size as needed.
151    fn truncate(&self, length: u64) -> impl Future<Output = Result<(), Status>> + Send;
152
153    /// Get a VMO representing this file.
154    /// If not supported by the underlying filesystem, should return Err(NOT_SUPPORTED).
155    #[cfg(target_os = "fuchsia")]
156    fn get_backing_memory(
157        &self,
158        _flags: fio::VmoFlags,
159    ) -> impl Future<Output = Result<zx::Vmo, Status>> + Send {
160        ready(Err(Status::NOT_SUPPORTED))
161    }
162
163    /// Get the size of this file.
164    /// This is used to calculate seek offset relative to the end.
165    fn get_size(&self) -> impl Future<Output = Result<u64, Status>> + Send;
166
167    /// Set the mutable attributes of this file based on the values in `attributes`. If the file
168    /// does not support updating *all* of the specified attributes, implementations should fail
169    /// with `ZX_ERR_NOT_SUPPORTED`.
170    fn update_attributes(
171        &self,
172        attributes: fio::MutableNodeAttributes,
173    ) -> impl Future<Output = Result<(), Status>> + Send;
174
175    /// Preallocate disk space for this range.
176    #[cfg(fuchsia_api_level_at_least = "HEAD")]
177    fn allocate(
178        &self,
179        _offset: u64,
180        _length: u64,
181        _mode: fio::AllocateMode,
182    ) -> impl Future<Output = Result<(), Status>> + Send {
183        ready(Err(Status::NOT_SUPPORTED))
184    }
185
186    /// Set the merkle tree and the descriptor for this file and mark the file as fsverity-enabled.
187    #[cfg(fuchsia_api_level_at_least = "HEAD")]
188    fn enable_verity(
189        &self,
190        _options: fio::VerificationOptions,
191    ) -> impl Future<Output = Result<(), Status>> + Send {
192        ready(Err(Status::NOT_SUPPORTED))
193    }
194
195    /// Sync this file's contents to the storage medium (probably disk).
196    /// This does not necessarily guarantee that the file will be completely written to disk once
197    /// the call returns. It merely guarantees that any changes to the file have been propagated
198    /// to the next layer in the storage stack.
199    fn sync(&self, mode: SyncMode) -> impl Future<Output = Result<(), Status>> + Send;
200}
201
202// Trait for handling reads and writes to a file. Files that support Streams should handle reads and
203// writes via a Pager instead of implementing this trait.
204pub trait FileIo: Send + Sync {
205    /// Read at most |buffer.len()| bytes starting at |offset| into |buffer|. The function may read
206    /// less than |count| bytes and still return success, in which case read_at returns the number
207    /// of bytes read into |buffer|.
208    fn read_at(
209        &self,
210        offset: u64,
211        buffer: &mut [u8],
212    ) -> impl Future<Output = Result<u64, Status>> + Send;
213
214    /// Write |content| starting at |offset|, returning the number of bytes that were successfully
215    /// written.
216    ///
217
218    /// If there are pending attributes to update (see `update_attributes`), they should also be
219    /// flushed at this time. Otherwise, no attributes should be updated, other than size as needed.
220    fn write_at(
221        &self,
222        offset: u64,
223        content: &[u8],
224    ) -> impl Future<Output = Result<u64, Status>> + Send;
225
226    /// Appends |content| returning, if successful, the number of bytes written, and the file offset
227    /// after writing.  Implementations should make the writes atomic, so in the event that multiple
228    /// requests to append are in-flight, it should appear that the two writes are applied in
229    /// sequence.
230    ///
231    /// If there are pending attributes to update (see `update_attributes`), they should also be
232    /// flushed at this time. Otherwise, no attributes should be updated, other than size as needed.
233    fn append(&self, content: &[u8]) -> impl Future<Output = Result<(u64, u64), Status>> + Send;
234}
235
236/// Trait for dispatching read, write, and seek FIDL requests for a given connection. The
237/// implementer of this trait is responsible for maintaining the per connection state.
238///
239/// Files that support Streams should handle reads and writes via a Pager instead of implementing
240/// this trait.
241pub trait RawFileIoConnection: Send + Sync {
242    /// Reads at most `count` bytes from the file starting at the connection's seek offset and
243    /// advances the seek offset.
244    fn read(&self, count: u64) -> impl Future<Output = Result<Vec<u8>, Status>> + Send;
245
246    /// Reads `count` bytes from the file starting at `offset`.
247    fn read_at(
248        &self,
249        offset: u64,
250        count: u64,
251    ) -> impl Future<Output = Result<Vec<u8>, Status>> + Send;
252
253    /// Writes `data` to the file starting at the connect's seek offset and advances the seek
254    /// offset. If the connection is in append mode then the seek offset is moved to the end of the
255    /// file before writing. Returns the number of bytes written.
256    fn write(&self, data: &[u8]) -> impl Future<Output = Result<u64, Status>> + Send;
257
258    /// Writes `data` to the file starting at `offset`. Returns the number of bytes written.
259    fn write_at(
260        &self,
261        offset: u64,
262        data: &[u8],
263    ) -> impl Future<Output = Result<u64, Status>> + Send;
264
265    /// Modifies the connection's seek offset. Returns the connections new seek offset.
266    fn seek(
267        &self,
268        offset: i64,
269        origin: fio::SeekOrigin,
270    ) -> impl Future<Output = Result<u64, Status>> + Send;
271
272    /// Notifies the `IoOpHandler` that the flags of the connection have changed.
273    fn set_flags(&self, flags: fio::Flags) -> Result<(), Status>;
274}
275
276pub trait FileLike: Node {
277    fn open(
278        self: Arc<Self>,
279        scope: ExecutionScope,
280        options: FileOptions,
281        object_request: ObjectRequestRef<'_>,
282    ) -> Result<(), Status>;
283}
284
285/// Helper to open a file or node as required.
286pub fn serve(
287    file: Arc<impl FileLike>,
288    scope: ExecutionScope,
289    protocols: &impl ProtocolsExt,
290    object_request: ObjectRequestRef<'_>,
291) -> Result<(), Status> {
292    if protocols.is_node() {
293        let options = protocols.to_node_options(file.entry_info().type_())?;
294        file.open_as_node(scope, options, object_request)
295    } else {
296        file.open(scope, protocols.to_file_options()?, object_request)
297    }
298}
299
300/// Serve the provided file object on a new execution scope with `flags`.
301pub fn serve_proxy(file: Arc<impl FileLike>, flags: fio::Flags) -> fio::FileProxy {
302    let (proxy, server) = fidl::endpoints::create_proxy::<fio::FileMarker>();
303    let request = flags.to_object_request(server);
304    request.handle(|request| serve(file, ExecutionScope::new(), &flags, request));
305    proxy
306}