diff --git a/src/drivers/fs/virtio_fs.rs b/src/drivers/fs/virtio_fs.rs index 8a30c3d540..f4502a4608 100644 --- a/src/drivers/fs/virtio_fs.rs +++ b/src/drivers/fs/virtio_fs.rs @@ -153,17 +153,13 @@ impl VirtioFsDriver { } impl FuseInterface for VirtioFsDriver { - fn send_command(&mut self, cmd: &fuse::Cmd, rsp: &mut fuse::Rsp) - where - S: fuse::FuseIn + core::fmt::Debug, - T: fuse::FuseOut + core::fmt::Debug, - { + fn send_command(&mut self, cmd: &fuse::Cmd, rsp: &mut fuse::Rsp) { if let Some(mut buff_tkn) = self.ready_queue.pop() { let cmd_len = Some(cmd.len()); let rsp_len = Some(rsp.len()); buff_tkn.restr_size(cmd_len, rsp_len).unwrap(); - let transfer_tkn = buff_tkn.write(Some(cmd), Some(rsp)).unwrap(); + let transfer_tkn = buff_tkn.write(Some(cmd), None::<&fuse::Rsp>).unwrap(); let transfer = transfer_tkn.dispatch_blocking().unwrap(); let (_, response) = transfer.ret_cpy().unwrap(); let tkn = transfer.reuse().unwrap(); diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 7ef39732c4..d9bdfebd86 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -4,10 +4,11 @@ use alloc::boxed::Box; use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; +use core::ffi::CStr; use core::mem::MaybeUninit; use core::sync::atomic::{AtomicU64, Ordering}; use core::task::Poll; -use core::{fmt, future, u32, u8}; +use core::{future, u32, u8}; use async_lock::Mutex; use async_trait::async_trait; @@ -21,15 +22,14 @@ use crate::drivers::virtio::virtqueue::AsSliceU8; use crate::executor::block_on; use crate::fd::{IoError, PollEvent}; use crate::fs::{ - self, AccessPermission, DirectoryEntry, FileAttr, NodeKind, ObjectInterface, OpenOption, - SeekWhence, VfsNode, + self, fuse_abi, AccessPermission, DirectoryEntry, FileAttr, NodeKind, ObjectInterface, + OpenOption, SeekWhence, VfsNode, }; // response out layout eg @ https://github.com/zargony/fuse-rs/blob/bf6d1cf03f3277e35b580f3c7b9999255d72ecf3/src/ll/request.rs#L44 // op in/out sizes/layout: https://github.com/hanwen/go-fuse/blob/204b45dba899dfa147235c255908236d5fde2d32/fuse/opcode.go#L439 // possible responses for command: qemu/tools/virtiofsd/fuse_lowlevel.h -const FUSE_ROOT_ID: u64 = 1; const MAX_READ_LEN: usize = 1024 * 64; const MAX_WRITE_LEN: usize = 1024 * 64; @@ -38,1164 +38,574 @@ const U64_SIZE: usize = ::core::mem::size_of::(); const S_IFLNK: u32 = 40960; const S_IFMT: u32 = 61440; -#[allow(dead_code)] -const FUSE_GETATTR_FH: u32 = 1 << 0; - -#[repr(C)] -#[derive(Debug)] -struct fuse_dirent { - pub d_ino: u64, - pub d_off: u64, - pub d_namelen: u32, - pub d_type: u32, - pub d_name: [u8; 0], -} - pub(crate) trait FuseInterface { - fn send_command(&mut self, cmd: &Cmd, rsp: &mut Rsp) - where - S: FuseIn + core::fmt::Debug, - T: FuseOut + core::fmt::Debug; + fn send_command(&mut self, cmd: &Cmd, rsp: &mut Rsp); fn get_mount_point(&self) -> String; } -#[repr(C)] -#[derive(Debug, Default)] -struct fuse_in_header { - pub len: u32, - pub opcode: u32, - pub unique: u64, - pub nodeid: u64, - pub uid: u32, - pub gid: u32, - pub pid: u32, - pub padding: u32, -} +pub(crate) mod ops { + use alloc::boxed::Box; + use core::ffi::CStr; + use core::mem::MaybeUninit; -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_out_header { - pub len: u32, - pub error: i32, - pub unique: u64, -} + use super::{Cmd, Rsp}; + use crate::fd::PollEvent; + use crate::fs::{fuse_abi, SeekWhence}; -#[repr(C)] -#[derive(Debug, Default)] -struct fuse_init_in { - pub major: u32, - pub minor: u32, - pub max_readahead: u32, - pub flags: u32, -} + pub(crate) trait Op { + const OP_CODE: fuse_abi::Opcode; -unsafe impl FuseIn for fuse_init_in {} + type InStruct: core::fmt::Debug; + type InPayload: ?Sized; + type OutStruct: core::fmt::Debug; + type OutPayload: ?Sized; + } -#[repr(C)] -#[derive(Debug, Default)] -struct fuse_init_out { - pub major: u32, - pub minor: u32, - pub max_readahead: u32, - pub flags: u32, - pub max_background: u16, - pub congestion_threshold: u16, - pub max_write: u32, - pub time_gran: u32, - pub unused: [u32; 9], -} -unsafe impl FuseOut for fuse_init_out {} + #[derive(Debug)] + pub(crate) struct Init; -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_read_in { - pub fh: u64, - pub offset: u64, - pub size: u32, - pub read_flags: u32, - pub lock_owner: u64, - pub flags: u32, - pub padding: u32, -} + impl Op for Init { + const OP_CODE: fuse_abi::Opcode = fuse_abi::Opcode::Init; + type InStruct = fuse_abi::InitIn; + type InPayload = (); + type OutStruct = fuse_abi::InitOut; + type OutPayload = (); + } -unsafe impl FuseIn for fuse_read_in {} + impl Init { + pub(crate) fn create() -> (Box>, Box>) { + let cmd = Cmd::::new( + fuse_abi::ROOT_ID, + fuse_abi::InitIn { + major: 7, + minor: 31, + max_readahead: 0, + flags: 0, + }, + ); + let rsp = unsafe { Box::new_uninit().assume_init() }; -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_write_in { - pub fh: u64, - pub offset: u64, - pub size: u32, - pub write_flags: u32, - pub lock_owner: u64, - pub flags: u32, - pub padding: u32, -} -unsafe impl FuseIn for fuse_write_in {} + (cmd, rsp) + } + } -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_write_out { - pub size: u32, - pub padding: u32, -} -unsafe impl FuseOut for fuse_write_out {} + #[derive(Debug)] + pub(crate) struct Create; -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_read_out {} -unsafe impl FuseOut for fuse_read_out {} + impl Op for Create { + const OP_CODE: fuse_abi::Opcode = fuse_abi::Opcode::Create; + type InStruct = fuse_abi::CreateIn; + type InPayload = CStr; + type OutStruct = fuse_abi::CreateOut; + type OutPayload = (); + } -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_lookup_in {} -unsafe impl FuseIn for fuse_lookup_in {} + impl Create { + #[allow(clippy::self_named_constructors)] + pub(crate) fn create( + path: &str, + flags: u32, + mode: u32, + ) -> (Box>, Box>) { + let cmd = Cmd::::from_str( + fuse_abi::ROOT_ID, + fuse_abi::CreateIn { + flags, + mode, + ..Default::default() + }, + path, + ); + let rsp = unsafe { Box::new_uninit().assume_init() }; -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_readlink_in {} + (cmd, rsp) + } + } -unsafe impl FuseIn for fuse_readlink_in {} + #[derive(Debug)] + pub(crate) struct Open; -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_readlink_out {} -unsafe impl FuseOut for fuse_readlink_out {} + impl Op for Open { + const OP_CODE: fuse_abi::Opcode = fuse_abi::Opcode::Open; + type InStruct = fuse_abi::OpenIn; + type InPayload = (); + type OutStruct = fuse_abi::OpenOut; + type OutPayload = (); + } -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_attr_out { - pub attr_valid: u64, - pub attr_valid_nsec: u32, - pub dummy: u32, - pub attr: fuse_attr, -} + impl Open { + pub(crate) fn create(nid: u64, flags: u32) -> (Box>, Box>) { + let cmd = Cmd::::new( + nid, + fuse_abi::OpenIn { + flags, + ..Default::default() + }, + ); + let rsp = unsafe { Box::new_uninit().assume_init() }; -unsafe impl FuseOut for fuse_attr_out {} + (cmd, rsp) + } + } -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_entry_out { - pub nodeid: u64, - pub generation: u64, - pub entry_valid: u64, - pub attr_valid: u64, - pub entry_valid_nsec: u32, - pub attr_valid_nsec: u32, - pub attr: fuse_attr, -} + #[derive(Debug)] + pub(crate) struct Write; -unsafe impl FuseOut for fuse_entry_out {} + impl Op for Write { + const OP_CODE: fuse_abi::Opcode = fuse_abi::Opcode::Write; + type InStruct = fuse_abi::WriteIn; + type InPayload = [u8]; + type OutStruct = fuse_abi::WriteOut; + type OutPayload = (); + } -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_attr { - /// inode number - pub ino: u64, - /// size in bytes - pub size: u64, - /// size in blocks - pub blocks: u64, - /// time of last access - pub atime: u64, - /// time of last modification - pub mtime: u64, - /// time of last status change - pub ctime: u64, - pub atimensec: u32, - pub mtimensec: u32, - pub ctimensec: u32, - /// access permissions - pub mode: u32, - /// number of hard links - pub nlink: u32, - /// user id - pub uid: u32, - /// group id - pub gid: u32, - /// device id - pub rdev: u32, - /// block size - pub blksize: u32, - pub padding: u32, -} + impl Write { + pub(crate) fn create( + nid: u64, + fh: u64, + buf: &[u8], + offset: u64, + ) -> (Box>, Box>) { + let cmd = Cmd::::from_array( + nid, + fuse_abi::WriteIn { + fh, + offset, + size: buf.len().try_into().unwrap(), + ..Default::default() + }, + buf, + ); + let rsp = unsafe { Box::new_uninit().assume_init() }; -impl From for FileAttr { - fn from(attr: fuse_attr) -> FileAttr { - FileAttr { - st_ino: attr.ino, - st_nlink: attr.nlink as u64, - st_mode: AccessPermission::from_bits(attr.mode).unwrap(), - st_uid: attr.uid, - st_gid: attr.gid, - st_rdev: attr.rdev as u64, - st_size: attr.size, - st_blksize: attr.blksize as i64, - st_blocks: attr.blocks.try_into().unwrap(), - st_atime: attr.atime, - st_atime_nsec: attr.atimensec as u64, - st_mtime: attr.mtime, - st_mtime_nsec: attr.atimensec as u64, - st_ctime: attr.ctime, - st_ctime_nsec: attr.ctimensec as u64, - ..Default::default() + (cmd, rsp) } } -} -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_create_in { - pub flags: u32, - pub mode: u32, - pub umask: u32, - pub open_flags: u32, -} -unsafe impl FuseIn for fuse_create_in {} + #[derive(Debug)] + pub(crate) struct Read; -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_create_out { - pub entry: fuse_entry_out, - pub open: fuse_open_out, -} + impl Op for Read { + const OP_CODE: fuse_abi::Opcode = fuse_abi::Opcode::Read; + type InStruct = fuse_abi::ReadIn; + type InPayload = (); + type OutStruct = fuse_abi::ReadOut; -unsafe impl FuseOut for fuse_create_out {} + // Since at the time of writing MaybeUninit does not support DSTs as type parameters, we have to define `OutPayload` as [MaybeUninit<_>] + // instead of a MaybeUninit<[_]>. + type OutPayload = [MaybeUninit]; + } -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_open_in { - pub flags: u32, - pub unused: u32, -} + impl Read { + pub(crate) fn create( + nid: u64, + fh: u64, + size: u32, + offset: u64, + ) -> (Box>, Box>) { + let cmd = Cmd::::new( + nid, + fuse_abi::ReadIn { + fh, + offset, + size, + ..Default::default() + }, + ); + let rsp = unsafe { Rsp::::new_uninit(size.try_into().unwrap()) }; -unsafe impl FuseIn for fuse_open_in {} + (cmd, rsp) + } + } -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_open_out { - pub fh: u64, - pub open_flags: u32, - pub padding: u32, -} + #[derive(Debug)] + pub(crate) struct Lseek; -unsafe impl FuseOut for fuse_open_out {} + impl Op for Lseek { + const OP_CODE: fuse_abi::Opcode = fuse_abi::Opcode::Lseek; + type InStruct = fuse_abi::LseekIn; + type InPayload = (); + type OutStruct = fuse_abi::LseekOut; + type OutPayload = (); + } -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_release_in { - pub fh: u64, - pub flags: u32, - pub release_flags: u32, - pub lock_owner: u64, -} + impl Lseek { + pub(crate) fn create( + nid: u64, + fh: u64, + offset: isize, + whence: SeekWhence, + ) -> (Box>, Box>) { + let cmd = Cmd::::new( + nid, + fuse_abi::LseekIn { + fh, + offset: offset.try_into().unwrap(), + whence: num::ToPrimitive::to_u32(&whence).unwrap(), + ..Default::default() + }, + ); + let rsp = unsafe { Box::new_uninit().assume_init() }; -unsafe impl FuseIn for fuse_release_in {} + (cmd, rsp) + } + } -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_release_out {} -unsafe impl FuseOut for fuse_release_out {} + #[derive(Debug)] + pub(crate) struct Readlink; -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_rmdir_in {} -unsafe impl FuseIn for fuse_rmdir_in {} + impl Op for Readlink { + const OP_CODE: fuse_abi::Opcode = fuse_abi::Opcode::Readlink; + type InStruct = fuse_abi::ReadlinkIn; + type InPayload = (); + type OutStruct = fuse_abi::ReadlinkOut; -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_rmdir_out {} -unsafe impl FuseOut for fuse_rmdir_out {} + // Since at the time of writing MaybeUninit does not support DSTs as type parameters, we have to define `OutPayload` as [MaybeUninit<_>] + // instead of a MaybeUninit<[_]>. + type OutPayload = [MaybeUninit]; + } -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_mkdir_in { - pub mode: u32, - pub umask: u32, -} -unsafe impl FuseIn for fuse_mkdir_in {} + impl Readlink { + pub(crate) fn create(nid: u64, size: u32) -> (Box>, Box>) { + let cmd = Cmd::::new(nid, fuse_abi::ReadlinkIn {}); + let rsp = unsafe { Rsp::::new_uninit(size.try_into().unwrap()) }; -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_unlink_in {} -unsafe impl FuseIn for fuse_unlink_in {} + (cmd, rsp) + } + } -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_unlink_out {} -unsafe impl FuseOut for fuse_unlink_out {} + #[derive(Debug)] + pub(crate) struct Release; -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_lseek_in { - pub fh: u64, - pub offset: u64, - pub whence: u32, - pub padding: u32, -} -unsafe impl FuseIn for fuse_lseek_in {} + impl Op for Release { + const OP_CODE: fuse_abi::Opcode = fuse_abi::Opcode::Release; + type InStruct = fuse_abi::ReleaseIn; + type InPayload = (); + type OutStruct = fuse_abi::ReleaseOut; + type OutPayload = (); + } -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_lseek_out { - offset: u64, -} -unsafe impl FuseOut for fuse_lseek_out {} + impl Release { + pub(crate) fn create(nid: u64, fh: u64) -> (Box>, Box>) { + let cmd = Cmd::::new( + nid, + fuse_abi::ReleaseIn { + fh, + ..Default::default() + }, + ); + let rsp = unsafe { Box::new_uninit().assume_init() }; -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_poll_in { - pub fh: u64, - pub kh: u64, - pub flags: u32, - pub events: u32, -} -unsafe impl FuseIn for fuse_poll_in {} + (cmd, rsp) + } + } -#[repr(C)] -#[derive(Default, Debug)] -struct fuse_poll_out { - revents: u32, - padding: u32, -} -unsafe impl FuseOut for fuse_poll_out {} - -#[repr(u32)] -#[derive(Debug, Copy, Clone)] -#[allow(non_camel_case_types)] -#[allow(dead_code)] -enum Opcode { - FUSE_LOOKUP = 1, - FUSE_FORGET = 2, // no reply - FUSE_GETATTR = 3, - FUSE_SETATTR = 4, - FUSE_READLINK = 5, - FUSE_SYMLINK = 6, - FUSE_MKNOD = 8, - FUSE_MKDIR = 9, - FUSE_UNLINK = 10, - FUSE_RMDIR = 11, - FUSE_RENAME = 12, - FUSE_LINK = 13, - FUSE_OPEN = 14, - FUSE_READ = 15, - FUSE_WRITE = 16, - FUSE_STATFS = 17, - FUSE_RELEASE = 18, - FUSE_FSYNC = 20, - FUSE_SETXATTR = 21, - FUSE_GETXATTR = 22, - FUSE_LISTXATTR = 23, - FUSE_REMOVEXATTR = 24, - FUSE_FLUSH = 25, - FUSE_INIT = 26, - FUSE_OPENDIR = 27, - FUSE_READDIR = 28, - FUSE_RELEASEDIR = 29, - FUSE_FSYNCDIR = 30, - FUSE_GETLK = 31, - FUSE_SETLK = 32, - FUSE_SETLKW = 33, - FUSE_ACCESS = 34, - FUSE_CREATE = 35, - FUSE_INTERRUPT = 36, - FUSE_BMAP = 37, - FUSE_DESTROY = 38, - FUSE_IOCTL = 39, - FUSE_POLL = 40, - FUSE_NOTIFY_REPLY = 41, - FUSE_BATCH_FORGET = 42, - FUSE_FALLOCATE = 43, - FUSE_READDIRPLUS = 44, - FUSE_RENAME2 = 45, - FUSE_LSEEK = 46, - - FUSE_SETVOLNAME = 61, - FUSE_GETXTIMES = 62, - FUSE_EXCHANGE = 63, - - CUSE_INIT = 4096, -} + #[derive(Debug)] + pub(crate) struct Poll; -/// Marker trait, which signals that a struct is a valid Fuse command. -/// Struct has to be repr(C)! -pub(crate) unsafe trait FuseIn {} -/// Marker trait, which signals that a struct is a valid Fuse response. -/// Struct has to be repr(C)! -pub(crate) unsafe trait FuseOut {} + impl Op for Poll { + const OP_CODE: fuse_abi::Opcode = fuse_abi::Opcode::Poll; + type InStruct = fuse_abi::PollIn; + type InPayload = (); + type OutStruct = fuse_abi::PollOut; + type OutPayload = (); + } -#[repr(C)] -#[derive(Debug)] -pub(crate) struct Cmd { - header: fuse_in_header, - cmd: T, - extra_buffer: [u8], -} + impl Poll { + pub(crate) fn create( + nid: u64, + fh: u64, + kh: u64, + event: PollEvent, + ) -> (Box>, Box>) { + let cmd = Cmd::::new( + nid, + fuse_abi::PollIn { + fh, + kh, + events: event.bits() as u32, + ..Default::default() + }, + ); + let rsp = unsafe { Box::new_uninit().assume_init() }; -impl AsSliceU8 for Cmd { - fn len(&self) -> usize { - self.header.len.try_into().unwrap() + (cmd, rsp) + } } -} -#[repr(C)] -#[derive(Debug)] -pub(crate) struct Rsp { - header: fuse_out_header, - rsp: MaybeUninit, - extra_buffer: [MaybeUninit], -} + #[derive(Debug)] + pub(crate) struct Mkdir; -impl AsSliceU8 for Rsp { - fn len(&self) -> usize { - self.header.len.try_into().unwrap() + impl Op for Mkdir { + const OP_CODE: fuse_abi::Opcode = fuse_abi::Opcode::Mkdir; + type InStruct = fuse_abi::MkdirIn; + type InPayload = CStr; + type OutStruct = fuse_abi::EntryOut; + type OutPayload = (); } -} -fn create_in_header(nodeid: u64, opcode: Opcode) -> fuse_in_header -where - T: FuseIn, -{ - fuse_in_header { - len: (core::mem::size_of::() + core::mem::size_of::()) as u32, - opcode: opcode as u32, - unique: 1, - nodeid, - ..Default::default() + impl Mkdir { + pub(crate) fn create(path: &str, mode: u32) -> (Box>, Box>) { + let cmd = Cmd::::from_str( + fuse_abi::ROOT_ID, + fuse_abi::MkdirIn { + mode, + ..Default::default() + }, + path, + ); + let rsp = unsafe { Box::new_uninit().assume_init() }; + + (cmd, rsp) + } } -} -fn create_init() -> (Box>, Box>) { - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Cmd; - (*raw).header = create_in_header::(FUSE_ROOT_ID, Opcode::FUSE_INIT); - (*raw).header.len = len.try_into().unwrap(); - (*raw).cmd = fuse_init_in { - major: 7, - minor: 31, - max_readahead: 0, - flags: 0, - }; + #[derive(Debug)] + pub(crate) struct Unlink; - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); - - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), - ..Default::default() - }; + impl Op for Unlink { + const OP_CODE: fuse_abi::Opcode = fuse_abi::Opcode::Unlink; + type InStruct = fuse_abi::UnlinkIn; + type InPayload = CStr; + type OutStruct = fuse_abi::UnlinkOut; + type OutPayload = (); + } - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); + impl Unlink { + pub(crate) fn create(name: &str) -> (Box>, Box>) { + let cmd = Cmd::::from_str(fuse_abi::ROOT_ID, fuse_abi::UnlinkIn {}, name); + let rsp = unsafe { Box::new_uninit().assume_init() }; - (cmd, rsp) -} + (cmd, rsp) + } + } -fn create_create( - path: &str, - flags: u32, - mode: u32, -) -> (Box>, Box>) { - let slice = path.as_bytes(); - let len = core::mem::size_of::() - + core::mem::size_of::() - + slice.len() - + 1; - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = - core::ptr::slice_from_raw_parts_mut(data, slice.len() + 1) as *mut Cmd; - (*raw).header = create_in_header::(FUSE_ROOT_ID, Opcode::FUSE_CREATE); - (*raw).header.len = len.try_into().unwrap(); - (*raw).cmd = fuse_create_in { - flags, - mode, - ..Default::default() - }; - (*raw).extra_buffer[..slice.len()].copy_from_slice(slice); - (*raw).extra_buffer[slice.len()] = 0; + #[derive(Debug)] + pub(crate) struct Rmdir; - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); - - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), - ..Default::default() - }; + impl Op for Rmdir { + const OP_CODE: fuse_abi::Opcode = fuse_abi::Opcode::Rmdir; + type InStruct = fuse_abi::RmdirIn; + type InPayload = CStr; + type OutStruct = fuse_abi::RmdirOut; + type OutPayload = (); + } - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); + impl Rmdir { + pub(crate) fn create(name: &str) -> (Box>, Box>) { + let cmd = Cmd::::from_str(fuse_abi::ROOT_ID, fuse_abi::RmdirIn {}, name); + let rsp = unsafe { Box::new_uninit().assume_init() }; - (cmd, rsp) -} + (cmd, rsp) + } + } -fn create_open(nid: u64, flags: u32) -> (Box>, Box>) { - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Cmd; - (*raw).header = create_in_header::(nid, Opcode::FUSE_OPEN); - (*raw).cmd = fuse_open_in { - flags, - ..Default::default() - }; + #[derive(Debug)] + pub(crate) struct Lookup; - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); - - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), - ..Default::default() - }; + impl Op for Lookup { + const OP_CODE: fuse_abi::Opcode = fuse_abi::Opcode::Lookup; + type InStruct = fuse_abi::LookupIn; + type InPayload = CStr; + type OutStruct = fuse_abi::EntryOut; + type OutPayload = (); + } - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); + impl Lookup { + pub(crate) fn create(name: &str) -> (Box>, Box>) { + let cmd = Cmd::::from_str(fuse_abi::ROOT_ID, fuse_abi::LookupIn {}, name); + let rsp = unsafe { Box::new_uninit().assume_init() }; - (cmd, rsp) + (cmd, rsp) + } + } } -// TODO: do write zerocopy? -fn create_write( - nid: u64, - fh: u64, - buf: &[u8], - offset: u64, -) -> (Box>, Box>) { - let len = - core::mem::size_of::() + core::mem::size_of::() + buf.len(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, buf.len()) as *mut Cmd; - (*raw).header = fuse_in_header { - len: len.try_into().unwrap(), - opcode: Opcode::FUSE_WRITE as u32, - unique: 1, - nodeid: nid, - ..Default::default() - }; - (*raw).cmd = fuse_write_in { - fh, - offset, - size: buf.len().try_into().unwrap(), - ..Default::default() - }; - (*raw).extra_buffer.copy_from_slice(buf); - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); - - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), +impl From for FileAttr { + fn from(attr: fuse_abi::Attr) -> FileAttr { + FileAttr { + st_ino: attr.ino, + st_nlink: attr.nlink as u64, + st_mode: AccessPermission::from_bits(attr.mode).unwrap(), + st_uid: attr.uid, + st_gid: attr.gid, + st_rdev: attr.rdev as u64, + st_size: attr.size, + st_blksize: attr.blksize as i64, + st_blocks: attr.blocks.try_into().unwrap(), + st_atime: attr.atime, + st_atime_nsec: attr.atimensec as u64, + st_mtime: attr.mtime, + st_mtime_nsec: attr.atimensec as u64, + st_ctime: attr.ctime, + st_ctime_nsec: attr.ctimensec as u64, ..Default::default() - }; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); - - (cmd, rsp) + } + } } -fn create_read( - nid: u64, - fh: u64, - size: u32, - offset: u64, -) -> (Box>, Box>) { - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Cmd; - (*raw).header = create_in_header::(nid, Opcode::FUSE_READ); - (*raw).cmd = fuse_read_in { - fh, - offset, - size, - ..Default::default() - }; +#[repr(C)] +#[derive(Debug)] +pub(crate) struct Cmd { + in_header: fuse_abi::InHeader, + op_header: O::InStruct, + payload: O::InPayload, +} - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); - - let len = core::mem::size_of::() - + core::mem::size_of::() - + usize::try_from(size).unwrap(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, size.try_into().unwrap()) - as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), - ..Default::default() - }; +#[repr(C)] +#[derive(Debug)] +pub(crate) struct UninitCmd { + in_header: MaybeUninit, + op_header: MaybeUninit, + payload: [MaybeUninit], +} - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); +// We use this struct to obtain the layout of the type without the payload. +#[repr(C)] +#[derive(Debug)] +pub(crate) struct PayloadlessCmd { + in_header: MaybeUninit, + op_header: MaybeUninit, + payload: (), +} - (cmd, rsp) +impl Cmd +where + O: ops::Op, +{ + fn new(nodeid: u64, op_header: O::InStruct) -> Box { + Box::new(Cmd { + in_header: fuse_abi::InHeader { + len: Layout::new::().size() as u32, + opcode: O::OP_CODE as u32, + nodeid, + unique: 1, + ..Default::default() + }, + op_header, + payload: (), + }) + } } -fn create_lseek( - nid: u64, - fh: u64, - offset: isize, - whence: SeekWhence, -) -> (Box>, Box>) { - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Cmd; - (*raw).header = fuse_in_header { - len: len.try_into().unwrap(), - opcode: Opcode::FUSE_LSEEK as u32, +impl Cmd { + fn with_capacity(nodeid: u64, op_header: O::InStruct, len: usize) -> Box> { + let mut cmd = unsafe { Self::new_uninit(len) }; + cmd.in_header = MaybeUninit::new(fuse_abi::InHeader { + len: core::mem::size_of_val(cmd.as_ref()) + .try_into() + .expect("The command is too large"), + opcode: O::OP_CODE as u32, + nodeid, unique: 1, - nodeid: nid, ..Default::default() - }; - (*raw).cmd = fuse_lseek_in { - fh, - offset: offset.try_into().unwrap(), - whence: num::ToPrimitive::to_u32(&whence).unwrap(), - ..Default::default() - }; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); - - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), - ..Default::default() - }; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); - - (cmd, rsp) + }); + cmd.op_header = MaybeUninit::new(op_header); + cmd + } } -fn create_readlink( - nid: u64, - size: u32, -) -> (Box>, Box>) { - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Cmd; - (*raw).header = create_in_header::(nid, Opcode::FUSE_READLINK); - (*raw).header.len = len.try_into().unwrap(); - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); - - let len = core::mem::size_of::() - + core::mem::size_of::() - + usize::try_from(size).unwrap(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, size.try_into().unwrap()) - as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), - ..Default::default() - }; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); - - (cmd, rsp) +impl Cmd +where + O: ops::Op, +{ + fn from_array(nodeid: u64, op_header: O::InStruct, data: &[u8]) -> Box> { + let mut cmd = Self::with_capacity(nodeid, op_header, data.len()); + for (target, source) in cmd.payload.iter_mut().zip(data) { + *target = MaybeUninit::new(*source); + } + unsafe { core::intrinsics::transmute(cmd) } + } } -fn create_release(nid: u64, fh: u64) -> (Box>, Box>) { - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Cmd; - (*raw).header = create_in_header::(nid, Opcode::FUSE_RELEASE); - (*raw).cmd = fuse_release_in { - fh, - ..Default::default() - }; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); - - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), - ..Default::default() - }; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); - - (cmd, rsp) +impl Cmd +where + O: ops::Op, +{ + fn from_str(nodeid: u64, op_header: O::InStruct, str: &str) -> Box> { + let str_bytes = str.as_bytes(); + // Plus one for the NUL terminator + let mut cmd = Self::with_capacity(nodeid, op_header, str_bytes.len() + 1); + for (target, source) in cmd.payload[..str_bytes.len()].iter_mut().zip(str_bytes) { + *target = MaybeUninit::new(*source); + } + cmd.payload[str_bytes.len()] = MaybeUninit::new(b'\0'); + unsafe { core::intrinsics::transmute(cmd) } + } } -fn create_poll( - nid: u64, - fh: u64, - kh: u64, - event: PollEvent, -) -> (Box>, Box>) { - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Cmd; - (*raw).header = create_in_header::(nid, Opcode::FUSE_POLL); - (*raw).cmd = fuse_poll_in { - fh, - kh, - events: event.bits() as u32, - ..Default::default() - }; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); - - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), - ..Default::default() - }; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); - - (cmd, rsp) +impl AsSliceU8 for Cmd { + fn len(&self) -> usize { + self.in_header.len.try_into().unwrap() + } } -fn create_mkdir(path: &str, mode: u32) -> (Box>, Box>) { - let slice = path.as_bytes(); - let len = core::mem::size_of::() - + core::mem::size_of::() - + slice.len() - + 1; - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = - core::ptr::slice_from_raw_parts_mut(data, slice.len() + 1) as *mut Cmd; - (*raw).header = create_in_header::(FUSE_ROOT_ID, Opcode::FUSE_MKDIR); - (*raw).header.len = len.try_into().unwrap(); - (*raw).cmd = fuse_mkdir_in { - mode, - ..Default::default() - }; - (*raw).extra_buffer[..slice.len()].copy_from_slice(slice); - (*raw).extra_buffer[slice.len()] = 0; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); - - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), - ..Default::default() - }; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); - - (cmd, rsp) +impl Cmd { + // MaybeUninit does not accept DSTs as type parameter + unsafe fn new_uninit(len: usize) -> Box> { + unsafe { + Box::from_raw(core::ptr::slice_from_raw_parts_mut( + alloc( + Layout::new::>() + .extend(Layout::array::(len).expect("The length is too much.")) + .expect("The layout size overflowed.") + .0 // We don't need the offset of `data_header` inside the type (the second element of the tuple) + .pad_to_align(), + ), + 0, + ) as *mut UninitCmd) + } + } } -fn create_unlink(name: &str) -> (Box>, Box>) { - let slice = name.as_bytes(); - let len = core::mem::size_of::() - + core::mem::size_of::() - + slice.len() - + 1; - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = - core::ptr::slice_from_raw_parts_mut(data, slice.len() + 1) as *mut Cmd; - (*raw).header = create_in_header::(FUSE_ROOT_ID, Opcode::FUSE_UNLINK); - (*raw).header.len = len.try_into().unwrap(); - (*raw).extra_buffer[..slice.len()].copy_from_slice(slice); - (*raw).extra_buffer[slice.len()] = 0; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); - - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), - ..Default::default() - }; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); - - (cmd, rsp) +#[repr(C)] +#[derive(Debug)] +pub(crate) struct Rsp { + out_header: MaybeUninit, + op_header: MaybeUninit, + payload: O::OutPayload, } -fn create_rmdir(name: &str) -> (Box>, Box>) { - let slice = name.as_bytes(); - let len = core::mem::size_of::() - + core::mem::size_of::() - + slice.len() - + 1; - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = - core::ptr::slice_from_raw_parts_mut(data, slice.len() + 1) as *mut Cmd; - (*raw).header = create_in_header::(FUSE_ROOT_ID, Opcode::FUSE_RMDIR); - (*raw).header.len = len.try_into().unwrap(); - (*raw).extra_buffer[..slice.len()].copy_from_slice(slice); - (*raw).extra_buffer[slice.len()] = 0; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); - - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), - ..Default::default() - }; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); - - (cmd, rsp) +#[repr(C)] +#[derive(Debug)] +pub(crate) struct PayloadlessRsp { + out_header: MaybeUninit, + op_header: MaybeUninit, + payload: (), } -fn create_lookup(name: &str) -> (Box>, Box>) { - let slice = name.as_bytes(); - let len = core::mem::size_of::() - + core::mem::size_of::() - + slice.len() - + 1; - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = - core::ptr::slice_from_raw_parts_mut(data, slice.len() + 1) as *mut Cmd; - (*raw).header = create_in_header::(FUSE_ROOT_ID, Opcode::FUSE_LOOKUP); - (*raw).header.len = len.try_into().unwrap(); - (*raw).extra_buffer[..slice.len()].copy_from_slice(slice); - (*raw).extra_buffer[slice.len()] = 0; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); - - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), - ..Default::default() - }; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); +// Since we don't bother with initializing the len field, we use the default len implementation. +impl AsSliceU8 for Rsp {} - (cmd, rsp) +impl Rsp +where + O: ops::Op]>, +{ + unsafe fn new_uninit(len: usize) -> Box { + unsafe { + Box::from_raw(core::ptr::slice_from_raw_parts_mut( + alloc( + Layout::new::>() + .extend(Layout::array::(len).expect("The length is too much.")) + .expect("The layout size overflowed.") + .0 // We don't need the offset of `data_header` inside the type (the second element of the tuple) + .pad_to_align(), + ), + 0, + ) as *mut Rsp) + } + } } fn lookup(name: &str) -> Option { - let (cmd, mut rsp) = create_lookup(name); + let (cmd, mut rsp) = ops::Lookup::create(name); get_filesystem_driver() .unwrap() .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - if rsp.header.error == 0 { - Some(unsafe { rsp.rsp.assume_init().nodeid }) + if unsafe { rsp.out_header.assume_init_ref().error } == 0 { + Some(unsafe { rsp.op_header.assume_init_ref().nodeid }) } else { None } @@ -1203,25 +613,25 @@ fn lookup(name: &str) -> Option { fn readlink(nid: u64) -> Result { let len = MAX_READ_LEN as u32; - let (cmd, mut rsp) = create_readlink(nid, len); + let (cmd, mut rsp) = ops::Readlink::create(nid, len); get_filesystem_driver() .unwrap() .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - let len: usize = if rsp.header.len as usize - - ::core::mem::size_of::() - - ::core::mem::size_of::() + let len: usize = if unsafe { rsp.out_header.assume_init_ref().len } as usize + - ::core::mem::size_of::() + - ::core::mem::size_of::() >= len.try_into().unwrap() { len.try_into().unwrap() } else { - rsp.header.len as usize - - ::core::mem::size_of::() - - ::core::mem::size_of::() + (unsafe { rsp.out_header.assume_init_ref().len } as usize) + - ::core::mem::size_of::() + - ::core::mem::size_of::() }; Ok(String::from_utf8(unsafe { - MaybeUninit::slice_assume_init_ref(&rsp.extra_buffer[..len]).to_vec() + MaybeUninit::slice_assume_init_ref(&rsp.payload[..len]).to_vec() }) .unwrap()) } @@ -1248,18 +658,20 @@ impl FuseFileHandleInner { future::poll_fn(|cx| { if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) { - let (cmd, mut rsp) = create_poll(nid, fh, kh, events); + let (cmd, mut rsp) = ops::Poll::create(nid, fh, kh, events); get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - if rsp.header.error < 0 { + if unsafe { rsp.out_header.assume_init_ref().error } < 0 { Poll::Ready(Err(IoError::EIO)) } else { let revents = unsafe { - PollEvent::from_bits(i16::try_from(rsp.rsp.assume_init().revents).unwrap()) - .unwrap() + PollEvent::from_bits( + i16::try_from(rsp.op_header.assume_init_ref().revents).unwrap(), + ) + .unwrap() }; if !revents.intersects(events) && !revents.intersects( @@ -1285,26 +697,27 @@ impl FuseFileHandleInner { len = MAX_READ_LEN; } if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) { - let (cmd, mut rsp) = create_read(nid, fh, len.try_into().unwrap(), self.offset as u64); + let (cmd, mut rsp) = + ops::Read::create(nid, fh, len.try_into().unwrap(), self.offset as u64); get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - let len: usize = if rsp.header.len as usize - - ::core::mem::size_of::() - - ::core::mem::size_of::() + let len: usize = if (unsafe { rsp.out_header.assume_init_ref().len } as usize) + - ::core::mem::size_of::() + - ::core::mem::size_of::() >= len { len } else { - rsp.header.len as usize - - ::core::mem::size_of::() - - ::core::mem::size_of::() + (unsafe { rsp.out_header.assume_init_ref().len } as usize) + - ::core::mem::size_of::() + - ::core::mem::size_of::() }; self.offset += len; buf[..len].copy_from_slice(unsafe { - MaybeUninit::slice_assume_init_ref(&rsp.extra_buffer[..len]) + MaybeUninit::slice_assume_init_ref(&rsp.payload[..len]) }); Ok(len) @@ -1326,17 +739,17 @@ impl FuseFileHandleInner { len = MAX_WRITE_LEN; } if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) { - let (cmd, mut rsp) = create_write(nid, fh, &buf[..len], self.offset as u64); + let (cmd, mut rsp) = ops::Write::create(nid, fh, &buf[..len], self.offset as u64); get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - if rsp.header.error < 0 { + if unsafe { rsp.out_header.assume_init_ref().error } < 0 { return Err(IoError::EIO); } - let rsp_size = unsafe { rsp.rsp.assume_init().size }; + let rsp_size = unsafe { rsp.op_header.assume_init_ref().size }; let len: usize = if rsp_size > buf.len().try_into().unwrap() { buf.len() } else { @@ -1354,17 +767,17 @@ impl FuseFileHandleInner { debug!("FUSE lseek"); if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) { - let (cmd, mut rsp) = create_lseek(nid, fh, offset, whence); + let (cmd, mut rsp) = ops::Lseek::create(nid, fh, offset, whence); get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - if rsp.header.error < 0 { + if unsafe { rsp.out_header.assume_init_ref().error } < 0 { return Err(IoError::EIO); } - let rsp_offset = unsafe { rsp.rsp.assume_init().offset }; + let rsp_offset = unsafe { rsp.op_header.assume_init_ref().offset }; Ok(rsp_offset.try_into().unwrap()) } else { @@ -1376,7 +789,8 @@ impl FuseFileHandleInner { impl Drop for FuseFileHandleInner { fn drop(&mut self) { if self.fuse_nid.is_some() && self.fuse_fh.is_some() { - let (cmd, mut rsp) = create_release(self.fuse_nid.unwrap(), self.fuse_fh.unwrap()); + let (cmd, mut rsp) = + ops::Release::create(self.fuse_nid.unwrap(), self.fuse_fh.unwrap()); get_filesystem_driver() .unwrap() .lock() @@ -1452,13 +866,13 @@ impl VfsNode for FuseDirectory { // Opendir // Flag 0x10000 for O_DIRECTORY might not be necessary - let (mut cmd, mut rsp) = create_open(fuse_nid, 0x10000); - cmd.header.opcode = Opcode::FUSE_OPENDIR as u32; + let (mut cmd, mut rsp) = ops::Open::create(fuse_nid, 0x10000); + cmd.in_header.opcode = fuse_abi::Opcode::Opendir as u32; get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - let fuse_fh = unsafe { rsp.rsp.assume_init().fh }; + let fuse_fh = unsafe { rsp.op_header.assume_init_ref().fh }; debug!("FUSE readdir: {}", path); @@ -1467,36 +881,38 @@ impl VfsNode for FuseDirectory { let mut offset: usize = 0; // read content of the directory - let (mut cmd, mut rsp) = create_read(fuse_nid, fuse_fh, len, 0); - cmd.header.opcode = Opcode::FUSE_READDIR as u32; + let (mut cmd, mut rsp) = ops::Read::create(fuse_nid, fuse_fh, len, 0); + cmd.in_header.opcode = fuse_abi::Opcode::Readdir as u32; get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - let len: usize = if rsp.header.len as usize - - ::core::mem::size_of::() - - ::core::mem::size_of::() + let len: usize = if unsafe { rsp.out_header.assume_init_ref().len } as usize + - ::core::mem::size_of::() + - ::core::mem::size_of::() >= len.try_into().unwrap() { len.try_into().unwrap() } else { - rsp.header.len as usize - - ::core::mem::size_of::() - - ::core::mem::size_of::() + (unsafe { rsp.out_header.assume_init_ref().len } as usize) + - ::core::mem::size_of::() + - ::core::mem::size_of::() }; - if len <= core::mem::size_of::() { + if len <= core::mem::size_of::() { debug!("FUSE no new dirs"); return Err(IoError::ENOENT); } let mut entries: Vec = Vec::new(); - while rsp.header.len as usize - offset > core::mem::size_of::() { + while (unsafe { rsp.out_header.assume_init_ref().len } as usize) - offset + > core::mem::size_of::() + { let dirent = - unsafe { &*(rsp.extra_buffer.as_ptr().byte_add(offset) as *const fuse_dirent) }; + unsafe { &*(rsp.payload.as_ptr().byte_add(offset) as *const fuse_abi::Dirent) }; - offset += core::mem::size_of::() + dirent.d_namelen as usize; + offset += core::mem::size_of::() + dirent.d_namelen as usize; // Allign to dirent struct offset = ((offset) + U64_SIZE - 1) & (!(U64_SIZE - 1)); @@ -1511,7 +927,7 @@ impl VfsNode for FuseDirectory { })); } - let (cmd, mut rsp) = create_release(fuse_nid, fuse_fh); + let (cmd, mut rsp) = ops::Release::create(fuse_nid, fuse_fh); get_filesystem_driver() .unwrap() .lock() @@ -1534,18 +950,18 @@ impl VfsNode for FuseDirectory { debug!("FUSE stat: {}", path); // Is there a better way to implement this? - let (cmd, mut rsp) = create_lookup(&path); + let (cmd, mut rsp) = ops::Lookup::create(&path); get_filesystem_driver() .unwrap() .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - if rsp.header.error != 0 { + if unsafe { rsp.out_header.assume_init_ref().error } != 0 { // TODO: Correct error handling return Err(IoError::EIO); } - let rsp = unsafe { rsp.rsp.assume_init() }; + let rsp = unsafe { rsp.op_header.assume_init() }; let attr = rsp.attr; if attr.mode & S_IFMT != S_IFLNK { @@ -1570,13 +986,13 @@ impl VfsNode for FuseDirectory { debug!("FUSE lstat: {}", path); - let (cmd, mut rsp) = create_lookup(&path); + let (cmd, mut rsp) = ops::Lookup::create(&path); get_filesystem_driver() .unwrap() .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - let attr = unsafe { rsp.rsp.assume_init().attr }; + let attr = unsafe { rsp.op_header.assume_init().attr }; Ok(FileAttr::from(attr)) } @@ -1616,21 +1032,22 @@ impl VfsNode for FuseDirectory { // 3.FUSE_OPEN(nodeid, O_RDONLY) -> fh let (cmd, mut rsp) = - create_open(file_guard.fuse_nid.unwrap(), opt.bits().try_into().unwrap()); + ops::Open::create(file_guard.fuse_nid.unwrap(), opt.bits().try_into().unwrap()); get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - file_guard.fuse_fh = Some(unsafe { rsp.rsp.assume_init().fh }); + file_guard.fuse_fh = Some(unsafe { rsp.op_header.assume_init_ref().fh }); } else { // Create file (opens implicitly, returns results from both lookup and open calls) - let (cmd, mut rsp) = create_create(&path, opt.bits().try_into().unwrap(), mode.bits()); + let (cmd, mut rsp) = + ops::Create::create(&path, opt.bits().try_into().unwrap(), mode.bits()); get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - let inner = unsafe { rsp.rsp.assume_init() }; + let inner = unsafe { rsp.op_header.assume_init() }; file_guard.fuse_nid = Some(inner.entry.nodeid); file_guard.fuse_fh = Some(inner.open.fh); } @@ -1651,7 +1068,7 @@ impl VfsNode for FuseDirectory { .collect() }; - let (cmd, mut rsp) = create_unlink(&path); + let (cmd, mut rsp) = ops::Unlink::create(&path); get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() @@ -1672,7 +1089,7 @@ impl VfsNode for FuseDirectory { .collect() }; - let (cmd, mut rsp) = create_rmdir(&path); + let (cmd, mut rsp) = ops::Rmdir::create(&path); get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() @@ -1696,16 +1113,19 @@ impl VfsNode for FuseDirectory { .map(|v| "/".to_owned() + v) .collect() }; - let (cmd, mut rsp) = create_mkdir(&path, mode.bits()); + let (cmd, mut rsp) = ops::Mkdir::create(&path, mode.bits()); get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - if rsp.header.error == 0 { + if unsafe { rsp.out_header.assume_init_ref().error } == 0 { Ok(()) } else { - Err(num::FromPrimitive::from_i32(rsp.header.error).unwrap()) + Err( + num::FromPrimitive::from_i32(unsafe { rsp.out_header.assume_init_ref().error }) + .unwrap(), + ) } } } @@ -1714,7 +1134,7 @@ pub(crate) fn init() { debug!("Try to initialize fuse filesystem"); if let Some(driver) = get_filesystem_driver() { - let (cmd, mut rsp) = create_init(); + let (cmd, mut rsp) = ops::Init::create(); driver.lock().send_command(cmd.as_ref(), rsp.as_mut()); trace!("fuse init answer: {:?}", rsp); diff --git a/src/fs/fuse_abi.rs b/src/fs/fuse_abi.rs new file mode 100644 index 0000000000..d4628dcd18 --- /dev/null +++ b/src/fs/fuse_abi.rs @@ -0,0 +1,314 @@ +#[cfg(all(feature = "fuse", feature = "pci"))] +pub(crate) const ROOT_ID: u64 = 1; + +#[allow(dead_code)] +pub(crate) const GETATTR_FH: u32 = 1 << 0; + +#[repr(C)] +#[derive(Debug)] +pub(crate) struct Dirent { + pub d_ino: u64, + pub d_off: u64, + pub d_namelen: u32, + pub d_type: u32, + pub d_name: [u8; 0], +} + +#[repr(C)] +#[derive(Debug, Default)] +pub(crate) struct InHeader { + pub len: u32, + pub opcode: u32, + pub unique: u64, + pub nodeid: u64, + pub uid: u32, + pub gid: u32, + pub pid: u32, + pub padding: u32, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct OutHeader { + pub len: u32, + pub error: i32, + pub unique: u64, +} + +#[repr(C)] +#[derive(Debug, Default)] +pub(crate) struct InitIn { + pub major: u32, + pub minor: u32, + pub max_readahead: u32, + pub flags: u32, +} + +#[repr(C)] +#[derive(Debug, Default)] +pub(crate) struct InitOut { + pub major: u32, + pub minor: u32, + pub max_readahead: u32, + pub flags: u32, + pub max_background: u16, + pub congestion_threshold: u16, + pub max_write: u32, + pub time_gran: u32, + pub unused: [u32; 9], +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct ReadIn { + pub fh: u64, + pub offset: u64, + pub size: u32, + pub read_flags: u32, + pub lock_owner: u64, + pub flags: u32, + pub padding: u32, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub struct WriteIn { + pub fh: u64, + pub offset: u64, + pub size: u32, + pub write_flags: u32, + pub lock_owner: u64, + pub flags: u32, + pub padding: u32, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct WriteOut { + pub size: u32, + pub padding: u32, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct ReadOut {} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct LookupIn {} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct ReadlinkIn {} + +#[repr(C)] +#[derive(Default, Debug)] +pub struct ReadlinkOut {} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct AttrOut { + pub attr_valid: u64, + pub attr_valid_nsec: u32, + pub dummy: u32, + pub attr: Attr, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct EntryOut { + pub nodeid: u64, + pub generation: u64, + pub entry_valid: u64, + pub attr_valid: u64, + pub entry_valid_nsec: u32, + pub attr_valid_nsec: u32, + pub attr: Attr, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct Attr { + /// inode number + pub ino: u64, + /// size in bytes + pub size: u64, + /// size in blocks + pub blocks: u64, + /// time of last access + pub atime: u64, + /// time of last modification + pub mtime: u64, + /// time of last status change + pub ctime: u64, + pub atimensec: u32, + pub mtimensec: u32, + pub ctimensec: u32, + /// access permissions + pub mode: u32, + /// number of hard links + pub nlink: u32, + /// user id + pub uid: u32, + /// group id + pub gid: u32, + /// device id + pub rdev: u32, + /// block size + pub blksize: u32, + pub padding: u32, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct CreateIn { + pub flags: u32, + pub mode: u32, + pub umask: u32, + pub open_flags: u32, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct CreateOut { + pub entry: EntryOut, + pub open: OpenOut, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct OpenIn { + pub flags: u32, + pub unused: u32, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct OpenOut { + pub fh: u64, + pub open_flags: u32, + pub padding: u32, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct ReleaseIn { + pub fh: u64, + pub flags: u32, + pub release_flags: u32, + pub lock_owner: u64, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct ReleaseOut {} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct RmdirIn {} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct RmdirOut {} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct MkdirIn { + pub mode: u32, + pub umask: u32, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct UnlinkIn {} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct UnlinkOut {} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct LseekIn { + pub fh: u64, + pub offset: u64, + pub whence: u32, + pub padding: u32, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct LseekOut { + pub(crate) offset: u64, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct PollIn { + pub fh: u64, + pub kh: u64, + pub flags: u32, + pub events: u32, +} + +#[repr(C)] +#[derive(Default, Debug)] +pub(crate) struct PollOut { + pub revents: u32, + padding: u32, +} + +#[repr(u32)] +#[derive(Debug, Copy, Clone)] +#[allow(dead_code)] +pub(crate) enum Opcode { + Lookup = 1, + Forget = 2, // no reply + Getattr = 3, + Setattr = 4, + Readlink = 5, + Symlink = 6, + Mknod = 8, + Mkdir = 9, + Unlink = 10, + Rmdir = 11, + Rename = 12, + Link = 13, + Open = 14, + Read = 15, + Write = 16, + Statfs = 17, + Release = 18, + Fsync = 20, + Setxattr = 21, + Getxattr = 22, + Listxattr = 23, + Removexattr = 24, + Flush = 25, + Init = 26, + Opendir = 27, + Readdir = 28, + Releasedir = 29, + Fsyncdir = 30, + Getlk = 31, + Setlk = 32, + Setlkw = 33, + Access = 34, + Create = 35, + Interrupt = 36, + Bmap = 37, + Destroy = 38, + Ioctl = 39, + Poll = 40, + NotifyReply = 41, + BatchForget = 42, + Fallocate = 43, + Readdirplus = 44, + Rename2 = 45, + Lseek = 46, + + Setvolname = 61, + Getxtimes = 62, + Exchange = 63, + + CuseInit = 4096, +} diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 0f0a77c653..c9ce5382ce 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -1,5 +1,6 @@ #[cfg(all(feature = "fuse", feature = "pci"))] pub(crate) mod fuse; +mod fuse_abi; mod mem; mod uhyve;