From eb52dbe3aa60630e8c5244eac1f4c7479a63e189 Mon Sep 17 00:00:00 2001 From: simonschoening Date: Mon, 15 May 2023 20:21:12 +0200 Subject: [PATCH 01/15] Add first code to read directories --- src/fs/fuse.rs | 132 ++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 1 + src/syscalls/fs.rs | 10 ++++ 3 files changed, 141 insertions(+), 2 deletions(-) diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 9d2f19b446..4502d1bd81 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -10,7 +10,7 @@ use crate::arch::kernel::mmio::get_filesystem_driver; #[cfg(feature = "pci")] use crate::drivers::pci::get_filesystem_driver; use crate::drivers::virtio::virtqueue::AsSliceU8; -use crate::syscalls::fs::{self, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence}; +use crate::syscalls::fs::{self, Dirent, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence}; // 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 @@ -20,6 +20,16 @@ const FUSE_ROOT_ID: u64 = 1; const MAX_READ_LEN: usize = 1024 * 64; const MAX_WRITE_LEN: usize = 1024 * 64; +#[repr(C)] +#[derive(Debug)] +pub struct fuse_dirent_head { + pub d_ino: u64, + pub off: u64, + pub namelen: u32, + pub type_var: u32, // type is keyword? + // name buffer +} + pub trait FuseInterface { fn send_command(&mut self, cmd: &Cmd, rsp: &mut Rsp) where @@ -83,7 +93,7 @@ impl PosixFileSystem for Fuse { trace!("unlink answer {:?}", rsp); Ok(()) - } + } } impl Fuse { @@ -108,6 +118,35 @@ impl Fuse { .send_command(cmd.as_ref(), rsp.as_mut()); Some(unsafe { rsp.rsp.assume_init().nodeid }) } + + fn opendir(&self, path: &str) -> Result, FileError> { + let mut file = FuseFile { + fuse_nid: None, + fuse_fh: None, + offset: 0, + }; + + // Lookup nodeid + + file.fuse_nid = self.lookup(path); + + if file.fuse_nid.is_none() { + warn!("Fuse lookup seems to have failed!"); + return Err(FileError::ENOENT); + } + + // Opendir + let (mut cmd, mut rsp) = create_open(file.fuse_nid.unwrap(), 0x10000); + cmd.header.opcode = Opcode::FUSE_OPENDIR as u32; + get_filesystem_driver() + .ok_or(FileError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + file.fuse_fh = Some(unsafe { rsp.rsp.assume_init().fh }); + + + Ok(Box::new(file)) + } } impl Default for Fuse { @@ -222,6 +261,77 @@ impl PosixFile for FuseFile { Err(FileError::EIO) } } + + fn readdir(&mut self) -> Result, FileError> { + // Linux seems to allocate a single page to store the dirfile + let mut len = MAX_READ_LEN as u32; + + if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) { + let (mut cmd, mut rsp) = create_read(nid, fh, len, self.offset as u64); + cmd.header.opcode = Opcode::FUSE_READDIR as u32; + get_filesystem_driver() + .ok_or(FileError::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::() + >= len.try_into().unwrap() + { + len.try_into().unwrap() + } else { + rsp.header.len as usize + - ::core::mem::size_of::() + - ::core::mem::size_of::() + }; + + // See https://elixir.bootlin.com/linux/v6.3.1/source/fs/fuse/readdir.c#L126 + // and http://libfuse.github.io/doxygen/structfuse__lowlevel__ops.html#a65b7d7fc14d3958d7fb7d215fda20301 + // Also interesting: fuse_readdir_common in libfuse/lib/fuse.c + let mut buffer_ptr = rsp.extra_buffer.as_ptr() as *const u8; + let buffer_end = buffer_ptr as usize + len; + let mut dir_vec = alloc::vec::Vec::::new(); + + while buffer_end - buffer_ptr as usize > core::mem::size_of::() { + let dirent = unsafe { &*(buffer_ptr as *const fuse_dirent_head) }; + info!("Len: {}", dirent.namelen); + unsafe { + buffer_ptr = buffer_ptr.byte_add(core::mem::size_of::()) + }; + let name = unsafe { + core::slice::from_raw_parts( + buffer_ptr, + dirent.namelen as usize + ) + }; + dir_vec.push( + Dirent { + d_ino: dirent.d_ino, + off: dirent.off, + type_var: dirent.type_var, + name: String::from_utf8(name.to_vec()).unwrap(), + } + ); + self.offset = dirent.off as usize; + + unsafe { + buffer_ptr = buffer_ptr + .byte_add(dirent.namelen as usize); + // align_offset seems to be dangerous + buffer_ptr = buffer_ptr + .wrapping_add( + buffer_ptr.align_offset(core::mem::size_of::()) + ); + }; + } + + + Ok(dir_vec) + } else { + warn!("Dir not open, cannot read!"); + Err(FileError::ENOENT) + } + } } #[repr(u32)] @@ -1047,10 +1157,28 @@ pub fn init() { let fuse = Box::new(Fuse::new()); fuse.send_init(); + + let nid = fuse.lookup("/"); + info!("Root node id {}", nid.expect("No root node?")); + + // Print dir content + let mut root_dir = fuse.opendir("/").unwrap(); + + while let Ok(dirent_vec) = root_dir.readdir() { + info!("Result {:#?}", dirent_vec); + + // The filesystem will return an empty buffer when all dirs have been read + if dirent_vec.len() == 0 { + break; + } + } + let mut fs = fs::FILESYSTEM.lock(); let mount_point = driver.lock().get_mount_point(); info!("Mounting virtio-fs at /{}", mount_point); fs.mount(mount_point.as_str(), fuse) .expect("Mount failed. Duplicate mount_point?"); + + panic!("Stopping."); } } diff --git a/src/lib.rs b/src/lib.rs index af74179cf0..954bc0b340 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ #![feature(linked_list_cursors)] #![feature(maybe_uninit_slice)] #![feature(naked_functions)] +#![feature(pointer_byte_offsets)] #![cfg_attr(target_arch = "aarch64", feature(specialization))] #![feature(strict_provenance)] #![cfg_attr(target_os = "none", no_std)] diff --git a/src/syscalls/fs.rs b/src/syscalls/fs.rs index 2694bdb838..c05cb8864e 100644 --- a/src/syscalls/fs.rs +++ b/src/syscalls/fs.rs @@ -216,6 +216,8 @@ pub trait PosixFile { fn read(&mut self, len: u32) -> Result, FileError>; fn write(&mut self, buf: &[u8]) -> Result; fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result; + + fn readdir(&mut self) -> Result, FileError>; } // TODO: raw is partially redundant, create nicer interface @@ -231,6 +233,14 @@ pub struct FilePerms { pub mode: u32, } +#[derive(Debug)] +pub struct Dirent { + pub d_ino: u64, + pub off: u64, + pub type_var: u32, // type is keyword? + pub name : String, +} + #[derive(Debug, FromPrimitive, ToPrimitive)] pub enum SeekWhence { Set = 0, From a892b04ced3cb502dc8715a30483379ea31bdc17 Mon Sep 17 00:00:00 2001 From: simonschoening Date: Mon, 15 May 2023 21:10:41 +0200 Subject: [PATCH 02/15] Format code --- src/fs/fuse.rs | 50 +++++++++++++++++++--------------------------- src/syscalls/fs.rs | 2 +- 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 4502d1bd81..64fc6a26d9 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -10,7 +10,9 @@ use crate::arch::kernel::mmio::get_filesystem_driver; #[cfg(feature = "pci")] use crate::drivers::pci::get_filesystem_driver; use crate::drivers::virtio::virtqueue::AsSliceU8; -use crate::syscalls::fs::{self, Dirent, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence}; +use crate::syscalls::fs::{ + self, Dirent, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence, +}; // 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 @@ -27,7 +29,7 @@ pub struct fuse_dirent_head { pub off: u64, pub namelen: u32, pub type_var: u32, // type is keyword? - // name buffer + // name buffer } pub trait FuseInterface { @@ -93,7 +95,7 @@ impl PosixFileSystem for Fuse { trace!("unlink answer {:?}", rsp); Ok(()) - } + } } impl Fuse { @@ -137,14 +139,13 @@ impl Fuse { // Opendir let (mut cmd, mut rsp) = create_open(file.fuse_nid.unwrap(), 0x10000); - cmd.header.opcode = Opcode::FUSE_OPENDIR as u32; + cmd.header.opcode = Opcode::FUSE_OPENDIR as u32; get_filesystem_driver() .ok_or(FileError::ENOSYS)? .lock() .send_command(cmd.as_ref(), rsp.as_mut()); file.fuse_fh = Some(unsafe { rsp.rsp.assume_init().fh }); - Ok(Box::new(file)) } } @@ -287,7 +288,7 @@ impl PosixFile for FuseFile { // See https://elixir.bootlin.com/linux/v6.3.1/source/fs/fuse/readdir.c#L126 // and http://libfuse.github.io/doxygen/structfuse__lowlevel__ops.html#a65b7d7fc14d3958d7fb7d215fda20301 - // Also interesting: fuse_readdir_common in libfuse/lib/fuse.c + // Also interesting: fuse_readdir_common in libfuse/lib/fuse.c let mut buffer_ptr = rsp.extra_buffer.as_ptr() as *const u8; let buffer_end = buffer_ptr as usize + len; let mut dir_vec = alloc::vec::Vec::::new(); @@ -295,37 +296,27 @@ impl PosixFile for FuseFile { while buffer_end - buffer_ptr as usize > core::mem::size_of::() { let dirent = unsafe { &*(buffer_ptr as *const fuse_dirent_head) }; info!("Len: {}", dirent.namelen); - unsafe { + unsafe { buffer_ptr = buffer_ptr.byte_add(core::mem::size_of::()) }; - let name = unsafe { - core::slice::from_raw_parts( - buffer_ptr, - dirent.namelen as usize - ) - }; - dir_vec.push( - Dirent { - d_ino: dirent.d_ino, - off: dirent.off, - type_var: dirent.type_var, - name: String::from_utf8(name.to_vec()).unwrap(), - } - ); + let name = + unsafe { core::slice::from_raw_parts(buffer_ptr, dirent.namelen as usize) }; + dir_vec.push(Dirent { + d_ino: dirent.d_ino, + off: dirent.off, + type_var: dirent.type_var, + name: String::from_utf8(name.to_vec()).unwrap(), + }); self.offset = dirent.off as usize; - unsafe { - buffer_ptr = buffer_ptr - .byte_add(dirent.namelen as usize); + unsafe { + buffer_ptr = buffer_ptr.byte_add(dirent.namelen as usize); // align_offset seems to be dangerous buffer_ptr = buffer_ptr - .wrapping_add( - buffer_ptr.align_offset(core::mem::size_of::()) - ); + .wrapping_add(buffer_ptr.align_offset(core::mem::size_of::())); }; } - Ok(dir_vec) } else { warn!("Dir not open, cannot read!"); @@ -1157,7 +1148,6 @@ pub fn init() { let fuse = Box::new(Fuse::new()); fuse.send_init(); - let nid = fuse.lookup("/"); info!("Root node id {}", nid.expect("No root node?")); @@ -1178,7 +1168,7 @@ pub fn init() { info!("Mounting virtio-fs at /{}", mount_point); fs.mount(mount_point.as_str(), fuse) .expect("Mount failed. Duplicate mount_point?"); - + panic!("Stopping."); } } diff --git a/src/syscalls/fs.rs b/src/syscalls/fs.rs index c05cb8864e..4c2181f7be 100644 --- a/src/syscalls/fs.rs +++ b/src/syscalls/fs.rs @@ -238,7 +238,7 @@ pub struct Dirent { pub d_ino: u64, pub off: u64, pub type_var: u32, // type is keyword? - pub name : String, + pub name: String, } #[derive(Debug, FromPrimitive, ToPrimitive)] From dd79fcbc55ddfc0b59b072da94bf4fde1880e190 Mon Sep 17 00:00:00 2001 From: simonschoening Date: Fri, 19 May 2023 00:32:43 +0200 Subject: [PATCH 03/15] Test mkdir --- src/fs/fuse.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++ src/syscalls/fs.rs | 1 + 2 files changed, 80 insertions(+) diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 64fc6a26d9..bce926785a 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -263,6 +263,15 @@ impl PosixFile for FuseFile { } } + fn mkdir(&self, name: &str, mode: u32) -> Option { + let (mut cmd, mut rsp) = create_mkdir(self.fuse_nid?, name, mode); + get_filesystem_driver() + .unwrap() + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + Some(unsafe { rsp.rsp.assume_init().nodeid }) + } + fn readdir(&mut self) -> Result, FileError> { // Linux seems to allocate a single page to store the dirfile let mut len = MAX_READ_LEN as u32; @@ -1071,6 +1080,14 @@ pub struct fuse_create_in { } unsafe impl FuseIn for fuse_create_in {} +#[repr(C)] +#[derive(Default, Debug)] +pub struct fuse_mkdir_in { + pub mode: u32, + pub umask: u32, +} +unsafe impl FuseIn for fuse_mkdir_in {} + #[repr(C)] #[derive(Default, Debug)] pub struct fuse_create_out { @@ -1142,6 +1159,67 @@ fn create_create( (cmd, rsp) } +fn create_mkdir( + nid: u64, + 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::(nid, 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) +} + pub fn init() { if let Some(driver) = get_filesystem_driver() { // Instantiate global fuse object @@ -1153,6 +1231,7 @@ pub fn init() { // Print dir content let mut root_dir = fuse.opendir("/").unwrap(); + root_dir.mkdir("new_dir",0x777); while let Ok(dirent_vec) = root_dir.readdir() { info!("Result {:#?}", dirent_vec); diff --git a/src/syscalls/fs.rs b/src/syscalls/fs.rs index 4c2181f7be..fd1cf3c851 100644 --- a/src/syscalls/fs.rs +++ b/src/syscalls/fs.rs @@ -218,6 +218,7 @@ pub trait PosixFile { fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result; fn readdir(&mut self) -> Result, FileError>; + fn mkdir(&self, name: &str, mode: u32) -> Option; } // TODO: raw is partially redundant, create nicer interface From 3c97febabee191532a7af615b321a4d5a0f2b29a Mon Sep 17 00:00:00 2001 From: simonschoening Date: Fri, 26 May 2023 13:04:11 +0200 Subject: [PATCH 04/15] Experiments with FS traits --- src/fs/fuse.rs | 267 +++++++++++++++++++++++++++------------------ src/syscalls/fs.rs | 35 ++++-- 2 files changed, 185 insertions(+), 117 deletions(-) diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index bce926785a..b14201dacf 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -1,9 +1,12 @@ use alloc::alloc::{alloc, Layout}; use alloc::boxed::Box; +use alloc::collections::VecDeque; use alloc::string::String; use alloc::vec::Vec; +use alloc::borrow::ToOwned; use core::mem::MaybeUninit; use core::{fmt, u32, u8}; +use num_traits::cast::FromPrimitive; #[cfg(not(feature = "pci"))] use crate::arch::kernel::mmio::get_filesystem_driver; @@ -11,7 +14,7 @@ use crate::arch::kernel::mmio::get_filesystem_driver; use crate::drivers::pci::get_filesystem_driver; use crate::drivers::virtio::virtqueue::AsSliceU8; use crate::syscalls::fs::{ - self, Dirent, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence, + self, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence, PosixReadDir, PosixDirEntry, PosixFileType, }; // response out layout eg @ https://github.com/zargony/fuse-rs/blob/bf6d1cf03f3277e35b580f3c7b9999255d72ecf3/src/ll/request.rs#L44 @@ -86,6 +89,36 @@ impl PosixFileSystem for Fuse { Ok(Box::new(file)) } + fn readdir(&self, path: &str) -> Result, FileError> { + let mut readdir = FuseReaddir { + fuse_nid: None, + fuse_fh: None, + offset: 0, + path: path.to_owned(), + local_cache: VecDeque::new() + }; + + // Lookup nodeid + + readdir.fuse_nid = self.lookup(path); + + if readdir.fuse_nid.is_none() { + warn!("Fuse lookup seems to have failed!"); + return Err(FileError::ENOENT); + } + + // Opendir + let (mut cmd, mut rsp) = create_open(readdir.fuse_nid.unwrap(), 0x10000); + cmd.header.opcode = Opcode::FUSE_OPENDIR as u32; + get_filesystem_driver() + .ok_or(FileError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + readdir.fuse_fh = Some(unsafe { rsp.rsp.assume_init().fh }); + + Ok(Box::new(readdir)) + } + fn unlink(&self, path: &str) -> core::result::Result<(), FileError> { let (cmd, mut rsp) = create_unlink(path); get_filesystem_driver() @@ -120,34 +153,6 @@ impl Fuse { .send_command(cmd.as_ref(), rsp.as_mut()); Some(unsafe { rsp.rsp.assume_init().nodeid }) } - - fn opendir(&self, path: &str) -> Result, FileError> { - let mut file = FuseFile { - fuse_nid: None, - fuse_fh: None, - offset: 0, - }; - - // Lookup nodeid - - file.fuse_nid = self.lookup(path); - - if file.fuse_nid.is_none() { - warn!("Fuse lookup seems to have failed!"); - return Err(FileError::ENOENT); - } - - // Opendir - let (mut cmd, mut rsp) = create_open(file.fuse_nid.unwrap(), 0x10000); - cmd.header.opcode = Opcode::FUSE_OPENDIR as u32; - get_filesystem_driver() - .ok_or(FileError::ENOSYS)? - .lock() - .send_command(cmd.as_ref(), rsp.as_mut()); - file.fuse_fh = Some(unsafe { rsp.rsp.assume_init().fh }); - - Ok(Box::new(file)) - } } impl Default for Fuse { @@ -162,6 +167,110 @@ struct FuseFile { offset: usize, } +struct FuseReaddir { + fuse_nid: Option, + fuse_fh: Option, + offset: usize, + local_cache: VecDeque, + path: String, +} + +#[derive(Debug)] +struct FuseDirent { + d_ino: u64, + off: u64, + type_var: u32, // type is keyword? + name: String, +} + +impl PosixReadDir for FuseReaddir { + + fn next(&mut self) -> Option, FileError>> { + if self.local_cache.is_empty() { + // Linux seems to allocate a single page to store the dirfile + let len = MAX_READ_LEN as u32; + + if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) { + let (mut cmd, mut rsp) = create_read(nid, fh, len, self.offset as u64); + cmd.header.opcode = Opcode::FUSE_READDIR as u32; + if let Some(fs_driver) = get_filesystem_driver() { + fs_driver + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + } + else { + return Some(Err(FileError::ENOSYS)); + } + + let len: usize = if rsp.header.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::() + }; + + // See https://elixir.bootlin.com/linux/v6.3.1/source/fs/fuse/readdir.c#L126 + // and http://libfuse.github.io/doxygen/structfuse__lowlevel__ops.html#a65b7d7fc14d3958d7fb7d215fda20301 + // Also interesting: fuse_readdir_common in libfuse/lib/fuse.c + let mut buffer_ptr = rsp.extra_buffer.as_ptr() as *const u8; + let buffer_end = buffer_ptr as usize + len; + + while buffer_end - buffer_ptr as usize > core::mem::size_of::() { + let dirent = unsafe { &*(buffer_ptr as *const fuse_dirent_head) }; + // info!("Len: {}", dirent.namelen); + unsafe { + buffer_ptr = buffer_ptr.byte_add(core::mem::size_of::()) + }; + let name = + unsafe { core::slice::from_raw_parts(buffer_ptr, dirent.namelen as usize) }; + self.local_cache.push_back(FuseDirent { + d_ino: dirent.d_ino, + off: dirent.off, + type_var: dirent.type_var, + name: String::from_utf8(name.to_vec()).unwrap(), + }); + self.offset = dirent.off as usize; + + unsafe { + buffer_ptr = buffer_ptr.byte_add(dirent.namelen as usize); + // align_offset seems to be dangerous + buffer_ptr = buffer_ptr + .wrapping_add(buffer_ptr.align_offset(core::mem::size_of::())); + }; + } + + } else { + warn!("Dir not open, cannot read!"); + return Some(Err(FileError::ENOENT)); + } + } + + if self.local_cache.is_empty() { + None + } + else { + Some(Ok(Box::new(self.local_cache.pop_front().unwrap()))) + } + } +} + +impl PosixDirEntry for FuseDirent { + fn internal_path(&self) -> String { + unimplemented!() + } + fn file_name(&self) -> String { + self.name.to_owned() + } + fn file_type(&self) -> PosixFileType { + PosixFileType::from_u32(self.type_var).unwrap_or(PosixFileType::Unknown) + } +} + impl PosixFile for FuseFile { fn close(&mut self) -> Result<(), FileError> { let (cmd, mut rsp) = create_release(self.fuse_nid.unwrap(), self.fuse_fh.unwrap()); @@ -263,75 +372,14 @@ impl PosixFile for FuseFile { } } - fn mkdir(&self, name: &str, mode: u32) -> Option { - let (mut cmd, mut rsp) = create_mkdir(self.fuse_nid?, name, mode); - get_filesystem_driver() - .unwrap() - .lock() - .send_command(cmd.as_ref(), rsp.as_mut()); - Some(unsafe { rsp.rsp.assume_init().nodeid }) - } - - fn readdir(&mut self) -> Result, FileError> { - // Linux seems to allocate a single page to store the dirfile - let mut len = MAX_READ_LEN as u32; - - if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) { - let (mut cmd, mut rsp) = create_read(nid, fh, len, self.offset as u64); - cmd.header.opcode = Opcode::FUSE_READDIR as u32; - get_filesystem_driver() - .ok_or(FileError::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::() - >= len.try_into().unwrap() - { - len.try_into().unwrap() - } else { - rsp.header.len as usize - - ::core::mem::size_of::() - - ::core::mem::size_of::() - }; - - // See https://elixir.bootlin.com/linux/v6.3.1/source/fs/fuse/readdir.c#L126 - // and http://libfuse.github.io/doxygen/structfuse__lowlevel__ops.html#a65b7d7fc14d3958d7fb7d215fda20301 - // Also interesting: fuse_readdir_common in libfuse/lib/fuse.c - let mut buffer_ptr = rsp.extra_buffer.as_ptr() as *const u8; - let buffer_end = buffer_ptr as usize + len; - let mut dir_vec = alloc::vec::Vec::::new(); - - while buffer_end - buffer_ptr as usize > core::mem::size_of::() { - let dirent = unsafe { &*(buffer_ptr as *const fuse_dirent_head) }; - info!("Len: {}", dirent.namelen); - unsafe { - buffer_ptr = buffer_ptr.byte_add(core::mem::size_of::()) - }; - let name = - unsafe { core::slice::from_raw_parts(buffer_ptr, dirent.namelen as usize) }; - dir_vec.push(Dirent { - d_ino: dirent.d_ino, - off: dirent.off, - type_var: dirent.type_var, - name: String::from_utf8(name.to_vec()).unwrap(), - }); - self.offset = dirent.off as usize; - - unsafe { - buffer_ptr = buffer_ptr.byte_add(dirent.namelen as usize); - // align_offset seems to be dangerous - buffer_ptr = buffer_ptr - .wrapping_add(buffer_ptr.align_offset(core::mem::size_of::())); - }; - } - - Ok(dir_vec) - } else { - warn!("Dir not open, cannot read!"); - Err(FileError::ENOENT) - } - } + // fn mkdir(&self, name: &str, mode: u32) -> Option { + // let (mut cmd, mut rsp) = create_mkdir(self.fuse_nid?, name, mode); + // get_filesystem_driver() + // .unwrap() + // .lock() + // .send_command(cmd.as_ref(), rsp.as_mut()); + // Some(unsafe { rsp.rsp.assume_init().nodeid }) + // } } #[repr(u32)] @@ -1230,18 +1278,23 @@ pub fn init() { info!("Root node id {}", nid.expect("No root node?")); // Print dir content - let mut root_dir = fuse.opendir("/").unwrap(); - root_dir.mkdir("new_dir",0x777); - - while let Ok(dirent_vec) = root_dir.readdir() { - info!("Result {:#?}", dirent_vec); + let mut root_dir = fuse.readdir("/").unwrap(); + // root_dir.mkdir("new_dir",0x777); - // The filesystem will return an empty buffer when all dirs have been read - if dirent_vec.len() == 0 { - break; - } + while let Some(dirent) = root_dir.next() { + let dirent = dirent.unwrap(); + info!("Name: {:#?}, Type: {:#?}", dirent.file_name(), dirent.file_type()); } + // while let Ok(dirent_vec) = root_dir.readdir() { + // info!("Result {:#?}", dirent_vec); + + // // The filesystem will return an empty buffer when all dirs have been read + // if dirent_vec.len() == 0 { + // break; + // } + // } + let mut fs = fs::FILESYSTEM.lock(); let mount_point = driver.lock().get_mount_point(); info!("Mounting virtio-fs at /{}", mount_point); diff --git a/src/syscalls/fs.rs b/src/syscalls/fs.rs index fd1cf3c851..e160d32e46 100644 --- a/src/syscalls/fs.rs +++ b/src/syscalls/fs.rs @@ -208,6 +208,7 @@ pub enum FileError { pub trait PosixFileSystem { fn open(&self, _path: &str, _perms: FilePerms) -> Result, FileError>; + fn readdir(&self, path: &str) -> Result, FileError>; fn unlink(&self, _path: &str) -> Result<(), FileError>; } @@ -216,9 +217,31 @@ pub trait PosixFile { fn read(&mut self, len: u32) -> Result, FileError>; fn write(&mut self, buf: &[u8]) -> Result; fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result; +} + +pub trait PosixReadDir { + fn next(&mut self) -> Option, FileError>>; +} + + +#[derive(Debug, FromPrimitive, ToPrimitive)] +pub enum PosixFileType { + Unknown = 0, // DT_UNKNOWN + Fifo = 1, // DT_FIFO + CharacterDevice = 2, // DT_CHR + Directory = 4, // DT_DIR + BlockDevice = 6, // DT_BLK + RegularFile = 8, // DT_REG + SymbolicLink = 10, // DT_LNK + Socket = 12, // DT_SOCK + Whiteout = 14 // DT_WHT +} - fn readdir(&mut self) -> Result, FileError>; - fn mkdir(&self, name: &str, mode: u32) -> Option; +pub trait PosixDirEntry { + fn internal_path(&self) -> String; + fn file_name(&self) -> String; + fn file_type(&self) -> PosixFileType; + // fn metadata(&self) -> PosixMetadata; } // TODO: raw is partially redundant, create nicer interface @@ -234,14 +257,6 @@ pub struct FilePerms { pub mode: u32, } -#[derive(Debug)] -pub struct Dirent { - pub d_ino: u64, - pub off: u64, - pub type_var: u32, // type is keyword? - pub name: String, -} - #[derive(Debug, FromPrimitive, ToPrimitive)] pub enum SeekWhence { Set = 0, From e56aa904dd69105219418f9a992acd10b2278f71 Mon Sep 17 00:00:00 2001 From: simonschoening Date: Fri, 9 Jun 2023 12:49:03 +0200 Subject: [PATCH 05/15] Implement new syscalls --- src/fd/file.rs | 12 ++++ src/fd/mod.rs | 38 +++++++++++++ src/fs/fuse.rs | 133 +++++++++++++++++++------------------------- src/syscalls/fs.rs | 31 +++++++---- src/syscalls/mod.rs | 19 +++++++ 5 files changed, 147 insertions(+), 86 deletions(-) diff --git a/src/fd/file.rs b/src/fd/file.rs index 16a02d2caa..ffaab63425 100644 --- a/src/fd/file.rs +++ b/src/fd/file.rs @@ -98,6 +98,18 @@ impl ObjectInterface for GenericFile { ret as isize } + + fn readdir(&self) -> *const u64 { + debug!("readdir ! {}", self.0); + + let mut fs = fs::FILESYSTEM.lock(); + let mut ret: *const u64 = core::ptr::null(); + fs.fd_op(self.0, |file: &mut Box| { + ret = file.readdir().unwrap(); // TODO: might fail + }); + + ret + } } impl Drop for GenericFile { diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 6bd16cc316..a1638c2b1e 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -204,6 +204,14 @@ pub trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { -EINVAL } + /// 'readdir' returns a pointer to a dirent structure + /// representing the next directory entry in the directory stream + /// pointed to by the file descriptor + fn readdir(&self) -> *const u64 { + // TODO: Error handling + core::ptr::null() + } + /// `accept` a connection on a socket #[cfg(all(feature = "tcp", not(feature = "newlib")))] fn accept(&self, _addr: *mut sockaddr, _addrlen: *mut socklen_t) -> i32 { @@ -324,6 +332,36 @@ pub(crate) fn open(name: *const u8, flags: i32, mode: i32) -> Result Result { + if env::is_uhyve() { + Err(-EINVAL) + } else { + #[cfg(target_arch = "x86_64")] + { + let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); + debug!("Open directory {}", name); + + let mut fs = fs::FILESYSTEM.lock(); + if let Ok(filesystem_fd) = fs.opendir(name) { + let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); + // Would a GenericDir make sense? + let file = GenericFile::new(filesystem_fd); + if OBJECT_MAP.write().try_insert(fd, Arc::new(file)).is_err() { + Err(-EINVAL) + } else { + Ok(fd as FileDescriptor) + } + } else { + Err(-EINVAL) + } + } + #[cfg(not(target_arch = "x86_64"))] + { + Err(-ENOSYS) + } + } +} + pub(crate) fn get_object(fd: FileDescriptor) -> Result, i32> { Ok((*(OBJECT_MAP.read().get(&fd).ok_or(-EINVAL)?)).clone()) } diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index b14201dacf..a912f9c099 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -1,12 +1,9 @@ use alloc::alloc::{alloc, Layout}; use alloc::boxed::Box; -use alloc::collections::VecDeque; use alloc::string::String; use alloc::vec::Vec; -use alloc::borrow::ToOwned; use core::mem::MaybeUninit; use core::{fmt, u32, u8}; -use num_traits::cast::FromPrimitive; #[cfg(not(feature = "pci"))] use crate::arch::kernel::mmio::get_filesystem_driver; @@ -14,7 +11,7 @@ use crate::arch::kernel::mmio::get_filesystem_driver; use crate::drivers::pci::get_filesystem_driver; use crate::drivers::virtio::virtqueue::AsSliceU8; use crate::syscalls::fs::{ - self, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence, PosixReadDir, PosixDirEntry, PosixFileType, + self, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence, }; // response out layout eg @ https://github.com/zargony/fuse-rs/blob/bf6d1cf03f3277e35b580f3c7b9999255d72ecf3/src/ll/request.rs#L44 @@ -89,13 +86,13 @@ impl PosixFileSystem for Fuse { Ok(Box::new(file)) } - fn readdir(&self, path: &str) -> Result, FileError> { - let mut readdir = FuseReaddir { + fn opendir(&self, path: &str) -> Result, FileError> { + let mut readdir = FuseDir { fuse_nid: None, fuse_fh: None, offset: 0, - path: path.to_owned(), - local_cache: VecDeque::new() + buffer_copy: Vec::new(), + buffer_ptr: 0, }; // Lookup nodeid @@ -108,6 +105,7 @@ impl PosixFileSystem for Fuse { } // Opendir + // Flag 0x10000 for O_DIRECTORY might not be necessary let (mut cmd, mut rsp) = create_open(readdir.fuse_nid.unwrap(), 0x10000); cmd.header.opcode = Opcode::FUSE_OPENDIR as u32; get_filesystem_driver() @@ -167,26 +165,35 @@ struct FuseFile { offset: usize, } -struct FuseReaddir { +struct FuseDir { fuse_nid: Option, fuse_fh: Option, offset: usize, - local_cache: VecDeque, - path: String, + buffer_copy: Vec, + buffer_ptr: usize, } -#[derive(Debug)] -struct FuseDirent { - d_ino: u64, - off: u64, - type_var: u32, // type is keyword? - name: String, -} +impl PosixFile for FuseDir { + + fn close(&mut self) -> Result<(), FileError> { + Err(FileError::ENOSYS) + } + fn read(&mut self, len: u32) -> Result, FileError> { + // Use readdir instead + Err(FileError::EISDIR) + } + fn write(&mut self, buf: &[u8]) -> Result { + // Not opened for writing + Err(FileError::EBADF) + } + fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result { + Err(FileError::ENOSYS) + } -impl PosixReadDir for FuseReaddir { + fn readdir(&mut self) -> Result<*const u64, FileError> { + // Check if we need to read new direntries + if self.buffer_copy.is_empty() || self.buffer_copy.len() - self.buffer_ptr <= core::mem::size_of::() { - fn next(&mut self) -> Option, FileError>> { - if self.local_cache.is_empty() { // Linux seems to allocate a single page to store the dirfile let len = MAX_READ_LEN as u32; @@ -199,7 +206,7 @@ impl PosixReadDir for FuseReaddir { .send_command(cmd.as_ref(), rsp.as_mut()); } else { - return Some(Err(FileError::ENOSYS)); + return Err(FileError::ENOSYS); } let len: usize = if rsp.header.len as usize @@ -214,60 +221,32 @@ impl PosixReadDir for FuseReaddir { - ::core::mem::size_of::() }; - // See https://elixir.bootlin.com/linux/v6.3.1/source/fs/fuse/readdir.c#L126 - // and http://libfuse.github.io/doxygen/structfuse__lowlevel__ops.html#a65b7d7fc14d3958d7fb7d215fda20301 - // Also interesting: fuse_readdir_common in libfuse/lib/fuse.c - let mut buffer_ptr = rsp.extra_buffer.as_ptr() as *const u8; - let buffer_end = buffer_ptr as usize + len; - - while buffer_end - buffer_ptr as usize > core::mem::size_of::() { - let dirent = unsafe { &*(buffer_ptr as *const fuse_dirent_head) }; - // info!("Len: {}", dirent.namelen); - unsafe { - buffer_ptr = buffer_ptr.byte_add(core::mem::size_of::()) - }; - let name = - unsafe { core::slice::from_raw_parts(buffer_ptr, dirent.namelen as usize) }; - self.local_cache.push_back(FuseDirent { - d_ino: dirent.d_ino, - off: dirent.off, - type_var: dirent.type_var, - name: String::from_utf8(name.to_vec()).unwrap(), - }); - self.offset = dirent.off as usize; - - unsafe { - buffer_ptr = buffer_ptr.byte_add(dirent.namelen as usize); - // align_offset seems to be dangerous - buffer_ptr = buffer_ptr - .wrapping_add(buffer_ptr.align_offset(core::mem::size_of::())); - }; + if len <= core::mem::size_of::() { + return Ok(core::ptr::null()); } + + self.buffer_copy = unsafe { MaybeUninit::slice_assume_init_ref(&rsp.extra_buffer[..len]).to_vec() }; + self.buffer_ptr = 0; } else { warn!("Dir not open, cannot read!"); - return Some(Err(FileError::ENOENT)); + return Err(FileError::EBADF); } } - if self.local_cache.is_empty() { - None - } - else { - Some(Ok(Box::new(self.local_cache.pop_front().unwrap()))) - } - } -} + let return_ptr: *const u8 = self.buffer_copy.get(self.buffer_ptr).unwrap(); + + let dirent = unsafe { &*(self.buffer_ptr as *const fuse_dirent_head) }; + info!("Len: {}", dirent.namelen); -impl PosixDirEntry for FuseDirent { - fn internal_path(&self) -> String { - unimplemented!() - } - fn file_name(&self) -> String { - self.name.to_owned() - } - fn file_type(&self) -> PosixFileType { - PosixFileType::from_u32(self.type_var).unwrap_or(PosixFileType::Unknown) + self.offset = dirent.off as usize; + + + self.buffer_ptr = core::mem::size_of::() + dirent.namelen as usize; + self.buffer_ptr = (((self.buffer_ptr) + core::mem::size_of::() - 1) & (!(core::mem::size_of::() - 1))); + + // TODO: Check alignment + Ok(return_ptr as *const u64) } } @@ -372,6 +351,10 @@ impl PosixFile for FuseFile { } } + fn readdir(&mut self) -> Result<*const u64, FileError> { + Err(FileError::EBADF) + } + // fn mkdir(&self, name: &str, mode: u32) -> Option { // let (mut cmd, mut rsp) = create_mkdir(self.fuse_nid?, name, mode); // get_filesystem_driver() @@ -1278,13 +1261,13 @@ pub fn init() { info!("Root node id {}", nid.expect("No root node?")); // Print dir content - let mut root_dir = fuse.readdir("/").unwrap(); - // root_dir.mkdir("new_dir",0x777); + // let mut root_dir = fuse.opendir("/").unwrap(); + // root_dir.mkdir("new_dir",0o777); - while let Some(dirent) = root_dir.next() { - let dirent = dirent.unwrap(); - info!("Name: {:#?}, Type: {:#?}", dirent.file_name(), dirent.file_type()); - } + // while let Some(dirent) = root_dir.next() { + // let dirent = dirent.unwrap(); + // info!("Name: {:#?}, Type: {:#?}", dirent.file_name(), dirent.file_type()); + // } // while let Ok(dirent_vec) = root_dir.readdir() { // info!("Result {:#?}", dirent_vec); @@ -1301,6 +1284,6 @@ pub fn init() { fs.mount(mount_point.as_str(), fuse) .expect("Mount failed. Duplicate mount_point?"); - panic!("Stopping."); + // panic!("Stopping."); } } diff --git a/src/syscalls/fs.rs b/src/syscalls/fs.rs index e160d32e46..15690aa7e8 100644 --- a/src/syscalls/fs.rs +++ b/src/syscalls/fs.rs @@ -141,6 +141,14 @@ impl Filesystem { Ok(self.add_file(file)) } + /// Similar to open + pub fn opendir(&mut self, path: &str) -> Result { + debug!("Opening dir {}", path); + let (fs, internal_path) = self.parse_path(path)?; + let file = fs.opendir(internal_path)?; + Ok(self.add_file(file)) + } + /// Closes a file with given fd. /// If the file is currently open, closes it /// Remove the file from map of open files @@ -204,11 +212,17 @@ pub enum FileError { ENOSYS, #[cfg(feature = "pci")] EIO, + #[cfg(feature = "pci")] + EBADF, + #[cfg(feature = "pci")] + EINVAL, + #[cfg(feature = "pci")] + EISDIR, } pub trait PosixFileSystem { fn open(&self, _path: &str, _perms: FilePerms) -> Result, FileError>; - fn readdir(&self, path: &str) -> Result, FileError>; + fn opendir(&self, path: &str) -> Result, FileError>; fn unlink(&self, _path: &str) -> Result<(), FileError>; } @@ -217,12 +231,14 @@ pub trait PosixFile { fn read(&mut self, len: u32) -> Result, FileError>; fn write(&mut self, buf: &[u8]) -> Result; fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result; -} -pub trait PosixReadDir { - fn next(&mut self) -> Option, FileError>>; + fn readdir(&mut self) -> Result<*const u64, FileError>; } +// pub trait PosixReadDir { +// fn next(&mut self) -> Option, FileError>>; +// } + #[derive(Debug, FromPrimitive, ToPrimitive)] pub enum PosixFileType { @@ -237,13 +253,6 @@ pub enum PosixFileType { Whiteout = 14 // DT_WHT } -pub trait PosixDirEntry { - fn internal_path(&self) -> String; - fn file_name(&self) -> String; - fn file_type(&self) -> PosixFileType; - // fn metadata(&self) -> PosixMetadata; -} - // TODO: raw is partially redundant, create nicer interface #[derive(Clone, Copy, Debug, Default)] pub struct FilePerms { diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index e8a64769c8..7ebae4d5c1 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -105,6 +105,15 @@ pub extern "C" fn sys_unlink(name: *const u8) -> i32 { kernel_function!(__sys_unlink(name)) } +extern "C" fn __sys_opendir(name: *const u8) -> FileDescriptor { + crate::fd::opendir(name).map_or_else(|e| e, |v| v) +} + +#[no_mangle] +pub extern "C" fn sys_opendir(name: *const u8) -> FileDescriptor { + kernel_function!(__sys_opendir(name)) +} + extern "C" fn __sys_open(name: *const u8, flags: i32, mode: i32) -> FileDescriptor { crate::fd::open(name, flags, mode).map_or_else(|e| e, |v| v) } @@ -167,6 +176,16 @@ pub extern "C" fn sys_lseek(fd: FileDescriptor, offset: isize, whence: i32) -> i kernel_function!(__sys_lseek(fd, offset, whence)) } +extern "C" fn __sys_readdir(fd: FileDescriptor) -> *const u64 { + let obj = get_object(fd); + obj.map_or(core::ptr::null(), |v| (*v).readdir()) +} + +#[no_mangle] +pub extern "C" fn sys_readdir(fd: FileDescriptor) -> *const u64 { + kernel_function!(__sys_readdir(fd)) +} + extern "C" fn __sys_stat(file: *const u8, st: usize) -> i32 { SYS.stat(file, st) } From 75af64cf4144a3b81632871dc6a6db39d63960da Mon Sep 17 00:00:00 2001 From: simonschoening Date: Wed, 14 Jun 2023 22:40:44 +0200 Subject: [PATCH 06/15] First working version of readdir and opendir syscalls --- src/fd/file.rs | 6 +-- src/fd/mod.rs | 6 +-- src/fs/fuse.rs | 124 ++++++++++++++++++++++---------------------- src/lib.rs | 1 + src/syscalls/fs.rs | 30 +++++------ src/syscalls/mod.rs | 5 +- 6 files changed, 84 insertions(+), 88 deletions(-) diff --git a/src/fd/file.rs b/src/fd/file.rs index ffaab63425..8a7a47de84 100644 --- a/src/fd/file.rs +++ b/src/fd/file.rs @@ -5,7 +5,7 @@ use crate::fd::{ uhyve_send, ObjectInterface, SysClose, SysLseek, SysRead, SysWrite, UHYVE_PORT_CLOSE, UHYVE_PORT_LSEEK, UHYVE_PORT_READ, UHYVE_PORT_WRITE, }; -use crate::syscalls::fs::{self, PosixFile, SeekWhence}; +use crate::syscalls::fs::{self, Dirent, PosixFile, SeekWhence}; #[derive(Debug, Clone)] pub struct UhyveFile(i32); @@ -99,11 +99,11 @@ impl ObjectInterface for GenericFile { ret as isize } - fn readdir(&self) -> *const u64 { + fn readdir(&self) -> *const Dirent { debug!("readdir ! {}", self.0); let mut fs = fs::FILESYSTEM.lock(); - let mut ret: *const u64 = core::ptr::null(); + let mut ret: *const Dirent = core::ptr::null(); fs.fd_op(self.0, |file: &mut Box| { ret = file.readdir().unwrap(); // TODO: might fail }); diff --git a/src/fd/mod.rs b/src/fd/mod.rs index a1638c2b1e..82f6857e22 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -13,7 +13,7 @@ use crate::env; use crate::errno::*; use crate::fd::file::{GenericFile, UhyveFile}; use crate::fd::stdio::*; -use crate::syscalls::fs::{self, FilePerms, SeekWhence}; +use crate::syscalls::fs::{self, Dirent, FilePerms, SeekWhence}; #[cfg(all(feature = "tcp", not(feature = "newlib")))] use crate::syscalls::net::*; @@ -205,9 +205,9 @@ pub trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { } /// 'readdir' returns a pointer to a dirent structure - /// representing the next directory entry in the directory stream + /// representing the next directory entry in the directory stream /// pointed to by the file descriptor - fn readdir(&self) -> *const u64 { + fn readdir(&self) -> *const Dirent { // TODO: Error handling core::ptr::null() } diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index a912f9c099..062fec3f9e 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -11,7 +11,7 @@ use crate::arch::kernel::mmio::get_filesystem_driver; use crate::drivers::pci::get_filesystem_driver; use crate::drivers::virtio::virtqueue::AsSliceU8; use crate::syscalls::fs::{ - self, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence, + self, Dirent, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence, }; // response out layout eg @ https://github.com/zargony/fuse-rs/blob/bf6d1cf03f3277e35b580f3c7b9999255d72ecf3/src/ll/request.rs#L44 @@ -22,14 +22,16 @@ const FUSE_ROOT_ID: u64 = 1; const MAX_READ_LEN: usize = 1024 * 64; const MAX_WRITE_LEN: usize = 1024 * 64; +const U64_SIZE: u32 = ::core::mem::size_of::() as u32; + #[repr(C)] #[derive(Debug)] -pub struct fuse_dirent_head { +pub struct fuse_dirent { pub d_ino: u64, - pub off: u64, - pub namelen: u32, - pub type_var: u32, // type is keyword? - // name buffer + pub d_off: u64, + pub d_namelen: u32, + pub d_type: u32, + pub d_name: [u8; 0], } pub trait FuseInterface { @@ -87,12 +89,14 @@ impl PosixFileSystem for Fuse { } fn opendir(&self, path: &str) -> Result, FileError> { + debug!("opendir: {}", path); + let mut readdir = FuseDir { fuse_nid: None, fuse_fh: None, offset: 0, - buffer_copy: Vec::new(), - buffer_ptr: 0, + response: None, + buffer_offset: 0, }; // Lookup nodeid @@ -168,32 +172,43 @@ struct FuseFile { struct FuseDir { fuse_nid: Option, fuse_fh: Option, - offset: usize, - buffer_copy: Vec, - buffer_ptr: usize, + offset: u64, + response: Option>>, + buffer_offset: u32, } impl PosixFile for FuseDir { - fn close(&mut self) -> Result<(), FileError> { - Err(FileError::ENOSYS) + let (mut cmd, mut rsp) = create_release(self.fuse_nid.unwrap(), self.fuse_fh.unwrap()); + cmd.header.opcode = Opcode::FUSE_RELEASEDIR as u32; + get_filesystem_driver() + .ok_or(FileError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + + Ok(()) } - fn read(&mut self, len: u32) -> Result, FileError> { + fn read(&mut self, _len: u32) -> Result, FileError> { // Use readdir instead Err(FileError::EISDIR) } - fn write(&mut self, buf: &[u8]) -> Result { + fn write(&mut self, _buf: &[u8]) -> Result { // Not opened for writing Err(FileError::EBADF) } - fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result { + fn lseek(&mut self, _offset: isize, _whence: SeekWhence) -> Result { Err(FileError::ENOSYS) } - fn readdir(&mut self) -> Result<*const u64, FileError> { - // Check if we need to read new direntries - if self.buffer_copy.is_empty() || self.buffer_copy.len() - self.buffer_ptr <= core::mem::size_of::() { + fn readdir(&mut self) -> Result<*const Dirent, FileError> { + // This code is ugly + // Check if we have to read the directory via FUSE or still have a direntry in the last respnse + if self.response.as_ref().map_or(true, |resp| { + resp.header.len - self.buffer_offset + <= core::mem::size_of::().try_into().unwrap() + }) { + debug!("Read from FUSE dirfile"); // Linux seems to allocate a single page to store the dirfile let len = MAX_READ_LEN as u32; @@ -201,14 +216,11 @@ impl PosixFile for FuseDir { let (mut cmd, mut rsp) = create_read(nid, fh, len, self.offset as u64); cmd.header.opcode = Opcode::FUSE_READDIR as u32; if let Some(fs_driver) = get_filesystem_driver() { - fs_driver - .lock() - .send_command(cmd.as_ref(), rsp.as_mut()); - } - else { + fs_driver.lock().send_command(cmd.as_ref(), rsp.as_mut()); + } else { return Err(FileError::ENOSYS); } - + let len: usize = if rsp.header.len as usize - ::core::mem::size_of::() - ::core::mem::size_of::() @@ -221,32 +233,43 @@ impl PosixFile for FuseDir { - ::core::mem::size_of::() }; - if len <= core::mem::size_of::() { + if len <= core::mem::size_of::() { + debug!("No new dirs"); return Ok(core::ptr::null()); } - - self.buffer_copy = unsafe { MaybeUninit::slice_assume_init_ref(&rsp.extra_buffer[..len]).to_vec() }; - self.buffer_ptr = 0; + // Keep smart pointer to response + self.response = Some(rsp); + self.buffer_offset = 0; + + debug!("New buffer len: {}", len); } else { warn!("Dir not open, cannot read!"); return Err(FileError::EBADF); } } - let return_ptr: *const u8 = self.buffer_copy.get(self.buffer_ptr).unwrap(); - - let dirent = unsafe { &*(self.buffer_ptr as *const fuse_dirent_head) }; - info!("Len: {}", dirent.namelen); + let return_ptr: *const u8 = unsafe { + self.response + .as_ref() + .unwrap() + .extra_buffer + .as_ptr() + .byte_add(self.buffer_offset.try_into().unwrap()) as _ + }; + + let dirent = unsafe { &*(return_ptr as *const fuse_dirent) }; - self.offset = dirent.off as usize; + self.offset = dirent.d_off; + self.buffer_offset += core::mem::size_of::() as u32 + dirent.d_namelen; - self.buffer_ptr = core::mem::size_of::() + dirent.namelen as usize; - self.buffer_ptr = (((self.buffer_ptr) + core::mem::size_of::() - 1) & (!(core::mem::size_of::() - 1))); + // Allign to dirent struct + self.buffer_offset = ((self.buffer_offset) + U64_SIZE - 1) & (!(U64_SIZE - 1)); - // TODO: Check alignment - Ok(return_ptr as *const u64) + // Check alignment + assert!(return_ptr.is_aligned_to(U64_SIZE.try_into().unwrap())); + Ok(return_ptr.cast()) } } @@ -351,7 +374,7 @@ impl PosixFile for FuseFile { } } - fn readdir(&mut self) -> Result<*const u64, FileError> { + fn readdir(&mut self) -> Result<*const Dirent, FileError> { Err(FileError::EBADF) } @@ -1257,33 +1280,10 @@ pub fn init() { let fuse = Box::new(Fuse::new()); fuse.send_init(); - let nid = fuse.lookup("/"); - info!("Root node id {}", nid.expect("No root node?")); - - // Print dir content - // let mut root_dir = fuse.opendir("/").unwrap(); - // root_dir.mkdir("new_dir",0o777); - - // while let Some(dirent) = root_dir.next() { - // let dirent = dirent.unwrap(); - // info!("Name: {:#?}, Type: {:#?}", dirent.file_name(), dirent.file_type()); - // } - - // while let Ok(dirent_vec) = root_dir.readdir() { - // info!("Result {:#?}", dirent_vec); - - // // The filesystem will return an empty buffer when all dirs have been read - // if dirent_vec.len() == 0 { - // break; - // } - // } - let mut fs = fs::FILESYSTEM.lock(); let mount_point = driver.lock().get_mount_point(); info!("Mounting virtio-fs at /{}", mount_point); fs.mount(mount_point.as_str(), fuse) .expect("Mount failed. Duplicate mount_point?"); - - // panic!("Stopping."); } } diff --git a/src/lib.rs b/src/lib.rs index 954bc0b340..ad5e2a09f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ #![feature(maybe_uninit_slice)] #![feature(naked_functions)] #![feature(pointer_byte_offsets)] +#![feature(pointer_is_aligned)] #![cfg_attr(target_arch = "aarch64", feature(specialization))] #![feature(strict_provenance)] #![cfg_attr(target_os = "none", no_std)] diff --git a/src/syscalls/fs.rs b/src/syscalls/fs.rs index 15690aa7e8..d90cc1155a 100644 --- a/src/syscalls/fs.rs +++ b/src/syscalls/fs.rs @@ -42,6 +42,7 @@ use hermit_sync::TicketMutex; /// TODO: /// - FileDescriptor newtype use crate::env::is_uhyve; +pub use crate::fs::fuse::fuse_dirent as Dirent; // TODO: lazy static could be replaced with explicit init on OS boot. pub static FILESYSTEM: TicketMutex = TicketMutex::new(Filesystem::new()); @@ -96,7 +97,7 @@ impl Filesystem { pathsplit.next(); // empty, since first char is / let mount = pathsplit.next().unwrap(); - let internal_path = pathsplit.next().unwrap(); + let internal_path = pathsplit.next().unwrap_or("/"); if let Some(fs) = self.mounts.get(mount) { return Ok((fs.deref(), internal_path)); } @@ -111,7 +112,7 @@ impl Filesystem { } else { "." }; - let internal_path = pathsplit.next().unwrap(); + let internal_path = pathsplit.next().unwrap_or("/"); debug!( "Assume that the directory '{}' is used as mount point!", @@ -215,8 +216,6 @@ pub enum FileError { #[cfg(feature = "pci")] EBADF, #[cfg(feature = "pci")] - EINVAL, - #[cfg(feature = "pci")] EISDIR, } @@ -232,25 +231,20 @@ pub trait PosixFile { fn write(&mut self, buf: &[u8]) -> Result; fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result; - fn readdir(&mut self) -> Result<*const u64, FileError>; + fn readdir(&mut self) -> Result<*const Dirent, FileError>; } -// pub trait PosixReadDir { -// fn next(&mut self) -> Option, FileError>>; -// } - - #[derive(Debug, FromPrimitive, ToPrimitive)] pub enum PosixFileType { - Unknown = 0, // DT_UNKNOWN - Fifo = 1, // DT_FIFO + Unknown = 0, // DT_UNKNOWN + Fifo = 1, // DT_FIFO CharacterDevice = 2, // DT_CHR - Directory = 4, // DT_DIR - BlockDevice = 6, // DT_BLK - RegularFile = 8, // DT_REG - SymbolicLink = 10, // DT_LNK - Socket = 12, // DT_SOCK - Whiteout = 14 // DT_WHT + Directory = 4, // DT_DIR + BlockDevice = 6, // DT_BLK + RegularFile = 8, // DT_REG + SymbolicLink = 10, // DT_LNK + Socket = 12, // DT_SOCK + Whiteout = 14, // DT_WHT } // TODO: raw is partially redundant, create nicer interface diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index 7ebae4d5c1..b52558cec8 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -16,6 +16,7 @@ pub use self::tasks::*; pub use self::timer::*; use crate::env; use crate::fd::{dup_object, get_object, remove_object, FileDescriptor}; +use crate::syscalls::fs::Dirent; use crate::syscalls::interfaces::SyscallInterface; #[cfg(target_os = "none")] use crate::{__sys_free, __sys_malloc, __sys_realloc}; @@ -176,13 +177,13 @@ pub extern "C" fn sys_lseek(fd: FileDescriptor, offset: isize, whence: i32) -> i kernel_function!(__sys_lseek(fd, offset, whence)) } -extern "C" fn __sys_readdir(fd: FileDescriptor) -> *const u64 { +extern "C" fn __sys_readdir(fd: FileDescriptor) -> *const Dirent { let obj = get_object(fd); obj.map_or(core::ptr::null(), |v| (*v).readdir()) } #[no_mangle] -pub extern "C" fn sys_readdir(fd: FileDescriptor) -> *const u64 { +pub extern "C" fn sys_readdir(fd: FileDescriptor) -> *const Dirent { kernel_function!(__sys_readdir(fd)) } From 9f23aacc9f01c120f59c30b149b7ac60c4bcf0cf Mon Sep 17 00:00:00 2001 From: simonschoening Date: Thu, 15 Jun 2023 21:26:57 +0200 Subject: [PATCH 07/15] Code refactoring --- src/fs/fuse.rs | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 062fec3f9e..05772acae7 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -201,18 +201,24 @@ impl PosixFile for FuseDir { } fn readdir(&mut self) -> Result<*const Dirent, FileError> { - // This code is ugly - // Check if we have to read the directory via FUSE or still have a direntry in the last respnse - if self.response.as_ref().map_or(true, |resp| { - resp.header.len - self.buffer_offset - <= core::mem::size_of::().try_into().unwrap() - }) { - debug!("Read from FUSE dirfile"); - // Linux seems to allocate a single page to store the dirfile - let len = MAX_READ_LEN as u32; - - if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) { + let resp: &_ = match &mut self.response { + Some(resp) + if resp.header.len - self.buffer_offset + > core::mem::size_of::().try_into().unwrap() => + { + resp + } + option => { + debug!("Read from FUSE dirfile"); + // Linux seems to allocate a single page to store the dirfile + let len = MAX_READ_LEN as u32; + + let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) else { + warn!("Dir not open, cannot read!"); + return Err(FileError::EBADF); + }; + let (mut cmd, mut rsp) = create_read(nid, fh, len, self.offset as u64); cmd.header.opcode = Opcode::FUSE_READDIR as u32; if let Some(fs_driver) = get_filesystem_driver() { @@ -239,21 +245,16 @@ impl PosixFile for FuseDir { } // Keep smart pointer to response - self.response = Some(rsp); + let rsp = option.insert(rsp); self.buffer_offset = 0; debug!("New buffer len: {}", len); - } else { - warn!("Dir not open, cannot read!"); - return Err(FileError::EBADF); + rsp } - } + }; let return_ptr: *const u8 = unsafe { - self.response - .as_ref() - .unwrap() - .extra_buffer + resp.extra_buffer .as_ptr() .byte_add(self.buffer_offset.try_into().unwrap()) as _ }; From 2a8e80e413b944517a2510c3e4ab2e6579f9b0e3 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Mon, 19 Jun 2023 17:14:28 +0200 Subject: [PATCH 08/15] Add basic rmdir support --- src/fs/fuse.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++++ src/syscalls/fs.rs | 2 ++ 2 files changed, 78 insertions(+) diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 05772acae7..737d7a1654 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -131,6 +131,17 @@ impl PosixFileSystem for Fuse { Ok(()) } + + fn rmdir(&self, path: &str) -> core::result::Result<(), FileError> { + let (cmd, mut rsp) = create_rmdir(path); + get_filesystem_driver() + .ok_or(FileError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + trace!("rmdir answer {:?}", rsp); + + Ok(()) + } } impl Fuse { @@ -1275,6 +1286,71 @@ fn create_mkdir( (cmd, rsp) } +#[repr(C)] +#[derive(Default, Debug)] +pub struct fuse_rmdir_in {} +unsafe impl FuseIn for fuse_rmdir_in {} + +#[repr(C)] +#[derive(Default, Debug)] +pub struct fuse_rmdir_out {} +unsafe impl FuseOut for fuse_rmdir_out {} + +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) +} + pub fn init() { if let Some(driver) = get_filesystem_driver() { // Instantiate global fuse object diff --git a/src/syscalls/fs.rs b/src/syscalls/fs.rs index d90cc1155a..3381c976e3 100644 --- a/src/syscalls/fs.rs +++ b/src/syscalls/fs.rs @@ -223,6 +223,8 @@ pub trait PosixFileSystem { fn open(&self, _path: &str, _perms: FilePerms) -> Result, FileError>; fn opendir(&self, path: &str) -> Result, FileError>; fn unlink(&self, _path: &str) -> Result<(), FileError>; + + fn rmdir(&self, _path: &str) -> Result<(), FileError>; } pub trait PosixFile { From 031be37a84f105303f4005a9877092ea4da3e089 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Mon, 19 Jun 2023 23:15:51 +0200 Subject: [PATCH 09/15] Add syscall support to rmdir --- src/fd/mod.rs | 7 ++++++- src/syscalls/fs.rs | 8 ++++++++ src/syscalls/interfaces/mod.rs | 12 ++++++++++++ src/syscalls/mod.rs | 9 +++++++++ 4 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 82f6857e22..db3811d35d 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -199,11 +199,16 @@ pub trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { (-EINVAL).try_into().unwrap() } - /// `unlink` removes directory entry + /// `unlink` removes file entry fn unlink(&self, _name: *const u8) -> i32 { -EINVAL } + /// `rmdir` removes directory entry + fn rmdir(&self, _name: *const u8) -> i32 { + -EINVAL + } + /// 'readdir' returns a pointer to a dirent structure /// representing the next directory entry in the directory stream /// pointed to by the file descriptor diff --git a/src/syscalls/fs.rs b/src/syscalls/fs.rs index 3381c976e3..4a5aed57ac 100644 --- a/src/syscalls/fs.rs +++ b/src/syscalls/fs.rs @@ -169,6 +169,14 @@ impl Filesystem { Ok(()) } + /// Remove directory given by path + pub fn rmdir(&mut self, path: &str) -> Result<(), FileError> { + debug!("Removing directory {}", path); + let (fs, internal_path) = self.parse_path(path)?; + fs.rmdir(internal_path)?; + Ok(()) + } + /// Create new backing-fs at mountpoint mntpath #[cfg(feature = "pci")] pub fn mount( diff --git a/src/syscalls/interfaces/mod.rs b/src/syscalls/interfaces/mod.rs index 3bc3dbc7cc..1a0f928bc2 100644 --- a/src/syscalls/interfaces/mod.rs +++ b/src/syscalls/interfaces/mod.rs @@ -73,6 +73,18 @@ pub trait SyscallInterface: Send + Sync { 0 } + #[cfg(target_arch = "x86_64")] + fn rmdir(&self, name: *const u8) -> i32 { + let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); + debug!("rmdir {}", name); + + fs::FILESYSTEM + .lock() + .rmdir(name) + .expect("Removing directory failed!"); // TODO: error handling + 0 + } + fn stat(&self, _file: *const u8, _st: usize) -> i32 { info!("stat is unimplemented"); -ENOSYS diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index b52558cec8..50263ffc9f 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -106,6 +106,15 @@ pub extern "C" fn sys_unlink(name: *const u8) -> i32 { kernel_function!(__sys_unlink(name)) } +extern "C" fn __sys_rmdir(name: *const u8) -> i32 { + SYS.rmdir(name) +} + +#[no_mangle] +pub extern "C" fn sys_rmdir(name: *const u8) -> i32 { + kernel_function!(__sys_rmdir(name)) +} + extern "C" fn __sys_opendir(name: *const u8) -> FileDescriptor { crate::fd::opendir(name).map_or_else(|e| e, |v| v) } From e4971201cb5ef0a14d841aed8ad9f802e2833c21 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Mon, 26 Jun 2023 19:39:50 +0200 Subject: [PATCH 10/15] Continue mkdir implementation --- src/fd/file.rs | 15 +++++++++++++++ src/fd/mod.rs | 5 +++++ src/fs/fuse.rs | 20 ++++++++++++-------- src/syscalls/fs.rs | 1 + src/syscalls/mod.rs | 10 ++++++++++ 5 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/fd/file.rs b/src/fd/file.rs index 8a7a47de84..45a147064a 100644 --- a/src/fd/file.rs +++ b/src/fd/file.rs @@ -1,5 +1,6 @@ use alloc::boxed::Box; use core::{isize, slice}; +use core::ffi::CStr; use crate::fd::{ uhyve_send, ObjectInterface, SysClose, SysLseek, SysRead, SysWrite, UHYVE_PORT_CLOSE, @@ -110,6 +111,20 @@ impl ObjectInterface for GenericFile { ret } + + fn mkdir(&self, name: *const u8, mode: u32) -> i32 { + let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); + + debug!("mkdir ! {}, {}, {}", self.0, name, mode); + + let mut fs = fs::FILESYSTEM.lock(); + let mut ret = 0; + fs.fd_op(self.0, |file: &mut Box| { + ret = file.mkdir(name, mode).unwrap(); // TODO: might fail + }); + + ret + } } impl Drop for GenericFile { diff --git a/src/fd/mod.rs b/src/fd/mod.rs index db3811d35d..626f1bdea4 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -217,6 +217,11 @@ pub trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { core::ptr::null() } + /// `mkdir` creates a directory entry + fn mkdir(&self, _name: *const u8, _mode: u32) -> i32 { + -EINVAL + } + /// `accept` a connection on a socket #[cfg(all(feature = "tcp", not(feature = "newlib")))] fn accept(&self, _addr: *mut sockaddr, _addrlen: *mut socklen_t) -> i32 { diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 737d7a1654..3cb4764749 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -283,6 +283,15 @@ impl PosixFile for FuseDir { assert!(return_ptr.is_aligned_to(U64_SIZE.try_into().unwrap())); Ok(return_ptr.cast()) } + + fn mkdir(&self, name: &str, mode: u32) -> Result { + let (mut cmd, mut rsp) = create_mkdir(self.fuse_nid.unwrap(), name, mode); + get_filesystem_driver() + .ok_or(FileError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + Ok(unsafe { rsp.rsp.assume_init().nodeid.try_into().unwrap() }) + } } impl PosixFile for FuseFile { @@ -390,14 +399,9 @@ impl PosixFile for FuseFile { Err(FileError::EBADF) } - // fn mkdir(&self, name: &str, mode: u32) -> Option { - // let (mut cmd, mut rsp) = create_mkdir(self.fuse_nid?, name, mode); - // get_filesystem_driver() - // .unwrap() - // .lock() - // .send_command(cmd.as_ref(), rsp.as_mut()); - // Some(unsafe { rsp.rsp.assume_init().nodeid }) - // } + fn mkdir(&self, name: &str, mode: u32) -> Result { + Err(FileError::EBADF) + } } #[repr(u32)] diff --git a/src/syscalls/fs.rs b/src/syscalls/fs.rs index 4a5aed57ac..4b8d5d41e2 100644 --- a/src/syscalls/fs.rs +++ b/src/syscalls/fs.rs @@ -242,6 +242,7 @@ pub trait PosixFile { fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result; fn readdir(&mut self) -> Result<*const Dirent, FileError>; + fn mkdir(&self, name: &str, mode: u32) -> Result; } #[derive(Debug, FromPrimitive, ToPrimitive)] diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index 50263ffc9f..c8233f5135 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -196,6 +196,16 @@ pub extern "C" fn sys_readdir(fd: FileDescriptor) -> *const Dirent { kernel_function!(__sys_readdir(fd)) } +extern "C" fn __sys_mkdir(fd: FileDescriptor, name: *const u8, mode: u32) -> i32 { + let obj = get_object(fd); + obj.map_or_else(|e| e, |v| (*v).mkdir(name, mode)) +} + +#[no_mangle] +pub extern "C" fn sys_mkdir(fd: FileDescriptor, name: *const u8, mode: u32) -> i32 { + kernel_function!(__sys_mkdir(fd, name, mode)) +} + extern "C" fn __sys_stat(file: *const u8, st: usize) -> i32 { SYS.stat(file, st) } From 86405857e4332336c2c4e3fe834d35958584c8ca Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Tue, 27 Jun 2023 15:47:27 +0200 Subject: [PATCH 11/15] Refactor so mkdir works relative to FUSE root dir --- src/fd/file.rs | 15 ---------- src/fs/fuse.rs | 53 ++++++++++++++++++++++++---------- src/syscalls/fs.rs | 10 ++++++- src/syscalls/interfaces/mod.rs | 12 ++++++++ src/syscalls/mod.rs | 19 ++++++------ 5 files changed, 68 insertions(+), 41 deletions(-) diff --git a/src/fd/file.rs b/src/fd/file.rs index 45a147064a..8a7a47de84 100644 --- a/src/fd/file.rs +++ b/src/fd/file.rs @@ -1,6 +1,5 @@ use alloc::boxed::Box; use core::{isize, slice}; -use core::ffi::CStr; use crate::fd::{ uhyve_send, ObjectInterface, SysClose, SysLseek, SysRead, SysWrite, UHYVE_PORT_CLOSE, @@ -111,20 +110,6 @@ impl ObjectInterface for GenericFile { ret } - - fn mkdir(&self, name: *const u8, mode: u32) -> i32 { - let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); - - debug!("mkdir ! {}, {}, {}", self.0, name, mode); - - let mut fs = fs::FILESYSTEM.lock(); - let mut ret = 0; - fs.fd_op(self.0, |file: &mut Box| { - ret = file.mkdir(name, mode).unwrap(); // TODO: might fail - }); - - ret - } } impl Drop for GenericFile { diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 3cb4764749..885ff6cf38 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -142,6 +142,21 @@ impl PosixFileSystem for Fuse { Ok(()) } + + fn mkdir(&self, name: &str, mode: u32) -> Result { + info!("Mkdir: {}, mode: {}", name, mode); + + let (mut cmd, mut rsp) = create_mkdir(name, mode); + + info!("Cmd: {:?}", cmd); + info!("Rsp: {:?}", rsp); + + get_filesystem_driver() + .ok_or(FileError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + Ok(unsafe { rsp.rsp.assume_init().nodeid.try_into().unwrap() }) + } } impl Fuse { @@ -283,15 +298,6 @@ impl PosixFile for FuseDir { assert!(return_ptr.is_aligned_to(U64_SIZE.try_into().unwrap())); Ok(return_ptr.cast()) } - - fn mkdir(&self, name: &str, mode: u32) -> Result { - let (mut cmd, mut rsp) = create_mkdir(self.fuse_nid.unwrap(), name, mode); - get_filesystem_driver() - .ok_or(FileError::ENOSYS)? - .lock() - .send_command(cmd.as_ref(), rsp.as_mut()); - Ok(unsafe { rsp.rsp.assume_init().nodeid.try_into().unwrap() }) - } } impl PosixFile for FuseFile { @@ -398,10 +404,6 @@ impl PosixFile for FuseFile { fn readdir(&mut self) -> Result<*const Dirent, FileError> { Err(FileError::EBADF) } - - fn mkdir(&self, name: &str, mode: u32) -> Result { - Err(FileError::EBADF) - } } #[repr(u32)] @@ -1230,7 +1232,6 @@ fn create_create( } fn create_mkdir( - nid: u64, path: &str, mode: u32, ) -> (Box>, Box>) { @@ -1252,7 +1253,7 @@ fn create_mkdir( 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::(nid, Opcode::FUSE_MKDIR); + (*raw).header = create_in_header::(FUSE_ROOT_ID, Opcode::FUSE_MKDIR); (*raw).header.len = len.try_into().unwrap(); (*raw).cmd = fuse_mkdir_in { mode, @@ -1361,6 +1362,28 @@ pub fn init() { let fuse = Box::new(Fuse::new()); fuse.send_init(); + // let nid = fuse.lookup("/"); + // info!("Root node id {}", nid.expect("No root node?")); + + // // Print dir content + // let mut root_dir = fuse.opendir("/").unwrap(); + // root_dir.mkdir("new_dir",0o777); + // fuse.rmdir("new_dir"); + + // while let Some(dirent) = root_dir.next() { + // let dirent = dirent.unwrap(); + // info!("Name: {:#?}, Type: {:#?}", dirent.file_name(), dirent.file_type()); + // } + + // while let Ok(dirent_vec) = root_dir.readdir() { + // info!("Result {:#?}", dirent_vec); + + // // The filesystem will return an empty buffer when all dirs have been read + // if dirent_vec.len() == 0 { + // break; + // } + // } + let mut fs = fs::FILESYSTEM.lock(); let mount_point = driver.lock().get_mount_point(); info!("Mounting virtio-fs at /{}", mount_point); diff --git a/src/syscalls/fs.rs b/src/syscalls/fs.rs index 4b8d5d41e2..44c9f33c55 100644 --- a/src/syscalls/fs.rs +++ b/src/syscalls/fs.rs @@ -177,6 +177,14 @@ impl Filesystem { Ok(()) } + /// Create directory given by path + pub fn mkdir(&mut self, path: &str, mode: u32) -> Result<(), FileError> { + debug!("Removing directory {}", path); + let (fs, internal_path) = self.parse_path(path)?; + fs.mkdir(internal_path, mode)?; + Ok(()) + } + /// Create new backing-fs at mountpoint mntpath #[cfg(feature = "pci")] pub fn mount( @@ -233,6 +241,7 @@ pub trait PosixFileSystem { fn unlink(&self, _path: &str) -> Result<(), FileError>; fn rmdir(&self, _path: &str) -> Result<(), FileError>; + fn mkdir(&self, name: &str, mode: u32) -> Result; } pub trait PosixFile { @@ -242,7 +251,6 @@ pub trait PosixFile { fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result; fn readdir(&mut self) -> Result<*const Dirent, FileError>; - fn mkdir(&self, name: &str, mode: u32) -> Result; } #[derive(Debug, FromPrimitive, ToPrimitive)] diff --git a/src/syscalls/interfaces/mod.rs b/src/syscalls/interfaces/mod.rs index 1a0f928bc2..94328ef1e6 100644 --- a/src/syscalls/interfaces/mod.rs +++ b/src/syscalls/interfaces/mod.rs @@ -85,6 +85,18 @@ pub trait SyscallInterface: Send + Sync { 0 } + #[cfg(target_arch = "x86_64")] + fn mkdir(&self, name: *const u8, mode: u32) -> i32 { + let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); + debug!("mkdir {}, mode {}", name, mode); + + fs::FILESYSTEM + .lock() + .mkdir(name, mode) + .expect("Creating directory failed!"); // TODO: error handling + 0 + } + fn stat(&self, _file: *const u8, _st: usize) -> i32 { info!("stat is unimplemented"); -ENOSYS diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index c8233f5135..bcf2f5b916 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -106,6 +106,15 @@ pub extern "C" fn sys_unlink(name: *const u8) -> i32 { kernel_function!(__sys_unlink(name)) } +extern "C" fn __sys_mkdir(name: *const u8, mode: u32) -> i32 { + SYS.mkdir(name, mode) +} + +#[no_mangle] +pub extern "C" fn sys_mkdir(name: *const u8, mode: u32) -> i32 { + kernel_function!(__sys_mkdir(name, mode)) +} + extern "C" fn __sys_rmdir(name: *const u8) -> i32 { SYS.rmdir(name) } @@ -196,16 +205,6 @@ pub extern "C" fn sys_readdir(fd: FileDescriptor) -> *const Dirent { kernel_function!(__sys_readdir(fd)) } -extern "C" fn __sys_mkdir(fd: FileDescriptor, name: *const u8, mode: u32) -> i32 { - let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).mkdir(name, mode)) -} - -#[no_mangle] -pub extern "C" fn sys_mkdir(fd: FileDescriptor, name: *const u8, mode: u32) -> i32 { - kernel_function!(__sys_mkdir(fd, name, mode)) -} - extern "C" fn __sys_stat(file: *const u8, st: usize) -> i32 { SYS.stat(file, st) } From 1f9cb1ecd746d7e1ac5aef1222b1cd9bc8289392 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Tue, 27 Jun 2023 15:50:32 +0200 Subject: [PATCH 12/15] Remove test code --- src/fs/fuse.rs | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 885ff6cf38..1f5a503ec3 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -1362,28 +1362,6 @@ pub fn init() { let fuse = Box::new(Fuse::new()); fuse.send_init(); - // let nid = fuse.lookup("/"); - // info!("Root node id {}", nid.expect("No root node?")); - - // // Print dir content - // let mut root_dir = fuse.opendir("/").unwrap(); - // root_dir.mkdir("new_dir",0o777); - // fuse.rmdir("new_dir"); - - // while let Some(dirent) = root_dir.next() { - // let dirent = dirent.unwrap(); - // info!("Name: {:#?}, Type: {:#?}", dirent.file_name(), dirent.file_type()); - // } - - // while let Ok(dirent_vec) = root_dir.readdir() { - // info!("Result {:#?}", dirent_vec); - - // // The filesystem will return an empty buffer when all dirs have been read - // if dirent_vec.len() == 0 { - // break; - // } - // } - let mut fs = fs::FILESYSTEM.lock(); let mount_point = driver.lock().get_mount_point(); info!("Mounting virtio-fs at /{}", mount_point); From 82acb98221aa0e74e87b209776d9b86df9831041 Mon Sep 17 00:00:00 2001 From: LucasHaug Date: Wed, 28 Jun 2023 22:56:28 +0200 Subject: [PATCH 13/15] Remove info loggers --- src/fs/fuse.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 1f5a503ec3..8308f7b2af 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -144,13 +144,8 @@ impl PosixFileSystem for Fuse { } fn mkdir(&self, name: &str, mode: u32) -> Result { - info!("Mkdir: {}, mode: {}", name, mode); - let (mut cmd, mut rsp) = create_mkdir(name, mode); - info!("Cmd: {:?}", cmd); - info!("Rsp: {:?}", rsp); - get_filesystem_driver() .ok_or(FileError::ENOSYS)? .lock() From e5c8786549b1a28469923133bb9ba7b6cd505097 Mon Sep 17 00:00:00 2001 From: simonschoening Date: Wed, 5 Jul 2023 22:39:11 +0200 Subject: [PATCH 14/15] Add syscalls for fs metadata --- src/errno.rs | 3 + src/fd/file.rs | 19 ++- src/fd/mod.rs | 11 +- src/fs/fuse.rs | 280 +++++++++++++++++++++++++++++++-- src/lib.rs | 1 + src/syscalls/fs.rs | 54 ++++++- src/syscalls/interfaces/mod.rs | 46 ++++-- src/syscalls/mod.rs | 58 +++++-- 8 files changed, 427 insertions(+), 45 deletions(-) diff --git a/src/errno.rs b/src/errno.rs index dc22dc8ced..148387cef1 100644 --- a/src/errno.rs +++ b/src/errno.rs @@ -1,3 +1,6 @@ +#[thread_local] +pub static mut ERRNO: i32 = 0; + /// Operation not permitted pub const EPERM: i32 = 1; diff --git a/src/fd/file.rs b/src/fd/file.rs index 8a7a47de84..74173ad44b 100644 --- a/src/fd/file.rs +++ b/src/fd/file.rs @@ -1,11 +1,12 @@ use alloc::boxed::Box; use core::{isize, slice}; +use crate::errno::*; use crate::fd::{ uhyve_send, ObjectInterface, SysClose, SysLseek, SysRead, SysWrite, UHYVE_PORT_CLOSE, UHYVE_PORT_LSEEK, UHYVE_PORT_READ, UHYVE_PORT_WRITE, }; -use crate::syscalls::fs::{self, Dirent, PosixFile, SeekWhence}; +use crate::syscalls::fs::{self, Dirent, FileAttr, PosixFile, SeekWhence}; #[derive(Debug, Clone)] pub struct UhyveFile(i32); @@ -99,13 +100,27 @@ impl ObjectInterface for GenericFile { ret as isize } + /// `fstat` + fn fstat(&self, stat: *mut FileAttr) -> i32 { + debug!("fstat ! {}", self.0); + let mut fs = fs::FILESYSTEM.lock(); + fs.fd_op(self.0, |file: &mut Box| { + file.fstat(stat).unwrap(); // TODO: might fail + }); + + 0 + } + fn readdir(&self) -> *const Dirent { debug!("readdir ! {}", self.0); let mut fs = fs::FILESYSTEM.lock(); let mut ret: *const Dirent = core::ptr::null(); fs.fd_op(self.0, |file: &mut Box| { - ret = file.readdir().unwrap(); // TODO: might fail + match file.readdir() { + Ok(dir_ptr) => ret = dir_ptr, + Err(e) => unsafe { ERRNO = num::ToPrimitive::to_i32(&e).unwrap() }, + } }); ret diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 8ea7728a8a..2994479d0c 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -13,7 +13,7 @@ use crate::env; use crate::errno::*; use crate::fd::file::{GenericFile, UhyveFile}; use crate::fd::stdio::*; -use crate::syscalls::fs::{self, Dirent, FilePerms, SeekWhence}; +use crate::syscalls::fs::{self, Dirent, FileAttr, FilePerms, SeekWhence}; #[cfg(all(feature = "tcp", not(feature = "newlib")))] use crate::syscalls::net::*; @@ -199,6 +199,11 @@ pub trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { (-EINVAL).try_into().unwrap() } + /// `fstat` + fn fstat(&self, _stat: *mut FileAttr) -> i32 { + -EINVAL + } + /// `unlink` removes file entry fn unlink(&self, _name: *const u8) -> i32 { -EINVAL @@ -213,7 +218,9 @@ pub trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// representing the next directory entry in the directory stream /// pointed to by the file descriptor fn readdir(&self) -> *const Dirent { - // TODO: Error handling + unsafe { + ERRNO = ENOSYS; + } core::ptr::null() } diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 8308f7b2af..443a5ff402 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -11,7 +11,7 @@ use crate::arch::kernel::mmio::get_filesystem_driver; use crate::drivers::pci::get_filesystem_driver; use crate::drivers::virtio::virtqueue::AsSliceU8; use crate::syscalls::fs::{ - self, Dirent, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence, + self, Dirent, FileAttr, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence, }; // response out layout eg @ https://github.com/zargony/fuse-rs/blob/bf6d1cf03f3277e35b580f3c7b9999255d72ecf3/src/ll/request.rs#L44 @@ -24,6 +24,9 @@ const MAX_WRITE_LEN: usize = 1024 * 64; const U64_SIZE: u32 = ::core::mem::size_of::() as u32; +const S_IFLNK: u32 = 40960; +const S_IFMT: u32 = 61440; + #[repr(C)] #[derive(Debug)] pub struct fuse_dirent { @@ -89,7 +92,7 @@ impl PosixFileSystem for Fuse { } fn opendir(&self, path: &str) -> Result, FileError> { - debug!("opendir: {}", path); + debug!("FUSE opendir: {}", path); let mut readdir = FuseDir { fuse_nid: None, @@ -152,6 +155,50 @@ impl PosixFileSystem for Fuse { .send_command(cmd.as_ref(), rsp.as_mut()); Ok(unsafe { rsp.rsp.assume_init().nodeid.try_into().unwrap() }) } + + fn stat(&self, path: &str, stat: *mut FileAttr) -> Result<(), FileError> { + debug!("FUSE stat: {}", path); + + // Is there a better way to implement this? + let (cmd, mut rsp) = create_lookup(path); + get_filesystem_driver() + .unwrap() + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + + if rsp.header.error != 0 { + // TODO: Correct error handling + return Err(FileError::EIO); + } + + let rsp = unsafe { rsp.rsp.assume_init() }; + let attr = rsp.attr; + + if attr.mode & S_IFMT != S_IFLNK { + unsafe { + attr.fill_stat(stat); + } + Ok(()) + } else { + self.stat(&self.readlink(rsp.nodeid)?, stat) + } + } + + fn lstat(&self, path: &str, stat: *mut FileAttr) -> Result<(), FileError> { + let (cmd, mut rsp) = create_lookup(path); + get_filesystem_driver() + .unwrap() + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + + let attr = unsafe { rsp.rsp.assume_init().attr }; + + unsafe { + attr.fill_stat(stat); + } + + Ok(()) + } } impl Fuse { @@ -176,6 +223,31 @@ impl Fuse { .send_command(cmd.as_ref(), rsp.as_mut()); Some(unsafe { rsp.rsp.assume_init().nodeid }) } + + pub fn readlink(&self, nid: u64) -> Result { + let mut len = MAX_READ_LEN as u32; + let (cmd, mut rsp) = create_readlink(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::() + >= len.try_into().unwrap() + { + len.try_into().unwrap() + } else { + rsp.header.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() + }) + .unwrap()) + } } impl Default for Fuse { @@ -231,12 +303,12 @@ impl PosixFile for FuseDir { resp } option => { - debug!("Read from FUSE dirfile"); + debug!("FUSE read from dirfile"); // Linux seems to allocate a single page to store the dirfile let len = MAX_READ_LEN as u32; let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) else { - warn!("Dir not open, cannot read!"); + warn!("FUSE dir not open, cannot read!"); return Err(FileError::EBADF); }; @@ -261,7 +333,7 @@ impl PosixFile for FuseDir { }; if len <= core::mem::size_of::() { - debug!("No new dirs"); + debug!("FUSE no new dirs"); return Ok(core::ptr::null()); } @@ -269,7 +341,7 @@ impl PosixFile for FuseDir { let rsp = option.insert(rsp); self.buffer_offset = 0; - debug!("New buffer len: {}", len); + debug!("FUSE new buffer len: {}", len); rsp } }; @@ -293,6 +365,10 @@ impl PosixFile for FuseDir { assert!(return_ptr.is_aligned_to(U64_SIZE.try_into().unwrap())); Ok(return_ptr.cast()) } + + fn fstat(&self, _stat: *mut FileAttr) -> Result<(), FileError> { + Err(FileError::ENOSYS) + } } impl PosixFile for FuseFile { @@ -339,7 +415,7 @@ impl PosixFile for FuseFile { } fn write(&mut self, buf: &[u8]) -> Result { - debug!("fuse write!"); + debug!("FUSE write!"); let mut len = buf.len(); if len > MAX_WRITE_LEN { debug!( @@ -375,7 +451,7 @@ impl PosixFile for FuseFile { } fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result { - debug!("fuse lseek"); + 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); @@ -399,6 +475,26 @@ impl PosixFile for FuseFile { fn readdir(&mut self) -> Result<*const Dirent, FileError> { Err(FileError::EBADF) } + + fn fstat(&self, stat: *mut FileAttr) -> Result<(), FileError> { + if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) { + let (cmd, mut rsp) = create_getattr(nid, fh, 0); + get_filesystem_driver() + .ok_or(FileError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + + if rsp.header.error < 0 { + return Err(FileError::EIO); + } + + let attr = unsafe { rsp.rsp.assume_init().attr }; + unsafe { attr.fill_stat(stat) }; + Ok(()) + } else { + Err(FileError::EIO) + } + } } #[repr(u32)] @@ -558,6 +654,60 @@ fn create_init() -> (Box>, Box>) { (cmd, rsp) } +fn create_getattr( + nid: u64, + fh: 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_GETATTR); + (*raw).cmd = fuse_getattr_in { + getattr_flags: flags, + dummy: 0, + fh: fh, + }; + + 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) +} + fn create_lookup(name: &str) -> (Box>, Box>) { let slice = name.as_bytes(); let len = core::mem::size_of::() @@ -611,6 +761,58 @@ fn create_lookup(name: &str) -> (Box>, Box (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) +} + #[repr(C)] #[derive(Debug, Default)] pub struct fuse_in_header { @@ -1039,6 +1241,38 @@ fn create_release(nid: u64, fh: u64) -> (Box>, Box (Box>, Box>) { +fn create_mkdir(path: &str, mode: u32) -> (Box>, Box>) { let slice = path.as_bytes(); let len = core::mem::size_of::() + core::mem::size_of::() @@ -1296,9 +1548,7 @@ unsafe impl FuseIn for fuse_rmdir_in {} pub struct fuse_rmdir_out {} unsafe impl FuseOut for fuse_rmdir_out {} -fn create_rmdir( - name: &str -) -> (Box>, Box>) { +fn create_rmdir(name: &str) -> (Box>, Box>) { let slice = name.as_bytes(); let len = core::mem::size_of::() + core::mem::size_of::() diff --git a/src/lib.rs b/src/lib.rs index f8601f377e..7239ab1fbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ #![feature(pointer_is_aligned)] #![cfg_attr(target_arch = "aarch64", feature(specialization))] #![feature(strict_provenance)] +#![feature(thread_local)] #![cfg_attr(target_os = "none", no_std)] #![cfg_attr(target_os = "none", feature(custom_test_frameworks))] #![cfg_attr(all(target_os = "none", test), test_runner(crate::test_runner))] diff --git a/src/syscalls/fs.rs b/src/syscalls/fs.rs index 44c9f33c55..324135819a 100644 --- a/src/syscalls/fs.rs +++ b/src/syscalls/fs.rs @@ -42,6 +42,7 @@ use hermit_sync::TicketMutex; /// TODO: /// - FileDescriptor newtype use crate::env::is_uhyve; +use crate::errno; pub use crate::fs::fuse::fuse_dirent as Dirent; // TODO: lazy static could be replaced with explicit init on OS boot. @@ -185,6 +186,22 @@ impl Filesystem { Ok(()) } + /// stat + pub fn stat(&mut self, path: &str, stat: *mut FileAttr) -> Result<(), FileError> { + debug!("Getting stats {}", path); + let (fs, internal_path) = self.parse_path(path)?; + fs.stat(internal_path, stat)?; + Ok(()) + } + + /// lstat + pub fn lstat(&mut self, path: &str, stat: *mut FileAttr) -> Result<(), FileError> { + debug!("Getting lstats {}", path); + let (fs, internal_path) = self.parse_path(path)?; + fs.lstat(internal_path, stat)?; + Ok(()) + } + /// Create new backing-fs at mountpoint mntpath #[cfg(feature = "pci")] pub fn mount( @@ -221,18 +238,19 @@ impl Filesystem { } } +// TODO: Integrate with src/errno.rs ? #[allow(clippy::upper_case_acronyms)] -#[derive(Debug)] +#[derive(Debug, FromPrimitive, ToPrimitive)] pub enum FileError { - ENOENT, + ENOENT = errno::ENOENT as isize, #[cfg(feature = "pci")] - ENOSYS, + ENOSYS = errno::ENOSYS as isize, #[cfg(feature = "pci")] - EIO, + EIO = errno::EIO as isize, #[cfg(feature = "pci")] - EBADF, + EBADF = errno::EBADF as isize, #[cfg(feature = "pci")] - EISDIR, + EISDIR = errno::EISDIR as isize, } pub trait PosixFileSystem { @@ -242,6 +260,8 @@ pub trait PosixFileSystem { fn rmdir(&self, _path: &str) -> Result<(), FileError>; fn mkdir(&self, name: &str, mode: u32) -> Result; + fn stat(&self, _path: &str, stat: *mut FileAttr) -> Result<(), FileError>; + fn lstat(&self, _path: &str, stat: *mut FileAttr) -> Result<(), FileError>; } pub trait PosixFile { @@ -251,6 +271,28 @@ pub trait PosixFile { fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result; fn readdir(&mut self) -> Result<*const Dirent, FileError>; + fn fstat(&self, stat: *mut FileAttr) -> Result<(), FileError>; +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct FileAttr { + pub st_dev: u64, + pub st_ino: u64, + pub st_nlink: u64, + pub st_mode: u32, + pub st_uid: u32, + pub st_gid: u32, + pub st_rdev: u64, + pub st_size: i64, + pub st_blksize: i64, + pub st_blocks: i64, + pub st_atime: i64, + pub st_atime_nsec: i64, + pub st_mtime: i64, + pub st_mtime_nsec: i64, + pub st_ctime: i64, + pub st_ctime_nsec: i64, } #[derive(Debug, FromPrimitive, ToPrimitive)] diff --git a/src/syscalls/interfaces/mod.rs b/src/syscalls/interfaces/mod.rs index be8bd47235..62e7dd7bab 100644 --- a/src/syscalls/interfaces/mod.rs +++ b/src/syscalls/interfaces/mod.rs @@ -4,8 +4,8 @@ use core::ffi::CStr; pub use self::generic::*; pub use self::uhyve::*; -use crate::errno::*; -use crate::syscalls::fs::{self}; +use crate::errno::ENOENT; +use crate::syscalls::fs::{self, FileAttr}; use crate::{arch, env}; mod generic; @@ -62,8 +62,7 @@ pub trait SyscallInterface: Send + Sync { fs::FILESYSTEM .lock() .unlink(name) - .expect("Unlinking failed!"); // TODO: error handling - 0 + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) } #[cfg(target_arch = "x86_64")] @@ -74,8 +73,7 @@ pub trait SyscallInterface: Send + Sync { fs::FILESYSTEM .lock() .rmdir(name) - .expect("Removing directory failed!"); // TODO: error handling - 0 + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) } #[cfg(target_arch = "x86_64")] @@ -86,12 +84,40 @@ pub trait SyscallInterface: Send + Sync { fs::FILESYSTEM .lock() .mkdir(name, mode) - .expect("Creating directory failed!"); // TODO: error handling - 0 + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) } - fn stat(&self, _file: *const u8, _st: usize) -> i32 { - info!("stat is unimplemented"); + #[cfg(not(target_arch = "x86_64"))] + fn stat(&self, _name: *const u8, _stat: *mut FileAttr) -> i32 { + debug!("stat is unimplemented, returning -ENOSYS"); -ENOSYS } + + #[cfg(target_arch = "x86_64")] + fn stat(&self, name: *const u8, stat: *mut FileAttr) -> i32 { + let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); + debug!("stat {}", name); + + fs::FILESYSTEM + .lock() + .stat(name, stat) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + } + + #[cfg(not(target_arch = "x86_64"))] + fn lstat(&self, _name: *const u8, _stat: *mut FileAttr) -> i32 { + debug!("lstat is unimplemented, returning -ENOSYS"); + -ENOSYS + } + + #[cfg(target_arch = "x86_64")] + fn lstat(&self, name: *const u8, stat: *mut FileAttr) -> i32 { + let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); + debug!("lstat {}", name); + + fs::FILESYSTEM + .lock() + .lstat(name, stat) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + } } diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index 7c4ab35f9b..8a5e7cd70b 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -15,8 +15,9 @@ pub use self::system::*; pub use self::tasks::*; pub use self::timer::*; use crate::env; +use crate::errno::ERRNO; use crate::fd::{dup_object, get_object, remove_object, FileDescriptor}; -use crate::syscalls::fs::Dirent; +use crate::syscalls::fs::{Dirent, FileAttr}; use crate::syscalls::interfaces::SyscallInterface; #[cfg(target_os = "none")] use crate::{__sys_free, __sys_malloc, __sys_realloc}; @@ -97,6 +98,24 @@ pub extern "C" fn sys_shutdown(arg: i32) -> ! { kernel_function!(__sys_shutdown(arg)) } +extern "C" fn __sys_get_errno() -> i32 { + unsafe { ERRNO } +} + +#[no_mangle] +pub extern "C" fn sys_get_errno() -> i32 { + kernel_function!(__sys_get_errno()) +} + +extern "C" fn __sys_set_errno(e: i32) { + unsafe { ERRNO = e }; +} + +#[no_mangle] +pub extern "C" fn sys_set_errno(e: i32) { + kernel_function!(__sys_set_errno(e)) +} + extern "C" fn __sys_unlink(name: *const u8) -> i32 { SYS.unlink(name) } @@ -124,6 +143,34 @@ pub extern "C" fn sys_rmdir(name: *const u8) -> i32 { kernel_function!(__sys_rmdir(name)) } +extern "C" fn __sys_stat(name: *const u8, stat: *mut FileAttr) -> i32 { + SYS.stat(name, stat) +} + +#[no_mangle] +pub extern "C" fn sys_stat(name: *const u8, stat: *mut FileAttr) -> i32 { + kernel_function!(__sys_stat(name, stat)) +} + +extern "C" fn __sys_lstat(name: *const u8, stat: *mut FileAttr) -> i32 { + SYS.lstat(name, stat) +} + +#[no_mangle] +pub extern "C" fn sys_lstat(name: *const u8, stat: *mut FileAttr) -> i32 { + kernel_function!(__sys_lstat(name, stat)) +} + +extern "C" fn __sys_fstat(fd: FileDescriptor, stat: *mut FileAttr) -> i32 { + let obj = get_object(fd); + obj.map_or_else(|e| e, |v| (*v).fstat(stat)) +} + +#[no_mangle] +pub extern "C" fn sys_fstat(fd: FileDescriptor, stat: *mut FileAttr) -> i32 { + kernel_function!(__sys_fstat(fd, stat)) +} + extern "C" fn __sys_opendir(name: *const u8) -> FileDescriptor { crate::fd::opendir(name).map_or_else(|e| e, |v| v) } @@ -205,15 +252,6 @@ pub extern "C" fn sys_readdir(fd: FileDescriptor) -> *const Dirent { kernel_function!(__sys_readdir(fd)) } -extern "C" fn __sys_stat(file: *const u8, st: usize) -> i32 { - SYS.stat(file, st) -} - -#[no_mangle] -pub extern "C" fn sys_stat(file: *const u8, st: usize) -> i32 { - kernel_function!(__sys_stat(file, st)) -} - extern "C" fn __sys_dup(fd: i32) -> i32 { dup_object(fd).map_or_else(|e| e, |v| v) } From 5554b1dba2e76cf949349a4cbf50e7425b03296e Mon Sep 17 00:00:00 2001 From: simonschoening Date: Wed, 12 Jul 2023 19:26:06 +0200 Subject: [PATCH 15/15] FUSE fixes --- src/fd/file.rs | 7 +++++-- src/fs/fuse.rs | 10 ++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/fd/file.rs b/src/fd/file.rs index 74173ad44b..64d5587464 100644 --- a/src/fd/file.rs +++ b/src/fd/file.rs @@ -103,12 +103,15 @@ impl ObjectInterface for GenericFile { /// `fstat` fn fstat(&self, stat: *mut FileAttr) -> i32 { debug!("fstat ! {}", self.0); + let mut result = 0; let mut fs = fs::FILESYSTEM.lock(); fs.fd_op(self.0, |file: &mut Box| { - file.fstat(stat).unwrap(); // TODO: might fail + result = file + .fstat(stat) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0); }); - 0 + result } fn readdir(&self) -> *const Dirent { diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 443a5ff402..1ccc4684c3 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -27,6 +27,8 @@ const U64_SIZE: u32 = ::core::mem::size_of::() as u32; const S_IFLNK: u32 = 40960; const S_IFMT: u32 = 61440; +const FUSE_GETATTR_FH: u32 = 1 << 0; + #[repr(C)] #[derive(Debug)] pub struct fuse_dirent { @@ -221,7 +223,11 @@ impl Fuse { .unwrap() .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - Some(unsafe { rsp.rsp.assume_init().nodeid }) + if rsp.header.error == 0 { + Some(unsafe { rsp.rsp.assume_init().nodeid }) + } else { + None + } } pub fn readlink(&self, nid: u64) -> Result { @@ -478,7 +484,7 @@ impl PosixFile for FuseFile { fn fstat(&self, stat: *mut FileAttr) -> Result<(), FileError> { if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) { - let (cmd, mut rsp) = create_getattr(nid, fh, 0); + let (cmd, mut rsp) = create_getattr(nid, fh, FUSE_GETATTR_FH); get_filesystem_driver() .ok_or(FileError::ENOSYS)? .lock()