From e1b85e3d759f2d0495148a5fee130a899f4afb67 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Mon, 25 Dec 2023 11:36:40 +0100 Subject: [PATCH 01/32] move virtual filesystem to the directory vfs The virtual filesystem should not be part of the system call implementation. --- src/fd/file.rs | 2 +- src/fd/mod.rs | 2 +- src/fs/fuse.rs | 2 +- src/fs/mod.rs | 344 ++++++++++++++++++++++++++++++++- src/lib.rs | 2 - src/syscalls/fs.rs | 339 -------------------------------- src/syscalls/interfaces/mod.rs | 2 +- src/syscalls/mod.rs | 3 +- 8 files changed, 347 insertions(+), 349 deletions(-) delete mode 100644 src/syscalls/fs.rs diff --git a/src/fd/file.rs b/src/fd/file.rs index b66fff58ae..2a3b126415 100644 --- a/src/fd/file.rs +++ b/src/fd/file.rs @@ -5,7 +5,7 @@ use crate::fd::{ uhyve_send, DirectoryEntry, ObjectInterface, SysClose, SysLseek, SysRead, SysWrite, UHYVE_PORT_CLOSE, UHYVE_PORT_LSEEK, UHYVE_PORT_READ, UHYVE_PORT_WRITE, }; -use crate::syscalls::fs::{self, FileAttr, PosixFile, SeekWhence}; +use crate::fs::{self, FileAttr, PosixFile, SeekWhence}; #[derive(Debug, Clone)] pub struct UhyveFile(i32); diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 83c02a21b8..632abdaa89 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -15,7 +15,7 @@ use crate::env; use crate::errno::*; use crate::fd::file::{GenericFile, UhyveFile}; use crate::fd::stdio::*; -use crate::syscalls::fs::{self, Dirent, FileAttr, FilePerms, SeekWhence}; +use crate::fs::{self, Dirent, FileAttr, FilePerms, SeekWhence}; #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] use crate::syscalls::net::*; diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index c9789e3559..2d7205ca0a 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::{ +use crate::fs::{ self, Dirent, FileAttr, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence, }; diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 0069cb3601..56c065e01e 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -1,7 +1,347 @@ +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use alloc::string::String; +use alloc::vec::Vec; +use core::ops::Deref; + +use hermit_sync::TicketMutex; + #[cfg(feature = "pci")] -pub mod fuse; +pub(crate) mod fuse; + +// TODO: lazy static could be replaced with explicit init on OS boot. +pub static FILESYSTEM: TicketMutex = TicketMutex::new(Filesystem::new()); + +pub struct Filesystem { + // Keep track of mount-points + mounts: BTreeMap>, + + // Keep track of open files + files: BTreeMap>, +} + +impl Filesystem { + pub const fn new() -> Self { + Self { + mounts: BTreeMap::new(), + files: BTreeMap::new(), + } + } + + /// Returns next free file-descriptor. We map index in files BTreeMap as fd's. + /// Done determining the current biggest stored index. + /// This is efficient, since BTreeMap's iter() calculates min and max key directly. + /// see + fn assign_new_fd(&self) -> u64 { + // BTreeMap has efficient max/min index calculation. One way to access these is the following iter. + // Add 1 to get next never-assigned fd num + if let Some((fd, _)) = self.files.iter().next_back() { + fd + 1 + } else { + 3 // start at 3, to reserve stdin/out/err + } + } + + /// Gets a new fd for a file and inserts it into open files. + /// Returns file descriptor + fn add_file(&mut self, file: Box) -> u64 { + let fd = self.assign_new_fd(); + self.files.insert(fd, file); + fd + } + + /// parses path `/MOUNTPOINT/internal-path` into mount-filesystem and internal_path + /// Returns (PosixFileSystem, internal_path) or Error on failure. + fn parse_path<'a, 'b>( + &'a self, + path: &'b str, + ) -> Result<(&'a (dyn PosixFileSystem + Send), &'b str), FileError> { + let mut pathsplit = path.splitn(3, '/'); + + if path.starts_with('/') { + pathsplit.next(); // empty, since first char is / + + let mount = pathsplit.next().unwrap(); + let internal_path = pathsplit.next().unwrap_or("/"); + if let Some(fs) = self.mounts.get(mount) { + return Ok((fs.deref(), internal_path)); + } + + warn!( + "Trying to open file on non-existing mount point '{}'!", + mount + ); + } else { + let mount = if !is_uhyve() { + option_env!("HERMIT_WD").unwrap_or("root") + } else { + "." + }; + let internal_path = pathsplit.next().unwrap_or("/"); + + debug!( + "Assume that the directory '{}' is used as mount point!", + mount + ); + + if let Some(fs) = self.mounts.get(mount) { + return Ok((fs.deref(), internal_path)); + } + + warn!( + "Trying to open file on non-existing mount point '{}'!", + mount + ); + } + + Err(FileError::ENOENT) + } + + /// Tries to open file at given path (/MOUNTPOINT/internal-path). + /// Looks up MOUNTPOINT in mounted dirs, passes internal-path to filesystem backend + /// Returns the file descriptor of the newly opened file, or an error on failure + pub fn open(&mut self, path: &str, perms: FilePerms) -> Result { + debug!("Opening file {} {:?}", path, perms); + let (fs, internal_path) = self.parse_path(path)?; + let file = fs.open(internal_path, perms)?; + Ok(self.add_file(file)) + } + + /// Similar to open + #[allow(dead_code)] + 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 + pub fn close(&mut self, fd: u64) { + debug!("Closing fd {}", fd); + if let Some(file) = self.files.get_mut(&fd) { + file.close().unwrap(); // TODO: handle error + } + self.files.remove(&fd); + } + + /// Unlinks a file given by path + pub fn unlink(&mut self, path: &str) -> Result<(), FileError> { + debug!("Unlinking file {}", path); + let (fs, internal_path) = self.parse_path(path)?; + fs.unlink(internal_path)?; + Ok(()) + } + + /// Remove directory given by path + #[allow(dead_code)] + 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 directory given by path + #[allow(dead_code)] + 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(()) + } + + /// stat + #[allow(dead_code)] + 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 + #[allow(dead_code)] + 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(()) + } -pub fn init() { + /// Create new backing-fs at mountpoint mntpath + #[cfg(feature = "fs")] + pub fn mount( + &mut self, + mntpath: &str, + mntobj: Box, + ) -> Result<(), ()> { + use alloc::string::ToString; + + debug!("Mounting {}", mntpath); + if mntpath.contains('/') { + warn!( + "Trying to mount at '{}', but slashes in name are not supported!", + mntpath + ); + return Err(()); + } + + // if mounts contains path already abort + if self.mounts.contains_key(mntpath) { + warn!("Mountpoint already exists!"); + return Err(()); + } + + // insert filesystem into mounts, done + self.mounts.insert(mntpath.to_string(), mntobj); + + Ok(()) + } + + /// Run closure on file referenced by file descriptor. + pub fn fd_op(&mut self, fd: u64, f: impl FnOnce(&mut Box)) { + f(self.files.get_mut(&fd).unwrap()); + } +} + +// TODO: Integrate with src/errno.rs ? +#[allow(clippy::upper_case_acronyms)] +#[derive(Debug, FromPrimitive, ToPrimitive)] +pub enum FileError { + ENOENT = errno::ENOENT as isize, + #[cfg(any(feature = "fs", feature = "pci"))] + ENOSYS = errno::ENOSYS as isize, + #[cfg(any(feature = "fs", feature = "pci"))] + EIO = errno::EIO as isize, #[cfg(feature = "pci")] + EBADF = errno::EBADF as isize, + #[cfg(feature = "pci")] + EISDIR = errno::EISDIR as isize, +} + +/// Design: +/// - want to support different backends. One of them virtiofs. +/// - want to support multiple mounted filesystems at once. +/// - for simplicity: no overlays. All 'folders' in / are mountpoints! +/// - manage all files in a global map. Do not hand out references, let syscalls operate by passing in closures (fd_op()) +/// +/// - we internally treat all file systems as posix filesystems. +/// - Have two traits. One representing a filesystem, another a file: PosixFileSystem and PosixFile +/// - filesystem.open creates new file +/// - trait methods like open return Result<....>, so we can catch errors on eg open() and NOT permanently assign an fd to it! +/// +/// - have a FUSE filesystem, which implements both PosixFileSystem and PosixFile +/// - fuse can have various FuseInterface backends. These only have to provide fuse command send/receive capabilities. +/// - virtiofs implements FuseInterface and sends commands via virtio queues. +/// +/// - fd management is only relevant for "user" facing code. We don't care how fuse etc. manages nodes internally. +/// - But we still want to have a list of open files and mounted filesystems (here in fs.rs). +/// +/// Open Questions: +/// - what is the maximum number of open files I want to support? if small, could have static allocation, no need for hashmap? +/// - create Stdin/out virtual files, assign fd's 0-2. Instantiate them on program start. currently fd 0-2 are hardcoded exceptions. +/// - optimize callchain? how does LTO work here?: +/// - app calls rust.open (which is stdlib hermit/fs.rs) [https://github.com/rust-lang/rust/blob/master/src/libstd/sys/hermit/fs.rs#L267] +/// - abi::open() (hermit-sys crate) +/// - [KERNEL BORDER] (uses C-interface. needed? Could just be alternative to native rust?) +/// - hermit-lib/....rs/sys_open() +/// - SyscallInterface.open (via &'static dyn ref) +/// - Filesystem::open() +/// - Fuse::open() +/// - VirtiofsDriver::send_command(...) +/// - [HYPERVISOR BORDER] (via virtio) +/// - virtiofsd receives fuse command and sends reply +/// +/// TODO: +/// - FileDescriptor newtype +use crate::env::is_uhyve; +use crate::errno; +#[cfg(feature = "fs")] +pub use crate::fs::fuse::fuse_dirent as Dirent; +#[cfg(not(feature = "fs"))] +pub struct Dirent; + +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>; + 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 { + fn close(&mut self) -> Result<(), FileError>; + 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<*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)] +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 +} + +// TODO: raw is partially redundant, create nicer interface +#[derive(Clone, Copy, Debug, Default)] +pub struct FilePerms { + pub write: bool, + pub creat: bool, + pub excl: bool, + pub trunc: bool, + pub append: bool, + pub directio: bool, + pub raw: u32, + pub mode: u32, +} + +#[derive(Debug, FromPrimitive, ToPrimitive)] +pub enum SeekWhence { + Set = 0, + Cur = 1, + End = 2, + Data = 3, + Hole = 4, +} + +pub(crate) fn init() { + #[cfg(all(feature = "fs", feature = "pci"))] fuse::init(); } diff --git a/src/lib.rs b/src/lib.rs index 0c0a0d52f7..751a308b47 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,7 +84,6 @@ mod env; pub mod errno; mod executor; pub(crate) mod fd; -#[cfg(feature = "fs")] pub(crate) mod fs; mod mm; mod scheduler; @@ -283,7 +282,6 @@ extern "C" fn initd(_arg: usize) { syscalls::init(); fd::init(); - #[cfg(feature = "fs")] fs::init(); // Get the application arguments and environment variables. diff --git a/src/syscalls/fs.rs b/src/syscalls/fs.rs deleted file mode 100644 index 7958715020..0000000000 --- a/src/syscalls/fs.rs +++ /dev/null @@ -1,339 +0,0 @@ -use alloc::boxed::Box; -use alloc::collections::BTreeMap; -use alloc::string::String; -use alloc::vec::Vec; -use core::ops::Deref; - -use hermit_sync::TicketMutex; - -/// Design: -/// - want to support different backends. One of them virtiofs. -/// - want to support multiple mounted filesystems at once. -/// - for simplicity: no overlays. All 'folders' in / are mountpoints! -/// - manage all files in a global map. Do not hand out references, let syscalls operate by passing in closures (fd_op()) -/// -/// - we internally treat all file systems as posix filesystems. -/// - Have two traits. One representing a filesystem, another a file: PosixFileSystem and PosixFile -/// - filesystem.open creates new file -/// - trait methods like open return Result<....>, so we can catch errors on eg open() and NOT permanently assign an fd to it! -/// -/// - have a FUSE filesystem, which implements both PosixFileSystem and PosixFile -/// - fuse can have various FuseInterface backends. These only have to provide fuse command send/receive capabilities. -/// - virtiofs implements FuseInterface and sends commands via virtio queues. -/// -/// - fd management is only relevant for "user" facing code. We don't care how fuse etc. manages nodes internally. -/// - But we still want to have a list of open files and mounted filesystems (here in fs.rs). -/// -/// Open Questions: -/// - what is the maximum number of open files I want to support? if small, could have static allocation, no need for hashmap? -/// - create Stdin/out virtual files, assign fd's 0-2. Instantiate them on program start. currently fd 0-2 are hardcoded exceptions. -/// - optimize callchain? how does LTO work here?: -/// - app calls rust.open (which is stdlib hermit/fs.rs) [https://github.com/rust-lang/rust/blob/master/src/libstd/sys/hermit/fs.rs#L267] -/// - abi::open() (hermit-sys crate) -/// - [KERNEL BORDER] (uses C-interface. needed? Could just be alternative to native rust?) -/// - hermit-lib/....rs/sys_open() -/// - SyscallInterface.open (via &'static dyn ref) -/// - Filesystem::open() -/// - Fuse::open() -/// - VirtiofsDriver::send_command(...) -/// - [HYPERVISOR BORDER] (via virtio) -/// - virtiofsd receives fuse command and sends reply -/// -/// TODO: -/// - FileDescriptor newtype -use crate::env::is_uhyve; -use crate::errno; -#[cfg(feature = "fs")] -pub use crate::fs::fuse::fuse_dirent as Dirent; -#[cfg(not(feature = "fs"))] -pub struct Dirent; - -// TODO: lazy static could be replaced with explicit init on OS boot. -pub static FILESYSTEM: TicketMutex = TicketMutex::new(Filesystem::new()); - -pub struct Filesystem { - // Keep track of mount-points - mounts: BTreeMap>, - - // Keep track of open files - files: BTreeMap>, -} - -impl Filesystem { - pub const fn new() -> Self { - Self { - mounts: BTreeMap::new(), - files: BTreeMap::new(), - } - } - - /// Returns next free file-descriptor. We map index in files BTreeMap as fd's. - /// Done determining the current biggest stored index. - /// This is efficient, since BTreeMap's iter() calculates min and max key directly. - /// see - fn assign_new_fd(&self) -> u64 { - // BTreeMap has efficient max/min index calculation. One way to access these is the following iter. - // Add 1 to get next never-assigned fd num - if let Some((fd, _)) = self.files.iter().next_back() { - fd + 1 - } else { - 3 // start at 3, to reserve stdin/out/err - } - } - - /// Gets a new fd for a file and inserts it into open files. - /// Returns file descriptor - fn add_file(&mut self, file: Box) -> u64 { - let fd = self.assign_new_fd(); - self.files.insert(fd, file); - fd - } - - /// parses path `/MOUNTPOINT/internal-path` into mount-filesystem and internal_path - /// Returns (PosixFileSystem, internal_path) or Error on failure. - fn parse_path<'a, 'b>( - &'a self, - path: &'b str, - ) -> Result<(&'a (dyn PosixFileSystem + Send), &'b str), FileError> { - let mut pathsplit = path.splitn(3, '/'); - - if path.starts_with('/') { - pathsplit.next(); // empty, since first char is / - - let mount = pathsplit.next().unwrap(); - let internal_path = pathsplit.next().unwrap_or("/"); - if let Some(fs) = self.mounts.get(mount) { - return Ok((fs.deref(), internal_path)); - } - - warn!( - "Trying to open file on non-existing mount point '{}'!", - mount - ); - } else { - let mount = if !is_uhyve() { - option_env!("HERMIT_WD").unwrap_or("root") - } else { - "." - }; - let internal_path = pathsplit.next().unwrap_or("/"); - - debug!( - "Assume that the directory '{}' is used as mount point!", - mount - ); - - if let Some(fs) = self.mounts.get(mount) { - return Ok((fs.deref(), internal_path)); - } - - warn!( - "Trying to open file on non-existing mount point '{}'!", - mount - ); - } - - Err(FileError::ENOENT) - } - - /// Tries to open file at given path (/MOUNTPOINT/internal-path). - /// Looks up MOUNTPOINT in mounted dirs, passes internal-path to filesystem backend - /// Returns the file descriptor of the newly opened file, or an error on failure - pub fn open(&mut self, path: &str, perms: FilePerms) -> Result { - debug!("Opening file {} {:?}", path, perms); - let (fs, internal_path) = self.parse_path(path)?; - let file = fs.open(internal_path, perms)?; - Ok(self.add_file(file)) - } - - /// Similar to open - #[allow(dead_code)] - 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 - pub fn close(&mut self, fd: u64) { - debug!("Closing fd {}", fd); - if let Some(file) = self.files.get_mut(&fd) { - file.close().unwrap(); // TODO: handle error - } - self.files.remove(&fd); - } - - /// Unlinks a file given by path - pub fn unlink(&mut self, path: &str) -> Result<(), FileError> { - debug!("Unlinking file {}", path); - let (fs, internal_path) = self.parse_path(path)?; - fs.unlink(internal_path)?; - Ok(()) - } - - /// Remove directory given by path - #[allow(dead_code)] - 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 directory given by path - #[allow(dead_code)] - 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(()) - } - - /// stat - #[allow(dead_code)] - 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 - #[allow(dead_code)] - 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 = "fs")] - pub fn mount( - &mut self, - mntpath: &str, - mntobj: Box, - ) -> Result<(), ()> { - use alloc::string::ToString; - - debug!("Mounting {}", mntpath); - if mntpath.contains('/') { - warn!( - "Trying to mount at '{}', but slashes in name are not supported!", - mntpath - ); - return Err(()); - } - - // if mounts contains path already abort - if self.mounts.contains_key(mntpath) { - warn!("Mountpoint already exists!"); - return Err(()); - } - - // insert filesystem into mounts, done - self.mounts.insert(mntpath.to_string(), mntobj); - - Ok(()) - } - - /// Run closure on file referenced by file descriptor. - pub fn fd_op(&mut self, fd: u64, f: impl FnOnce(&mut Box)) { - f(self.files.get_mut(&fd).unwrap()); - } -} - -// TODO: Integrate with src/errno.rs ? -#[allow(clippy::upper_case_acronyms)] -#[derive(Debug, FromPrimitive, ToPrimitive)] -pub enum FileError { - ENOENT = errno::ENOENT as isize, - #[cfg(any(feature = "fs", feature = "pci"))] - ENOSYS = errno::ENOSYS as isize, - #[cfg(any(feature = "fs", feature = "pci"))] - EIO = errno::EIO as isize, - #[cfg(feature = "pci")] - EBADF = errno::EBADF as isize, - #[cfg(feature = "pci")] - EISDIR = errno::EISDIR as isize, -} - -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>; - 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 { - fn close(&mut self) -> Result<(), FileError>; - 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<*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)] -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 -} - -// TODO: raw is partially redundant, create nicer interface -#[derive(Clone, Copy, Debug, Default)] -pub struct FilePerms { - pub write: bool, - pub creat: bool, - pub excl: bool, - pub trunc: bool, - pub append: bool, - pub directio: bool, - pub raw: u32, - pub mode: u32, -} - -#[derive(Debug, FromPrimitive, ToPrimitive)] -pub enum SeekWhence { - Set = 0, - Cur = 1, - End = 2, - Data = 3, - Hole = 4, -} diff --git a/src/syscalls/interfaces/mod.rs b/src/syscalls/interfaces/mod.rs index 6b8be563fc..b65ca42be2 100644 --- a/src/syscalls/interfaces/mod.rs +++ b/src/syscalls/interfaces/mod.rs @@ -6,7 +6,7 @@ pub use self::generic::*; pub use self::uhyve::*; #[cfg(not(target_arch = "x86_64"))] use crate::errno::ENOSYS; -use crate::syscalls::fs::{self, FileAttr}; +use crate::fs::{self, FileAttr}; use crate::{arch, env}; mod generic; diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index 7d1c55d910..c95b038d91 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -17,14 +17,13 @@ pub use self::tasks::*; pub use self::timer::*; use crate::env; use crate::fd::{dup_object, get_object, remove_object, DirectoryEntry, FileDescriptor}; -use crate::syscalls::fs::FileAttr; +use crate::fs::FileAttr; use crate::syscalls::interfaces::SyscallInterface; #[cfg(target_os = "none")] use crate::{__sys_free, __sys_malloc, __sys_realloc}; mod condvar; mod entropy; -pub(crate) mod fs; mod futex; mod interfaces; #[cfg(feature = "newlib")] From 5ee8bd0efcad076662e2110ad895458ff6a67a2c Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Mon, 25 Dec 2023 12:57:42 +0100 Subject: [PATCH 02/32] rename feature fs to fuse Rename feature fs to fuse, because it disables the support of the fuse file system. For legacy reasons, the feature fs will enable fuse. --- Cargo.toml | 5 +++-- src/drivers/mod.rs | 12 ++++++------ src/drivers/pci.rs | 18 +++++++++--------- src/drivers/virtio/mod.rs | 6 +++--- src/drivers/virtio/transport/pci.rs | 8 ++++---- src/fs/mod.rs | 12 +++--------- src/lib.rs | 4 ++-- 7 files changed, 30 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 70e7530f8a..35eca252ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,14 +45,15 @@ name = "measure_startup_time" harness = false [features] -default = ["pci", "pci-ids", "acpi", "fsgsbase", "smp", "tcp", "dhcpv4", "fs"] +default = ["pci", "pci-ids", "acpi", "fsgsbase", "smp", "tcp", "dhcpv4", "fuse"] acpi = [] dhcpv4 = [ "smoltcp", "smoltcp/proto-dhcpv4", "smoltcp/socket-dhcpv4", ] -fs = ["pci"] +fs = ["fuse"] +fuse = ["pci"] fsgsbase = [] gem-net = ["tcp"] newlib = [] diff --git a/src/drivers/mod.rs b/src/drivers/mod.rs index b8259d96a0..d610415885 100644 --- a/src/drivers/mod.rs +++ b/src/drivers/mod.rs @@ -1,6 +1,6 @@ //! A module containing hermit-rs driver, hermit-rs driver trait and driver specific errors. -#[cfg(feature = "fs")] +#[cfg(feature = "fuse")] pub mod fs; #[cfg(not(feature = "pci"))] pub mod mmio; @@ -10,7 +10,7 @@ pub mod net; pub mod pci; #[cfg(any( all(any(feature = "tcp", feature = "udp"), not(feature = "rtl8139")), - feature = "fs" + feature = "fuse" ))] pub mod virtio; @@ -26,7 +26,7 @@ pub mod error { use crate::drivers::net::rtl8139::RTL8139Error; #[cfg(any( all(any(feature = "tcp", feature = "udp"), not(feature = "rtl8139")), - feature = "fs" + feature = "fuse" ))] use crate::drivers::virtio::error::VirtioError; @@ -34,7 +34,7 @@ pub mod error { pub enum DriverError { #[cfg(any( all(any(feature = "tcp", feature = "udp"), not(feature = "rtl8139")), - feature = "fs" + feature = "fuse" ))] InitVirtioDevFail(VirtioError), #[cfg(feature = "rtl8139")] @@ -45,7 +45,7 @@ pub mod error { #[cfg(any( all(any(feature = "tcp", feature = "udp"), not(feature = "rtl8139")), - feature = "fs" + feature = "fuse" ))] impl From for DriverError { fn from(err: VirtioError) -> Self { @@ -73,7 +73,7 @@ pub mod error { match *self { #[cfg(any( all(any(feature = "tcp", feature = "udp"), not(feature = "rtl8139")), - feature = "fs" + feature = "fuse" ))] DriverError::InitVirtioDevFail(ref err) => { write!(f, "Virtio driver failed: {err:?}") diff --git a/src/drivers/pci.rs b/src/drivers/pci.rs index 0d5fa44f1a..d8e26c8a38 100644 --- a/src/drivers/pci.rs +++ b/src/drivers/pci.rs @@ -5,7 +5,7 @@ use core::fmt; use bitflags::bitflags; use hermit_sync::without_interrupts; -#[cfg(any(feature = "tcp", feature = "udp", feature = "fs"))] +#[cfg(any(feature = "tcp", feature = "udp", feature = "fuse"))] use hermit_sync::InterruptTicketMutex; use pci_types::{ Bar, ConfigRegionAccess, DeviceId, EndpointHeader, InterruptLine, InterruptPin, PciAddress, @@ -14,7 +14,7 @@ use pci_types::{ use crate::arch::mm::{PhysAddr, VirtAddr}; use crate::arch::pci::PciConfigRegion; -#[cfg(feature = "fs")] +#[cfg(feature = "fuse")] use crate::drivers::fs::virtio_fs::VirtioFsDriver; #[cfg(feature = "rtl8139")] use crate::drivers::net::rtl8139::{self, RTL8139Driver}; @@ -22,12 +22,12 @@ use crate::drivers::net::rtl8139::{self, RTL8139Driver}; use crate::drivers::net::virtio_net::VirtioNetDriver; #[cfg(any( all(any(feature = "tcp", feature = "udp"), not(feature = "rtl8139")), - feature = "fs" + feature = "fuse" ))] use crate::drivers::virtio::transport::pci as pci_virtio; #[cfg(any( all(any(feature = "tcp", feature = "udp"), not(feature = "rtl8139")), - feature = "fs" + feature = "fuse" ))] use crate::drivers::virtio::transport::pci::VirtioDriver; @@ -466,7 +466,7 @@ pub(crate) fn print_information() { #[allow(clippy::large_enum_variant)] pub(crate) enum PciDriver { - #[cfg(feature = "fs")] + #[cfg(feature = "fuse")] VirtioFs(InterruptTicketMutex), #[cfg(all(not(feature = "rtl8139"), any(feature = "tcp", feature = "udp")))] VirtioNet(InterruptTicketMutex), @@ -493,7 +493,7 @@ impl PciDriver { } } - #[cfg(feature = "fs")] + #[cfg(feature = "fuse")] fn get_filesystem_driver(&self) -> Option<&InterruptTicketMutex> { match self { Self::VirtioFs(drv) => Some(drv), @@ -519,7 +519,7 @@ pub(crate) fn get_network_driver() -> Option<&'static InterruptTicketMutex Option<&'static InterruptTicketMutex> { unsafe { PCI_DRIVERS @@ -544,14 +544,14 @@ pub(crate) fn init_drivers() { #[cfg(any( all(any(feature = "tcp", feature = "udp"), not(feature = "rtl8139")), - feature = "fs" + feature = "fuse" ))] match pci_virtio::init_device(adapter) { #[cfg(all(not(feature = "rtl8139"), any(feature = "tcp", feature = "udp")))] Ok(VirtioDriver::Network(drv)) => { register_driver(PciDriver::VirtioNet(InterruptTicketMutex::new(drv))) } - #[cfg(feature = "fs")] + #[cfg(feature = "fuse")] Ok(VirtioDriver::FileSystem(drv)) => { register_driver(PciDriver::VirtioFs(InterruptTicketMutex::new(drv))) } diff --git a/src/drivers/virtio/mod.rs b/src/drivers/virtio/mod.rs index 8c14e61dde..85f7eda928 100644 --- a/src/drivers/virtio/mod.rs +++ b/src/drivers/virtio/mod.rs @@ -8,7 +8,7 @@ pub mod virtqueue; pub mod error { use core::fmt; - #[cfg(feature = "fs")] + #[cfg(feature = "fuse")] pub use crate::drivers::fs::virtio_fs::error::VirtioFsError; #[cfg(all(not(feature = "rtl8139"), any(feature = "tcp", feature = "udp")))] pub use crate::drivers::net::virtio_net::error::VirtioNetError; @@ -23,7 +23,7 @@ pub mod error { DevNotSupported(u16), #[cfg(all(not(feature = "rtl8139"), any(feature = "tcp", feature = "udp")))] NetDriver(VirtioNetError), - #[cfg(feature = "fs")] + #[cfg(feature = "fuse")] FsDriver(VirtioFsError), #[cfg(not(feature = "pci"))] Unknown, @@ -56,7 +56,7 @@ pub mod error { VirtioNetError::ProcessOngoing => write!(f, "Virtio network performed an unsuitable operation upon an ongoging transfer."), VirtioNetError::Unknown => write!(f, "Virtio network driver failed due unknown reason!"), }, - #[cfg(feature = "fs")] + #[cfg(feature = "fuse")] VirtioError::FsDriver(fs_error) => match fs_error { VirtioFsError::NoDevCfg(id) => write!(f, "Virtio filesystem driver failed, for device {id:x}, due to a missing or malformed device config!"), VirtioFsError::NoComCfg(id) => write!(f, "Virtio filesystem driver failed, for device {id:x}, due to a missing or malformed common config!"), diff --git a/src/drivers/virtio/transport/pci.rs b/src/drivers/virtio/transport/pci.rs index 03b4ad9a20..2922d0da69 100644 --- a/src/drivers/virtio/transport/pci.rs +++ b/src/drivers/virtio/transport/pci.rs @@ -14,7 +14,7 @@ use crate::arch::memory_barrier; use crate::arch::mm::PhysAddr; use crate::arch::pci::PciConfigRegion; use crate::drivers::error::DriverError; -#[cfg(feature = "fs")] +#[cfg(feature = "fuse")] use crate::drivers::fs::virtio_fs::VirtioFsDriver; #[cfg(all(not(feature = "rtl8139"), any(feature = "tcp", feature = "udp")))] use crate::drivers::net::network_irqhandler; @@ -1267,7 +1267,7 @@ pub(crate) fn init_device( Err(DriverError::InitVirtioDevFail(virtio_error)) } }, - #[cfg(feature = "fs")] + #[cfg(feature = "fuse")] DevId::VIRTIO_DEV_ID_FS => { // TODO: check subclass // TODO: proper error handling on driver creation fail @@ -1311,7 +1311,7 @@ pub(crate) fn init_device( Ok(drv) } - #[cfg(feature = "fs")] + #[cfg(feature = "fuse")] VirtioDriver::FileSystem(_) => Ok(drv), } } @@ -1322,6 +1322,6 @@ pub(crate) fn init_device( pub(crate) enum VirtioDriver { #[cfg(all(not(feature = "rtl8139"), any(feature = "tcp", feature = "udp")))] Network(VirtioNetDriver), - #[cfg(feature = "fs")] + #[cfg(feature = "fuse")] FileSystem(VirtioFsDriver), } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 56c065e01e..6b112fc1cb 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -6,7 +6,7 @@ use core::ops::Deref; use hermit_sync::TicketMutex; -#[cfg(feature = "pci")] +#[cfg(all(feature = "fuse", feature = "pci"))] pub(crate) mod fuse; // TODO: lazy static could be replaced with explicit init on OS boot. @@ -172,7 +172,6 @@ impl Filesystem { } /// Create new backing-fs at mountpoint mntpath - #[cfg(feature = "fs")] pub fn mount( &mut self, mntpath: &str, @@ -212,13 +211,9 @@ impl Filesystem { #[derive(Debug, FromPrimitive, ToPrimitive)] pub enum FileError { ENOENT = errno::ENOENT as isize, - #[cfg(any(feature = "fs", feature = "pci"))] ENOSYS = errno::ENOSYS as isize, - #[cfg(any(feature = "fs", feature = "pci"))] EIO = errno::EIO as isize, - #[cfg(feature = "pci")] EBADF = errno::EBADF as isize, - #[cfg(feature = "pci")] EISDIR = errno::EISDIR as isize, } @@ -259,9 +254,8 @@ pub enum FileError { /// - FileDescriptor newtype use crate::env::is_uhyve; use crate::errno; -#[cfg(feature = "fs")] +#[cfg(feature = "fuse")] pub use crate::fs::fuse::fuse_dirent as Dirent; -#[cfg(not(feature = "fs"))] pub struct Dirent; pub trait PosixFileSystem { @@ -342,6 +336,6 @@ pub enum SeekWhence { } pub(crate) fn init() { - #[cfg(all(feature = "fs", feature = "pci"))] + #[cfg(all(feature = "fuse", feature = "pci"))] fuse::init(); } diff --git a/src/lib.rs b/src/lib.rs index 751a308b47..443d443b20 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -252,7 +252,7 @@ extern "C" fn initd(_arg: usize) { #[cfg(all(not(test), not(feature = "syscall")))] fn runtime_entry(argc: i32, argv: *const *const u8, env: *const *const u8) -> !; #[cfg(all(not(test), feature = "syscall"))] - fn main() -> !; + fn main(argc: i32, argv: *const *const u8, env: *const *const u8); #[cfg(feature = "newlib")] fn init_lwip(); #[cfg(feature = "newlib")] @@ -297,7 +297,7 @@ extern "C" fn initd(_arg: usize) { #[cfg(all(not(test), not(feature = "syscall")))] runtime_entry(argc, argv, environ); #[cfg(all(not(test), feature = "syscall"))] - main(); + main(argc, argv, environ); } #[cfg(test)] test_main(); From ba9f92cc9186c7b4790e90b1aa89ca1c8302e4e5 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 2 Jan 2024 10:36:26 +0000 Subject: [PATCH 03/32] revise filesystem interface - remove old style - using of slices instead of raw pointer --- src/executor/network.rs | 13 +- src/fd/file.rs | 137 --- src/fd/mod.rs | 381 ++----- src/fd/socket/mod.rs | 119 +- src/fd/socket/tcp.rs | 257 ++--- src/fd/socket/udp.rs | 328 +++--- src/fd/stdio.rs | 91 +- src/fs/fuse.rs | 1792 +++++++++++++++--------------- src/fs/mem.rs | 416 +++++++ src/fs/mod.rs | 464 ++++---- src/fs/uhyve.rs | 317 ++++++ src/lib.rs | 1 + src/syscalls/interfaces/mod.rs | 45 +- src/syscalls/interfaces/uhyve.rs | 33 +- src/syscalls/mod.rs | 59 +- 15 files changed, 2492 insertions(+), 1961 deletions(-) delete mode 100644 src/fd/file.rs create mode 100644 src/fs/mem.rs create mode 100644 src/fs/uhyve.rs diff --git a/src/executor/network.rs b/src/executor/network.rs index be49df67dd..258a396ff7 100644 --- a/src/executor/network.rs +++ b/src/executor/network.rs @@ -29,6 +29,7 @@ use crate::drivers::net::NetworkDriver; use crate::drivers::pci::get_network_driver; use crate::executor::device::HermitNet; use crate::executor::{spawn, TaskNotify}; +use crate::fd::IoError; use crate::scheduler::PerCoreSchedulerExt; pub(crate) enum NetworkState<'a> { @@ -248,9 +249,9 @@ fn network_poll(timestamp: Instant) { } /// Blocks the current thread on `f`, running the executor when idling. -pub(crate) fn block_on(future: F, timeout: Option) -> Result +pub(crate) fn block_on(future: F, timeout: Option) -> Result where - F: Future>, + F: Future>, { // disable network interrupts let no_retransmission = { @@ -297,7 +298,7 @@ where // allow network interrupts get_network_driver().unwrap().lock().set_polling_mode(false); - return Err(-crate::errno::ETIME); + return Err(IoError::ETIME); } } @@ -327,9 +328,9 @@ where } /// Blocks the current thread on `f`, running the executor when idling. -pub(crate) fn poll_on(future: F, timeout: Option) -> Result +pub(crate) fn poll_on(future: F, timeout: Option) -> Result where - F: Future>, + F: Future>, { // disable network interrupts let no_retransmission = { @@ -372,7 +373,7 @@ where // allow network interrupts get_network_driver().unwrap().lock().set_polling_mode(false); - return Err(-crate::errno::ETIME); + return Err(IoError::ETIME); } } } diff --git a/src/fd/file.rs b/src/fd/file.rs deleted file mode 100644 index 2a3b126415..0000000000 --- a/src/fd/file.rs +++ /dev/null @@ -1,137 +0,0 @@ -use alloc::boxed::Box; -use core::{isize, slice}; - -use crate::fd::{ - uhyve_send, DirectoryEntry, ObjectInterface, SysClose, SysLseek, SysRead, SysWrite, - UHYVE_PORT_CLOSE, UHYVE_PORT_LSEEK, UHYVE_PORT_READ, UHYVE_PORT_WRITE, -}; -use crate::fs::{self, FileAttr, PosixFile, SeekWhence}; - -#[derive(Debug, Clone)] -pub struct UhyveFile(i32); - -impl UhyveFile { - pub fn new(fd: i32) -> Self { - Self(fd) - } -} - -impl ObjectInterface for UhyveFile { - fn write(&self, buf: *const u8, len: usize) -> isize { - let mut syswrite = SysWrite::new(self.0, buf, len); - uhyve_send(UHYVE_PORT_WRITE, &mut syswrite); - - syswrite.len as isize - } - - fn read(&self, buf: *mut u8, len: usize) -> isize { - let mut sysread = SysRead::new(self.0, buf, len); - uhyve_send(UHYVE_PORT_READ, &mut sysread); - - sysread.ret - } - - fn lseek(&self, offset: isize, whence: SeekWhence) -> isize { - let mut syslseek = SysLseek::new(self.0, offset, whence); - uhyve_send(UHYVE_PORT_LSEEK, &mut syslseek); - - syslseek.offset - } -} - -impl Drop for UhyveFile { - fn drop(&mut self) { - let mut sysclose = SysClose::new(self.0); - uhyve_send(UHYVE_PORT_CLOSE, &mut sysclose); - } -} - -#[derive(Debug, Clone)] -pub struct GenericFile(u64); - -impl GenericFile { - pub fn new(fd: u64) -> Self { - Self(fd) - } -} - -impl ObjectInterface for GenericFile { - fn write(&self, buf: *const u8, len: usize) -> isize { - assert!(len <= isize::MAX as usize); - let buf = unsafe { slice::from_raw_parts(buf, len) }; - - // Normal file - let mut written_bytes = 0; - let mut fs = fs::FILESYSTEM.lock(); - fs.fd_op(self.0, |file: &mut Box| { - written_bytes = file.write(buf).unwrap(); // TODO: might fail - }); - debug!("Write done! {}", written_bytes); - written_bytes as isize - } - - fn read(&self, buf: *mut u8, len: usize) -> isize { - debug!("Read! {}, {}", self.0, len); - - let mut fs = fs::FILESYSTEM.lock(); - let mut read_bytes = 0; - fs.fd_op(self.0, |file: &mut Box| { - let dat = file.read(len as u32).unwrap(); // TODO: might fail - - read_bytes = dat.len(); - unsafe { - core::slice::from_raw_parts_mut(buf, read_bytes).copy_from_slice(&dat); - } - }); - - read_bytes as isize - } - - fn lseek(&self, offset: isize, whence: SeekWhence) -> isize { - debug!("lseek! {}, {}, {:?}", self.0, offset, whence); - - let mut fs = fs::FILESYSTEM.lock(); - let mut ret = 0; - fs.fd_op(self.0, |file: &mut Box| { - ret = file.lseek(offset, whence).unwrap(); // TODO: might fail - }); - - ret as isize - } - - /// `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| { - result = file - .fstat(stat) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0); - }); - - result - } - - fn readdir(&self) -> DirectoryEntry { - debug!("readdir ! {}", self.0); - - let mut fs = fs::FILESYSTEM.lock(); - let mut ret = DirectoryEntry::Invalid(-crate::errno::EINVAL); - fs.fd_op(self.0, |file: &mut Box| { - match file.readdir() { - Ok(dir_ptr) => ret = DirectoryEntry::Valid(dir_ptr), - Err(e) => ret = DirectoryEntry::Invalid(-num::ToPrimitive::to_i32(&e).unwrap()), - } - }); - - ret - } -} - -impl Drop for GenericFile { - fn drop(&mut self) { - let mut fs = fs::FILESYSTEM.lock(); - fs.close(self.0); - } -} diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 632abdaa89..79355a1545 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -1,39 +1,45 @@ use alloc::sync::Arc; -use core::ffi::{c_void, CStr}; -#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] -use core::ptr; +use core::ffi::c_void; use core::sync::atomic::{AtomicI32, Ordering}; use ahash::RandomState; use dyn_clone::DynClone; use hashbrown::HashMap; -#[cfg(target_arch = "x86_64")] -use x86::io::*; -use crate::arch::mm::{paging, PhysAddr, VirtAddr}; use crate::env; use crate::errno::*; -use crate::fd::file::{GenericFile, UhyveFile}; use crate::fd::stdio::*; -use crate::fs::{self, Dirent, FileAttr, FilePerms, SeekWhence}; +use crate::fs::{self, FileAttr, SeekWhence}; #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] use crate::syscalls::net::*; -mod file; #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] pub mod socket; mod stdio; -const UHYVE_PORT_WRITE: u16 = 0x400; -const UHYVE_PORT_OPEN: u16 = 0x440; -const UHYVE_PORT_CLOSE: u16 = 0x480; -const UHYVE_PORT_READ: u16 = 0x500; -const UHYVE_PORT_LSEEK: u16 = 0x580; - const STDIN_FILENO: FileDescriptor = 0; const STDOUT_FILENO: FileDescriptor = 1; const STDERR_FILENO: FileDescriptor = 2; +// TODO: Integrate with src/errno.rs ? +#[allow(clippy::upper_case_acronyms)] +#[derive(Debug, PartialEq, FromPrimitive, ToPrimitive)] +pub(crate) enum IoError { + ENOENT = crate::errno::ENOENT as isize, + ENOSYS = crate::errno::ENOSYS as isize, + EIO = crate::errno::EIO as isize, + EBADF = crate::errno::EBADF as isize, + EISDIR = crate::errno::EISDIR as isize, + EINVAL = crate::errno::EINVAL as isize, + ETIME = crate::errno::ETIME as isize, + EAGAIN = crate::errno::EAGAIN as isize, + EFAULT = crate::errno::EFAULT as isize, + ENOBUFS = crate::errno::ENOBUFS as isize, + ENOTCONN = crate::errno::ENOTCONN as isize, + ENOTDIR = crate::errno::ENOTDIR as isize, + EMFILE = crate::errno::EMFILE as isize, +} + pub(crate) type FileDescriptor = i32; /// Mapping between file descriptor and the referenced object @@ -46,148 +52,29 @@ static OBJECT_MAP: pflock::PFLock SysOpen { - SysOpen { - name: paging::virtual_to_physical(name).unwrap(), - flags, - mode, - ret: -1, - } - } -} - -#[repr(C, packed)] -struct SysClose { - fd: i32, - ret: i32, -} - -impl SysClose { - fn new(fd: i32) -> SysClose { - SysClose { fd, ret: -1 } - } -} - -#[repr(C, packed)] -struct SysRead { - fd: i32, - buf: *const u8, - len: usize, - ret: isize, -} - -impl SysRead { - fn new(fd: i32, buf: *const u8, len: usize) -> SysRead { - SysRead { - fd, - buf, - len, - ret: -1, - } - } -} - -#[repr(C, packed)] -struct SysWrite { - fd: i32, - buf: *const u8, - len: usize, -} - -impl SysWrite { - pub fn new(fd: i32, buf: *const u8, len: usize) -> SysWrite { - SysWrite { fd, buf, len } - } -} - -#[repr(C, packed)] -struct SysLseek { - pub fd: i32, - pub offset: isize, - pub whence: i32, -} - -impl SysLseek { - fn new(fd: i32, offset: isize, whence: SeekWhence) -> SysLseek { - let whence: i32 = num::ToPrimitive::to_i32(&whence).unwrap(); - - SysLseek { fd, offset, whence } +bitflags! { + /// Options for opening files + #[derive(Debug, Copy, Clone, Default)] + pub(crate) struct OpenOption: i32 { + const O_RDONLY = 0o0000; + const O_WRONLY = 0o0001; + const O_RDWR = 0o0002; + const O_CREAT = 0o0100; + const O_EXCL = 0o0200; + const O_TRUNC = 0o1000; + const O_APPEND = 0o2000; + const O_DIRECT = 0o40000; } } -/// forward a request to the hypervisor uhyve -#[inline] -#[cfg(target_arch = "x86_64")] -fn uhyve_send(port: u16, data: &mut T) { - let ptr = VirtAddr(ptr::from_mut(data).addr() as u64); - let physical_address = paging::virtual_to_physical(ptr).unwrap(); - - unsafe { - outl(port, physical_address.as_u64() as u32); - } -} - -/// forward a request to the hypervisor uhyve -#[inline] -#[cfg(target_arch = "aarch64")] -fn uhyve_send(port: u16, data: &mut T) { - use core::arch::asm; - - let ptr = VirtAddr(ptr::from_mut(data).addr() as u64); - let physical_address = paging::virtual_to_physical(ptr).unwrap(); - - unsafe { - asm!( - "str x8, [{port}]", - port = in(reg) u64::from(port), - in("x8") physical_address.as_u64(), - options(nostack), - ); - } -} - -/// forward a request to the hypervisor uhyve -#[inline] -#[cfg(target_arch = "riscv64")] -fn uhyve_send(_port: u16, _data: &mut T) { - todo!() -} - -fn open_flags_to_perm(flags: i32, mode: u32) -> FilePerms { - let mut perms = FilePerms { - raw: flags as u32, - mode, - ..Default::default() - }; - perms.write = flags & (O_WRONLY | O_RDWR) != 0; - perms.creat = flags & (O_CREAT) != 0; - perms.excl = flags & (O_EXCL) != 0; - perms.trunc = flags & (O_TRUNC) != 0; - perms.append = flags & (O_APPEND) != 0; - perms.directio = flags & (O_DIRECT) != 0; - if flags & !(O_WRONLY | O_RDWR | O_CREAT | O_EXCL | O_TRUNC | O_APPEND | O_DIRECT) != 0 { - warn!("Unknown file flags used! {}", flags); - } - perms +#[repr(C)] +#[derive(Debug, Copy, Clone, Default)] +pub struct Dirent { + pub d_ino: u64, + pub d_off: u64, + pub d_namelen: u32, + pub d_type: u32, + pub d_name: [u8; 0], } #[derive(Copy, Clone, Debug)] @@ -200,34 +87,34 @@ pub enum DirectoryEntry { pub trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// `read` attempts to read `len` bytes from the object references /// by the descriptor - fn read(&self, _buf: *mut u8, _len: usize) -> isize { - (-ENOSYS).try_into().unwrap() + fn read(&self, _buf: &mut [u8]) -> Result { + Err(IoError::ENOSYS) } /// `write` attempts to write `len` bytes to the object references /// by the descriptor - fn write(&self, _buf: *const u8, _len: usize) -> isize { - (-EINVAL).try_into().unwrap() + fn write(&self, _buf: &[u8]) -> Result { + Err(IoError::ENOSYS) } /// `lseek` function repositions the offset of the file descriptor fildes - fn lseek(&self, _offset: isize, _whence: SeekWhence) -> isize { - (-EINVAL).try_into().unwrap() + fn lseek(&self, _offset: isize, _whence: SeekWhence) -> Result { + Err(IoError::EINVAL) } /// `fstat` - fn fstat(&self, _stat: *mut FileAttr) -> i32 { - -EINVAL + fn fstat(&self, _stat: &mut FileAttr) -> Result<(), IoError> { + Err(IoError::EINVAL) } /// `unlink` removes file entry - fn unlink(&self, _name: *const u8) -> i32 { - -EINVAL + fn unlink(&self, _path: &str) -> Result<(), IoError> { + Err(IoError::EINVAL) } /// `rmdir` removes directory entry - fn rmdir(&self, _name: *const u8) -> i32 { - -EINVAL + fn rmdir(&self, _path: &str) -> Result<(), IoError> { + Err(IoError::EINVAL) } /// 'readdir' returns a pointer to a dirent structure @@ -238,32 +125,32 @@ pub trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { } /// `mkdir` creates a directory entry - fn mkdir(&self, _name: *const u8, _mode: u32) -> i32 { - -EINVAL + fn mkdir(&self, _path: &str, _mode: u32) -> Result<(), IoError> { + Err(IoError::EINVAL) } /// `accept` a connection on a socket #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn accept(&self, _addr: *mut sockaddr, _addrlen: *mut socklen_t) -> i32 { - -EINVAL + fn accept(&self, _addr: *mut sockaddr, _addrlen: *mut socklen_t) -> Result { + Err(IoError::EINVAL) } /// initiate a connection on a socket #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn connect(&self, _name: *const sockaddr, _namelen: socklen_t) -> i32 { - -EINVAL + fn connect(&self, _name: *const sockaddr, _namelen: socklen_t) -> Result { + Err(IoError::EINVAL) } /// `bind` a name to a socket #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn bind(&self, _name: *const sockaddr, _namelen: socklen_t) -> i32 { - -EINVAL + fn bind(&self, _name: *const sockaddr, _namelen: socklen_t) -> Result<(), IoError> { + Err(IoError::EINVAL) } /// `listen` for connections on a socket #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn listen(&self, _backlog: i32) -> i32 { - -EINVAL + fn listen(&self, _backlog: i32) -> Result<(), IoError> { + Err(IoError::EINVAL) } /// `setsockopt` sets options on sockets @@ -274,8 +161,8 @@ pub trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { _optname: i32, _optval: *const c_void, _optlen: socklen_t, - ) -> i32 { - -EINVAL + ) -> Result<(), IoError> { + Err(IoError::EINVAL) } /// `getsockopt` gets options on sockets @@ -286,20 +173,20 @@ pub trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { _option_name: i32, _optval: *mut c_void, _optlen: *mut socklen_t, - ) -> i32 { - -EINVAL + ) -> Result<(), IoError> { + Err(IoError::EINVAL) } /// `getsockname` gets socket name #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn getsockname(&self, _name: *mut sockaddr, _namelen: *mut socklen_t) -> i32 { - -EINVAL + fn getsockname(&self, _name: *mut sockaddr, _namelen: *mut socklen_t) -> Result<(), IoError> { + Err(IoError::EINVAL) } /// `getpeername` get address of connected peer #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn getpeername(&self, _name: *mut sockaddr, _namelen: *mut socklen_t) -> i32 { - -EINVAL + fn getpeername(&self, _name: *mut sockaddr, _namelen: *mut socklen_t) -> Result<(), IoError> { + Err(IoError::EINVAL) } /// receive a message from a socket @@ -311,12 +198,11 @@ pub trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] fn recvfrom( &self, - _buffer: *mut u8, - _len: usize, + _buffer: &mut [u8], _address: *mut sockaddr, _address_len: *mut socklen_t, - ) -> isize { - (-ENOSYS).try_into().unwrap() + ) -> Result { + Err(IoError::ENOSYS) } /// send a message from a socket @@ -329,102 +215,71 @@ pub trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] fn sendto( &self, - _buffer: *const u8, - _len: usize, + _buffer: &[u8], _addr: *const sockaddr, _addr_len: socklen_t, - ) -> isize { - (-ENOSYS).try_into().unwrap() + ) -> Result { + Err(IoError::ENOSYS) } /// shut down part of a full-duplex connection #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn shutdown(&self, _how: i32) -> i32 { - -EINVAL + fn shutdown(&self, _how: i32) -> Result<(), IoError> { + Err(IoError::ENOSYS) } /// The `ioctl` function manipulates the underlying device parameters of special /// files. - fn ioctl(&self, _cmd: i32, _argp: *mut c_void) -> i32 { - -EINVAL + fn ioctl(&self, _cmd: i32, _argp: *mut c_void) -> Result<(), IoError> { + Err(IoError::ENOSYS) } } -pub(crate) fn open(name: *const u8, flags: i32, mode: i32) -> Result { - if env::is_uhyve() { - let mut sysopen = SysOpen::new(VirtAddr(name as u64), flags, mode); - uhyve_send(UHYVE_PORT_OPEN, &mut sysopen); +pub(crate) fn open(name: &str, flags: i32, mode: i32) -> Result { + { + // mode is 0x777 (0b0111_0111_0111), when flags | O_CREAT, else 0 + // flags is bitmask of O_DEC_* defined above. + // (taken from rust stdlib/sys hermit target ) - if sysopen.ret > 0 { - let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); - let file = UhyveFile::new(sysopen.ret); + debug!("Open {}, {}, {}", name, flags, mode); - if OBJECT_MAP.write().try_insert(fd, Arc::new(file)).is_err() { - Err(-EINVAL) + let fs = fs::FILESYSTEM.get().unwrap(); + if let Ok(file) = fs.open( + name, + OpenOption::from_bits(mode).expect("Invalid open flags"), + ) { + let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); + if OBJECT_MAP.write().try_insert(fd, file).is_err() { + Err(IoError::EINVAL) } else { Ok(fd as FileDescriptor) } } else { - Err(sysopen.ret) + Err(IoError::EINVAL) } - } else { - { - // mode is 0x777 (0b0111_0111_0111), when flags | O_CREAT, else 0 - // flags is bitmask of O_DEC_* defined above. - // (taken from rust stdlib/sys hermit target ) - - let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); - debug!("Open {}, {}, {}", name, flags, mode); - - let mut fs = fs::FILESYSTEM.lock(); - if let Ok(filesystem_fd) = fs.open(name, open_flags_to_perm(flags, mode as u32)) { - let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); - 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) - } - } - } + } //} } #[allow(unused_variables)] -pub(crate) fn opendir(name: *const u8) -> 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 opendir(name: &str) -> Result { + debug!("Open directory {}", name); + + let fs = fs::FILESYSTEM.get().unwrap(); + if let Ok(obj) = fs.opendir(name) { + let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); + // Would a GenericDir make sense? + if OBJECT_MAP.write().try_insert(fd, obj).is_err() { + Err(IoError::EINVAL) + } else { + Ok(fd as FileDescriptor) } + } else { + Err(IoError::EINVAL) } } -pub(crate) fn get_object(fd: FileDescriptor) -> Result, i32> { - Ok((*(OBJECT_MAP.read().get(&fd).ok_or(-EINVAL)?)).clone()) +pub(crate) fn get_object(fd: FileDescriptor) -> Result, IoError> { + Ok((*(OBJECT_MAP.read().get(&fd).ok_or(IoError::EINVAL)?)).clone()) } #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] @@ -439,9 +294,9 @@ pub(crate) fn insert_object( // to the same open file description as the descriptor oldfd. The new // file descriptor number is guaranteed to be the lowest-numbered // file descriptor that was unused in the calling process. -pub(crate) fn dup_object(fd: FileDescriptor) -> Result { +pub(crate) fn dup_object(fd: FileDescriptor) -> Result { let mut guard = OBJECT_MAP.write(); - let obj = (*(guard.get(&fd).ok_or(-EINVAL)?)).clone(); + let obj = (*(guard.get(&fd).ok_or(IoError::EINVAL)?)).clone(); let new_fd = || -> i32 { for i in 3..FD_COUNTER.load(Ordering::SeqCst) { @@ -454,17 +309,17 @@ pub(crate) fn dup_object(fd: FileDescriptor) -> Result { let fd = new_fd(); if guard.try_insert(fd, obj).is_err() { - Err(-EMFILE) + Err(IoError::EMFILE) } else { Ok(fd as FileDescriptor) } } -pub(crate) fn remove_object(fd: FileDescriptor) -> Result, i32> { +pub(crate) fn remove_object(fd: FileDescriptor) -> Result, IoError> { if fd <= 2 { - Err(-EINVAL) + Err(IoError::EINVAL) } else { - let obj = OBJECT_MAP.write().remove(&fd).ok_or(-EINVAL)?; + let obj = OBJECT_MAP.write().remove(&fd).ok_or(IoError::EINVAL)?; Ok(obj) } } diff --git a/src/fd/socket/mod.rs b/src/fd/socket/mod.rs index 8eabd403b0..980773d31d 100644 --- a/src/fd/socket/mod.rs +++ b/src/fd/socket/mod.rs @@ -84,36 +84,58 @@ pub(crate) extern "C" fn __sys_accept( ) -> i32 { let obj = get_object(fd); obj.map_or_else( - |e| e, + |e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| { - let result = (*v).accept(addr, addrlen); - if result >= 0 { - let new_obj = dyn_clone::clone_box(&*v); - insert_object(fd, Arc::from(new_obj)); - let new_fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); - (*v).listen(1); - insert_object(new_fd, v.clone()); - new_fd - } else { - result - } + (*v).accept(addr, addrlen).map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |_| { + let new_obj = dyn_clone::clone_box(&*v); + insert_object(fd, Arc::from(new_obj)); + let new_fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); + match (*v).listen(1) { + Ok(_) => { + insert_object(new_fd, v.clone()); + new_fd + } + Err(e) => -num::ToPrimitive::to_i32(&e).unwrap(), + } + }, + ) }, ) } pub(crate) extern "C" fn __sys_listen(fd: i32, backlog: i32) -> i32 { let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).listen(backlog)) + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).listen(backlog) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) } pub(crate) extern "C" fn __sys_bind(fd: i32, name: *const sockaddr, namelen: socklen_t) -> i32 { let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).bind(name, namelen)) + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).bind(name, namelen) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) } pub(crate) extern "C" fn __sys_connect(fd: i32, name: *const sockaddr, namelen: socklen_t) -> i32 { let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).connect(name, namelen)) + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).connect(name, namelen) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| v) + }, + ) } pub(crate) extern "C" fn __sys_getsockname( @@ -122,7 +144,13 @@ pub(crate) extern "C" fn __sys_getsockname( namelen: *mut socklen_t, ) -> i32 { let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).getsockname(name, namelen)) + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).getsockname(name, namelen) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) } pub(crate) extern "C" fn __sys_setsockopt( @@ -138,7 +166,13 @@ pub(crate) extern "C" fn __sys_setsockopt( ); let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).setsockopt(level, optname, optval, optlen)) + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).setsockopt(level, optname, optval, optlen) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) } pub(crate) extern "C" fn __sys_getsockopt( @@ -154,7 +188,13 @@ pub(crate) extern "C" fn __sys_getsockopt( ); let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).getsockopt(level, optname, optval, optlen)) + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).getsockopt(level, optname, optval, optlen) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) } pub(crate) extern "C" fn __sys_getpeername( @@ -163,7 +203,13 @@ pub(crate) extern "C" fn __sys_getpeername( namelen: *mut socklen_t, ) -> i32 { let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).getpeername(name, namelen)) + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).getpeername(name, namelen) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) } pub extern "C" fn __sys_freeaddrinfo(_ai: *mut addrinfo) {} @@ -179,12 +225,25 @@ pub extern "C" fn __sys_getaddrinfo( pub extern "C" fn __sys_shutdown_socket(fd: i32, how: i32) -> i32 { let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).shutdown(how)) + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).shutdown(how) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) } pub extern "C" fn __sys_recv(fd: i32, buf: *mut u8, len: usize) -> isize { + let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; let obj = get_object(fd); - obj.map_or_else(|e| e as isize, |v| (*v).read(buf, len)) + obj.map_or_else( + |e| e as isize, + |v| { + (*v).read(slice) + .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + }, + ) } pub extern "C" fn __sys_sendto( @@ -195,8 +254,15 @@ pub extern "C" fn __sys_sendto( addr: *const sockaddr, addr_len: socklen_t, ) -> isize { + let slice = unsafe { core::slice::from_raw_parts(buf, len) }; let obj = get_object(fd); - obj.map_or_else(|e| e as isize, |v| (*v).sendto(buf, len, addr, addr_len)) + obj.map_or_else( + |e| e as isize, + |v| { + (*v).sendto(slice, addr, addr_len) + .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + }, + ) } pub extern "C" fn __sys_recvfrom( @@ -207,6 +273,13 @@ pub extern "C" fn __sys_recvfrom( addr: *mut sockaddr, addr_len: *mut socklen_t, ) -> isize { + let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; let obj = get_object(fd); - obj.map_or_else(|e| e as isize, |v| (*v).recvfrom(buf, len, addr, addr_len)) + obj.map_or_else( + |e| e as isize, + |v| { + (*v).recvfrom(slice, addr, addr_len) + .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + }, + ) } diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index 8d464f1dd7..c75a9c3f67 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -11,9 +11,8 @@ use smoltcp::socket::tcp; use smoltcp::time::Duration; use smoltcp::wire::IpAddress; -use crate::errno::*; use crate::executor::network::{block_on, now, poll_on, Handle, NetworkState, NIC}; -use crate::fd::ObjectInterface; +use crate::fd::{IoError, ObjectInterface}; use crate::syscalls::net::*; use crate::DEFAULT_KEEP_ALIVE_INTERVAL; @@ -69,7 +68,7 @@ impl Socket { // TODO: Remove allow once fixed: // https://github.com/rust-lang/rust-clippy/issues/11380 #[allow(clippy::needless_pass_by_ref_mut)] - async fn async_read(&self, buffer: &mut [u8]) -> Result { + async fn async_read(&self, buffer: &mut [u8]) -> Result { future::poll_fn(|cx| { self.with(|socket| match socket.state() { tcp::State::Closed | tcp::State::Closing | tcp::State::CloseWait => { @@ -78,7 +77,7 @@ impl Socket { tcp::State::FinWait1 | tcp::State::FinWait2 | tcp::State::Listen - | tcp::State::TimeWait => Poll::Ready(Err(-crate::errno::EIO)), + | tcp::State::TimeWait => Poll::Ready(Err(IoError::EIO)), _ => { if socket.can_recv() { Poll::Ready( @@ -88,7 +87,7 @@ impl Socket { buffer[..len].copy_from_slice(&data[..len]); (len, isize::try_from(len).unwrap()) }) - .map_err(|_| -crate::errno::EIO), + .map_err(|_| IoError::EIO), ) } else { socket.register_recv_waker(cx.waker()); @@ -100,7 +99,7 @@ impl Socket { .await } - async fn async_write(&self, buffer: &[u8]) -> Result { + async fn async_write(&self, buffer: &[u8]) -> Result { let mut pos: usize = 0; while pos < buffer.len() { @@ -113,13 +112,11 @@ impl Socket { tcp::State::FinWait1 | tcp::State::FinWait2 | tcp::State::Listen - | tcp::State::TimeWait => Poll::Ready(Err(-crate::errno::EIO)), + | tcp::State::TimeWait => Poll::Ready(Err(IoError::EIO)), _ => { if socket.can_send() { Poll::Ready( - socket - .send_slice(&buffer[pos..]) - .map_err(|_| -crate::errno::EIO), + socket.send_slice(&buffer[pos..]).map_err(|_| IoError::EIO), ) } else if pos > 0 { // we already send some data => return 0 as signal to stop the @@ -145,19 +142,17 @@ impl Socket { Ok(pos.try_into().unwrap()) } - async fn async_connect(&self, address: IpAddress, port: u16) -> Result { + async fn async_connect(&self, address: IpAddress, port: u16) -> Result { self.with_context(|socket, cx| socket.connect(cx, (address, port), get_ephemeral_port())) .map_err(|x| { info!("x {:?}", x); - -crate::errno::EIO + IoError::EIO })?; future::poll_fn(|cx| { self.with(|socket| match socket.state() { - tcp::State::Closed | tcp::State::TimeWait => { - Poll::Ready(Err(-crate::errno::EFAULT)) - } - tcp::State::Listen => Poll::Ready(Err(-crate::errno::EIO)), + tcp::State::Closed | tcp::State::TimeWait => Poll::Ready(Err(IoError::EFAULT)), + tcp::State::Listen => Poll::Ready(Err(IoError::EIO)), tcp::State::SynSent | tcp::State::SynReceived => { socket.register_send_waker(cx.waker()); Poll::Pending @@ -168,14 +163,14 @@ impl Socket { .await } - async fn async_close(&self) -> Result<(), i32> { + async fn async_close(&self) -> Result<(), IoError> { future::poll_fn(|cx| { self.with(|socket| match socket.state() { tcp::State::FinWait1 | tcp::State::FinWait2 | tcp::State::Closed | tcp::State::Closing - | tcp::State::TimeWait => Poll::Ready(Err(-crate::errno::EIO)), + | tcp::State::TimeWait => Poll::Ready(Err(IoError::EIO)), _ => { if socket.send_queue() > 0 { socket.register_send_waker(cx.waker()); @@ -209,7 +204,7 @@ impl Socket { &self, _addr: *mut sockaddr, _addrlen: *mut socklen_t, - ) -> Result<(), i32> { + ) -> Result<(), IoError> { future::poll_fn(|cx| { self.with(|socket| match socket.state() { tcp::State::Closed => { @@ -234,7 +229,7 @@ impl Socket { tcp::State::Closed | tcp::State::Closing | tcp::State::FinWait1 - | tcp::State::FinWait2 => Poll::Ready(Err(-crate::errno::EIO)), + | tcp::State::FinWait2 => Poll::Ready(Err(IoError::EIO)), _ => { socket.register_recv_waker(cx.waker()); Poll::Pending @@ -246,74 +241,66 @@ impl Socket { .await?; let mut guard = NIC.lock(); - let nic = guard.as_nic_mut().map_err(|_| -crate::errno::EIO)?; + let nic = guard.as_nic_mut().map_err(|_| IoError::EIO)?; let socket = nic.get_mut_socket::>(self.handle); socket.set_keep_alive(Some(Duration::from_millis(DEFAULT_KEEP_ALIVE_INTERVAL))); Ok(()) } - fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> i32 { - block_on(self.async_accept(addr, addrlen), None) - .map(|_| 0) - .unwrap_or_else(|x| x) + fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> Result { + block_on(self.async_accept(addr, addrlen), None).map(|_| 0) } - fn read(&self, buf: *mut u8, len: usize) -> isize { - if len == 0 { - return 0; + fn read(&self, buf: &mut [u8]) -> Result { + if buf.len() == 0 { + return Ok(0); } - let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; - if self.nonblocking.load(Ordering::Acquire) { - poll_on(self.async_read(slice), Some(Duration::ZERO)).unwrap_or_else(|x| { - if x == -ETIME { - (-EAGAIN).try_into().unwrap() + poll_on(self.async_read(buf), Some(Duration::ZERO)).map_err(|x| { + if x == IoError::ETIME { + IoError::EAGAIN } else { - x.try_into().unwrap() + x } }) } else { - poll_on(self.async_read(slice), Some(Duration::from_secs(2))).unwrap_or_else(|x| { - if x == -ETIME { - block_on(self.async_read(slice), None).unwrap_or_else(|y| y.try_into().unwrap()) - } else { - x.try_into().unwrap() - } - }) + match poll_on(self.async_read(buf), Some(Duration::from_secs(2))) { + Err(IoError::ETIME) => block_on(self.async_read(buf), None), + Err(x) => Err(x), + Ok(x) => Ok(x), + } } } - fn write(&self, buf: *const u8, len: usize) -> isize { - if len == 0 { - return 0; + fn write(&self, buf: &[u8]) -> Result { + if buf.len() == 0 { + return Ok(0); } - let slice = unsafe { core::slice::from_raw_parts(buf, len) }; - if self.nonblocking.load(Ordering::Acquire) { - poll_on(self.async_write(slice), Some(Duration::ZERO)).unwrap_or_else(|x| { - if x == -ETIME { - (-EAGAIN).try_into().unwrap() + poll_on(self.async_write(buf), Some(Duration::ZERO)).map_err(|x| { + if x == IoError::ETIME { + IoError::EAGAIN } else { - x.try_into().unwrap() + x } }) } else { - poll_on(self.async_write(slice), None).unwrap_or_else(|x| x.try_into().unwrap()) + poll_on(self.async_write(buf), None) } } - fn listen(&self, _backlog: i32) -> i32 { + fn listen(&self, _backlog: i32) -> Result<(), IoError> { self.with(|socket| { if !socket.is_open() { socket .listen(self.port.load(Ordering::Acquire)) - .map(|_| 0) - .unwrap_or_else(|_| -crate::errno::EIO) + .map(|_| ()) + .map_err(|_| IoError::EIO) } else { - -crate::errno::EIO + Err(IoError::EIO) } }) } @@ -324,7 +311,7 @@ impl Socket { optname: i32, optval: *const c_void, optlen: socklen_t, - ) -> i32 { + ) -> Result<(), IoError> { if level == IPPROTO_TCP && optname == TCP_NODELAY && optlen == size_of::().try_into().unwrap() @@ -338,12 +325,12 @@ impl Socket { socket.set_ack_delay(Some(Duration::from_millis(10))); } }); - 0 + Ok(()) } else if level == SOL_SOCKET && optname == SO_REUSEADDR { // smoltcp is always able to reuse the addr - 0 + Ok(()) } else { - -EINVAL + Err(IoError::EINVAL) } } @@ -353,7 +340,7 @@ impl Socket { optname: i32, optval: *mut c_void, optlen: *mut socklen_t, - ) -> i32 { + ) -> Result<(), IoError> { if level == IPPROTO_TCP && optname == TCP_NODELAY { let optlen = unsafe { &mut *optlen }; if *optlen >= size_of::().try_into().unwrap() { @@ -367,25 +354,25 @@ impl Socket { }); *optlen = size_of::().try_into().unwrap(); - 0 + Ok(()) } else { - -EINVAL + Err(IoError::EINVAL) } } else { - -EINVAL + Err(IoError::EINVAL) } } - fn shutdown(&self, how: i32) -> i32 { + fn shutdown(&self, how: i32) -> Result<(), IoError> { match how { SHUT_RD /* Read */ | SHUT_WR /* Write */ | - SHUT_RDWR /* Both */ => 0, - _ => -EINVAL, + SHUT_RDWR /* Both */ => Ok(()), + _ => Err(IoError::EINVAL), } } - fn ioctl(&self, cmd: i32, argp: *mut c_void) -> i32 { + fn ioctl(&self, cmd: i32, argp: *mut c_void) -> Result<(), IoError> { if cmd == FIONBIO { let value = unsafe { *(argp as *const i32) }; if value != 0 { @@ -396,9 +383,9 @@ impl Socket { self.nonblocking.store(false, Ordering::Release); } - 0 + Ok(()) } else { - -EINVAL + Err(IoError::EINVAL) } } } @@ -430,18 +417,18 @@ impl Drop for Socket { } impl ObjectInterface for Socket { - fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { + fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> Result<(), IoError> { if namelen == size_of::().try_into().unwrap() { let addr = unsafe { *(name as *const sockaddr_in) }; let port = u16::from_be(addr.sin_port); self.port.store(port, Ordering::Release); - 0 + Ok(()) } else { - -EINVAL + Err(IoError::EINVAL) } } - fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { + fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> Result { if namelen == size_of::().try_into().unwrap() { let saddr = unsafe { *(name as *const sockaddr_in) }; let port = u16::from_be(saddr.sin_port); @@ -453,31 +440,29 @@ impl ObjectInterface for Socket { ); if self.nonblocking.load(Ordering::Acquire) { - block_on(self.async_connect(address, port), Some(Duration::ZERO)).unwrap_or_else( - |x| { - if x == -ETIME { - -EAGAIN - } else { - x - } - }, - ) + block_on(self.async_connect(address, port), Some(Duration::ZERO)).map_err(|x| { + if x == IoError::ETIME { + IoError::EAGAIN + } else { + x + } + }) } else { - block_on(self.async_connect(address, port), None).unwrap_or_else(|x| x) + block_on(self.async_connect(address, port), None) } } else { - -EINVAL + Err(IoError::EINVAL) } } - fn getpeername(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> i32 { + fn getpeername(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> Result<(), IoError> { if namelen.is_null() { - return -ENOBUFS; + return Err(IoError::ENOBUFS); } let namelen = unsafe { &mut *namelen }; if *namelen >= size_of::().try_into().unwrap() { - let mut ret: i32 = 0; + let mut ret: Result<(), IoError> = Ok(()); let addr = unsafe { &mut *(name as *mut sockaddr_in) }; self.with(|socket| { @@ -488,7 +473,7 @@ impl ObjectInterface for Socket { addr.sin_addr.s_addr.copy_from_slice(ip.as_bytes()); } } else { - ret = -crate::errno::ENOTCONN; + ret = Err(IoError::ENOTCONN); } }); @@ -496,13 +481,13 @@ impl ObjectInterface for Socket { ret } else { - -EINVAL + Err(IoError::EINVAL) } } - fn getsockname(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> i32 { + fn getsockname(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> Result<(), IoError> { if namelen.is_null() { - return -ENOBUFS; + return Err(IoError::ENOBUFS); } let namelen = unsafe { &mut *namelen }; @@ -522,25 +507,25 @@ impl ObjectInterface for Socket { *namelen = size_of::().try_into().unwrap(); - 0 + Ok(()) } else { - -EINVAL + Err(IoError::EINVAL) } } - fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> i32 { + fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> Result { self.accept(addr, addrlen) } - fn read(&self, buf: *mut u8, len: usize) -> isize { - self.read(buf, len) + fn read(&self, buf: &mut [u8]) -> Result { + self.read(buf) } - fn write(&self, buf: *const u8, len: usize) -> isize { - self.write(buf, len) + fn write(&self, buf: &[u8]) -> Result { + self.write(buf) } - fn listen(&self, backlog: i32) -> i32 { + fn listen(&self, backlog: i32) -> Result<(), IoError> { self.listen(backlog) } @@ -550,7 +535,7 @@ impl ObjectInterface for Socket { optname: i32, optval: *const c_void, optlen: socklen_t, - ) -> i32 { + ) -> Result<(), IoError> { self.setsockopt(level, optname, optval, optlen) } @@ -560,31 +545,31 @@ impl ObjectInterface for Socket { optname: i32, optval: *mut c_void, optlen: *mut socklen_t, - ) -> i32 { + ) -> Result<(), IoError> { self.getsockopt(level, optname, optval, optlen) } - fn shutdown(&self, how: i32) -> i32 { + fn shutdown(&self, how: i32) -> Result<(), IoError> { self.shutdown(how) } - fn ioctl(&self, cmd: i32, argp: *mut c_void) -> i32 { + fn ioctl(&self, cmd: i32, argp: *mut c_void) -> Result<(), IoError> { self.ioctl(cmd, argp) } } impl ObjectInterface for Socket { - fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { + fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> Result<(), IoError> { if namelen == size_of::().try_into().unwrap() { let addr = unsafe { *(name as *const sockaddr_in6) }; self.port.store(addr.sin6_port, Ordering::Release); - 0 + Ok(()) } else { - -EINVAL + Err(IoError::EINVAL) } } - fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { + fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> Result { if namelen == size_of::().try_into().unwrap() { let saddr = unsafe { *(name as *const sockaddr_in6) }; let port = u16::from_be(saddr.sin6_port); @@ -602,31 +587,29 @@ impl ObjectInterface for Socket { let address = IpAddress::v6(a0, a1, a2, a3, a4, a5, a6, a7); if self.nonblocking.load(Ordering::Acquire) { - block_on(self.async_connect(address, port), Some(Duration::ZERO)).unwrap_or_else( - |x| { - if x == -ETIME { - -EAGAIN - } else { - x - } - }, - ) + block_on(self.async_connect(address, port), Some(Duration::ZERO)).map_err(|x| { + if x == IoError::ETIME { + IoError::EAGAIN + } else { + x + } + }) } else { - block_on(self.async_connect(address, port), None).unwrap_or_else(|x| x) + block_on(self.async_connect(address, port), None) } } else { - -EINVAL + Err(IoError::EINVAL) } } - fn getpeername(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> i32 { + fn getpeername(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> Result<(), IoError> { if namelen.is_null() { - return -ENOBUFS; + return Err(IoError::ENOBUFS); } let namelen = unsafe { &mut *namelen }; if *namelen >= size_of::().try_into().unwrap() { - let mut ret: i32 = 0; + let mut ret: Result<(), IoError> = Ok(()); let addr = unsafe { &mut *(name as *mut sockaddr_in6) }; self.with(|socket| { @@ -637,7 +620,7 @@ impl ObjectInterface for Socket { addr.sin6_addr.s6_addr.copy_from_slice(ip.as_bytes()); } } else { - ret = -crate::errno::ENOTCONN; + ret = Err(IoError::ENOTCONN); } }); @@ -645,13 +628,13 @@ impl ObjectInterface for Socket { ret } else { - -EINVAL + Err(IoError::EINVAL) } } - fn getsockname(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> i32 { + fn getsockname(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> Result<(), IoError> { if namelen.is_null() { - return -ENOBUFS; + return Err(IoError::ENOBUFS); } let namelen = unsafe { &mut *namelen }; @@ -671,25 +654,25 @@ impl ObjectInterface for Socket { *namelen = size_of::().try_into().unwrap(); - 0 + Ok(()) } else { - -EINVAL + Err(IoError::EINVAL) } } - fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> i32 { + fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> Result { self.accept(addr, addrlen) } - fn read(&self, buf: *mut u8, len: usize) -> isize { - self.read(buf, len) + fn read(&self, buf: &mut [u8]) -> Result { + self.read(buf) } - fn write(&self, buf: *const u8, len: usize) -> isize { - self.write(buf, len) + fn write(&self, buf: &[u8]) -> Result { + self.write(buf) } - fn listen(&self, backlog: i32) -> i32 { + fn listen(&self, backlog: i32) -> Result<(), IoError> { self.listen(backlog) } @@ -699,7 +682,7 @@ impl ObjectInterface for Socket { optname: i32, optval: *const c_void, optlen: socklen_t, - ) -> i32 { + ) -> Result<(), IoError> { self.setsockopt(level, optname, optval, optlen) } @@ -709,15 +692,15 @@ impl ObjectInterface for Socket { optname: i32, optval: *mut c_void, optlen: *mut socklen_t, - ) -> i32 { + ) -> Result<(), IoError> { self.getsockopt(level, optname, optval, optlen) } - fn shutdown(&self, how: i32) -> i32 { + fn shutdown(&self, how: i32) -> Result<(), IoError> { self.shutdown(how) } - fn ioctl(&self, cmd: i32, argp: *mut c_void) -> i32 { + fn ioctl(&self, cmd: i32, argp: *mut c_void) -> Result<(), IoError> { self.ioctl(cmd, argp) } } diff --git a/src/fd/socket/udp.rs b/src/fd/socket/udp.rs index e9748d80fa..6889ae9ca0 100644 --- a/src/fd/socket/udp.rs +++ b/src/fd/socket/udp.rs @@ -12,9 +12,8 @@ use smoltcp::socket::udp::UdpMetadata; use smoltcp::time::Duration; use smoltcp::wire::{IpAddress, IpEndpoint, IpListenEndpoint, Ipv4Address, Ipv6Address}; -use crate::errno::*; use crate::executor::network::{block_on, now, poll_on, Handle, NetworkState, NIC}; -use crate::fd::ObjectInterface; +use crate::fd::{IoError, ObjectInterface}; use crate::syscalls::net::*; #[derive(Debug)] @@ -50,7 +49,7 @@ impl Socket { result } - async fn async_close(&self) -> Result<(), i32> { + async fn async_close(&self) -> Result<(), IoError> { future::poll_fn(|_cx| { self.with(|socket| { socket.close(); @@ -60,7 +59,7 @@ impl Socket { .await } - async fn async_read(&self, buffer: &mut [u8]) -> Result { + async fn async_read(&self, buffer: &mut [u8]) -> Result { future::poll_fn(|cx| { self.with(|socket| { if socket.is_open() { @@ -78,21 +77,21 @@ impl Socket { } None => Poll::Ready(Ok(len.try_into().unwrap())), }, - _ => Poll::Ready(Err(-crate::errno::EIO)), + _ => Poll::Ready(Err(IoError::EIO)), } } else { socket.register_recv_waker(cx.waker()); Poll::Pending } } else { - Poll::Ready(Err(-crate::errno::EIO)) + Poll::Ready(Err(IoError::EIO)) } }) }) .await } - async fn async_recvfrom(&self, buffer: &mut [u8]) -> Result<(isize, UdpMetadata), i32> { + async fn async_recvfrom(&self, buffer: &mut [u8]) -> Result<(isize, UdpMetadata), IoError> { future::poll_fn(|cx| { self.with(|socket| { if socket.is_open() { @@ -110,21 +109,21 @@ impl Socket { } None => Poll::Ready(Ok((len.try_into().unwrap(), meta))), }, - _ => Poll::Ready(Err(-crate::errno::EIO)), + _ => Poll::Ready(Err(IoError::EIO)), } } else { socket.register_recv_waker(cx.waker()); Poll::Pending } } else { - Poll::Ready(Err(-crate::errno::EIO)) + Poll::Ready(Err(IoError::EIO)) } }) }) .await } - async fn async_write(&self, buffer: &[u8], meta: &UdpMetadata) -> Result { + async fn async_write(&self, buffer: &[u8], meta: &UdpMetadata) -> Result { future::poll_fn(|cx| { self.with(|socket| { if socket.is_open() { @@ -133,73 +132,62 @@ impl Socket { socket .send_slice(buffer, *meta) .map(|_| buffer.len() as isize) - .map_err(|_| -crate::errno::EIO), + .map_err(|_| IoError::EIO), ) } else { socket.register_recv_waker(cx.waker()); Poll::Pending } } else { - Poll::Ready(Err(-crate::errno::EIO)) + Poll::Ready(Err(IoError::EIO)) } }) }) .await } - fn read(&self, buf: *mut u8, len: usize) -> isize { - if len == 0 { - return 0; + fn read(&self, buf: &mut [u8]) -> Result { + if buf.len() == 0 { + return Ok(0); } - let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; - if self.nonblocking.load(Ordering::Acquire) { - poll_on(self.async_read(slice), Some(Duration::ZERO)).unwrap_or_else(|x| { - if x == -ETIME { - (-EAGAIN).try_into().unwrap() + poll_on(self.async_read(buf), Some(Duration::ZERO)).map_err(|x| { + if x == IoError::ETIME { + IoError::EAGAIN } else { - x.try_into().unwrap() + x } }) } else { - poll_on(self.async_read(slice), Some(Duration::from_secs(2))).unwrap_or_else(|x| { - if x == -ETIME { - block_on(self.async_read(slice), None).unwrap_or_else(|y| y.try_into().unwrap()) - } else { - x.try_into().unwrap() - } - }) + match poll_on(self.async_read(buf), Some(Duration::from_secs(2))) { + Err(IoError::ETIME) => block_on(self.async_read(buf), None), + Err(x) => Err(x), + Ok(x) => Ok(x), + } } } - fn write(&self, buf: *const u8, len: usize) -> isize { - if len == 0 { - return 0; + fn write(&self, buf: &[u8]) -> Result { + if buf.len() == 0 { + return Ok(0); } let endpoint = self.endpoint.load(); if endpoint.is_none() { - return (-EINVAL).try_into().unwrap(); + return Err(IoError::EINVAL); } let meta = UdpMetadata::from(endpoint.unwrap()); - let slice = unsafe { core::slice::from_raw_parts(buf, len) }; if self.nonblocking.load(Ordering::Acquire) { - poll_on(self.async_write(slice, &meta), Some(Duration::ZERO)).unwrap_or_else(|x| { - if x == -ETIME { - (-EAGAIN).try_into().unwrap() - } else { - x.try_into().unwrap() - } - }) + poll_on(self.async_write(buf, &meta), Some(Duration::ZERO)) } else { - poll_on(self.async_write(slice, &meta), None).unwrap_or_else(|x| x.try_into().unwrap()) + poll_on(self.async_write(buf, &meta), None) } } - fn ioctl(&self, cmd: i32, argp: *mut c_void) -> i32 { + fn ioctl(&self, cmd: i32, argp: *mut c_void) -> Result<(), IoError> { if cmd == FIONBIO { let value = unsafe { *(argp as *const i32) }; if value != 0 { @@ -210,9 +198,9 @@ impl Socket { self.nonblocking.store(false, Ordering::Release); } - 0 + Ok(()) } else { - -EINVAL + Err(IoError::EINVAL) } } } @@ -243,7 +231,7 @@ impl Drop for Socket { } impl ObjectInterface for Socket { - fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { + fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> Result<(), IoError> { if namelen == size_of::().try_into().unwrap() { let addr = unsafe { *(name as *const sockaddr_in) }; let s_addr = addr.sin_addr.s_addr; @@ -263,13 +251,13 @@ impl ObjectInterface for Socket { } }); - 0 + Ok(()) } else { - -EINVAL + Err(IoError::EINVAL) } } - fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { + fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> Result { if namelen == size_of::().try_into().unwrap() { let saddr = unsafe { *(name as *const sockaddr_in) }; let port = u16::from_be(saddr.sin_port); @@ -282,29 +270,28 @@ impl ObjectInterface for Socket { self.endpoint.store(Some(IpEndpoint::new(address, port))); - 0 + Ok(0) } else { - -EINVAL + Err(IoError::EINVAL) } } - fn read(&self, buf: *mut u8, len: usize) -> isize { - self.read(buf, len) + fn read(&self, buf: &mut [u8]) -> Result { + self.read(buf) } - fn write(&self, buf: *const u8, len: usize) -> isize { - self.write(buf, len) + fn write(&self, buf: &[u8]) -> Result { + self.write(buf) } fn sendto( &self, - buf: *const u8, - len: usize, + buf: &[u8], addr: *const sockaddr, addr_len: socklen_t, - ) -> isize { + ) -> Result { if addr.is_null() || addr_len == 0 { - self.write(buf, len) + self.write(buf) } else { if addr_len >= size_of::().try_into().unwrap() { let addr = unsafe { &*(addr as *const sockaddr_in) }; @@ -312,58 +299,46 @@ impl ObjectInterface for Socket { let endpoint = IpEndpoint::new(ip, u16::from_be(addr.sin_port)); self.endpoint.store(Some(endpoint)); let meta = UdpMetadata::from(endpoint); - let slice = unsafe { core::slice::from_raw_parts(buf, len) }; if self.nonblocking.load(Ordering::Acquire) { - poll_on(self.async_write(slice, &meta), Some(Duration::ZERO)).unwrap_or_else( - |x| { - if x == -ETIME { - (-EAGAIN).try_into().unwrap() - } else { - x.try_into().unwrap() - } - }, - ) + poll_on(self.async_write(buf, &meta), Some(Duration::ZERO)).map_err(|x| { + if x == IoError::ETIME { + IoError::EAGAIN + } else { + x + } + }) } else { - poll_on(self.async_write(slice, &meta), None) - .unwrap_or_else(|x| x.try_into().unwrap()) + poll_on(self.async_write(buf, &meta), None) } } else { - (-EINVAL).try_into().unwrap() + Err(IoError::EINVAL) } } } fn recvfrom( &self, - buf: *mut u8, - len: usize, + buf: &mut [u8], address: *mut sockaddr, address_len: *mut socklen_t, - ) -> isize { + ) -> Result { if !address_len.is_null() { let len = unsafe { &mut *address_len }; if *len < size_of::().try_into().unwrap() { - return (-EINVAL).try_into().unwrap(); + return Err(IoError::EINVAL); } } - if len == 0 { - return (-EINVAL).try_into().unwrap(); + if buf.len() == 0 { + return Err(IoError::EINVAL); } - let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; - if self.nonblocking.load(Ordering::Acquire) { - poll_on(self.async_recvfrom(slice), Some(Duration::ZERO)).map_or_else( - |x| { - if x == -ETIME { - (-EAGAIN).try_into().unwrap() - } else { - x.try_into().unwrap() - } - }, - |(x, meta)| { + match poll_on(self.async_recvfrom(buf), Some(Duration::ZERO)) { + Err(IoError::ETIME) => Err(IoError::EAGAIN), + Err(e) => Err(e), + Ok((x, meta)) => { let len = unsafe { &mut *address_len }; if address.is_null() { *len = 0; @@ -375,35 +350,27 @@ impl ObjectInterface for Socket { } *len = size_of::().try_into().unwrap(); } - x.try_into().unwrap() - }, - ) + Ok(x) + } + } } else { - poll_on(self.async_recvfrom(slice), Some(Duration::from_secs(2))).map_or_else( - |x| { - if x == -ETIME { - block_on(self.async_recvfrom(slice), None).map_or_else( - |x| x.try_into().unwrap(), - |(x, meta)| { - let len = unsafe { &mut *address_len }; - if address.is_null() { - *len = 0; - } else { - let addr = unsafe { &mut *(address as *mut sockaddr_in) }; - addr.sin_port = meta.endpoint.port.to_be(); - if let IpAddress::Ipv4(ip) = meta.endpoint.addr { - addr.sin_addr.s_addr.copy_from_slice(ip.as_bytes()); - } - *len = size_of::().try_into().unwrap(); - } - x.try_into().unwrap() - }, - ) + match poll_on(self.async_recvfrom(buf), Some(Duration::from_secs(2))) { + Err(IoError::ETIME) => block_on(self.async_recvfrom(buf), None).map(|(x, meta)| { + let len = unsafe { &mut *address_len }; + if address.is_null() { + *len = 0; } else { - x.try_into().unwrap() + let addr = unsafe { &mut *(address as *mut sockaddr_in) }; + addr.sin_port = meta.endpoint.port.to_be(); + if let IpAddress::Ipv4(ip) = meta.endpoint.addr { + addr.sin_addr.s_addr.copy_from_slice(ip.as_bytes()); + } + *len = size_of::().try_into().unwrap(); } - }, - |(x, meta)| { + x + }), + Err(e) => Err(e), + Ok((x, meta)) => { let len = unsafe { &mut *address_len }; if address.is_null() { *len = 0; @@ -415,15 +382,15 @@ impl ObjectInterface for Socket { } *len = size_of::().try_into().unwrap(); } - x.try_into().unwrap() - }, - ) + Ok(x) + } + } } } } impl ObjectInterface for Socket { - fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { + fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> Result<(), IoError> { if namelen == size_of::().try_into().unwrap() { let addr = unsafe { *(name as *const sockaddr_in6) }; let s6_addr = addr.sin6_addr.s6_addr; @@ -453,13 +420,13 @@ impl ObjectInterface for Socket { } }); - 0 + Ok(()) } else { - -EINVAL + Err(IoError::EINVAL) } } - fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { + fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> Result { if namelen == size_of::().try_into().unwrap() { let saddr = unsafe { *(name as *const sockaddr_in6) }; let s6_addr = saddr.sin6_addr.s6_addr; @@ -477,25 +444,24 @@ impl ObjectInterface for Socket { self.endpoint.store(Some(IpEndpoint::new(address, port))); - 0 + Ok(0) } else { - -EINVAL + Err(IoError::EINVAL) } } - fn read(&self, buf: *mut u8, len: usize) -> isize { - self.read(buf, len) + fn read(&self, buf: &mut [u8]) -> Result { + self.read(buf) } fn sendto( &self, - buf: *const u8, - len: usize, + buf: &[u8], addr: *const sockaddr, addr_len: socklen_t, - ) -> isize { + ) -> Result { if addr.is_null() || addr_len == 0 { - self.write(buf, len) + self.write(buf) } else { if addr_len >= size_of::().try_into().unwrap() { let addr = unsafe { &*(addr as *const sockaddr_in6) }; @@ -503,58 +469,51 @@ impl ObjectInterface for Socket { let endpoint = IpEndpoint::new(ip, u16::from_be(addr.sin6_port)); self.endpoint.store(Some(endpoint)); let meta = UdpMetadata::from(endpoint); - let slice = unsafe { core::slice::from_raw_parts(buf, len) }; if self.nonblocking.load(Ordering::Acquire) { - poll_on(self.async_write(slice, &meta), Some(Duration::ZERO)).unwrap_or_else( - |x| { - if x == -ETIME { - (-EAGAIN).try_into().unwrap() - } else { - x.try_into().unwrap() - } - }, - ) + poll_on(self.async_write(buf, &meta), Some(Duration::ZERO)).map_err(|x| { + if x == IoError::ETIME { + IoError::EAGAIN + } else { + x + } + }) } else { - poll_on(self.async_write(slice, &meta), None) - .unwrap_or_else(|x| x.try_into().unwrap()) + poll_on(self.async_write(buf, &meta), None) } } else { - (-EINVAL).try_into().unwrap() + Err(IoError::EINVAL) } } } fn recvfrom( &self, - buf: *mut u8, - len: usize, + buf: &mut [u8], address: *mut sockaddr, address_len: *mut socklen_t, - ) -> isize { + ) -> Result { if !address_len.is_null() { let len = unsafe { &mut *address_len }; if *len < size_of::().try_into().unwrap() { - return (-EINVAL).try_into().unwrap(); + return Err(IoError::EINVAL); } } - if len == 0 { - return (-EINVAL).try_into().unwrap(); + if buf.len() == 0 { + return Err(IoError::EINVAL); } - let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; - if self.nonblocking.load(Ordering::Acquire) { - poll_on(self.async_recvfrom(slice), Some(Duration::ZERO)).map_or_else( - |x| { - if x == -ETIME { - (-EAGAIN).try_into().unwrap() + poll_on(self.async_recvfrom(buf), Some(Duration::ZERO)) + .map_err(|x| { + if x == IoError::ETIME { + IoError::EAGAIN } else { - x.try_into().unwrap() + x } - }, - |(x, meta)| { + }) + .map(|(x, meta)| { let len = unsafe { &mut *address_len }; if address.is_null() { *len = 0; @@ -566,35 +525,26 @@ impl ObjectInterface for Socket { } *len = size_of::().try_into().unwrap(); } - x.try_into().unwrap() - }, - ) + x + }) } else { - poll_on(self.async_recvfrom(slice), Some(Duration::from_secs(2))).map_or_else( - |x| { - if x == -ETIME { - block_on(self.async_recvfrom(slice), None).map_or_else( - |x| x.try_into().unwrap(), - |(x, meta)| { - let len = unsafe { &mut *address_len }; - if address.is_null() { - *len = 0; - } else { - let addr = unsafe { &mut *(address as *mut sockaddr_in6) }; - addr.sin6_port = meta.endpoint.port.to_be(); - if let IpAddress::Ipv6(ip) = meta.endpoint.addr { - addr.sin6_addr.s6_addr.copy_from_slice(ip.as_bytes()); - } - *len = size_of::().try_into().unwrap(); - } - x.try_into().unwrap() - }, - ) + match poll_on(self.async_recvfrom(buf), Some(Duration::from_secs(2))) { + Err(IoError::ETIME) => block_on(self.async_recvfrom(buf), None).map(|(x, meta)| { + let len = unsafe { &mut *address_len }; + if address.is_null() { + *len = 0; } else { - x.try_into().unwrap() + let addr = unsafe { &mut *(address as *mut sockaddr_in6) }; + addr.sin6_port = meta.endpoint.port.to_be(); + if let IpAddress::Ipv6(ip) = meta.endpoint.addr { + addr.sin6_addr.s6_addr.copy_from_slice(ip.as_bytes()); + } + *len = size_of::().try_into().unwrap(); } - }, - |(x, meta)| { + x + }), + Err(e) => Err(e), + Ok((x, meta)) => { let len = unsafe { &mut *address_len }; if address.is_null() { *len = 0; @@ -606,17 +556,17 @@ impl ObjectInterface for Socket { } *len = size_of::().try_into().unwrap(); } - x.try_into().unwrap() - }, - ) + Ok(x) + } + } } } - fn write(&self, buf: *const u8, len: usize) -> isize { - self.write(buf, len) + fn write(&self, buf: &[u8]) -> Result { + self.write(buf) } - fn ioctl(&self, cmd: i32, argp: *mut c_void) -> i32 { + fn ioctl(&self, cmd: i32, argp: *mut c_void) -> Result<(), IoError> { self.ioctl(cmd, argp) } } diff --git a/src/fd/stdio.rs b/src/fd/stdio.rs index 4c550df882..a8c644bdbe 100644 --- a/src/fd/stdio.rs +++ b/src/fd/stdio.rs @@ -1,9 +1,66 @@ -use core::{isize, slice}; +use core::isize; +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] +use core::ptr; +#[cfg(target_arch = "x86_64")] +use x86::io::*; + +use crate::arch::mm::{paging, VirtAddr}; use crate::console::CONSOLE; -use crate::fd::{ - uhyve_send, ObjectInterface, SysWrite, STDERR_FILENO, STDOUT_FILENO, UHYVE_PORT_WRITE, -}; +use crate::fd::{IoError, ObjectInterface, STDERR_FILENO, STDOUT_FILENO}; + +const UHYVE_PORT_WRITE: u16 = 0x400; + +#[repr(C, packed)] +struct SysWrite { + fd: i32, + buf: *const u8, + len: usize, +} + +impl SysWrite { + pub fn new(fd: i32, buf: *const u8, len: usize) -> SysWrite { + SysWrite { fd, buf, len } + } +} + +/// forward a request to the hypervisor uhyve +#[inline] +#[cfg(target_arch = "x86_64")] +fn uhyve_send(port: u16, data: &mut T) { + let ptr = VirtAddr(ptr::from_mut(data).addr() as u64); + let physical_address = paging::virtual_to_physical(ptr).unwrap(); + + unsafe { + outl(port, physical_address.as_u64() as u32); + } +} + +/// forward a request to the hypervisor uhyve +#[inline] +#[cfg(target_arch = "aarch64")] +fn uhyve_send(port: u16, data: &mut T) { + use core::arch::asm; + + let ptr = VirtAddr(ptr::from_mut(data).addr() as u64); + let physical_address = paging::virtual_to_physical(ptr).unwrap(); + + unsafe { + asm!( + "str x8, [{port}]", + port = in(reg) u64::from(port), + in("x8") physical_address.as_u64(), + options(nostack), + ); + } +} + +/// forward a request to the hypervisor uhyve +#[inline] +#[cfg(target_arch = "riscv64")] +fn uhyve_send(_port: u16, _data: &mut T) { + todo!() +} #[derive(Debug, Clone)] pub struct GenericStdin; @@ -20,14 +77,11 @@ impl GenericStdin { pub struct GenericStdout; impl ObjectInterface for GenericStdout { - fn write(&self, buf: *const u8, len: usize) -> isize { - assert!(len <= isize::MAX as usize); - let buf = unsafe { slice::from_raw_parts(buf, len) }; - + fn write(&self, buf: &[u8]) -> Result { // stdin/err/out all go to console CONSOLE.lock().write_all(buf); - len as isize + Ok(buf.len().try_into().unwrap()) } } @@ -41,14 +95,11 @@ impl GenericStdout { pub struct GenericStderr; impl ObjectInterface for GenericStderr { - fn write(&self, buf: *const u8, len: usize) -> isize { - assert!(len <= isize::MAX as usize); - let buf = unsafe { slice::from_raw_parts(buf, len) }; - + fn write(&self, buf: &[u8]) -> Result { // stdin/err/out all go to console CONSOLE.lock().write_all(buf); - len as isize + Ok(buf.len().try_into().unwrap()) } } @@ -73,11 +124,11 @@ impl UhyveStdin { pub struct UhyveStdout; impl ObjectInterface for UhyveStdout { - fn write(&self, buf: *const u8, len: usize) -> isize { - let mut syswrite = SysWrite::new(STDOUT_FILENO, buf, len); + fn write(&self, buf: &[u8]) -> Result { + let mut syswrite = SysWrite::new(STDOUT_FILENO, buf.as_ptr(), buf.len()); uhyve_send(UHYVE_PORT_WRITE, &mut syswrite); - syswrite.len as isize + Ok(syswrite.len as isize) } } @@ -91,11 +142,11 @@ impl UhyveStdout { pub struct UhyveStderr; impl ObjectInterface for UhyveStderr { - fn write(&self, buf: *const u8, len: usize) -> isize { - let mut syswrite = SysWrite::new(STDERR_FILENO, buf, len); + fn write(&self, buf: &[u8]) -> Result { + let mut syswrite = SysWrite::new(STDERR_FILENO, buf.as_ptr(), buf.len()); uhyve_send(UHYVE_PORT_WRITE, &mut syswrite); - syswrite.len as isize + Ok(syswrite.len as isize) } } diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 2d7205ca0a..07559718a6 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -1,18 +1,22 @@ use alloc::alloc::{alloc, Layout}; +use alloc::borrow::ToOwned; use alloc::boxed::Box; use alloc::string::String; +use alloc::sync::Arc; use alloc::vec::Vec; use core::mem::MaybeUninit; use core::{fmt, u32, u8}; +use hermit_sync::SpinMutex; + +use crate::alloc::string::ToString; #[cfg(not(feature = "pci"))] 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::fs::{ - self, Dirent, FileAttr, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence, -}; +use crate::fd::{DirectoryEntry, IoError}; +use crate::fs::{self, 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 @@ -31,7 +35,7 @@ const FUSE_GETATTR_FH: u32 = 1 << 0; #[repr(C)] #[derive(Debug)] -pub struct fuse_dirent { +struct fuse_dirent { pub d_ino: u64, pub d_off: u64, pub d_namelen: u32, @@ -39,7 +43,7 @@ pub struct fuse_dirent { pub d_name: [u8; 0], } -pub trait FuseInterface { +pub(crate) trait FuseInterface { fn send_command(&mut self, cmd: &Cmd, rsp: &mut Rsp) where S: FuseIn + core::fmt::Debug, @@ -48,466 +52,282 @@ pub trait FuseInterface { fn get_mount_point(&self) -> String; } -pub struct Fuse; +#[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, +} -impl PosixFileSystem for Fuse { - fn open(&self, path: &str, perms: FilePerms) -> Result, FileError> { - let mut file = FuseFile { - fuse_nid: None, - fuse_fh: None, - offset: 0, - }; - // 1.FUSE_INIT to create session - // Already done +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_out_header { + pub len: u32, + pub error: i32, + pub unique: u64, +} - // Differentiate between opening and creating new file, since fuse does not support O_CREAT on open. - if !perms.creat { - // 2.FUSE_LOOKUP(FUSE_ROOT_ID, “foo”) -> nodeid - file.fuse_nid = self.lookup(path); +#[repr(C)] +#[derive(Debug, Default)] +struct fuse_init_in { + pub major: u32, + pub minor: u32, + pub max_readahead: u32, + pub flags: u32, +} - if file.fuse_nid.is_none() { - warn!("Fuse lookup seems to have failed!"); - return Err(FileError::ENOENT); - } +unsafe impl FuseIn for fuse_init_in {} - // 3.FUSE_OPEN(nodeid, O_RDONLY) -> fh - let (cmd, mut rsp) = create_open(file.fuse_nid.unwrap(), perms.raw); - 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 }); - } else { - // Create file (opens implicitly, returns results from both lookup and open calls) - let (cmd, mut rsp) = create_create(path, perms.raw, perms.mode); - get_filesystem_driver() - .ok_or(FileError::ENOSYS)? - .lock() - .send_command(cmd.as_ref(), rsp.as_mut()); +#[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 {} - let inner = unsafe { rsp.rsp.assume_init() }; - file.fuse_nid = Some(inner.entry.nodeid); - file.fuse_fh = Some(inner.open.fh); - } +#[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, +} - Ok(Box::new(file)) - } +unsafe impl FuseIn for fuse_read_in {} - fn opendir(&self, path: &str) -> Result, FileError> { - debug!("FUSE opendir: {}", path); +#[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 {} - let mut readdir = FuseDir { - fuse_nid: None, - fuse_fh: None, - offset: 0, - response: None, - buffer_offset: 0, - }; +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_write_out { + pub size: u32, + pub padding: u32, +} +unsafe impl FuseOut for fuse_write_out {} - // Lookup nodeid +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_read_out {} +unsafe impl FuseOut for fuse_read_out {} - readdir.fuse_nid = self.lookup(path); +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_lookup_in {} +unsafe impl FuseIn for fuse_lookup_in {} - if readdir.fuse_nid.is_none() { - warn!("Fuse lookup seems to have failed!"); - return Err(FileError::ENOENT); - } +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_readlink_in {} - // 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() - .ok_or(FileError::ENOSYS)? - .lock() - .send_command(cmd.as_ref(), rsp.as_mut()); - readdir.fuse_fh = Some(unsafe { rsp.rsp.assume_init().fh }); +unsafe impl FuseIn for fuse_readlink_in {} - Ok(Box::new(readdir)) - } +#[repr(C)] +#[derive(Default, Debug)] +pub struct fuse_readlink_out {} +unsafe impl FuseOut for fuse_readlink_out {} - fn unlink(&self, path: &str) -> core::result::Result<(), FileError> { - let (cmd, mut rsp) = create_unlink(path); - get_filesystem_driver() - .ok_or(FileError::ENOSYS)? - .lock() - .send_command(cmd.as_ref(), rsp.as_mut()); - trace!("unlink answer {:?}", rsp); +#[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, +} - Ok(()) - } +unsafe impl FuseOut for fuse_attr_out {} - 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); +#[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, +} - Ok(()) - } +unsafe impl FuseOut for fuse_entry_out {} - fn mkdir(&self, name: &str, mode: u32) -> Result { - let (cmd, mut rsp) = create_mkdir(name, mode); +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_attr { + pub ino: u64, + pub size: u64, + pub blocks: u64, + pub atime: u64, + pub mtime: u64, + pub ctime: u64, + pub atimensec: u32, + pub mtimensec: u32, + pub ctimensec: u32, + pub mode: u32, + pub nlink: u32, + pub uid: u32, + pub gid: u32, + pub rdev: u32, + pub blksize: u32, + pub padding: u32, +} - 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_attr { + fn to_stat(self) -> FileAttr { + FileAttr { + st_ino: self.ino, + st_nlink: self.nlink as u64, + st_mode: self.mode, + st_uid: self.uid, + st_gid: self.gid, + st_rdev: self.rdev as u64, + st_size: self.size.try_into().unwrap(), + st_blksize: self.blksize as i64, + st_blocks: self.blocks.try_into().unwrap(), + st_atime: self.atime.try_into().unwrap(), + st_atime_nsec: self.atimensec as i64, + st_mtime: self.mtime.try_into().unwrap(), + st_mtime_nsec: self.atimensec as i64, + st_ctime: self.ctime.try_into().unwrap(), + st_ctime_nsec: self.ctimensec as i64, + ..Default::default() + } } +} - 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()); +#[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 {} - if rsp.header.error != 0 { - // TODO: Correct error handling - return Err(FileError::EIO); - } +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_create_out { + pub entry: fuse_entry_out, + pub open: fuse_open_out, +} - let rsp = unsafe { rsp.rsp.assume_init() }; - let attr = rsp.attr; +unsafe impl FuseOut for fuse_create_out {} - if attr.mode & S_IFMT != S_IFLNK { - unsafe { - attr.fill_stat(stat); - } - Ok(()) - } else { - self.stat(&self.readlink(rsp.nodeid)?, stat) - } - } +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_open_in { + pub flags: u32, + pub unused: u32, +} - 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()); +unsafe impl FuseIn for fuse_open_in {} - let attr = unsafe { rsp.rsp.assume_init().attr }; +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_open_out { + pub fh: u64, + pub open_flags: u32, + pub padding: u32, +} - unsafe { - attr.fill_stat(stat); - } +unsafe impl FuseOut for fuse_open_out {} - Ok(()) - } +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_release_in { + pub fh: u64, + pub flags: u32, + pub release_flags: u32, + pub lock_owner: u64, } -impl Fuse { - pub fn new() -> Self { - Self {} - } +unsafe impl FuseIn for fuse_release_in {} - pub fn send_init(&self) { - let (cmd, mut rsp) = create_init(); - get_filesystem_driver() - .unwrap() - .lock() - .send_command(cmd.as_ref(), rsp.as_mut()); - trace!("fuse init answer: {:?}", rsp); - } +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_release_out {} +unsafe impl FuseOut for fuse_release_out {} - pub fn lookup(&self, name: &str) -> Option { - let (cmd, mut rsp) = create_lookup(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 }) - } else { - None - } - } - - pub fn readlink(&self, nid: u64) -> Result { - let 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 { - fn default() -> Self { - Self::new() - } -} +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_rmdir_in {} +unsafe impl FuseIn for fuse_rmdir_in {} -struct FuseFile { - fuse_nid: Option, - fuse_fh: Option, - offset: usize, -} +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_rmdir_out {} +unsafe impl FuseOut for fuse_rmdir_out {} -struct FuseDir { - fuse_nid: Option, - fuse_fh: Option, - offset: u64, - response: Option>>, - buffer_offset: u32, +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_mkdir_in { + pub mode: u32, + pub umask: u32, } +unsafe impl FuseIn for fuse_mkdir_in {} -impl PosixFile for FuseDir { - fn close(&mut self) -> Result<(), FileError> { - 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> { - // 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) - } - - fn readdir(&mut self) -> Result<*const Dirent, FileError> { - // Check if we have to read the directory via FUSE or still have a direntry in the last respnse - 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!("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!("FUSE dir not open, cannot read!"); - return Err(FileError::EBADF); - }; - - let (mut cmd, mut rsp) = create_read(nid, fh, len, self.offset); - 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 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::() - }; - - if len <= core::mem::size_of::() { - debug!("FUSE no new dirs"); - return Ok(core::ptr::null()); - } - - // Keep smart pointer to response - let rsp = option.insert(rsp); - self.buffer_offset = 0; - - debug!("FUSE new buffer len: {}", len); - rsp - } - }; - - let return_ptr: *const u8 = unsafe { - resp.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.d_off; - - self.buffer_offset += core::mem::size_of::() as u32 + dirent.d_namelen; - - // Allign to dirent struct - self.buffer_offset = ((self.buffer_offset) + U64_SIZE - 1) & (!(U64_SIZE - 1)); +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_unlink_in {} +unsafe impl FuseIn for fuse_unlink_in {} - // Check alignment - assert!(return_ptr.is_aligned_to(U64_SIZE.try_into().unwrap())); - Ok(return_ptr.cast()) - } +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_unlink_out {} +unsafe impl FuseOut for fuse_unlink_out {} - fn fstat(&self, _stat: *mut FileAttr) -> Result<(), FileError> { - Err(FileError::ENOSYS) - } +#[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 PosixFile for FuseFile { - fn close(&mut self) -> Result<(), FileError> { - let (cmd, mut rsp) = create_release(self.fuse_nid.unwrap(), self.fuse_fh.unwrap()); - 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> { - let mut len = len; - if len as usize > MAX_READ_LEN { - debug!("Reading longer than max_read_len: {}", len); - len = MAX_READ_LEN as u32; - } - if let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) { - let (cmd, mut rsp) = create_read(nid, fh, len, self.offset as u64); - 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::() - }; - self.offset += len; - - Ok(unsafe { MaybeUninit::slice_assume_init_ref(&rsp.extra_buffer[..len]).to_vec() }) - } else { - warn!("File not open, cannot read!"); - Err(FileError::ENOENT) - } - } - - fn write(&mut self, buf: &[u8]) -> Result { - debug!("FUSE write!"); - let mut len = buf.len(); - if len > MAX_WRITE_LEN { - debug!( - "Writing longer than max_write_len: {} > {}", - buf.len(), - MAX_WRITE_LEN - ); - 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); - 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 rsp_size = unsafe { rsp.rsp.assume_init().size }; - let len: usize = if rsp_size > buf.len().try_into().unwrap() { - buf.len() - } else { - rsp_size.try_into().unwrap() - }; - self.offset += len; - Ok(len.try_into().unwrap()) - } else { - warn!("File not open, cannot read!"); - Err(FileError::ENOENT) - } - } - - fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result { - 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); - 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 rsp_offset = unsafe { rsp.rsp.assume_init().offset }; - - Ok(rsp_offset.try_into().unwrap()) - } else { - Err(FileError::EIO) - } - } - - 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, FUSE_GETATTR_FH); - 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(C)] +#[derive(Default, Debug)] +struct fuse_lseek_out { + offset: u64, } +unsafe impl FuseOut for fuse_lseek_out {} #[repr(u32)] -#[derive(Copy, Clone, Debug)] +#[derive(Debug, Copy, Clone)] #[allow(non_camel_case_types)] #[allow(dead_code)] -pub enum Opcode { +enum Opcode { FUSE_LOOKUP = 1, FUSE_FORGET = 2, // no reply FUSE_GETATTR = 3, @@ -562,14 +382,14 @@ pub enum Opcode { /// Marker trait, which signals that a struct is a valid Fuse command. /// Struct has to be repr(C)! -pub unsafe trait FuseIn {} +pub(crate) unsafe trait FuseIn {} /// Marker trait, which signals that a struct is a valid Fuse response. /// Struct has to be repr(C)! -pub unsafe trait FuseOut {} +pub(crate) unsafe trait FuseOut {} #[repr(C)] #[derive(Debug)] -pub struct Cmd { +pub(crate) struct Cmd { header: fuse_in_header, cmd: T, extra_buffer: [u8], @@ -583,7 +403,7 @@ impl AsSliceU8 for Cmd { #[repr(C)] #[derive(Debug)] -pub struct Rsp { +pub(crate) struct Rsp { header: fuse_out_header, rsp: MaybeUninit, extra_buffer: [MaybeUninit], @@ -660,48 +480,56 @@ fn create_init() -> (Box>, Box>) { (cmd, rsp) } -fn create_getattr( - nid: u64, - fh: u64, +fn create_create( + path: &str, flags: u32, -) -> (Box>, Box>) { - let len = core::mem::size_of::() + core::mem::size_of::(); + 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::(), + 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, + 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; Box::from_raw(raw) }; assert_eq!(layout, Layout::for_value(&*cmd)); - let len = core::mem::size_of::() + core::mem::size_of::(); + 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::(), + 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; + 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() @@ -714,47 +542,43 @@ fn create_getattr( (cmd, rsp) } -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; +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::(), + 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; + 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() + }; Box::from_raw(raw) }; assert_eq!(layout, Layout::for_value(&*cmd)); - let len = core::mem::size_of::() + core::mem::size_of::(); + 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::(), + 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; + 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() @@ -767,46 +591,59 @@ fn create_lookup(name: &str) -> (Box>, Box (Box>, Box>) { - let len = core::mem::size_of::() + core::mem::size_of::(); + 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::(), + 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(); + 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::() - + usize::try_from(size).unwrap(); + 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::(), + 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; + 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() @@ -819,72 +656,6 @@ fn create_readlink( (cmd, rsp) } -#[repr(C)] -#[derive(Debug, Default)] -pub 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, -} - -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_out_header { - pub len: u32, - pub error: i32, - pub unique: u64, -} - -#[repr(C)] -#[derive(Debug, Default)] -pub struct fuse_init_in { - pub major: u32, - pub minor: u32, - pub max_readahead: u32, - pub flags: u32, -} - -unsafe impl FuseIn for fuse_init_in {} - -#[repr(C)] -#[derive(Debug, Default)] -pub 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 {} - -#[repr(C)] -#[derive(Default, Debug)] -pub 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, -} - -unsafe impl FuseIn for fuse_read_in {} - -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_read_out {} -unsafe impl FuseOut for fuse_read_out {} - fn create_read( nid: u64, fh: u64, @@ -944,23 +715,6 @@ fn create_read( (cmd, rsp) } -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_lseek_in { - pub fh: u64, - pub offset: u64, - pub whence: u32, - pub padding: u32, -} -unsafe impl FuseIn for fuse_lseek_in {} - -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_lseek_out { - offset: u64, -} -unsafe impl FuseOut for fuse_lseek_out {} - fn create_lseek( nid: u64, fh: u64, @@ -1023,80 +777,46 @@ fn create_lseek( (cmd, rsp) } -#[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 {} - -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_write_out { - pub size: u32, - pub padding: u32, -} -unsafe impl FuseOut for fuse_write_out {} - -// TODO: do write zerocopy? -fn create_write( +fn create_readlink( nid: u64, - fh: u64, - buf: &[u8], - offset: u64, -) -> (Box>, Box>) { - let len = - core::mem::size_of::() + core::mem::size_of::() + buf.len(); + 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::(), + 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); + 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::(); + 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::(), + 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; + 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() @@ -1109,42 +829,23 @@ fn create_write( (cmd, rsp) } -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_open_in { - pub flags: u32, - pub unused: u32, -} - -unsafe impl FuseIn for fuse_open_in {} - -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_open_out { - pub fh: u64, - pub open_flags: u32, - pub padding: u32, -} - -unsafe impl FuseOut for fuse_open_out {} - -fn create_open(nid: u64, flags: u32) -> (Box>, Box>) { - let len = core::mem::size_of::() + core::mem::size_of::(); +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::(), + 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, + 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() }; @@ -1152,19 +853,19 @@ fn create_open(nid: u64, flags: u32) -> (Box>, Box() + core::mem::size_of::(); + 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::(), + 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; + 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() @@ -1177,59 +878,51 @@ fn create_open(nid: u64, flags: u32) -> (Box>, Box (Box>, Box>) { - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( +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::(), + 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, + 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 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::(), + 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; + 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() @@ -1242,111 +935,6 @@ fn create_release(nid: u64, fh: u64) -> (Box>, Box (Box>, Box>) { let slice = name.as_bytes(); let len = core::mem::size_of::() @@ -1400,48 +988,17 @@ fn create_unlink(name: &str) -> (Box>, Box (Box>, Box>) { - let slice = path.as_bytes(); +fn create_rmdir(name: &str) -> (Box>, Box>) { + let slice = name.as_bytes(); let len = core::mem::size_of::() - + 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::(), + core::mem::align_of::(), ), ) .unwrap() @@ -1449,14 +1006,9 @@ fn create_create( 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); + 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).cmd = fuse_create_in { - flags, - mode, - ..Default::default() - }; (*raw).extra_buffer[..slice.len()].copy_from_slice(slice); (*raw).extra_buffer[slice.len()] = 0; @@ -1464,19 +1016,19 @@ fn create_create( }; assert_eq!(layout, Layout::for_value(&*cmd)); - let len = core::mem::size_of::() + core::mem::size_of::(); + 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::(), + 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; + 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() @@ -1489,17 +1041,17 @@ fn create_create( (cmd, rsp) } -fn create_mkdir(path: &str, mode: u32) -> (Box>, Box>) { - let slice = path.as_bytes(); +fn create_lookup(name: &str) -> (Box>, Box>) { + let slice = name.as_bytes(); let len = core::mem::size_of::() - + 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::(), + core::mem::align_of::(), ), ) .unwrap() @@ -1507,13 +1059,9 @@ fn create_mkdir(path: &str, mode: u32) -> (Box>, Box; - (*raw).header = create_in_header::(FUSE_ROOT_ID, Opcode::FUSE_MKDIR); + 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).cmd = fuse_mkdir_in { - mode, - ..Default::default() - }; (*raw).extra_buffer[..slice.len()].copy_from_slice(slice); (*raw).extra_buffer[slice.len()] = 0; @@ -1546,79 +1094,557 @@ fn create_mkdir(path: &str, mode: u32) -> (Box>, Box (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; +fn lookup(name: &str) -> Option { + let (cmd, mut rsp) = create_lookup(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 }) + } else { + None + } +} - Box::from_raw(raw) +fn readlink(nid: u64) -> Result { + let 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::() }; - 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)); + Ok(String::from_utf8(unsafe { + MaybeUninit::slice_assume_init_ref(&rsp.extra_buffer[..len]).to_vec() + }) + .unwrap()) +} - (cmd, rsp) +#[derive(Debug)] +struct FuseFileHandleInner { + fuse_nid: Option, + fuse_fh: Option, + offset: usize, } -pub fn init() { - if let Some(driver) = get_filesystem_driver() { - // Instantiate global fuse object - let fuse = Box::new(Fuse::new()); - fuse.send_init(); - - 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) +impl FuseFileHandleInner { + pub fn new() -> Self { + Self { + fuse_nid: None, + fuse_fh: None, + offset: 0, + } + } + + fn read(&mut self, buf: &mut [u8]) -> Result { + debug!("FUSE read!"); + let mut len = buf.len(); + if len as usize > MAX_READ_LEN { + debug!("Reading longer than max_read_len: {}", len); + 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); + 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::() + >= len.try_into().unwrap() + { + len.try_into().unwrap() + } else { + rsp.header.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]) + }); + + Ok(len.try_into().unwrap()) + } else { + debug!("File not open, cannot read!"); + Err(IoError::ENOENT) + } + } + + fn write(&mut self, buf: &[u8]) -> Result { + debug!("FUSE write!"); + let mut len = buf.len(); + if len > MAX_WRITE_LEN { + debug!( + "Writing longer than max_write_len: {} > {}", + buf.len(), + MAX_WRITE_LEN + ); + 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); + get_filesystem_driver() + .ok_or(IoError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + + if rsp.header.error < 0 { + return Err(IoError::EIO); + } + + let rsp_size = unsafe { rsp.rsp.assume_init().size }; + let len: usize = if rsp_size > buf.len().try_into().unwrap() { + buf.len() + } else { + rsp_size.try_into().unwrap() + }; + self.offset += len; + Ok(len.try_into().unwrap()) + } else { + warn!("File not open, cannot read!"); + Err(IoError::ENOENT) + } + } + + fn lseek(&mut self, offset: isize, whence: SeekWhence) -> Result { + 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); + get_filesystem_driver() + .ok_or(IoError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + + if rsp.header.error < 0 { + return Err(IoError::EIO); + } + + let rsp_offset = unsafe { rsp.rsp.assume_init().offset }; + + Ok(rsp_offset.try_into().unwrap()) + } else { + Err(IoError::EIO) + } + } +} + +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()); + get_filesystem_driver() + .unwrap() + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + } + } +} + +#[derive(Debug)] +struct FuseFileHandle(pub Arc>); + +impl FuseFileHandle { + pub fn new() -> Self { + Self(Arc::new(SpinMutex::new(FuseFileHandleInner::new()))) + } +} + +impl ObjectInterface for FuseFileHandle { + fn read(&self, buf: &mut [u8]) -> Result { + self.0.lock().read(buf) + } + + fn write(&self, buf: &[u8]) -> Result { + self.0.lock().write(buf) + } + + fn lseek(&self, offset: isize, whence: SeekWhence) -> Result { + self.0.lock().lseek(offset, whence) + } +} + +impl Clone for FuseFileHandle { + fn clone(&self) -> Self { + warn!("FuseFileHandle: clone not tested"); + Self(self.0.clone()) + } +} + +#[derive(Debug)] +struct FuseDirectoryHandleInner { + fuse_nid: Option, + fuse_fh: Option, + offset: u64, + buffer_offset: u32, + response: Option>>, +} + +impl FuseDirectoryHandleInner { + pub fn new() -> Self { + Self { + fuse_nid: None, + fuse_fh: None, + offset: 0, + buffer_offset: 0, + response: None, + } + } + + fn readdir(&mut self) -> DirectoryEntry { + // Check if we have to read the directory via FUSE or still have a direntry in the last respnse + 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!("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!("FUSE dir not open, cannot read!"); + return DirectoryEntry::Invalid(-crate::errno::EBADF); + }; + + let (mut cmd, mut rsp) = create_read(nid, fh, len, self.offset); + 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 DirectoryEntry::Invalid(-crate::errno::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::() + }; + + if len <= core::mem::size_of::() { + debug!("FUSE no new dirs"); + return DirectoryEntry::Valid(core::ptr::null()); + } + + // Keep smart pointer to response + let rsp = option.insert(rsp); + self.buffer_offset = 0; + + debug!("FUSE new buffer len: {}", len); + rsp + } + }; + + let return_ptr: *const u8 = unsafe { + resp.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.d_off; + + self.buffer_offset += core::mem::size_of::() as u32 + dirent.d_namelen; + + // Allign to dirent struct + self.buffer_offset = ((self.buffer_offset) + U64_SIZE - 1) & (!(U64_SIZE - 1)); + + // Check alignment + assert!(return_ptr.is_aligned_to(U64_SIZE.try_into().unwrap())); + + DirectoryEntry::Valid(return_ptr.cast()) + } +} + +impl Drop for FuseDirectoryHandleInner { + fn drop(&mut self) { + if self.fuse_nid.is_some() && self.fuse_fh.is_some() { + 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() + .unwrap() + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + } + } +} + +#[derive(Debug)] +struct FuseDirectoryHandle(pub Arc>); + +impl Clone for FuseDirectoryHandle { + fn clone(&self) -> Self { + warn!("FuseDirectoryHandle: clone not tested"); + Self(self.0.clone()) + } +} + +impl FuseDirectoryHandle { + pub fn new() -> Self { + Self(Arc::new(SpinMutex::new(FuseDirectoryHandleInner::new()))) + } +} + +impl ObjectInterface for FuseDirectoryHandle { + fn readdir(&self) -> DirectoryEntry { + self.0.lock().readdir() + } +} + +#[derive(Debug)] +pub(crate) struct FuseDirectory; + +impl FuseDirectory { + pub const fn new() -> Self { + FuseDirectory {} + } +} + +impl VfsNode for FuseDirectory { + /// Returns the node type + fn get_kind(&self) -> NodeKind { + NodeKind::Directory + } + + fn traverse_opendir( + &self, + components: &mut Vec<&str>, + ) -> Result, IoError> { + let path: String = if components.is_empty() { + "/".to_string() + } else { + components.iter().map(|v| "/".to_owned() + v).collect() + }; + + debug!("FUSE opendir: {}", path); + + let readdir = FuseDirectoryHandle::new(); + + // Lookup nodeid + + let mut readdir_guard = readdir.0.lock(); + readdir_guard.fuse_nid = lookup(&path); + + if readdir_guard.fuse_nid.is_none() { + warn!("Fuse lookup seems to have failed!"); + return Err(IoError::ENOENT); + } + + // Opendir + // Flag 0x10000 for O_DIRECTORY might not be necessary + let (mut cmd, mut rsp) = create_open(readdir_guard.fuse_nid.unwrap(), 0x10000); + cmd.header.opcode = Opcode::FUSE_OPENDIR as u32; + get_filesystem_driver() + .ok_or(IoError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + readdir_guard.fuse_fh = Some(unsafe { rsp.rsp.assume_init().fh }); + + drop(readdir_guard); + + Ok(Arc::new(readdir)) + } + + fn traverse_stat(&self, components: &mut Vec<&str>) -> Result { + let path: String = if components.is_empty() { + "/".to_string() + } else { + components.iter().map(|v| "/".to_owned() + v).collect() + }; + + 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(IoError::EIO); + } + + let rsp = unsafe { rsp.rsp.assume_init() }; + let attr = rsp.attr; + + if attr.mode & S_IFMT != S_IFLNK { + Ok(attr.to_stat()) + } else { + let path = readlink(rsp.nodeid)?; + let mut components: Vec<&str> = path.split("/").collect(); + self.traverse_stat(&mut components) + } + } + + fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { + let path: String = if components.is_empty() { + "/".to_string() + } else { + components.iter().map(|v| "/".to_owned() + v).collect() + }; + + debug!("FUSE lstat: {}", path); + + 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 }; + Ok(attr.to_stat()) + } + + fn traverse_open( + &self, + components: &mut Vec<&str>, + opt: OpenOption, + ) -> Result, IoError> { + let path: String = if components.is_empty() { + "/".to_string() + } else { + components.iter().map(|v| "/".to_owned() + v).collect() + }; + + info!("FUSE open: {}, {:?}", path, opt); + + let file = FuseFileHandle::new(); + + // 1.FUSE_INIT to create session + // Already done + let mut file_guard = file.0.lock(); + + // Differentiate between opening and creating new file, since fuse does not support O_CREAT on open. + if !opt.contains(OpenOption::O_CREAT) { + // 2.FUSE_LOOKUP(FUSE_ROOT_ID, “foo”) -> nodeid + file_guard.fuse_nid = lookup(&path); + + if file_guard.fuse_nid.is_none() { + warn!("Fuse lookup seems to have failed!"); + return Err(IoError::ENOENT); + } + + // 3.FUSE_OPEN(nodeid, O_RDONLY) -> fh + let (cmd, mut rsp) = + create_open(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 }); + } 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(), 0); + get_filesystem_driver() + .ok_or(IoError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + + let inner = unsafe { rsp.rsp.assume_init() }; + file_guard.fuse_nid = Some(inner.entry.nodeid); + file_guard.fuse_fh = Some(inner.open.fh); + } + + drop(file_guard); + + Ok(Arc::new(file)) + } + + fn traverse_unlink(&self, components: &mut Vec<&str>) -> core::result::Result<(), IoError> { + let path: String = if components.is_empty() { + "/".to_string() + } else { + components.iter().map(|v| "/".to_owned() + v).collect() + }; + + let (cmd, mut rsp) = create_unlink(&path); + get_filesystem_driver() + .ok_or(IoError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + trace!("unlink answer {:?}", rsp); + + Ok(()) + } + + fn traverse_rmdir(&self, components: &mut Vec<&str>) -> core::result::Result<(), IoError> { + let path: String = if components.is_empty() { + "/".to_string() + } else { + components.iter().map(|v| "/".to_owned() + v).collect() + }; + + let (cmd, mut rsp) = create_rmdir(&path); + get_filesystem_driver() + .ok_or(IoError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + trace!("rmdir answer {:?}", rsp); + + Ok(()) + } + + fn traverse_mkdir(&self, components: &mut Vec<&str>, mode: u32) -> Result<(), IoError> { + let path: String = if components.is_empty() { + "/".to_string() + } else { + components.iter().map(|v| "/".to_owned() + v).collect() + }; + let (cmd, mut rsp) = create_mkdir(&path, mode); + + get_filesystem_driver() + .ok_or(IoError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + if rsp.header.error == 0 { + Ok(()) + } else { + Err(num::FromPrimitive::from_i32(rsp.header.error).unwrap()) + } + } +} + +pub(crate) fn init() { + debug!("Try to initialize fuse filesystem"); + + if let Some(driver) = get_filesystem_driver() { + let (cmd, mut rsp) = create_init(); + driver.lock().send_command(cmd.as_ref(), rsp.as_mut()); + trace!("fuse init answer: {:?}", rsp); + + let mount_point = format!("/{}", driver.lock().get_mount_point()); + info!("Mounting virtio-fs at {}", mount_point); + fs::FILESYSTEM + .get() + .unwrap() + .mount(mount_point.as_str(), Box::new(FuseDirectory::new())) .expect("Mount failed. Duplicate mount_point?"); } } diff --git a/src/fs/mem.rs b/src/fs/mem.rs new file mode 100644 index 0000000000..e23312e0a9 --- /dev/null +++ b/src/fs/mem.rs @@ -0,0 +1,416 @@ +// Copyright (c) 2019 Stefan Lankes, RWTH Aachen University +// +// Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be +// copied, modified, or distributed except according to those terms. + +//! Implements basic functions to realize a simple in-memory file system + +#![allow(dead_code)] + +use alloc::alloc::{alloc_zeroed, Layout}; +use alloc::boxed::Box; +use alloc::collections::BTreeMap; +use alloc::string::{String, ToString}; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::ops::Deref; +use core::slice; +use core::sync::atomic::{AtomicUsize, Ordering}; + +use hermit_sync::RwSpinLock; + +use crate::fd::{DirectoryEntry, Dirent, IoError, ObjectInterface, OpenOption}; +use crate::fs::{FileAttr, NodeKind, VfsNode}; + +#[derive(Debug)] +struct MemDirectoryInner( + pub Arc< + RwSpinLock>>, + >, + AtomicUsize, +); + +impl MemDirectoryInner { + pub fn new() -> Self { + Self( + Arc::new(RwSpinLock::new(BTreeMap::new())), + AtomicUsize::new(0), + ) + } +} + +impl ObjectInterface for MemDirectoryInner { + fn readdir(&self) -> DirectoryEntry { + let pos = self.1.fetch_add(1, Ordering::SeqCst); + + if pos == 0 { + let name = "."; + let name_len = name.len(); + + let len = core::mem::size_of::() + name_len + 1; + let layout = Layout::from_size_align(len, core::mem::align_of::()) + .unwrap() + .pad_to_align(); + + let raw = unsafe { + let raw = alloc_zeroed(layout) as *mut Dirent; + (*raw).d_namelen = name_len.try_into().unwrap(); + core::ptr::copy_nonoverlapping( + name.as_ptr(), + &mut (*raw).d_name as *mut u8, + name_len, + ); + + raw + }; + + DirectoryEntry::Valid(raw) + } else if pos == 1 { + let name = ".."; + let name_len = name.len(); + + let len = core::mem::size_of::() + name_len + 1; + let layout = Layout::from_size_align(len, core::mem::align_of::()) + .unwrap() + .pad_to_align(); + + let raw = unsafe { + let raw = alloc_zeroed(layout) as *mut Dirent; + (*raw).d_namelen = name_len.try_into().unwrap(); + core::ptr::copy_nonoverlapping( + name.as_ptr(), + &mut (*raw).d_name as *mut u8, + name_len, + ); + + raw + }; + + DirectoryEntry::Valid(raw) + } else { + let keys: Vec<_> = self.0.read().keys().cloned().collect(); + + if keys.len() > pos - 2 { + let name_len = keys[pos - 2].len(); + + let len = core::mem::size_of::() + name_len + 1; + let layout = Layout::from_size_align(len, core::mem::align_of::()) + .unwrap() + .pad_to_align(); + + let raw = unsafe { + let raw = alloc_zeroed(layout) as *mut Dirent; + (*raw).d_namelen = name_len.try_into().unwrap(); + core::ptr::copy_nonoverlapping( + keys[pos - 2].as_ptr(), + &mut (*raw).d_name as *mut u8, + name_len, + ); + + raw + }; + + DirectoryEntry::Valid(raw) + } else { + DirectoryEntry::Valid(core::ptr::null()) + } + } + } +} + +impl Clone for MemDirectoryInner { + fn clone(&self) -> Self { + Self( + self.0.clone(), + AtomicUsize::new(self.1.load(Ordering::Relaxed)), + ) + } +} + +#[derive(Debug)] +pub(crate) struct MemDirectory { + inner: Arc, +} + +impl MemDirectory { + pub fn new() -> Self { + Self { + inner: Arc::new(MemDirectoryInner::new()), + } + } + + pub fn create_file(&self, name: &str, ptr: *const u8, length: usize) -> Result<(), IoError> { + let name = name.trim(); + if name.find("/").is_none() { + let file = unsafe { MemFile::from_raw_parts(ptr, length) }; + self.inner + .0 + .write() + .insert(name.to_string(), Box::new(file)); + Ok(()) + } else { + Err(IoError::EBADF) + } + } +} + +impl VfsNode for MemDirectory { + /// Returns the node type + fn get_kind(&self) -> NodeKind { + NodeKind::Directory + } + + fn traverse_mkdir(&self, components: &mut Vec<&str>, mode: u32) -> Result<(), IoError> { + if let Some(component) = components.pop() { + let node_name = String::from(component); + + if let Some(directory) = self.inner.0.read().get(&node_name) { + return directory.traverse_mkdir(components, mode); + } + + if components.is_empty() { + self.inner + .0 + .write() + .insert(node_name, Box::new(MemDirectory::new())); + return Ok(()); + } + } + + Err(IoError::EBADF) + } + + fn traverse_rmdir(&self, components: &mut Vec<&str>) -> Result<(), IoError> { + if let Some(component) = components.pop() { + let node_name = String::from(component); + + if let Some(directory) = self.inner.0.read().get(&node_name) { + return directory.traverse_rmdir(components); + } + + if components.is_empty() { + let mut guard = self.inner.0.write(); + + let obj = guard.remove(&node_name).ok_or(IoError::ENOENT)?; + if obj.get_kind() == NodeKind::Directory { + return Ok(()); + } else { + guard.insert(node_name, obj); + return Err(IoError::ENOTDIR); + } + } + } + + Err(IoError::EBADF) + } + + fn traverse_unlink(&self, components: &mut Vec<&str>) -> Result<(), IoError> { + if let Some(component) = components.pop() { + let node_name = String::from(component); + + if let Some(directory) = self.inner.0.read().get(&node_name) { + return directory.traverse_unlink(components); + } + + if components.is_empty() { + let mut guard = self.inner.0.write(); + + let obj = guard.remove(&node_name).ok_or(IoError::ENOENT)?; + if obj.get_kind() == NodeKind::Directory { + guard.insert(node_name, obj); + return Err(IoError::EISDIR); + } else { + return Ok(()); + } + } + } + + Err(IoError::EBADF) + } + + fn traverse_opendir( + &self, + components: &mut Vec<&str>, + ) -> Result, IoError> { + if let Some(component) = components.pop() { + let node_name = String::from(component); + + if let Some(directory) = self.inner.0.read().get(&node_name) { + directory.traverse_opendir(components) + } else { + Err(IoError::EBADF) + } + } else { + Ok(self.inner.clone()) + } + } + + fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { + if let Some(component) = components.pop() { + let node_name = String::from(component); + + if let Some(directory) = self.inner.0.read().get(&node_name) { + directory.traverse_lstat(components) + } else { + Err(IoError::EBADF) + } + } else { + Err(IoError::ENOSYS) + } + } + + fn traverse_stat(&self, components: &mut Vec<&str>) -> Result { + if let Some(component) = components.pop() { + let node_name = String::from(component); + + if let Some(directory) = self.inner.0.read().get(&node_name) { + directory.traverse_stat(components) + } else { + Err(IoError::EBADF) + } + } else { + Err(IoError::ENOSYS) + } + } + + fn traverse_mount( + &self, + components: &mut Vec<&str>, + obj: Box, + ) -> Result<(), IoError> { + if let Some(component) = components.pop() { + let node_name = String::from(component); + + if let Some(directory) = self.inner.0.read().get(&node_name) { + return directory.traverse_mount(components, obj); + } + + if components.is_empty() { + self.inner.0.write().insert(node_name, obj); + return Ok(()); + } + } + + Err(IoError::EBADF) + } + + fn traverse_open( + &self, + components: &mut Vec<&str>, + opt: OpenOption, + ) -> Result, IoError> { + if let Some(component) = components.pop() { + let node_name = String::from(component); + + if let Some(directory) = self.inner.0.read().get(&node_name) { + return directory.traverse_open(components, opt); + } + + /*if components.is_empty() { + self.children.insert(node_name, obj); + return Ok(()); + }*/ + } + + Err(IoError::EBADF) + } +} + +#[derive(Debug)] +pub struct RomHandle { + /// Position within the file + pos: AtomicUsize, + /// File content + data: Arc>, +} + +impl RomHandle { + pub unsafe fn new(addr: *const u8, len: usize) -> Self { + RomHandle { + pos: AtomicUsize::new(0), + data: Arc::new(RwSpinLock::new(unsafe { slice::from_raw_parts(addr, len) })), + } + } + + pub fn len(&self) -> usize { + let guard = self.data.read(); + guard.len() as usize + } +} + +impl Clone for RomHandle { + fn clone(&self) -> Self { + RomHandle { + pos: AtomicUsize::new(self.pos.load(Ordering::Relaxed)), + data: self.data.clone(), + } + } +} + +#[derive(Debug)] +pub struct RamHandle { + /// Position within the file + pos: AtomicUsize, + /// File content + data: Arc>>, +} + +impl RamHandle { + pub fn new() -> Self { + RamHandle { + pos: AtomicUsize::new(0), + data: Arc::new(RwSpinLock::new(Vec::new())), + } + } + + pub fn len(&self) -> usize { + let guard = self.data.read(); + let ref vec: &Vec = guard.deref(); + vec.len() as usize + } +} + +impl Clone for RamHandle { + fn clone(&self) -> Self { + RamHandle { + pos: AtomicUsize::new(self.pos.load(Ordering::Relaxed)), + data: self.data.clone(), + } + } +} + +/// Enumeration of possible methods to seek within an I/O object. +#[derive(Debug, Clone)] +enum DataHandle { + RAM(RamHandle), + ROM(RomHandle), +} + +#[derive(Debug)] +pub(crate) struct MemFile { + /// File content + data: DataHandle, +} + +impl MemFile { + pub fn new() -> Self { + Self { + data: DataHandle::RAM(RamHandle::new()), + } + } + + pub unsafe fn from_raw_parts(ptr: *const u8, length: usize) -> Self { + Self { + data: unsafe { DataHandle::ROM(RomHandle::new(ptr, length)) }, + } + } +} + +impl VfsNode for MemFile { + /// Returns the node type + fn get_kind(&self) -> NodeKind { + NodeKind::File + } +} diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 6b112fc1cb..cd5c04cb57 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -1,286 +1,208 @@ +#[cfg(all(feature = "fuse", feature = "pci"))] +pub(crate) mod fuse; +mod mem; +mod uhyve; + use alloc::boxed::Box; -use alloc::collections::BTreeMap; use alloc::string::String; +use alloc::sync::Arc; use alloc::vec::Vec; -use core::ops::Deref; -use hermit_sync::TicketMutex; - -#[cfg(all(feature = "fuse", feature = "pci"))] -pub(crate) mod fuse; +use hermit_sync::OnceCell; +use mem::MemDirectory; -// TODO: lazy static could be replaced with explicit init on OS boot. -pub static FILESYSTEM: TicketMutex = TicketMutex::new(Filesystem::new()); +use crate::fd::{IoError, ObjectInterface, OpenOption}; -pub struct Filesystem { - // Keep track of mount-points - mounts: BTreeMap>, +pub(crate) static FILESYSTEM: OnceCell = OnceCell::new(); - // Keep track of open files - files: BTreeMap>, +/// Type of the VNode +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum NodeKind { + /// Node represent a file + File, + /// Node represent a directory + Directory, } -impl Filesystem { - pub const fn new() -> Self { - Self { - mounts: BTreeMap::new(), - files: BTreeMap::new(), - } - } +/// VfsNode represents an internal node of the ramdisk. +pub(crate) trait VfsNode: core::fmt::Debug { + /// Determines the current node type + fn get_kind(&self) -> NodeKind; - /// Returns next free file-descriptor. We map index in files BTreeMap as fd's. - /// Done determining the current biggest stored index. - /// This is efficient, since BTreeMap's iter() calculates min and max key directly. - /// see - fn assign_new_fd(&self) -> u64 { - // BTreeMap has efficient max/min index calculation. One way to access these is the following iter. - // Add 1 to get next never-assigned fd num - if let Some((fd, _)) = self.files.iter().next_back() { - fd + 1 - } else { - 3 // start at 3, to reserve stdin/out/err - } + /// Helper function to create a new dirctory node + fn traverse_mkdir(&self, _components: &mut Vec<&str>, _mode: u32) -> Result<(), IoError> { + Err(IoError::ENOSYS) } - /// Gets a new fd for a file and inserts it into open files. - /// Returns file descriptor - fn add_file(&mut self, file: Box) -> u64 { - let fd = self.assign_new_fd(); - self.files.insert(fd, file); - fd + /// Helper function to delete a dirctory node + fn traverse_rmdir(&self, _components: &mut Vec<&str>) -> core::result::Result<(), IoError> { + Err(IoError::ENOSYS) } - /// parses path `/MOUNTPOINT/internal-path` into mount-filesystem and internal_path - /// Returns (PosixFileSystem, internal_path) or Error on failure. - fn parse_path<'a, 'b>( - &'a self, - path: &'b str, - ) -> Result<(&'a (dyn PosixFileSystem + Send), &'b str), FileError> { - let mut pathsplit = path.splitn(3, '/'); - - if path.starts_with('/') { - pathsplit.next(); // empty, since first char is / - - let mount = pathsplit.next().unwrap(); - let internal_path = pathsplit.next().unwrap_or("/"); - if let Some(fs) = self.mounts.get(mount) { - return Ok((fs.deref(), internal_path)); - } - - warn!( - "Trying to open file on non-existing mount point '{}'!", - mount - ); - } else { - let mount = if !is_uhyve() { - option_env!("HERMIT_WD").unwrap_or("root") - } else { - "." - }; - let internal_path = pathsplit.next().unwrap_or("/"); - - debug!( - "Assume that the directory '{}' is used as mount point!", - mount - ); + /// Helper function to remove the specified file + fn traverse_unlink(&self, _components: &mut Vec<&str>) -> Result<(), IoError> { + Err(IoError::ENOSYS) + } - if let Some(fs) = self.mounts.get(mount) { - return Ok((fs.deref(), internal_path)); - } + /// Helper function to open a directory + fn traverse_opendir( + &self, + _components: &mut Vec<&str>, + ) -> Result, IoError> { + Err(IoError::ENOSYS) + } - warn!( - "Trying to open file on non-existing mount point '{}'!", - mount - ); - } + /// Helper function to get file status + fn traverse_lstat(&self, _components: &mut Vec<&str>) -> Result { + Err(IoError::ENOSYS) + } - Err(FileError::ENOENT) + /// Helper function to get file status + fn traverse_stat(&self, _components: &mut Vec<&str>) -> Result { + Err(IoError::ENOSYS) } - /// Tries to open file at given path (/MOUNTPOINT/internal-path). - /// Looks up MOUNTPOINT in mounted dirs, passes internal-path to filesystem backend - /// Returns the file descriptor of the newly opened file, or an error on failure - pub fn open(&mut self, path: &str, perms: FilePerms) -> Result { - debug!("Opening file {} {:?}", path, perms); - let (fs, internal_path) = self.parse_path(path)?; - let file = fs.open(internal_path, perms)?; - Ok(self.add_file(file)) + /// Helper function to mount a file system + fn traverse_mount( + &self, + _components: &mut Vec<&str>, + _obj: Box, + ) -> Result<(), IoError> { + Err(IoError::ENOSYS) } - /// Similar to open - #[allow(dead_code)] - 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)) + /// Helper function to open a file + fn traverse_open( + &self, + _components: &mut Vec<&str>, + _option: OpenOption, + ) -> Result, IoError> { + Err(IoError::ENOSYS) } +} + +#[derive(Debug)] +pub(crate) struct Filesystem { + root: MemDirectory, +} - /// Closes a file with given fd. - /// If the file is currently open, closes it - /// Remove the file from map of open files - pub fn close(&mut self, fd: u64) { - debug!("Closing fd {}", fd); - if let Some(file) = self.files.get_mut(&fd) { - file.close().unwrap(); // TODO: handle error +impl Filesystem { + pub fn new() -> Self { + Self { + root: MemDirectory::new(), } - self.files.remove(&fd); + } + + /// Tries to open file at given path. + pub fn open(&self, path: &str, opt: OpenOption) -> Result, IoError> { + info!("Open file {}", path); + let mut components: Vec<&str> = path.split("/").collect(); + + components.reverse(); + components.pop(); + + self.root.traverse_open(&mut components, opt) } /// Unlinks a file given by path - pub fn unlink(&mut self, path: &str) -> Result<(), FileError> { + pub fn unlink(&self, path: &str) -> Result<(), IoError> { debug!("Unlinking file {}", path); - let (fs, internal_path) = self.parse_path(path)?; - fs.unlink(internal_path)?; - Ok(()) + let mut components: Vec<&str> = path.split("/").collect(); + + components.reverse(); + components.pop(); + + self.root.traverse_unlink(&mut components) } /// Remove directory given by path - #[allow(dead_code)] - pub fn rmdir(&mut self, path: &str) -> Result<(), FileError> { + pub fn rmdir(&self, path: &str) -> Result<(), IoError> { debug!("Removing directory {}", path); - let (fs, internal_path) = self.parse_path(path)?; - fs.rmdir(internal_path)?; - Ok(()) + let mut components: Vec<&str> = path.split("/").collect(); + + components.reverse(); + components.pop(); + + self.root.traverse_rmdir(&mut components) } /// Create directory given by path - #[allow(dead_code)] - 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(()) + pub fn mkdir(&self, path: &str, mode: u32) -> Result<(), IoError> { + debug!("Create directory {}", path); + let mut components: Vec<&str> = path.split("/").collect(); + + components.reverse(); + components.pop(); + + self.root.traverse_mkdir(&mut components, mode) + } + + /// List given directory + pub fn opendir(&self, path: &str) -> Result, IoError> { + if path.trim() == "/" { + let mut components: Vec<&str> = Vec::new(); + self.root.traverse_opendir(&mut components) + } else { + let mut components: Vec<&str> = path.split("/").collect(); + + components.reverse(); + components.pop(); + + self.root.traverse_opendir(&mut components) + } } /// stat - #[allow(dead_code)] - pub fn stat(&mut self, path: &str, stat: *mut FileAttr) -> Result<(), FileError> { + pub fn stat(&self, path: &str) -> Result { debug!("Getting stats {}", path); - let (fs, internal_path) = self.parse_path(path)?; - fs.stat(internal_path, stat)?; - Ok(()) + + let mut components: Vec<&str> = path.split("/").collect(); + components.reverse(); + components.pop(); + + self.root.traverse_stat(&mut components) } /// lstat - #[allow(dead_code)] - pub fn lstat(&mut self, path: &str, stat: *mut FileAttr) -> Result<(), FileError> { + pub fn lstat(&self, path: &str) -> Result { debug!("Getting lstats {}", path); - let (fs, internal_path) = self.parse_path(path)?; - fs.lstat(internal_path, stat)?; - Ok(()) + + let mut components: Vec<&str> = path.split("/").collect(); + components.reverse(); + components.pop(); + + self.root.traverse_lstat(&mut components) } /// Create new backing-fs at mountpoint mntpath pub fn mount( - &mut self, - mntpath: &str, - mntobj: Box, - ) -> Result<(), ()> { - use alloc::string::ToString; - - debug!("Mounting {}", mntpath); - if mntpath.contains('/') { - warn!( - "Trying to mount at '{}', but slashes in name are not supported!", - mntpath - ); - return Err(()); - } + &self, + path: &str, + obj: Box, + ) -> Result<(), IoError> { + debug!("Mounting {}", path); - // if mounts contains path already abort - if self.mounts.contains_key(mntpath) { - warn!("Mountpoint already exists!"); - return Err(()); - } + let mut components: Vec<&str> = path.split("/").collect(); - // insert filesystem into mounts, done - self.mounts.insert(mntpath.to_string(), mntobj); + components.reverse(); + components.pop(); - Ok(()) + self.root.traverse_mount(&mut components, obj) } - /// Run closure on file referenced by file descriptor. - pub fn fd_op(&mut self, fd: u64, f: impl FnOnce(&mut Box)) { - f(self.files.get_mut(&fd).unwrap()); + /// Create file from ROM + pub unsafe fn create_file( + &self, + name: &str, + ptr: *const u8, + length: usize, + ) -> Result<(), IoError> { + self.root.create_file(name, ptr, length) } } -// TODO: Integrate with src/errno.rs ? -#[allow(clippy::upper_case_acronyms)] -#[derive(Debug, FromPrimitive, ToPrimitive)] -pub enum FileError { - ENOENT = errno::ENOENT as isize, - ENOSYS = errno::ENOSYS as isize, - EIO = errno::EIO as isize, - EBADF = errno::EBADF as isize, - EISDIR = errno::EISDIR as isize, -} - -/// Design: -/// - want to support different backends. One of them virtiofs. -/// - want to support multiple mounted filesystems at once. -/// - for simplicity: no overlays. All 'folders' in / are mountpoints! -/// - manage all files in a global map. Do not hand out references, let syscalls operate by passing in closures (fd_op()) -/// -/// - we internally treat all file systems as posix filesystems. -/// - Have two traits. One representing a filesystem, another a file: PosixFileSystem and PosixFile -/// - filesystem.open creates new file -/// - trait methods like open return Result<....>, so we can catch errors on eg open() and NOT permanently assign an fd to it! -/// -/// - have a FUSE filesystem, which implements both PosixFileSystem and PosixFile -/// - fuse can have various FuseInterface backends. These only have to provide fuse command send/receive capabilities. -/// - virtiofs implements FuseInterface and sends commands via virtio queues. -/// -/// - fd management is only relevant for "user" facing code. We don't care how fuse etc. manages nodes internally. -/// - But we still want to have a list of open files and mounted filesystems (here in fs.rs). -/// -/// Open Questions: -/// - what is the maximum number of open files I want to support? if small, could have static allocation, no need for hashmap? -/// - create Stdin/out virtual files, assign fd's 0-2. Instantiate them on program start. currently fd 0-2 are hardcoded exceptions. -/// - optimize callchain? how does LTO work here?: -/// - app calls rust.open (which is stdlib hermit/fs.rs) [https://github.com/rust-lang/rust/blob/master/src/libstd/sys/hermit/fs.rs#L267] -/// - abi::open() (hermit-sys crate) -/// - [KERNEL BORDER] (uses C-interface. needed? Could just be alternative to native rust?) -/// - hermit-lib/....rs/sys_open() -/// - SyscallInterface.open (via &'static dyn ref) -/// - Filesystem::open() -/// - Fuse::open() -/// - VirtiofsDriver::send_command(...) -/// - [HYPERVISOR BORDER] (via virtio) -/// - virtiofsd receives fuse command and sends reply -/// -/// TODO: -/// - FileDescriptor newtype -use crate::env::is_uhyve; -use crate::errno; -#[cfg(feature = "fuse")] -pub use crate::fs::fuse::fuse_dirent as Dirent; -pub struct Dirent; - -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>; - 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 { - fn close(&mut self) -> Result<(), FileError>; - 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<*const Dirent, FileError>; - fn fstat(&self, stat: *mut FileAttr) -> Result<(), FileError>; -} - #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone)] pub struct FileAttr { pub st_dev: u64, pub st_ino: u64, @@ -301,7 +223,7 @@ pub struct FileAttr { } #[derive(Debug, FromPrimitive, ToPrimitive)] -pub enum PosixFileType { +pub enum FileType { Unknown = 0, // DT_UNKNOWN Fifo = 1, // DT_FIFO CharacterDevice = 2, // DT_CHR @@ -313,19 +235,6 @@ pub enum PosixFileType { Whiteout = 14, // DT_WHT } -// TODO: raw is partially redundant, create nicer interface -#[derive(Clone, Copy, Debug, Default)] -pub struct FilePerms { - pub write: bool, - pub creat: bool, - pub excl: bool, - pub trunc: bool, - pub append: bool, - pub directio: bool, - pub raw: u32, - pub mode: u32, -} - #[derive(Debug, FromPrimitive, ToPrimitive)] pub enum SeekWhence { Set = 0, @@ -336,6 +245,79 @@ pub enum SeekWhence { } pub(crate) fn init() { + FILESYSTEM.set(Filesystem::new()).unwrap(); + FILESYSTEM + .get() + .unwrap() + .mkdir("/tmp", 777) + .expect("Unable to create /tmp"); + #[cfg(all(feature = "fuse", feature = "pci"))] fuse::init(); + uhyve::init(); + + /*let fd = crate::sys_opendir("/\0".as_ptr()); + info!("fd {}", fd); + loop { + if let crate::fd::DirectoryEntry::Valid(dirent) = crate::sys_readdir(fd) { + if dirent == core::ptr::null() { + break; + } + + let s = unsafe { + String::from_raw_parts( + &(*dirent).d_name as *const _ as *mut u8, + (*dirent).d_namelen as usize, + (*dirent).d_namelen as usize, + ) + }; + info!("dirent.d_name {:?}", s); + core::mem::forget(s); + } + } + + let fd = crate::sys_opendir("/root\0".as_ptr()); + info!("fd {}", fd); + loop { + if let crate::fd::DirectoryEntry::Valid(dirent) = crate::sys_readdir(fd) { + if dirent == core::ptr::null() { + break; + } + + let s = unsafe { + String::from_raw_parts( + &(*dirent).d_name as *const _ as *mut u8, + (*dirent).d_namelen as usize, + (*dirent).d_namelen as usize, + ) + }; + info!("dirent.d_name {:?}", s); + core::mem::forget(s); + } + } + + let mut attr: FileAttr = Default::default(); + let ret = crate::sys_lstat("/root/hello.txt\0".as_ptr(), &mut attr as *mut FileAttr); + info!("ret {} {:?}", ret, attr); + + let fd = crate::sys_open("/root/hello.txt\0".as_ptr(), 0o0000, 0); + info!("fd {}", fd); + let mut values = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + let ret = crate::sys_read(fd, values.as_mut_ptr(), values.len()); + info!( + "result {} {}", + unsafe { String::from_utf8_unchecked(values) }, + ret + ); + crate::sys_close(fd);*/ +} + +pub unsafe fn create_file(name: &str, ptr: *const u8, length: usize) { + unsafe { + FILESYSTEM + .get() + .unwrap() + .create_file(name, ptr, length) + .expect("Unable to create file from ROM") + } } diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs new file mode 100644 index 0000000000..2f6e522dc9 --- /dev/null +++ b/src/fs/uhyve.rs @@ -0,0 +1,317 @@ +use alloc::borrow::ToOwned; +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::ptr; + +use hermit_sync::SpinMutex; +#[cfg(target_arch = "x86_64")] +use x86::io::outl; + +use crate::arch::mm::{paging, PhysAddr, VirtAddr}; +use crate::env::is_uhyve; +use crate::fd::IoError; +use crate::fs::{self, FileAttr, NodeKind, ObjectInterface, OpenOption, SeekWhence, VfsNode}; + +/// forward a request to the hypervisor uhyve +#[inline] +#[cfg(target_arch = "x86_64")] +fn uhyve_send(port: u16, data: &mut T) { + let ptr = VirtAddr(ptr::from_mut(data).addr() as u64); + let physical_address = paging::virtual_to_physical(ptr).unwrap(); + + unsafe { + outl(port, physical_address.as_u64() as u32); + } +} + +/// forward a request to the hypervisor uhyve +#[inline] +#[cfg(target_arch = "aarch64")] +fn uhyve_send(port: u16, data: &mut T) { + use core::arch::asm; + + let ptr = VirtAddr(ptr::from_mut(data).addr() as u64); + let physical_address = paging::virtual_to_physical(ptr).unwrap(); + + unsafe { + asm!( + "str x8, [{port}]", + port = in(reg) u64::from(port), + in("x8") physical_address.as_u64(), + options(nostack), + ); + } +} + +/// forward a request to the hypervisor uhyve +#[inline] +#[cfg(target_arch = "riscv64")] +fn uhyve_send(_port: u16, _data: &mut T) { + todo!() +} + +const UHYVE_PORT_WRITE: u16 = 0x400; +const UHYVE_PORT_OPEN: u16 = 0x440; +const UHYVE_PORT_CLOSE: u16 = 0x480; +const UHYVE_PORT_READ: u16 = 0x500; +const UHYVE_PORT_LSEEK: u16 = 0x580; +const UHYVE_PORT_UNLINK: u16 = 0x840; + +#[repr(C, packed)] +struct SysOpen { + name: PhysAddr, + flags: i32, + mode: i32, + ret: i32, +} + +impl SysOpen { + fn new(name: VirtAddr, flags: i32, mode: i32) -> SysOpen { + SysOpen { + name: paging::virtual_to_physical(name).unwrap(), + flags, + mode, + ret: -1, + } + } +} + +#[repr(C, packed)] +struct SysClose { + fd: i32, + ret: i32, +} + +impl SysClose { + fn new(fd: i32) -> SysClose { + SysClose { fd, ret: -1 } + } +} + +#[repr(C, packed)] +struct SysRead { + fd: i32, + buf: *const u8, + len: usize, + ret: isize, +} + +impl SysRead { + fn new(fd: i32, buf: *const u8, len: usize) -> SysRead { + SysRead { + fd, + buf, + len, + ret: -1, + } + } +} + +#[repr(C, packed)] +struct SysWrite { + fd: i32, + buf: *const u8, + len: usize, +} + +impl SysWrite { + pub fn new(fd: i32, buf: *const u8, len: usize) -> SysWrite { + SysWrite { fd, buf, len } + } +} + +#[repr(C, packed)] +struct SysLseek { + pub fd: i32, + pub offset: isize, + pub whence: i32, +} + +impl SysLseek { + fn new(fd: i32, offset: isize, whence: SeekWhence) -> SysLseek { + let whence: i32 = num::ToPrimitive::to_i32(&whence).unwrap(); + + SysLseek { fd, offset, whence } + } +} + +#[repr(C, packed)] +struct SysUnlink { + name: PhysAddr, + ret: i32, +} + +impl SysUnlink { + fn new(name: VirtAddr) -> SysUnlink { + SysUnlink { + name: paging::virtual_to_physical(name).unwrap(), + ret: -1, + } + } +} + +#[derive(Debug)] +struct UhyveFileHandleInner(i32); + +impl UhyveFileHandleInner { + pub fn new(fd: i32) -> Self { + Self(fd) + } + + fn read(&mut self, buf: &mut [u8]) -> Result { + let mut sysread = SysRead::new(self.0, buf.as_mut_ptr(), buf.len()); + uhyve_send(UHYVE_PORT_READ, &mut sysread); + + if sysread.ret >= 0 { + Ok(sysread.ret) + } else { + Err(num::FromPrimitive::from_isize(sysread.ret).unwrap()) + } + } + + fn write(&mut self, buf: &[u8]) -> Result { + let mut syswrite = SysWrite::new(self.0, buf.as_ptr(), buf.len()); + uhyve_send(UHYVE_PORT_WRITE, &mut syswrite); + + Ok(syswrite.len.try_into().unwrap()) + } + + fn lseek(&self, offset: isize, whence: SeekWhence) -> Result { + let mut syslseek = SysLseek::new(self.0, offset, whence); + uhyve_send(UHYVE_PORT_LSEEK, &mut syslseek); + + if syslseek.offset >= 0 { + Ok(syslseek.offset) + } else { + Err(IoError::EINVAL) + } + } +} + +impl Drop for UhyveFileHandleInner { + fn drop(&mut self) { + let mut sysclose = SysClose::new(self.0); + uhyve_send(UHYVE_PORT_CLOSE, &mut sysclose); + } +} + +#[derive(Debug)] +struct UhyveFileHandle(pub Arc>); + +impl UhyveFileHandle { + pub fn new(fd: i32) -> Self { + Self(Arc::new(SpinMutex::new(UhyveFileHandleInner::new(fd)))) + } +} + +impl ObjectInterface for UhyveFileHandle { + fn read(&self, buf: &mut [u8]) -> Result { + self.0.lock().read(buf) + } + + fn write(&self, buf: &[u8]) -> Result { + self.0.lock().write(buf) + } + + fn lseek(&self, offset: isize, whence: SeekWhence) -> Result { + self.0.lock().lseek(offset, whence) + } +} + +impl Clone for UhyveFileHandle { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +#[derive(Debug)] +pub(crate) struct UhyveDirectory; + +impl UhyveDirectory { + pub const fn new() -> Self { + UhyveDirectory {} + } +} + +impl VfsNode for UhyveDirectory { + /// Returns the node type + fn get_kind(&self) -> NodeKind { + NodeKind::Directory + } + + fn traverse_opendir( + &self, + _omponents: &mut Vec<&str>, + ) -> Result, IoError> { + Err(IoError::ENOSYS) + } + + fn traverse_stat(&self, _components: &mut Vec<&str>) -> Result { + Err(IoError::ENOSYS) + } + + fn traverse_lstat(&self, _components: &mut Vec<&str>) -> Result { + Err(IoError::ENOSYS) + } + + fn traverse_open( + &self, + components: &mut Vec<&str>, + opt: OpenOption, + ) -> Result, IoError> { + let path: String = if components.is_empty() { + "/\0".to_string() + } else { + components.iter().map(|v| "/".to_owned() + v).collect() + }; + + let mut sysopen = SysOpen::new(VirtAddr(path.as_ptr() as u64), opt.bits(), 0); + uhyve_send(UHYVE_PORT_OPEN, &mut sysopen); + + if sysopen.ret > 0 { + Ok(Arc::new(UhyveFileHandle::new(sysopen.ret))) + } else { + Err(num::FromPrimitive::from_i32(sysopen.ret).unwrap()) + } + } + + fn traverse_unlink(&self, components: &mut Vec<&str>) -> core::result::Result<(), IoError> { + let path: String = if components.is_empty() { + "/".to_string() + } else { + components.iter().map(|v| "/".to_owned() + v).collect() + }; + + let mut sysunlink = SysUnlink::new(VirtAddr(path.as_ptr() as u64)); + uhyve_send(UHYVE_PORT_UNLINK, &mut sysunlink); + + if sysunlink.ret == 0 { + Ok(()) + } else { + Err(num::FromPrimitive::from_i32(sysunlink.ret).unwrap()) + } + } + + fn traverse_rmdir(&self, _components: &mut Vec<&str>) -> core::result::Result<(), IoError> { + Err(IoError::ENOSYS) + } + + fn traverse_mkdir(&self, _components: &mut Vec<&str>, _mode: u32) -> Result<(), IoError> { + Err(IoError::ENOSYS) + } +} + +pub(crate) fn init() { + info!("Try to initialize uhyve filesystem"); + if is_uhyve() { + let mount_point = format!("/host"); + info!("Mounting virtio-fs at {}", mount_point); + fs::FILESYSTEM + .get() + .unwrap() + .mount(mount_point.as_str(), Box::new(UhyveDirectory::new())) + .expect("Mount failed. Duplicate mount_point?"); + } +} diff --git a/src/lib.rs b/src/lib.rs index 443d443b20..4df38e7afd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,6 +65,7 @@ use mm::allocator::LockedAllocator; pub(crate) use crate::arch::*; pub(crate) use crate::config::*; +pub use crate::fs::create_file; use crate::kernel::is_uhyve_with_pci; use crate::scheduler::{PerCoreScheduler, PerCoreSchedulerExt}; pub use crate::syscalls::*; diff --git a/src/syscalls/interfaces/mod.rs b/src/syscalls/interfaces/mod.rs index b65ca42be2..2fdbe145d1 100644 --- a/src/syscalls/interfaces/mod.rs +++ b/src/syscalls/interfaces/mod.rs @@ -61,7 +61,8 @@ pub trait SyscallInterface: Send + Sync { debug!("unlink {}", name); fs::FILESYSTEM - .lock() + .get() + .unwrap() .unlink(name) .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) } @@ -72,7 +73,8 @@ pub trait SyscallInterface: Send + Sync { debug!("rmdir {}", name); fs::FILESYSTEM - .lock() + .get() + .unwrap() .rmdir(name) .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) } @@ -83,42 +85,35 @@ pub trait SyscallInterface: Send + Sync { debug!("mkdir {}, mode {}", name, mode); fs::FILESYSTEM - .lock() + .get() + .unwrap() .mkdir(name, mode) .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) } - #[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 + match fs::FILESYSTEM.get().unwrap().stat(name) { + Ok(attr) => unsafe { + *stat = attr; + 0 + }, + Err(e) => -num::ToPrimitive::to_i32(&e).unwrap(), + } } - #[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) + match fs::FILESYSTEM.get().unwrap().lstat(name) { + Ok(attr) => unsafe { + *stat = attr; + 0 + }, + Err(e) => -num::ToPrimitive::to_i32(&e).unwrap(), + } } } diff --git a/src/syscalls/interfaces/uhyve.rs b/src/syscalls/interfaces/uhyve.rs index 637c3f9a83..7b66b7948e 100644 --- a/src/syscalls/interfaces/uhyve.rs +++ b/src/syscalls/interfaces/uhyve.rs @@ -14,10 +14,9 @@ use crate::syscalls::lwip::sys_lwip_get_errno; #[cfg(feature = "newlib")] use crate::syscalls::{LWIP_FD_BIT, LWIP_LOCK}; -pub(crate) const UHYVE_PORT_EXIT: u16 = 0x540; -pub(crate) const UHYVE_PORT_CMDSIZE: u16 = 0x740; -pub(crate) const UHYVE_PORT_CMDVAL: u16 = 0x780; -pub(crate) const UHYVE_PORT_UNLINK: u16 = 0x840; +const UHYVE_PORT_EXIT: u16 = 0x540; +const UHYVE_PORT_CMDSIZE: u16 = 0x740; +const UHYVE_PORT_CMDVAL: u16 = 0x780; #[cfg(feature = "newlib")] extern "C" { @@ -28,7 +27,7 @@ extern "C" { /// forward a request to the hypervisor uhyve #[inline] #[cfg(target_arch = "x86_64")] -pub(crate) fn uhyve_send(port: u16, data: &mut T) { +fn uhyve_send(port: u16, data: &mut T) { let ptr = VirtAddr(ptr::from_mut(data).addr() as u64); let physical_address = paging::virtual_to_physical(ptr).unwrap(); @@ -40,7 +39,7 @@ pub(crate) fn uhyve_send(port: u16, data: &mut T) { /// forward a request to the hypervisor uhyve #[inline] #[cfg(target_arch = "aarch64")] -pub(crate) fn uhyve_send(port: u16, data: &mut T) { +fn uhyve_send(port: u16, data: &mut T) { use core::arch::asm; let ptr = VirtAddr(ptr::from_mut(data).addr() as u64); @@ -110,31 +109,9 @@ impl SysExit { } } -#[repr(C, packed)] -struct SysUnlink { - name: PhysAddr, - ret: i32, -} - -impl SysUnlink { - fn new(name: VirtAddr) -> SysUnlink { - SysUnlink { - name: paging::virtual_to_physical(name).unwrap(), - ret: -1, - } - } -} - pub struct Uhyve; impl SyscallInterface for Uhyve { - fn unlink(&self, name: *const u8) -> i32 { - let mut sysunlink = SysUnlink::new(VirtAddr(name as u64)); - uhyve_send(UHYVE_PORT_UNLINK, &mut sysunlink); - - sysunlink.ret - } - /// ToDo: This function needs a description - also applies to trait in src/syscalls/interfaces/mod.rs /// /// ToDo: Add Safety section under which circumctances this is safe/unsafe to use diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index c95b038d91..f9210c7408 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -1,5 +1,7 @@ #![allow(clippy::result_unit_err)] +use core::ffi::CStr; + #[cfg(feature = "newlib")] use hermit_sync::InterruptTicketMutex; use hermit_sync::Lazy; @@ -156,8 +158,15 @@ pub extern "C" fn sys_lstat(name: *const u8, stat: *mut FileAttr) -> i32 { } extern "C" fn __sys_fstat(fd: FileDescriptor, stat: *mut FileAttr) -> i32 { + let stat = unsafe { &mut *stat }; let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).fstat(stat)) + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).fstat(stat) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) } #[no_mangle] @@ -166,7 +175,11 @@ pub extern "C" fn sys_fstat(fd: FileDescriptor, stat: *mut FileAttr) -> i32 { } extern "C" fn __sys_opendir(name: *const u8) -> FileDescriptor { - crate::fd::opendir(name).map_or_else(|e| e, |v| v) + if let Ok(name) = unsafe { CStr::from_ptr(name as _) }.to_str() { + crate::fd::opendir(name).map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| v) + } else { + -crate::errno::EINVAL + } } #[no_mangle] @@ -175,7 +188,12 @@ pub extern "C" fn sys_opendir(name: *const u8) -> FileDescriptor { } 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) + if let Ok(name) = unsafe { CStr::from_ptr(name as _) }.to_str() { + crate::fd::open(name, flags, mode) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| v) + } else { + -crate::errno::EINVAL + } } #[no_mangle] @@ -185,7 +203,7 @@ pub extern "C" fn sys_open(name: *const u8, flags: i32, mode: i32) -> FileDescri extern "C" fn __sys_close(fd: FileDescriptor) -> i32 { let obj = remove_object(fd); - obj.map_or_else(|e| e, |_| 0) + obj.map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) } #[no_mangle] @@ -194,8 +212,15 @@ pub extern "C" fn sys_close(fd: FileDescriptor) -> i32 { } extern "C" fn __sys_read(fd: FileDescriptor, buf: *mut u8, len: usize) -> isize { + let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; let obj = get_object(fd); - obj.map_or_else(|e| e as isize, |v| (*v).read(buf, len)) + obj.map_or_else( + |e| e as isize, + |v| { + (*v).read(slice) + .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + }, + ) } #[no_mangle] @@ -204,8 +229,15 @@ pub extern "C" fn sys_read(fd: FileDescriptor, buf: *mut u8, len: usize) -> isiz } extern "C" fn __sys_write(fd: FileDescriptor, buf: *const u8, len: usize) -> isize { + let slice = unsafe { core::slice::from_raw_parts(buf, len) }; let obj = get_object(fd); - obj.map_or_else(|e| e as isize, |v| (*v).write(buf, len)) + obj.map_or_else( + |e| e as isize, + |v| { + (*v).write(slice) + .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + }, + ) } #[no_mangle] @@ -215,7 +247,13 @@ pub extern "C" fn sys_write(fd: FileDescriptor, buf: *const u8, len: usize) -> i extern "C" fn __sys_ioctl(fd: FileDescriptor, cmd: i32, argp: *mut core::ffi::c_void) -> i32 { let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).ioctl(cmd, argp)) + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).ioctl(cmd, argp) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) } #[no_mangle] @@ -227,7 +265,10 @@ extern "C" fn __sys_lseek(fd: FileDescriptor, offset: isize, whence: i32) -> isi let obj = get_object(fd); obj.map_or_else( |e| e as isize, - |v| (*v).lseek(offset, num::FromPrimitive::from_i32(whence).unwrap()), + |v| { + (*v).lseek(offset, num::FromPrimitive::from_i32(whence).unwrap()) + .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |_| 0) + }, ) } @@ -249,7 +290,7 @@ pub extern "C" fn sys_readdir(fd: FileDescriptor) -> DirectoryEntry { } extern "C" fn __sys_dup(fd: i32) -> i32 { - dup_object(fd).map_or_else(|e| e, |v| v) + dup_object(fd).map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| v) } #[no_mangle] From fa56ef3d20d599f5cee5dca3900b5180c2f79754 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 2 Jan 2024 12:02:31 +0000 Subject: [PATCH 04/32] remove some clippy warnings --- src/fd/socket/tcp.rs | 4 +-- src/fs/fuse.rs | 9 +++--- src/fs/mem.rs | 16 +++++----- src/fs/mod.rs | 72 +++++--------------------------------------- src/fs/uhyve.rs | 2 +- 5 files changed, 24 insertions(+), 79 deletions(-) diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index c75a9c3f67..307ea91b1c 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -253,7 +253,7 @@ impl Socket { } fn read(&self, buf: &mut [u8]) -> Result { - if buf.len() == 0 { + if buf.is_empty() { return Ok(0); } @@ -275,7 +275,7 @@ impl Socket { } fn write(&self, buf: &[u8]) -> Result { - if buf.len() == 0 { + if buf.is_empty() { return Ok(0); } diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 07559718a6..2ddaac0235 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -31,6 +31,7 @@ const U64_SIZE: u32 = ::core::mem::size_of::() as u32; const S_IFLNK: u32 = 40960; const S_IFMT: u32 = 61440; +#[allow(dead_code)] const FUSE_GETATTR_FH: u32 = 1 << 0; #[repr(C)] @@ -202,7 +203,7 @@ struct fuse_attr { } impl fuse_attr { - fn to_stat(self) -> FileAttr { + fn to_stat(&self) -> FileAttr { FileAttr { st_ino: self.ino, st_nlink: self.nlink as u64, @@ -1151,7 +1152,7 @@ impl FuseFileHandleInner { fn read(&mut self, buf: &mut [u8]) -> Result { debug!("FUSE read!"); let mut len = buf.len(); - if len as usize > MAX_READ_LEN { + if len > MAX_READ_LEN { debug!("Reading longer than max_read_len: {}", len); len = MAX_READ_LEN; } @@ -1164,7 +1165,7 @@ impl FuseFileHandleInner { let len: usize = if rsp.header.len as usize - ::core::mem::size_of::() - ::core::mem::size_of::() - >= len.try_into().unwrap() + >= len { len.try_into().unwrap() } else { @@ -1498,7 +1499,7 @@ impl VfsNode for FuseDirectory { Ok(attr.to_stat()) } else { let path = readlink(rsp.nodeid)?; - let mut components: Vec<&str> = path.split("/").collect(); + let mut components: Vec<&str> = path.split('/').collect(); self.traverse_stat(&mut components) } } diff --git a/src/fs/mem.rs b/src/fs/mem.rs index e23312e0a9..0886e78466 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -143,7 +143,7 @@ impl MemDirectory { pub fn create_file(&self, name: &str, ptr: *const u8, length: usize) -> Result<(), IoError> { let name = name.trim(); - if name.find("/").is_none() { + if name.find('/').is_none() { let file = unsafe { MemFile::from_raw_parts(ptr, length) }; self.inner .0 @@ -336,7 +336,7 @@ impl RomHandle { pub fn len(&self) -> usize { let guard = self.data.read(); - guard.len() as usize + guard.len() } } @@ -367,8 +367,8 @@ impl RamHandle { pub fn len(&self) -> usize { let guard = self.data.read(); - let ref vec: &Vec = guard.deref(); - vec.len() as usize + let vec: &Vec = guard.deref(); + vec.len() } } @@ -384,8 +384,8 @@ impl Clone for RamHandle { /// Enumeration of possible methods to seek within an I/O object. #[derive(Debug, Clone)] enum DataHandle { - RAM(RamHandle), - ROM(RomHandle), + Ram(RamHandle), + Rom(RomHandle), } #[derive(Debug)] @@ -397,13 +397,13 @@ pub(crate) struct MemFile { impl MemFile { pub fn new() -> Self { Self { - data: DataHandle::RAM(RamHandle::new()), + data: DataHandle::Ram(RamHandle::new()), } } pub unsafe fn from_raw_parts(ptr: *const u8, length: usize) -> Self { Self { - data: unsafe { DataHandle::ROM(RomHandle::new(ptr, length)) }, + data: unsafe { DataHandle::Rom(RomHandle::new(ptr, length)) }, } } } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index cd5c04cb57..61ffbb8120 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -4,7 +4,6 @@ mod mem; mod uhyve; use alloc::boxed::Box; -use alloc::string::String; use alloc::sync::Arc; use alloc::vec::Vec; @@ -96,7 +95,7 @@ impl Filesystem { /// Tries to open file at given path. pub fn open(&self, path: &str, opt: OpenOption) -> Result, IoError> { info!("Open file {}", path); - let mut components: Vec<&str> = path.split("/").collect(); + let mut components: Vec<&str> = path.split('/').collect(); components.reverse(); components.pop(); @@ -107,7 +106,7 @@ impl Filesystem { /// Unlinks a file given by path pub fn unlink(&self, path: &str) -> Result<(), IoError> { debug!("Unlinking file {}", path); - let mut components: Vec<&str> = path.split("/").collect(); + let mut components: Vec<&str> = path.split('/').collect(); components.reverse(); components.pop(); @@ -118,7 +117,7 @@ impl Filesystem { /// Remove directory given by path pub fn rmdir(&self, path: &str) -> Result<(), IoError> { debug!("Removing directory {}", path); - let mut components: Vec<&str> = path.split("/").collect(); + let mut components: Vec<&str> = path.split('/').collect(); components.reverse(); components.pop(); @@ -129,7 +128,7 @@ impl Filesystem { /// Create directory given by path pub fn mkdir(&self, path: &str, mode: u32) -> Result<(), IoError> { debug!("Create directory {}", path); - let mut components: Vec<&str> = path.split("/").collect(); + let mut components: Vec<&str> = path.split('/').collect(); components.reverse(); components.pop(); @@ -143,7 +142,7 @@ impl Filesystem { let mut components: Vec<&str> = Vec::new(); self.root.traverse_opendir(&mut components) } else { - let mut components: Vec<&str> = path.split("/").collect(); + let mut components: Vec<&str> = path.split('/').collect(); components.reverse(); components.pop(); @@ -156,7 +155,7 @@ impl Filesystem { pub fn stat(&self, path: &str) -> Result { debug!("Getting stats {}", path); - let mut components: Vec<&str> = path.split("/").collect(); + let mut components: Vec<&str> = path.split('/').collect(); components.reverse(); components.pop(); @@ -167,7 +166,7 @@ impl Filesystem { pub fn lstat(&self, path: &str) -> Result { debug!("Getting lstats {}", path); - let mut components: Vec<&str> = path.split("/").collect(); + let mut components: Vec<&str> = path.split('/').collect(); components.reverse(); components.pop(); @@ -182,7 +181,7 @@ impl Filesystem { ) -> Result<(), IoError> { debug!("Mounting {}", path); - let mut components: Vec<&str> = path.split("/").collect(); + let mut components: Vec<&str> = path.split('/').collect(); components.reverse(); components.pop(); @@ -255,61 +254,6 @@ pub(crate) fn init() { #[cfg(all(feature = "fuse", feature = "pci"))] fuse::init(); uhyve::init(); - - /*let fd = crate::sys_opendir("/\0".as_ptr()); - info!("fd {}", fd); - loop { - if let crate::fd::DirectoryEntry::Valid(dirent) = crate::sys_readdir(fd) { - if dirent == core::ptr::null() { - break; - } - - let s = unsafe { - String::from_raw_parts( - &(*dirent).d_name as *const _ as *mut u8, - (*dirent).d_namelen as usize, - (*dirent).d_namelen as usize, - ) - }; - info!("dirent.d_name {:?}", s); - core::mem::forget(s); - } - } - - let fd = crate::sys_opendir("/root\0".as_ptr()); - info!("fd {}", fd); - loop { - if let crate::fd::DirectoryEntry::Valid(dirent) = crate::sys_readdir(fd) { - if dirent == core::ptr::null() { - break; - } - - let s = unsafe { - String::from_raw_parts( - &(*dirent).d_name as *const _ as *mut u8, - (*dirent).d_namelen as usize, - (*dirent).d_namelen as usize, - ) - }; - info!("dirent.d_name {:?}", s); - core::mem::forget(s); - } - } - - let mut attr: FileAttr = Default::default(); - let ret = crate::sys_lstat("/root/hello.txt\0".as_ptr(), &mut attr as *mut FileAttr); - info!("ret {} {:?}", ret, attr); - - let fd = crate::sys_open("/root/hello.txt\0".as_ptr(), 0o0000, 0); - info!("fd {}", fd); - let mut values = vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; - let ret = crate::sys_read(fd, values.as_mut_ptr(), values.len()); - info!( - "result {} {}", - unsafe { String::from_utf8_unchecked(values) }, - ret - ); - crate::sys_close(fd);*/ } pub unsafe fn create_file(name: &str, ptr: *const u8, length: usize) { diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index 2f6e522dc9..e7dc9af143 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -306,7 +306,7 @@ impl VfsNode for UhyveDirectory { pub(crate) fn init() { info!("Try to initialize uhyve filesystem"); if is_uhyve() { - let mount_point = format!("/host"); + let mount_point = "/host".to_string(); info!("Mounting virtio-fs at {}", mount_point); fs::FILESYSTEM .get() From 0ae2f2ee3dff0757b7a9a842489fc682188c6e69 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 2 Jan 2024 15:29:36 +0000 Subject: [PATCH 05/32] remove platform independent part to the syscall interface --- src/syscalls/interfaces/mod.rs | 63 ---------------------------------- src/syscalls/mod.rs | 54 ++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 79 deletions(-) diff --git a/src/syscalls/interfaces/mod.rs b/src/syscalls/interfaces/mod.rs index 2fdbe145d1..6ccb58b93b 100644 --- a/src/syscalls/interfaces/mod.rs +++ b/src/syscalls/interfaces/mod.rs @@ -1,12 +1,10 @@ use alloc::boxed::Box; use alloc::vec::Vec; -use core::ffi::CStr; pub use self::generic::*; pub use self::uhyve::*; #[cfg(not(target_arch = "x86_64"))] use crate::errno::ENOSYS; -use crate::fs::{self, FileAttr}; use crate::{arch, env}; mod generic; @@ -55,65 +53,4 @@ pub trait SyscallInterface: Send + Sync { fn shutdown(&self, _arg: i32) -> ! { arch::processor::shutdown() } - - fn unlink(&self, name: *const u8) -> i32 { - let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); - debug!("unlink {}", name); - - fs::FILESYSTEM - .get() - .unwrap() - .unlink(name) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 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 - .get() - .unwrap() - .rmdir(name) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 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 - .get() - .unwrap() - .mkdir(name, mode) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) - } - - fn stat(&self, name: *const u8, stat: *mut FileAttr) -> i32 { - let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); - debug!("stat {}", name); - - match fs::FILESYSTEM.get().unwrap().stat(name) { - Ok(attr) => unsafe { - *stat = attr; - 0 - }, - Err(e) => -num::ToPrimitive::to_i32(&e).unwrap(), - } - } - - fn lstat(&self, name: *const u8, stat: *mut FileAttr) -> i32 { - let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); - debug!("lstat {}", name); - - match fs::FILESYSTEM.get().unwrap().lstat(name) { - Ok(attr) => unsafe { - *stat = attr; - 0 - }, - Err(e) => -num::ToPrimitive::to_i32(&e).unwrap(), - } - } } diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index f9210c7408..2b9e078d35 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -19,7 +19,7 @@ pub use self::tasks::*; pub use self::timer::*; use crate::env; use crate::fd::{dup_object, get_object, remove_object, DirectoryEntry, FileDescriptor}; -use crate::fs::FileAttr; +use crate::fs::{self, FileAttr}; use crate::syscalls::interfaces::SyscallInterface; #[cfg(target_os = "none")] use crate::{__sys_free, __sys_malloc, __sys_realloc}; @@ -101,7 +101,13 @@ pub extern "C" fn sys_shutdown(arg: i32) -> ! { } extern "C" fn __sys_unlink(name: *const u8) -> i32 { - SYS.unlink(name) + let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); + + fs::FILESYSTEM + .get() + .unwrap() + .unlink(name) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) } #[no_mangle] @@ -109,14 +115,14 @@ pub extern "C" fn sys_unlink(name: *const u8) -> i32 { kernel_function!(__sys_unlink(name)) } -#[cfg(target_arch = "x86_64")] extern "C" fn __sys_mkdir(name: *const u8, mode: u32) -> i32 { - SYS.mkdir(name, mode) -} + let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); -#[cfg(not(target_arch = "x86_64"))] -extern "C" fn __sys_mkdir(_name: *const u8, _mode: u32) -> i32 { - -crate::errno::ENOSYS + fs::FILESYSTEM + .get() + .unwrap() + .mkdir(name, mode) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) } #[no_mangle] @@ -124,14 +130,14 @@ pub extern "C" fn sys_mkdir(name: *const u8, mode: u32) -> i32 { kernel_function!(__sys_mkdir(name, mode)) } -#[cfg(target_arch = "x86_64")] extern "C" fn __sys_rmdir(name: *const u8) -> i32 { - SYS.rmdir(name) -} + let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); -#[cfg(not(target_arch = "x86_64"))] -extern "C" fn __sys_rmdir(_name: *const u8) -> i32 { - -crate::errno::ENOSYS + fs::FILESYSTEM + .get() + .unwrap() + .rmdir(name) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) } #[no_mangle] @@ -140,7 +146,15 @@ pub extern "C" fn sys_rmdir(name: *const u8) -> i32 { } extern "C" fn __sys_stat(name: *const u8, stat: *mut FileAttr) -> i32 { - SYS.stat(name, stat) + let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); + + match fs::FILESYSTEM.get().unwrap().stat(name) { + Ok(attr) => unsafe { + *stat = attr; + 0 + }, + Err(e) => -num::ToPrimitive::to_i32(&e).unwrap(), + } } #[no_mangle] @@ -149,7 +163,15 @@ pub extern "C" fn sys_stat(name: *const u8, stat: *mut FileAttr) -> i32 { } extern "C" fn __sys_lstat(name: *const u8, stat: *mut FileAttr) -> i32 { - SYS.lstat(name, stat) + let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); + + match fs::FILESYSTEM.get().unwrap().lstat(name) { + Ok(attr) => unsafe { + *stat = attr; + 0 + }, + Err(e) => -num::ToPrimitive::to_i32(&e).unwrap(), + } } #[no_mangle] From 30d52b1e086cc01d2d19cfa2576691d30302de20 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 2 Jan 2024 15:55:28 +0000 Subject: [PATCH 06/32] revise visibility of the modules - required to use the kernel as common crate instead of a static library - revise error handling of get_object, error number has to be negative - remove clippy warnings --- src/fd/mod.rs | 4 ++-- src/fd/socket/mod.rs | 6 +++--- src/fs/fuse.rs | 2 +- src/lib.rs | 4 ++-- src/syscalls/interfaces/mod.rs | 2 -- src/syscalls/mod.rs | 6 +++--- 6 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 79355a1545..291b528ba3 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -14,7 +14,7 @@ use crate::fs::{self, FileAttr, SeekWhence}; use crate::syscalls::net::*; #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] -pub mod socket; +pub(crate) mod socket; mod stdio; const STDIN_FILENO: FileDescriptor = 0; @@ -84,7 +84,7 @@ pub enum DirectoryEntry { Valid(*const Dirent), } -pub trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { +pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// `read` attempts to read `len` bytes from the object references /// by the descriptor fn read(&self, _buf: &mut [u8]) -> Result { diff --git a/src/fd/socket/mod.rs b/src/fd/socket/mod.rs index 980773d31d..0fcf799e4a 100644 --- a/src/fd/socket/mod.rs +++ b/src/fd/socket/mod.rs @@ -238,7 +238,7 @@ pub extern "C" fn __sys_recv(fd: i32, buf: *mut u8, len: usize) -> isize { let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; let obj = get_object(fd); obj.map_or_else( - |e| e as isize, + |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { (*v).read(slice) .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) @@ -257,7 +257,7 @@ pub extern "C" fn __sys_sendto( let slice = unsafe { core::slice::from_raw_parts(buf, len) }; let obj = get_object(fd); obj.map_or_else( - |e| e as isize, + |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { (*v).sendto(slice, addr, addr_len) .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) @@ -276,7 +276,7 @@ pub extern "C" fn __sys_recvfrom( let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; let obj = get_object(fd); obj.map_or_else( - |e| e as isize, + |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { (*v).recvfrom(slice, addr, addr_len) .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 2ddaac0235..0e3649e674 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -1167,7 +1167,7 @@ impl FuseFileHandleInner { - ::core::mem::size_of::() >= len { - len.try_into().unwrap() + len } else { rsp.header.len as usize - ::core::mem::size_of::() diff --git a/src/lib.rs b/src/lib.rs index 4df38e7afd..91cdad60ac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,8 +84,8 @@ mod entropy; mod env; pub mod errno; mod executor; -pub(crate) mod fd; -pub(crate) mod fs; +pub mod fd; +pub mod fs; mod mm; mod scheduler; mod synch; diff --git a/src/syscalls/interfaces/mod.rs b/src/syscalls/interfaces/mod.rs index 6ccb58b93b..bf32eecfbf 100644 --- a/src/syscalls/interfaces/mod.rs +++ b/src/syscalls/interfaces/mod.rs @@ -3,8 +3,6 @@ use alloc::vec::Vec; pub use self::generic::*; pub use self::uhyve::*; -#[cfg(not(target_arch = "x86_64"))] -use crate::errno::ENOSYS; use crate::{arch, env}; mod generic; diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index 2b9e078d35..c79b9a91df 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -237,7 +237,7 @@ extern "C" fn __sys_read(fd: FileDescriptor, buf: *mut u8, len: usize) -> isize let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; let obj = get_object(fd); obj.map_or_else( - |e| e as isize, + |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { (*v).read(slice) .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) @@ -254,7 +254,7 @@ extern "C" fn __sys_write(fd: FileDescriptor, buf: *const u8, len: usize) -> isi let slice = unsafe { core::slice::from_raw_parts(buf, len) }; let obj = get_object(fd); obj.map_or_else( - |e| e as isize, + |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { (*v).write(slice) .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) @@ -286,7 +286,7 @@ pub extern "C" fn sys_ioctl(fd: FileDescriptor, cmd: i32, argp: *mut core::ffi:: extern "C" fn __sys_lseek(fd: FileDescriptor, offset: isize, whence: i32) -> isize { let obj = get_object(fd); obj.map_or_else( - |e| e as isize, + |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { (*v).lseek(offset, num::FromPrimitive::from_i32(whence).unwrap()) .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |_| 0) From e0f25fb5dc19626f475319efa139c0d238d73250 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 3 Jan 2024 00:25:06 +0000 Subject: [PATCH 07/32] add basic support to create, read and write files - currently, the access permission will be not checked --- src/fd/mod.rs | 42 +++--- src/fs/mem.rs | 304 ++++++++++++++++++++++++++++---------------- src/fs/mod.rs | 7 +- src/syscalls/mod.rs | 8 +- 4 files changed, 232 insertions(+), 129 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 291b528ba3..c643c60382 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -38,6 +38,7 @@ pub(crate) enum IoError { ENOTCONN = crate::errno::ENOTCONN as isize, ENOTDIR = crate::errno::ENOTDIR as isize, EMFILE = crate::errno::EMFILE as isize, + EEXIST = crate::errno::EEXIST as isize, } pub(crate) type FileDescriptor = i32; @@ -233,31 +234,32 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { fn ioctl(&self, _cmd: i32, _argp: *mut c_void) -> Result<(), IoError> { Err(IoError::ENOSYS) } + + // close a file descriptor + fn close(&self) {} } pub(crate) fn open(name: &str, flags: i32, mode: i32) -> Result { - { - // mode is 0x777 (0b0111_0111_0111), when flags | O_CREAT, else 0 - // flags is bitmask of O_DEC_* defined above. - // (taken from rust stdlib/sys hermit target ) - - debug!("Open {}, {}, {}", name, flags, mode); - - let fs = fs::FILESYSTEM.get().unwrap(); - if let Ok(file) = fs.open( - name, - OpenOption::from_bits(mode).expect("Invalid open flags"), - ) { - let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); - if OBJECT_MAP.write().try_insert(fd, file).is_err() { - Err(IoError::EINVAL) - } else { - Ok(fd as FileDescriptor) - } - } else { + // mode is 0x777 (0b0111_0111_0111), when flags | O_CREAT, else 0 + // flags is bitmask of O_DEC_* defined above. + // (taken from rust stdlib/sys hermit target ) + + debug!("Open {}, {}, {}", name, flags, mode); + + let fs = fs::FILESYSTEM.get().unwrap(); + if let Ok(file) = fs.open( + name, + OpenOption::from_bits(flags).expect("Invalid open flags"), + ) { + let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); + if OBJECT_MAP.write().try_insert(fd, file).is_err() { Err(IoError::EINVAL) + } else { + Ok(fd as FileDescriptor) } - } //} + } else { + Err(IoError::EINVAL) + } } #[allow(unused_variables)] diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 0886e78466..473736f993 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -15,15 +15,187 @@ use alloc::collections::BTreeMap; use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::vec::Vec; -use core::ops::Deref; +use core::ops::{Deref, DerefMut}; use core::slice; use core::sync::atomic::{AtomicUsize, Ordering}; -use hermit_sync::RwSpinLock; +use hermit_sync::{RwSpinLock, SpinMutex}; use crate::fd::{DirectoryEntry, Dirent, IoError, ObjectInterface, OpenOption}; use crate::fs::{FileAttr, NodeKind, VfsNode}; +#[derive(Debug)] +struct RomFileInner { + /// Position within the file + pos: SpinMutex, + /// File content + data: Arc>, +} + +impl ObjectInterface for RomFileInner { + fn close(&self) { + *self.pos.lock() = 0; + } + + fn read(&self, buf: &mut [u8]) -> Result { + let vec = self.data.read(); + let mut pos_guard = self.pos.lock(); + let pos = *pos_guard; + + if pos >= vec.len() { + return Ok(0); + } + + let len = if vec.len() - pos < buf.len() { + vec.len() - pos + } else { + buf.len() + }; + + buf[0..len].clone_from_slice(&vec[pos..pos + len]); + *pos_guard = pos + len; + + Ok(len.try_into().unwrap()) + } +} + +impl RomFileInner { + pub unsafe fn new(addr: *const u8, len: usize) -> Self { + Self { + pos: SpinMutex::new(0), + data: Arc::new(RwSpinLock::new(unsafe { slice::from_raw_parts(addr, len) })), + } + } + + pub fn len(&self) -> usize { + let guard = self.data.read(); + guard.len() + } +} + +impl Clone for RomFileInner { + fn clone(&self) -> Self { + RomFileInner { + pos: SpinMutex::new(0), + data: self.data.clone(), + } + } +} + +#[derive(Debug)] +pub struct RamFileInner { + /// Position within the file + pos: SpinMutex, + /// File content + data: Arc>>, +} + +impl ObjectInterface for RamFileInner { + fn close(&self) { + *self.pos.lock() = 0; + } + + fn read(&self, buf: &mut [u8]) -> Result { + let guard = self.data.read(); + let vec = guard.deref(); + let mut pos_guard = self.pos.lock(); + let pos = *pos_guard; + + if pos >= vec.len() { + return Ok(0); + } + + let len = if vec.len() - pos < buf.len() { + vec.len() - pos + } else { + buf.len() + }; + + buf[0..len].clone_from_slice(&vec[pos..pos + len]); + *pos_guard = pos + len; + + Ok(len.try_into().unwrap()) + } + + fn write(&self, buf: &[u8]) -> Result { + let mut guard = self.data.write(); + let vec = guard.deref_mut(); + let mut pos_guard = self.pos.lock(); + let pos = *pos_guard; + + if pos + buf.len() > vec.len() { + vec.resize(pos + buf.len(), 0); + } + + vec[pos..pos + buf.len()].clone_from_slice(buf); + *pos_guard = pos + buf.len(); + + Ok(buf.len().try_into().unwrap()) + } +} + +impl RamFileInner { + pub fn new() -> Self { + Self { + pos: SpinMutex::new(0), + data: Arc::new(RwSpinLock::new(Vec::new())), + } + } + + pub fn len(&self) -> usize { + let guard = self.data.read(); + let vec: &Vec = guard.deref(); + vec.len() + } +} + +impl Clone for RamFileInner { + fn clone(&self) -> Self { + RamFileInner { + pos: SpinMutex::new(0), + data: self.data.clone(), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct RomFile(Arc); + +impl VfsNode for RomFile { + fn get_kind(&self) -> NodeKind { + NodeKind::File + } + + fn get_object(&self) -> Result, IoError> { + Ok(self.0.clone()) + } +} + +impl RomFile { + pub unsafe fn new(ptr: *const u8, length: usize) -> Self { + Self(Arc::new(RomFileInner::new(ptr, length))) + } +} + +#[derive(Debug, Clone)] +pub(crate) struct RamFile(Arc); + +impl VfsNode for RamFile { + fn get_kind(&self) -> NodeKind { + NodeKind::File + } + + fn get_object(&self) -> Result, IoError> { + Ok(self.0.clone()) + } +} + +impl RamFile { + pub fn new() -> Self { + Self(Arc::new(RamFileInner::new())) + } +} + #[derive(Debug)] struct MemDirectoryInner( pub Arc< @@ -144,7 +316,7 @@ impl MemDirectory { pub fn create_file(&self, name: &str, ptr: *const u8, length: usize) -> Result<(), IoError> { let name = name.trim(); if name.find('/').is_none() { - let file = unsafe { MemFile::from_raw_parts(ptr, length) }; + let file = unsafe { RomFile::new(ptr, length) }; self.inner .0 .write() @@ -157,7 +329,6 @@ impl MemDirectory { } impl VfsNode for MemDirectory { - /// Returns the node type fn get_kind(&self) -> NodeKind { NodeKind::Directory } @@ -304,113 +475,32 @@ impl VfsNode for MemDirectory { if let Some(component) = components.pop() { let node_name = String::from(component); + if components.is_empty() { + let mut guard = self.inner.0.write(); + if opt.contains(OpenOption::O_CREAT) || opt.contains(OpenOption::O_CREAT) { + if guard.get(&node_name).is_some() { + return Err(IoError::EEXIST); + } else { + let file = Box::new(RamFile::new()); + guard.insert(node_name, file.clone()); + return Ok(file.0.clone()); + } + } else if let Some(file) = guard.get(&node_name) { + if file.get_kind() == NodeKind::File { + return file.get_object(); + } else { + return Err(IoError::ENOENT); + } + } else { + return Err(IoError::ENOENT); + } + } + if let Some(directory) = self.inner.0.read().get(&node_name) { return directory.traverse_open(components, opt); } - - /*if components.is_empty() { - self.children.insert(node_name, obj); - return Ok(()); - }*/ - } - - Err(IoError::EBADF) - } -} - -#[derive(Debug)] -pub struct RomHandle { - /// Position within the file - pos: AtomicUsize, - /// File content - data: Arc>, -} - -impl RomHandle { - pub unsafe fn new(addr: *const u8, len: usize) -> Self { - RomHandle { - pos: AtomicUsize::new(0), - data: Arc::new(RwSpinLock::new(unsafe { slice::from_raw_parts(addr, len) })), - } - } - - pub fn len(&self) -> usize { - let guard = self.data.read(); - guard.len() - } -} - -impl Clone for RomHandle { - fn clone(&self) -> Self { - RomHandle { - pos: AtomicUsize::new(self.pos.load(Ordering::Relaxed)), - data: self.data.clone(), } - } -} - -#[derive(Debug)] -pub struct RamHandle { - /// Position within the file - pos: AtomicUsize, - /// File content - data: Arc>>, -} -impl RamHandle { - pub fn new() -> Self { - RamHandle { - pos: AtomicUsize::new(0), - data: Arc::new(RwSpinLock::new(Vec::new())), - } - } - - pub fn len(&self) -> usize { - let guard = self.data.read(); - let vec: &Vec = guard.deref(); - vec.len() - } -} - -impl Clone for RamHandle { - fn clone(&self) -> Self { - RamHandle { - pos: AtomicUsize::new(self.pos.load(Ordering::Relaxed)), - data: self.data.clone(), - } - } -} - -/// Enumeration of possible methods to seek within an I/O object. -#[derive(Debug, Clone)] -enum DataHandle { - Ram(RamHandle), - Rom(RomHandle), -} - -#[derive(Debug)] -pub(crate) struct MemFile { - /// File content - data: DataHandle, -} - -impl MemFile { - pub fn new() -> Self { - Self { - data: DataHandle::Ram(RamHandle::new()), - } - } - - pub unsafe fn from_raw_parts(ptr: *const u8, length: usize) -> Self { - Self { - data: unsafe { DataHandle::Rom(RomHandle::new(ptr, length)) }, - } - } -} - -impl VfsNode for MemFile { - /// Returns the node type - fn get_kind(&self) -> NodeKind { - NodeKind::File + Err(IoError::ENOENT) } } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 61ffbb8120..77b489678a 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -28,6 +28,11 @@ pub(crate) trait VfsNode: core::fmt::Debug { /// Determines the current node type fn get_kind(&self) -> NodeKind; + /// Determine the syscall interface + fn get_object(&self) -> Result, IoError> { + Err(IoError::ENOSYS) + } + /// Helper function to create a new dirctory node fn traverse_mkdir(&self, _components: &mut Vec<&str>, _mode: u32) -> Result<(), IoError> { Err(IoError::ENOSYS) @@ -94,7 +99,7 @@ impl Filesystem { /// Tries to open file at given path. pub fn open(&self, path: &str, opt: OpenOption) -> Result, IoError> { - info!("Open file {}", path); + debug!("Open file {} with {:?}", path, opt); let mut components: Vec<&str> = path.split('/').collect(); components.reverse(); diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index c79b9a91df..8522dc7b7d 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -225,7 +225,13 @@ pub extern "C" fn sys_open(name: *const u8, flags: i32, mode: i32) -> FileDescri extern "C" fn __sys_close(fd: FileDescriptor) -> i32 { let obj = remove_object(fd); - obj.map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + v.close(); + 0 + }, + ) } #[no_mangle] From 3e2061dbc8f6f57ec6c6accf8df2b114c4e8d33d Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 3 Jan 2024 00:29:01 +0000 Subject: [PATCH 08/32] remove clippy warnings --- src/fd/stdio.rs | 1 + src/fs/mem.rs | 2 +- src/fs/uhyve.rs | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/fd/stdio.rs b/src/fd/stdio.rs index a8c644bdbe..67af71caf0 100644 --- a/src/fd/stdio.rs +++ b/src/fd/stdio.rs @@ -5,6 +5,7 @@ use core::ptr; #[cfg(target_arch = "x86_64")] use x86::io::*; +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] use crate::arch::mm::{paging, VirtAddr}; use crate::console::CONSOLE; use crate::fd::{IoError, ObjectInterface, STDERR_FILENO, STDOUT_FILENO}; diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 473736f993..7a363de65d 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -173,7 +173,7 @@ impl VfsNode for RomFile { impl RomFile { pub unsafe fn new(ptr: *const u8, length: usize) -> Self { - Self(Arc::new(RomFileInner::new(ptr, length))) + Self(Arc::new(unsafe { RomFileInner::new(ptr, length) })) } } diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index e7dc9af143..47b24477a4 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -3,6 +3,7 @@ use alloc::boxed::Box; use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::vec::Vec; +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] use core::ptr; use hermit_sync::SpinMutex; From 0f32627cccd3d7afcc621689e31e54114c70a8aa Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 3 Jan 2024 09:14:08 +0000 Subject: [PATCH 09/32] add tracing of closing a file descriptor --- src/fd/mod.rs | 4 +++- src/fs/mem.rs | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index c643c60382..cc46be0901 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -236,7 +236,9 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { } // close a file descriptor - fn close(&self) {} + fn close(&self) { + trace!("close file descriptor"); + } } pub(crate) fn open(name: &str, flags: i32, mode: i32) -> Result { diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 7a363de65d..c036a16c38 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -34,6 +34,7 @@ struct RomFileInner { impl ObjectInterface for RomFileInner { fn close(&self) { + trace!("close file"); *self.pos.lock() = 0; } @@ -92,6 +93,7 @@ pub struct RamFileInner { impl ObjectInterface for RamFileInner { fn close(&self) { + trace!("close file"); *self.pos.lock() = 0; } @@ -214,6 +216,11 @@ impl MemDirectoryInner { } impl ObjectInterface for MemDirectoryInner { + fn close(&self) { + trace!("close directory"); + self.1.store(0, Ordering::SeqCst); + } + fn readdir(&self) -> DirectoryEntry { let pos = self.1.fetch_add(1, Ordering::SeqCst); From ac6a4bc968b064cd020dbb0742d6076c3812be32 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 3 Jan 2024 10:50:24 +0000 Subject: [PATCH 10/32] fix bug in accept - listen should be not reinitiate - remove some error messages - revise Result of accept, returns () is everthing is Ok --- src/fd/mod.rs | 6 ++---- src/fd/socket/mod.rs | 10 +++------- src/fd/socket/tcp.rs | 13 +++++-------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index cc46be0901..5ae4243487 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -132,7 +132,7 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// `accept` a connection on a socket #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn accept(&self, _addr: *mut sockaddr, _addrlen: *mut socklen_t) -> Result { + fn accept(&self, _addr: *mut sockaddr, _addrlen: *mut socklen_t) -> Result<(), IoError> { Err(IoError::EINVAL) } @@ -236,9 +236,7 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { } // close a file descriptor - fn close(&self) { - trace!("close file descriptor"); - } + fn close(&self) {} } pub(crate) fn open(name: &str, flags: i32, mode: i32) -> Result { diff --git a/src/fd/socket/mod.rs b/src/fd/socket/mod.rs index 0fcf799e4a..63798ddfd4 100644 --- a/src/fd/socket/mod.rs +++ b/src/fd/socket/mod.rs @@ -92,13 +92,9 @@ pub(crate) extern "C" fn __sys_accept( let new_obj = dyn_clone::clone_box(&*v); insert_object(fd, Arc::from(new_obj)); let new_fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); - match (*v).listen(1) { - Ok(_) => { - insert_object(new_fd, v.clone()); - new_fd - } - Err(e) => -num::ToPrimitive::to_i32(&e).unwrap(), - } + insert_object(new_fd, v.clone()); + + new_fd }, ) }, diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index 307ea91b1c..eb0c5114ca 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -144,10 +144,7 @@ impl Socket { async fn async_connect(&self, address: IpAddress, port: u16) -> Result { self.with_context(|socket, cx| socket.connect(cx, (address, port), get_ephemeral_port())) - .map_err(|x| { - info!("x {:?}", x); - IoError::EIO - })?; + .map_err(|x| IoError::EIO)?; future::poll_fn(|cx| { self.with(|socket| match socket.state() { @@ -248,8 +245,8 @@ impl Socket { Ok(()) } - fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> Result { - block_on(self.async_accept(addr, addrlen), None).map(|_| 0) + fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> Result<(), IoError> { + block_on(self.async_accept(addr, addrlen), None) } fn read(&self, buf: &mut [u8]) -> Result { @@ -513,7 +510,7 @@ impl ObjectInterface for Socket { } } - fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> Result { + fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> Result<(), IoError> { self.accept(addr, addrlen) } @@ -660,7 +657,7 @@ impl ObjectInterface for Socket { } } - fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> Result { + fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> Result<(), IoError> { self.accept(addr, addrlen) } From db0e0971f10ff6591c07ee7e1210a95cbdeb6c21 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 3 Jan 2024 10:58:31 +0000 Subject: [PATCH 11/32] remove clippy warnings --- src/fd/socket/tcp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index eb0c5114ca..0bff5187c9 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -144,7 +144,7 @@ impl Socket { async fn async_connect(&self, address: IpAddress, port: u16) -> Result { self.with_context(|socket, cx| socket.connect(cx, (address, port), get_ephemeral_port())) - .map_err(|x| IoError::EIO)?; + .map_err(|_| IoError::EIO)?; future::poll_fn(|cx| { self.with(|socket| match socket.state() { From b838a7f45b913f8e2aaa8991e5beba79b18b1f03 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 3 Jan 2024 15:20:11 +0000 Subject: [PATCH 12/32] revise socket interface - interface based now on data types from smoltcp - all legacy support is move to the system call interface - reduce the usage of raw pointers --- src/fd/mod.rs | 59 ++----- src/fd/socket/mod.rs | 270 ++++++++++++++++++++++------- src/fd/socket/tcp.rs | 399 ++++++------------------------------------ src/fd/socket/udp.rs | 401 +++++-------------------------------------- src/syscalls/net.rs | 128 +++++++++++++- 5 files changed, 447 insertions(+), 810 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 5ae4243487..904f4f0b72 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -5,13 +5,13 @@ use core::sync::atomic::{AtomicI32, Ordering}; use ahash::RandomState; use dyn_clone::DynClone; use hashbrown::HashMap; +#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] +use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use crate::env; use crate::errno::*; use crate::fd::stdio::*; use crate::fs::{self, FileAttr, SeekWhence}; -#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] -use crate::syscalls::net::*; #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] pub(crate) mod socket; @@ -39,6 +39,12 @@ pub(crate) enum IoError { ENOTDIR = crate::errno::ENOTDIR as isize, EMFILE = crate::errno::EMFILE as isize, EEXIST = crate::errno::EEXIST as isize, + EADDRINUSE = crate::errno::EADDRINUSE as isize, +} + +#[derive(Debug, PartialEq)] +pub(crate) enum SocketOption { + TcpNoDelay, } pub(crate) type FileDescriptor = i32; @@ -132,19 +138,19 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// `accept` a connection on a socket #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn accept(&self, _addr: *mut sockaddr, _addrlen: *mut socklen_t) -> Result<(), IoError> { + fn accept(&self) -> Result { Err(IoError::EINVAL) } /// initiate a connection on a socket #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn connect(&self, _name: *const sockaddr, _namelen: socklen_t) -> Result { + fn connect(&self, _endpoint: IpEndpoint) -> Result<(), IoError> { Err(IoError::EINVAL) } /// `bind` a name to a socket #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn bind(&self, _name: *const sockaddr, _namelen: socklen_t) -> Result<(), IoError> { + fn bind(&self, _name: IpListenEndpoint) -> Result<(), IoError> { Err(IoError::EINVAL) } @@ -156,53 +162,31 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// `setsockopt` sets options on sockets #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn setsockopt( - &self, - _level: i32, - _optname: i32, - _optval: *const c_void, - _optlen: socklen_t, - ) -> Result<(), IoError> { + fn setsockopt(&self, _opt: SocketOption, _optval: bool) -> Result<(), IoError> { Err(IoError::EINVAL) } /// `getsockopt` gets options on sockets #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn getsockopt( - &self, - _level: i32, - _option_name: i32, - _optval: *mut c_void, - _optlen: *mut socklen_t, - ) -> Result<(), IoError> { + fn getsockopt(&self, _opt: SocketOption) -> Result { Err(IoError::EINVAL) } /// `getsockname` gets socket name #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn getsockname(&self, _name: *mut sockaddr, _namelen: *mut socklen_t) -> Result<(), IoError> { - Err(IoError::EINVAL) + fn getsockname(&self) -> Option { + None } /// `getpeername` get address of connected peer #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn getpeername(&self, _name: *mut sockaddr, _namelen: *mut socklen_t) -> Result<(), IoError> { - Err(IoError::EINVAL) + fn getpeername(&self) -> Option { + None } /// receive a message from a socket - /// - /// If `address` is not a null pointer, the source address of the message is filled in. The - /// `address_len` argument is a value-result argument, initialized to the size - /// of the buffer associated with address, and modified on return to - /// indicate the actual size of the address stored there. #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn recvfrom( - &self, - _buffer: &mut [u8], - _address: *mut sockaddr, - _address_len: *mut socklen_t, - ) -> Result { + fn recvfrom(&self, _buffer: &mut [u8]) -> Result<(isize, IpEndpoint), IoError> { Err(IoError::ENOSYS) } @@ -214,12 +198,7 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// be sent to the address specified by dest_addr (overriding the pre-specified peer /// address). #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn sendto( - &self, - _buffer: &[u8], - _addr: *const sockaddr, - _addr_len: socklen_t, - ) -> Result { + fn sendto(&self, _buffer: &[u8], _endpoint: IpEndpoint) -> Result { Err(IoError::ENOSYS) } diff --git a/src/fd/socket/mod.rs b/src/fd/socket/mod.rs index 63798ddfd4..2fb2efea3d 100644 --- a/src/fd/socket/mod.rs +++ b/src/fd/socket/mod.rs @@ -1,11 +1,14 @@ use alloc::sync::Arc; use core::ffi::c_void; +use core::mem::size_of; use core::ops::DerefMut; use core::sync::atomic::Ordering; +use smoltcp::wire::{IpAddress, IpEndpoint, IpListenEndpoint}; + use crate::errno::*; use crate::executor::network::{NetworkState, NIC}; -use crate::fd::{get_object, insert_object, FD_COUNTER, OBJECT_MAP}; +use crate::fd::{get_object, insert_object, SocketOption, FD_COUNTER, OBJECT_MAP}; use crate::syscalls::net::*; #[cfg(feature = "tcp")] @@ -33,40 +36,22 @@ pub(crate) extern "C" fn __sys_socket(domain: i32, type_: i32, protocol: i32) -> #[cfg(feature = "udp")] if type_ == SOCK_DGRAM { let handle = nic.create_udp_handle().unwrap(); - if domain == AF_INET { - let socket = self::udp::Socket::::new(handle); - if OBJECT_MAP.write().try_insert(fd, Arc::new(socket)).is_err() { - return -EINVAL; - } else { - return fd; - } + let socket = self::udp::Socket::new(handle); + if OBJECT_MAP.write().try_insert(fd, Arc::new(socket)).is_err() { + return -EINVAL; } else { - let socket = self::udp::Socket::::new(handle); - if OBJECT_MAP.write().try_insert(fd, Arc::new(socket)).is_err() { - return -EINVAL; - } else { - return fd; - } + return fd; } } #[cfg(feature = "tcp")] if type_ == SOCK_STREAM { let handle = nic.create_tcp_handle().unwrap(); - if domain == AF_INET { - let socket = self::tcp::Socket::::new(handle); - if OBJECT_MAP.write().try_insert(fd, Arc::new(socket)).is_err() { - return -EINVAL; - } else { - return fd; - } + let socket = self::tcp::Socket::new(handle); + if OBJECT_MAP.write().try_insert(fd, Arc::new(socket)).is_err() { + return -EINVAL; } else { - let socket = self::tcp::Socket::::new(handle); - if OBJECT_MAP.write().try_insert(fd, Arc::new(socket)).is_err() { - return -EINVAL; - } else { - return fd; - } + return fd; } } @@ -86,14 +71,35 @@ pub(crate) extern "C" fn __sys_accept( obj.map_or_else( |e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| { - (*v).accept(addr, addrlen).map_or_else( + (*v).accept().map_or_else( |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |_| { + |endpoint| { let new_obj = dyn_clone::clone_box(&*v); insert_object(fd, Arc::from(new_obj)); let new_fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); insert_object(new_fd, v.clone()); + if !addr.is_null() && !addrlen.is_null() { + let addrlen = unsafe { &mut *addrlen }; + + match endpoint.addr { + IpAddress::Ipv4(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in) }; + *addr = sockaddr_in::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } + } + IpAddress::Ipv6(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in6) }; + *addr = sockaddr_in6::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } + } + } + } + new_fd }, ) @@ -113,38 +119,82 @@ pub(crate) extern "C" fn __sys_listen(fd: i32, backlog: i32) -> i32 { } pub(crate) extern "C" fn __sys_bind(fd: i32, name: *const sockaddr, namelen: socklen_t) -> i32 { + let endpoint = if namelen == size_of::().try_into().unwrap() { + IpListenEndpoint::from(unsafe { *(name as *const sockaddr_in) }) + } else if namelen == size_of::().try_into().unwrap() { + IpListenEndpoint::from(unsafe { *(name as *const sockaddr_in6) }) + } else { + return -crate::errno::EINVAL; + }; + let obj = get_object(fd); obj.map_or_else( |e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| { - (*v).bind(name, namelen) + (*v).bind(endpoint) .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) }, ) } pub(crate) extern "C" fn __sys_connect(fd: i32, name: *const sockaddr, namelen: socklen_t) -> i32 { + let endpoint = if namelen == size_of::().try_into().unwrap() { + IpEndpoint::from(unsafe { *(name as *const sockaddr_in) }) + } else if namelen == size_of::().try_into().unwrap() { + IpEndpoint::from(unsafe { *(name as *const sockaddr_in6) }) + } else { + return -crate::errno::EINVAL; + }; + let obj = get_object(fd); obj.map_or_else( |e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| { - (*v).connect(name, namelen) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| v) + (*v).connect(endpoint) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) }, ) } pub(crate) extern "C" fn __sys_getsockname( fd: i32, - name: *mut sockaddr, - namelen: *mut socklen_t, + addr: *mut sockaddr, + addrlen: *mut socklen_t, ) -> i32 { let obj = get_object(fd); obj.map_or_else( |e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| { - (*v).getsockname(name, namelen) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + if let Some(endpoint) = (*v).getsockname() { + if !addr.is_null() && !addrlen.is_null() { + let addrlen = unsafe { &mut *addrlen }; + + match endpoint.addr { + IpAddress::Ipv4(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in) }; + *addr = sockaddr_in::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } else { + return -crate::errno::EINVAL; + } + } + IpAddress::Ipv6(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in6) }; + *addr = sockaddr_in6::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } else { + return -crate::errno::EINVAL; + } + } + } + } else { + return -crate::errno::EINVAL; + } + } + + 0 }, ) } @@ -161,14 +211,28 @@ pub(crate) extern "C" fn __sys_setsockopt( fd, level, optname ); - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |v| { - (*v).setsockopt(level, optname, optval, optlen) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) - }, - ) + if level == IPPROTO_TCP + && optname == TCP_NODELAY + && optlen == size_of::().try_into().unwrap() + { + if optval.is_null() { + return -crate::errno::EINVAL; + } + + let value = unsafe { *(optval as *const i32) }; + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).setsockopt(SocketOption::TcpNoDelay, value != 0) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) + } else if level == SOL_SOCKET && optname == SO_REUSEADDR { + 0 + } else { + -crate::errno::EINVAL + } } pub(crate) extern "C" fn __sys_getsockopt( @@ -183,27 +247,76 @@ pub(crate) extern "C" fn __sys_getsockopt( fd, level, optname ); - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |v| { - (*v).getsockopt(level, optname, optval, optlen) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) - }, - ) + if level == IPPROTO_TCP && optname == TCP_NODELAY { + if optval.is_null() || optlen.is_null() { + return -crate::errno::EINVAL; + } + + let optval = unsafe { &mut *(optval as *mut i32) }; + let optlen = unsafe { &mut *(optlen as *mut socklen_t) }; + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).getsockopt(SocketOption::TcpNoDelay).map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |value| { + if value { + *optval = 1; + } else { + *optval = 0; + } + *optlen = core::mem::size_of::().try_into().unwrap(); + + 0 + }, + ) + }, + ) + } else { + -crate::errno::EINVAL + } } pub(crate) extern "C" fn __sys_getpeername( fd: i32, - name: *mut sockaddr, - namelen: *mut socklen_t, + addr: *mut sockaddr, + addrlen: *mut socklen_t, ) -> i32 { let obj = get_object(fd); obj.map_or_else( |e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| { - (*v).getpeername(name, namelen) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + if let Some(endpoint) = (*v).getsockname() { + if !addr.is_null() && !addrlen.is_null() { + let addrlen = unsafe { &mut *addrlen }; + + match endpoint.addr { + IpAddress::Ipv4(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in) }; + *addr = sockaddr_in::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } else { + return -crate::errno::EINVAL; + } + } + IpAddress::Ipv6(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in6) }; + *addr = sockaddr_in6::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } else { + return -crate::errno::EINVAL; + } + } + } + } else { + return -crate::errno::EINVAL; + } + } + + 0 }, ) } @@ -250,12 +363,20 @@ pub extern "C" fn __sys_sendto( addr: *const sockaddr, addr_len: socklen_t, ) -> isize { + let endpoint = if addr_len == size_of::().try_into().unwrap() { + IpEndpoint::from(unsafe { *(addr as *const sockaddr_in) }) + } else if addr_len == size_of::().try_into().unwrap() { + IpEndpoint::from(unsafe { *(addr as *const sockaddr_in6) }) + } else { + return (-crate::errno::EINVAL).try_into().unwrap(); + }; let slice = unsafe { core::slice::from_raw_parts(buf, len) }; let obj = get_object(fd); + obj.map_or_else( |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { - (*v).sendto(slice, addr, addr_len) + (*v).sendto(slice, endpoint) .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) }, ) @@ -267,15 +388,44 @@ pub extern "C" fn __sys_recvfrom( len: usize, _flags: i32, addr: *mut sockaddr, - addr_len: *mut socklen_t, + addrlen: *mut socklen_t, ) -> isize { let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; let obj = get_object(fd); obj.map_or_else( |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { - (*v).recvfrom(slice, addr, addr_len) - .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + (*v).recvfrom(slice).map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |(len, endpoint)| { + if !addr.is_null() && !addrlen.is_null() { + let addrlen = unsafe { &mut *addrlen }; + + match endpoint.addr { + IpAddress::Ipv4(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in) }; + *addr = sockaddr_in::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } else { + return (-crate::errno::EINVAL).try_into().unwrap(); + } + } + IpAddress::Ipv6(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in6) }; + *addr = sockaddr_in6::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } else { + return (-crate::errno::EINVAL).try_into().unwrap(); + } + } + } + } + + len + }, + ) }, ) } diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index 0bff5187c9..c8337eb320 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -1,7 +1,5 @@ use core::ffi::c_void; use core::future; -use core::marker::PhantomData; -use core::mem::size_of; use core::ops::DerefMut; use core::sync::atomic::{AtomicBool, AtomicU16, Ordering}; use core::task::Poll; @@ -9,10 +7,10 @@ use core::task::Poll; use smoltcp::iface; use smoltcp::socket::tcp; use smoltcp::time::Duration; -use smoltcp::wire::IpAddress; +use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use crate::executor::network::{block_on, now, poll_on, Handle, NetworkState, NIC}; -use crate::fd::{IoError, ObjectInterface}; +use crate::fd::{IoError, ObjectInterface, SocketOption}; use crate::syscalls::net::*; use crate::DEFAULT_KEEP_ALIVE_INTERVAL; @@ -29,20 +27,18 @@ pub struct IPv4; pub struct IPv6; #[derive(Debug)] -pub struct Socket { +pub struct Socket { handle: Handle, port: AtomicU16, nonblocking: AtomicBool, - phantom: PhantomData, } -impl Socket { +impl Socket { pub fn new(handle: Handle) -> Self { Self { handle, port: AtomicU16::new(0), nonblocking: AtomicBool::new(false), - phantom: PhantomData, } } @@ -142,8 +138,8 @@ impl Socket { Ok(pos.try_into().unwrap()) } - async fn async_connect(&self, address: IpAddress, port: u16) -> Result { - self.with_context(|socket, cx| socket.connect(cx, (address, port), get_ephemeral_port())) + async fn async_connect(&self, endpoint: IpEndpoint) -> Result<(), IoError> { + self.with_context(|socket, cx| socket.connect(cx, endpoint, get_ephemeral_port())) .map_err(|_| IoError::EIO)?; future::poll_fn(|cx| { @@ -154,7 +150,7 @@ impl Socket { socket.register_send_waker(cx.waker()); Poll::Pending } - _ => Poll::Ready(Ok(0)), + _ => Poll::Ready(Ok(())), }) }) .await @@ -197,11 +193,7 @@ impl Socket { .await } - async fn async_accept( - &self, - _addr: *mut sockaddr, - _addrlen: *mut socklen_t, - ) -> Result<(), IoError> { + async fn async_accept(&self) -> Result { future::poll_fn(|cx| { self.with(|socket| match socket.state() { tcp::State::Closed => { @@ -242,11 +234,40 @@ impl Socket { let socket = nic.get_mut_socket::>(self.handle); socket.set_keep_alive(Some(Duration::from_millis(DEFAULT_KEEP_ALIVE_INTERVAL))); + Ok(socket.remote_endpoint().unwrap()) + } +} + +impl ObjectInterface for Socket { + fn bind(&self, endpoint: IpListenEndpoint) -> Result<(), IoError> { + self.port.store(endpoint.port, Ordering::Release); Ok(()) } - fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> Result<(), IoError> { - block_on(self.async_accept(addr, addrlen), None) + fn connect(&self, endpoint: IpEndpoint) -> Result<(), IoError> { + if self.nonblocking.load(Ordering::Acquire) { + block_on(self.async_connect(endpoint), Some(Duration::ZERO)).map_err(|x| { + if x == IoError::ETIME { + IoError::EAGAIN + } else { + x + } + }) + } else { + block_on(self.async_connect(endpoint), None) + } + } + + fn accept(&self) -> Result { + block_on(self.async_accept(), None) + } + + fn getpeername(&self) -> Option { + self.with(|socket| socket.remote_endpoint()) + } + + fn getsockname(&self) -> Option { + self.with(|socket| socket.local_endpoint()) } fn read(&self, buf: &mut [u8]) -> Result { @@ -302,59 +323,25 @@ impl Socket { }) } - fn setsockopt( - &self, - level: i32, - optname: i32, - optval: *const c_void, - optlen: socklen_t, - ) -> Result<(), IoError> { - if level == IPPROTO_TCP - && optname == TCP_NODELAY - && optlen == size_of::().try_into().unwrap() - { - let value = unsafe { *(optval as *const i32) }; + fn setsockopt(&self, opt: SocketOption, optval: bool) -> Result<(), IoError> { + if opt == SocketOption::TcpNoDelay { self.with(|socket| { - socket.set_nagle_enabled(value != 0); - if value == 0 { + socket.set_nagle_enabled(optval); + if optval { socket.set_ack_delay(None); } else { socket.set_ack_delay(Some(Duration::from_millis(10))); } }); Ok(()) - } else if level == SOL_SOCKET && optname == SO_REUSEADDR { - // smoltcp is always able to reuse the addr - Ok(()) } else { Err(IoError::EINVAL) } } - fn getsockopt( - &self, - level: i32, - optname: i32, - optval: *mut c_void, - optlen: *mut socklen_t, - ) -> Result<(), IoError> { - if level == IPPROTO_TCP && optname == TCP_NODELAY { - let optlen = unsafe { &mut *optlen }; - if *optlen >= size_of::().try_into().unwrap() { - let optval = unsafe { &mut *(optval as *mut i32) }; - self.with(|socket| { - if socket.nagle_enabled() { - *optval = 0; - } else { - *optval = 1; - } - }); - *optlen = size_of::().try_into().unwrap(); - - Ok(()) - } else { - Err(IoError::EINVAL) - } + fn getsockopt(&self, opt: SocketOption) -> Result { + if opt == SocketOption::TcpNoDelay { + self.with(|socket| Ok(socket.nagle_enabled())) } else { Err(IoError::EINVAL) } @@ -387,7 +374,7 @@ impl Socket { } } -impl Clone for Socket { +impl Clone for Socket { fn clone(&self) -> Self { let mut guard = NIC.lock(); @@ -401,303 +388,13 @@ impl Clone for Socket { handle, port: AtomicU16::new(self.port.load(Ordering::Acquire)), nonblocking: AtomicBool::new(self.nonblocking.load(Ordering::Acquire)), - phantom: PhantomData, } } } -impl Drop for Socket { +impl Drop for Socket { fn drop(&mut self) { let _ = block_on(self.async_close(), None); NIC.lock().as_nic_mut().unwrap().destroy_socket(self.handle); } } - -impl ObjectInterface for Socket { - fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> Result<(), IoError> { - if namelen == size_of::().try_into().unwrap() { - let addr = unsafe { *(name as *const sockaddr_in) }; - let port = u16::from_be(addr.sin_port); - self.port.store(port, Ordering::Release); - Ok(()) - } else { - Err(IoError::EINVAL) - } - } - - fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> Result { - if namelen == size_of::().try_into().unwrap() { - let saddr = unsafe { *(name as *const sockaddr_in) }; - let port = u16::from_be(saddr.sin_port); - let address = IpAddress::v4( - saddr.sin_addr.s_addr[0], - saddr.sin_addr.s_addr[1], - saddr.sin_addr.s_addr[2], - saddr.sin_addr.s_addr[3], - ); - - if self.nonblocking.load(Ordering::Acquire) { - block_on(self.async_connect(address, port), Some(Duration::ZERO)).map_err(|x| { - if x == IoError::ETIME { - IoError::EAGAIN - } else { - x - } - }) - } else { - block_on(self.async_connect(address, port), None) - } - } else { - Err(IoError::EINVAL) - } - } - - fn getpeername(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> Result<(), IoError> { - if namelen.is_null() { - return Err(IoError::ENOBUFS); - } - - let namelen = unsafe { &mut *namelen }; - if *namelen >= size_of::().try_into().unwrap() { - let mut ret: Result<(), IoError> = Ok(()); - let addr = unsafe { &mut *(name as *mut sockaddr_in) }; - - self.with(|socket| { - if let Some(remote) = socket.remote_endpoint() { - addr.sin_port = remote.port.to_be(); - - if let IpAddress::Ipv4(ip) = remote.addr { - addr.sin_addr.s_addr.copy_from_slice(ip.as_bytes()); - } - } else { - ret = Err(IoError::ENOTCONN); - } - }); - - *namelen = size_of::().try_into().unwrap(); - - ret - } else { - Err(IoError::EINVAL) - } - } - - fn getsockname(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> Result<(), IoError> { - if namelen.is_null() { - return Err(IoError::ENOBUFS); - } - - let namelen = unsafe { &mut *namelen }; - if *namelen >= size_of::().try_into().unwrap() { - let addr = unsafe { &mut *(name as *mut sockaddr_in) }; - addr.sin_family = AF_INET.try_into().unwrap(); - - self.with(|socket| { - if let Some(local) = socket.local_endpoint() { - addr.sin_port = local.port.to_be(); - - if let IpAddress::Ipv4(ip) = local.addr { - addr.sin_addr.s_addr.copy_from_slice(ip.as_bytes()); - } - } - }); - - *namelen = size_of::().try_into().unwrap(); - - Ok(()) - } else { - Err(IoError::EINVAL) - } - } - - fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> Result<(), IoError> { - self.accept(addr, addrlen) - } - - fn read(&self, buf: &mut [u8]) -> Result { - self.read(buf) - } - - fn write(&self, buf: &[u8]) -> Result { - self.write(buf) - } - - fn listen(&self, backlog: i32) -> Result<(), IoError> { - self.listen(backlog) - } - - fn setsockopt( - &self, - level: i32, - optname: i32, - optval: *const c_void, - optlen: socklen_t, - ) -> Result<(), IoError> { - self.setsockopt(level, optname, optval, optlen) - } - - fn getsockopt( - &self, - level: i32, - optname: i32, - optval: *mut c_void, - optlen: *mut socklen_t, - ) -> Result<(), IoError> { - self.getsockopt(level, optname, optval, optlen) - } - - fn shutdown(&self, how: i32) -> Result<(), IoError> { - self.shutdown(how) - } - - fn ioctl(&self, cmd: i32, argp: *mut c_void) -> Result<(), IoError> { - self.ioctl(cmd, argp) - } -} - -impl ObjectInterface for Socket { - fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> Result<(), IoError> { - if namelen == size_of::().try_into().unwrap() { - let addr = unsafe { *(name as *const sockaddr_in6) }; - self.port.store(addr.sin6_port, Ordering::Release); - Ok(()) - } else { - Err(IoError::EINVAL) - } - } - - fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> Result { - if namelen == size_of::().try_into().unwrap() { - let saddr = unsafe { *(name as *const sockaddr_in6) }; - let port = u16::from_be(saddr.sin6_port); - let a0 = ((saddr.sin6_addr.s6_addr[0] as u16) << 8) | saddr.sin6_addr.s6_addr[1] as u16; - let a1 = ((saddr.sin6_addr.s6_addr[2] as u16) << 8) | saddr.sin6_addr.s6_addr[3] as u16; - let a2 = ((saddr.sin6_addr.s6_addr[4] as u16) << 8) | saddr.sin6_addr.s6_addr[5] as u16; - let a3 = ((saddr.sin6_addr.s6_addr[6] as u16) << 8) | saddr.sin6_addr.s6_addr[7] as u16; - let a4 = ((saddr.sin6_addr.s6_addr[8] as u16) << 8) | saddr.sin6_addr.s6_addr[9] as u16; - let a5 = - ((saddr.sin6_addr.s6_addr[10] as u16) << 8) | saddr.sin6_addr.s6_addr[11] as u16; - let a6 = - ((saddr.sin6_addr.s6_addr[12] as u16) << 8) | saddr.sin6_addr.s6_addr[13] as u16; - let a7 = - ((saddr.sin6_addr.s6_addr[14] as u16) << 8) | saddr.sin6_addr.s6_addr[15] as u16; - let address = IpAddress::v6(a0, a1, a2, a3, a4, a5, a6, a7); - - if self.nonblocking.load(Ordering::Acquire) { - block_on(self.async_connect(address, port), Some(Duration::ZERO)).map_err(|x| { - if x == IoError::ETIME { - IoError::EAGAIN - } else { - x - } - }) - } else { - block_on(self.async_connect(address, port), None) - } - } else { - Err(IoError::EINVAL) - } - } - - fn getpeername(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> Result<(), IoError> { - if namelen.is_null() { - return Err(IoError::ENOBUFS); - } - - let namelen = unsafe { &mut *namelen }; - if *namelen >= size_of::().try_into().unwrap() { - let mut ret: Result<(), IoError> = Ok(()); - let addr = unsafe { &mut *(name as *mut sockaddr_in6) }; - - self.with(|socket| { - if let Some(remote) = socket.remote_endpoint() { - addr.sin6_port = remote.port.to_be(); - - if let IpAddress::Ipv6(ip) = remote.addr { - addr.sin6_addr.s6_addr.copy_from_slice(ip.as_bytes()); - } - } else { - ret = Err(IoError::ENOTCONN); - } - }); - - *namelen = size_of::().try_into().unwrap(); - - ret - } else { - Err(IoError::EINVAL) - } - } - - fn getsockname(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> Result<(), IoError> { - if namelen.is_null() { - return Err(IoError::ENOBUFS); - } - - let namelen = unsafe { &mut *namelen }; - if *namelen >= size_of::().try_into().unwrap() { - let addr = unsafe { &mut *(name as *mut sockaddr_in6) }; - addr.sin6_family = AF_INET6.try_into().unwrap(); - - self.with(|socket| { - if let Some(local) = socket.local_endpoint() { - addr.sin6_port = local.port.to_be(); - - if let IpAddress::Ipv6(ip) = local.addr { - addr.sin6_addr.s6_addr.copy_from_slice(ip.as_bytes()); - } - } - }); - - *namelen = size_of::().try_into().unwrap(); - - Ok(()) - } else { - Err(IoError::EINVAL) - } - } - - fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> Result<(), IoError> { - self.accept(addr, addrlen) - } - - fn read(&self, buf: &mut [u8]) -> Result { - self.read(buf) - } - - fn write(&self, buf: &[u8]) -> Result { - self.write(buf) - } - - fn listen(&self, backlog: i32) -> Result<(), IoError> { - self.listen(backlog) - } - - fn setsockopt( - &self, - level: i32, - optname: i32, - optval: *const c_void, - optlen: socklen_t, - ) -> Result<(), IoError> { - self.setsockopt(level, optname, optval, optlen) - } - - fn getsockopt( - &self, - level: i32, - optname: i32, - optval: *mut c_void, - optlen: *mut socklen_t, - ) -> Result<(), IoError> { - self.getsockopt(level, optname, optval, optlen) - } - - fn shutdown(&self, how: i32) -> Result<(), IoError> { - self.shutdown(how) - } - - fn ioctl(&self, cmd: i32, argp: *mut c_void) -> Result<(), IoError> { - self.ioctl(cmd, argp) - } -} diff --git a/src/fd/socket/udp.rs b/src/fd/socket/udp.rs index 6889ae9ca0..411b04589a 100644 --- a/src/fd/socket/udp.rs +++ b/src/fd/socket/udp.rs @@ -1,7 +1,5 @@ use core::ffi::c_void; use core::future; -use core::marker::PhantomData; -use core::mem::size_of; use core::ops::DerefMut; use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Poll; @@ -10,7 +8,7 @@ use crossbeam_utils::atomic::AtomicCell; use smoltcp::socket::udp; use smoltcp::socket::udp::UdpMetadata; use smoltcp::time::Duration; -use smoltcp::wire::{IpAddress, IpEndpoint, IpListenEndpoint, Ipv4Address, Ipv6Address}; +use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use crate::executor::network::{block_on, now, poll_on, Handle, NetworkState, NIC}; use crate::fd::{IoError, ObjectInterface}; @@ -23,20 +21,18 @@ pub struct IPv4; pub struct IPv6; #[derive(Debug)] -pub struct Socket { +pub struct Socket { handle: Handle, nonblocking: AtomicBool, endpoint: AtomicCell>, - phantom: PhantomData, } -impl Socket { +impl Socket { pub fn new(handle: Handle) -> Self { Self { handle, nonblocking: AtomicBool::new(false), endpoint: AtomicCell::new(None), - phantom: PhantomData, } } @@ -91,7 +87,7 @@ impl Socket { .await } - async fn async_recvfrom(&self, buffer: &mut [u8]) -> Result<(isize, UdpMetadata), IoError> { + async fn async_recvfrom(&self, buffer: &mut [u8]) -> Result<(isize, IpEndpoint), IoError> { future::poll_fn(|cx| { self.with(|socket| { if socket.is_open() { @@ -100,14 +96,14 @@ impl Socket { Ok((len, meta)) => match self.endpoint.load() { Some(ep) => { if meta.endpoint == ep { - Poll::Ready(Ok((len.try_into().unwrap(), meta))) + Poll::Ready(Ok((len.try_into().unwrap(), meta.endpoint))) } else { buffer[..len].iter_mut().for_each(|x| *x = 0); socket.register_recv_waker(cx.waker()); Poll::Pending } } - None => Poll::Ready(Ok((len.try_into().unwrap(), meta))), + None => Poll::Ready(Ok((len.try_into().unwrap(), meta.endpoint))), }, _ => Poll::Ready(Err(IoError::EIO)), } @@ -145,6 +141,45 @@ impl Socket { }) .await } +} + +impl ObjectInterface for Socket { + fn bind(&self, endpoint: IpListenEndpoint) -> Result<(), IoError> { + self.with(|socket| socket.bind(endpoint).map_err(|_| IoError::EADDRINUSE)) + } + + fn connect(&self, endpoint: IpEndpoint) -> Result<(), IoError> { + self.endpoint.store(Some(endpoint)); + Ok(()) + } + + fn sendto(&self, buf: &[u8], endpoint: IpEndpoint) -> Result { + let meta = UdpMetadata::from(endpoint); + + if self.nonblocking.load(Ordering::Acquire) { + poll_on(self.async_write(buf, &meta), Some(Duration::ZERO)) + } else { + poll_on(self.async_write(buf, &meta), None) + } + } + + fn recvfrom(&self, buf: &mut [u8]) -> Result<(isize, IpEndpoint), IoError> { + if self.nonblocking.load(Ordering::Acquire) { + poll_on(self.async_recvfrom(buf), Some(Duration::ZERO)).map_err(|x| { + if x == IoError::ETIME { + IoError::EAGAIN + } else { + x + } + }) + } else { + match poll_on(self.async_recvfrom(buf), Some(Duration::from_secs(2))) { + Err(IoError::ETIME) => block_on(self.async_recvfrom(buf), None), + Err(x) => Err(x), + Ok(x) => Ok(x), + } + } + } fn read(&self, buf: &mut [u8]) -> Result { if buf.len() == 0 { @@ -205,7 +240,7 @@ impl Socket { } } -impl Clone for Socket { +impl Clone for Socket { fn clone(&self) -> Self { let mut guard = NIC.lock(); @@ -219,354 +254,12 @@ impl Clone for Socket { handle, nonblocking: AtomicBool::new(self.nonblocking.load(Ordering::Acquire)), endpoint: AtomicCell::new(self.endpoint.load()), - phantom: PhantomData, } } } -impl Drop for Socket { +impl Drop for Socket { fn drop(&mut self) { let _ = block_on(self.async_close(), None); } } - -impl ObjectInterface for Socket { - fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> Result<(), IoError> { - if namelen == size_of::().try_into().unwrap() { - let addr = unsafe { *(name as *const sockaddr_in) }; - let s_addr = addr.sin_addr.s_addr; - let port = u16::from_be(addr.sin_port); - let endpoint = if s_addr.into_iter().all(|b| b == 0) { - IpListenEndpoint { addr: None, port } - } else { - IpListenEndpoint { - addr: Some(IpAddress::v4(s_addr[0], s_addr[1], s_addr[2], s_addr[3])), - port, - } - }; - self.with(|socket| { - if !socket.is_open() { - let _ = socket.bind(endpoint).unwrap(); - debug!("{:?}", endpoint); - } - }); - - Ok(()) - } else { - Err(IoError::EINVAL) - } - } - - fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> Result { - if namelen == size_of::().try_into().unwrap() { - let saddr = unsafe { *(name as *const sockaddr_in) }; - let port = u16::from_be(saddr.sin_port); - let address = IpAddress::v4( - saddr.sin_addr.s_addr[0], - saddr.sin_addr.s_addr[1], - saddr.sin_addr.s_addr[2], - saddr.sin_addr.s_addr[3], - ); - - self.endpoint.store(Some(IpEndpoint::new(address, port))); - - Ok(0) - } else { - Err(IoError::EINVAL) - } - } - - fn read(&self, buf: &mut [u8]) -> Result { - self.read(buf) - } - - fn write(&self, buf: &[u8]) -> Result { - self.write(buf) - } - - fn sendto( - &self, - buf: &[u8], - addr: *const sockaddr, - addr_len: socklen_t, - ) -> Result { - if addr.is_null() || addr_len == 0 { - self.write(buf) - } else { - if addr_len >= size_of::().try_into().unwrap() { - let addr = unsafe { &*(addr as *const sockaddr_in) }; - let ip = IpAddress::from(Ipv4Address::from_bytes(&addr.sin_addr.s_addr[0..])); - let endpoint = IpEndpoint::new(ip, u16::from_be(addr.sin_port)); - self.endpoint.store(Some(endpoint)); - let meta = UdpMetadata::from(endpoint); - - if self.nonblocking.load(Ordering::Acquire) { - poll_on(self.async_write(buf, &meta), Some(Duration::ZERO)).map_err(|x| { - if x == IoError::ETIME { - IoError::EAGAIN - } else { - x - } - }) - } else { - poll_on(self.async_write(buf, &meta), None) - } - } else { - Err(IoError::EINVAL) - } - } - } - - fn recvfrom( - &self, - buf: &mut [u8], - address: *mut sockaddr, - address_len: *mut socklen_t, - ) -> Result { - if !address_len.is_null() { - let len = unsafe { &mut *address_len }; - if *len < size_of::().try_into().unwrap() { - return Err(IoError::EINVAL); - } - } - - if buf.len() == 0 { - return Err(IoError::EINVAL); - } - - if self.nonblocking.load(Ordering::Acquire) { - match poll_on(self.async_recvfrom(buf), Some(Duration::ZERO)) { - Err(IoError::ETIME) => Err(IoError::EAGAIN), - Err(e) => Err(e), - Ok((x, meta)) => { - let len = unsafe { &mut *address_len }; - if address.is_null() { - *len = 0; - } else { - let addr = unsafe { &mut *(address as *mut sockaddr_in) }; - addr.sin_port = meta.endpoint.port.to_be(); - if let IpAddress::Ipv4(ip) = meta.endpoint.addr { - addr.sin_addr.s_addr.copy_from_slice(ip.as_bytes()); - } - *len = size_of::().try_into().unwrap(); - } - Ok(x) - } - } - } else { - match poll_on(self.async_recvfrom(buf), Some(Duration::from_secs(2))) { - Err(IoError::ETIME) => block_on(self.async_recvfrom(buf), None).map(|(x, meta)| { - let len = unsafe { &mut *address_len }; - if address.is_null() { - *len = 0; - } else { - let addr = unsafe { &mut *(address as *mut sockaddr_in) }; - addr.sin_port = meta.endpoint.port.to_be(); - if let IpAddress::Ipv4(ip) = meta.endpoint.addr { - addr.sin_addr.s_addr.copy_from_slice(ip.as_bytes()); - } - *len = size_of::().try_into().unwrap(); - } - x - }), - Err(e) => Err(e), - Ok((x, meta)) => { - let len = unsafe { &mut *address_len }; - if address.is_null() { - *len = 0; - } else { - let addr = unsafe { &mut *(address as *mut sockaddr_in) }; - addr.sin_port = meta.endpoint.port.to_be(); - if let IpAddress::Ipv4(ip) = meta.endpoint.addr { - addr.sin_addr.s_addr.copy_from_slice(ip.as_bytes()); - } - *len = size_of::().try_into().unwrap(); - } - Ok(x) - } - } - } - } -} - -impl ObjectInterface for Socket { - fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> Result<(), IoError> { - if namelen == size_of::().try_into().unwrap() { - let addr = unsafe { *(name as *const sockaddr_in6) }; - let s6_addr = addr.sin6_addr.s6_addr; - let port = u16::from_be(addr.sin6_port); - let endpoint = if s6_addr.into_iter().all(|b| b == 0) { - IpListenEndpoint { addr: None, port } - } else { - let addr = IpAddress::v6( - u16::from_ne_bytes(s6_addr[0..1].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[2..3].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[4..5].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[6..7].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[8..9].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[10..11].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[12..13].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[14..15].try_into().unwrap()), - ); - - IpListenEndpoint { - addr: Some(addr), - port, - } - }; - self.with(|socket| { - if !socket.is_open() { - let _ = socket.bind(endpoint).unwrap(); - } - }); - - Ok(()) - } else { - Err(IoError::EINVAL) - } - } - - fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> Result { - if namelen == size_of::().try_into().unwrap() { - let saddr = unsafe { *(name as *const sockaddr_in6) }; - let s6_addr = saddr.sin6_addr.s6_addr; - let port = u16::from_be(saddr.sin6_port); - let address = IpAddress::v6( - u16::from_ne_bytes(s6_addr[0..1].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[2..3].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[4..5].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[6..7].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[8..9].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[10..11].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[12..13].try_into().unwrap()), - u16::from_ne_bytes(s6_addr[14..15].try_into().unwrap()), - ); - - self.endpoint.store(Some(IpEndpoint::new(address, port))); - - Ok(0) - } else { - Err(IoError::EINVAL) - } - } - - fn read(&self, buf: &mut [u8]) -> Result { - self.read(buf) - } - - fn sendto( - &self, - buf: &[u8], - addr: *const sockaddr, - addr_len: socklen_t, - ) -> Result { - if addr.is_null() || addr_len == 0 { - self.write(buf) - } else { - if addr_len >= size_of::().try_into().unwrap() { - let addr = unsafe { &*(addr as *const sockaddr_in6) }; - let ip = IpAddress::from(Ipv6Address::from_bytes(&addr.sin6_addr.s6_addr[0..])); - let endpoint = IpEndpoint::new(ip, u16::from_be(addr.sin6_port)); - self.endpoint.store(Some(endpoint)); - let meta = UdpMetadata::from(endpoint); - - if self.nonblocking.load(Ordering::Acquire) { - poll_on(self.async_write(buf, &meta), Some(Duration::ZERO)).map_err(|x| { - if x == IoError::ETIME { - IoError::EAGAIN - } else { - x - } - }) - } else { - poll_on(self.async_write(buf, &meta), None) - } - } else { - Err(IoError::EINVAL) - } - } - } - - fn recvfrom( - &self, - buf: &mut [u8], - address: *mut sockaddr, - address_len: *mut socklen_t, - ) -> Result { - if !address_len.is_null() { - let len = unsafe { &mut *address_len }; - if *len < size_of::().try_into().unwrap() { - return Err(IoError::EINVAL); - } - } - - if buf.len() == 0 { - return Err(IoError::EINVAL); - } - - if self.nonblocking.load(Ordering::Acquire) { - poll_on(self.async_recvfrom(buf), Some(Duration::ZERO)) - .map_err(|x| { - if x == IoError::ETIME { - IoError::EAGAIN - } else { - x - } - }) - .map(|(x, meta)| { - let len = unsafe { &mut *address_len }; - if address.is_null() { - *len = 0; - } else { - let addr = unsafe { &mut *(address as *mut sockaddr_in6) }; - addr.sin6_port = meta.endpoint.port.to_be(); - if let IpAddress::Ipv6(ip) = meta.endpoint.addr { - addr.sin6_addr.s6_addr.copy_from_slice(ip.as_bytes()); - } - *len = size_of::().try_into().unwrap(); - } - x - }) - } else { - match poll_on(self.async_recvfrom(buf), Some(Duration::from_secs(2))) { - Err(IoError::ETIME) => block_on(self.async_recvfrom(buf), None).map(|(x, meta)| { - let len = unsafe { &mut *address_len }; - if address.is_null() { - *len = 0; - } else { - let addr = unsafe { &mut *(address as *mut sockaddr_in6) }; - addr.sin6_port = meta.endpoint.port.to_be(); - if let IpAddress::Ipv6(ip) = meta.endpoint.addr { - addr.sin6_addr.s6_addr.copy_from_slice(ip.as_bytes()); - } - *len = size_of::().try_into().unwrap(); - } - x - }), - Err(e) => Err(e), - Ok((x, meta)) => { - let len = unsafe { &mut *address_len }; - if address.is_null() { - *len = 0; - } else { - let addr = unsafe { &mut *(address as *mut sockaddr_in6) }; - addr.sin6_port = meta.endpoint.port.to_be(); - if let IpAddress::Ipv6(ip) = meta.endpoint.addr { - addr.sin6_addr.s6_addr.copy_from_slice(ip.as_bytes()); - } - *len = size_of::().try_into().unwrap(); - } - Ok(x) - } - } - } - } - - fn write(&self, buf: &[u8]) -> Result { - self.write(buf) - } - - fn ioctl(&self, cmd: i32, argp: *mut c_void) -> Result<(), IoError> { - self.ioctl(cmd, argp) - } -} diff --git a/src/syscalls/net.rs b/src/syscalls/net.rs index 4202be0a10..e84f2cd3c9 100644 --- a/src/syscalls/net.rs +++ b/src/syscalls/net.rs @@ -2,6 +2,9 @@ #![allow(nonstandard_style)] use core::ffi::c_void; +#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] +use smoltcp::wire::{IpAddress, IpEndpoint, IpListenEndpoint}; + use crate::fd::socket::*; use crate::syscalls::__sys_write; @@ -47,19 +50,19 @@ pub type in_port_t = u16; pub type time_t = i64; #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone)] pub struct in_addr { pub s_addr: [u8; 4], } #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone)] pub struct in6_addr { pub s6_addr: [u8; 16], } #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone)] pub struct sockaddr { pub sa_len: u8, pub sa_family: sa_family_t, @@ -67,7 +70,7 @@ pub struct sockaddr { } #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone)] pub struct sockaddr_in { pub sin_len: u8, pub sin_family: sa_family_t, @@ -76,8 +79,63 @@ pub struct sockaddr_in { pub sin_zero: [u8; 8], } +#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] +impl From for IpListenEndpoint { + fn from(addr: sockaddr_in) -> IpListenEndpoint { + let port = u16::from_be(addr.sin_port); + if addr.sin_addr.s_addr.into_iter().all(|b| b == 0) { + IpListenEndpoint { addr: None, port } + } else { + let address = IpAddress::v4( + addr.sin_addr.s_addr[0], + addr.sin_addr.s_addr[1], + addr.sin_addr.s_addr[2], + addr.sin_addr.s_addr[3], + ); + + IpListenEndpoint::from((address, port)) + } + } +} + +#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] +impl From for IpEndpoint { + fn from(addr: sockaddr_in) -> IpEndpoint { + let port = u16::from_be(addr.sin_port); + let address = IpAddress::v4( + addr.sin_addr.s_addr[0], + addr.sin_addr.s_addr[1], + addr.sin_addr.s_addr[2], + addr.sin_addr.s_addr[3], + ); + + IpEndpoint::from((address, port)) + } +} + +#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] +impl From for sockaddr_in { + fn from(endpoint: IpEndpoint) -> Self { + match endpoint.addr { + IpAddress::Ipv4(ip) => { + let mut in_addr: in_addr = Default::default(); + in_addr.s_addr.copy_from_slice(ip.as_bytes()); + + Self { + sin_len: core::mem::size_of::().try_into().unwrap(), + sin_port: endpoint.port.to_be(), + sin_family: AF_INET.try_into().unwrap(), + sin_addr: in_addr, + ..Default::default() + } + } + IpAddress::Ipv6(_) => panic!("Unable to convert IPv6 address to sockadd_in"), + } + } +} + #[repr(C)] -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Default, Copy, Clone)] pub struct sockaddr_in6 { pub sin6_family: sa_family_t, pub sin6_port: in_port_t, @@ -86,6 +144,66 @@ pub struct sockaddr_in6 { pub sin6_scope_id: u32, } +#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] +impl From for IpListenEndpoint { + fn from(addr: sockaddr_in6) -> IpListenEndpoint { + let port = u16::from_be(addr.sin6_port); + if addr.sin6_addr.s6_addr.into_iter().all(|b| b == 0) { + IpListenEndpoint { addr: None, port } + } else { + let a0 = ((addr.sin6_addr.s6_addr[0] as u16) << 8) | addr.sin6_addr.s6_addr[1] as u16; + let a1 = ((addr.sin6_addr.s6_addr[2] as u16) << 8) | addr.sin6_addr.s6_addr[3] as u16; + let a2 = ((addr.sin6_addr.s6_addr[4] as u16) << 8) | addr.sin6_addr.s6_addr[5] as u16; + let a3 = ((addr.sin6_addr.s6_addr[6] as u16) << 8) | addr.sin6_addr.s6_addr[7] as u16; + let a4 = ((addr.sin6_addr.s6_addr[8] as u16) << 8) | addr.sin6_addr.s6_addr[9] as u16; + let a5 = ((addr.sin6_addr.s6_addr[10] as u16) << 8) | addr.sin6_addr.s6_addr[11] as u16; + let a6 = ((addr.sin6_addr.s6_addr[12] as u16) << 8) | addr.sin6_addr.s6_addr[13] as u16; + let a7 = ((addr.sin6_addr.s6_addr[14] as u16) << 8) | addr.sin6_addr.s6_addr[15] as u16; + let address = IpAddress::v6(a0, a1, a2, a3, a4, a5, a6, a7); + + IpListenEndpoint::from((address, port)) + } + } +} + +#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] +impl From for IpEndpoint { + fn from(addr: sockaddr_in6) -> IpEndpoint { + let port = u16::from_be(addr.sin6_port); + let a0 = ((addr.sin6_addr.s6_addr[0] as u16) << 8) | addr.sin6_addr.s6_addr[1] as u16; + let a1 = ((addr.sin6_addr.s6_addr[2] as u16) << 8) | addr.sin6_addr.s6_addr[3] as u16; + let a2 = ((addr.sin6_addr.s6_addr[4] as u16) << 8) | addr.sin6_addr.s6_addr[5] as u16; + let a3 = ((addr.sin6_addr.s6_addr[6] as u16) << 8) | addr.sin6_addr.s6_addr[7] as u16; + let a4 = ((addr.sin6_addr.s6_addr[8] as u16) << 8) | addr.sin6_addr.s6_addr[9] as u16; + let a5 = ((addr.sin6_addr.s6_addr[10] as u16) << 8) | addr.sin6_addr.s6_addr[11] as u16; + let a6 = ((addr.sin6_addr.s6_addr[12] as u16) << 8) | addr.sin6_addr.s6_addr[13] as u16; + let a7 = ((addr.sin6_addr.s6_addr[14] as u16) << 8) | addr.sin6_addr.s6_addr[15] as u16; + let address = IpAddress::v6(a0, a1, a2, a3, a4, a5, a6, a7); + + IpEndpoint::from((address, port)) + } +} + +#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] +impl From for sockaddr_in6 { + fn from(endpoint: IpEndpoint) -> Self { + match endpoint.addr { + IpAddress::Ipv6(ip) => { + let mut in6_addr: in6_addr = Default::default(); + in6_addr.s6_addr.copy_from_slice(ip.as_bytes()); + + Self { + sin6_port: endpoint.port.to_be(), + sin6_family: AF_INET6.try_into().unwrap(), + sin6_addr: in6_addr, + ..Default::default() + } + } + IpAddress::Ipv4(_) => panic!("Unable to convert IPv4 address to sockadd_in6"), + } + } +} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct ip_mreq { From fd908eb14f55adf07939d7668a75576a37e8a4ee Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 3 Jan 2024 19:30:47 +0000 Subject: [PATCH 13/32] remove clippy warnings --- src/fd/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 904f4f0b72..a699e4f513 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -42,6 +42,7 @@ pub(crate) enum IoError { EADDRINUSE = crate::errno::EADDRINUSE as isize, } +#[allow(dead_code)] #[derive(Debug, PartialEq)] pub(crate) enum SocketOption { TcpNoDelay, From 7b886b1eb4644df688dacd26c23f89181b576d20 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Wed, 3 Jan 2024 21:18:57 +0000 Subject: [PATCH 14/32] revise interface to enable the nonblocking mode - move the usage of raw pointer in the system call interface --- src/fd/mod.rs | 9 +++++++-- src/fd/socket/tcp.rs | 10 ++++------ src/fd/socket/udp.rs | 11 ++++------- src/syscalls/mod.rs | 26 +++++++++++++++++--------- src/syscalls/net.rs | 1 - 5 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index a699e4f513..1c16363a7f 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -1,5 +1,4 @@ use alloc::sync::Arc; -use core::ffi::c_void; use core::sync::atomic::{AtomicI32, Ordering}; use ahash::RandomState; @@ -48,6 +47,12 @@ pub(crate) enum SocketOption { TcpNoDelay, } +#[allow(dead_code)] +#[derive(Debug, PartialEq)] +pub(crate) enum IoCtl { + NonBlocking, +} + pub(crate) type FileDescriptor = i32; /// Mapping between file descriptor and the referenced object @@ -211,7 +216,7 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// The `ioctl` function manipulates the underlying device parameters of special /// files. - fn ioctl(&self, _cmd: i32, _argp: *mut c_void) -> Result<(), IoError> { + fn ioctl(&self, _cmd: IoCtl, _value: bool) -> Result<(), IoError> { Err(IoError::ENOSYS) } diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index c8337eb320..09474e54b4 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -1,4 +1,3 @@ -use core::ffi::c_void; use core::future; use core::ops::DerefMut; use core::sync::atomic::{AtomicBool, AtomicU16, Ordering}; @@ -10,7 +9,7 @@ use smoltcp::time::Duration; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use crate::executor::network::{block_on, now, poll_on, Handle, NetworkState, NIC}; -use crate::fd::{IoError, ObjectInterface, SocketOption}; +use crate::fd::{IoCtl, IoError, ObjectInterface, SocketOption}; use crate::syscalls::net::*; use crate::DEFAULT_KEEP_ALIVE_INTERVAL; @@ -356,10 +355,9 @@ impl ObjectInterface for Socket { } } - fn ioctl(&self, cmd: i32, argp: *mut c_void) -> Result<(), IoError> { - if cmd == FIONBIO { - let value = unsafe { *(argp as *const i32) }; - if value != 0 { + fn ioctl(&self, cmd: IoCtl, value: bool) -> Result<(), IoError> { + if cmd == IoCtl::NonBlocking { + if value { info!("set device to nonblocking mode"); self.nonblocking.store(true, Ordering::Release); } else { diff --git a/src/fd/socket/udp.rs b/src/fd/socket/udp.rs index 411b04589a..f0b516f9f8 100644 --- a/src/fd/socket/udp.rs +++ b/src/fd/socket/udp.rs @@ -1,4 +1,3 @@ -use core::ffi::c_void; use core::future; use core::ops::DerefMut; use core::sync::atomic::{AtomicBool, Ordering}; @@ -11,8 +10,7 @@ use smoltcp::time::Duration; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use crate::executor::network::{block_on, now, poll_on, Handle, NetworkState, NIC}; -use crate::fd::{IoError, ObjectInterface}; -use crate::syscalls::net::*; +use crate::fd::{IoCtl, IoError, ObjectInterface}; #[derive(Debug)] pub struct IPv4; @@ -222,10 +220,9 @@ impl ObjectInterface for Socket { } } - fn ioctl(&self, cmd: i32, argp: *mut c_void) -> Result<(), IoError> { - if cmd == FIONBIO { - let value = unsafe { *(argp as *const i32) }; - if value != 0 { + fn ioctl(&self, cmd: IoCtl, value: bool) -> Result<(), IoError> { + if cmd == IoCtl::NonBlocking { + if value { info!("set device to nonblocking mode"); self.nonblocking.store(true, Ordering::Release); } else { diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index 8522dc7b7d..a749453929 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -18,7 +18,7 @@ pub use self::system::*; pub use self::tasks::*; pub use self::timer::*; use crate::env; -use crate::fd::{dup_object, get_object, remove_object, DirectoryEntry, FileDescriptor}; +use crate::fd::{dup_object, get_object, remove_object, DirectoryEntry, FileDescriptor, IoCtl}; use crate::fs::{self, FileAttr}; use crate::syscalls::interfaces::SyscallInterface; #[cfg(target_os = "none")] @@ -273,15 +273,23 @@ pub extern "C" fn sys_write(fd: FileDescriptor, buf: *const u8, len: usize) -> i kernel_function!(__sys_write(fd, buf, len)) } +pub const FIONBIO: i32 = 0x8008667eu32 as i32; + extern "C" fn __sys_ioctl(fd: FileDescriptor, cmd: i32, argp: *mut core::ffi::c_void) -> i32 { - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |v| { - (*v).ioctl(cmd, argp) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) - }, - ) + if cmd == FIONBIO { + let value = unsafe { *(argp as *const i32) }; + + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).ioctl(IoCtl::NonBlocking, value != 0) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) + } else { + -crate::errno::EINVAL + } } #[no_mangle] diff --git a/src/syscalls/net.rs b/src/syscalls/net.rs index e84f2cd3c9..d2cbca6434 100644 --- a/src/syscalls/net.rs +++ b/src/syscalls/net.rs @@ -37,7 +37,6 @@ pub const SO_SNDTIMEO: i32 = 4101; pub const SO_LINGER: i32 = 128; pub const TCP_NODELAY: i32 = 1; pub const MSG_PEEK: i32 = 1; -pub const FIONBIO: i32 = 0x8008667eu32 as i32; pub const EAI_NONAME: i32 = -2200; pub const EAI_SERVICE: i32 = -2201; pub const EAI_FAIL: i32 = -2202; From 2bc8a33a1f60841826ef687b73be99d039a203f4 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Thu, 4 Jan 2024 07:10:57 +0000 Subject: [PATCH 15/32] add bitflag CreationMode to specify the mode of new files --- src/fd/mod.rs | 21 ++++++++++++++++++++- src/fs/fuse.rs | 21 ++++++++++++++++----- src/fs/mem.rs | 11 ++++++++--- src/fs/mod.rs | 45 +++++++++++++++++++++++++++++++++++++++------ src/fs/uhyve.rs | 24 +++++++++++++++++------- src/syscalls/mod.rs | 10 ++++++---- 6 files changed, 106 insertions(+), 26 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 1c16363a7f..bc27670f4a 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -80,6 +80,24 @@ bitflags! { } } +bitflags! { + #[derive(Debug, Copy, Clone, Default)] + pub(crate) struct CreationMode: i32 { + const S_IRUSR = 0o400; + const S_IWUSR = 0o200; + const S_IXUSR = 0o100; + const S_IRWXU = 0o700; + const S_IRGRP = 0o040; + const S_IWGRP = 0o020; + const S_IXGRP = 0o010; + const S_IRWXG = 0o070; + const S_IROTH = 0o004; + const S_IWOTH = 0o002; + const S_IXOTH = 0o001; + const S_IRWXO = 0o007; + } +} + #[repr(C)] #[derive(Debug, Copy, Clone, Default)] pub struct Dirent { @@ -234,7 +252,8 @@ pub(crate) fn open(name: &str, flags: i32, mode: i32) -> Result, opt: OpenOption, + mode: CreationMode, ) -> Result, IoError> { let path: String = if components.is_empty() { "/".to_string() @@ -1534,7 +1537,7 @@ impl VfsNode for FuseDirectory { components.iter().map(|v| "/".to_owned() + v).collect() }; - info!("FUSE open: {}, {:?}", path, opt); + info!("FUSE open: {}, {:?} {:?}", path, opt, mode); let file = FuseFileHandle::new(); @@ -1562,7 +1565,11 @@ impl VfsNode for FuseDirectory { file_guard.fuse_fh = Some(unsafe { rsp.rsp.assume_init().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(), 0); + let (cmd, mut rsp) = create_create( + &path, + opt.bits().try_into().unwrap(), + mode.bits().try_into().unwrap(), + ); get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() @@ -1612,13 +1619,17 @@ impl VfsNode for FuseDirectory { Ok(()) } - fn traverse_mkdir(&self, components: &mut Vec<&str>, mode: u32) -> Result<(), IoError> { + fn traverse_mkdir( + &self, + components: &mut Vec<&str>, + mode: CreationMode, + ) -> Result<(), IoError> { let path: String = if components.is_empty() { "/".to_string() } else { components.iter().map(|v| "/".to_owned() + v).collect() }; - let (cmd, mut rsp) = create_mkdir(&path, mode); + let (cmd, mut rsp) = create_mkdir(&path, mode.bits().try_into().unwrap()); get_filesystem_driver() .ok_or(IoError::ENOSYS)? diff --git a/src/fs/mem.rs b/src/fs/mem.rs index c036a16c38..8603196df4 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -21,7 +21,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use hermit_sync::{RwSpinLock, SpinMutex}; -use crate::fd::{DirectoryEntry, Dirent, IoError, ObjectInterface, OpenOption}; +use crate::fd::{CreationMode, DirectoryEntry, Dirent, IoError, ObjectInterface, OpenOption}; use crate::fs::{FileAttr, NodeKind, VfsNode}; #[derive(Debug)] @@ -340,7 +340,11 @@ impl VfsNode for MemDirectory { NodeKind::Directory } - fn traverse_mkdir(&self, components: &mut Vec<&str>, mode: u32) -> Result<(), IoError> { + fn traverse_mkdir( + &self, + components: &mut Vec<&str>, + mode: CreationMode, + ) -> Result<(), IoError> { if let Some(component) = components.pop() { let node_name = String::from(component); @@ -478,6 +482,7 @@ impl VfsNode for MemDirectory { &self, components: &mut Vec<&str>, opt: OpenOption, + mode: CreationMode, ) -> Result, IoError> { if let Some(component) = components.pop() { let node_name = String::from(component); @@ -504,7 +509,7 @@ impl VfsNode for MemDirectory { } if let Some(directory) = self.inner.0.read().get(&node_name) { - return directory.traverse_open(components, opt); + return directory.traverse_open(components, opt, mode); } } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 77b489678a..cf77c5afc9 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -10,7 +10,7 @@ use alloc::vec::Vec; use hermit_sync::OnceCell; use mem::MemDirectory; -use crate::fd::{IoError, ObjectInterface, OpenOption}; +use crate::fd::{CreationMode, IoError, ObjectInterface, OpenOption}; pub(crate) static FILESYSTEM: OnceCell = OnceCell::new(); @@ -34,7 +34,11 @@ pub(crate) trait VfsNode: core::fmt::Debug { } /// Helper function to create a new dirctory node - fn traverse_mkdir(&self, _components: &mut Vec<&str>, _mode: u32) -> Result<(), IoError> { + fn traverse_mkdir( + &self, + _components: &mut Vec<&str>, + _mode: CreationMode, + ) -> Result<(), IoError> { Err(IoError::ENOSYS) } @@ -80,6 +84,7 @@ pub(crate) trait VfsNode: core::fmt::Debug { &self, _components: &mut Vec<&str>, _option: OpenOption, + _mode: CreationMode, ) -> Result, IoError> { Err(IoError::ENOSYS) } @@ -98,14 +103,19 @@ impl Filesystem { } /// Tries to open file at given path. - pub fn open(&self, path: &str, opt: OpenOption) -> Result, IoError> { + pub fn open( + &self, + path: &str, + opt: OpenOption, + mode: CreationMode, + ) -> Result, IoError> { debug!("Open file {} with {:?}", path, opt); let mut components: Vec<&str> = path.split('/').collect(); components.reverse(); components.pop(); - self.root.traverse_open(&mut components, opt) + self.root.traverse_open(&mut components, opt, mode) } /// Unlinks a file given by path @@ -131,7 +141,7 @@ impl Filesystem { } /// Create directory given by path - pub fn mkdir(&self, path: &str, mode: u32) -> Result<(), IoError> { + pub fn mkdir(&self, path: &str, mode: CreationMode) -> Result<(), IoError> { debug!("Create directory {}", path); let mut components: Vec<&str> = path.split('/').collect(); @@ -249,12 +259,35 @@ pub enum SeekWhence { } pub(crate) fn init() { + const VERSION: &str = env!("CARGO_PKG_VERSION"); + FILESYSTEM.set(Filesystem::new()).unwrap(); FILESYSTEM .get() .unwrap() - .mkdir("/tmp", 777) + .mkdir("/tmp", CreationMode::from_bits(0o777).unwrap()) .expect("Unable to create /tmp"); + FILESYSTEM + .get() + .unwrap() + .mkdir("/etc", CreationMode::from_bits(0o777).unwrap()) + .expect("Unable to create /tmp"); + if let Ok(fd) = FILESYSTEM.get().unwrap().open( + "/etc/hostname", + OpenOption::O_CREAT | OpenOption::O_RDWR, + CreationMode::from_bits(0o666).unwrap(), + ) { + let _ret = fd.write(b"Hermit"); + fd.close(); + } + if let Ok(fd) = FILESYSTEM.get().unwrap().open( + "/etc/version", + OpenOption::O_CREAT | OpenOption::O_RDWR, + CreationMode::from_bits(0o666).unwrap(), + ) { + let _ret = fd.write(VERSION.as_bytes()); + fd.close(); + } #[cfg(all(feature = "fuse", feature = "pci"))] fuse::init(); diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index 47b24477a4..b6a952a122 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -13,7 +13,9 @@ use x86::io::outl; use crate::arch::mm::{paging, PhysAddr, VirtAddr}; use crate::env::is_uhyve; use crate::fd::IoError; -use crate::fs::{self, FileAttr, NodeKind, ObjectInterface, OpenOption, SeekWhence, VfsNode}; +use crate::fs::{ + self, CreationMode, FileAttr, NodeKind, ObjectInterface, OpenOption, SeekWhence, VfsNode, +}; /// forward a request to the hypervisor uhyve #[inline] @@ -168,7 +170,7 @@ impl UhyveFileHandleInner { if sysread.ret >= 0 { Ok(sysread.ret) } else { - Err(num::FromPrimitive::from_isize(sysread.ret).unwrap()) + Err(IoError::EIO) } } @@ -261,20 +263,24 @@ impl VfsNode for UhyveDirectory { &self, components: &mut Vec<&str>, opt: OpenOption, + mode: CreationMode, ) -> Result, IoError> { let path: String = if components.is_empty() { "/\0".to_string() } else { - components.iter().map(|v| "/".to_owned() + v).collect() + let mut path: String = components.iter().map(|v| "/".to_owned() + v).collect(); + path.push('\0'); + path.remove(0); + path }; - let mut sysopen = SysOpen::new(VirtAddr(path.as_ptr() as u64), opt.bits(), 0); + let mut sysopen = SysOpen::new(VirtAddr(path.as_ptr() as u64), opt.bits(), mode.bits()); uhyve_send(UHYVE_PORT_OPEN, &mut sysopen); if sysopen.ret > 0 { Ok(Arc::new(UhyveFileHandle::new(sysopen.ret))) } else { - Err(num::FromPrimitive::from_i32(sysopen.ret).unwrap()) + Err(IoError::EIO) } } @@ -291,7 +297,7 @@ impl VfsNode for UhyveDirectory { if sysunlink.ret == 0 { Ok(()) } else { - Err(num::FromPrimitive::from_i32(sysunlink.ret).unwrap()) + Err(IoError::EIO) } } @@ -299,7 +305,11 @@ impl VfsNode for UhyveDirectory { Err(IoError::ENOSYS) } - fn traverse_mkdir(&self, _components: &mut Vec<&str>, _mode: u32) -> Result<(), IoError> { + fn traverse_mkdir( + &self, + _components: &mut Vec<&str>, + _mode: CreationMode, + ) -> Result<(), IoError> { Err(IoError::ENOSYS) } } diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index a749453929..f70c1c4819 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -18,7 +18,9 @@ pub use self::system::*; pub use self::tasks::*; pub use self::timer::*; use crate::env; -use crate::fd::{dup_object, get_object, remove_object, DirectoryEntry, FileDescriptor, IoCtl}; +use crate::fd::{ + dup_object, get_object, remove_object, CreationMode, DirectoryEntry, FileDescriptor, IoCtl, +}; use crate::fs::{self, FileAttr}; use crate::syscalls::interfaces::SyscallInterface; #[cfg(target_os = "none")] @@ -115,18 +117,18 @@ 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 { +extern "C" fn __sys_mkdir(name: *const u8, mode: i32) -> i32 { let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); fs::FILESYSTEM .get() .unwrap() - .mkdir(name, mode) + .mkdir(name, CreationMode::from_bits(mode).unwrap()) .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) } #[no_mangle] -pub extern "C" fn sys_mkdir(name: *const u8, mode: u32) -> i32 { +pub extern "C" fn sys_mkdir(name: *const u8, mode: i32) -> i32 { kernel_function!(__sys_mkdir(name, mode)) } From aeb501615b8b522d26e3099027e74edab92fb97a Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Thu, 4 Jan 2024 07:29:52 +0000 Subject: [PATCH 16/32] add option to specify the mount point of uhyve's filesystem --- src/env.rs | 4 ++++ src/fs/uhyve.rs | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/env.rs b/src/env.rs index 4ca18d5558..09d6c0a5ba 100644 --- a/src/env.rs +++ b/src/env.rs @@ -81,6 +81,10 @@ impl Default for Cli { let gateway = expect_arg(words.next(), word.as_str()); env_vars.insert(String::from("HERMIT_GATEWAY"), gateway); } + "-mount" => { + let gateway = expect_arg(words.next(), word.as_str()); + env_vars.insert(String::from("UHYVE_MOUNT"), gateway); + } "--" => args.extend(&mut words), _ if image_path.is_none() => image_path = Some(word), word => panic!( diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index b6a952a122..da3f346843 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -317,12 +317,12 @@ impl VfsNode for UhyveDirectory { pub(crate) fn init() { info!("Try to initialize uhyve filesystem"); if is_uhyve() { - let mount_point = "/host".to_string(); + let mount_point = hermit_var_or!("UHYVE_MOUNT", "/host").to_string(); info!("Mounting virtio-fs at {}", mount_point); fs::FILESYSTEM .get() .unwrap() - .mount(mount_point.as_str(), Box::new(UhyveDirectory::new())) + .mount(&mount_point, Box::new(UhyveDirectory::new())) .expect("Mount failed. Duplicate mount_point?"); } } From 21b9688307ae553aca672cfa98350c7bae4a460d Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Thu, 4 Jan 2024 09:56:08 +0000 Subject: [PATCH 17/32] return an error number if unwrapping of CreationOption fails --- src/fd/mod.rs | 5 ++--- src/syscalls/mod.rs | 7 ++++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index bc27670f4a..752ad03ded 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -252,8 +252,8 @@ pub(crate) fn open(name: &str, flags: i32, mode: i32) -> Result Result Result { debug!("Open directory {}", name); diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index f70c1c4819..ed94d6b54f 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -119,11 +119,16 @@ pub extern "C" fn sys_unlink(name: *const u8) -> i32 { extern "C" fn __sys_mkdir(name: *const u8, mode: i32) -> i32 { let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); + let mode = if let Some(mode) = CreationMode::from_bits(mode) { + mode + } else { + return -crate::errno::EINVAL; + }; fs::FILESYSTEM .get() .unwrap() - .mkdir(name, CreationMode::from_bits(mode).unwrap()) + .mkdir(name, mode) .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) } From baa86cf2b5b1675a8d35e2bb7f0844c9452f734d Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Thu, 4 Jan 2024 17:55:45 +0000 Subject: [PATCH 18/32] create on demand the object interface to the ramdisk - simplifies the handling of the file position --- src/fs/mem.rs | 145 ++++++++++++++++++++++++-------------------------- 1 file changed, 69 insertions(+), 76 deletions(-) diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 8603196df4..36fdd0999c 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -25,19 +25,14 @@ use crate::fd::{CreationMode, DirectoryEntry, Dirent, IoError, ObjectInterface, use crate::fs::{FileAttr, NodeKind, VfsNode}; #[derive(Debug)] -struct RomFileInner { +struct RomFileInterface { /// Position within the file pos: SpinMutex, /// File content data: Arc>, } -impl ObjectInterface for RomFileInner { - fn close(&self) { - trace!("close file"); - *self.pos.lock() = 0; - } - +impl ObjectInterface for RomFileInterface { fn read(&self, buf: &mut [u8]) -> Result { let vec = self.data.read(); let mut pos_guard = self.pos.lock(); @@ -60,11 +55,11 @@ impl ObjectInterface for RomFileInner { } } -impl RomFileInner { - pub unsafe fn new(addr: *const u8, len: usize) -> Self { +impl RomFileInterface { + pub fn new(data: Arc>) -> Self { Self { pos: SpinMutex::new(0), - data: Arc::new(RwSpinLock::new(unsafe { slice::from_raw_parts(addr, len) })), + data, } } @@ -74,29 +69,24 @@ impl RomFileInner { } } -impl Clone for RomFileInner { +impl Clone for RomFileInterface { fn clone(&self) -> Self { - RomFileInner { - pos: SpinMutex::new(0), + Self { + pos: SpinMutex::new(*self.pos.lock()), data: self.data.clone(), } } } #[derive(Debug)] -pub struct RamFileInner { +pub struct RamFileInterface { /// Position within the file pos: SpinMutex, /// File content data: Arc>>, } -impl ObjectInterface for RamFileInner { - fn close(&self) { - trace!("close file"); - *self.pos.lock() = 0; - } - +impl ObjectInterface for RamFileInterface { fn read(&self, buf: &mut [u8]) -> Result { let guard = self.data.read(); let vec = guard.deref(); @@ -136,11 +126,11 @@ impl ObjectInterface for RamFileInner { } } -impl RamFileInner { - pub fn new() -> Self { +impl RamFileInterface { + pub fn new(data: Arc>>) -> Self { Self { pos: SpinMutex::new(0), - data: Arc::new(RwSpinLock::new(Vec::new())), + data, } } @@ -151,17 +141,17 @@ impl RamFileInner { } } -impl Clone for RamFileInner { +impl Clone for RamFileInterface { fn clone(&self) -> Self { - RamFileInner { - pos: SpinMutex::new(0), + Self { + pos: SpinMutex::new(*self.pos.lock()), data: self.data.clone(), } } } #[derive(Debug, Clone)] -pub(crate) struct RomFile(Arc); +pub(crate) struct RomFile(Arc>); impl VfsNode for RomFile { fn get_kind(&self) -> NodeKind { @@ -169,18 +159,20 @@ impl VfsNode for RomFile { } fn get_object(&self) -> Result, IoError> { - Ok(self.0.clone()) + Ok(Arc::new(RomFileInterface::new(self.0.clone()))) } } impl RomFile { pub unsafe fn new(ptr: *const u8, length: usize) -> Self { - Self(Arc::new(unsafe { RomFileInner::new(ptr, length) })) + Self(Arc::new(RwSpinLock::new(unsafe { + slice::from_raw_parts(ptr, length) + }))) } } #[derive(Debug, Clone)] -pub(crate) struct RamFile(Arc); +pub(crate) struct RamFile(Arc>>); impl VfsNode for RamFile { fn get_kind(&self) -> NodeKind { @@ -188,41 +180,44 @@ impl VfsNode for RamFile { } fn get_object(&self) -> Result, IoError> { - Ok(self.0.clone()) + Ok(Arc::new(RamFileInterface::new(self.0.clone()))) } } impl RamFile { pub fn new() -> Self { - Self(Arc::new(RamFileInner::new())) + Self(Arc::new(RwSpinLock::new(Vec::new()))) } } #[derive(Debug)] -struct MemDirectoryInner( - pub Arc< +struct MemDirectoryInterface { + /// Position within the file + pos: AtomicUsize, + /// File content + data: Arc< RwSpinLock>>, >, - AtomicUsize, -); - -impl MemDirectoryInner { - pub fn new() -> Self { - Self( - Arc::new(RwSpinLock::new(BTreeMap::new())), - AtomicUsize::new(0), - ) - } } -impl ObjectInterface for MemDirectoryInner { - fn close(&self) { - trace!("close directory"); - self.1.store(0, Ordering::SeqCst); +impl MemDirectoryInterface { + pub fn new( + data: Arc< + RwSpinLock< + BTreeMap>, + >, + >, + ) -> Self { + Self { + pos: AtomicUsize::new(0), + data, + } } +} +impl ObjectInterface for MemDirectoryInterface { fn readdir(&self) -> DirectoryEntry { - let pos = self.1.fetch_add(1, Ordering::SeqCst); + let pos = self.pos.fetch_add(1, Ordering::SeqCst); if pos == 0 { let name = "."; @@ -269,7 +264,7 @@ impl ObjectInterface for MemDirectoryInner { DirectoryEntry::Valid(raw) } else { - let keys: Vec<_> = self.0.read().keys().cloned().collect(); + let keys: Vec<_> = self.data.read().keys().cloned().collect(); if keys.len() > pos - 2 { let name_len = keys[pos - 2].len(); @@ -299,24 +294,26 @@ impl ObjectInterface for MemDirectoryInner { } } -impl Clone for MemDirectoryInner { +impl Clone for MemDirectoryInterface { fn clone(&self) -> Self { - Self( - self.0.clone(), - AtomicUsize::new(self.1.load(Ordering::Relaxed)), - ) + Self { + pos: AtomicUsize::new(self.pos.load(Ordering::SeqCst)), + data: self.data.clone(), + } } } #[derive(Debug)] pub(crate) struct MemDirectory { - inner: Arc, + inner: Arc< + RwSpinLock>>, + >, } impl MemDirectory { pub fn new() -> Self { Self { - inner: Arc::new(MemDirectoryInner::new()), + inner: Arc::new(RwSpinLock::new(BTreeMap::new())), } } @@ -324,10 +321,7 @@ impl MemDirectory { let name = name.trim(); if name.find('/').is_none() { let file = unsafe { RomFile::new(ptr, length) }; - self.inner - .0 - .write() - .insert(name.to_string(), Box::new(file)); + self.inner.write().insert(name.to_string(), Box::new(file)); Ok(()) } else { Err(IoError::EBADF) @@ -348,13 +342,12 @@ impl VfsNode for MemDirectory { if let Some(component) = components.pop() { let node_name = String::from(component); - if let Some(directory) = self.inner.0.read().get(&node_name) { + if let Some(directory) = self.inner.read().get(&node_name) { return directory.traverse_mkdir(components, mode); } if components.is_empty() { self.inner - .0 .write() .insert(node_name, Box::new(MemDirectory::new())); return Ok(()); @@ -368,12 +361,12 @@ impl VfsNode for MemDirectory { if let Some(component) = components.pop() { let node_name = String::from(component); - if let Some(directory) = self.inner.0.read().get(&node_name) { + if let Some(directory) = self.inner.read().get(&node_name) { return directory.traverse_rmdir(components); } if components.is_empty() { - let mut guard = self.inner.0.write(); + let mut guard = self.inner.write(); let obj = guard.remove(&node_name).ok_or(IoError::ENOENT)?; if obj.get_kind() == NodeKind::Directory { @@ -392,12 +385,12 @@ impl VfsNode for MemDirectory { if let Some(component) = components.pop() { let node_name = String::from(component); - if let Some(directory) = self.inner.0.read().get(&node_name) { + if let Some(directory) = self.inner.read().get(&node_name) { return directory.traverse_unlink(components); } if components.is_empty() { - let mut guard = self.inner.0.write(); + let mut guard = self.inner.write(); let obj = guard.remove(&node_name).ok_or(IoError::ENOENT)?; if obj.get_kind() == NodeKind::Directory { @@ -419,13 +412,13 @@ impl VfsNode for MemDirectory { if let Some(component) = components.pop() { let node_name = String::from(component); - if let Some(directory) = self.inner.0.read().get(&node_name) { + if let Some(directory) = self.inner.read().get(&node_name) { directory.traverse_opendir(components) } else { Err(IoError::EBADF) } } else { - Ok(self.inner.clone()) + Ok(Arc::new(MemDirectoryInterface::new(self.inner.clone()))) } } @@ -433,7 +426,7 @@ impl VfsNode for MemDirectory { if let Some(component) = components.pop() { let node_name = String::from(component); - if let Some(directory) = self.inner.0.read().get(&node_name) { + if let Some(directory) = self.inner.read().get(&node_name) { directory.traverse_lstat(components) } else { Err(IoError::EBADF) @@ -447,7 +440,7 @@ impl VfsNode for MemDirectory { if let Some(component) = components.pop() { let node_name = String::from(component); - if let Some(directory) = self.inner.0.read().get(&node_name) { + if let Some(directory) = self.inner.read().get(&node_name) { directory.traverse_stat(components) } else { Err(IoError::EBADF) @@ -465,12 +458,12 @@ impl VfsNode for MemDirectory { if let Some(component) = components.pop() { let node_name = String::from(component); - if let Some(directory) = self.inner.0.read().get(&node_name) { + if let Some(directory) = self.inner.read().get(&node_name) { return directory.traverse_mount(components, obj); } if components.is_empty() { - self.inner.0.write().insert(node_name, obj); + self.inner.write().insert(node_name, obj); return Ok(()); } } @@ -488,14 +481,14 @@ impl VfsNode for MemDirectory { let node_name = String::from(component); if components.is_empty() { - let mut guard = self.inner.0.write(); + let mut guard = self.inner.write(); if opt.contains(OpenOption::O_CREAT) || opt.contains(OpenOption::O_CREAT) { if guard.get(&node_name).is_some() { return Err(IoError::EEXIST); } else { let file = Box::new(RamFile::new()); guard.insert(node_name, file.clone()); - return Ok(file.0.clone()); + return Ok(Arc::new(RamFileInterface::new(file.0.clone()))); } } else if let Some(file) = guard.get(&node_name) { if file.get_kind() == NodeKind::File { @@ -508,7 +501,7 @@ impl VfsNode for MemDirectory { } } - if let Some(directory) = self.inner.0.read().get(&node_name) { + if let Some(directory) = self.inner.read().get(&node_name) { return directory.traverse_open(components, opt, mode); } } From 631d0dbc8b7c94f8442f385ff8885be362e2e622 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Thu, 4 Jan 2024 18:37:00 +0000 Subject: [PATCH 19/32] rename CreationMode to AccessPermission --- src/fd/mod.rs | 4 ++-- src/fs/fuse.rs | 6 +++--- src/fs/mem.rs | 6 +++--- src/fs/mod.rs | 18 +++++++++--------- src/fs/uhyve.rs | 6 +++--- src/syscalls/mod.rs | 4 ++-- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 752ad03ded..018cf206f9 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -82,7 +82,7 @@ bitflags! { bitflags! { #[derive(Debug, Copy, Clone, Default)] - pub(crate) struct CreationMode: i32 { + pub(crate) struct AccessPermission: i32 { const S_IRUSR = 0o400; const S_IWUSR = 0o200; const S_IXUSR = 0o100; @@ -253,7 +253,7 @@ pub(crate) fn open(name: &str, flags: i32, mode: i32) -> Result, opt: OpenOption, - mode: CreationMode, + mode: AccessPermission, ) -> Result, IoError> { let path: String = if components.is_empty() { "/".to_string() @@ -1622,7 +1622,7 @@ impl VfsNode for FuseDirectory { fn traverse_mkdir( &self, components: &mut Vec<&str>, - mode: CreationMode, + mode: AccessPermission, ) -> Result<(), IoError> { let path: String = if components.is_empty() { "/".to_string() diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 36fdd0999c..06ca4b3a12 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -21,7 +21,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use hermit_sync::{RwSpinLock, SpinMutex}; -use crate::fd::{CreationMode, DirectoryEntry, Dirent, IoError, ObjectInterface, OpenOption}; +use crate::fd::{AccessPermission, DirectoryEntry, Dirent, IoError, ObjectInterface, OpenOption}; use crate::fs::{FileAttr, NodeKind, VfsNode}; #[derive(Debug)] @@ -337,7 +337,7 @@ impl VfsNode for MemDirectory { fn traverse_mkdir( &self, components: &mut Vec<&str>, - mode: CreationMode, + mode: AccessPermission, ) -> Result<(), IoError> { if let Some(component) = components.pop() { let node_name = String::from(component); @@ -475,7 +475,7 @@ impl VfsNode for MemDirectory { &self, components: &mut Vec<&str>, opt: OpenOption, - mode: CreationMode, + mode: AccessPermission, ) -> Result, IoError> { if let Some(component) = components.pop() { let node_name = String::from(component); diff --git a/src/fs/mod.rs b/src/fs/mod.rs index cf77c5afc9..f850cae487 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -10,7 +10,7 @@ use alloc::vec::Vec; use hermit_sync::OnceCell; use mem::MemDirectory; -use crate::fd::{CreationMode, IoError, ObjectInterface, OpenOption}; +use crate::fd::{AccessPermission, IoError, ObjectInterface, OpenOption}; pub(crate) static FILESYSTEM: OnceCell = OnceCell::new(); @@ -37,7 +37,7 @@ pub(crate) trait VfsNode: core::fmt::Debug { fn traverse_mkdir( &self, _components: &mut Vec<&str>, - _mode: CreationMode, + _mode: AccessPermission, ) -> Result<(), IoError> { Err(IoError::ENOSYS) } @@ -84,7 +84,7 @@ pub(crate) trait VfsNode: core::fmt::Debug { &self, _components: &mut Vec<&str>, _option: OpenOption, - _mode: CreationMode, + _mode: AccessPermission, ) -> Result, IoError> { Err(IoError::ENOSYS) } @@ -107,7 +107,7 @@ impl Filesystem { &self, path: &str, opt: OpenOption, - mode: CreationMode, + mode: AccessPermission, ) -> Result, IoError> { debug!("Open file {} with {:?}", path, opt); let mut components: Vec<&str> = path.split('/').collect(); @@ -141,7 +141,7 @@ impl Filesystem { } /// Create directory given by path - pub fn mkdir(&self, path: &str, mode: CreationMode) -> Result<(), IoError> { + pub fn mkdir(&self, path: &str, mode: AccessPermission) -> Result<(), IoError> { debug!("Create directory {}", path); let mut components: Vec<&str> = path.split('/').collect(); @@ -265,17 +265,17 @@ pub(crate) fn init() { FILESYSTEM .get() .unwrap() - .mkdir("/tmp", CreationMode::from_bits(0o777).unwrap()) + .mkdir("/tmp", AccessPermission::from_bits(0o777).unwrap()) .expect("Unable to create /tmp"); FILESYSTEM .get() .unwrap() - .mkdir("/etc", CreationMode::from_bits(0o777).unwrap()) + .mkdir("/etc", AccessPermission::from_bits(0o777).unwrap()) .expect("Unable to create /tmp"); if let Ok(fd) = FILESYSTEM.get().unwrap().open( "/etc/hostname", OpenOption::O_CREAT | OpenOption::O_RDWR, - CreationMode::from_bits(0o666).unwrap(), + AccessPermission::from_bits(0o666).unwrap(), ) { let _ret = fd.write(b"Hermit"); fd.close(); @@ -283,7 +283,7 @@ pub(crate) fn init() { if let Ok(fd) = FILESYSTEM.get().unwrap().open( "/etc/version", OpenOption::O_CREAT | OpenOption::O_RDWR, - CreationMode::from_bits(0o666).unwrap(), + AccessPermission::from_bits(0o666).unwrap(), ) { let _ret = fd.write(VERSION.as_bytes()); fd.close(); diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index da3f346843..c9caa4ca92 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -14,7 +14,7 @@ use crate::arch::mm::{paging, PhysAddr, VirtAddr}; use crate::env::is_uhyve; use crate::fd::IoError; use crate::fs::{ - self, CreationMode, FileAttr, NodeKind, ObjectInterface, OpenOption, SeekWhence, VfsNode, + self, AccessPermission, FileAttr, NodeKind, ObjectInterface, OpenOption, SeekWhence, VfsNode, }; /// forward a request to the hypervisor uhyve @@ -263,7 +263,7 @@ impl VfsNode for UhyveDirectory { &self, components: &mut Vec<&str>, opt: OpenOption, - mode: CreationMode, + mode: AccessPermission, ) -> Result, IoError> { let path: String = if components.is_empty() { "/\0".to_string() @@ -308,7 +308,7 @@ impl VfsNode for UhyveDirectory { fn traverse_mkdir( &self, _components: &mut Vec<&str>, - _mode: CreationMode, + _mode: AccessPermission, ) -> Result<(), IoError> { Err(IoError::ENOSYS) } diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index ed94d6b54f..c845e42a9b 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -19,7 +19,7 @@ pub use self::tasks::*; pub use self::timer::*; use crate::env; use crate::fd::{ - dup_object, get_object, remove_object, CreationMode, DirectoryEntry, FileDescriptor, IoCtl, + dup_object, get_object, remove_object, AccessPermission, DirectoryEntry, FileDescriptor, IoCtl, }; use crate::fs::{self, FileAttr}; use crate::syscalls::interfaces::SyscallInterface; @@ -119,7 +119,7 @@ pub extern "C" fn sys_unlink(name: *const u8) -> i32 { extern "C" fn __sys_mkdir(name: *const u8, mode: i32) -> i32 { let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); - let mode = if let Some(mode) = CreationMode::from_bits(mode) { + let mode = if let Some(mode) = AccessPermission::from_bits(mode) { mode } else { return -crate::errno::EINVAL; From b6869de26f74a1d5f683add9f276ee17ebfe0b67 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Thu, 4 Jan 2024 20:09:49 +0000 Subject: [PATCH 20/32] add basic support of file attributes --- src/fd/mod.rs | 12 +++-- src/fs/fuse.rs | 46 ++++++++--------- src/fs/mem.rs | 117 ++++++++++++++++++++++++++++++++++++++------ src/fs/mod.rs | 20 +++++--- src/fs/uhyve.rs | 4 +- src/syscalls/mod.rs | 8 +-- 6 files changed, 150 insertions(+), 57 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 018cf206f9..8db34177b8 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -81,8 +81,8 @@ bitflags! { } bitflags! { - #[derive(Debug, Copy, Clone, Default)] - pub(crate) struct AccessPermission: i32 { + #[derive(Debug, Copy, Clone)] + pub struct AccessPermission: u32 { const S_IRUSR = 0o400; const S_IWUSR = 0o200; const S_IXUSR = 0o100; @@ -98,6 +98,12 @@ bitflags! { } } +impl Default for AccessPermission { + fn default() -> Self { + AccessPermission::from_bits(0o666).unwrap() + } +} + #[repr(C)] #[derive(Debug, Copy, Clone, Default)] pub struct Dirent { @@ -242,7 +248,7 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { fn close(&self) {} } -pub(crate) fn open(name: &str, flags: i32, mode: i32) -> Result { +pub(crate) fn open(name: &str, flags: i32, mode: u32) -> Result { // mode is 0x777 (0b0111_0111_0111), when flags | O_CREAT, else 0 // flags is bitmask of O_DEC_* defined above. // (taken from rust stdlib/sys hermit target ) diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 6ed885302d..e5137e55fa 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -204,24 +204,24 @@ struct fuse_attr { pub padding: u32, } -impl fuse_attr { - fn to_stat(&self) -> FileAttr { +impl From for FileAttr { + fn from(attr: fuse_attr) -> FileAttr { FileAttr { - st_ino: self.ino, - st_nlink: self.nlink as u64, - st_mode: self.mode, - st_uid: self.uid, - st_gid: self.gid, - st_rdev: self.rdev as u64, - st_size: self.size.try_into().unwrap(), - st_blksize: self.blksize as i64, - st_blocks: self.blocks.try_into().unwrap(), - st_atime: self.atime.try_into().unwrap(), - st_atime_nsec: self.atimensec as i64, - st_mtime: self.mtime.try_into().unwrap(), - st_mtime_nsec: self.atimensec as i64, - st_ctime: self.ctime.try_into().unwrap(), - st_ctime_nsec: self.ctimensec as i64, + 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.try_into().unwrap(), + st_blksize: attr.blksize as i64, + st_blocks: attr.blocks.try_into().unwrap(), + st_atime: attr.atime.try_into().unwrap(), + st_atime_nsec: attr.atimensec as i64, + st_mtime: attr.mtime.try_into().unwrap(), + st_mtime_nsec: attr.atimensec as i64, + st_ctime: attr.ctime.try_into().unwrap(), + st_ctime_nsec: attr.ctimensec as i64, ..Default::default() } } @@ -1498,7 +1498,7 @@ impl VfsNode for FuseDirectory { let attr = rsp.attr; if attr.mode & S_IFMT != S_IFLNK { - Ok(attr.to_stat()) + Ok(FileAttr::from(attr)) } else { let path = readlink(rsp.nodeid)?; let mut components: Vec<&str> = path.split('/').collect(); @@ -1522,7 +1522,7 @@ impl VfsNode for FuseDirectory { .send_command(cmd.as_ref(), rsp.as_mut()); let attr = unsafe { rsp.rsp.assume_init().attr }; - Ok(attr.to_stat()) + Ok(FileAttr::from(attr)) } fn traverse_open( @@ -1565,11 +1565,7 @@ impl VfsNode for FuseDirectory { file_guard.fuse_fh = Some(unsafe { rsp.rsp.assume_init().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().try_into().unwrap(), - ); + let (cmd, mut rsp) = create_create(&path, opt.bits().try_into().unwrap(), mode.bits()); get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() @@ -1629,7 +1625,7 @@ impl VfsNode for FuseDirectory { } else { components.iter().map(|v| "/".to_owned() + v).collect() }; - let (cmd, mut rsp) = create_mkdir(&path, mode.bits().try_into().unwrap()); + let (cmd, mut rsp) = create_mkdir(&path, mode.bits()); get_filesystem_driver() .ok_or(IoError::ENOSYS)? diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 06ca4b3a12..5beeaad9b6 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -151,7 +151,10 @@ impl Clone for RamFileInterface { } #[derive(Debug, Clone)] -pub(crate) struct RomFile(Arc>); +pub(crate) struct RomFile { + data: Arc>, + attr: FileAttr, +} impl VfsNode for RomFile { fn get_kind(&self) -> NodeKind { @@ -159,20 +162,49 @@ impl VfsNode for RomFile { } fn get_object(&self) -> Result, IoError> { - Ok(Arc::new(RomFileInterface::new(self.0.clone()))) + Ok(Arc::new(RomFileInterface::new(self.data.clone()))) + } + + fn get_file_attributes(&self) -> Result { + Ok(self.attr) + } + + fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { + if components.is_empty() { + Ok(self.attr) + } else { + Err(IoError::EBADF) + } + } + + fn traverse_stat(&self, components: &mut Vec<&str>) -> Result { + if components.is_empty() { + Ok(self.attr) + } else { + Err(IoError::EBADF) + } } } impl RomFile { - pub unsafe fn new(ptr: *const u8, length: usize) -> Self { - Self(Arc::new(RwSpinLock::new(unsafe { - slice::from_raw_parts(ptr, length) - }))) + pub unsafe fn new(ptr: *const u8, length: usize, mode: AccessPermission) -> Self { + Self { + data: Arc::new(RwSpinLock::new(unsafe { + slice::from_raw_parts(ptr, length) + })), + attr: FileAttr { + st_mode: mode, + ..Default::default() + }, + } } } #[derive(Debug, Clone)] -pub(crate) struct RamFile(Arc>>); +pub(crate) struct RamFile { + data: Arc>>, + attr: FileAttr, +} impl VfsNode for RamFile { fn get_kind(&self) -> NodeKind { @@ -180,13 +212,39 @@ impl VfsNode for RamFile { } fn get_object(&self) -> Result, IoError> { - Ok(Arc::new(RamFileInterface::new(self.0.clone()))) + Ok(Arc::new(RamFileInterface::new(self.data.clone()))) + } + + fn get_file_attributes(&self) -> Result { + Ok(self.attr) + } + + fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { + if components.is_empty() { + Ok(self.attr) + } else { + Err(IoError::EBADF) + } + } + + fn traverse_stat(&self, components: &mut Vec<&str>) -> Result { + if components.is_empty() { + Ok(self.attr) + } else { + Err(IoError::EBADF) + } } } impl RamFile { - pub fn new() -> Self { - Self(Arc::new(RwSpinLock::new(Vec::new()))) + pub fn new(mode: AccessPermission) -> Self { + Self { + data: Arc::new(RwSpinLock::new(Vec::new())), + attr: FileAttr { + st_mode: mode, + ..Default::default() + }, + } } } @@ -308,19 +366,30 @@ pub(crate) struct MemDirectory { inner: Arc< RwSpinLock>>, >, + attr: FileAttr, } impl MemDirectory { - pub fn new() -> Self { + pub fn new(mode: AccessPermission) -> Self { Self { inner: Arc::new(RwSpinLock::new(BTreeMap::new())), + attr: FileAttr { + st_mode: mode, + ..Default::default() + }, } } - pub fn create_file(&self, name: &str, ptr: *const u8, length: usize) -> Result<(), IoError> { + pub fn create_file( + &self, + name: &str, + ptr: *const u8, + length: usize, + mode: AccessPermission, + ) -> Result<(), IoError> { let name = name.trim(); if name.find('/').is_none() { - let file = unsafe { RomFile::new(ptr, length) }; + let file = unsafe { RomFile::new(ptr, length, mode) }; self.inner.write().insert(name.to_string(), Box::new(file)); Ok(()) } else { @@ -334,6 +403,10 @@ impl VfsNode for MemDirectory { NodeKind::Directory } + fn get_file_attributes(&self) -> Result { + Ok(self.attr) + } + fn traverse_mkdir( &self, components: &mut Vec<&str>, @@ -349,7 +422,7 @@ impl VfsNode for MemDirectory { if components.is_empty() { self.inner .write() - .insert(node_name, Box::new(MemDirectory::new())); + .insert(node_name, Box::new(MemDirectory::new(mode))); return Ok(()); } } @@ -426,6 +499,12 @@ impl VfsNode for MemDirectory { if let Some(component) = components.pop() { let node_name = String::from(component); + if components.is_empty() { + if let Some(node) = self.inner.read().get(&node_name) { + node.get_file_attributes()?; + } + } + if let Some(directory) = self.inner.read().get(&node_name) { directory.traverse_lstat(components) } else { @@ -440,6 +519,12 @@ impl VfsNode for MemDirectory { if let Some(component) = components.pop() { let node_name = String::from(component); + if components.is_empty() { + if let Some(node) = self.inner.read().get(&node_name) { + node.get_file_attributes()?; + } + } + if let Some(directory) = self.inner.read().get(&node_name) { directory.traverse_stat(components) } else { @@ -486,9 +571,9 @@ impl VfsNode for MemDirectory { if guard.get(&node_name).is_some() { return Err(IoError::EEXIST); } else { - let file = Box::new(RamFile::new()); + let file = Box::new(RamFile::new(mode)); guard.insert(node_name, file.clone()); - return Ok(Arc::new(RamFileInterface::new(file.0.clone()))); + return Ok(Arc::new(RamFileInterface::new(file.data.clone()))); } } else if let Some(file) = guard.get(&node_name) { if file.get_kind() == NodeKind::File { diff --git a/src/fs/mod.rs b/src/fs/mod.rs index f850cae487..242c8e6ef2 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -28,6 +28,11 @@ pub(crate) trait VfsNode: core::fmt::Debug { /// Determines the current node type fn get_kind(&self) -> NodeKind; + /// determines the current file attribute + fn get_file_attributes(&self) -> Result { + Err(IoError::ENOSYS) + } + /// Determine the syscall interface fn get_object(&self) -> Result, IoError> { Err(IoError::ENOSYS) @@ -98,7 +103,7 @@ pub(crate) struct Filesystem { impl Filesystem { pub fn new() -> Self { Self { - root: MemDirectory::new(), + root: MemDirectory::new(AccessPermission::from_bits(0o777).unwrap()), } } @@ -210,8 +215,9 @@ impl Filesystem { name: &str, ptr: *const u8, length: usize, + mode: AccessPermission, ) -> Result<(), IoError> { - self.root.create_file(name, ptr, length) + self.root.create_file(name, ptr, length, mode) } } @@ -221,7 +227,7 @@ pub struct FileAttr { pub st_dev: u64, pub st_ino: u64, pub st_nlink: u64, - pub st_mode: u32, + pub st_mode: AccessPermission, pub st_uid: u32, pub st_gid: u32, pub st_rdev: u64, @@ -275,7 +281,7 @@ pub(crate) fn init() { if let Ok(fd) = FILESYSTEM.get().unwrap().open( "/etc/hostname", OpenOption::O_CREAT | OpenOption::O_RDWR, - AccessPermission::from_bits(0o666).unwrap(), + AccessPermission::from_bits(0o644).unwrap(), ) { let _ret = fd.write(b"Hermit"); fd.close(); @@ -283,7 +289,7 @@ pub(crate) fn init() { if let Ok(fd) = FILESYSTEM.get().unwrap().open( "/etc/version", OpenOption::O_CREAT | OpenOption::O_RDWR, - AccessPermission::from_bits(0o666).unwrap(), + AccessPermission::from_bits(0o644).unwrap(), ) { let _ret = fd.write(VERSION.as_bytes()); fd.close(); @@ -294,12 +300,12 @@ pub(crate) fn init() { uhyve::init(); } -pub unsafe fn create_file(name: &str, ptr: *const u8, length: usize) { +pub unsafe fn create_file(name: &str, ptr: *const u8, length: usize, mode: AccessPermission) { unsafe { FILESYSTEM .get() .unwrap() - .create_file(name, ptr, length) + .create_file(name, ptr, length, mode) .expect("Unable to create file from ROM") } } diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index c9caa4ca92..136cbb7d05 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -66,12 +66,12 @@ const UHYVE_PORT_UNLINK: u16 = 0x840; struct SysOpen { name: PhysAddr, flags: i32, - mode: i32, + mode: u32, ret: i32, } impl SysOpen { - fn new(name: VirtAddr, flags: i32, mode: i32) -> SysOpen { + fn new(name: VirtAddr, flags: i32, mode: u32) -> SysOpen { SysOpen { name: paging::virtual_to_physical(name).unwrap(), flags, diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index c845e42a9b..c161ee3b04 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -117,7 +117,7 @@ pub extern "C" fn sys_unlink(name: *const u8) -> i32 { kernel_function!(__sys_unlink(name)) } -extern "C" fn __sys_mkdir(name: *const u8, mode: i32) -> i32 { +extern "C" fn __sys_mkdir(name: *const u8, mode: u32) -> i32 { let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); let mode = if let Some(mode) = AccessPermission::from_bits(mode) { mode @@ -133,7 +133,7 @@ extern "C" fn __sys_mkdir(name: *const u8, mode: i32) -> i32 { } #[no_mangle] -pub extern "C" fn sys_mkdir(name: *const u8, mode: i32) -> i32 { +pub extern "C" fn sys_mkdir(name: *const u8, mode: u32) -> i32 { kernel_function!(__sys_mkdir(name, mode)) } @@ -216,7 +216,7 @@ 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 { +extern "C" fn __sys_open(name: *const u8, flags: i32, mode: u32) -> FileDescriptor { if let Ok(name) = unsafe { CStr::from_ptr(name as _) }.to_str() { crate::fd::open(name, flags, mode) .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| v) @@ -226,7 +226,7 @@ extern "C" fn __sys_open(name: *const u8, flags: i32, mode: i32) -> FileDescript } #[no_mangle] -pub extern "C" fn sys_open(name: *const u8, flags: i32, mode: i32) -> FileDescriptor { +pub extern "C" fn sys_open(name: *const u8, flags: i32, mode: u32) -> FileDescriptor { kernel_function!(__sys_open(name, flags, mode)) } From a44a3733d668213a3fee247e6e983fe3030699ae Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Thu, 4 Jan 2024 21:52:33 +0000 Subject: [PATCH 21/32] move all functions to realize system calls to the directory syscalls --- src/fd/mod.rs | 2 +- src/fd/socket/mod.rs | 431 +------------------------------------------ src/syscalls/net.rs | 408 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 410 insertions(+), 431 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 8db34177b8..c5d6725fa8 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -63,7 +63,7 @@ static OBJECT_MAP: pflock::PFLock::with_hasher(RandomState::with_seeds(0, 0, 0, 0))); /// Atomic counter to determine the next unused file descriptor -static FD_COUNTER: AtomicI32 = AtomicI32::new(3); +pub(crate) static FD_COUNTER: AtomicI32 = AtomicI32::new(3); bitflags! { /// Options for opening files diff --git a/src/fd/socket/mod.rs b/src/fd/socket/mod.rs index 2fb2efea3d..7a41790273 100644 --- a/src/fd/socket/mod.rs +++ b/src/fd/socket/mod.rs @@ -1,431 +1,4 @@ -use alloc::sync::Arc; -use core::ffi::c_void; -use core::mem::size_of; -use core::ops::DerefMut; -use core::sync::atomic::Ordering; - -use smoltcp::wire::{IpAddress, IpEndpoint, IpListenEndpoint}; - -use crate::errno::*; -use crate::executor::network::{NetworkState, NIC}; -use crate::fd::{get_object, insert_object, SocketOption, FD_COUNTER, OBJECT_MAP}; -use crate::syscalls::net::*; - #[cfg(feature = "tcp")] -mod tcp; +pub(crate) mod tcp; #[cfg(feature = "udp")] -mod udp; - -pub(crate) extern "C" fn __sys_socket(domain: i32, type_: i32, protocol: i32) -> i32 { - debug!( - "sys_socket: domain {}, type {}, protocol {}", - domain, type_, protocol - ); - - if (domain != AF_INET && domain != AF_INET6) - || (type_ != SOCK_STREAM && type_ != SOCK_DGRAM) - || protocol != 0 - { - -EINVAL - } else { - let mut guard = NIC.lock(); - - if let NetworkState::Initialized(nic) = guard.deref_mut() { - let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); - - #[cfg(feature = "udp")] - if type_ == SOCK_DGRAM { - let handle = nic.create_udp_handle().unwrap(); - let socket = self::udp::Socket::new(handle); - if OBJECT_MAP.write().try_insert(fd, Arc::new(socket)).is_err() { - return -EINVAL; - } else { - return fd; - } - } - - #[cfg(feature = "tcp")] - if type_ == SOCK_STREAM { - let handle = nic.create_tcp_handle().unwrap(); - let socket = self::tcp::Socket::new(handle); - if OBJECT_MAP.write().try_insert(fd, Arc::new(socket)).is_err() { - return -EINVAL; - } else { - return fd; - } - } - - -EINVAL - } else { - -EINVAL - } - } -} - -pub(crate) extern "C" fn __sys_accept( - fd: i32, - addr: *mut sockaddr, - addrlen: *mut socklen_t, -) -> i32 { - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |v| { - (*v).accept().map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |endpoint| { - let new_obj = dyn_clone::clone_box(&*v); - insert_object(fd, Arc::from(new_obj)); - let new_fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); - insert_object(new_fd, v.clone()); - - if !addr.is_null() && !addrlen.is_null() { - let addrlen = unsafe { &mut *addrlen }; - - match endpoint.addr { - IpAddress::Ipv4(_) => { - if *addrlen >= size_of::().try_into().unwrap() { - let addr = unsafe { &mut *(addr as *mut sockaddr_in) }; - *addr = sockaddr_in::from(endpoint); - *addrlen = size_of::().try_into().unwrap(); - } - } - IpAddress::Ipv6(_) => { - if *addrlen >= size_of::().try_into().unwrap() { - let addr = unsafe { &mut *(addr as *mut sockaddr_in6) }; - *addr = sockaddr_in6::from(endpoint); - *addrlen = size_of::().try_into().unwrap(); - } - } - } - } - - new_fd - }, - ) - }, - ) -} - -pub(crate) extern "C" fn __sys_listen(fd: i32, backlog: i32) -> i32 { - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |v| { - (*v).listen(backlog) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) - }, - ) -} - -pub(crate) extern "C" fn __sys_bind(fd: i32, name: *const sockaddr, namelen: socklen_t) -> i32 { - let endpoint = if namelen == size_of::().try_into().unwrap() { - IpListenEndpoint::from(unsafe { *(name as *const sockaddr_in) }) - } else if namelen == size_of::().try_into().unwrap() { - IpListenEndpoint::from(unsafe { *(name as *const sockaddr_in6) }) - } else { - return -crate::errno::EINVAL; - }; - - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |v| { - (*v).bind(endpoint) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) - }, - ) -} - -pub(crate) extern "C" fn __sys_connect(fd: i32, name: *const sockaddr, namelen: socklen_t) -> i32 { - let endpoint = if namelen == size_of::().try_into().unwrap() { - IpEndpoint::from(unsafe { *(name as *const sockaddr_in) }) - } else if namelen == size_of::().try_into().unwrap() { - IpEndpoint::from(unsafe { *(name as *const sockaddr_in6) }) - } else { - return -crate::errno::EINVAL; - }; - - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |v| { - (*v).connect(endpoint) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) - }, - ) -} - -pub(crate) extern "C" fn __sys_getsockname( - fd: i32, - addr: *mut sockaddr, - addrlen: *mut socklen_t, -) -> i32 { - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |v| { - if let Some(endpoint) = (*v).getsockname() { - if !addr.is_null() && !addrlen.is_null() { - let addrlen = unsafe { &mut *addrlen }; - - match endpoint.addr { - IpAddress::Ipv4(_) => { - if *addrlen >= size_of::().try_into().unwrap() { - let addr = unsafe { &mut *(addr as *mut sockaddr_in) }; - *addr = sockaddr_in::from(endpoint); - *addrlen = size_of::().try_into().unwrap(); - } else { - return -crate::errno::EINVAL; - } - } - IpAddress::Ipv6(_) => { - if *addrlen >= size_of::().try_into().unwrap() { - let addr = unsafe { &mut *(addr as *mut sockaddr_in6) }; - *addr = sockaddr_in6::from(endpoint); - *addrlen = size_of::().try_into().unwrap(); - } else { - return -crate::errno::EINVAL; - } - } - } - } else { - return -crate::errno::EINVAL; - } - } - - 0 - }, - ) -} - -pub(crate) extern "C" fn __sys_setsockopt( - fd: i32, - level: i32, - optname: i32, - optval: *const c_void, - optlen: socklen_t, -) -> i32 { - debug!( - "sys_setsockopt: {}, level {}, optname {}", - fd, level, optname - ); - - if level == IPPROTO_TCP - && optname == TCP_NODELAY - && optlen == size_of::().try_into().unwrap() - { - if optval.is_null() { - return -crate::errno::EINVAL; - } - - let value = unsafe { *(optval as *const i32) }; - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |v| { - (*v).setsockopt(SocketOption::TcpNoDelay, value != 0) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) - }, - ) - } else if level == SOL_SOCKET && optname == SO_REUSEADDR { - 0 - } else { - -crate::errno::EINVAL - } -} - -pub(crate) extern "C" fn __sys_getsockopt( - fd: i32, - level: i32, - optname: i32, - optval: *mut c_void, - optlen: *mut socklen_t, -) -> i32 { - debug!( - "sys_getsockopt: {}, level {}, optname {}", - fd, level, optname - ); - - if level == IPPROTO_TCP && optname == TCP_NODELAY { - if optval.is_null() || optlen.is_null() { - return -crate::errno::EINVAL; - } - - let optval = unsafe { &mut *(optval as *mut i32) }; - let optlen = unsafe { &mut *(optlen as *mut socklen_t) }; - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |v| { - (*v).getsockopt(SocketOption::TcpNoDelay).map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |value| { - if value { - *optval = 1; - } else { - *optval = 0; - } - *optlen = core::mem::size_of::().try_into().unwrap(); - - 0 - }, - ) - }, - ) - } else { - -crate::errno::EINVAL - } -} - -pub(crate) extern "C" fn __sys_getpeername( - fd: i32, - addr: *mut sockaddr, - addrlen: *mut socklen_t, -) -> i32 { - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |v| { - if let Some(endpoint) = (*v).getsockname() { - if !addr.is_null() && !addrlen.is_null() { - let addrlen = unsafe { &mut *addrlen }; - - match endpoint.addr { - IpAddress::Ipv4(_) => { - if *addrlen >= size_of::().try_into().unwrap() { - let addr = unsafe { &mut *(addr as *mut sockaddr_in) }; - *addr = sockaddr_in::from(endpoint); - *addrlen = size_of::().try_into().unwrap(); - } else { - return -crate::errno::EINVAL; - } - } - IpAddress::Ipv6(_) => { - if *addrlen >= size_of::().try_into().unwrap() { - let addr = unsafe { &mut *(addr as *mut sockaddr_in6) }; - *addr = sockaddr_in6::from(endpoint); - *addrlen = size_of::().try_into().unwrap(); - } else { - return -crate::errno::EINVAL; - } - } - } - } else { - return -crate::errno::EINVAL; - } - } - - 0 - }, - ) -} - -pub extern "C" fn __sys_freeaddrinfo(_ai: *mut addrinfo) {} - -pub extern "C" fn __sys_getaddrinfo( - _nodename: *const u8, - _servname: *const u8, - _hints: *const addrinfo, - _res: *mut *mut addrinfo, -) -> i32 { - -EINVAL -} - -pub extern "C" fn __sys_shutdown_socket(fd: i32, how: i32) -> i32 { - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), - |v| { - (*v).shutdown(how) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) - }, - ) -} - -pub extern "C" fn __sys_recv(fd: i32, buf: *mut u8, len: usize) -> isize { - let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_isize(&e).unwrap(), - |v| { - (*v).read(slice) - .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) - }, - ) -} - -pub extern "C" fn __sys_sendto( - fd: i32, - buf: *const u8, - len: usize, - _flags: i32, - addr: *const sockaddr, - addr_len: socklen_t, -) -> isize { - let endpoint = if addr_len == size_of::().try_into().unwrap() { - IpEndpoint::from(unsafe { *(addr as *const sockaddr_in) }) - } else if addr_len == size_of::().try_into().unwrap() { - IpEndpoint::from(unsafe { *(addr as *const sockaddr_in6) }) - } else { - return (-crate::errno::EINVAL).try_into().unwrap(); - }; - let slice = unsafe { core::slice::from_raw_parts(buf, len) }; - let obj = get_object(fd); - - obj.map_or_else( - |e| -num::ToPrimitive::to_isize(&e).unwrap(), - |v| { - (*v).sendto(slice, endpoint) - .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) - }, - ) -} - -pub extern "C" fn __sys_recvfrom( - fd: i32, - buf: *mut u8, - len: usize, - _flags: i32, - addr: *mut sockaddr, - addrlen: *mut socklen_t, -) -> isize { - let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; - let obj = get_object(fd); - obj.map_or_else( - |e| -num::ToPrimitive::to_isize(&e).unwrap(), - |v| { - (*v).recvfrom(slice).map_or_else( - |e| -num::ToPrimitive::to_isize(&e).unwrap(), - |(len, endpoint)| { - if !addr.is_null() && !addrlen.is_null() { - let addrlen = unsafe { &mut *addrlen }; - - match endpoint.addr { - IpAddress::Ipv4(_) => { - if *addrlen >= size_of::().try_into().unwrap() { - let addr = unsafe { &mut *(addr as *mut sockaddr_in) }; - *addr = sockaddr_in::from(endpoint); - *addrlen = size_of::().try_into().unwrap(); - } else { - return (-crate::errno::EINVAL).try_into().unwrap(); - } - } - IpAddress::Ipv6(_) => { - if *addrlen >= size_of::().try_into().unwrap() { - let addr = unsafe { &mut *(addr as *mut sockaddr_in6) }; - *addr = sockaddr_in6::from(endpoint); - *addrlen = size_of::().try_into().unwrap(); - } else { - return (-crate::errno::EINVAL).try_into().unwrap(); - } - } - } - } - - len - }, - ) - }, - ) -} +pub(crate) mod udp; diff --git a/src/syscalls/net.rs b/src/syscalls/net.rs index d2cbca6434..46b5b8c40e 100644 --- a/src/syscalls/net.rs +++ b/src/syscalls/net.rs @@ -1,11 +1,18 @@ #![allow(dead_code)] #![allow(nonstandard_style)] +use alloc::sync::Arc; use core::ffi::c_void; +use core::mem::size_of; +use core::ops::DerefMut; +use core::sync::atomic::Ordering; #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] use smoltcp::wire::{IpAddress, IpEndpoint, IpListenEndpoint}; -use crate::fd::socket::*; +use crate::errno::*; +use crate::executor::network::{NetworkState, NIC}; +use crate::fd::socket::{tcp, udp}; +use crate::fd::{get_object, insert_object, SocketOption, FD_COUNTER}; use crate::syscalls::__sys_write; pub const AF_INET: i32 = 0; @@ -237,6 +244,405 @@ pub struct linger { pub l_linger: i32, } +extern "C" fn __sys_socket(domain: i32, type_: i32, protocol: i32) -> i32 { + debug!( + "sys_socket: domain {}, type {}, protocol {}", + domain, type_, protocol + ); + + if (domain != AF_INET && domain != AF_INET6) + || (type_ != SOCK_STREAM && type_ != SOCK_DGRAM) + || protocol != 0 + { + -EINVAL + } else { + let mut guard = NIC.lock(); + + if let NetworkState::Initialized(nic) = guard.deref_mut() { + let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); + + #[cfg(feature = "udp")] + if type_ == SOCK_DGRAM { + let handle = nic.create_udp_handle().unwrap(); + let socket = udp::Socket::new(handle); + + insert_object(fd, Arc::new(socket)); + + return fd; + } + + #[cfg(feature = "tcp")] + if type_ == SOCK_STREAM { + let handle = nic.create_tcp_handle().unwrap(); + let socket = tcp::Socket::new(handle); + insert_object(fd, Arc::new(socket)); + + return fd; + } + + -EINVAL + } else { + -EINVAL + } + } +} + +extern "C" fn __sys_accept(fd: i32, addr: *mut sockaddr, addrlen: *mut socklen_t) -> i32 { + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).accept().map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |endpoint| { + let new_obj = dyn_clone::clone_box(&*v); + insert_object(fd, Arc::from(new_obj)); + let new_fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); + insert_object(new_fd, v.clone()); + + if !addr.is_null() && !addrlen.is_null() { + let addrlen = unsafe { &mut *addrlen }; + + match endpoint.addr { + IpAddress::Ipv4(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in) }; + *addr = sockaddr_in::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } + } + IpAddress::Ipv6(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in6) }; + *addr = sockaddr_in6::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } + } + } + } + + new_fd + }, + ) + }, + ) +} + +extern "C" fn __sys_listen(fd: i32, backlog: i32) -> i32 { + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).listen(backlog) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) +} + +extern "C" fn __sys_bind(fd: i32, name: *const sockaddr, namelen: socklen_t) -> i32 { + let endpoint = if namelen == size_of::().try_into().unwrap() { + IpListenEndpoint::from(unsafe { *(name as *const sockaddr_in) }) + } else if namelen == size_of::().try_into().unwrap() { + IpListenEndpoint::from(unsafe { *(name as *const sockaddr_in6) }) + } else { + return -crate::errno::EINVAL; + }; + + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).bind(endpoint) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) +} + +extern "C" fn __sys_connect(fd: i32, name: *const sockaddr, namelen: socklen_t) -> i32 { + let endpoint = if namelen == size_of::().try_into().unwrap() { + IpEndpoint::from(unsafe { *(name as *const sockaddr_in) }) + } else if namelen == size_of::().try_into().unwrap() { + IpEndpoint::from(unsafe { *(name as *const sockaddr_in6) }) + } else { + return -crate::errno::EINVAL; + }; + + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).connect(endpoint) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) +} + +extern "C" fn __sys_getsockname(fd: i32, addr: *mut sockaddr, addrlen: *mut socklen_t) -> i32 { + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + if let Some(endpoint) = (*v).getsockname() { + if !addr.is_null() && !addrlen.is_null() { + let addrlen = unsafe { &mut *addrlen }; + + match endpoint.addr { + IpAddress::Ipv4(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in) }; + *addr = sockaddr_in::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } else { + return -crate::errno::EINVAL; + } + } + IpAddress::Ipv6(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in6) }; + *addr = sockaddr_in6::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } else { + return -crate::errno::EINVAL; + } + } + } + } else { + return -crate::errno::EINVAL; + } + } + + 0 + }, + ) +} + +extern "C" fn __sys_setsockopt( + fd: i32, + level: i32, + optname: i32, + optval: *const c_void, + optlen: socklen_t, +) -> i32 { + debug!( + "sys_setsockopt: {}, level {}, optname {}", + fd, level, optname + ); + + if level == IPPROTO_TCP + && optname == TCP_NODELAY + && optlen == size_of::().try_into().unwrap() + { + if optval.is_null() { + return -crate::errno::EINVAL; + } + + let value = unsafe { *(optval as *const i32) }; + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).setsockopt(SocketOption::TcpNoDelay, value != 0) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) + } else if level == SOL_SOCKET && optname == SO_REUSEADDR { + 0 + } else { + -crate::errno::EINVAL + } +} + +extern "C" fn __sys_getsockopt( + fd: i32, + level: i32, + optname: i32, + optval: *mut c_void, + optlen: *mut socklen_t, +) -> i32 { + debug!( + "sys_getsockopt: {}, level {}, optname {}", + fd, level, optname + ); + + if level == IPPROTO_TCP && optname == TCP_NODELAY { + if optval.is_null() || optlen.is_null() { + return -crate::errno::EINVAL; + } + + let optval = unsafe { &mut *(optval as *mut i32) }; + let optlen = unsafe { &mut *(optlen as *mut socklen_t) }; + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).getsockopt(SocketOption::TcpNoDelay).map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |value| { + if value { + *optval = 1; + } else { + *optval = 0; + } + *optlen = core::mem::size_of::().try_into().unwrap(); + + 0 + }, + ) + }, + ) + } else { + -crate::errno::EINVAL + } +} + +extern "C" fn __sys_getpeername(fd: i32, addr: *mut sockaddr, addrlen: *mut socklen_t) -> i32 { + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + if let Some(endpoint) = (*v).getsockname() { + if !addr.is_null() && !addrlen.is_null() { + let addrlen = unsafe { &mut *addrlen }; + + match endpoint.addr { + IpAddress::Ipv4(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in) }; + *addr = sockaddr_in::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } else { + return -crate::errno::EINVAL; + } + } + IpAddress::Ipv6(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in6) }; + *addr = sockaddr_in6::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } else { + return -crate::errno::EINVAL; + } + } + } + } else { + return -crate::errno::EINVAL; + } + } + + 0 + }, + ) +} + +extern "C" fn __sys_freeaddrinfo(_ai: *mut addrinfo) {} + +extern "C" fn __sys_getaddrinfo( + _nodename: *const u8, + _servname: *const u8, + _hints: *const addrinfo, + _res: *mut *mut addrinfo, +) -> i32 { + -EINVAL +} + +extern "C" fn __sys_shutdown_socket(fd: i32, how: i32) -> i32 { + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + (*v).shutdown(how) + .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) + }, + ) +} + +extern "C" fn __sys_recv(fd: i32, buf: *mut u8, len: usize) -> isize { + let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| { + (*v).read(slice) + .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + }, + ) +} + +extern "C" fn __sys_sendto( + fd: i32, + buf: *const u8, + len: usize, + _flags: i32, + addr: *const sockaddr, + addr_len: socklen_t, +) -> isize { + let endpoint = if addr_len == size_of::().try_into().unwrap() { + IpEndpoint::from(unsafe { *(addr as *const sockaddr_in) }) + } else if addr_len == size_of::().try_into().unwrap() { + IpEndpoint::from(unsafe { *(addr as *const sockaddr_in6) }) + } else { + return (-crate::errno::EINVAL).try_into().unwrap(); + }; + let slice = unsafe { core::slice::from_raw_parts(buf, len) }; + let obj = get_object(fd); + + obj.map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| { + (*v).sendto(slice, endpoint) + .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + }, + ) +} + +extern "C" fn __sys_recvfrom( + fd: i32, + buf: *mut u8, + len: usize, + _flags: i32, + addr: *mut sockaddr, + addrlen: *mut socklen_t, +) -> isize { + let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; + let obj = get_object(fd); + obj.map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| { + (*v).recvfrom(slice).map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |(len, endpoint)| { + if !addr.is_null() && !addrlen.is_null() { + let addrlen = unsafe { &mut *addrlen }; + + match endpoint.addr { + IpAddress::Ipv4(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in) }; + *addr = sockaddr_in::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } else { + return (-crate::errno::EINVAL).try_into().unwrap(); + } + } + IpAddress::Ipv6(_) => { + if *addrlen >= size_of::().try_into().unwrap() { + let addr = unsafe { &mut *(addr as *mut sockaddr_in6) }; + *addr = sockaddr_in6::from(endpoint); + *addrlen = size_of::().try_into().unwrap(); + } else { + return (-crate::errno::EINVAL).try_into().unwrap(); + } + } + } + } + + len + }, + ) + }, + ) +} + #[no_mangle] pub extern "C" fn sys_socket(domain: i32, type_: i32, protocol: i32) -> i32 { kernel_function!(__sys_socket(domain, type_, protocol)) From 1eaab6cef32362cc80290f838915154a61613cd6 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Thu, 4 Jan 2024 21:58:55 +0000 Subject: [PATCH 22/32] add check if udp and/or tcp support is activated - include only the required modules --- src/syscalls/net.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/syscalls/net.rs b/src/syscalls/net.rs index 46b5b8c40e..80d0cff503 100644 --- a/src/syscalls/net.rs +++ b/src/syscalls/net.rs @@ -11,7 +11,10 @@ use smoltcp::wire::{IpAddress, IpEndpoint, IpListenEndpoint}; use crate::errno::*; use crate::executor::network::{NetworkState, NIC}; -use crate::fd::socket::{tcp, udp}; +#[cfg(feature = "tcp")] +use crate::fd::socket::tcp; +#[cfg(feature = "udp")] +use crate::fd::socket::udp; use crate::fd::{get_object, insert_object, SocketOption, FD_COUNTER}; use crate::syscalls::__sys_write; From 3eab876027e342b61f590ea595f6d09ff09a400b Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Fri, 5 Jan 2024 20:31:21 +0000 Subject: [PATCH 23/32] revise IO interface - using more suitable data types - create file /proc/version to show the kernel version - create abstraction layout for file access, which is derived from Rust's std --- Cargo.toml | 1 + src/fd/mod.rs | 72 +++++--------- src/fd/socket/tcp.rs | 12 +-- src/fd/socket/udp.rs | 24 ++--- src/fd/stdio.rs | 17 ++-- src/fs/fuse.rs | 227 +++++++++++++------------------------------ src/fs/mem.rs | 145 +++------------------------ src/fs/mod.rs | 200 +++++++++++++++++++++++++++++++++----- src/fs/uhyve.rs | 19 ++-- src/io.rs | 97 ++++++++++++++++++ src/lib.rs | 3 +- src/syscalls/mod.rs | 72 +++++++++++--- src/syscalls/net.rs | 14 ++- 13 files changed, 482 insertions(+), 421 deletions(-) create mode 100644 src/io.rs diff --git a/Cargo.toml b/Cargo.toml index 35eca252ec..763e50a70a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,7 @@ take-static = "0.1" talc = { version = "4" } time = { version = "0.3", default-features = false } zerocopy = { version = "0.7", features = ["derive"] } +build-time = "0.1.3" [dependencies.smoltcp] version = "0.10" diff --git a/src/fd/mod.rs b/src/fd/mod.rs index c5d6725fa8..f39af6da8b 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -8,9 +8,8 @@ use hashbrown::HashMap; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use crate::env; -use crate::errno::*; use crate::fd::stdio::*; -use crate::fs::{self, FileAttr, SeekWhence}; +use crate::fs::{self, DirectoryEntry, FileAttr, SeekWhence}; #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] pub(crate) mod socket; @@ -23,7 +22,7 @@ const STDERR_FILENO: FileDescriptor = 2; // TODO: Integrate with src/errno.rs ? #[allow(clippy::upper_case_acronyms)] #[derive(Debug, PartialEq, FromPrimitive, ToPrimitive)] -pub(crate) enum IoError { +pub enum IoError { ENOENT = crate::errno::ENOENT as isize, ENOSYS = crate::errno::ENOSYS as isize, EIO = crate::errno::EIO as isize, @@ -104,33 +103,16 @@ impl Default for AccessPermission { } } -#[repr(C)] -#[derive(Debug, Copy, Clone, Default)] -pub struct Dirent { - pub d_ino: u64, - pub d_off: u64, - pub d_namelen: u32, - pub d_type: u32, - pub d_name: [u8; 0], -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub enum DirectoryEntry { - Invalid(i32), - Valid(*const Dirent), -} - pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// `read` attempts to read `len` bytes from the object references /// by the descriptor - fn read(&self, _buf: &mut [u8]) -> Result { + fn read(&self, _buf: &mut [u8]) -> Result { Err(IoError::ENOSYS) } /// `write` attempts to write `len` bytes to the object references /// by the descriptor - fn write(&self, _buf: &[u8]) -> Result { + fn write(&self, _buf: &[u8]) -> Result { Err(IoError::ENOSYS) } @@ -157,8 +139,8 @@ pub(crate) 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 /// pointed to by the file descriptor - fn readdir(&self) -> DirectoryEntry { - DirectoryEntry::Invalid(-ENOSYS) + fn readdir(&self) -> Result, IoError> { + Err(IoError::EINVAL) } /// `mkdir` creates a directory entry @@ -216,7 +198,7 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// receive a message from a socket #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn recvfrom(&self, _buffer: &mut [u8]) -> Result<(isize, IpEndpoint), IoError> { + fn recvfrom(&self, _buffer: &mut [u8]) -> Result<(usize, IpEndpoint), IoError> { Err(IoError::ENOSYS) } @@ -228,7 +210,7 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// be sent to the address specified by dest_addr (overriding the pre-specified peer /// address). #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn sendto(&self, _buffer: &[u8], _endpoint: IpEndpoint) -> Result { + fn sendto(&self, _buffer: &[u8], _endpoint: IpEndpoint) -> Result { Err(IoError::ENOSYS) } @@ -248,19 +230,19 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { fn close(&self) {} } -pub(crate) fn open(name: &str, flags: i32, mode: u32) -> Result { +pub(crate) fn open( + name: &str, + flags: OpenOption, + mode: AccessPermission, +) -> Result { // mode is 0x777 (0b0111_0111_0111), when flags | O_CREAT, else 0 // flags is bitmask of O_DEC_* defined above. // (taken from rust stdlib/sys hermit target ) - debug!("Open {}, {}, {}", name, flags, mode); + debug!("Open {}, {:?}, {:?}", name, flags, mode); let fs = fs::FILESYSTEM.get().unwrap(); - if let Ok(file) = fs.open( - name, - OpenOption::from_bits(flags).ok_or(IoError::EINVAL)?, - AccessPermission::from_bits(mode).ok_or(IoError::EINVAL)?, - ) { + if let Ok(file) = fs.open(name, flags, mode) { let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); if OBJECT_MAP.write().try_insert(fd, file).is_err() { Err(IoError::EINVAL) @@ -272,28 +254,22 @@ pub(crate) fn open(name: &str, flags: i32, mode: u32) -> Result Result { - debug!("Open directory {}", name); +pub(crate) fn close(fd: FileDescriptor) { + let _ = remove_object(fd).map(|v| v.close()); +} - let fs = fs::FILESYSTEM.get().unwrap(); - if let Ok(obj) = fs.opendir(name) { - let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); - // Would a GenericDir make sense? - if OBJECT_MAP.write().try_insert(fd, obj).is_err() { - Err(IoError::EINVAL) - } else { - Ok(fd as FileDescriptor) - } - } else { - Err(IoError::EINVAL) - } +pub(crate) fn read(fd: FileDescriptor, buf: &mut [u8]) -> Result { + get_object(fd)?.read(buf) +} + +pub(crate) fn write(fd: FileDescriptor, buf: &[u8]) -> Result { + get_object(fd)?.write(buf) } pub(crate) fn get_object(fd: FileDescriptor) -> Result, IoError> { Ok((*(OBJECT_MAP.read().get(&fd).ok_or(IoError::EINVAL)?)).clone()) } -#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] pub(crate) fn insert_object( fd: FileDescriptor, obj: Arc, diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index 09474e54b4..896f968ce2 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -63,7 +63,7 @@ impl Socket { // TODO: Remove allow once fixed: // https://github.com/rust-lang/rust-clippy/issues/11380 #[allow(clippy::needless_pass_by_ref_mut)] - async fn async_read(&self, buffer: &mut [u8]) -> Result { + async fn async_read(&self, buffer: &mut [u8]) -> Result { future::poll_fn(|cx| { self.with(|socket| match socket.state() { tcp::State::Closed | tcp::State::Closing | tcp::State::CloseWait => { @@ -80,7 +80,7 @@ impl Socket { .recv(|data| { let len = core::cmp::min(buffer.len(), data.len()); buffer[..len].copy_from_slice(&data[..len]); - (len, isize::try_from(len).unwrap()) + (len, len) }) .map_err(|_| IoError::EIO), ) @@ -94,7 +94,7 @@ impl Socket { .await } - async fn async_write(&self, buffer: &[u8]) -> Result { + async fn async_write(&self, buffer: &[u8]) -> Result { let mut pos: usize = 0; while pos < buffer.len() { @@ -134,7 +134,7 @@ impl Socket { pos += n; } - Ok(pos.try_into().unwrap()) + Ok(pos) } async fn async_connect(&self, endpoint: IpEndpoint) -> Result<(), IoError> { @@ -269,7 +269,7 @@ impl ObjectInterface for Socket { self.with(|socket| socket.local_endpoint()) } - fn read(&self, buf: &mut [u8]) -> Result { + fn read(&self, buf: &mut [u8]) -> Result { if buf.is_empty() { return Ok(0); } @@ -291,7 +291,7 @@ impl ObjectInterface for Socket { } } - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { if buf.is_empty() { return Ok(0); } diff --git a/src/fd/socket/udp.rs b/src/fd/socket/udp.rs index f0b516f9f8..a3002855e6 100644 --- a/src/fd/socket/udp.rs +++ b/src/fd/socket/udp.rs @@ -53,7 +53,7 @@ impl Socket { .await } - async fn async_read(&self, buffer: &mut [u8]) -> Result { + async fn async_read(&self, buffer: &mut [u8]) -> Result { future::poll_fn(|cx| { self.with(|socket| { if socket.is_open() { @@ -62,14 +62,14 @@ impl Socket { Ok((len, meta)) => match self.endpoint.load() { Some(ep) => { if meta.endpoint == ep { - Poll::Ready(Ok(len.try_into().unwrap())) + Poll::Ready(Ok(len)) } else { buffer[..len].iter_mut().for_each(|x| *x = 0); socket.register_recv_waker(cx.waker()); Poll::Pending } } - None => Poll::Ready(Ok(len.try_into().unwrap())), + None => Poll::Ready(Ok(len)), }, _ => Poll::Ready(Err(IoError::EIO)), } @@ -85,7 +85,7 @@ impl Socket { .await } - async fn async_recvfrom(&self, buffer: &mut [u8]) -> Result<(isize, IpEndpoint), IoError> { + async fn async_recvfrom(&self, buffer: &mut [u8]) -> Result<(usize, IpEndpoint), IoError> { future::poll_fn(|cx| { self.with(|socket| { if socket.is_open() { @@ -94,14 +94,14 @@ impl Socket { Ok((len, meta)) => match self.endpoint.load() { Some(ep) => { if meta.endpoint == ep { - Poll::Ready(Ok((len.try_into().unwrap(), meta.endpoint))) + Poll::Ready(Ok((len, meta.endpoint))) } else { buffer[..len].iter_mut().for_each(|x| *x = 0); socket.register_recv_waker(cx.waker()); Poll::Pending } } - None => Poll::Ready(Ok((len.try_into().unwrap(), meta.endpoint))), + None => Poll::Ready(Ok((len, meta.endpoint))), }, _ => Poll::Ready(Err(IoError::EIO)), } @@ -117,7 +117,7 @@ impl Socket { .await } - async fn async_write(&self, buffer: &[u8], meta: &UdpMetadata) -> Result { + async fn async_write(&self, buffer: &[u8], meta: &UdpMetadata) -> Result { future::poll_fn(|cx| { self.with(|socket| { if socket.is_open() { @@ -125,7 +125,7 @@ impl Socket { Poll::Ready( socket .send_slice(buffer, *meta) - .map(|_| buffer.len() as isize) + .map(|_| buffer.len()) .map_err(|_| IoError::EIO), ) } else { @@ -151,7 +151,7 @@ impl ObjectInterface for Socket { Ok(()) } - fn sendto(&self, buf: &[u8], endpoint: IpEndpoint) -> Result { + fn sendto(&self, buf: &[u8], endpoint: IpEndpoint) -> Result { let meta = UdpMetadata::from(endpoint); if self.nonblocking.load(Ordering::Acquire) { @@ -161,7 +161,7 @@ impl ObjectInterface for Socket { } } - fn recvfrom(&self, buf: &mut [u8]) -> Result<(isize, IpEndpoint), IoError> { + fn recvfrom(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), IoError> { if self.nonblocking.load(Ordering::Acquire) { poll_on(self.async_recvfrom(buf), Some(Duration::ZERO)).map_err(|x| { if x == IoError::ETIME { @@ -179,7 +179,7 @@ impl ObjectInterface for Socket { } } - fn read(&self, buf: &mut [u8]) -> Result { + fn read(&self, buf: &mut [u8]) -> Result { if buf.len() == 0 { return Ok(0); } @@ -201,7 +201,7 @@ impl ObjectInterface for Socket { } } - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { if buf.len() == 0 { return Ok(0); } diff --git a/src/fd/stdio.rs b/src/fd/stdio.rs index 67af71caf0..79fec62f77 100644 --- a/src/fd/stdio.rs +++ b/src/fd/stdio.rs @@ -1,4 +1,3 @@ -use core::isize; #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] use core::ptr; @@ -78,11 +77,11 @@ impl GenericStdin { pub struct GenericStdout; impl ObjectInterface for GenericStdout { - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { // stdin/err/out all go to console CONSOLE.lock().write_all(buf); - Ok(buf.len().try_into().unwrap()) + Ok(buf.len()) } } @@ -96,11 +95,11 @@ impl GenericStdout { pub struct GenericStderr; impl ObjectInterface for GenericStderr { - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { // stdin/err/out all go to console CONSOLE.lock().write_all(buf); - Ok(buf.len().try_into().unwrap()) + Ok(buf.len()) } } @@ -125,11 +124,11 @@ impl UhyveStdin { pub struct UhyveStdout; impl ObjectInterface for UhyveStdout { - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { let mut syswrite = SysWrite::new(STDOUT_FILENO, buf.as_ptr(), buf.len()); uhyve_send(UHYVE_PORT_WRITE, &mut syswrite); - Ok(syswrite.len as isize) + Ok(syswrite.len) } } @@ -143,11 +142,11 @@ impl UhyveStdout { pub struct UhyveStderr; impl ObjectInterface for UhyveStderr { - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { let mut syswrite = SysWrite::new(STDERR_FILENO, buf.as_ptr(), buf.len()); uhyve_send(UHYVE_PORT_WRITE, &mut syswrite); - Ok(syswrite.len as isize) + Ok(syswrite.len) } } diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index e5137e55fa..5aa6412b32 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -15,9 +15,10 @@ 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::fd::{DirectoryEntry, IoError}; +use crate::fd::IoError; use crate::fs::{ - self, AccessPermission, FileAttr, NodeKind, ObjectInterface, OpenOption, SeekWhence, VfsNode, + self, 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 @@ -28,7 +29,7 @@ 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; +const U64_SIZE: usize = ::core::mem::size_of::(); const S_IFLNK: u32 = 40960; const S_IFMT: u32 = 61440; @@ -1151,7 +1152,7 @@ impl FuseFileHandleInner { } } - fn read(&mut self, buf: &mut [u8]) -> Result { + fn read(&mut self, buf: &mut [u8]) -> Result { debug!("FUSE read!"); let mut len = buf.len(); if len > MAX_READ_LEN { @@ -1181,14 +1182,14 @@ impl FuseFileHandleInner { MaybeUninit::slice_assume_init_ref(&rsp.extra_buffer[..len]) }); - Ok(len.try_into().unwrap()) + Ok(len) } else { debug!("File not open, cannot read!"); Err(IoError::ENOENT) } } - fn write(&mut self, buf: &[u8]) -> Result { + fn write(&mut self, buf: &[u8]) -> Result { debug!("FUSE write!"); let mut len = buf.len(); if len > MAX_WRITE_LEN { @@ -1217,7 +1218,7 @@ impl FuseFileHandleInner { rsp_size.try_into().unwrap() }; self.offset += len; - Ok(len.try_into().unwrap()) + Ok(len) } else { warn!("File not open, cannot read!"); Err(IoError::ENOENT) @@ -1269,11 +1270,11 @@ impl FuseFileHandle { } impl ObjectInterface for FuseFileHandle { - fn read(&self, buf: &mut [u8]) -> Result { + fn read(&self, buf: &mut [u8]) -> Result { self.0.lock().read(buf) } - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { self.0.lock().write(buf) } @@ -1289,136 +1290,6 @@ impl Clone for FuseFileHandle { } } -#[derive(Debug)] -struct FuseDirectoryHandleInner { - fuse_nid: Option, - fuse_fh: Option, - offset: u64, - buffer_offset: u32, - response: Option>>, -} - -impl FuseDirectoryHandleInner { - pub fn new() -> Self { - Self { - fuse_nid: None, - fuse_fh: None, - offset: 0, - buffer_offset: 0, - response: None, - } - } - - fn readdir(&mut self) -> DirectoryEntry { - // Check if we have to read the directory via FUSE or still have a direntry in the last respnse - 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!("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!("FUSE dir not open, cannot read!"); - return DirectoryEntry::Invalid(-crate::errno::EBADF); - }; - - let (mut cmd, mut rsp) = create_read(nid, fh, len, self.offset); - 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 DirectoryEntry::Invalid(-crate::errno::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::() - }; - - if len <= core::mem::size_of::() { - debug!("FUSE no new dirs"); - return DirectoryEntry::Valid(core::ptr::null()); - } - - // Keep smart pointer to response - let rsp = option.insert(rsp); - self.buffer_offset = 0; - - debug!("FUSE new buffer len: {}", len); - rsp - } - }; - - let return_ptr: *const u8 = unsafe { - resp.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.d_off; - - self.buffer_offset += core::mem::size_of::() as u32 + dirent.d_namelen; - - // Allign to dirent struct - self.buffer_offset = ((self.buffer_offset) + U64_SIZE - 1) & (!(U64_SIZE - 1)); - - // Check alignment - assert!(return_ptr.is_aligned_to(U64_SIZE.try_into().unwrap())); - - DirectoryEntry::Valid(return_ptr.cast()) - } -} - -impl Drop for FuseDirectoryHandleInner { - fn drop(&mut self) { - if self.fuse_nid.is_some() && self.fuse_fh.is_some() { - 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() - .unwrap() - .lock() - .send_command(cmd.as_ref(), rsp.as_mut()); - } - } -} - -#[derive(Debug)] -struct FuseDirectoryHandle(pub Arc>); - -impl Clone for FuseDirectoryHandle { - fn clone(&self) -> Self { - warn!("FuseDirectoryHandle: clone not tested"); - Self(self.0.clone()) - } -} - -impl FuseDirectoryHandle { - pub fn new() -> Self { - Self(Arc::new(SpinMutex::new(FuseDirectoryHandleInner::new()))) - } -} - -impl ObjectInterface for FuseDirectoryHandle { - fn readdir(&self) -> DirectoryEntry { - self.0.lock().readdir() - } -} - #[derive(Debug)] pub(crate) struct FuseDirectory; @@ -1434,10 +1305,7 @@ impl VfsNode for FuseDirectory { NodeKind::Directory } - fn traverse_opendir( - &self, - components: &mut Vec<&str>, - ) -> Result, IoError> { + fn traverse_readdir(&self, components: &mut Vec<&str>) -> Result, IoError> { let path: String = if components.is_empty() { "/".to_string() } else { @@ -1446,31 +1314,74 @@ impl VfsNode for FuseDirectory { debug!("FUSE opendir: {}", path); - let readdir = FuseDirectoryHandle::new(); - - // Lookup nodeid - - let mut readdir_guard = readdir.0.lock(); - readdir_guard.fuse_nid = lookup(&path); - - if readdir_guard.fuse_nid.is_none() { - warn!("Fuse lookup seems to have failed!"); - return Err(IoError::ENOENT); - } + let fuse_nid = lookup(&path).ok_or(IoError::ENOENT)?; // Opendir // Flag 0x10000 for O_DIRECTORY might not be necessary - let (mut cmd, mut rsp) = create_open(readdir_guard.fuse_nid.unwrap(), 0x10000); + let (mut cmd, mut rsp) = create_open(fuse_nid, 0x10000); cmd.header.opcode = Opcode::FUSE_OPENDIR as u32; get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - readdir_guard.fuse_fh = Some(unsafe { rsp.rsp.assume_init().fh }); + let fuse_fh = unsafe { rsp.rsp.assume_init().fh }; + + debug!("FUSE readdir: {}", path); + + // Linux seems to allocate a single page to store the dirfile + let len = MAX_READ_LEN as u32; + 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; + 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::() + >= len.try_into().unwrap() + { + len.try_into().unwrap() + } else { + rsp.header.len as usize + - ::core::mem::size_of::() + - ::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::() { + let dirent = + unsafe { &*(rsp.extra_buffer.as_ptr().byte_add(offset) as *const fuse_dirent) }; - drop(readdir_guard); + offset += core::mem::size_of::() + dirent.d_namelen as usize; + // Allign to dirent struct + offset = ((offset) + U64_SIZE - 1) & (!(U64_SIZE - 1)); + + let name: &'static [u8] = unsafe { + core::slice::from_raw_parts( + dirent.d_name.as_ptr(), + dirent.d_namelen.try_into().unwrap(), + ) + }; + entries.push(DirectoryEntry::new(name)); + } + + let (cmd, mut rsp) = create_release(fuse_nid, fuse_fh); + get_filesystem_driver() + .unwrap() + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); - Ok(Arc::new(readdir)) + Ok(entries) } fn traverse_stat(&self, components: &mut Vec<&str>) -> Result { diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 5beeaad9b6..061338934d 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -9,7 +9,6 @@ #![allow(dead_code)] -use alloc::alloc::{alloc_zeroed, Layout}; use alloc::boxed::Box; use alloc::collections::BTreeMap; use alloc::string::{String, ToString}; @@ -17,12 +16,11 @@ use alloc::sync::Arc; use alloc::vec::Vec; use core::ops::{Deref, DerefMut}; use core::slice; -use core::sync::atomic::{AtomicUsize, Ordering}; use hermit_sync::{RwSpinLock, SpinMutex}; -use crate::fd::{AccessPermission, DirectoryEntry, Dirent, IoError, ObjectInterface, OpenOption}; -use crate::fs::{FileAttr, NodeKind, VfsNode}; +use crate::fd::{AccessPermission, IoError, ObjectInterface, OpenOption}; +use crate::fs::{DirectoryEntry, FileAttr, NodeKind, VfsNode}; #[derive(Debug)] struct RomFileInterface { @@ -33,7 +31,7 @@ struct RomFileInterface { } impl ObjectInterface for RomFileInterface { - fn read(&self, buf: &mut [u8]) -> Result { + fn read(&self, buf: &mut [u8]) -> Result { let vec = self.data.read(); let mut pos_guard = self.pos.lock(); let pos = *pos_guard; @@ -51,7 +49,7 @@ impl ObjectInterface for RomFileInterface { buf[0..len].clone_from_slice(&vec[pos..pos + len]); *pos_guard = pos + len; - Ok(len.try_into().unwrap()) + Ok(len) } } @@ -87,7 +85,7 @@ pub struct RamFileInterface { } impl ObjectInterface for RamFileInterface { - fn read(&self, buf: &mut [u8]) -> Result { + fn read(&self, buf: &mut [u8]) -> Result { let guard = self.data.read(); let vec = guard.deref(); let mut pos_guard = self.pos.lock(); @@ -106,10 +104,10 @@ impl ObjectInterface for RamFileInterface { buf[0..len].clone_from_slice(&vec[pos..pos + len]); *pos_guard = pos + len; - Ok(len.try_into().unwrap()) + Ok(len) } - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { let mut guard = self.data.write(); let vec = guard.deref_mut(); let mut pos_guard = self.pos.lock(); @@ -122,7 +120,7 @@ impl ObjectInterface for RamFileInterface { vec[pos..pos + buf.len()].clone_from_slice(buf); *pos_guard = pos + buf.len(); - Ok(buf.len().try_into().unwrap()) + Ok(buf.len()) } } @@ -248,119 +246,6 @@ impl RamFile { } } -#[derive(Debug)] -struct MemDirectoryInterface { - /// Position within the file - pos: AtomicUsize, - /// File content - data: Arc< - RwSpinLock>>, - >, -} - -impl MemDirectoryInterface { - pub fn new( - data: Arc< - RwSpinLock< - BTreeMap>, - >, - >, - ) -> Self { - Self { - pos: AtomicUsize::new(0), - data, - } - } -} - -impl ObjectInterface for MemDirectoryInterface { - fn readdir(&self) -> DirectoryEntry { - let pos = self.pos.fetch_add(1, Ordering::SeqCst); - - if pos == 0 { - let name = "."; - let name_len = name.len(); - - let len = core::mem::size_of::() + name_len + 1; - let layout = Layout::from_size_align(len, core::mem::align_of::()) - .unwrap() - .pad_to_align(); - - let raw = unsafe { - let raw = alloc_zeroed(layout) as *mut Dirent; - (*raw).d_namelen = name_len.try_into().unwrap(); - core::ptr::copy_nonoverlapping( - name.as_ptr(), - &mut (*raw).d_name as *mut u8, - name_len, - ); - - raw - }; - - DirectoryEntry::Valid(raw) - } else if pos == 1 { - let name = ".."; - let name_len = name.len(); - - let len = core::mem::size_of::() + name_len + 1; - let layout = Layout::from_size_align(len, core::mem::align_of::()) - .unwrap() - .pad_to_align(); - - let raw = unsafe { - let raw = alloc_zeroed(layout) as *mut Dirent; - (*raw).d_namelen = name_len.try_into().unwrap(); - core::ptr::copy_nonoverlapping( - name.as_ptr(), - &mut (*raw).d_name as *mut u8, - name_len, - ); - - raw - }; - - DirectoryEntry::Valid(raw) - } else { - let keys: Vec<_> = self.data.read().keys().cloned().collect(); - - if keys.len() > pos - 2 { - let name_len = keys[pos - 2].len(); - - let len = core::mem::size_of::() + name_len + 1; - let layout = Layout::from_size_align(len, core::mem::align_of::()) - .unwrap() - .pad_to_align(); - - let raw = unsafe { - let raw = alloc_zeroed(layout) as *mut Dirent; - (*raw).d_namelen = name_len.try_into().unwrap(); - core::ptr::copy_nonoverlapping( - keys[pos - 2].as_ptr(), - &mut (*raw).d_name as *mut u8, - name_len, - ); - - raw - }; - - DirectoryEntry::Valid(raw) - } else { - DirectoryEntry::Valid(core::ptr::null()) - } - } - } -} - -impl Clone for MemDirectoryInterface { - fn clone(&self) -> Self { - Self { - pos: AtomicUsize::new(self.pos.load(Ordering::SeqCst)), - data: self.data.clone(), - } - } -} - #[derive(Debug)] pub(crate) struct MemDirectory { inner: Arc< @@ -478,20 +363,22 @@ impl VfsNode for MemDirectory { Err(IoError::EBADF) } - fn traverse_opendir( - &self, - components: &mut Vec<&str>, - ) -> Result, IoError> { + fn traverse_readdir(&self, components: &mut Vec<&str>) -> Result, IoError> { if let Some(component) = components.pop() { let node_name = String::from(component); if let Some(directory) = self.inner.read().get(&node_name) { - directory.traverse_opendir(components) + directory.traverse_readdir(components) } else { Err(IoError::EBADF) } } else { - Ok(Arc::new(MemDirectoryInterface::new(self.inner.clone()))) + let mut entries: Vec = Vec::new(); + for name in self.inner.read().keys() { + entries.push(DirectoryEntry::new(name.as_bytes())); + } + + Ok(entries) } } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 242c8e6ef2..e31dfef378 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -6,14 +6,53 @@ mod uhyve; use alloc::boxed::Box; use alloc::sync::Arc; use alloc::vec::Vec; +use core::ffi::CStr; +use core::fmt; +use core::sync::atomic::{AtomicUsize, Ordering}; use hermit_sync::OnceCell; use mem::MemDirectory; -use crate::fd::{AccessPermission, IoError, ObjectInterface, OpenOption}; +use crate::fd::{ + insert_object, AccessPermission, IoError, ObjectInterface, OpenOption, FD_COUNTER, +}; +use crate::io::Write; pub(crate) static FILESYSTEM: OnceCell = OnceCell::new(); +pub const MAX_NAME_LENGTH: usize = 256; + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct DirectoryEntry { + pub d_name: [u8; MAX_NAME_LENGTH], +} + +impl DirectoryEntry { + pub fn new(d_name: &[u8]) -> Self { + let len = core::cmp::min(d_name.len(), MAX_NAME_LENGTH); + let mut entry = Self { + d_name: [0; MAX_NAME_LENGTH], + }; + + entry.d_name[..len].copy_from_slice(&d_name[..len]); + + entry + } +} + +impl fmt::Debug for DirectoryEntry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let d_name = unsafe { CStr::from_ptr(self.d_name.as_ptr() as _) } + .to_str() + .unwrap(); + + f.debug_struct("DirectoryEntry") + .field("d_name", &d_name) + .finish() + } +} + /// Type of the VNode #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum NodeKind { @@ -58,10 +97,10 @@ pub(crate) trait VfsNode: core::fmt::Debug { } /// Helper function to open a directory - fn traverse_opendir( + fn traverse_readdir( &self, _components: &mut Vec<&str>, - ) -> Result, IoError> { + ) -> Result, IoError> { Err(IoError::ENOSYS) } @@ -95,6 +134,41 @@ pub(crate) trait VfsNode: core::fmt::Debug { } } +#[derive(Debug)] +struct DirectoryReader { + pos: AtomicUsize, + data: Vec, +} + +impl DirectoryReader { + pub fn new(data: Vec) -> Self { + Self { + pos: AtomicUsize::new(0), + data, + } + } +} + +impl ObjectInterface for DirectoryReader { + fn readdir(&self) -> Result, IoError> { + let pos = self.pos.fetch_add(1, Ordering::SeqCst); + if pos < self.data.len() { + Ok(Some(self.data[pos])) + } else { + Ok(None) + } + } +} + +impl Clone for DirectoryReader { + fn clone(&self) -> Self { + Self { + pos: AtomicUsize::new(self.pos.load(Ordering::SeqCst)), + data: self.data.clone(), + } + } +} + #[derive(Debug)] pub(crate) struct Filesystem { root: MemDirectory, @@ -156,18 +230,23 @@ impl Filesystem { self.root.traverse_mkdir(&mut components, mode) } - /// List given directory pub fn opendir(&self, path: &str) -> Result, IoError> { + debug!("Open directory {}", path); + Ok(Arc::new(DirectoryReader::new(self.readdir(path)?))) + } + + /// List given directory + pub fn readdir(&self, path: &str) -> Result, IoError> { if path.trim() == "/" { let mut components: Vec<&str> = Vec::new(); - self.root.traverse_opendir(&mut components) + self.root.traverse_readdir(&mut components) } else { let mut components: Vec<&str> = path.split('/').collect(); components.reverse(); components.pop(); - self.root.traverse_opendir(&mut components) + self.root.traverse_readdir(&mut components) } } @@ -266,6 +345,7 @@ pub enum SeekWhence { pub(crate) fn init() { const VERSION: &str = env!("CARGO_PKG_VERSION"); + const UTC_BUILT_TIME: &str = build_time::build_time_utc!(); FILESYSTEM.set(Filesystem::new()).unwrap(); FILESYSTEM @@ -276,23 +356,15 @@ pub(crate) fn init() { FILESYSTEM .get() .unwrap() - .mkdir("/etc", AccessPermission::from_bits(0o777).unwrap()) - .expect("Unable to create /tmp"); - if let Ok(fd) = FILESYSTEM.get().unwrap().open( - "/etc/hostname", - OpenOption::O_CREAT | OpenOption::O_RDWR, - AccessPermission::from_bits(0o644).unwrap(), - ) { - let _ret = fd.write(b"Hermit"); - fd.close(); - } - if let Ok(fd) = FILESYSTEM.get().unwrap().open( - "/etc/version", - OpenOption::O_CREAT | OpenOption::O_RDWR, - AccessPermission::from_bits(0o644).unwrap(), - ) { - let _ret = fd.write(VERSION.as_bytes()); - fd.close(); + .mkdir("/proc", AccessPermission::from_bits(0o777).unwrap()) + .expect("Unable to create /proc"); + + if let Ok(mut file) = File::create("/proc/version") { + if write!(file, "HermitOS version {VERSION} # UTC {UTC_BUILT_TIME}").is_err() { + error!("Unable to write in /proc/version"); + } + } else { + error!("Unable to create /proc/version"); } #[cfg(all(feature = "fuse", feature = "pci"))] @@ -300,12 +372,88 @@ pub(crate) fn init() { uhyve::init(); } -pub unsafe fn create_file(name: &str, ptr: *const u8, length: usize, mode: AccessPermission) { +pub unsafe fn create_file( + name: &str, + ptr: *const u8, + length: usize, + mode: AccessPermission, +) -> Result<(), IoError> { unsafe { FILESYSTEM .get() - .unwrap() + .ok_or(IoError::EINVAL)? .create_file(name, ptr, length, mode) - .expect("Unable to create file from ROM") + } +} + +/// Returns an vectri with all the entries within a directory. +pub fn readdir(name: &str) -> Result, IoError> { + debug!("Read directory {}", name); + + FILESYSTEM.get().ok_or(IoError::EINVAL)?.readdir(name) +} + +/// a +pub(crate) fn opendir(name: &str) -> Result { + let obj = FILESYSTEM.get().unwrap().opendir(name)?; + let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); + + let _ = insert_object(fd, obj); + + Ok(fd) +} + +use crate::fd::{self, FileDescriptor}; + +pub fn file_attributes(path: &str) -> Result { + FILESYSTEM.get().unwrap().lstat(path) +} + +#[derive(Debug)] +pub struct File(FileDescriptor); + +impl File { + /// Creates a new file in read-write mode; error if the file exists. + /// + /// This function will create a file if it does not exist, or return + /// an error if it does. This way, if the call succeeds, the file + /// returned is guaranteed to be new. + pub fn create(path: &str) -> Result { + let fd = fd::open( + path, + OpenOption::O_CREAT | OpenOption::O_RDWR, + AccessPermission::from_bits(0o666).unwrap(), + )?; + + Ok(File(fd)) + } + + /// Attempts to open a file in read-write mode. + pub fn open(path: &str) -> Result { + let fd = fd::open( + path, + OpenOption::O_RDWR, + AccessPermission::from_bits(0o666).unwrap(), + )?; + + Ok(File(fd)) + } +} + +impl crate::io::Read for File { + fn read(&mut self, buf: &mut [u8]) -> Result { + fd::read(self.0, buf) + } +} + +impl crate::io::Write for File { + fn write(&mut self, buf: &[u8]) -> Result { + fd::write(self.0, buf) + } +} + +impl Drop for File { + fn drop(&mut self) { + fd::close(self.0); } } diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index 136cbb7d05..2c6926dca7 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -163,22 +163,22 @@ impl UhyveFileHandleInner { Self(fd) } - fn read(&mut self, buf: &mut [u8]) -> Result { + fn read(&mut self, buf: &mut [u8]) -> Result { let mut sysread = SysRead::new(self.0, buf.as_mut_ptr(), buf.len()); uhyve_send(UHYVE_PORT_READ, &mut sysread); if sysread.ret >= 0 { - Ok(sysread.ret) + Ok(sysread.ret.try_into().unwrap()) } else { Err(IoError::EIO) } } - fn write(&mut self, buf: &[u8]) -> Result { + fn write(&mut self, buf: &[u8]) -> Result { let mut syswrite = SysWrite::new(self.0, buf.as_ptr(), buf.len()); uhyve_send(UHYVE_PORT_WRITE, &mut syswrite); - Ok(syswrite.len.try_into().unwrap()) + Ok(syswrite.len) } fn lseek(&self, offset: isize, whence: SeekWhence) -> Result { @@ -210,11 +210,11 @@ impl UhyveFileHandle { } impl ObjectInterface for UhyveFileHandle { - fn read(&self, buf: &mut [u8]) -> Result { + fn read(&self, buf: &mut [u8]) -> Result { self.0.lock().read(buf) } - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { self.0.lock().write(buf) } @@ -244,13 +244,6 @@ impl VfsNode for UhyveDirectory { NodeKind::Directory } - fn traverse_opendir( - &self, - _omponents: &mut Vec<&str>, - ) -> Result, IoError> { - Err(IoError::ENOSYS) - } - fn traverse_stat(&self, _components: &mut Vec<&str>) -> Result { Err(IoError::ENOSYS) } diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000000..725c586204 --- /dev/null +++ b/src/io.rs @@ -0,0 +1,97 @@ +use alloc::string::String; +use alloc::vec::Vec; +use core::fmt; + +use crate::fd::IoError; + +/// The Read trait allows for reading bytes from a source. +/// +/// The Read trait is derived from Rust's std library. +pub trait Read { + fn read(&mut self, buf: &mut [u8]) -> Result; + + /// Read all bytes until EOF in this source, placing them into buf. + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + let start_len = buf.len(); + + loop { + let mut probe = [0u8; 512]; + + match self.read(&mut probe) { + Ok(0) => return Ok(buf.len() - start_len), + Ok(n) => { + buf.extend_from_slice(&probe[..n]); + } + Err(e) => return Err(e), + } + } + } + + /// Read all bytes until EOF in this source, appending them to `buf`. + /// + /// If successful, this function returns the number of bytes which were read + /// and appended to `buf`. + fn read_to_string(&mut self, buf: &mut String) -> Result { + unsafe { self.read_to_end(buf.as_mut_vec()) } + } +} + +/// The Write trait allows for reading bytes from a source. +/// +/// The Write trait is derived from Rust's std library. +pub trait Write { + fn write(&mut self, buf: &[u8]) -> Result; + + /// Attempts to write an entire buffer into this writer. + fn write_all(&mut self, mut buf: &[u8]) -> Result<(), IoError> { + while !buf.is_empty() { + match self.write(buf) { + Ok(0) => { + return Err(IoError::EIO); + } + Ok(n) => buf = &buf[n..], + Err(e) => return Err(e), + } + } + + Ok(()) + } + + /// Writes a formatted string into this writer, returning any error encountered. + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<(), IoError> { + // Create a shim which translates a Write to a fmt::Write and saves + // off I/O errors. instead of discarding them + struct Adapter<'a, T: ?Sized> { + inner: &'a mut T, + error: Result<(), IoError>, + } + + impl fmt::Write for Adapter<'_, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adapter { + inner: self, + error: Ok(()), + }; + match fmt::write(&mut output, fmt) { + Ok(()) => Ok(()), + Err(..) => { + // check if the error came from the underlying `Write` or not + if output.error.is_err() { + output.error + } else { + Err(IoError::EINVAL) + } + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 91cdad60ac..8752cde908 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,10 +86,11 @@ pub mod errno; mod executor; pub mod fd; pub mod fs; +pub mod io; mod mm; mod scheduler; mod synch; -mod syscalls; +pub mod syscalls; #[cfg(target_os = "none")] hermit_entry::define_entry_version!(); diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index c161ee3b04..b396a33530 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -19,9 +19,9 @@ pub use self::tasks::*; pub use self::timer::*; use crate::env; use crate::fd::{ - dup_object, get_object, remove_object, AccessPermission, DirectoryEntry, FileDescriptor, IoCtl, + dup_object, get_object, remove_object, AccessPermission, FileDescriptor, IoCtl, OpenOption, }; -use crate::fs::{self, FileAttr}; +use crate::fs::{self, DirectoryEntry, FileAttr}; use crate::syscalls::interfaces::SyscallInterface; #[cfg(target_os = "none")] use crate::{__sys_free, __sys_malloc, __sys_realloc}; @@ -47,7 +47,7 @@ mod timer; const LWIP_FD_BIT: i32 = 1 << 30; #[cfg(feature = "newlib")] -pub static LWIP_LOCK: InterruptTicketMutex<()> = InterruptTicketMutex::new(()); +pub(crate) static LWIP_LOCK: InterruptTicketMutex<()> = InterruptTicketMutex::new(()); pub(crate) static SYS: Lazy<&'static dyn SyscallInterface> = Lazy::new(|| { if env::is_uhyve() { @@ -205,7 +205,7 @@ pub extern "C" fn sys_fstat(fd: FileDescriptor, stat: *mut FileAttr) -> i32 { extern "C" fn __sys_opendir(name: *const u8) -> FileDescriptor { if let Ok(name) = unsafe { CStr::from_ptr(name as _) }.to_str() { - crate::fd::opendir(name).map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| v) + crate::fs::opendir(name).map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| v) } else { -crate::errno::EINVAL } @@ -217,6 +217,17 @@ pub extern "C" fn sys_opendir(name: *const u8) -> FileDescriptor { } extern "C" fn __sys_open(name: *const u8, flags: i32, mode: u32) -> FileDescriptor { + let flags = if let Some(flags) = OpenOption::from_bits(flags) { + flags + } else { + return -crate::errno::EINVAL; + }; + let mode = if let Some(mode) = AccessPermission::from_bits(mode) { + mode + } else { + return -crate::errno::EINVAL; + }; + if let Ok(name) = unsafe { CStr::from_ptr(name as _) }.to_str() { crate::fd::open(name, flags, mode) .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| v) @@ -252,8 +263,10 @@ extern "C" fn __sys_read(fd: FileDescriptor, buf: *mut u8, len: usize) -> isize obj.map_or_else( |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { - (*v).read(slice) - .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + (*v).read(slice).map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| v.try_into().unwrap(), + ) }, ) } @@ -269,8 +282,10 @@ extern "C" fn __sys_write(fd: FileDescriptor, buf: *const u8, len: usize) -> isi obj.map_or_else( |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { - (*v).write(slice) - .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + (*v).write(slice).map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| v.try_into().unwrap(), + ) }, ) } @@ -320,16 +335,45 @@ 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) -> DirectoryEntry { +extern "C" fn __sys_readdir( + fd: FileDescriptor, + dirent: *mut DirectoryEntry, + dirent_len: *mut usize, +) -> i32 { + if dirent.is_null() || dirent_len.is_null() { + return -crate::errno::EINVAL; + } + + let dirent = unsafe { &mut *dirent }; + let dirent_len = unsafe { &mut *dirent_len }; let obj = get_object(fd); - obj.map_or(DirectoryEntry::Invalid(-crate::errno::EINVAL), |v| { - (*v).readdir() - }) + obj.map_or_else( + |_| -crate::errno::EINVAL, + |v| { + (*v).readdir().map_or_else( + |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |v| { + if let Some(v) = v { + *dirent = v; + *dirent_len = core::mem::size_of::(); + } else { + *dirent_len = 0; + } + + 0 + }, + ) + }, + ) } #[no_mangle] -pub extern "C" fn sys_readdir(fd: FileDescriptor) -> DirectoryEntry { - kernel_function!(__sys_readdir(fd)) +pub extern "C" fn sys_readdir( + fd: FileDescriptor, + dirent: *mut DirectoryEntry, + dirent_len: *mut usize, +) -> i32 { + kernel_function!(__sys_readdir(fd, dirent, dirent_len)) } extern "C" fn __sys_dup(fd: i32) -> i32 { diff --git a/src/syscalls/net.rs b/src/syscalls/net.rs index 80d0cff503..dc33909d5d 100644 --- a/src/syscalls/net.rs +++ b/src/syscalls/net.rs @@ -565,8 +565,10 @@ extern "C" fn __sys_recv(fd: i32, buf: *mut u8, len: usize) -> isize { obj.map_or_else( |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { - (*v).read(slice) - .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + (*v).read(slice).map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| v.try_into().unwrap(), + ) }, ) } @@ -592,8 +594,10 @@ extern "C" fn __sys_sendto( obj.map_or_else( |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { - (*v).sendto(slice, endpoint) - .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + (*v).sendto(slice, endpoint).map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| v.try_into().unwrap(), + ) }, ) } @@ -639,7 +643,7 @@ extern "C" fn __sys_recvfrom( } } - len + len.try_into().unwrap() }, ) }, From c79af48d15a799effdadfc588b5000728c047b93 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Sat, 6 Jan 2024 08:25:56 +0000 Subject: [PATCH 24/32] add constants to specifiy the file type --- src/fd/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index f39af6da8b..bb54bdecc2 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -82,6 +82,10 @@ bitflags! { bitflags! { #[derive(Debug, Copy, Clone)] pub struct AccessPermission: u32 { + const S_IFMT = 0o170000; + const S_IFSOCK = 0140000; + const S_IFLNK = 0o120000; + const S_IFREG = 0o100000; const S_IRUSR = 0o400; const S_IWUSR = 0o200; const S_IXUSR = 0o100; From f66b3cb90124ecb942b3c2d85d3f51c57c7ed95c Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Sat, 6 Jan 2024 08:27:16 +0000 Subject: [PATCH 25/32] add option to support file metadata - usefull to determine the file size --- src/fs/mod.rs | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/fs/mod.rs b/src/fs/mod.rs index e31dfef378..b0eab13e77 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -4,6 +4,7 @@ mod mem; mod uhyve; use alloc::boxed::Box; +use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::vec::Vec; use core::ffi::CStr; @@ -393,7 +394,7 @@ pub fn readdir(name: &str) -> Result, IoError> { FILESYSTEM.get().ok_or(IoError::EINVAL)?.readdir(name) } -/// a +/// Open a directory to read the directory entries pub(crate) fn opendir(name: &str) -> Result { let obj = FILESYSTEM.get().unwrap().opendir(name)?; let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); @@ -409,8 +410,25 @@ pub fn file_attributes(path: &str) -> Result { FILESYSTEM.get().unwrap().lstat(path) } +#[derive(Debug, Copy, Clone)] +pub struct Metadata(FileAttr); + +impl Metadata { + pub fn new(attr: FileAttr) -> Self { + Self(attr) + } + + /// Returns the size of the file, in bytes + pub fn len(&self) -> usize { + self.0.st_size.try_into().unwrap() + } +} + #[derive(Debug)] -pub struct File(FileDescriptor); +pub struct File { + fd: FileDescriptor, + path: String, +} impl File { /// Creates a new file in read-write mode; error if the file exists. @@ -425,7 +443,10 @@ impl File { AccessPermission::from_bits(0o666).unwrap(), )?; - Ok(File(fd)) + Ok(File { + fd, + path: path.to_string(), + }) } /// Attempts to open a file in read-write mode. @@ -436,24 +457,31 @@ impl File { AccessPermission::from_bits(0o666).unwrap(), )?; - Ok(File(fd)) + Ok(File { + fd, + path: path.to_string(), + }) + } + + pub fn metadata(&self) -> Result { + Ok(Metadata::new(file_attributes(&self.path)?)) } } impl crate::io::Read for File { fn read(&mut self, buf: &mut [u8]) -> Result { - fd::read(self.0, buf) + fd::read(self.fd, buf) } } impl crate::io::Write for File { fn write(&mut self, buf: &[u8]) -> Result { - fd::write(self.0, buf) + fd::write(self.fd, buf) } } impl Drop for File { fn drop(&mut self) { - fd::close(self.0); + fd::close(self.fd); } } From f2d12f63eceb1138981a479006c2e628f0750be5 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Sat, 6 Jan 2024 08:28:40 +0000 Subject: [PATCH 26/32] fix bug in the reconstruction of the file path --- src/fs/fuse.rs | 45 ++++++++++++++++++++++++++++++++++++--------- src/fs/mem.rs | 8 ++++++-- src/fs/uhyve.rs | 12 ++++++++++-- 3 files changed, 52 insertions(+), 13 deletions(-) diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 5aa6412b32..f0fe16419a 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -1153,7 +1153,6 @@ impl FuseFileHandleInner { } fn read(&mut self, buf: &mut [u8]) -> Result { - debug!("FUSE read!"); let mut len = buf.len(); if len > MAX_READ_LEN { debug!("Reading longer than max_read_len: {}", len); @@ -1309,7 +1308,11 @@ impl VfsNode for FuseDirectory { let path: String = if components.is_empty() { "/".to_string() } else { - components.iter().map(|v| "/".to_owned() + v).collect() + components + .iter() + .rev() + .map(|v| "/".to_owned() + v) + .collect() }; debug!("FUSE opendir: {}", path); @@ -1388,7 +1391,11 @@ impl VfsNode for FuseDirectory { let path: String = if components.is_empty() { "/".to_string() } else { - components.iter().map(|v| "/".to_owned() + v).collect() + components + .iter() + .rev() + .map(|v| "/".to_owned() + v) + .collect() }; debug!("FUSE stat: {}", path); @@ -1421,7 +1428,11 @@ impl VfsNode for FuseDirectory { let path: String = if components.is_empty() { "/".to_string() } else { - components.iter().map(|v| "/".to_owned() + v).collect() + components + .iter() + .rev() + .map(|v| "/".to_owned() + v) + .collect() }; debug!("FUSE lstat: {}", path); @@ -1445,10 +1456,14 @@ impl VfsNode for FuseDirectory { let path: String = if components.is_empty() { "/".to_string() } else { - components.iter().map(|v| "/".to_owned() + v).collect() + components + .iter() + .rev() + .map(|v| "/".to_owned() + v) + .collect() }; - info!("FUSE open: {}, {:?} {:?}", path, opt, mode); + debug!("FUSE open: {}, {:?} {:?}", path, opt, mode); let file = FuseFileHandle::new(); @@ -1496,7 +1511,11 @@ impl VfsNode for FuseDirectory { let path: String = if components.is_empty() { "/".to_string() } else { - components.iter().map(|v| "/".to_owned() + v).collect() + components + .iter() + .rev() + .map(|v| "/".to_owned() + v) + .collect() }; let (cmd, mut rsp) = create_unlink(&path); @@ -1513,7 +1532,11 @@ impl VfsNode for FuseDirectory { let path: String = if components.is_empty() { "/".to_string() } else { - components.iter().map(|v| "/".to_owned() + v).collect() + components + .iter() + .rev() + .map(|v| "/".to_owned() + v) + .collect() }; let (cmd, mut rsp) = create_rmdir(&path); @@ -1534,7 +1557,11 @@ impl VfsNode for FuseDirectory { let path: String = if components.is_empty() { "/".to_string() } else { - components.iter().map(|v| "/".to_owned() + v).collect() + components + .iter() + .rev() + .map(|v| "/".to_owned() + v) + .collect() }; let (cmd, mut rsp) = create_mkdir(&path, mode.bits()); diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 061338934d..30a56bb935 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -164,7 +164,9 @@ impl VfsNode for RomFile { } fn get_file_attributes(&self) -> Result { - Ok(self.attr) + let mut attr = self.attr; + attr.st_size = self.data.read().len().try_into().unwrap(); + Ok(attr) } fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { @@ -214,7 +216,9 @@ impl VfsNode for RamFile { } fn get_file_attributes(&self) -> Result { - Ok(self.attr) + let mut attr = self.attr; + attr.st_size = self.data.read().len().try_into().unwrap(); + Ok(attr) } fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index 2c6926dca7..12b624b097 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -261,7 +261,11 @@ impl VfsNode for UhyveDirectory { let path: String = if components.is_empty() { "/\0".to_string() } else { - let mut path: String = components.iter().map(|v| "/".to_owned() + v).collect(); + let mut path: String = components + .iter() + .rev() + .map(|v| "/".to_owned() + v) + .collect(); path.push('\0'); path.remove(0); path @@ -281,7 +285,11 @@ impl VfsNode for UhyveDirectory { let path: String = if components.is_empty() { "/".to_string() } else { - components.iter().map(|v| "/".to_owned() + v).collect() + components + .iter() + .rev() + .map(|v| "/".to_owned() + v) + .collect() }; let mut sysunlink = SysUnlink::new(VirtAddr(path.as_ptr() as u64)); From 6342e970ae77f2f1edffd3eeed7ab63bb9dbb8b1 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Sat, 6 Jan 2024 09:49:55 +0000 Subject: [PATCH 27/32] extend AccessPermission to determine the file type --- src/fd/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index bb54bdecc2..ada94b5ffa 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -86,6 +86,10 @@ bitflags! { const S_IFSOCK = 0140000; const S_IFLNK = 0o120000; const S_IFREG = 0o100000; + const S_IFBLK = 0o060000; + const S_IFDIR = 0o040000; + const S_IFCHR = 0o020000; + const S_IFIFO = 0o010000; const S_IRUSR = 0o400; const S_IWUSR = 0o200; const S_IXUSR = 0o100; From 53e09bf026d14b0f157089a08ff642e575e68fee Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Sat, 6 Jan 2024 09:50:19 +0000 Subject: [PATCH 28/32] add comments and extend support of meta information - add functions to determine the file type and the file size --- src/fd/mod.rs | 2 +- src/fs/fuse.rs | 12 ++++++++++++ src/fs/mem.rs | 20 ++++++++++---------- src/fs/mod.rs | 32 +++++++++++++++++++++++++++----- 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index ada94b5ffa..f2f4909585 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -83,7 +83,7 @@ bitflags! { #[derive(Debug, Copy, Clone)] pub struct AccessPermission: u32 { const S_IFMT = 0o170000; - const S_IFSOCK = 0140000; + const S_IFSOCK = 0o140000; const S_IFLNK = 0o120000; const S_IFREG = 0o100000; const S_IFBLK = 0o060000; diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index f0fe16419a..a994a21363 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -187,20 +187,32 @@ unsafe impl FuseOut for fuse_entry_out {} #[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, } diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 30a56bb935..dc23653f21 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -148,7 +148,7 @@ impl Clone for RamFileInterface { } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub(crate) struct RomFile { data: Arc>, attr: FileAttr, @@ -171,7 +171,7 @@ impl VfsNode for RomFile { fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { if components.is_empty() { - Ok(self.attr) + self.get_file_attributes() } else { Err(IoError::EBADF) } @@ -179,7 +179,7 @@ impl VfsNode for RomFile { fn traverse_stat(&self, components: &mut Vec<&str>) -> Result { if components.is_empty() { - Ok(self.attr) + self.get_file_attributes() } else { Err(IoError::EBADF) } @@ -193,7 +193,7 @@ impl RomFile { slice::from_raw_parts(ptr, length) })), attr: FileAttr { - st_mode: mode, + st_mode: mode | AccessPermission::S_IFREG, ..Default::default() }, } @@ -223,7 +223,7 @@ impl VfsNode for RamFile { fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { if components.is_empty() { - Ok(self.attr) + self.get_file_attributes() } else { Err(IoError::EBADF) } @@ -231,7 +231,7 @@ impl VfsNode for RamFile { fn traverse_stat(&self, components: &mut Vec<&str>) -> Result { if components.is_empty() { - Ok(self.attr) + self.get_file_attributes() } else { Err(IoError::EBADF) } @@ -243,7 +243,7 @@ impl RamFile { Self { data: Arc::new(RwSpinLock::new(Vec::new())), attr: FileAttr { - st_mode: mode, + st_mode: mode | AccessPermission::S_IFREG, ..Default::default() }, } @@ -263,7 +263,7 @@ impl MemDirectory { Self { inner: Arc::new(RwSpinLock::new(BTreeMap::new())), attr: FileAttr { - st_mode: mode, + st_mode: mode | AccessPermission::S_IFDIR, ..Default::default() }, } @@ -392,7 +392,7 @@ impl VfsNode for MemDirectory { if components.is_empty() { if let Some(node) = self.inner.read().get(&node_name) { - node.get_file_attributes()?; + return node.get_file_attributes(); } } @@ -412,7 +412,7 @@ impl VfsNode for MemDirectory { if components.is_empty() { if let Some(node) = self.inner.read().get(&node_name) { - node.get_file_attributes()?; + return node.get_file_attributes(); } } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index b0eab13e77..cdec07096f 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -307,17 +307,27 @@ pub struct FileAttr { pub st_dev: u64, pub st_ino: u64, pub st_nlink: u64, + /// access permissions pub st_mode: AccessPermission, + /// user id pub st_uid: u32, + /// group id pub st_gid: u32, + /// device id pub st_rdev: u64, + /// size in bytes pub st_size: i64, + /// block size pub st_blksize: i64, + /// size in blocks pub st_blocks: i64, + /// time of last access pub st_atime: i64, pub st_atime_nsec: i64, + /// time of last modification pub st_mtime: i64, pub st_mtime_nsec: i64, + /// time of last status change pub st_ctime: i64, pub st_ctime_nsec: i64, } @@ -410,18 +420,30 @@ pub fn file_attributes(path: &str) -> Result { FILESYSTEM.get().unwrap().lstat(path) } +#[allow(clippy::len_without_is_empty)] #[derive(Debug, Copy, Clone)] pub struct Metadata(FileAttr); impl Metadata { - pub fn new(attr: FileAttr) -> Self { - Self(attr) - } - /// Returns the size of the file, in bytes pub fn len(&self) -> usize { self.0.st_size.try_into().unwrap() } + + /// Returns true if this metadata is for a file. + pub fn is_file(&self) -> bool { + self.0.st_mode.contains(AccessPermission::S_IFREG) + } + + /// Returns true if this metadata is for a directory. + pub fn is_dir(&self) -> bool { + self.0.st_mode.contains(AccessPermission::S_IFDIR) + } +} + +/// Given a path, query the file system to get information about a file, directory, etc. +pub fn metadata(path: &str) -> Result { + Ok(Metadata(file_attributes(path)?)) } #[derive(Debug)] @@ -464,7 +486,7 @@ impl File { } pub fn metadata(&self) -> Result { - Ok(Metadata::new(file_attributes(&self.path)?)) + metadata(&self.path) } } From c91f514a4f3cdbfa7a2a22a73570f7bcd614f261 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Sat, 6 Jan 2024 13:05:44 +0000 Subject: [PATCH 29/32] extend Metadata to determine last access and modification time --- src/fs/fuse.rs | 14 ++-- src/fs/mem.rs | 162 +++++++++++++++++++++++++++++------------- src/fs/mod.rs | 29 ++++++-- src/lib.rs | 1 + src/syscalls/futex.rs | 4 +- src/syscalls/tasks.rs | 2 +- src/syscalls/timer.rs | 45 ++---------- src/time.rs | 80 +++++++++++++++++++++ tests/thread.rs | 3 +- 9 files changed, 232 insertions(+), 108 deletions(-) create mode 100644 src/time.rs diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index a994a21363..5b3777a201 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -226,15 +226,15 @@ impl From for FileAttr { st_uid: attr.uid, st_gid: attr.gid, st_rdev: attr.rdev as u64, - st_size: attr.size.try_into().unwrap(), + st_size: attr.size, st_blksize: attr.blksize as i64, st_blocks: attr.blocks.try_into().unwrap(), - st_atime: attr.atime.try_into().unwrap(), - st_atime_nsec: attr.atimensec as i64, - st_mtime: attr.mtime.try_into().unwrap(), - st_mtime_nsec: attr.atimensec as i64, - st_ctime: attr.ctime.try_into().unwrap(), - st_ctime_nsec: attr.ctimensec as i64, + 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() } } diff --git a/src/fs/mem.rs b/src/fs/mem.rs index dc23653f21..e43a147366 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -14,25 +14,47 @@ use alloc::collections::BTreeMap; use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::vec::Vec; -use core::ops::{Deref, DerefMut}; use core::slice; use hermit_sync::{RwSpinLock, SpinMutex}; +use crate::arch; use crate::fd::{AccessPermission, IoError, ObjectInterface, OpenOption}; use crate::fs::{DirectoryEntry, FileAttr, NodeKind, VfsNode}; +#[derive(Debug, Clone)] +pub(crate) struct RomFileInner { + pub data: &'static [u8], + pub attr: FileAttr, +} + +impl RomFileInner { + pub unsafe fn new(ptr: *const u8, length: usize, attr: FileAttr) -> Self { + Self { + data: unsafe { slice::from_raw_parts(ptr, length) }, + attr, + } + } +} + #[derive(Debug)] struct RomFileInterface { /// Position within the file pos: SpinMutex, /// File content - data: Arc>, + inner: Arc>, } impl ObjectInterface for RomFileInterface { fn read(&self, buf: &mut [u8]) -> Result { - let vec = self.data.read(); + { + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + let mut guard = self.inner.write(); + guard.attr.st_atime = microseconds / 1_000_000; + guard.attr.st_atime_nsec = (microseconds % 1_000_000) * 1000; + } + + let vec = self.inner.read().data; let mut pos_guard = self.pos.lock(); let pos = *pos_guard; @@ -54,16 +76,15 @@ impl ObjectInterface for RomFileInterface { } impl RomFileInterface { - pub fn new(data: Arc>) -> Self { + pub fn new(inner: Arc>) -> Self { Self { pos: SpinMutex::new(0), - data, + inner, } } pub fn len(&self) -> usize { - let guard = self.data.read(); - guard.len() + self.inner.read().data.len() } } @@ -71,7 +92,22 @@ impl Clone for RomFileInterface { fn clone(&self) -> Self { Self { pos: SpinMutex::new(*self.pos.lock()), - data: self.data.clone(), + inner: self.inner.clone(), + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct RamFileInner { + pub data: Vec, + pub attr: FileAttr, +} + +impl RamFileInner { + pub fn new(attr: FileAttr) -> Self { + Self { + data: Vec::new(), + attr, } } } @@ -81,43 +117,56 @@ pub struct RamFileInterface { /// Position within the file pos: SpinMutex, /// File content - data: Arc>>, + inner: Arc>, } impl ObjectInterface for RamFileInterface { fn read(&self, buf: &mut [u8]) -> Result { - let guard = self.data.read(); - let vec = guard.deref(); + { + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + let mut guard = self.inner.write(); + guard.attr.st_atime = microseconds / 1_000_000; + guard.attr.st_atime_nsec = (microseconds % 1_000_000) * 1000; + } + + let guard = self.inner.read(); let mut pos_guard = self.pos.lock(); let pos = *pos_guard; - if pos >= vec.len() { + if pos >= guard.data.len() { return Ok(0); } - let len = if vec.len() - pos < buf.len() { - vec.len() - pos + let len = if guard.data.len() - pos < buf.len() { + guard.data.len() - pos } else { buf.len() }; - buf[0..len].clone_from_slice(&vec[pos..pos + len]); + buf[0..len].clone_from_slice(&guard.data[pos..pos + len]); *pos_guard = pos + len; Ok(len) } fn write(&self, buf: &[u8]) -> Result { - let mut guard = self.data.write(); - let vec = guard.deref_mut(); + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + let mut guard = self.inner.write(); let mut pos_guard = self.pos.lock(); let pos = *pos_guard; - if pos + buf.len() > vec.len() { - vec.resize(pos + buf.len(), 0); + if pos + buf.len() > guard.data.len() { + guard.data.resize(pos + buf.len(), 0); + guard.attr.st_size = guard.data.len().try_into().unwrap(); } - - vec[pos..pos + buf.len()].clone_from_slice(buf); + guard.attr.st_atime = microseconds / 1_000_000; + guard.attr.st_atime_nsec = (microseconds % 1_000_000) * 1000; + guard.attr.st_mtime = guard.attr.st_atime; + guard.attr.st_mtime_nsec = guard.attr.st_atime_nsec; + guard.attr.st_ctime = guard.attr.st_atime; + guard.attr.st_ctime_nsec = guard.attr.st_atime_nsec; + + guard.data[pos..pos + buf.len()].clone_from_slice(buf); *pos_guard = pos + buf.len(); Ok(buf.len()) @@ -125,17 +174,15 @@ impl ObjectInterface for RamFileInterface { } impl RamFileInterface { - pub fn new(data: Arc>>) -> Self { + pub fn new(inner: Arc>) -> Self { Self { pos: SpinMutex::new(0), - data, + inner, } } pub fn len(&self) -> usize { - let guard = self.data.read(); - let vec: &Vec = guard.deref(); - vec.len() + self.inner.read().data.len() } } @@ -143,15 +190,14 @@ impl Clone for RamFileInterface { fn clone(&self) -> Self { Self { pos: SpinMutex::new(*self.pos.lock()), - data: self.data.clone(), + inner: self.inner.clone(), } } } #[derive(Debug)] pub(crate) struct RomFile { - data: Arc>, - attr: FileAttr, + data: Arc>, } impl VfsNode for RomFile { @@ -164,9 +210,7 @@ impl VfsNode for RomFile { } fn get_file_attributes(&self) -> Result { - let mut attr = self.attr; - attr.st_size = self.data.read().len().try_into().unwrap(); - Ok(attr) + Ok(self.data.read().attr) } fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { @@ -188,22 +232,28 @@ impl VfsNode for RomFile { impl RomFile { pub unsafe fn new(ptr: *const u8, length: usize, mode: AccessPermission) -> Self { + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + let attr = FileAttr { + st_size: length.try_into().unwrap(), + st_mode: mode | AccessPermission::S_IFREG, + st_atime: microseconds / 1_000_000, + st_atime_nsec: (microseconds % 1_000_000) * 1000, + st_mtime: microseconds / 1_000_000, + st_mtime_nsec: (microseconds % 1_000_000) * 1000, + st_ctime: microseconds / 1_000_000, + st_ctime_nsec: (microseconds % 1_000_000) * 1000, + ..Default::default() + }; + Self { - data: Arc::new(RwSpinLock::new(unsafe { - slice::from_raw_parts(ptr, length) - })), - attr: FileAttr { - st_mode: mode | AccessPermission::S_IFREG, - ..Default::default() - }, + data: unsafe { Arc::new(RwSpinLock::new(RomFileInner::new(ptr, length, attr))) }, } } } #[derive(Debug, Clone)] pub(crate) struct RamFile { - data: Arc>>, - attr: FileAttr, + data: Arc>, } impl VfsNode for RamFile { @@ -216,9 +266,7 @@ impl VfsNode for RamFile { } fn get_file_attributes(&self) -> Result { - let mut attr = self.attr; - attr.st_size = self.data.read().len().try_into().unwrap(); - Ok(attr) + Ok(self.data.read().attr) } fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { @@ -240,12 +288,20 @@ impl VfsNode for RamFile { impl RamFile { pub fn new(mode: AccessPermission) -> Self { + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + let attr = FileAttr { + st_mode: mode | AccessPermission::S_IFREG, + st_atime: microseconds / 1_000_000, + st_atime_nsec: (microseconds % 1_000_000) * 1000, + st_mtime: microseconds / 1_000_000, + st_mtime_nsec: (microseconds % 1_000_000) * 1000, + st_ctime: microseconds / 1_000_000, + st_ctime_nsec: (microseconds % 1_000_000) * 1000, + ..Default::default() + }; + Self { - data: Arc::new(RwSpinLock::new(Vec::new())), - attr: FileAttr { - st_mode: mode | AccessPermission::S_IFREG, - ..Default::default() - }, + data: Arc::new(RwSpinLock::new(RamFileInner::new(attr))), } } } @@ -260,10 +316,18 @@ pub(crate) struct MemDirectory { impl MemDirectory { pub fn new(mode: AccessPermission) -> Self { + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + Self { inner: Arc::new(RwSpinLock::new(BTreeMap::new())), attr: FileAttr { st_mode: mode | AccessPermission::S_IFDIR, + st_atime: microseconds / 1_000_000, + st_atime_nsec: (microseconds % 1_000_000) * 1000, + st_mtime: microseconds / 1_000_000, + st_mtime_nsec: (microseconds % 1_000_000) * 1000, + st_ctime: microseconds / 1_000_000, + st_ctime_nsec: (microseconds % 1_000_000) * 1000, ..Default::default() }, } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index cdec07096f..101f7da748 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -18,6 +18,7 @@ use crate::fd::{ insert_object, AccessPermission, IoError, ObjectInterface, OpenOption, FD_COUNTER, }; use crate::io::Write; +use crate::time::{timespec, SystemTime}; pub(crate) static FILESYSTEM: OnceCell = OnceCell::new(); @@ -316,20 +317,20 @@ pub struct FileAttr { /// device id pub st_rdev: u64, /// size in bytes - pub st_size: i64, + pub st_size: u64, /// block size pub st_blksize: i64, /// size in blocks pub st_blocks: i64, /// time of last access - pub st_atime: i64, - pub st_atime_nsec: i64, + pub st_atime: u64, + pub st_atime_nsec: u64, /// time of last modification - pub st_mtime: i64, - pub st_mtime_nsec: i64, + pub st_mtime: u64, + pub st_mtime_nsec: u64, /// time of last status change - pub st_ctime: i64, - pub st_ctime_nsec: i64, + pub st_ctime: u64, + pub st_ctime_nsec: u64, } #[derive(Debug, FromPrimitive, ToPrimitive)] @@ -439,6 +440,20 @@ impl Metadata { pub fn is_dir(&self) -> bool { self.0.st_mode.contains(AccessPermission::S_IFDIR) } + + /// Returns the last modification time listed in this metadata. + pub fn modified(&self) -> Result { + Ok(SystemTime::from(timespec::from_usec( + self.0.st_mtime * 1_000_000 + self.0.st_mtime_nsec / 1000, + ))) + } + + /// Returns the last modification time listed in this metadata. + pub fn accessed(&self) -> Result { + Ok(SystemTime::from(timespec::from_usec( + self.0.st_atime * 1_000_000 + self.0.st_atime_nsec / 1000, + ))) + } } /// Given a path, query the file system to get information about a file, directory, etc. diff --git a/src/lib.rs b/src/lib.rs index 8752cde908..f4944e4f33 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -91,6 +91,7 @@ mod mm; mod scheduler; mod synch; pub mod syscalls; +pub mod time; #[cfg(target_os = "none")] hermit_entry::define_entry_version!(); diff --git a/src/syscalls/futex.rs b/src/syscalls/futex.rs index db301880fc..649aa46750 100644 --- a/src/syscalls/futex.rs +++ b/src/syscalls/futex.rs @@ -2,7 +2,7 @@ use core::sync::atomic::AtomicU32; use crate::errno::EINVAL; use crate::synch::futex::{self as synch, Flags}; -use crate::{timespec, timespec_to_microseconds}; +use crate::time::timespec; /// Like `synch::futex_wait`, but does extra sanity checks and takes a `timespec`. /// @@ -24,7 +24,7 @@ extern "C" fn __sys_futex_wait( let timeout = if timeout.is_null() { None } else { - match timespec_to_microseconds(unsafe { timeout.read() }) { + match unsafe { timeout.read().into_usec() } { t @ Some(_) => t, None => return -EINVAL, } diff --git a/src/syscalls/tasks.rs b/src/syscalls/tasks.rs index e4962dae15..62ec673715 100644 --- a/src/syscalls/tasks.rs +++ b/src/syscalls/tasks.rs @@ -15,7 +15,7 @@ use crate::errno::*; use crate::mm::{task_heap_end, task_heap_start}; use crate::scheduler::task::{Priority, TaskHandle, TaskId}; use crate::scheduler::PerCoreSchedulerExt; -use crate::syscalls::timer::timespec; +use crate::time::timespec; use crate::{arch, scheduler, syscalls}; #[cfg(feature = "newlib")] diff --git a/src/syscalls/timer.rs b/src/syscalls/timer.rs index 87a2df5d04..75ecf49a65 100644 --- a/src/syscalls/timer.rs +++ b/src/syscalls/timer.rs @@ -1,27 +1,7 @@ use crate::arch; use crate::errno::*; use crate::syscalls::__sys_usleep; - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub struct itimerval { - pub it_interval: timeval, - pub it_value: timeval, -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub struct timespec { - pub tv_sec: i64, - pub tv_nsec: i64, -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub struct timeval { - pub tv_sec: i64, - pub tv_usec: i64, -} +use crate::time::{itimerval, timespec, timeval}; pub(crate) const CLOCK_REALTIME: u64 = 1; pub(crate) const CLOCK_PROCESS_CPUTIME_ID: u64 = 2; @@ -29,23 +9,6 @@ pub(crate) const CLOCK_THREAD_CPUTIME_ID: u64 = 3; pub(crate) const CLOCK_MONOTONIC: u64 = 4; pub(crate) const TIMER_ABSTIME: i32 = 4; -fn microseconds_to_timespec(microseconds: u64, result: &mut timespec) { - result.tv_sec = (microseconds / 1_000_000) as i64; - result.tv_nsec = ((microseconds % 1_000_000) * 1000) as i64; -} - -fn microseconds_to_timeval(microseconds: u64, result: &mut timeval) { - result.tv_sec = (microseconds / 1_000_000) as i64; - result.tv_usec = (microseconds % 1_000_000) as i64; -} - -pub(crate) fn timespec_to_microseconds(time: timespec) -> Option { - u64::try_from(time.tv_sec) - .ok() - .and_then(|secs| secs.checked_mul(1_000_000)) - .and_then(|millions| millions.checked_add(u64::try_from(time.tv_nsec).ok()? / 1000)) -} - /// Finds the resolution (or precision) of a clock. /// /// This function gets the clock resolution of the clock with `clock_id` and stores it in parameter `res`. @@ -66,7 +29,7 @@ extern "C" fn __sys_clock_getres(clock_id: u64, res: *mut timespec) -> i32 { match clock_id { CLOCK_REALTIME | CLOCK_PROCESS_CPUTIME_ID | CLOCK_THREAD_CPUTIME_ID | CLOCK_MONOTONIC => { // All clocks in Hermit have 1 microsecond resolution. - microseconds_to_timespec(1, result); + *result = timespec::from_usec(1); 0 } _ => { @@ -104,7 +67,7 @@ extern "C" fn __sys_clock_gettime(clock_id: u64, tp: *mut timespec) -> i32 { microseconds += arch::get_boot_time(); } - microseconds_to_timespec(microseconds, result); + *result = timespec::from_usec(microseconds); 0 } _ => { @@ -202,7 +165,7 @@ extern "C" fn __sys_gettimeofday(tp: *mut timeval, tz: usize) -> i32 { // Return the current time based on the wallclock time when we were booted up // plus the current timer ticks. let microseconds = arch::get_boot_time() + arch::processor::get_timer_ticks(); - microseconds_to_timeval(microseconds, result); + *result = timeval::from_usec(microseconds); } if tz > 0 { diff --git a/src/time.rs b/src/time.rs new file mode 100644 index 0000000000..d2633d14b9 --- /dev/null +++ b/src/time.rs @@ -0,0 +1,80 @@ +use crate::arch; + +/// Represent the number of seconds and microseconds since +/// the Epoch (1970-01-01 00:00:00 +0000 (UTC)) +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct timeval { + /// seconds + pub tv_sec: i64, + /// microseconds + pub tv_usec: i64, +} + +impl timeval { + pub fn from_usec(microseconds: u64) -> Self { + Self { + tv_sec: (microseconds / 1_000_000) as i64, + tv_usec: (microseconds % 1_000_000) as i64, + } + } + + pub fn into_usec(&self) -> Option { + u64::try_from(self.tv_sec) + .ok() + .and_then(|secs| secs.checked_mul(1_000_000)) + .and_then(|millions| millions.checked_add(u64::try_from(self.tv_usec).ok()?)) + } +} + +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct itimerval { + pub it_interval: timeval, + pub it_value: timeval, +} + +/// Represent the number of seconds and nanoseconds since +/// the Epoch (1970-01-01 00:00:00 +0000 (UTC)) +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub struct timespec { + /// seconds + pub tv_sec: i64, + /// nanoseconds + pub tv_nsec: i64, +} + +impl timespec { + pub fn from_usec(microseconds: u64) -> Self { + Self { + tv_sec: (microseconds / 1_000_000) as i64, + tv_nsec: ((microseconds % 1_000_000) * 1000) as i64, + } + } + + pub fn into_usec(&self) -> Option { + u64::try_from(self.tv_sec) + .ok() + .and_then(|secs| secs.checked_mul(1_000_000)) + .and_then(|millions| millions.checked_add(u64::try_from(self.tv_nsec).ok()? / 1000)) + } +} + +#[derive(Copy, Clone, Debug)] +pub struct SystemTime(timespec); + +impl SystemTime { + /// Returns the system time corresponding to "now". + pub fn now() -> Self { + let microseconds = arch::processor::get_timer_ticks() + arch::get_boot_time(); + + Self(timespec::from_usec(microseconds)) + } +} + +impl From for SystemTime { + fn from(t: timespec) -> Self { + Self(t) + } +} diff --git a/tests/thread.rs b/tests/thread.rs index 52ba5f3b1a..9a624b1e9b 100644 --- a/tests/thread.rs +++ b/tests/thread.rs @@ -18,7 +18,8 @@ mod common; use alloc::vec; use hermit::errno::{EAGAIN, ETIMEDOUT}; -use hermit::{sys_futex_wait, sys_futex_wake, sys_join, sys_spawn2, sys_usleep, timespec}; +use hermit::time::timespec; +use hermit::{sys_futex_wait, sys_futex_wake, sys_join, sys_spawn2, sys_usleep}; const USER_STACK_SIZE: usize = 1_048_576; const NORMAL_PRIO: u8 = 2; From d1d5b755ee00736ac37a1b6eea772fd33d4f306c Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Sat, 6 Jan 2024 23:27:02 +0100 Subject: [PATCH 30/32] allows the creation of a file at any position of the fs --- src/fs/mem.rs | 41 ++++++++++++++++++++++++----------------- src/fs/mod.rs | 25 ++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/fs/mem.rs b/src/fs/mem.rs index e43a147366..34de4da98d 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -332,23 +332,6 @@ impl MemDirectory { }, } } - - pub fn create_file( - &self, - name: &str, - ptr: *const u8, - length: usize, - mode: AccessPermission, - ) -> Result<(), IoError> { - let name = name.trim(); - if name.find('/').is_none() { - let file = unsafe { RomFile::new(ptr, length, mode) }; - self.inner.write().insert(name.to_string(), Box::new(file)); - Ok(()) - } else { - Err(IoError::EBADF) - } - } } impl VfsNode for MemDirectory { @@ -548,4 +531,28 @@ impl VfsNode for MemDirectory { Err(IoError::ENOENT) } + + fn traverse_create_file( + &self, + components: &mut Vec<&str>, + ptr: *const u8, + length: usize, + mode: AccessPermission, + ) -> Result<(), IoError> { + if let Some(component) = components.pop() { + let name = String::from(component); + + if components.is_empty() { + let file = unsafe { RomFile::new(ptr, length, mode) }; + self.inner.write().insert(name.to_string(), Box::new(file)); + return Ok(()); + } + + if let Some(directory) = self.inner.read().get(&name) { + return directory.traverse_create_file(components, ptr, length, mode); + } + } + + Err(IoError::ENOENT) + } } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 101f7da748..dfae99306c 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -134,6 +134,17 @@ pub(crate) trait VfsNode: core::fmt::Debug { ) -> Result, IoError> { Err(IoError::ENOSYS) } + + /// Helper function to create a read-only file + fn traverse_create_file( + &self, + _components: &mut Vec<&str>, + _ptr: *const u8, + _length: usize, + _mode: AccessPermission, + ) -> Result<(), IoError> { + Err(IoError::ENOSYS) + } } #[derive(Debug)] @@ -290,15 +301,23 @@ impl Filesystem { self.root.traverse_mount(&mut components, obj) } - /// Create file from ROM + /// Create read-only file pub unsafe fn create_file( &self, - name: &str, + path: &str, ptr: *const u8, length: usize, mode: AccessPermission, ) -> Result<(), IoError> { - self.root.create_file(name, ptr, length, mode) + debug!("Create read-only file {}", path); + + let mut components: Vec<&str> = path.split('/').collect(); + + components.reverse(); + components.pop(); + + self.root + .traverse_create_file(&mut components, ptr, length, mode) } } From f4205b83e3ec3cf06c9f491f132af6f33844eb32 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Tue, 9 Jan 2024 15:27:59 +0100 Subject: [PATCH 31/32] replace sys_readdir by sys_getdents64 sys_getdents64 is compatible to Linux' system call getdents64. Consequently, it offers the reusing of an existing ABI. --- src/fd/mod.rs | 3 +- src/fs/fuse.rs | 4 ++- src/fs/mem.rs | 2 +- src/fs/mod.rs | 64 ++++++------------------------------- src/lib.rs | 8 ++--- src/syscalls/mod.rs | 78 +++++++++++++++++++++++++++++++-------------- 6 files changed, 74 insertions(+), 85 deletions(-) diff --git a/src/fd/mod.rs b/src/fd/mod.rs index f2f4909585..2fc4cffcc9 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -1,4 +1,5 @@ use alloc::sync::Arc; +use alloc::vec::Vec; use core::sync::atomic::{AtomicI32, Ordering}; use ahash::RandomState; @@ -147,7 +148,7 @@ pub(crate) 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 /// pointed to by the file descriptor - fn readdir(&self) -> Result, IoError> { + fn readdir(&self) -> Result, IoError> { Err(IoError::EINVAL) } diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index 5b3777a201..802ffb2e6e 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -1387,7 +1387,9 @@ impl VfsNode for FuseDirectory { dirent.d_namelen.try_into().unwrap(), ) }; - entries.push(DirectoryEntry::new(name)); + entries.push(DirectoryEntry::new(unsafe { + core::str::from_utf8_unchecked(name).to_string() + })); } let (cmd, mut rsp) = create_release(fuse_nid, fuse_fh); diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 34de4da98d..92b5565a81 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -426,7 +426,7 @@ impl VfsNode for MemDirectory { } else { let mut entries: Vec = Vec::new(); for name in self.inner.read().keys() { - entries.push(DirectoryEntry::new(name.as_bytes())); + entries.push(DirectoryEntry::new(name.to_string())); } Ok(entries) diff --git a/src/fs/mod.rs b/src/fs/mod.rs index dfae99306c..0f0a77c653 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -7,9 +7,7 @@ use alloc::boxed::Box; use alloc::string::{String, ToString}; use alloc::sync::Arc; use alloc::vec::Vec; -use core::ffi::CStr; -use core::fmt; -use core::sync::atomic::{AtomicUsize, Ordering}; +use core::sync::atomic::Ordering; use hermit_sync::OnceCell; use mem::MemDirectory; @@ -22,36 +20,14 @@ use crate::time::{timespec, SystemTime}; pub(crate) static FILESYSTEM: OnceCell = OnceCell::new(); -pub const MAX_NAME_LENGTH: usize = 256; - -#[repr(C)] -#[derive(Copy, Clone)] +#[derive(Debug, Clone)] pub struct DirectoryEntry { - pub d_name: [u8; MAX_NAME_LENGTH], + pub name: String, } impl DirectoryEntry { - pub fn new(d_name: &[u8]) -> Self { - let len = core::cmp::min(d_name.len(), MAX_NAME_LENGTH); - let mut entry = Self { - d_name: [0; MAX_NAME_LENGTH], - }; - - entry.d_name[..len].copy_from_slice(&d_name[..len]); - - entry - } -} - -impl fmt::Debug for DirectoryEntry { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let d_name = unsafe { CStr::from_ptr(self.d_name.as_ptr() as _) } - .to_str() - .unwrap(); - - f.debug_struct("DirectoryEntry") - .field("d_name", &d_name) - .finish() + pub fn new(name: String) -> Self { + Self { name } } } @@ -147,38 +123,18 @@ pub(crate) trait VfsNode: core::fmt::Debug { } } -#[derive(Debug)] -struct DirectoryReader { - pos: AtomicUsize, - data: Vec, -} +#[derive(Debug, Clone)] +struct DirectoryReader(Vec); impl DirectoryReader { pub fn new(data: Vec) -> Self { - Self { - pos: AtomicUsize::new(0), - data, - } + Self(data) } } impl ObjectInterface for DirectoryReader { - fn readdir(&self) -> Result, IoError> { - let pos = self.pos.fetch_add(1, Ordering::SeqCst); - if pos < self.data.len() { - Ok(Some(self.data[pos])) - } else { - Ok(None) - } - } -} - -impl Clone for DirectoryReader { - fn clone(&self) -> Self { - Self { - pos: AtomicUsize::new(self.pos.load(Ordering::SeqCst)), - data: self.data.clone(), - } + fn readdir(&self) -> Result, IoError> { + Ok(self.0.clone()) } } diff --git a/src/lib.rs b/src/lib.rs index f4944e4f33..799948874e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -252,9 +252,9 @@ pub(crate) extern "C" fn __sys_free(ptr: *mut u8, size: usize, align: usize) { #[cfg(target_os = "none")] extern "C" fn initd(_arg: usize) { extern "C" { - #[cfg(all(not(test), not(feature = "syscall")))] + #[cfg(all(not(test), not(feature = "common-os")))] fn runtime_entry(argc: i32, argv: *const *const u8, env: *const *const u8) -> !; - #[cfg(all(not(test), feature = "syscall"))] + #[cfg(all(not(test), feature = "common-os"))] fn main(argc: i32, argv: *const *const u8, env: *const *const u8); #[cfg(feature = "newlib")] fn init_lwip(); @@ -297,9 +297,9 @@ extern "C" fn initd(_arg: usize) { #[cfg(not(test))] unsafe { // And finally start the application. - #[cfg(all(not(test), not(feature = "syscall")))] + #[cfg(all(not(test), not(feature = "common-os")))] runtime_entry(argc, argv, environ); - #[cfg(all(not(test), feature = "syscall"))] + #[cfg(all(not(test), feature = "common-os"))] main(argc, argv, environ); } #[cfg(test)] diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index b396a33530..7a805d36de 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -1,6 +1,7 @@ #![allow(clippy::result_unit_err)] use core::ffi::CStr; +use core::marker::PhantomData; #[cfg(feature = "newlib")] use hermit_sync::InterruptTicketMutex; @@ -21,7 +22,7 @@ use crate::env; use crate::fd::{ dup_object, get_object, remove_object, AccessPermission, FileDescriptor, IoCtl, OpenOption, }; -use crate::fs::{self, DirectoryEntry, FileAttr}; +use crate::fs::{self, FileAttr}; use crate::syscalls::interfaces::SyscallInterface; #[cfg(target_os = "none")] use crate::{__sys_free, __sys_malloc, __sys_realloc}; @@ -335,32 +336,65 @@ 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, - dirent: *mut DirectoryEntry, - dirent_len: *mut usize, -) -> i32 { - if dirent.is_null() || dirent_len.is_null() { - return -crate::errno::EINVAL; +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct Dirent64 { + /// 64-bit inode number + pub d_ino: u64, + /// 64-bit offset to next structure + pub d_off: i64, + /// Size of this dirent + pub d_reclen: u16, + /// File type + pub d_type: u8, + /// Filename (null-terminated) + pub d_name: PhantomData, +} + +extern "C" fn __sys_getdents64(fd: FileDescriptor, dirp: *mut Dirent64, count: usize) -> i64 { + if dirp.is_null() || count == 0 { + return -crate::errno::EINVAL as i64; } - let dirent = unsafe { &mut *dirent }; - let dirent_len = unsafe { &mut *dirent_len }; + let limit = dirp as usize + count; + let mut dirp: *mut Dirent64 = dirp; + let mut offset: i64 = 0; let obj = get_object(fd); obj.map_or_else( - |_| -crate::errno::EINVAL, + |_| -crate::errno::EINVAL as i64, |v| { (*v).readdir().map_or_else( - |e| -num::ToPrimitive::to_i32(&e).unwrap(), + |e| -num::ToPrimitive::to_i64(&e).unwrap(), |v| { - if let Some(v) = v { - *dirent = v; - *dirent_len = core::mem::size_of::(); - } else { - *dirent_len = 0; + for i in v.iter() { + let len = i.name.len(); + if dirp as usize + core::mem::size_of::() + len + 1 >= limit { + return -crate::errno::EINVAL as i64; + } + + let dir = unsafe { &mut *dirp }; + + dir.d_ino = 0; + dir.d_type = 0; + dir.d_reclen = (core::mem::size_of::() + len + 1) + .try_into() + .unwrap(); + offset += i64::from(dir.d_reclen); + dir.d_off = offset; + + // copy null-terminated filename + let s = &mut dir.d_name as *mut _ as *mut u8; + unsafe { + core::ptr::copy_nonoverlapping(i.name.as_ptr(), s, len); + s.add(len).write_bytes(0, 1); + } + + dirp = unsafe { + (dirp as *mut u8).add(dir.d_reclen as usize) as *mut Dirent64 + }; } - 0 + offset }, ) }, @@ -368,12 +402,8 @@ extern "C" fn __sys_readdir( } #[no_mangle] -pub extern "C" fn sys_readdir( - fd: FileDescriptor, - dirent: *mut DirectoryEntry, - dirent_len: *mut usize, -) -> i32 { - kernel_function!(__sys_readdir(fd, dirent, dirent_len)) +pub extern "C" fn sys_getdents64(fd: FileDescriptor, dirp: *mut Dirent64, count: usize) -> i64 { + kernel_function!(__sys_getdents64(fd, dirp, count)) } extern "C" fn __sys_dup(fd: i32) -> i32 { From 7aa591ae77e92e73b7e7d67a8feba06ef339d236 Mon Sep 17 00:00:00 2001 From: Stefan Lankes Date: Mon, 15 Jan 2024 22:13:26 +0100 Subject: [PATCH 32/32] remove obsolete clone operations - according to POSIX standard, the position between file descriptors will be shared after a dup operation --- src/fs/mem.rs | 34 ++++++++-------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 92b5565a81..8c15ee5a3c 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -22,7 +22,7 @@ use crate::arch; use crate::fd::{AccessPermission, IoError, ObjectInterface, OpenOption}; use crate::fs::{DirectoryEntry, FileAttr, NodeKind, VfsNode}; -#[derive(Debug, Clone)] +#[derive(Debug)] pub(crate) struct RomFileInner { pub data: &'static [u8], pub attr: FileAttr, @@ -37,10 +37,10 @@ impl RomFileInner { } } -#[derive(Debug)] +#[derive(Debug, Clone)] struct RomFileInterface { /// Position within the file - pos: SpinMutex, + pos: Arc>, /// File content inner: Arc>, } @@ -78,7 +78,7 @@ impl ObjectInterface for RomFileInterface { impl RomFileInterface { pub fn new(inner: Arc>) -> Self { Self { - pos: SpinMutex::new(0), + pos: Arc::new(SpinMutex::new(0)), inner, } } @@ -88,16 +88,7 @@ impl RomFileInterface { } } -impl Clone for RomFileInterface { - fn clone(&self) -> Self { - Self { - pos: SpinMutex::new(*self.pos.lock()), - inner: self.inner.clone(), - } - } -} - -#[derive(Debug, Clone)] +#[derive(Debug)] pub(crate) struct RamFileInner { pub data: Vec, pub attr: FileAttr, @@ -112,10 +103,10 @@ impl RamFileInner { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct RamFileInterface { /// Position within the file - pos: SpinMutex, + pos: Arc>, /// File content inner: Arc>, } @@ -176,7 +167,7 @@ impl ObjectInterface for RamFileInterface { impl RamFileInterface { pub fn new(inner: Arc>) -> Self { Self { - pos: SpinMutex::new(0), + pos: Arc::new(SpinMutex::new(0)), inner, } } @@ -186,15 +177,6 @@ impl RamFileInterface { } } -impl Clone for RamFileInterface { - fn clone(&self) -> Self { - Self { - pos: SpinMutex::new(*self.pos.lock()), - inner: self.inner.clone(), - } - } -} - #[derive(Debug)] pub(crate) struct RomFile { data: Arc>,