vfs/file/
vmo.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//! Implementation of a file backed by a VMO buffer shared by all the file connections.
6
7#[cfg(test)]
8mod tests;
9
10use crate::directory::entry::{DirectoryEntry, EntryInfo, GetEntryInfo, OpenRequest};
11use crate::execution_scope::ExecutionScope;
12use crate::file::common::vmo_flags_to_rights;
13use crate::file::{File, FileLike, FileOptions, GetVmo, StreamIoConnection, SyncMode};
14use crate::node::Node;
15use crate::ObjectRequestRef;
16use fidl_fuchsia_io as fio;
17use std::sync::Arc;
18use zx::{self as zx, HandleBased as _, Status, Vmo};
19
20/// Creates a new read-only `VmoFile` with the specified `content`.
21///
22/// ## Panics
23///
24/// This function panics if a VMO could not be created, or if `content` could not be written to the
25/// VMO.
26///
27/// ## Examples
28/// ```
29/// // Using static data:
30/// let from_str = read_only("str");
31/// let from_bytes = read_only(b"bytes");
32/// // Using owned data:
33/// let from_string = read_only(String::from("owned"));
34/// let from_vec = read_only(vec![0u8; 2]);
35/// ```
36pub fn read_only(content: impl AsRef<[u8]>) -> Arc<VmoFile> {
37    let bytes: &[u8] = content.as_ref();
38    let vmo = Vmo::create(bytes.len().try_into().unwrap()).unwrap();
39    if bytes.len() > 0 {
40        vmo.write(bytes, 0).unwrap();
41    }
42    VmoFile::new(vmo)
43}
44
45/// Implementation of a VMO-backed file in a virtual file system.
46pub struct VmoFile {
47    /// Specifies if the file can be opened as executable.
48    executable: bool,
49
50    /// Specifies the inode for this file. Can be [`fio::INO_UNKNOWN`] if not required.
51    inode: u64,
52
53    /// Vmo that backs the file.
54    vmo: Vmo,
55}
56
57unsafe impl Sync for VmoFile {}
58
59impl VmoFile {
60    /// Create a new VmoFile which is backed by an existing Vmo.
61    ///
62    /// # Arguments
63    ///
64    /// * `vmo` - Vmo backing this file object.
65    pub fn new(vmo: zx::Vmo) -> Arc<Self> {
66        Self::new_with_inode(vmo, fio::INO_UNKNOWN)
67    }
68
69    /// Create a new VmoFile with a specified inode value.
70    ///
71    /// # Arguments
72    ///
73    /// * `vmo` - Vmo backing this file object.
74    /// * `inode` - Inode value to report when getting the VmoFile's attributes.
75    pub fn new_with_inode(vmo: zx::Vmo, inode: u64) -> Arc<Self> {
76        Self::new_with_inode_and_executable(vmo, inode, false)
77    }
78
79    /// Create a new VmoFile with specified inode and executable values.
80    ///
81    /// # Arguments
82    ///
83    /// * `vmo` - Vmo backing this file object.
84    /// * `inode` - Inode value to report when getting the VmoFile's attributes.
85    /// * `executable` - If true, allow connections with OpenFlags::PERM_EXECUTE.
86    pub fn new_with_inode_and_executable(vmo: zx::Vmo, inode: u64, executable: bool) -> Arc<Self> {
87        Arc::new(VmoFile { executable, inode, vmo })
88    }
89}
90
91impl FileLike for VmoFile {
92    fn open(
93        self: Arc<Self>,
94        scope: ExecutionScope,
95        options: FileOptions,
96        object_request: ObjectRequestRef<'_>,
97    ) -> Result<(), Status> {
98        StreamIoConnection::create_sync(scope, self, options, object_request.take());
99        Ok(())
100    }
101}
102
103impl GetEntryInfo for VmoFile {
104    fn entry_info(&self) -> EntryInfo {
105        EntryInfo::new(self.inode, fio::DirentType::File)
106    }
107}
108
109impl DirectoryEntry for VmoFile {
110    fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
111        request.open_file(self)
112    }
113}
114
115impl Node for VmoFile {
116    async fn get_attributes(
117        &self,
118        requested_attributes: fio::NodeAttributesQuery,
119    ) -> Result<fio::NodeAttributes2, Status> {
120        let content_size = if requested_attributes.intersects(
121            fio::NodeAttributesQuery::CONTENT_SIZE.union(fio::NodeAttributesQuery::STORAGE_SIZE),
122        ) {
123            Some(self.vmo.get_content_size()?)
124        } else {
125            None
126        };
127        let mut abilities = fio::Operations::GET_ATTRIBUTES | fio::Operations::READ_BYTES;
128        if self.executable {
129            abilities |= fio::Operations::EXECUTE
130        }
131        Ok(immutable_attributes!(
132            requested_attributes,
133            Immutable {
134                protocols: fio::NodeProtocolKinds::FILE,
135                abilities: abilities,
136                content_size: content_size,
137                storage_size: content_size,
138                id: self.inode,
139            }
140        ))
141    }
142}
143
144// Required by `StreamIoConnection`.
145impl GetVmo for VmoFile {
146    fn get_vmo(&self) -> &zx::Vmo {
147        &self.vmo
148    }
149}
150
151impl File for VmoFile {
152    fn readable(&self) -> bool {
153        true
154    }
155
156    fn writable(&self) -> bool {
157        false
158    }
159
160    fn executable(&self) -> bool {
161        self.executable
162    }
163
164    async fn open_file(&self, _options: &FileOptions) -> Result<(), Status> {
165        Ok(())
166    }
167
168    async fn truncate(&self, _length: u64) -> Result<(), Status> {
169        Err(Status::NOT_SUPPORTED)
170    }
171
172    async fn get_backing_memory(&self, flags: fio::VmoFlags) -> Result<zx::Vmo, Status> {
173        // Logic here matches fuchsia.io requirements and matches what works for memfs.
174        // Shared requests are satisfied by duplicating an handle, and private shares are
175        // child VMOs.
176        let vmo_rights = vmo_flags_to_rights(flags)
177            | zx::Rights::BASIC
178            | zx::Rights::MAP
179            | zx::Rights::GET_PROPERTY;
180        // Unless private sharing mode is specified, we always default to shared.
181        if flags.contains(fio::VmoFlags::PRIVATE_CLONE) {
182            get_as_private(&self.vmo, vmo_rights)
183        } else {
184            self.vmo.duplicate_handle(vmo_rights)
185        }
186    }
187
188    async fn get_size(&self) -> Result<u64, Status> {
189        Ok(self.vmo.get_content_size()?)
190    }
191
192    async fn update_attributes(
193        &self,
194        _attributes: fio::MutableNodeAttributes,
195    ) -> Result<(), Status> {
196        Err(Status::NOT_SUPPORTED)
197    }
198
199    async fn sync(&self, _mode: SyncMode) -> Result<(), Status> {
200        Ok(())
201    }
202}
203
204fn get_as_private(vmo: &zx::Vmo, mut rights: zx::Rights) -> Result<zx::Vmo, zx::Status> {
205    // SNAPSHOT_AT_LEAST_ON_WRITE removes ZX_RIGHT_EXECUTE even if the parent VMO has it, adding
206    // CHILD_NO_WRITE will ensure EXECUTE is maintained.
207    const CHILD_OPTIONS: zx::VmoChildOptions =
208        zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE.union(zx::VmoChildOptions::NO_WRITE);
209
210    // Allow for the child VMO's content size and name to be changed.
211    rights |= zx::Rights::SET_PROPERTY;
212
213    let size = vmo.get_content_size()?;
214    let new_vmo = vmo.create_child(CHILD_OPTIONS, 0, size)?;
215    new_vmo.replace_handle(rights)
216}