fs.rs0.00%
1
// Copyright 2025 Google LLC2
//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 at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// 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 and13
// limitations under the License.14
15
pub mod shared_dir;16
#[cfg(target_os = "linux")]17
pub mod vu;18
19
use std::fmt::Debug;20
use std::fs::File;21
use std::io::{self, IoSlice, IoSliceMut, Read};22
use std::os::fd::AsRawFd;23
use std::sync::Arc;24
use std::sync::mpsc::Receiver;25
use std::thread::JoinHandle;26
27
use mio::Registry;28
use mio::event::Event;29
use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};30
31
use crate::fuse::bindings::{FuseInHeader, FuseOpcode, FuseOutHeader, FuseSetupmappingFlag};32
use crate::fuse::{self, DaxRegion, Fuse};33
use crate::hv::IoeventFd;34
use crate::mem::mapped::{ArcMemPages, RamBus};35
use crate::mem::{MemRegion, MemRegionType};36
use crate::sync::notifier::Notifier;37
#[cfg(target_os = "linux")]38
use crate::virtio::dev::fs::vu::VuDaxRegion;39
use crate::virtio::dev::{Result, Virtio, WakeEvent};40
use crate::virtio::queue::{DescChain, QueueReg, Status, VirtQueue};41
#[cfg(target_os = "linux")]42
use crate::virtio::vu::conn::VuChannel;43
use crate::virtio::worker::mio::{ActiveMio, Mio, VirtioMio};44
use crate::virtio::{DeviceId, FEATURE_BUILT_IN, IrqSender};45
use crate::{bitflags, ffi, impl_mmio_for_zerocopy};46
47
impl 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_FAILED80
)?;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_FAILED91
)?;92
93
Ok(())94
}95
}96
97
#[repr(C, align(4))]98
#[derive(Debug, FromBytes, Immutable, IntoBytes)]99
pub struct FsConfig {100
pub tag: [u8; 36],101
pub num_request_queues: u32,102
pub notify_buf_size: u32,103
}104
105
impl_mmio_for_zerocopy!(FsConfig);106
107
bitflags! {108
pub struct FsFeature(u128) {109
NOTIFICATION = 1 << 0;110
}111
}112
113
#[derive(Debug)]114
pub 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
123
impl<F> Fs<F>124
where125
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
where165
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
where178
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
335
impl<F> VirtioMio for Fs<F>336
where337
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
where345
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
where359
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
where372
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
394
impl<F> Virtio for Fs<F>395
where396
F: Fuse + Debug + Send + Sync + 'static,397
{398
type Config = FsConfig;399
type Feature = FsFeature;400
401
fn id(&self) -> DeviceId {402
DeviceId::FILE_SYSTEM403
}404
405
fn name(&self) -> &str {406
&self.name407
}408
409
fn feature(&self) -> u128 {410
self.feature.bits() | FEATURE_BUILT_IN411
}412
413
fn num_queues(&self) -> u16 {414
let mut count = 1; // high priority queue415
if self.feature.contains(FsFeature::NOTIFICATION) {416
count += 1;417
}418
count + self.config.num_request_queues as u16 * 2419
}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
where432
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