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