diff --git a/Cargo.toml b/Cargo.toml index 1530270f86..b019182fdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -93,6 +93,7 @@ take-static = "0.1" talc = { version = "4" } time = { version = "0.3", default-features = false } zerocopy = { version = "0.7", features = ["derive"] } +build-time = "0.1.3" [dependencies.smoltcp] version = "0.10" diff --git a/src/fd/mod.rs b/src/fd/mod.rs index c5d6725fa8..66f0cd4aba 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -8,9 +8,8 @@ use hashbrown::HashMap; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; use crate::env; -use crate::errno::*; use crate::fd::stdio::*; -use crate::fs::{self, FileAttr, SeekWhence}; +use crate::fs::{self, DirectoryEntry, FileAttr, SeekWhence}; #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] pub(crate) mod socket; @@ -23,7 +22,7 @@ const STDERR_FILENO: FileDescriptor = 2; // TODO: Integrate with src/errno.rs ? #[allow(clippy::upper_case_acronyms)] #[derive(Debug, PartialEq, FromPrimitive, ToPrimitive)] -pub(crate) enum IoError { +pub enum IoError { ENOENT = crate::errno::ENOENT as isize, ENOSYS = crate::errno::ENOSYS as isize, EIO = crate::errno::EIO as isize, @@ -104,33 +103,16 @@ impl Default for AccessPermission { } } -#[repr(C)] -#[derive(Debug, Copy, Clone, Default)] -pub struct Dirent { - pub d_ino: u64, - pub d_off: u64, - pub d_namelen: u32, - pub d_type: u32, - pub d_name: [u8; 0], -} - -#[derive(Copy, Clone, Debug)] -#[repr(C)] -pub enum DirectoryEntry { - Invalid(i32), - Valid(*const Dirent), -} - pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// `read` attempts to read `len` bytes from the object references /// by the descriptor - fn read(&self, _buf: &mut [u8]) -> Result { + fn read(&self, _buf: &mut [u8]) -> Result { Err(IoError::ENOSYS) } /// `write` attempts to write `len` bytes to the object references /// by the descriptor - fn write(&self, _buf: &[u8]) -> Result { + fn write(&self, _buf: &[u8]) -> Result { Err(IoError::ENOSYS) } @@ -157,8 +139,8 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// 'readdir' returns a pointer to a dirent structure /// representing the next directory entry in the directory stream /// pointed to by the file descriptor - fn readdir(&self) -> DirectoryEntry { - DirectoryEntry::Invalid(-ENOSYS) + fn readdir(&self) -> Result, IoError> { + Err(IoError::EINVAL) } /// `mkdir` creates a directory entry @@ -216,7 +198,7 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// receive a message from a socket #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn recvfrom(&self, _buffer: &mut [u8]) -> Result<(isize, IpEndpoint), IoError> { + fn recvfrom(&self, _buffer: &mut [u8]) -> Result<(usize, IpEndpoint), IoError> { Err(IoError::ENOSYS) } @@ -228,7 +210,7 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { /// be sent to the address specified by dest_addr (overriding the pre-specified peer /// address). #[cfg(all(any(feature = "tcp", feature = "udp"), not(feature = "newlib")))] - fn sendto(&self, _buffer: &[u8], _endpoint: IpEndpoint) -> Result { + fn sendto(&self, _buffer: &[u8], _endpoint: IpEndpoint) -> Result { Err(IoError::ENOSYS) } @@ -248,19 +230,19 @@ pub(crate) trait ObjectInterface: Sync + Send + core::fmt::Debug + DynClone { fn close(&self) {} } -pub(crate) fn open(name: &str, flags: i32, mode: u32) -> Result { +pub(crate) fn open( + name: &str, + flags: OpenOption, + mode: AccessPermission, +) -> Result { // mode is 0x777 (0b0111_0111_0111), when flags | O_CREAT, else 0 // flags is bitmask of O_DEC_* defined above. // (taken from rust stdlib/sys hermit target ) - debug!("Open {}, {}, {}", name, flags, mode); + debug!("Open {}, {:?}, {:?}", name, flags, mode); let fs = fs::FILESYSTEM.get().unwrap(); - if let Ok(file) = fs.open( - name, - OpenOption::from_bits(flags).ok_or(IoError::EINVAL)?, - AccessPermission::from_bits(mode).ok_or(IoError::EINVAL)?, - ) { + if let Ok(file) = fs.open(name, flags, mode) { let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); if OBJECT_MAP.write().try_insert(fd, file).is_err() { Err(IoError::EINVAL) @@ -272,21 +254,16 @@ pub(crate) fn open(name: &str, flags: i32, mode: u32) -> Result Result { - debug!("Open directory {}", name); +pub(crate) fn close(fd: FileDescriptor) { + let _ = remove_object(fd).map(|v| v.close()); +} - let fs = fs::FILESYSTEM.get().unwrap(); - if let Ok(obj) = fs.opendir(name) { - let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); - // Would a GenericDir make sense? - if OBJECT_MAP.write().try_insert(fd, obj).is_err() { - Err(IoError::EINVAL) - } else { - Ok(fd as FileDescriptor) - } - } else { - Err(IoError::EINVAL) - } +pub(crate) fn read(fd: FileDescriptor, buf: &mut [u8]) -> Result { + get_object(fd).map_or_else(|e| Err(e), |v| v.read(buf)) +} + +pub(crate) fn write(fd: FileDescriptor, buf: &[u8]) -> Result { + get_object(fd).map_or_else(|e| Err(e), |v| v.write(buf)) } pub(crate) fn get_object(fd: FileDescriptor) -> Result, IoError> { diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index 09474e54b4..896f968ce2 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -63,7 +63,7 @@ impl Socket { // TODO: Remove allow once fixed: // https://github.com/rust-lang/rust-clippy/issues/11380 #[allow(clippy::needless_pass_by_ref_mut)] - async fn async_read(&self, buffer: &mut [u8]) -> Result { + async fn async_read(&self, buffer: &mut [u8]) -> Result { future::poll_fn(|cx| { self.with(|socket| match socket.state() { tcp::State::Closed | tcp::State::Closing | tcp::State::CloseWait => { @@ -80,7 +80,7 @@ impl Socket { .recv(|data| { let len = core::cmp::min(buffer.len(), data.len()); buffer[..len].copy_from_slice(&data[..len]); - (len, isize::try_from(len).unwrap()) + (len, len) }) .map_err(|_| IoError::EIO), ) @@ -94,7 +94,7 @@ impl Socket { .await } - async fn async_write(&self, buffer: &[u8]) -> Result { + async fn async_write(&self, buffer: &[u8]) -> Result { let mut pos: usize = 0; while pos < buffer.len() { @@ -134,7 +134,7 @@ impl Socket { pos += n; } - Ok(pos.try_into().unwrap()) + Ok(pos) } async fn async_connect(&self, endpoint: IpEndpoint) -> Result<(), IoError> { @@ -269,7 +269,7 @@ impl ObjectInterface for Socket { self.with(|socket| socket.local_endpoint()) } - fn read(&self, buf: &mut [u8]) -> Result { + fn read(&self, buf: &mut [u8]) -> Result { if buf.is_empty() { return Ok(0); } @@ -291,7 +291,7 @@ impl ObjectInterface for Socket { } } - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { if buf.is_empty() { return Ok(0); } diff --git a/src/fd/socket/udp.rs b/src/fd/socket/udp.rs index f0b516f9f8..a3002855e6 100644 --- a/src/fd/socket/udp.rs +++ b/src/fd/socket/udp.rs @@ -53,7 +53,7 @@ impl Socket { .await } - async fn async_read(&self, buffer: &mut [u8]) -> Result { + async fn async_read(&self, buffer: &mut [u8]) -> Result { future::poll_fn(|cx| { self.with(|socket| { if socket.is_open() { @@ -62,14 +62,14 @@ impl Socket { Ok((len, meta)) => match self.endpoint.load() { Some(ep) => { if meta.endpoint == ep { - Poll::Ready(Ok(len.try_into().unwrap())) + Poll::Ready(Ok(len)) } else { buffer[..len].iter_mut().for_each(|x| *x = 0); socket.register_recv_waker(cx.waker()); Poll::Pending } } - None => Poll::Ready(Ok(len.try_into().unwrap())), + None => Poll::Ready(Ok(len)), }, _ => Poll::Ready(Err(IoError::EIO)), } @@ -85,7 +85,7 @@ impl Socket { .await } - async fn async_recvfrom(&self, buffer: &mut [u8]) -> Result<(isize, IpEndpoint), IoError> { + async fn async_recvfrom(&self, buffer: &mut [u8]) -> Result<(usize, IpEndpoint), IoError> { future::poll_fn(|cx| { self.with(|socket| { if socket.is_open() { @@ -94,14 +94,14 @@ impl Socket { Ok((len, meta)) => match self.endpoint.load() { Some(ep) => { if meta.endpoint == ep { - Poll::Ready(Ok((len.try_into().unwrap(), meta.endpoint))) + Poll::Ready(Ok((len, meta.endpoint))) } else { buffer[..len].iter_mut().for_each(|x| *x = 0); socket.register_recv_waker(cx.waker()); Poll::Pending } } - None => Poll::Ready(Ok((len.try_into().unwrap(), meta.endpoint))), + None => Poll::Ready(Ok((len, meta.endpoint))), }, _ => Poll::Ready(Err(IoError::EIO)), } @@ -117,7 +117,7 @@ impl Socket { .await } - async fn async_write(&self, buffer: &[u8], meta: &UdpMetadata) -> Result { + async fn async_write(&self, buffer: &[u8], meta: &UdpMetadata) -> Result { future::poll_fn(|cx| { self.with(|socket| { if socket.is_open() { @@ -125,7 +125,7 @@ impl Socket { Poll::Ready( socket .send_slice(buffer, *meta) - .map(|_| buffer.len() as isize) + .map(|_| buffer.len()) .map_err(|_| IoError::EIO), ) } else { @@ -151,7 +151,7 @@ impl ObjectInterface for Socket { Ok(()) } - fn sendto(&self, buf: &[u8], endpoint: IpEndpoint) -> Result { + fn sendto(&self, buf: &[u8], endpoint: IpEndpoint) -> Result { let meta = UdpMetadata::from(endpoint); if self.nonblocking.load(Ordering::Acquire) { @@ -161,7 +161,7 @@ impl ObjectInterface for Socket { } } - fn recvfrom(&self, buf: &mut [u8]) -> Result<(isize, IpEndpoint), IoError> { + fn recvfrom(&self, buf: &mut [u8]) -> Result<(usize, IpEndpoint), IoError> { if self.nonblocking.load(Ordering::Acquire) { poll_on(self.async_recvfrom(buf), Some(Duration::ZERO)).map_err(|x| { if x == IoError::ETIME { @@ -179,7 +179,7 @@ impl ObjectInterface for Socket { } } - fn read(&self, buf: &mut [u8]) -> Result { + fn read(&self, buf: &mut [u8]) -> Result { if buf.len() == 0 { return Ok(0); } @@ -201,7 +201,7 @@ impl ObjectInterface for Socket { } } - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { if buf.len() == 0 { return Ok(0); } diff --git a/src/fd/stdio.rs b/src/fd/stdio.rs index 67af71caf0..79fec62f77 100644 --- a/src/fd/stdio.rs +++ b/src/fd/stdio.rs @@ -1,4 +1,3 @@ -use core::isize; #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] use core::ptr; @@ -78,11 +77,11 @@ impl GenericStdin { pub struct GenericStdout; impl ObjectInterface for GenericStdout { - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { // stdin/err/out all go to console CONSOLE.lock().write_all(buf); - Ok(buf.len().try_into().unwrap()) + Ok(buf.len()) } } @@ -96,11 +95,11 @@ impl GenericStdout { pub struct GenericStderr; impl ObjectInterface for GenericStderr { - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { // stdin/err/out all go to console CONSOLE.lock().write_all(buf); - Ok(buf.len().try_into().unwrap()) + Ok(buf.len()) } } @@ -125,11 +124,11 @@ impl UhyveStdin { pub struct UhyveStdout; impl ObjectInterface for UhyveStdout { - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { let mut syswrite = SysWrite::new(STDOUT_FILENO, buf.as_ptr(), buf.len()); uhyve_send(UHYVE_PORT_WRITE, &mut syswrite); - Ok(syswrite.len as isize) + Ok(syswrite.len) } } @@ -143,11 +142,11 @@ impl UhyveStdout { pub struct UhyveStderr; impl ObjectInterface for UhyveStderr { - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { let mut syswrite = SysWrite::new(STDERR_FILENO, buf.as_ptr(), buf.len()); uhyve_send(UHYVE_PORT_WRITE, &mut syswrite); - Ok(syswrite.len as isize) + Ok(syswrite.len) } } diff --git a/src/fs/fuse.rs b/src/fs/fuse.rs index e5137e55fa..5aa6412b32 100644 --- a/src/fs/fuse.rs +++ b/src/fs/fuse.rs @@ -15,9 +15,10 @@ use crate::arch::kernel::mmio::get_filesystem_driver; #[cfg(feature = "pci")] use crate::drivers::pci::get_filesystem_driver; use crate::drivers::virtio::virtqueue::AsSliceU8; -use crate::fd::{DirectoryEntry, IoError}; +use crate::fd::IoError; use crate::fs::{ - self, AccessPermission, FileAttr, NodeKind, ObjectInterface, OpenOption, SeekWhence, VfsNode, + self, AccessPermission, DirectoryEntry, FileAttr, NodeKind, ObjectInterface, OpenOption, + SeekWhence, VfsNode, }; // response out layout eg @ https://github.com/zargony/fuse-rs/blob/bf6d1cf03f3277e35b580f3c7b9999255d72ecf3/src/ll/request.rs#L44 @@ -28,7 +29,7 @@ const FUSE_ROOT_ID: u64 = 1; const MAX_READ_LEN: usize = 1024 * 64; const MAX_WRITE_LEN: usize = 1024 * 64; -const U64_SIZE: u32 = ::core::mem::size_of::() as u32; +const U64_SIZE: usize = ::core::mem::size_of::(); const S_IFLNK: u32 = 40960; const S_IFMT: u32 = 61440; @@ -1151,7 +1152,7 @@ impl FuseFileHandleInner { } } - fn read(&mut self, buf: &mut [u8]) -> Result { + fn read(&mut self, buf: &mut [u8]) -> Result { debug!("FUSE read!"); let mut len = buf.len(); if len > MAX_READ_LEN { @@ -1181,14 +1182,14 @@ impl FuseFileHandleInner { MaybeUninit::slice_assume_init_ref(&rsp.extra_buffer[..len]) }); - Ok(len.try_into().unwrap()) + Ok(len) } else { debug!("File not open, cannot read!"); Err(IoError::ENOENT) } } - fn write(&mut self, buf: &[u8]) -> Result { + fn write(&mut self, buf: &[u8]) -> Result { debug!("FUSE write!"); let mut len = buf.len(); if len > MAX_WRITE_LEN { @@ -1217,7 +1218,7 @@ impl FuseFileHandleInner { rsp_size.try_into().unwrap() }; self.offset += len; - Ok(len.try_into().unwrap()) + Ok(len) } else { warn!("File not open, cannot read!"); Err(IoError::ENOENT) @@ -1269,11 +1270,11 @@ impl FuseFileHandle { } impl ObjectInterface for FuseFileHandle { - fn read(&self, buf: &mut [u8]) -> Result { + fn read(&self, buf: &mut [u8]) -> Result { self.0.lock().read(buf) } - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { self.0.lock().write(buf) } @@ -1289,136 +1290,6 @@ impl Clone for FuseFileHandle { } } -#[derive(Debug)] -struct FuseDirectoryHandleInner { - fuse_nid: Option, - fuse_fh: Option, - offset: u64, - buffer_offset: u32, - response: Option>>, -} - -impl FuseDirectoryHandleInner { - pub fn new() -> Self { - Self { - fuse_nid: None, - fuse_fh: None, - offset: 0, - buffer_offset: 0, - response: None, - } - } - - fn readdir(&mut self) -> DirectoryEntry { - // Check if we have to read the directory via FUSE or still have a direntry in the last respnse - let resp: &_ = match &mut self.response { - Some(resp) - if resp.header.len - self.buffer_offset - > core::mem::size_of::().try_into().unwrap() => - { - resp - } - option => { - debug!("FUSE read from dirfile"); - // Linux seems to allocate a single page to store the dirfile - let len = MAX_READ_LEN as u32; - - let (Some(nid), Some(fh)) = (self.fuse_nid, self.fuse_fh) else { - warn!("FUSE dir not open, cannot read!"); - return DirectoryEntry::Invalid(-crate::errno::EBADF); - }; - - let (mut cmd, mut rsp) = create_read(nid, fh, len, self.offset); - cmd.header.opcode = Opcode::FUSE_READDIR as u32; - if let Some(fs_driver) = get_filesystem_driver() { - fs_driver.lock().send_command(cmd.as_ref(), rsp.as_mut()); - } else { - return DirectoryEntry::Invalid(-crate::errno::ENOSYS); - } - - let len: usize = if rsp.header.len as usize - - ::core::mem::size_of::() - - ::core::mem::size_of::() - >= len.try_into().unwrap() - { - len.try_into().unwrap() - } else { - rsp.header.len as usize - - ::core::mem::size_of::() - - ::core::mem::size_of::() - }; - - if len <= core::mem::size_of::() { - debug!("FUSE no new dirs"); - return DirectoryEntry::Valid(core::ptr::null()); - } - - // Keep smart pointer to response - let rsp = option.insert(rsp); - self.buffer_offset = 0; - - debug!("FUSE new buffer len: {}", len); - rsp - } - }; - - let return_ptr: *const u8 = unsafe { - resp.extra_buffer - .as_ptr() - .byte_add(self.buffer_offset.try_into().unwrap()) as _ - }; - - let dirent = unsafe { &*(return_ptr as *const fuse_dirent) }; - - self.offset = dirent.d_off; - - self.buffer_offset += core::mem::size_of::() as u32 + dirent.d_namelen; - - // Allign to dirent struct - self.buffer_offset = ((self.buffer_offset) + U64_SIZE - 1) & (!(U64_SIZE - 1)); - - // Check alignment - assert!(return_ptr.is_aligned_to(U64_SIZE.try_into().unwrap())); - - DirectoryEntry::Valid(return_ptr.cast()) - } -} - -impl Drop for FuseDirectoryHandleInner { - fn drop(&mut self) { - if self.fuse_nid.is_some() && self.fuse_fh.is_some() { - let (mut cmd, mut rsp) = create_release(self.fuse_nid.unwrap(), self.fuse_fh.unwrap()); - cmd.header.opcode = Opcode::FUSE_RELEASEDIR as u32; - get_filesystem_driver() - .unwrap() - .lock() - .send_command(cmd.as_ref(), rsp.as_mut()); - } - } -} - -#[derive(Debug)] -struct FuseDirectoryHandle(pub Arc>); - -impl Clone for FuseDirectoryHandle { - fn clone(&self) -> Self { - warn!("FuseDirectoryHandle: clone not tested"); - Self(self.0.clone()) - } -} - -impl FuseDirectoryHandle { - pub fn new() -> Self { - Self(Arc::new(SpinMutex::new(FuseDirectoryHandleInner::new()))) - } -} - -impl ObjectInterface for FuseDirectoryHandle { - fn readdir(&self) -> DirectoryEntry { - self.0.lock().readdir() - } -} - #[derive(Debug)] pub(crate) struct FuseDirectory; @@ -1434,10 +1305,7 @@ impl VfsNode for FuseDirectory { NodeKind::Directory } - fn traverse_opendir( - &self, - components: &mut Vec<&str>, - ) -> Result, IoError> { + fn traverse_readdir(&self, components: &mut Vec<&str>) -> Result, IoError> { let path: String = if components.is_empty() { "/".to_string() } else { @@ -1446,31 +1314,74 @@ impl VfsNode for FuseDirectory { debug!("FUSE opendir: {}", path); - let readdir = FuseDirectoryHandle::new(); - - // Lookup nodeid - - let mut readdir_guard = readdir.0.lock(); - readdir_guard.fuse_nid = lookup(&path); - - if readdir_guard.fuse_nid.is_none() { - warn!("Fuse lookup seems to have failed!"); - return Err(IoError::ENOENT); - } + let fuse_nid = lookup(&path).ok_or(IoError::ENOENT)?; // Opendir // Flag 0x10000 for O_DIRECTORY might not be necessary - let (mut cmd, mut rsp) = create_open(readdir_guard.fuse_nid.unwrap(), 0x10000); + let (mut cmd, mut rsp) = create_open(fuse_nid, 0x10000); cmd.header.opcode = Opcode::FUSE_OPENDIR as u32; get_filesystem_driver() .ok_or(IoError::ENOSYS)? .lock() .send_command(cmd.as_ref(), rsp.as_mut()); - readdir_guard.fuse_fh = Some(unsafe { rsp.rsp.assume_init().fh }); + let fuse_fh = unsafe { rsp.rsp.assume_init().fh }; + + debug!("FUSE readdir: {}", path); + + // Linux seems to allocate a single page to store the dirfile + let len = MAX_READ_LEN as u32; + let mut offset: usize = 0; + + // read content of the directory + let (mut cmd, mut rsp) = create_read(fuse_nid, fuse_fh, len, 0); + cmd.header.opcode = Opcode::FUSE_READDIR as u32; + get_filesystem_driver() + .ok_or(IoError::ENOSYS)? + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); + + let len: usize = if rsp.header.len as usize + - ::core::mem::size_of::() + - ::core::mem::size_of::() + >= len.try_into().unwrap() + { + len.try_into().unwrap() + } else { + rsp.header.len as usize + - ::core::mem::size_of::() + - ::core::mem::size_of::() + }; + + if len <= core::mem::size_of::() { + debug!("FUSE no new dirs"); + return Err(IoError::ENOENT); + } + + let mut entries: Vec = Vec::new(); + while rsp.header.len as usize - offset > core::mem::size_of::() { + let dirent = + unsafe { &*(rsp.extra_buffer.as_ptr().byte_add(offset) as *const fuse_dirent) }; - drop(readdir_guard); + offset += core::mem::size_of::() + dirent.d_namelen as usize; + // Allign to dirent struct + offset = ((offset) + U64_SIZE - 1) & (!(U64_SIZE - 1)); + + let name: &'static [u8] = unsafe { + core::slice::from_raw_parts( + dirent.d_name.as_ptr(), + dirent.d_namelen.try_into().unwrap(), + ) + }; + entries.push(DirectoryEntry::new(name)); + } + + let (cmd, mut rsp) = create_release(fuse_nid, fuse_fh); + get_filesystem_driver() + .unwrap() + .lock() + .send_command(cmd.as_ref(), rsp.as_mut()); - Ok(Arc::new(readdir)) + Ok(entries) } fn traverse_stat(&self, components: &mut Vec<&str>) -> Result { diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 5beeaad9b6..061338934d 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -9,7 +9,6 @@ #![allow(dead_code)] -use alloc::alloc::{alloc_zeroed, Layout}; use alloc::boxed::Box; use alloc::collections::BTreeMap; use alloc::string::{String, ToString}; @@ -17,12 +16,11 @@ use alloc::sync::Arc; use alloc::vec::Vec; use core::ops::{Deref, DerefMut}; use core::slice; -use core::sync::atomic::{AtomicUsize, Ordering}; use hermit_sync::{RwSpinLock, SpinMutex}; -use crate::fd::{AccessPermission, DirectoryEntry, Dirent, IoError, ObjectInterface, OpenOption}; -use crate::fs::{FileAttr, NodeKind, VfsNode}; +use crate::fd::{AccessPermission, IoError, ObjectInterface, OpenOption}; +use crate::fs::{DirectoryEntry, FileAttr, NodeKind, VfsNode}; #[derive(Debug)] struct RomFileInterface { @@ -33,7 +31,7 @@ struct RomFileInterface { } impl ObjectInterface for RomFileInterface { - fn read(&self, buf: &mut [u8]) -> Result { + fn read(&self, buf: &mut [u8]) -> Result { let vec = self.data.read(); let mut pos_guard = self.pos.lock(); let pos = *pos_guard; @@ -51,7 +49,7 @@ impl ObjectInterface for RomFileInterface { buf[0..len].clone_from_slice(&vec[pos..pos + len]); *pos_guard = pos + len; - Ok(len.try_into().unwrap()) + Ok(len) } } @@ -87,7 +85,7 @@ pub struct RamFileInterface { } impl ObjectInterface for RamFileInterface { - fn read(&self, buf: &mut [u8]) -> Result { + fn read(&self, buf: &mut [u8]) -> Result { let guard = self.data.read(); let vec = guard.deref(); let mut pos_guard = self.pos.lock(); @@ -106,10 +104,10 @@ impl ObjectInterface for RamFileInterface { buf[0..len].clone_from_slice(&vec[pos..pos + len]); *pos_guard = pos + len; - Ok(len.try_into().unwrap()) + Ok(len) } - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { let mut guard = self.data.write(); let vec = guard.deref_mut(); let mut pos_guard = self.pos.lock(); @@ -122,7 +120,7 @@ impl ObjectInterface for RamFileInterface { vec[pos..pos + buf.len()].clone_from_slice(buf); *pos_guard = pos + buf.len(); - Ok(buf.len().try_into().unwrap()) + Ok(buf.len()) } } @@ -248,119 +246,6 @@ impl RamFile { } } -#[derive(Debug)] -struct MemDirectoryInterface { - /// Position within the file - pos: AtomicUsize, - /// File content - data: Arc< - RwSpinLock>>, - >, -} - -impl MemDirectoryInterface { - pub fn new( - data: Arc< - RwSpinLock< - BTreeMap>, - >, - >, - ) -> Self { - Self { - pos: AtomicUsize::new(0), - data, - } - } -} - -impl ObjectInterface for MemDirectoryInterface { - fn readdir(&self) -> DirectoryEntry { - let pos = self.pos.fetch_add(1, Ordering::SeqCst); - - if pos == 0 { - let name = "."; - let name_len = name.len(); - - let len = core::mem::size_of::() + name_len + 1; - let layout = Layout::from_size_align(len, core::mem::align_of::()) - .unwrap() - .pad_to_align(); - - let raw = unsafe { - let raw = alloc_zeroed(layout) as *mut Dirent; - (*raw).d_namelen = name_len.try_into().unwrap(); - core::ptr::copy_nonoverlapping( - name.as_ptr(), - &mut (*raw).d_name as *mut u8, - name_len, - ); - - raw - }; - - DirectoryEntry::Valid(raw) - } else if pos == 1 { - let name = ".."; - let name_len = name.len(); - - let len = core::mem::size_of::() + name_len + 1; - let layout = Layout::from_size_align(len, core::mem::align_of::()) - .unwrap() - .pad_to_align(); - - let raw = unsafe { - let raw = alloc_zeroed(layout) as *mut Dirent; - (*raw).d_namelen = name_len.try_into().unwrap(); - core::ptr::copy_nonoverlapping( - name.as_ptr(), - &mut (*raw).d_name as *mut u8, - name_len, - ); - - raw - }; - - DirectoryEntry::Valid(raw) - } else { - let keys: Vec<_> = self.data.read().keys().cloned().collect(); - - if keys.len() > pos - 2 { - let name_len = keys[pos - 2].len(); - - let len = core::mem::size_of::() + name_len + 1; - let layout = Layout::from_size_align(len, core::mem::align_of::()) - .unwrap() - .pad_to_align(); - - let raw = unsafe { - let raw = alloc_zeroed(layout) as *mut Dirent; - (*raw).d_namelen = name_len.try_into().unwrap(); - core::ptr::copy_nonoverlapping( - keys[pos - 2].as_ptr(), - &mut (*raw).d_name as *mut u8, - name_len, - ); - - raw - }; - - DirectoryEntry::Valid(raw) - } else { - DirectoryEntry::Valid(core::ptr::null()) - } - } - } -} - -impl Clone for MemDirectoryInterface { - fn clone(&self) -> Self { - Self { - pos: AtomicUsize::new(self.pos.load(Ordering::SeqCst)), - data: self.data.clone(), - } - } -} - #[derive(Debug)] pub(crate) struct MemDirectory { inner: Arc< @@ -478,20 +363,22 @@ impl VfsNode for MemDirectory { Err(IoError::EBADF) } - fn traverse_opendir( - &self, - components: &mut Vec<&str>, - ) -> Result, IoError> { + fn traverse_readdir(&self, components: &mut Vec<&str>) -> Result, IoError> { if let Some(component) = components.pop() { let node_name = String::from(component); if let Some(directory) = self.inner.read().get(&node_name) { - directory.traverse_opendir(components) + directory.traverse_readdir(components) } else { Err(IoError::EBADF) } } else { - Ok(Arc::new(MemDirectoryInterface::new(self.inner.clone()))) + let mut entries: Vec = Vec::new(); + for name in self.inner.read().keys() { + entries.push(DirectoryEntry::new(name.as_bytes())); + } + + Ok(entries) } } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 242c8e6ef2..55ab2ea123 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -6,14 +6,53 @@ mod uhyve; use alloc::boxed::Box; use alloc::sync::Arc; use alloc::vec::Vec; +use core::ffi::CStr; +use core::fmt; +use core::sync::atomic::{AtomicUsize, Ordering}; use hermit_sync::OnceCell; use mem::MemDirectory; -use crate::fd::{AccessPermission, IoError, ObjectInterface, OpenOption}; +use crate::fd::{ + insert_object, AccessPermission, IoError, ObjectInterface, OpenOption, FD_COUNTER, +}; +use crate::io::Write; pub(crate) static FILESYSTEM: OnceCell = OnceCell::new(); +pub const MAX_NAME_LENGTH: usize = 256; + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct DirectoryEntry { + pub d_name: [u8; MAX_NAME_LENGTH], +} + +impl DirectoryEntry { + pub fn new(d_name: &[u8]) -> Self { + let len = core::cmp::min(d_name.len(), MAX_NAME_LENGTH); + let mut entry = Self { + d_name: [0; MAX_NAME_LENGTH], + }; + + entry.d_name[..len].copy_from_slice(&d_name[..len]); + + entry + } +} + +impl fmt::Debug for DirectoryEntry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let d_name = unsafe { CStr::from_ptr(self.d_name.as_ptr() as _) } + .to_str() + .unwrap(); + + f.debug_struct("DirectoryEntry") + .field("d_name", &d_name) + .finish() + } +} + /// Type of the VNode #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum NodeKind { @@ -58,10 +97,10 @@ pub(crate) trait VfsNode: core::fmt::Debug { } /// Helper function to open a directory - fn traverse_opendir( + fn traverse_readdir( &self, _components: &mut Vec<&str>, - ) -> Result, IoError> { + ) -> Result, IoError> { Err(IoError::ENOSYS) } @@ -95,6 +134,41 @@ pub(crate) trait VfsNode: core::fmt::Debug { } } +#[derive(Debug)] +struct DirectoryReader { + pos: AtomicUsize, + data: Vec, +} + +impl DirectoryReader { + pub fn new(data: Vec) -> Self { + Self { + pos: AtomicUsize::new(0), + data, + } + } +} + +impl ObjectInterface for DirectoryReader { + fn readdir(&self) -> Result, IoError> { + let pos = self.pos.fetch_add(1, Ordering::SeqCst); + if pos < self.data.len() { + Ok(Some(self.data[pos])) + } else { + Ok(None) + } + } +} + +impl Clone for DirectoryReader { + fn clone(&self) -> Self { + Self { + pos: AtomicUsize::new(self.pos.load(Ordering::SeqCst)), + data: self.data.clone(), + } + } +} + #[derive(Debug)] pub(crate) struct Filesystem { root: MemDirectory, @@ -156,18 +230,23 @@ impl Filesystem { self.root.traverse_mkdir(&mut components, mode) } - /// List given directory pub fn opendir(&self, path: &str) -> Result, IoError> { + debug!("Open directory {}", path); + Ok(Arc::new(DirectoryReader::new(self.readdir(path)?))) + } + + /// List given directory + pub fn readdir(&self, path: &str) -> Result, IoError> { if path.trim() == "/" { let mut components: Vec<&str> = Vec::new(); - self.root.traverse_opendir(&mut components) + self.root.traverse_readdir(&mut components) } else { let mut components: Vec<&str> = path.split('/').collect(); components.reverse(); components.pop(); - self.root.traverse_opendir(&mut components) + self.root.traverse_readdir(&mut components) } } @@ -266,6 +345,7 @@ pub enum SeekWhence { pub(crate) fn init() { const VERSION: &str = env!("CARGO_PKG_VERSION"); + const UTC_BUILT_TIME: &str = build_time::build_time_utc!(); FILESYSTEM.set(Filesystem::new()).unwrap(); FILESYSTEM @@ -276,23 +356,21 @@ pub(crate) fn init() { FILESYSTEM .get() .unwrap() - .mkdir("/etc", AccessPermission::from_bits(0o777).unwrap()) - .expect("Unable to create /tmp"); - if let Ok(fd) = FILESYSTEM.get().unwrap().open( - "/etc/hostname", - OpenOption::O_CREAT | OpenOption::O_RDWR, - AccessPermission::from_bits(0o644).unwrap(), - ) { - let _ret = fd.write(b"Hermit"); - fd.close(); - } - if let Ok(fd) = FILESYSTEM.get().unwrap().open( - "/etc/version", - OpenOption::O_CREAT | OpenOption::O_RDWR, - AccessPermission::from_bits(0o644).unwrap(), - ) { - let _ret = fd.write(VERSION.as_bytes()); - fd.close(); + .mkdir("/proc", AccessPermission::from_bits(0o777).unwrap()) + .expect("Unable to create /proc"); + + if let Ok(mut file) = File::create("/proc/version") { + if file + .write_fmt(format_args!( + "HermitOS version {} # {}", + VERSION, UTC_BUILT_TIME + )) + .is_err() + { + error!("Unable to write in /proc/version"); + } + } else { + error!("Unable to create /proc/version"); } #[cfg(all(feature = "fuse", feature = "pci"))] @@ -300,12 +378,88 @@ pub(crate) fn init() { uhyve::init(); } -pub unsafe fn create_file(name: &str, ptr: *const u8, length: usize, mode: AccessPermission) { +pub unsafe fn create_file( + name: &str, + ptr: *const u8, + length: usize, + mode: AccessPermission, +) -> Result<(), IoError> { unsafe { FILESYSTEM .get() - .unwrap() + .ok_or(IoError::EINVAL)? .create_file(name, ptr, length, mode) - .expect("Unable to create file from ROM") + } +} + +/// Returns an vectri with all the entries within a directory. +pub fn readdir(name: &str) -> Result, IoError> { + debug!("Read directory {}", name); + + FILESYSTEM.get().ok_or(IoError::EINVAL)?.readdir(name) +} + +/// a +pub(crate) fn opendir(name: &str) -> Result { + let obj = FILESYSTEM.get().unwrap().opendir(name)?; + let fd = FD_COUNTER.fetch_add(1, Ordering::SeqCst); + + let _ = insert_object(fd, obj); + + Ok(fd) +} + +use crate::fd::{self, FileDescriptor}; + +pub fn file_attributes(path: &str) -> Result { + FILESYSTEM.get().unwrap().lstat(path) +} + +#[derive(Debug)] +pub struct File(FileDescriptor); + +impl File { + /// Creates a new file in read-write mode; error if the file exists. + /// + /// This function will create a file if it does not exist, or return + /// an error if it does. This way, if the call succeeds, the file + /// returned is guaranteed to be new. + pub fn create(path: &str) -> Result { + let fd = fd::open( + path, + OpenOption::O_CREAT | OpenOption::O_RDWR, + AccessPermission::from_bits(0o666).unwrap(), + )?; + + Ok(File(fd)) + } + + /// Attempts to open a file in read-write mode. + pub fn open(path: &str) -> Result { + let fd = fd::open( + path, + OpenOption::O_RDWR, + AccessPermission::from_bits(0o666).unwrap(), + )?; + + Ok(File(fd)) + } +} + +impl crate::io::Read for File { + fn read(&mut self, buf: &mut [u8]) -> Result { + fd::read(self.0, buf) + } +} + +impl crate::io::Write for File { + fn write(&mut self, buf: &[u8]) -> Result { + fd::write(self.0, buf) + } +} + +impl Drop for File { + fn drop(&mut self) { + fd::close(self.0); } } diff --git a/src/fs/uhyve.rs b/src/fs/uhyve.rs index 136cbb7d05..7d749a4090 100644 --- a/src/fs/uhyve.rs +++ b/src/fs/uhyve.rs @@ -163,18 +163,18 @@ impl UhyveFileHandleInner { Self(fd) } - fn read(&mut self, buf: &mut [u8]) -> Result { + fn read(&mut self, buf: &mut [u8]) -> Result { let mut sysread = SysRead::new(self.0, buf.as_mut_ptr(), buf.len()); uhyve_send(UHYVE_PORT_READ, &mut sysread); if sysread.ret >= 0 { - Ok(sysread.ret) + Ok(sysread.ret.try_into().unwrap()) } else { Err(IoError::EIO) } } - fn write(&mut self, buf: &[u8]) -> Result { + fn write(&mut self, buf: &[u8]) -> Result { let mut syswrite = SysWrite::new(self.0, buf.as_ptr(), buf.len()); uhyve_send(UHYVE_PORT_WRITE, &mut syswrite); @@ -210,11 +210,11 @@ impl UhyveFileHandle { } impl ObjectInterface for UhyveFileHandle { - fn read(&self, buf: &mut [u8]) -> Result { + fn read(&self, buf: &mut [u8]) -> Result { self.0.lock().read(buf) } - fn write(&self, buf: &[u8]) -> Result { + fn write(&self, buf: &[u8]) -> Result { self.0.lock().write(buf) } @@ -244,13 +244,6 @@ impl VfsNode for UhyveDirectory { NodeKind::Directory } - fn traverse_opendir( - &self, - _omponents: &mut Vec<&str>, - ) -> Result, IoError> { - Err(IoError::ENOSYS) - } - fn traverse_stat(&self, _components: &mut Vec<&str>) -> Result { Err(IoError::ENOSYS) } diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000000..725c586204 --- /dev/null +++ b/src/io.rs @@ -0,0 +1,97 @@ +use alloc::string::String; +use alloc::vec::Vec; +use core::fmt; + +use crate::fd::IoError; + +/// The Read trait allows for reading bytes from a source. +/// +/// The Read trait is derived from Rust's std library. +pub trait Read { + fn read(&mut self, buf: &mut [u8]) -> Result; + + /// Read all bytes until EOF in this source, placing them into buf. + fn read_to_end(&mut self, buf: &mut Vec) -> Result { + let start_len = buf.len(); + + loop { + let mut probe = [0u8; 512]; + + match self.read(&mut probe) { + Ok(0) => return Ok(buf.len() - start_len), + Ok(n) => { + buf.extend_from_slice(&probe[..n]); + } + Err(e) => return Err(e), + } + } + } + + /// Read all bytes until EOF in this source, appending them to `buf`. + /// + /// If successful, this function returns the number of bytes which were read + /// and appended to `buf`. + fn read_to_string(&mut self, buf: &mut String) -> Result { + unsafe { self.read_to_end(buf.as_mut_vec()) } + } +} + +/// The Write trait allows for reading bytes from a source. +/// +/// The Write trait is derived from Rust's std library. +pub trait Write { + fn write(&mut self, buf: &[u8]) -> Result; + + /// Attempts to write an entire buffer into this writer. + fn write_all(&mut self, mut buf: &[u8]) -> Result<(), IoError> { + while !buf.is_empty() { + match self.write(buf) { + Ok(0) => { + return Err(IoError::EIO); + } + Ok(n) => buf = &buf[n..], + Err(e) => return Err(e), + } + } + + Ok(()) + } + + /// Writes a formatted string into this writer, returning any error encountered. + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> Result<(), IoError> { + // Create a shim which translates a Write to a fmt::Write and saves + // off I/O errors. instead of discarding them + struct Adapter<'a, T: ?Sized> { + inner: &'a mut T, + error: Result<(), IoError>, + } + + impl fmt::Write for Adapter<'_, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adapter { + inner: self, + error: Ok(()), + }; + match fmt::write(&mut output, fmt) { + Ok(()) => Ok(()), + Err(..) => { + // check if the error came from the underlying `Write` or not + if output.error.is_err() { + output.error + } else { + Err(IoError::EINVAL) + } + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 91cdad60ac..8752cde908 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -86,10 +86,11 @@ pub mod errno; mod executor; pub mod fd; pub mod fs; +pub mod io; mod mm; mod scheduler; mod synch; -mod syscalls; +pub mod syscalls; #[cfg(target_os = "none")] hermit_entry::define_entry_version!(); diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index c161ee3b04..3b39741672 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -19,9 +19,9 @@ pub use self::tasks::*; pub use self::timer::*; use crate::env; use crate::fd::{ - dup_object, get_object, remove_object, AccessPermission, DirectoryEntry, FileDescriptor, IoCtl, + dup_object, get_object, remove_object, AccessPermission, FileDescriptor, IoCtl, OpenOption, }; -use crate::fs::{self, FileAttr}; +use crate::fs::{self, DirectoryEntry, FileAttr}; use crate::syscalls::interfaces::SyscallInterface; #[cfg(target_os = "none")] use crate::{__sys_free, __sys_malloc, __sys_realloc}; @@ -47,7 +47,7 @@ mod timer; const LWIP_FD_BIT: i32 = 1 << 30; #[cfg(feature = "newlib")] -pub static LWIP_LOCK: InterruptTicketMutex<()> = InterruptTicketMutex::new(()); +pub(crate) static LWIP_LOCK: InterruptTicketMutex<()> = InterruptTicketMutex::new(()); pub(crate) static SYS: Lazy<&'static dyn SyscallInterface> = Lazy::new(|| { if env::is_uhyve() { @@ -205,7 +205,7 @@ pub extern "C" fn sys_fstat(fd: FileDescriptor, stat: *mut FileAttr) -> i32 { extern "C" fn __sys_opendir(name: *const u8) -> FileDescriptor { if let Ok(name) = unsafe { CStr::from_ptr(name as _) }.to_str() { - crate::fd::opendir(name).map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| v) + crate::fs::opendir(name).map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| v) } else { -crate::errno::EINVAL } @@ -217,6 +217,17 @@ pub extern "C" fn sys_opendir(name: *const u8) -> FileDescriptor { } extern "C" fn __sys_open(name: *const u8, flags: i32, mode: u32) -> FileDescriptor { + let flags = if let Some(flags) = OpenOption::from_bits(flags) { + flags + } else { + return -crate::errno::EINVAL; + }; + let mode = if let Some(mode) = AccessPermission::from_bits(mode) { + mode + } else { + return -crate::errno::EINVAL; + }; + if let Ok(name) = unsafe { CStr::from_ptr(name as _) }.to_str() { crate::fd::open(name, flags, mode) .map_or_else(|e| -num::ToPrimitive::to_i32(&e).unwrap(), |v| v) @@ -252,8 +263,10 @@ extern "C" fn __sys_read(fd: FileDescriptor, buf: *mut u8, len: usize) -> isize obj.map_or_else( |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { - (*v).read(slice) - .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + (*v).read(slice).map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| v.try_into().unwrap(), + ) }, ) } @@ -269,8 +282,10 @@ extern "C" fn __sys_write(fd: FileDescriptor, buf: *const u8, len: usize) -> isi obj.map_or_else( |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { - (*v).write(slice) - .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + (*v).write(slice).map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| v.try_into().unwrap(), + ) }, ) } @@ -320,15 +335,35 @@ 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 { +#[derive(Copy, Clone, Debug)] +#[repr(C)] +pub enum Dirent { + Error(i32), + Some(DirectoryEntry), + None, +} + +extern "C" fn __sys_readdir(fd: FileDescriptor) -> Dirent { let obj = get_object(fd); - obj.map_or(DirectoryEntry::Invalid(-crate::errno::EINVAL), |v| { - (*v).readdir() - }) + obj.map_or_else( + |_| Dirent::Error(crate::errno::EINVAL), + |v| { + (*v).readdir().map_or_else( + |e| Dirent::Error(num::ToPrimitive::to_i32(&e).unwrap()), + |v| { + if let Some(v) = v { + Dirent::Some(v) + } else { + Dirent::None + } + }, + ) + }, + ) } #[no_mangle] -pub extern "C" fn sys_readdir(fd: FileDescriptor) -> DirectoryEntry { +pub extern "C" fn sys_readdir(fd: FileDescriptor) -> Dirent { kernel_function!(__sys_readdir(fd)) } diff --git a/src/syscalls/net.rs b/src/syscalls/net.rs index 80d0cff503..dc33909d5d 100644 --- a/src/syscalls/net.rs +++ b/src/syscalls/net.rs @@ -565,8 +565,10 @@ extern "C" fn __sys_recv(fd: i32, buf: *mut u8, len: usize) -> isize { obj.map_or_else( |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { - (*v).read(slice) - .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + (*v).read(slice).map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| v.try_into().unwrap(), + ) }, ) } @@ -592,8 +594,10 @@ extern "C" fn __sys_sendto( obj.map_or_else( |e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| { - (*v).sendto(slice, endpoint) - .map_or_else(|e| -num::ToPrimitive::to_isize(&e).unwrap(), |v| v) + (*v).sendto(slice, endpoint).map_or_else( + |e| -num::ToPrimitive::to_isize(&e).unwrap(), + |v| v.try_into().unwrap(), + ) }, ) } @@ -639,7 +643,7 @@ extern "C" fn __sys_recvfrom( } } - len + len.try_into().unwrap() }, ) },