1use 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#[sorted]
69#[derive(Error, Debug)]
70pub enum Error {
71 #[error("failed to create file system: {0}")]
73 CreateFs(io::Error),
74 #[error("failed to create WaitContext: {0}")]
76 CreateWaitContext(SysError),
77 #[error("fuse error: {0}")]
79 FuseError(fuse::Error),
80 #[error("failed to get uids for the worker thread: {0}")]
82 GetResuid(SysError),
83 #[error("failed to get securebits for the worker thread: {0}")]
85 GetSecurebits(SysError),
86 #[error("request does not have any readable descriptors")]
88 NoReadableDescriptors,
89 #[error("request does not have any writable descriptors")]
91 NoWritableDescriptors,
92 #[error("failed to read from virtio queue Event: {0}")]
94 ReadQueueEvent(SysError),
95 #[error("failed to set securebits for the worker thread: {0}")]
97 SetSecurebits(SysError),
98 #[error("failed to signal used queue: {0}")]
100 SignalUsedQueue(SysError),
101 #[error("Fs device tag is too long: len = {0}, max = {FS_MAX_TAG_LEN}")]
103 TagTooLong(usize),
104 #[error("failed to unshare fs from parent: {0}")]
106 UnshareFromParent(SysError),
107 #[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 let num_queues = num_workers + 1;
157
158 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 let unrequested_features = v & !self.avail_features;
205 if unrequested_features != 0 {
206 warn!("virtio_fs got unknown feature ack: {:x}", v);
207
208 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 if let Some(pci_bar) = self.pci_bar {
240 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}