Alioth Code Coverage

fs.rs0.00%

1// Copyright 2025 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15pub mod shared_dir;
16#[cfg(target_os = "linux")]
17pub mod vu;
18
19use std::fmt::Debug;
20use std::fs::File;
21use std::io::{self, IoSlice, IoSliceMut, Read};
22use std::os::fd::AsRawFd;
23use std::sync::Arc;
24use std::sync::mpsc::Receiver;
25use std::thread::JoinHandle;
26
27use mio::Registry;
28use mio::event::Event;
29use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
30
31use crate::fuse::bindings::{FuseInHeader, FuseOpcode, FuseOutHeader, FuseSetupmappingFlag};
32use crate::fuse::{self, DaxRegion, Fuse};
33use crate::hv::IoeventFd;
34use crate::mem::mapped::{ArcMemPages, RamBus};
35use crate::mem::{MemRegion, MemRegionType};
36use crate::sync::notifier::Notifier;
37#[cfg(target_os = "linux")]
38use crate::virtio::dev::fs::vu::VuDaxRegion;
39use crate::virtio::dev::{Result, Virtio, WakeEvent};
40use crate::virtio::queue::{DescChain, QueueReg, Status, VirtQueue};
41#[cfg(target_os = "linux")]
42use crate::virtio::vu::conn::VuChannel;
43use crate::virtio::worker::mio::{ActiveMio, Mio, VirtioMio};
44use crate::virtio::{DeviceId, FEATURE_BUILT_IN, IrqSender};
45use crate::{bitflags, ffi, impl_mmio_for_zerocopy};
46
47impl DaxRegion for ArcMemPages {
48 fn map(
49 &self,
50 m_offset: u64,
51 fd: &File,
52 f_offset: u64,
53 len: u64,
54 flag: FuseSetupmappingFlag,
55 ) -> fuse::Result<()> {
56 let fd = fd.as_raw_fd();
57
58 let map_addr = self.addr() + m_offset as usize;
59
60 let mut prot = 0;
61 if flag.contains(FuseSetupmappingFlag::READ) {
62 prot |= libc::PROT_READ;
63 };
64 if flag.contains(FuseSetupmappingFlag::WRITE) {
65 prot |= libc::PROT_WRITE;
66 }
67
68 ffi!(
69 unsafe {
70 libc::mmap(
71 map_addr as _,
72 len as usize,
73 prot,
74 libc::MAP_SHARED | libc::MAP_FIXED,
75 fd,
76 f_offset as _,
77 )
78 },
79 libc::MAP_FAILED
80 )?;
81
82 Ok(())
83 }
84
85 fn unmap(&self, m_offset: u64, len: u64) -> fuse::Result<()> {
86 let map_addr = self.addr() + m_offset as usize;
87 let flags = libc::MAP_ANONYMOUS | libc::MAP_PRIVATE | libc::MAP_FIXED;
88 ffi!(
89 unsafe { libc::mmap(map_addr as _, len as _, libc::PROT_NONE, flags, -1, 0) },
90 libc::MAP_FAILED
91 )?;
92
93 Ok(())
94 }
95}
96
97#[repr(C, align(4))]
98#[derive(Debug, FromBytes, Immutable, IntoBytes)]
99pub struct FsConfig {
100 pub tag: [u8; 36],
101 pub num_request_queues: u32,
102 pub notify_buf_size: u32,
103}
104
105impl_mmio_for_zerocopy!(FsConfig);
106
107bitflags! {
108 pub struct FsFeature(u128) {
109 NOTIFICATION = 1 << 0;
110 }
111}
112
113#[derive(Debug)]
114pub struct Fs<F> {
115 name: Arc<str>,
116 config: Arc<FsConfig>,
117 fuse: F,
118 feature: FsFeature,
119 driver_feature: FsFeature,
120 dax_region: Option<ArcMemPages>,
121}
122
123impl<F> Fs<F>
124where
125 F: Fuse,
126{
127 pub fn new(
128 name: impl Into<Arc<str>>,
129 mut fuse: F,
130 config: FsConfig,
131 dax_window: usize,
132 ) -> Result<Self> {
133 let mut feature = FsFeature::empty();
134 if config.notify_buf_size > 0 {
135 feature |= FsFeature::NOTIFICATION;
136 }
137 let mut dax_region = None;
138 if dax_window > 0 {
139 let prot = Some(libc::PROT_NONE);
140 let region = ArcMemPages::from_anonymous(dax_window, prot, None)?;
141 fuse.set_dax_region(Box::new(region.clone()));
142 dax_region = Some(region);
143 };
144 Ok(Fs {
145 name: name.into(),
146 config: Arc::new(config),
147 fuse,
148 feature,
149 driver_feature: FsFeature::empty(),
150 dax_region,
151 })
152 }
153
154 fn handle_msg(
155 &mut self,
156 hdr: &FuseInHeader,
157 in_: &[IoSlice],
158 out: &mut [IoSliceMut],
159 ) -> fuse::Result<usize> {
160 let name = &*self.name;
161 let opcode = hdr.opcode;
162
163 fn parse_in<'a, T>(bufs: &'a [IoSlice<'a>]) -> fuse::Result<(&'a T, &'a [u8])>
164 where
165 T: FromBytes + KnownLayout + Immutable,
166 {
167 let [buf] = bufs else {
168 return Err(io::Error::from_raw_os_error(libc::EINVAL))?;
169 };
170 match T::ref_from_prefix(buf) {
171 Ok((r, buf)) => Ok((r, buf)),
172 Err(_) => Err(io::Error::from_raw_os_error(libc::EINVAL))?,
173 }
174 }
175
176 fn parse_in_iov<'a, T>(bufs: &'a [IoSlice<'a>]) -> fuse::Result<(&'a T, &'a [IoSlice<'a>])>
177 where
178 T: FromBytes + KnownLayout + Immutable,
179 {
180 let [h, bufs @ ..] = bufs else {
181 return Err(io::Error::from_raw_os_error(libc::EINVAL))?;
182 };
183 match T::ref_from_bytes(h) {
184 Ok(r) => Ok((r, bufs)),
185 Err(_) => Err(io::Error::from_raw_os_error(libc::EINVAL))?,
186 }
187 }
188
189 macro_rules! opcode_branch {
190 ($func:ident, &[u8],_) => {{
191 let [in_] = in_ else {
192 return Err(io::Error::from_raw_os_error(libc::EINVAL))?;
193 };
194 let ret = self.fuse.$func(hdr, in_)?;
195 let size = ret.as_bytes().read_vectored(out)?;
196 let in_s = String::from_utf8_lossy(in_);
197 log::trace!("{name}: {opcode:?}\n{in_s:?}\n{ret:x?}");
198 Ok(size)
199 }};
200 ($func:ident, &[u8], &mut[u8]) => {{
201 let ([in_], [out]) = (in_, out) else {
202 return Err(io::Error::from_raw_os_error(libc::EINVAL))?;
203 };
204 let size = self.fuse.$func(hdr, in_, out)?;
205 let in_s = String::from_utf8_lossy(in_);
206 log::trace!("{name}: {opcode:?}\n{in_s:?}\nsize = {size:?}",);
207 Ok(size)
208 }};
209 ($func:ident, &_, &mut[u8]) => {{
210 let [out] = out else {
211 return Err(io::Error::from_raw_os_error(libc::EINVAL))?;
212 };
213 let (in_, _) = parse_in(in_)?;
214 let size = self.fuse.$func(hdr, in_, out)?;
215 log::trace!("{name}: {opcode:?}\n{in_:x?}\nsize = {size}");
216 Ok(size)
217 }};
218 ($func:ident, &_, &mut[IoSliceMut]) => {{
219 let (in_, _) = parse_in(in_)?;
220 let size = self.fuse.$func(hdr, in_, out)?;
221 log::trace!("{name}: {opcode:?}\n{in_:x?}\nsize = {size}");
222 Ok(size)
223 }};
224 ($func:ident, &_,_) => {{
225 let (in_, _) = parse_in(in_)?;
226 let ret = self.fuse.$func(hdr, in_)?;
227 let size = ret.as_bytes().read_vectored(out)?;
228 log::trace!("{name}: {opcode:?}\n{in_:x?}\n{ret:x?}");
229 Ok(size)
230 }};
231 ($func:ident, &_, &[u8],_) => {{
232 let (in_, buf) = parse_in(in_)?;
233 let ret = self.fuse.$func(hdr, in_, buf)?;
234 let size = ret.as_bytes().read_vectored(out)?;
235 log::trace!("{name}: {opcode:?}\n{in_:x?}\n{ret:x?}");
236 Ok(size)
237 }};
238 ($func:ident, &_, &[IoSlice],_) => {{
239 let (in_, bufs) = parse_in_iov(in_)?;
240 let ret = self.fuse.$func(hdr, in_, bufs)?;
241 let size = ret.as_bytes().read_vectored(out)?;
242 log::trace!("{name}: {opcode:?}\n{in_:x?}\n{ret:x?}");
243 Ok(size)
244 }};
245 }
246 match opcode {
247 FuseOpcode::INIT => opcode_branch!(init, &_, _),
248 FuseOpcode::GETATTR => opcode_branch!(get_attr, &_, _),
249 FuseOpcode::OPEN => opcode_branch!(open, &_, _),
250 FuseOpcode::OPENDIR => opcode_branch!(open_dir, &_, _),
251 FuseOpcode::READDIR => opcode_branch!(read_dir, &_, &mut [u8]),
252 FuseOpcode::RELEASEDIR => opcode_branch!(release_dir, &_, _),
253 FuseOpcode::LOOKUP => opcode_branch!(lookup, &[u8], _),
254 FuseOpcode::FORGET => opcode_branch!(forget, &_, _),
255 FuseOpcode::POLL => opcode_branch!(poll, &_, _),
256 FuseOpcode::READ => opcode_branch!(read, &_, &mut [IoSliceMut]),
257 FuseOpcode::FLUSH => opcode_branch!(flush, &_, _),
258 FuseOpcode::RELEASE => opcode_branch!(release, &_, _),
259 FuseOpcode::SYNCFS => opcode_branch!(syncfs, &_, _),
260 FuseOpcode::IOCTL => opcode_branch!(ioctl, &_, _),
261 FuseOpcode::GETXATTR => opcode_branch!(get_xattr, &[u8], &mut [u8]),
262 FuseOpcode::SETXATTR => opcode_branch!(set_xattr, &[u8], _),
263 FuseOpcode::CREATE => opcode_branch!(create, &_, &[u8], _),
264 FuseOpcode::UNLINK => opcode_branch!(unlink, &[u8], _),
265 FuseOpcode::RMDIR => opcode_branch!(rmdir, &[u8], _),
266 FuseOpcode::RENAME => opcode_branch!(rename, &_, &[u8], _),
267 FuseOpcode::WRITE => opcode_branch!(write, &_, &[IoSlice], _),
268 FuseOpcode::RENAME2 => opcode_branch!(rename2, &_, &[u8], _),
269 FuseOpcode::SETUPMAPPING => opcode_branch!(setup_mapping, &_, _),
270 FuseOpcode::REMOVEMAPPING => opcode_branch!(remove_mapping, &[u8], _),
271 _ => Err(io::Error::from_raw_os_error(libc::ENOSYS))?,
272 }
273 }
274
275 fn handle_desc(&mut self, desc: &mut DescChain, _registry: &Registry) -> Result<u32> {
276 let name = &*self.name;
277
278 let (hdr_out, out) = match &mut desc.writable[..] {
279 [] => (None, &mut [] as &mut _),
280 [hdr, out @ ..] => {
281 let Ok(hdr) = FuseOutHeader::mut_from_bytes(hdr) else {
282 log::error!("{name}: cannot parse FuseOutHeader");
283 return Ok(0);
284 };
285 (Some(hdr), out)
286 }
287 };
288
289 let Some((hdr_in, mut in_)) = desc.readable.split_first() else {
290 log::error!("{name}: cannot find opcode");
291 return Ok(0);
292 };
293
294 let Ok((hdr_in, tail)) = FuseInHeader::ref_from_prefix(hdr_in) else {
295 log::error!("{name}: cannot parse FuseInHeader");
296 return Ok(0);
297 };
298 let opcode = hdr_in.opcode;
299
300 let tails = [IoSlice::new(tail)];
301 if !tail.is_empty() {
302 if !in_.is_empty() {
303 let len = tail.len();
304 log::error!("{name}: {opcode:?}: cannot handle {len} bytes after header");
305 return Ok(0);
306 }
307 in_ = &tails;
308 }
309
310 log::trace!("{name}: {opcode:?}, nodeid = {:#x}", hdr_in.nodeid);
311
312 let ret = self.handle_msg(hdr_in, in_, out);
313 if let Err(e) = &ret {
314 log::error!("{}: {opcode:?}: {e:?}", self.name);
315 };
316
317 let Some(hdr_out) = hdr_out else {
318 return Ok(0);
319 };
320 hdr_out.unique = hdr_in.unique;
321 match ret {
322 Ok(size) => {
323 hdr_out.error = 0;
324 hdr_out.len = (size + size_of_val(hdr_out)) as u32;
325 }
326 Err(e) => {
327 hdr_out.error = -e.error_code();
328 hdr_out.len = size_of_val(hdr_out) as u32;
329 }
330 }
331 Ok(hdr_out.len)
332 }
333}
334
335impl<F> VirtioMio for Fs<F>
336where
337 F: Fuse + Debug + Send + Sync + 'static,
338{
339 fn activate<'m, Q, S, E>(
340 &mut self,
341 feature: u128,
342 _active_mio: &mut ActiveMio<'_, '_, 'm, Q, S, E>,
343 ) -> Result<()>
344 where
345 Q: VirtQueue<'m>,
346 S: IrqSender,
347 E: IoeventFd,
348 {
349 self.driver_feature = FsFeature::from_bits_retain(feature);
350 Ok(())
351 }
352
353 fn handle_event<'a, 'm, Q, S, E>(
354 &mut self,
355 _event: &Event,
356 _active_mio: &mut ActiveMio<'_, '_, 'm, Q, S, E>,
357 ) -> Result<()>
358 where
359 Q: VirtQueue<'m>,
360 S: IrqSender,
361 E: IoeventFd,
362 {
363 unreachable!()
364 }
365
366 fn handle_queue<'m, Q, S, E>(
367 &mut self,
368 index: u16,
369 active_mio: &mut ActiveMio<'_, '_, 'm, Q, S, E>,
370 ) -> Result<()>
371 where
372 Q: VirtQueue<'m>,
373 S: IrqSender,
374 E: IoeventFd,
375 {
376 let Some(Some(queue)) = active_mio.queues.get_mut(index as usize) else {
377 log::error!("{}: invalid queue index {index}", self.name);
378 return Ok(());
379 };
380 if self.feature.contains(FsFeature::NOTIFICATION) && index == 1 {
381 todo!("handle notification queue");
382 }
383 let irq_sender = active_mio.irq_sender;
384 let registry = active_mio.poll.registry();
385 queue.handle_desc(index, irq_sender, |chain| {
386 let len = self.handle_desc(chain, registry)?;
387 Ok(Status::Done { len })
388 })
389 }
390
391 fn reset(&mut self, _registry: &Registry) {}
392}
393
394impl<F> Virtio for Fs<F>
395where
396 F: Fuse + Debug + Send + Sync + 'static,
397{
398 type Config = FsConfig;
399 type Feature = FsFeature;
400
401 fn id(&self) -> DeviceId {
402 DeviceId::FILE_SYSTEM
403 }
404
405 fn name(&self) -> &str {
406 &self.name
407 }
408
409 fn feature(&self) -> u128 {
410 self.feature.bits() | FEATURE_BUILT_IN
411 }
412
413 fn num_queues(&self) -> u16 {
414 let mut count = 1; // high priority queue
415 if self.feature.contains(FsFeature::NOTIFICATION) {
416 count += 1;
417 }
418 count + self.config.num_request_queues as u16 * 2
419 }
420
421 fn config(&self) -> Arc<FsConfig> {
422 self.config.clone()
423 }
424
425 fn spawn_worker<S, E>(
426 self,
427 event_rx: Receiver<WakeEvent<S, E>>,
428 memory: Arc<RamBus>,
429 queue_regs: Arc<[QueueReg]>,
430 ) -> Result<(JoinHandle<()>, Arc<Notifier>)>
431 where
432 S: IrqSender,
433 E: IoeventFd,
434 {
435 Mio::spawn_worker(self, event_rx, memory, queue_regs)
436 }
437
438 fn shared_mem_regions(&self) -> Option<Arc<MemRegion>> {
439 let dax_region = self.dax_region.as_ref()?;
440 Some(Arc::new(MemRegion::with_dev_mem(
441 dax_region.clone(),
442 MemRegionType::Hidden,
443 )))
444 }
445
446 #[cfg(target_os = "linux")]
447 fn set_vu_channel(&mut self, channel: Arc<VuChannel>) {
448 let vu_dax_region = VuDaxRegion { channel };
449 self.fuse.set_dax_region(Box::new(vu_dax_region));
450 }
451}
452