diff --git a/Cargo.toml b/Cargo.toml index c7c732dcb7..4cd8155f7c 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 = [] @@ -92,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.11" diff --git a/src/drivers/mod.rs b/src/drivers/mod.rs index 629e53076a..9d31c91c5c 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 c4cc6ee1c7..631b782d59 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/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/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 b66fff58ae..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::syscalls::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 83c02a21b8..2fc4cffcc9 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -1,39 +1,58 @@ use alloc::sync::Arc; -use core::ffi::{c_void, CStr}; -#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] -use core::ptr; +use alloc::vec::Vec; use core::sync::atomic::{AtomicI32, Ordering}; use ahash::RandomState; use dyn_clone::DynClone; use hashbrown::HashMap; -#[cfg(target_arch = "x86_64")] -use x86::io::*; +#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] +use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; -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::syscalls::fs::{self, Dirent, FileAttr, FilePerms, SeekWhence}; -#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] -use crate::syscalls::net::*; +use crate::fs::{self, DirectoryEntry, FileAttr, SeekWhence}; -mod file; #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] -pub mod socket; +pub(crate) 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 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, + EEXIST = crate::errno::EEXIST as isize, + EADDRINUSE = crate::errno::EADDRINUSE as isize, +} + +#[allow(dead_code)] +#[derive(Debug, PartialEq)] +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 @@ -44,279 +63,152 @@ 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); - -// TODO: these are defined in hermit-abi. Should we use a constants crate imported in both? -//const O_RDONLY: i32 = 0o0000; -const O_WRONLY: i32 = 0o0001; -const O_RDWR: i32 = 0o0002; -const O_CREAT: i32 = 0o0100; -const O_EXCL: i32 = 0o0200; -const O_TRUNC: i32 = 0o1000; -const O_APPEND: i32 = 0o2000; -const O_DIRECT: i32 = 0o40000; - -#[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 } - } -} - -/// 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); +pub(crate) static FD_COUNTER: AtomicI32 = AtomicI32::new(3); + +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 = "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), - ); +bitflags! { + #[derive(Debug, Copy, Clone)] + pub struct AccessPermission: u32 { + const S_IFMT = 0o170000; + const S_IFSOCK = 0o140000; + 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; + 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; } } -/// 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); +impl Default for AccessPermission { + fn default() -> Self { + AccessPermission::from_bits(0o666).unwrap() } - perms -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub enum DirectoryEntry { - Invalid(i32), - 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, _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 /// 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 - 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) -> 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, _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) -> i32 { - -EINVAL + fn bind(&self, _name: IpListenEndpoint) -> 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 #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn setsockopt( - &self, - _level: i32, - _optname: i32, - _optval: *const c_void, - _optlen: socklen_t, - ) -> i32 { - -EINVAL + 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, - ) -> i32 { - -EINVAL + 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) -> i32 { - -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) -> i32 { - -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, - _len: usize, - _address: *mut sockaddr, - _address_len: *mut socklen_t, - ) -> isize { - (-ENOSYS).try_into().unwrap() + fn recvfrom(&self, _buffer: &mut [u8]) -> Result<(usize, IpEndpoint), IoError> { + Err(IoError::ENOSYS) } /// send a message from a socket @@ -327,107 +219,66 @@ pub 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: *const u8, - _len: usize, - _addr: *const sockaddr, - _addr_len: socklen_t, - ) -> isize { - (-ENOSYS).try_into().unwrap() + fn sendto(&self, _buffer: &[u8], _endpoint: IpEndpoint) -> 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: IoCtl, _value: bool) -> 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); - if sysopen.ret > 0 { - let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); - let file = UhyveFile::new(sysopen.ret); + // close a file descriptor + fn close(&self) {} +} - if OBJECT_MAP.write().try_insert(fd, Arc::new(file)).is_err() { - Err(-EINVAL) - } else { - Ok(fd as FileDescriptor) - } +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); + + let fs = fs::FILESYSTEM.get().unwrap(); + 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) } else { - Err(sysopen.ret) + 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 ) - - 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) - } - } + Err(IoError::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 close(fd: FileDescriptor) { + let _ = remove_object(fd).map(|v| v.close()); } -pub(crate) fn get_object(fd: FileDescriptor) -> Result, i32> { - Ok((*(OBJECT_MAP.read().get(&fd).ok_or(-EINVAL)?)).clone()) +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, @@ -439,9 +290,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 +305,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..7a41790273 100644 --- a/src/fd/socket/mod.rs +++ b/src/fd/socket/mod.rs @@ -1,212 +1,4 @@ -use alloc::sync::Arc; -use core::ffi::c_void; -use core::ops::DerefMut; -use core::sync::atomic::Ordering; - -use crate::errno::*; -use crate::executor::network::{NetworkState, NIC}; -use crate::fd::{get_object, insert_object, 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(); - 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; - } - } 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; - } - } - } - - #[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; - } - } 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; - } - } - } - - -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| e, - |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 - } - }, - ) -} - -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)) -} - -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)) -} - -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)) -} - -pub(crate) extern "C" fn __sys_getsockname( - fd: i32, - name: *mut sockaddr, - namelen: *mut socklen_t, -) -> i32 { - let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).getsockname(name, namelen)) -} - -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 - ); - - let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).setsockopt(level, optname, optval, optlen)) -} - -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 - ); - - let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).getsockopt(level, optname, optval, optlen)) -} - -pub(crate) extern "C" fn __sys_getpeername( - fd: i32, - name: *mut sockaddr, - namelen: *mut socklen_t, -) -> i32 { - let obj = get_object(fd); - obj.map_or_else(|e| e, |v| (*v).getpeername(name, namelen)) -} - -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| e, |v| (*v).shutdown(how)) -} - -pub extern "C" fn __sys_recv(fd: i32, buf: *mut u8, len: usize) -> isize { - let obj = get_object(fd); - obj.map_or_else(|e| e as isize, |v| (*v).read(buf, len)) -} - -pub extern "C" fn __sys_sendto( - fd: i32, - buf: *const u8, - len: usize, - _flags: i32, - addr: *const sockaddr, - addr_len: socklen_t, -) -> isize { - let obj = get_object(fd); - obj.map_or_else(|e| e as isize, |v| (*v).sendto(buf, len, addr, addr_len)) -} - -pub extern "C" fn __sys_recvfrom( - fd: i32, - buf: *mut u8, - len: usize, - _flags: i32, - addr: *mut sockaddr, - addr_len: *mut socklen_t, -) -> isize { - let obj = get_object(fd); - obj.map_or_else(|e| e as isize, |v| (*v).recvfrom(buf, len, addr, addr_len)) -} +pub(crate) mod udp; diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index 8d464f1dd7..896f968ce2 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -1,7 +1,4 @@ -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,11 +6,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::errno::*; use crate::executor::network::{block_on, now, poll_on, Handle, NetworkState, NIC}; -use crate::fd::ObjectInterface; +use crate::fd::{IoCtl, IoError, ObjectInterface, SocketOption}; use crate::syscalls::net::*; use crate::DEFAULT_KEEP_ALIVE_INTERVAL; @@ -30,20 +26,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, } } @@ -69,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 => { @@ -78,7 +72,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( @@ -86,9 +80,9 @@ 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(|_| -crate::errno::EIO), + .map_err(|_| IoError::EIO), ) } else { socket.register_recv_waker(cx.waker()); @@ -100,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() { @@ -113,13 +107,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 @@ -142,40 +134,35 @@ impl Socket { pos += n; } - Ok(pos.try_into().unwrap()) + Ok(pos) } - 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 - })?; + 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| { 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 } - _ => Poll::Ready(Ok(0)), + _ => Poll::Ready(Ok(())), }) }) .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()); @@ -205,11 +192,7 @@ impl Socket { .await } - async fn async_accept( - &self, - _addr: *mut sockaddr, - _addrlen: *mut socklen_t, - ) -> Result<(), i32> { + async fn async_accept(&self) -> Result { future::poll_fn(|cx| { self.with(|socket| match socket.state() { tcp::State::Closed => { @@ -234,7 +217,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,149 +229,135 @@ 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(()) + Ok(socket.remote_endpoint().unwrap()) } +} - 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) +impl ObjectInterface for Socket { + fn bind(&self, endpoint: IpListenEndpoint) -> Result<(), IoError> { + self.port.store(endpoint.port, Ordering::Release); + Ok(()) } - fn read(&self, buf: *mut u8, len: usize) -> isize { - if len == 0 { - return 0; - } - - let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; - + fn connect(&self, endpoint: IpEndpoint) -> Result<(), IoError> { 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() + block_on(self.async_connect(endpoint), 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()) + 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 { + if buf.is_empty() { + return Ok(0); + } + + if self.nonblocking.load(Ordering::Acquire) { + 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 { + 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.is_empty() { + 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) } }) } - fn setsockopt( - &self, - level: i32, - optname: i32, - optval: *const c_void, - optlen: socklen_t, - ) -> i32 { - 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))); } }); - 0 - } else if level == SOL_SOCKET && optname == SO_REUSEADDR { - // smoltcp is always able to reuse the addr - 0 + Ok(()) } else { - -EINVAL + Err(IoError::EINVAL) } } - fn getsockopt( - &self, - level: i32, - optname: i32, - optval: *mut c_void, - optlen: *mut socklen_t, - ) -> i32 { - 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(); - - 0 - } else { - -EINVAL - } + fn getsockopt(&self, opt: SocketOption) -> Result { + if opt == SocketOption::TcpNoDelay { + self.with(|socket| Ok(socket.nagle_enabled())) } 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 { - 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 { @@ -396,14 +365,14 @@ impl Socket { self.nonblocking.store(false, Ordering::Release); } - 0 + Ok(()) } else { - -EINVAL + Err(IoError::EINVAL) } } } -impl Clone for Socket { +impl Clone for Socket { fn clone(&self) -> Self { let mut guard = NIC.lock(); @@ -417,307 +386,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) -> i32 { - 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 - } else { - -EINVAL - } - } - - fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { - 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)).unwrap_or_else( - |x| { - if x == -ETIME { - -EAGAIN - } else { - x - } - }, - ) - } else { - block_on(self.async_connect(address, port), None).unwrap_or_else(|x| x) - } - } else { - -EINVAL - } - } - - fn getpeername(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> i32 { - if namelen.is_null() { - return -ENOBUFS; - } - - let namelen = unsafe { &mut *namelen }; - if *namelen >= size_of::().try_into().unwrap() { - let mut ret: i32 = 0; - 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 = -crate::errno::ENOTCONN; - } - }); - - *namelen = size_of::().try_into().unwrap(); - - ret - } else { - -EINVAL - } - } - - fn getsockname(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> i32 { - if namelen.is_null() { - return -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(); - - 0 - } else { - -EINVAL - } - } - - fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> i32 { - self.accept(addr, addrlen) - } - - fn read(&self, buf: *mut u8, len: usize) -> isize { - self.read(buf, len) - } - - fn write(&self, buf: *const u8, len: usize) -> isize { - self.write(buf, len) - } - - fn listen(&self, backlog: i32) -> i32 { - self.listen(backlog) - } - - fn setsockopt( - &self, - level: i32, - optname: i32, - optval: *const c_void, - optlen: socklen_t, - ) -> i32 { - self.setsockopt(level, optname, optval, optlen) - } - - fn getsockopt( - &self, - level: i32, - optname: i32, - optval: *mut c_void, - optlen: *mut socklen_t, - ) -> i32 { - self.getsockopt(level, optname, optval, optlen) - } - - fn shutdown(&self, how: i32) -> i32 { - self.shutdown(how) - } - - fn ioctl(&self, cmd: i32, argp: *mut c_void) -> i32 { - self.ioctl(cmd, argp) - } -} - -impl ObjectInterface for Socket { - fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { - if namelen == size_of::().try_into().unwrap() { - let addr = unsafe { *(name as *const sockaddr_in6) }; - self.port.store(addr.sin6_port, Ordering::Release); - 0 - } else { - -EINVAL - } - } - - fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { - 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)).unwrap_or_else( - |x| { - if x == -ETIME { - -EAGAIN - } else { - x - } - }, - ) - } else { - block_on(self.async_connect(address, port), None).unwrap_or_else(|x| x) - } - } else { - -EINVAL - } - } - - fn getpeername(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> i32 { - if namelen.is_null() { - return -ENOBUFS; - } - - let namelen = unsafe { &mut *namelen }; - if *namelen >= size_of::().try_into().unwrap() { - let mut ret: i32 = 0; - 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 = -crate::errno::ENOTCONN; - } - }); - - *namelen = size_of::().try_into().unwrap(); - - ret - } else { - -EINVAL - } - } - - fn getsockname(&self, name: *mut sockaddr, namelen: *mut socklen_t) -> i32 { - if namelen.is_null() { - return -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(); - - 0 - } else { - -EINVAL - } - } - - fn accept(&self, addr: *mut sockaddr, addrlen: *mut socklen_t) -> i32 { - self.accept(addr, addrlen) - } - - fn read(&self, buf: *mut u8, len: usize) -> isize { - self.read(buf, len) - } - - fn write(&self, buf: *const u8, len: usize) -> isize { - self.write(buf, len) - } - - fn listen(&self, backlog: i32) -> i32 { - self.listen(backlog) - } - - fn setsockopt( - &self, - level: i32, - optname: i32, - optval: *const c_void, - optlen: socklen_t, - ) -> i32 { - self.setsockopt(level, optname, optval, optlen) - } - - fn getsockopt( - &self, - level: i32, - optname: i32, - optval: *mut c_void, - optlen: *mut socklen_t, - ) -> i32 { - self.getsockopt(level, optname, optval, optlen) - } - - fn shutdown(&self, how: i32) -> i32 { - self.shutdown(how) - } - - fn ioctl(&self, cmd: i32, argp: *mut c_void) -> i32 { - self.ioctl(cmd, argp) - } -} diff --git a/src/fd/socket/udp.rs b/src/fd/socket/udp.rs index e9748d80fa..a3002855e6 100644 --- a/src/fd/socket/udp.rs +++ b/src/fd/socket/udp.rs @@ -1,7 +1,4 @@ -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,12 +7,10 @@ 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::errno::*; use crate::executor::network::{block_on, now, poll_on, Handle, NetworkState, NIC}; -use crate::fd::ObjectInterface; -use crate::syscalls::net::*; +use crate::fd::{IoCtl, IoError, ObjectInterface}; #[derive(Debug)] pub struct IPv4; @@ -24,20 +19,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, } } @@ -50,7 +43,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 +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() { @@ -69,30 +62,30 @@ 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(-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<(usize, IpEndpoint), IoError> { future::poll_fn(|cx| { self.with(|socket| { if socket.is_open() { @@ -101,30 +94,30 @@ 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, 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, meta.endpoint))), }, - _ => 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() { @@ -132,77 +125,104 @@ impl Socket { Poll::Ready( socket .send_slice(buffer, *meta) - .map(|_| buffer.len() as isize) - .map_err(|_| -crate::errno::EIO), + .map(|_| buffer.len()) + .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; - } +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(()) + } - let slice = unsafe { core::slice::from_raw_parts_mut(buf, len) }; + fn sendto(&self, buf: &[u8], endpoint: IpEndpoint) -> Result { + let meta = UdpMetadata::from(endpoint); 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_write(buf, &meta), Some(Duration::ZERO)) + } else { + poll_on(self.async_write(buf, &meta), None) + } + } + + 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 { + 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()) + 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 { + return Ok(0); + } + + if self.nonblocking.load(Ordering::Acquire) { + 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 { + 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 { - 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 { @@ -210,14 +230,14 @@ impl Socket { self.nonblocking.store(false, Ordering::Release); } - 0 + Ok(()) } else { - -EINVAL + Err(IoError::EINVAL) } } } -impl Clone for Socket { +impl Clone for Socket { fn clone(&self) -> Self { let mut guard = NIC.lock(); @@ -231,392 +251,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) -> i32 { - 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); - } - }); - - 0 - } else { - -EINVAL - } - } - - fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { - 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))); - - 0 - } else { - -EINVAL - } - } - - fn read(&self, buf: *mut u8, len: usize) -> isize { - self.read(buf, len) - } - - fn write(&self, buf: *const u8, len: usize) -> isize { - self.write(buf, len) - } - - fn sendto( - &self, - buf: *const u8, - len: usize, - addr: *const sockaddr, - addr_len: socklen_t, - ) -> isize { - if addr.is_null() || addr_len == 0 { - self.write(buf, len) - } 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); - 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() - } - }, - ) - } else { - poll_on(self.async_write(slice, &meta), None) - .unwrap_or_else(|x| x.try_into().unwrap()) - } - } else { - (-EINVAL).try_into().unwrap() - } - } - } - - fn recvfrom( - &self, - buf: *mut u8, - len: usize, - address: *mut sockaddr, - address_len: *mut socklen_t, - ) -> isize { - if !address_len.is_null() { - let len = unsafe { &mut *address_len }; - if *len < size_of::().try_into().unwrap() { - return (-EINVAL).try_into().unwrap(); - } - } - - if len == 0 { - return (-EINVAL).try_into().unwrap(); - } - - 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)| { - 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() - }, - ) - } 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() - }, - ) - } else { - 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() - }, - ) - } - } -} - -impl ObjectInterface for Socket { - fn bind(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { - 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(); - } - }); - - 0 - } else { - -EINVAL - } - } - - fn connect(&self, name: *const sockaddr, namelen: socklen_t) -> i32 { - 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))); - - 0 - } else { - -EINVAL - } - } - - fn read(&self, buf: *mut u8, len: usize) -> isize { - self.read(buf, len) - } - - fn sendto( - &self, - buf: *const u8, - len: usize, - addr: *const sockaddr, - addr_len: socklen_t, - ) -> isize { - if addr.is_null() || addr_len == 0 { - self.write(buf, len) - } 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); - 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() - } - }, - ) - } else { - poll_on(self.async_write(slice, &meta), None) - .unwrap_or_else(|x| x.try_into().unwrap()) - } - } else { - (-EINVAL).try_into().unwrap() - } - } - } - - fn recvfrom( - &self, - buf: *mut u8, - len: usize, - address: *mut sockaddr, - address_len: *mut socklen_t, - ) -> isize { - if !address_len.is_null() { - let len = unsafe { &mut *address_len }; - if *len < size_of::().try_into().unwrap() { - return (-EINVAL).try_into().unwrap(); - } - } - - if len == 0 { - return (-EINVAL).try_into().unwrap(); - } - - 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)| { - 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() - }, - ) - } 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() - }, - ) - } else { - 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() - }, - ) - } - } - - fn write(&self, buf: *const u8, len: usize) -> isize { - self.write(buf, len) - } - - fn ioctl(&self, cmd: i32, argp: *mut c_void) -> i32 { - self.ioctl(cmd, argp) - } -} diff --git a/src/fd/stdio.rs b/src/fd/stdio.rs index 4c550df882..79fec62f77 100644 --- a/src/fd/stdio.rs +++ b/src/fd/stdio.rs @@ -1,9 +1,66 @@ -use core::{isize, slice}; +#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] +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::{ - 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()) } } @@ -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()) } } @@ -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) } } @@ -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) } } diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index c9789e3559..802ffb2e6e 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -1,17 +1,24 @@ 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::syscalls::fs::{ - self, Dirent, FileAttr, FileError, FilePerms, PosixFile, PosixFileSystem, SeekWhence, +use crate::fd::IoError; +use crate::fs::{ + 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 @@ -22,16 +29,17 @@ 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; +#[allow(dead_code)] 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 +47,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 +56,294 @@ 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 { + /// 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, +} - 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 From for FileAttr { + fn from(attr: fuse_attr) -> FileAttr { + FileAttr { + st_ino: attr.ino, + st_nlink: attr.nlink as u64, + st_mode: AccessPermission::from_bits(attr.mode).unwrap(), + st_uid: attr.uid, + st_gid: attr.gid, + st_rdev: attr.rdev as u64, + st_size: attr.size, + st_blksize: attr.blksize as i64, + st_blocks: attr.blocks.try_into().unwrap(), + st_atime: attr.atime, + st_atime_nsec: attr.atimensec as u64, + st_mtime: attr.mtime, + st_mtime_nsec: attr.atimensec as u64, + st_ctime: attr.ctime, + st_ctime_nsec: attr.ctimensec as u64, + ..Default::default() + } } +} - 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 - } - } +#[repr(C)] +#[derive(Default, Debug)] +struct fuse_rmdir_in {} +unsafe impl FuseIn for fuse_rmdir_in {} - 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() - } -} - -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 +398,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 +419,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 +496,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 +558,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 +607,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 +672,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 +731,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 +793,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 +845,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 +869,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 +894,51 @@ fn create_open(nid: u64, flags: u32) -> (Box>, Box (Box>, Box>) { - let len = core::mem::size_of::() + core::mem::size_of::(); +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,138 +951,33 @@ fn create_release(nid: u64, fh: u64) -> (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_UNLINK); + (*raw).header.len = len.try_into().unwrap(); + (*raw).extra_buffer[..slice.len()].copy_from_slice(slice); + (*raw).extra_buffer[slice.len()] = 0; -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_readlink_in {} - -unsafe impl FuseIn for fuse_readlink_in {} - -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_readlink_out {} -unsafe impl FuseOut for fuse_readlink_out {} - -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_getattr_in { - pub getattr_flags: u32, - pub dummy: u32, - pub fh: u64, -} - -unsafe impl FuseIn for fuse_getattr_in {} - -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_attr_out { - pub attr_valid: u64, - pub attr_valid_nsec: u32, - pub dummy: u32, - pub attr: fuse_attr, -} - -unsafe impl FuseOut for fuse_attr_out {} - -#[repr(C)] -#[derive(Default, Debug)] -pub 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, -} - -unsafe impl FuseOut for fuse_entry_out {} - -#[repr(C)] -#[derive(Default, Debug)] -pub 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, -} - -impl fuse_attr { - unsafe fn fill_stat(self, stat: *mut FileAttr) { - unsafe { - (*stat).st_dev = 0; - (*stat).st_ino = self.ino; - (*stat).st_nlink = self.nlink as u64; - (*stat).st_mode = self.mode; - (*stat).st_uid = self.uid; - (*stat).st_gid = self.gid; - (*stat).st_rdev = self.rdev as u64; - (*stat).st_size = self.size.try_into().unwrap(); - (*stat).st_blksize = self.blksize as i64; - (*stat).st_blocks = self.blocks.try_into().unwrap(); - (*stat).st_atime = self.atime.try_into().unwrap(); - (*stat).st_atime_nsec = self.atimensec as i64; - (*stat).st_mtime = self.mtime.try_into().unwrap(); - (*stat).st_mtime_nsec = self.atimensec as i64; - (*stat).st_ctime = self.ctime.try_into().unwrap(); - (*stat).st_ctime_nsec = self.ctimensec as i64; - } - } -} - -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_unlink_in {} -unsafe impl FuseIn for fuse_unlink_in {} - -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_unlink_out {} -unsafe impl FuseOut for fuse_unlink_out {} - -fn create_unlink(name: &str) -> (Box>, Box>) { - let slice = name.as_bytes(); - let len = core::mem::size_of::() - + core::mem::size_of::() - + slice.len() - + 1; - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = - core::ptr::slice_from_raw_parts_mut(data, slice.len() + 1) as *mut Cmd; - (*raw).header = create_in_header::(FUSE_ROOT_ID, Opcode::FUSE_UNLINK); - (*raw).header.len = len.try_into().unwrap(); - (*raw).extra_buffer[..slice.len()].copy_from_slice(slice); - (*raw).extra_buffer[slice.len()] = 0; - - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); + Box::from_raw(raw) + }; + assert_eq!(layout, Layout::for_value(&*cmd)); let len = core::mem::size_of::() + core::mem::size_of::(); let layout = Layout::from_size_align( @@ -1400,48 +1004,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 +1022,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 +1032,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 +1057,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 +1075,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 +1110,501 @@ fn create_mkdir(path: &str, mode: u32) -> (Box>, Box 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 + } +} -#[repr(C)] -#[derive(Default, Debug)] -pub struct fuse_rmdir_out {} -unsafe impl FuseOut for fuse_rmdir_out {} +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::() + }; -fn create_rmdir(name: &str) -> (Box>, Box>) { - let slice = name.as_bytes(); - let len = core::mem::size_of::() - + core::mem::size_of::() - + slice.len() - + 1; - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let cmd = unsafe { - let data = alloc(layout); - let raw = - core::ptr::slice_from_raw_parts_mut(data, slice.len() + 1) as *mut Cmd; - (*raw).header = create_in_header::(FUSE_ROOT_ID, Opcode::FUSE_RMDIR); - (*raw).header.len = len.try_into().unwrap(); - (*raw).extra_buffer[..slice.len()].copy_from_slice(slice); - (*raw).extra_buffer[slice.len()] = 0; + Ok(String::from_utf8(unsafe { + MaybeUninit::slice_assume_init_ref(&rsp.extra_buffer[..len]).to_vec() + }) + .unwrap()) +} - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*cmd)); +#[derive(Debug)] +struct FuseFileHandleInner { + fuse_nid: Option, + fuse_fh: Option, + offset: usize, +} - let len = core::mem::size_of::() + core::mem::size_of::(); - let layout = Layout::from_size_align( - len, - core::cmp::max( - core::mem::align_of::(), - core::mem::align_of::(), - ), - ) - .unwrap() - .pad_to_align(); - let rsp = unsafe { - let data = alloc(layout); - let raw = core::ptr::slice_from_raw_parts_mut(data, 0) as *mut Rsp; - (*raw).header = fuse_out_header { - len: len.try_into().unwrap(), - ..Default::default() +impl FuseFileHandleInner { + pub fn new() -> Self { + Self { + fuse_nid: None, + fuse_fh: None, + offset: 0, + } + } + + fn read(&mut self, buf: &mut [u8]) -> Result { + let mut len = buf.len(); + if len > 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 + { + len + } 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) + } 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) + } 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)] +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_readdir(&self, components: &mut Vec<&str>) -> Result, IoError> { + let path: String = if components.is_empty() { + "/".to_string() + } else { + components + .iter() + .rev() + .map(|v| "/".to_owned() + v) + .collect() }; - Box::from_raw(raw) - }; - assert_eq!(layout, Layout::for_value(&*rsp)); + debug!("FUSE opendir: {}", path); - (cmd, rsp) + 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(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()); + 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) }; + + 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(unsafe { + core::str::from_utf8_unchecked(name).to_string() + })); + } + + let (cmd, mut rsp) = create_release(fuse_nid, fuse_fh); + get_filesystem_driver() + .unwrap() + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + + Ok(entries) + } + + fn traverse_stat(&self, components: &mut Vec<&str>) -> Result { + let path: String = if components.is_empty() { + "/".to_string() + } else { + components + .iter() + .rev() + .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(FileAttr::from(attr)) + } 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() + .rev() + .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(FileAttr::from(attr)) + } + + fn traverse_open( + &self, + components: &mut Vec<&str>, + opt: OpenOption, + mode: AccessPermission, + ) -> Result, IoError> { + let path: String = if components.is_empty() { + "/".to_string() + } else { + components + .iter() + .rev() + .map(|v| "/".to_owned() + v) + .collect() + }; + + debug!("FUSE open: {}, {:?} {:?}", path, opt, mode); + + 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(), mode.bits()); + get_filesystem_driver() + .ok_or(IoError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + + let inner = unsafe { rsp.rsp.assume_init() }; + 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() + .rev() + .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() + .rev() + .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: AccessPermission, + ) -> Result<(), IoError> { + let path: String = if components.is_empty() { + "/".to_string() + } else { + components + .iter() + .rev() + .map(|v| "/".to_owned() + v) + .collect() + }; + let (cmd, mut rsp) = create_mkdir(&path, mode.bits()); + + 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 fn init() { +pub(crate) fn init() { + debug!("Try to initialize fuse filesystem"); + 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) + 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..8c15ee5a3c --- /dev/null +++ b/src/fs/mem.rs @@ -0,0 +1,540 @@ +// 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::boxed::Box; +use alloc::collections::BTreeMap; +use alloc::string::{String, ToString}; +use alloc::sync::Arc; +use alloc::vec::Vec; +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)] +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, Clone)] +struct RomFileInterface { + /// Position within the file + pos: Arc>, + /// File content + inner: Arc>, +} + +impl ObjectInterface for RomFileInterface { + fn read(&self, buf: &mut [u8]) -> Result { + { + 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; + + 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) + } +} + +impl RomFileInterface { + pub fn new(inner: Arc>) -> Self { + Self { + pos: Arc::new(SpinMutex::new(0)), + inner, + } + } + + pub fn len(&self) -> usize { + self.inner.read().data.len() + } +} + +#[derive(Debug)] +pub(crate) struct RamFileInner { + pub data: Vec, + pub attr: FileAttr, +} + +impl RamFileInner { + pub fn new(attr: FileAttr) -> Self { + Self { + data: Vec::new(), + attr, + } + } +} + +#[derive(Debug, Clone)] +pub struct RamFileInterface { + /// Position within the file + pos: Arc>, + /// File content + inner: Arc>, +} + +impl ObjectInterface for RamFileInterface { + fn read(&self, buf: &mut [u8]) -> Result { + { + 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 >= guard.data.len() { + return Ok(0); + } + + let len = if guard.data.len() - pos < buf.len() { + guard.data.len() - pos + } else { + buf.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 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() > guard.data.len() { + guard.data.resize(pos + buf.len(), 0); + guard.attr.st_size = guard.data.len().try_into().unwrap(); + } + 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()) + } +} + +impl RamFileInterface { + pub fn new(inner: Arc>) -> Self { + Self { + pos: Arc::new(SpinMutex::new(0)), + inner, + } + } + + pub fn len(&self) -> usize { + self.inner.read().data.len() + } +} + +#[derive(Debug)] +pub(crate) struct RomFile { + data: Arc>, +} + +impl VfsNode for RomFile { + fn get_kind(&self) -> NodeKind { + NodeKind::File + } + + fn get_object(&self) -> Result, IoError> { + Ok(Arc::new(RomFileInterface::new(self.data.clone()))) + } + + fn get_file_attributes(&self) -> Result { + Ok(self.data.read().attr) + } + + fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { + if components.is_empty() { + self.get_file_attributes() + } else { + Err(IoError::EBADF) + } + } + + fn traverse_stat(&self, components: &mut Vec<&str>) -> Result { + if components.is_empty() { + self.get_file_attributes() + } else { + Err(IoError::EBADF) + } + } +} + +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: unsafe { Arc::new(RwSpinLock::new(RomFileInner::new(ptr, length, attr))) }, + } + } +} + +#[derive(Debug, Clone)] +pub(crate) struct RamFile { + data: Arc>, +} + +impl VfsNode for RamFile { + fn get_kind(&self) -> NodeKind { + NodeKind::File + } + + fn get_object(&self) -> Result, IoError> { + Ok(Arc::new(RamFileInterface::new(self.data.clone()))) + } + + fn get_file_attributes(&self) -> Result { + Ok(self.data.read().attr) + } + + fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { + if components.is_empty() { + self.get_file_attributes() + } else { + Err(IoError::EBADF) + } + } + + fn traverse_stat(&self, components: &mut Vec<&str>) -> Result { + if components.is_empty() { + self.get_file_attributes() + } else { + Err(IoError::EBADF) + } + } +} + +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(RamFileInner::new(attr))), + } + } +} + +#[derive(Debug)] +pub(crate) struct MemDirectory { + inner: Arc< + RwSpinLock>>, + >, + attr: FileAttr, +} + +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() + }, + } + } +} + +impl VfsNode for MemDirectory { + fn get_kind(&self) -> NodeKind { + NodeKind::Directory + } + + fn get_file_attributes(&self) -> Result { + Ok(self.attr) + } + + fn traverse_mkdir( + &self, + components: &mut Vec<&str>, + mode: AccessPermission, + ) -> Result<(), IoError> { + if let Some(component) = components.pop() { + let node_name = String::from(component); + + if let Some(directory) = self.inner.read().get(&node_name) { + return directory.traverse_mkdir(components, mode); + } + + if components.is_empty() { + self.inner + .write() + .insert(node_name, Box::new(MemDirectory::new(mode))); + 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.read().get(&node_name) { + return directory.traverse_rmdir(components); + } + + if components.is_empty() { + let mut guard = self.inner.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.read().get(&node_name) { + return directory.traverse_unlink(components); + } + + if components.is_empty() { + let mut guard = self.inner.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_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_readdir(components) + } else { + Err(IoError::EBADF) + } + } else { + let mut entries: Vec = Vec::new(); + for name in self.inner.read().keys() { + entries.push(DirectoryEntry::new(name.to_string())); + } + + Ok(entries) + } + } + + fn traverse_lstat(&self, components: &mut Vec<&str>) -> Result { + 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) { + return node.get_file_attributes(); + } + } + + if let Some(directory) = self.inner.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 components.is_empty() { + if let Some(node) = self.inner.read().get(&node_name) { + return node.get_file_attributes(); + } + } + + if let Some(directory) = self.inner.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.read().get(&node_name) { + return directory.traverse_mount(components, obj); + } + + if components.is_empty() { + self.inner.write().insert(node_name, obj); + return Ok(()); + } + } + + Err(IoError::EBADF) + } + + fn traverse_open( + &self, + components: &mut Vec<&str>, + opt: OpenOption, + mode: AccessPermission, + ) -> Result, IoError> { + if let Some(component) = components.pop() { + let node_name = String::from(component); + + if components.is_empty() { + 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(mode)); + guard.insert(node_name, file.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 { + return file.get_object(); + } else { + return Err(IoError::ENOENT); + } + } else { + return Err(IoError::ENOENT); + } + } + + if let Some(directory) = self.inner.read().get(&node_name) { + return directory.traverse_open(components, opt, mode); + } + } + + 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 0069cb3601..0f0a77c653 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -1,7 +1,499 @@ -#[cfg(feature = "pci")] -pub mod fuse; +#[cfg(all(feature = "fuse", feature = "pci"))] +pub(crate) mod fuse; +mod mem; +mod uhyve; -pub fn init() { - #[cfg(feature = "pci")] +use alloc::boxed::Box; +use alloc::string::{String, ToString}; +use alloc::sync::Arc; +use alloc::vec::Vec; +use core::sync::atomic::Ordering; + +use hermit_sync::OnceCell; +use mem::MemDirectory; + +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(); + +#[derive(Debug, Clone)] +pub struct DirectoryEntry { + pub name: String, +} + +impl DirectoryEntry { + pub fn new(name: String) -> Self { + Self { name } + } +} + +/// Type of the VNode +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum NodeKind { + /// Node represent a file + File, + /// Node represent a directory + Directory, +} + +/// 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; + + /// 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) + } + + /// Helper function to create a new dirctory node + fn traverse_mkdir( + &self, + _components: &mut Vec<&str>, + _mode: AccessPermission, + ) -> Result<(), IoError> { + Err(IoError::ENOSYS) + } + + /// Helper function to delete a dirctory node + fn traverse_rmdir(&self, _components: &mut Vec<&str>) -> core::result::Result<(), IoError> { + Err(IoError::ENOSYS) + } + + /// Helper function to remove the specified file + fn traverse_unlink(&self, _components: &mut Vec<&str>) -> Result<(), IoError> { + Err(IoError::ENOSYS) + } + + /// Helper function to open a directory + fn traverse_readdir( + &self, + _components: &mut Vec<&str>, + ) -> Result, IoError> { + Err(IoError::ENOSYS) + } + + /// Helper function to get file status + fn traverse_lstat(&self, _components: &mut Vec<&str>) -> Result { + Err(IoError::ENOSYS) + } + + /// Helper function to get file status + fn traverse_stat(&self, _components: &mut Vec<&str>) -> Result { + Err(IoError::ENOSYS) + } + + /// Helper function to mount a file system + fn traverse_mount( + &self, + _components: &mut Vec<&str>, + _obj: Box, + ) -> Result<(), IoError> { + Err(IoError::ENOSYS) + } + + /// Helper function to open a file + fn traverse_open( + &self, + _components: &mut Vec<&str>, + _option: OpenOption, + _mode: AccessPermission, + ) -> 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, Clone)] +struct DirectoryReader(Vec); + +impl DirectoryReader { + pub fn new(data: Vec) -> Self { + Self(data) + } +} + +impl ObjectInterface for DirectoryReader { + fn readdir(&self) -> Result, IoError> { + Ok(self.0.clone()) + } +} + +#[derive(Debug)] +pub(crate) struct Filesystem { + root: MemDirectory, +} + +impl Filesystem { + pub fn new() -> Self { + Self { + root: MemDirectory::new(AccessPermission::from_bits(0o777).unwrap()), + } + } + + /// Tries to open file at given path. + pub fn open( + &self, + path: &str, + opt: OpenOption, + mode: AccessPermission, + ) -> 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, mode) + } + + /// 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(); + + components.reverse(); + components.pop(); + + self.root.traverse_unlink(&mut components) + } + + /// 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(); + + components.reverse(); + components.pop(); + + self.root.traverse_rmdir(&mut components) + } + + /// Create directory given by path + pub fn mkdir(&self, path: &str, mode: AccessPermission) -> 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) + } + + 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_readdir(&mut components) + } else { + let mut components: Vec<&str> = path.split('/').collect(); + + components.reverse(); + components.pop(); + + self.root.traverse_readdir(&mut components) + } + } + + /// stat + pub fn stat(&self, path: &str) -> Result { + debug!("Getting stats {}", path); + + let mut components: Vec<&str> = path.split('/').collect(); + components.reverse(); + components.pop(); + + self.root.traverse_stat(&mut components) + } + + /// lstat + pub fn lstat(&self, path: &str) -> Result { + debug!("Getting lstats {}", path); + + 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( + &self, + path: &str, + obj: Box, + ) -> Result<(), IoError> { + debug!("Mounting {}", path); + + let mut components: Vec<&str> = path.split('/').collect(); + + components.reverse(); + components.pop(); + + self.root.traverse_mount(&mut components, obj) + } + + /// Create read-only file + pub unsafe fn create_file( + &self, + path: &str, + ptr: *const u8, + length: usize, + mode: AccessPermission, + ) -> Result<(), IoError> { + 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) + } +} + +#[repr(C)] +#[derive(Debug, Default, Copy, Clone)] +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: u64, + /// block size + pub st_blksize: i64, + /// size in blocks + pub st_blocks: i64, + /// time of last access + pub st_atime: u64, + pub st_atime_nsec: u64, + /// time of last modification + pub st_mtime: u64, + pub st_mtime_nsec: u64, + /// time of last status change + pub st_ctime: u64, + pub st_ctime_nsec: u64, +} + +#[derive(Debug, FromPrimitive, ToPrimitive)] +pub enum FileType { + 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 +} + +#[derive(Debug, FromPrimitive, ToPrimitive)] +pub enum SeekWhence { + Set = 0, + Cur = 1, + End = 2, + Data = 3, + Hole = 4, +} + +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 + .get() + .unwrap() + .mkdir("/tmp", AccessPermission::from_bits(0o777).unwrap()) + .expect("Unable to create /tmp"); + FILESYSTEM + .get() + .unwrap() + .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"))] fuse::init(); + uhyve::init(); +} + +pub unsafe fn create_file( + name: &str, + ptr: *const u8, + length: usize, + mode: AccessPermission, +) -> Result<(), IoError> { + unsafe { + FILESYSTEM + .get() + .ok_or(IoError::EINVAL)? + .create_file(name, ptr, length, mode) + } +} + +/// 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) +} + +/// 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); + + let _ = insert_object(fd, obj); + + Ok(fd) +} + +use crate::fd::{self, FileDescriptor}; + +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 { + /// 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) + } + + /// 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. +pub fn metadata(path: &str) -> Result { + Ok(Metadata(file_attributes(path)?)) +} + +#[derive(Debug)] +pub struct File { + fd: FileDescriptor, + path: String, +} + +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, + path: path.to_string(), + }) + } + + /// 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, + path: path.to_string(), + }) + } + + pub fn metadata(&self) -> Result { + metadata(&self.path) + } +} + +impl crate::io::Read for File { + fn read(&mut self, buf: &mut [u8]) -> Result { + fd::read(self.fd, buf) + } +} + +impl crate::io::Write for File { + fn write(&mut self, buf: &[u8]) -> Result { + fd::write(self.fd, buf) + } +} + +impl Drop for File { + fn drop(&mut self) { + fd::close(self.fd); + } } diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs new file mode 100644 index 0000000000..12b624b097 --- /dev/null +++ b/src/fs/uhyve.rs @@ -0,0 +1,329 @@ +use alloc::borrow::ToOwned; +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; +#[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, AccessPermission, 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: u32, + ret: i32, +} + +impl SysOpen { + fn new(name: VirtAddr, flags: i32, mode: u32) -> 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.try_into().unwrap()) + } else { + Err(IoError::EIO) + } + } + + 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) + } + + 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_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, + mode: AccessPermission, + ) -> Result, IoError> { + let path: String = if components.is_empty() { + "/\0".to_string() + } else { + let mut path: String = components + .iter() + .rev() + .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(), mode.bits()); + uhyve_send(UHYVE_PORT_OPEN, &mut sysopen); + + if sysopen.ret > 0 { + Ok(Arc::new(UhyveFileHandle::new(sysopen.ret))) + } else { + Err(IoError::EIO) + } + } + + fn traverse_unlink(&self, components: &mut Vec<&str>) -> core::result::Result<(), IoError> { + let path: String = if components.is_empty() { + "/".to_string() + } else { + components + .iter() + .rev() + .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(IoError::EIO) + } + } + + fn traverse_rmdir(&self, _components: &mut Vec<&str>) -> core::result::Result<(), IoError> { + Err(IoError::ENOSYS) + } + + fn traverse_mkdir( + &self, + _components: &mut Vec<&str>, + _mode: AccessPermission, + ) -> Result<(), IoError> { + Err(IoError::ENOSYS) + } +} + +pub(crate) fn init() { + info!("Try to initialize uhyve filesystem"); + if is_uhyve() { + 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, Box::new(UhyveDirectory::new())) + .expect("Mount failed. Duplicate mount_point?"); + } +} 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 41a4dfb6dd..d9e7980cff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,6 +64,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::*; @@ -82,13 +83,14 @@ mod entropy; mod env; pub mod errno; mod executor; -pub(crate) mod fd; -#[cfg(feature = "fs")] -pub(crate) mod fs; +pub mod fd; +pub mod fs; +pub mod io; mod mm; mod scheduler; mod synch; -mod syscalls; +pub mod syscalls; +pub mod time; #[cfg(target_os = "none")] hermit_entry::define_entry_version!(); @@ -249,10 +251,10 @@ 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"))] - fn main() -> !; + #[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(); #[cfg(feature = "newlib")] @@ -282,7 +284,6 @@ extern "C" fn initd(_arg: usize) { syscalls::init(); fd::init(); - #[cfg(feature = "fs")] fs::init(); // Get the application arguments and environment variables. @@ -295,10 +296,10 @@ 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"))] - main(); + #[cfg(all(not(test), feature = "common-os"))] + main(argc, argv, environ); } #[cfg(test)] test_main(); 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/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/interfaces/mod.rs b/src/syscalls/interfaces/mod.rs index 6b8be563fc..bf32eecfbf 100644 --- a/src/syscalls/interfaces/mod.rs +++ b/src/syscalls/interfaces/mod.rs @@ -1,12 +1,8 @@ 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::syscalls::fs::{self, FileAttr}; use crate::{arch, env}; mod generic; @@ -55,70 +51,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 - .lock() - .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 - .lock() - .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 - .lock() - .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 - } - - #[cfg(target_arch = "x86_64")] - fn lstat(&self, name: *const u8, stat: *mut FileAttr) -> i32 { - let name = unsafe { CStr::from_ptr(name as _) }.to_str().unwrap(); - debug!("lstat {}", name); - - fs::FILESYSTEM - .lock() - .lstat(name, stat) - .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |_| 0) - } } diff --git a/src/syscalls/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 7d1c55d910..7a805d36de 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -1,5 +1,8 @@ #![allow(clippy::result_unit_err)] +use core::ffi::CStr; +use core::marker::PhantomData; + #[cfg(feature = "newlib")] use hermit_sync::InterruptTicketMutex; use hermit_sync::Lazy; @@ -16,15 +19,16 @@ 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::syscalls::fs::FileAttr; +use crate::fd::{ + dup_object, get_object, remove_object, AccessPermission, FileDescriptor, IoCtl, OpenOption, +}; +use crate::fs::{self, 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")] @@ -44,7 +48,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() { @@ -100,7 +104,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] @@ -108,14 +118,19 @@ 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(); + let mode = if let Some(mode) = AccessPermission::from_bits(mode) { + mode + } else { + return -crate::errno::EINVAL; + }; -#[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] @@ -123,14 +138,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] @@ -139,7 +154,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] @@ -148,7 +171,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] @@ -157,8 +188,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] @@ -167,7 +205,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::fs::opendir(name).map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| v) + } else { + -crate::errno::EINVAL + } } #[no_mangle] @@ -175,18 +217,40 @@ pub extern "C" fn sys_opendir(name: *const u8) -> FileDescriptor { kernel_function!(__sys_opendir(name)) } -extern "C" fn __sys_open(name: *const u8, flags: i32, mode: i32) -> FileDescriptor { - crate::fd::open(name, flags, mode).map_or_else(|e| e, |v| v) +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) + } else { + -crate::errno::EINVAL + } } #[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)) } 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(), + |v| { + v.close(); + 0 + }, + ) } #[no_mangle] @@ -195,8 +259,17 @@ 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| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| { + (*v).read(slice).map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| v.try_into().unwrap(), + ) + }, + ) } #[no_mangle] @@ -205,8 +278,17 @@ 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| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| { + (*v).write(slice).map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| v.try_into().unwrap(), + ) + }, + ) } #[no_mangle] @@ -214,9 +296,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| e, |v| (*v).ioctl(cmd, argp)) + 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] @@ -227,8 +323,11 @@ 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, - |v| (*v).lseek(offset, num::FromPrimitive::from_i32(whence).unwrap()), + |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) + }, ) } @@ -237,20 +336,78 @@ 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 { +#[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 limit = dirp as usize + count; + let mut dirp: *mut Dirent64 = dirp; + let mut offset: i64 = 0; let obj = get_object(fd); - obj.map_or(DirectoryEntry::Invalid(-crate::errno::EINVAL), |v| { - (*v).readdir() - }) + obj.map_or_else( + |_| -crate::errno::EINVAL as i64, + |v| { + (*v).readdir().map_or_else( + |e| -num::ToPrimitive::to_i64(&e).unwrap(), + |v| { + 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 + }; + } + + offset + }, + ) + }, + ) } #[no_mangle] -pub extern "C" fn sys_readdir(fd: FileDescriptor) -> DirectoryEntry { - kernel_function!(__sys_readdir(fd)) +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 { - 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] diff --git a/src/syscalls/net.rs b/src/syscalls/net.rs index 4202be0a10..dc33909d5d 100644 --- a/src/syscalls/net.rs +++ b/src/syscalls/net.rs @@ -1,8 +1,21 @@ #![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; -use crate::fd::socket::*; +#[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] +use smoltcp::wire::{IpAddress, IpEndpoint, IpListenEndpoint}; + +use crate::errno::*; +use crate::executor::network::{NetworkState, NIC}; +#[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; pub const AF_INET: i32 = 0; @@ -34,7 +47,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; @@ -47,19 +59,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 +79,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 +88,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 +153,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 { @@ -120,6 +247,409 @@ 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.try_into().unwrap(), + ) + }, + ) +} + +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.try_into().unwrap(), + ) + }, + ) +} + +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.try_into().unwrap() + }, + ) + }, + ) +} + #[no_mangle] pub extern "C" fn sys_socket(domain: i32, type_: i32, protocol: i32) -> i32 { kernel_function!(__sys_socket(domain, type_, protocol)) 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;