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, true, false, false)
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    /// * `readable` - Must be `true`, VmoFile needs to be readable.
66    /// * `writable` - Must be `false`, VmoFile no longer supports writing.
67    /// * `executable` - If true, allow connections with OpenFlags::RIGHT_EXECUTABLE.
68    pub fn new(vmo: zx::Vmo, readable: bool, writable: bool, executable: bool) -> Arc<Self> {
69        // TODO(https://fxbug.dev/294078001) Remove the readable and writable arguments.
70        assert!(readable, "VmoFile must be readable");
71        assert!(!writable, "VmoFile no longer supports writing");
72        Self::new_with_inode(vmo, readable, writable, executable, fio::INO_UNKNOWN)
73    }
74
75    /// Create a new VmoFile with the specified options and inode value.
76    ///
77    /// # Arguments
78    ///
79    /// * `vmo` - Vmo backing this file object.
80    /// * `readable` - Must be `true`, VmoFile needs to be readable.
81    /// * `writable` - Must be `false`, VmoFile no longer supports writing.
82    /// * `executable` - If true, allow connections with OpenFlags::RIGHT_EXECUTABLE.
83    /// * `inode` - Inode value to report when getting the VmoFile's attributes.
84    pub fn new_with_inode(
85        vmo: zx::Vmo,
86        readable: bool,
87        writable: bool,
88        executable: bool,
89        inode: u64,
90    ) -> Arc<Self> {
91        // TODO(https://fxbug.dev/294078001) Remove the readable and writable arguments.
92        assert!(readable, "VmoFile must be readable");
93        assert!(!writable, "VmoFile no longer supports writing");
94        Arc::new(VmoFile { executable, inode, vmo })
95    }
96}
97
98impl FileLike for VmoFile {
99    fn open(
100        self: Arc<Self>,
101        scope: ExecutionScope,
102        options: FileOptions,
103        object_request: ObjectRequestRef<'_>,
104    ) -> Result<(), Status> {
105        StreamIoConnection::create_sync(scope, self, options, object_request.take());
106        Ok(())
107    }
108}
109
110impl GetEntryInfo for VmoFile {
111    fn entry_info(&self) -> EntryInfo {
112        EntryInfo::new(self.inode, fio::DirentType::File)
113    }
114}
115
116impl DirectoryEntry for VmoFile {
117    fn open_entry(self: Arc<Self>, request: OpenRequest<'_>) -> Result<(), Status> {
118        request.open_file(self)
119    }
120}
121
122impl Node for VmoFile {
123    async fn get_attributes(
124        &self,
125        requested_attributes: fio::NodeAttributesQuery,
126    ) -> Result<fio::NodeAttributes2, Status> {
127        let content_size = if requested_attributes.intersects(
128            fio::NodeAttributesQuery::CONTENT_SIZE.union(fio::NodeAttributesQuery::STORAGE_SIZE),
129        ) {
130            Some(self.vmo.get_content_size()?)
131        } else {
132            None
133        };
134        let mut abilities = fio::Operations::GET_ATTRIBUTES | fio::Operations::READ_BYTES;
135        if self.executable {
136            abilities |= fio::Operations::EXECUTE
137        }
138        Ok(immutable_attributes!(
139            requested_attributes,
140            Immutable {
141                protocols: fio::NodeProtocolKinds::FILE,
142                abilities: abilities,
143                content_size: content_size,
144                storage_size: content_size,
145                id: self.inode,
146            }
147        ))
148    }
149}
150
151// Required by `StreamIoConnection`.
152impl GetVmo for VmoFile {
153    fn get_vmo(&self) -> &zx::Vmo {
154        &self.vmo
155    }
156}
157
158impl File for VmoFile {
159    fn readable(&self) -> bool {
160        true
161    }
162
163    fn writable(&self) -> bool {
164        false
165    }
166
167    fn executable(&self) -> bool {
168        self.executable
169    }
170
171    async fn open_file(&self, _options: &FileOptions) -> Result<(), Status> {
172        Ok(())
173    }
174
175    async fn truncate(&self, _length: u64) -> Result<(), Status> {
176        Err(Status::NOT_SUPPORTED)
177    }
178
179    async fn get_backing_memory(&self, flags: fio::VmoFlags) -> Result<zx::Vmo, Status> {
180        // Logic here matches fuchsia.io requirements and matches what works for memfs.
181        // Shared requests are satisfied by duplicating an handle, and private shares are
182        // child VMOs.
183        let vmo_rights = vmo_flags_to_rights(flags)
184            | zx::Rights::BASIC
185            | zx::Rights::MAP
186            | zx::Rights::GET_PROPERTY;
187        // Unless private sharing mode is specified, we always default to shared.
188        if flags.contains(fio::VmoFlags::PRIVATE_CLONE) {
189            get_as_private(&self.vmo, vmo_rights)
190        } else {
191            self.vmo.duplicate_handle(vmo_rights)
192        }
193    }
194
195    async fn get_size(&self) -> Result<u64, Status> {
196        Ok(self.vmo.get_content_size()?)
197    }
198
199    async fn update_attributes(
200        &self,
201        _attributes: fio::MutableNodeAttributes,
202    ) -> Result<(), Status> {
203        Err(Status::NOT_SUPPORTED)
204    }
205
206    async fn sync(&self, _mode: SyncMode) -> Result<(), Status> {
207        Ok(())
208    }
209}
210
211fn get_as_private(vmo: &zx::Vmo, mut rights: zx::Rights) -> Result<zx::Vmo, zx::Status> {
212    // SNAPSHOT_AT_LEAST_ON_WRITE removes ZX_RIGHT_EXECUTE even if the parent VMO has it, adding
213    // CHILD_NO_WRITE will ensure EXECUTE is maintained.
214    const CHILD_OPTIONS: zx::VmoChildOptions =
215        zx::VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE.union(zx::VmoChildOptions::NO_WRITE);
216
217    // Allow for the child VMO's content size and name to be changed.
218    rights |= zx::Rights::SET_PROPERTY;
219
220    let size = vmo.get_content_size()?;
221    let new_vmo = vmo.create_child(CHILD_OPTIONS, 0, size)?;
222    new_vmo.replace_handle(rights)
223}