devices/virtio/fs/
mod.rs

1// Copyright 2019 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::collections::BTreeMap;
6use std::io;
7use std::sync::Arc;
8
9use anyhow::anyhow;
10use base::error;
11use base::warn;
12use base::AsRawDescriptor;
13use base::Error as SysError;
14use base::RawDescriptor;
15use base::Tube;
16use base::WorkerThread;
17use data_model::Le32;
18use remain::sorted;
19use resources::Alloc;
20use sync::Mutex;
21use thiserror::Error;
22use virtio_sys::virtio_fs::virtio_fs_config;
23use virtio_sys::virtio_fs::VIRTIO_FS_SHMCAP_ID_CACHE;
24use vm_control::FsMappingRequest;
25use vm_control::VmResponse;
26use vm_memory::GuestMemory;
27use zerocopy::IntoBytes;
28
29use crate::pci::PciAddress;
30use crate::pci::PciBarConfiguration;
31use crate::pci::PciBarPrefetchable;
32use crate::pci::PciBarRegionType;
33use crate::pci::PciCapability;
34use crate::virtio::copy_config;
35use crate::virtio::device_constants::fs::FS_MAX_TAG_LEN;
36use crate::virtio::DeviceType;
37use crate::virtio::Interrupt;
38use crate::virtio::PciCapabilityType;
39use crate::virtio::Queue;
40use crate::virtio::VirtioDevice;
41use crate::virtio::VirtioPciShmCap;
42
43mod allowlist;
44#[cfg(feature = "arc_quota")]
45mod arc_ioctl;
46mod caps;
47mod config;
48mod expiring_map;
49mod multikey;
50pub mod passthrough;
51mod read_dir;
52mod worker;
53
54pub use allowlist::PathAllowlist;
55pub use config::CachePolicy;
56pub use config::Config;
57use fuse::Server;
58use passthrough::PassthroughFs;
59pub use worker::Worker;
60
61const QUEUE_SIZE: u16 = 1024;
62
63const FS_BAR_NUM: u8 = 4;
64const FS_BAR_OFFSET: u64 = 0;
65const FS_BAR_SIZE: u64 = 1 << 33;
66
67/// Errors that may occur during the creation or operation of an Fs device.
68#[sorted]
69#[derive(Error, Debug)]
70pub enum Error {
71    /// Failed to create the file system.
72    #[error("failed to create file system: {0}")]
73    CreateFs(io::Error),
74    /// Creating WaitContext failed.
75    #[error("failed to create WaitContext: {0}")]
76    CreateWaitContext(SysError),
77    /// Error happened in FUSE.
78    #[error("fuse error: {0}")]
79    FuseError(fuse::Error),
80    /// Failed to get the uids for the worker thread.
81    #[error("failed to get uids for the worker thread: {0}")]
82    GetResuid(SysError),
83    /// Failed to get the securebits for the worker thread.
84    #[error("failed to get securebits for the worker thread: {0}")]
85    GetSecurebits(SysError),
86    /// A request is missing readable descriptors.
87    #[error("request does not have any readable descriptors")]
88    NoReadableDescriptors,
89    /// A request is missing writable descriptors.
90    #[error("request does not have any writable descriptors")]
91    NoWritableDescriptors,
92    /// Error while reading from the virtio queue's Event.
93    #[error("failed to read from virtio queue Event: {0}")]
94    ReadQueueEvent(SysError),
95    /// Failed to set the securebits for the worker thread.
96    #[error("failed to set securebits for the worker thread: {0}")]
97    SetSecurebits(SysError),
98    /// Failed to signal the virio used queue.
99    #[error("failed to signal used queue: {0}")]
100    SignalUsedQueue(SysError),
101    /// The tag for the Fs device was too long to fit in the config space.
102    #[error("Fs device tag is too long: len = {0}, max = {FS_MAX_TAG_LEN}")]
103    TagTooLong(usize),
104    /// Calling unshare to disassociate FS attributes from parent failed.
105    #[error("failed to unshare fs from parent: {0}")]
106    UnshareFromParent(SysError),
107    /// Error while polling for events.
108    #[error("failed to wait for events: {0}")]
109    WaitError(SysError),
110}
111
112impl From<fuse::Error> for Error {
113    fn from(err: fuse::Error) -> Error {
114        Error::FuseError(err)
115    }
116}
117
118pub type Result<T> = ::std::result::Result<T, Error>;
119
120pub struct Fs {
121    cfg: virtio_fs_config,
122    tag: String,
123    fs: Option<PassthroughFs>,
124    queue_sizes: Box<[u16]>,
125    avail_features: u64,
126    acked_features: u64,
127    use_dax: bool,
128    pci_bar: Option<Alloc>,
129    tube: Option<Tube>,
130    workers: Vec<WorkerThread<()>>,
131}
132
133impl Fs {
134    pub fn new(
135        base_features: u64,
136        tag: &str,
137        num_workers: usize,
138        fs_cfg: Config,
139        tube: Tube,
140    ) -> Result<Fs> {
141        if tag.len() > FS_MAX_TAG_LEN {
142            return Err(Error::TagTooLong(tag.len()));
143        }
144
145        let mut cfg_tag = [0u8; FS_MAX_TAG_LEN];
146        cfg_tag[..tag.len()].copy_from_slice(tag.as_bytes());
147
148        let cfg = virtio_fs_config {
149            tag: cfg_tag,
150            num_request_queues: Le32::from(num_workers as u32),
151        };
152
153        let fs = PassthroughFs::new(tag, fs_cfg).map_err(Error::CreateFs)?;
154
155        // There is always a high priority queue in addition to the request queues.
156        let num_queues = num_workers + 1;
157
158        // TODO(b/176129399): Remove cfg! once DAX is supported on ARM.
159        let use_dax = cfg!(target_arch = "x86_64") && fs.cfg().use_dax;
160
161        Ok(Fs {
162            cfg,
163            tag: tag.to_string(),
164            fs: Some(fs),
165            queue_sizes: vec![QUEUE_SIZE; num_queues].into_boxed_slice(),
166            avail_features: base_features,
167            acked_features: 0,
168            use_dax,
169            pci_bar: None,
170            tube: Some(tube),
171            workers: Vec::with_capacity(num_workers + 1),
172        })
173    }
174}
175
176impl VirtioDevice for Fs {
177    fn keep_rds(&self) -> Vec<RawDescriptor> {
178        let mut fds = self
179            .fs
180            .as_ref()
181            .map(PassthroughFs::keep_rds)
182            .unwrap_or_default();
183        if let Some(rd) = self.tube.as_ref().map(|s| s.as_raw_descriptor()) {
184            fds.push(rd);
185        }
186
187        fds
188    }
189
190    fn device_type(&self) -> DeviceType {
191        DeviceType::Fs
192    }
193
194    fn queue_max_sizes(&self) -> &[u16] {
195        &self.queue_sizes
196    }
197
198    fn features(&self) -> u64 {
199        self.avail_features
200    }
201
202    fn ack_features(&mut self, mut v: u64) {
203        // Check if the guest is ACK'ing a feature that we didn't claim to have.
204        let unrequested_features = v & !self.avail_features;
205        if unrequested_features != 0 {
206            warn!("virtio_fs got unknown feature ack: {:x}", v);
207
208            // Don't count these features as acked.
209            v &= !unrequested_features;
210        }
211        self.acked_features |= v;
212    }
213
214    fn read_config(&self, offset: u64, data: &mut [u8]) {
215        copy_config(data, 0, self.cfg.as_bytes(), offset)
216    }
217
218    fn activate(
219        &mut self,
220        _guest_mem: GuestMemory,
221        _interrupt: Interrupt,
222        queues: BTreeMap<usize, Queue>,
223    ) -> anyhow::Result<()> {
224        if queues.len() != self.queue_sizes.len() {
225            return Err(anyhow!(
226                "expected {} queues, got {}",
227                self.queue_sizes.len(),
228                queues.len()
229            ));
230        }
231
232        let fs = self.fs.take().expect("missing file system implementation");
233
234        let server = Arc::new(Server::new(fs));
235        let socket = self.tube.take().expect("missing mapping socket");
236        let mut slot = 0;
237
238        // Set up shared memory for DAX.
239        if let Some(pci_bar) = self.pci_bar {
240            // Create the shared memory region now before we start processing requests.
241            let request = FsMappingRequest::AllocateSharedMemoryRegion(pci_bar);
242            socket
243                .send(&request)
244                .expect("failed to send allocation message");
245            slot = match socket.recv() {
246                Ok(VmResponse::RegisterMemory { slot }) => slot,
247                Ok(VmResponse::Err(e)) => panic!("failed to allocate shared memory region: {e}"),
248                r => panic!("unexpected response to allocate shared memory region: {r:?}"),
249            };
250        }
251
252        let socket = Arc::new(Mutex::new(socket));
253
254        self.workers = queues
255            .into_iter()
256            .map(|(idx, queue)| {
257                let server = server.clone();
258                let socket = Arc::clone(&socket);
259
260                WorkerThread::start(format!("v_fs:{}:{}", self.tag, idx), move |kill_evt| {
261                    let mut worker = Worker::new(queue, server, socket, slot);
262                    if let Err(e) = worker.run(kill_evt) {
263                        error!("virtio-fs worker failed: {e:#}");
264                    }
265                })
266            })
267            .collect();
268        Ok(())
269    }
270
271    fn get_device_bars(&mut self, address: PciAddress) -> Vec<PciBarConfiguration> {
272        if !self.use_dax {
273            return vec![];
274        }
275
276        self.pci_bar = Some(Alloc::PciBar {
277            bus: address.bus,
278            dev: address.dev,
279            func: address.func,
280            bar: FS_BAR_NUM,
281        });
282
283        vec![PciBarConfiguration::new(
284            FS_BAR_NUM as usize,
285            FS_BAR_SIZE,
286            PciBarRegionType::Memory64BitRegion,
287            PciBarPrefetchable::Prefetchable,
288        )]
289    }
290
291    fn get_device_caps(&self) -> Vec<Box<dyn PciCapability>> {
292        if !self.use_dax {
293            return vec![];
294        }
295
296        vec![Box::new(VirtioPciShmCap::new(
297            PciCapabilityType::SharedMemoryConfig,
298            FS_BAR_NUM,
299            FS_BAR_OFFSET,
300            FS_BAR_SIZE,
301            VIRTIO_FS_SHMCAP_ID_CACHE as u8,
302        ))]
303    }
304}