From a65283c5b11bd5c1cfcaaca755dc84cf1c1d52c3 Mon Sep 17 00:00:00 2001 From: Louis Vialar Date: Thu, 21 Aug 2025 14:04:09 +0200 Subject: [PATCH 1/6] sys_fcntl: return 0 on F_GETFD --- src/syscalls/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index 53c0490422..e1b905df0a 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -684,6 +684,7 @@ pub unsafe extern "C" fn sys_ioctl(fd: RawFd, cmd: i32, argp: *mut core::ffi::c_ #[hermit_macro::system(errno)] #[unsafe(no_mangle)] pub extern "C" fn sys_fcntl(fd: i32, cmd: i32, arg: i32) -> i32 { + const F_GETFD: i32 = 1; const F_SETFD: i32 = 2; const F_GETFL: i32 = 3; const F_SETFL: i32 = 4; @@ -691,6 +692,9 @@ pub extern "C" fn sys_fcntl(fd: i32, cmd: i32, arg: i32) -> i32 { if cmd == F_SETFD && arg == FD_CLOEXEC { 0 + } else if cmd == F_GETFD { + // Only the FD_CLOEXEC flag is defined, and it has no effect in hermit, so always return 0 + 0 } else if cmd == F_GETFL { let obj = get_object(fd); obj.map_or_else( From fd9c087a387a293376f875a44838d45bb66989b3 Mon Sep 17 00:00:00 2001 From: Louis Vialar Date: Mon, 22 Sep 2025 17:36:29 +0200 Subject: [PATCH 2/6] fs: sys_access, remove incorrect check --- src/syscalls/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index e1b905df0a..34decdb462 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -482,10 +482,6 @@ pub unsafe extern "C" fn sys_access(name: *const c_char, flags: i32) -> i32 { return -i32::from(Errno::Inval); }; - if access_option.contains(AccessOption::F_OK) && access_option != AccessOption::F_OK { - return -i32::from(Errno::Inval); - } - let Ok(name) = unsafe { CStr::from_ptr(name) }.to_str() else { return -i32::from(Errno::Inval); }; From fca0d2d0510a6154c85c1939b898bae1ee2308f7 Mon Sep 17 00:00:00 2001 From: Louis Vialar Date: Mon, 22 Sep 2025 17:36:41 +0200 Subject: [PATCH 3/6] fs: fcntl: return 0 for unknown operations --- Cargo.toml | 6 ++++++ src/syscalls/mod.rs | 3 +++ 2 files changed, 9 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 5cf5f8b083..4cc530d0d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,6 +79,12 @@ mman = [] ## [hermit-c]: https://github.com/hermit-os/hermit-c newlib = [] +## Enable stubbing result values for some system calls. +## +## For some system calls related to system options in which Hermit does not implement all possible options, +## return 0 (success) instead of an error when the option is unknown. +syscall-fake-success = [] + #! ### Platform Features ## Enables [Uhyve] support. diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index 34decdb462..98725cc32a 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -717,6 +717,9 @@ pub extern "C" fn sys_fcntl(fd: i32, cmd: i32, arg: i32) -> i32 { .map_or_else(|e| -i32::from(e), |()| 0) }, ) + } else if cfg!(feature = "syscall-fake-success") { + warn!("[syscall-fake-success] Unknown fcntl flag {cmd} {arg}, returning 0"); + 0 } else { -i32::from(Errno::Inval) } From cb1f6f959556e3ec8178378f8df9ac4fc3a5caa3 Mon Sep 17 00:00:00 2001 From: Louis Vialar Date: Tue, 23 Sep 2025 09:15:58 +0200 Subject: [PATCH 4/6] fs/mem: give fake blocksize/inode number to files --- Cargo.toml | 4 ++-- src/fs/mem.rs | 13 +++++++++++++ src/syscalls/mod.rs | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4cc530d0d2..5651c7ec5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,8 +82,8 @@ newlib = [] ## Enable stubbing result values for some system calls. ## ## For some system calls related to system options in which Hermit does not implement all possible options, -## return 0 (success) instead of an error when the option is unknown. -syscall-fake-success = [] +## return 0 (success) instead of an error when the option is unknown ; or return a convincing fake value. +syscall-fake-values = [] #! ### Platform Features diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 61002da066..25e30e92b7 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -8,6 +8,8 @@ use alloc::sync::Arc; use alloc::vec::Vec; use core::marker::PhantomData; use core::mem::MaybeUninit; +#[cfg(feature = "syscall-fake-values")] +use core::sync::atomic::{AtomicU64, Ordering}; use core::{mem, ptr}; use align_address::Align; @@ -36,6 +38,9 @@ impl RomFileInner { } } +#[cfg(feature = "syscall-fake-values")] +static VFS_INO_NUM: AtomicU64 = AtomicU64::new(10_000); + pub struct RomFileInterface { /// Position within the file pos: Mutex, @@ -306,6 +311,8 @@ impl RomFile { st_atim: t, st_mtim: t, st_ctim: t, + #[cfg(feature = "syscall-fake-values")] + st_ino: VFS_INO_NUM.fetch_add(1, Ordering::AcqRel), ..Default::default() }; @@ -361,6 +368,12 @@ impl RamFile { st_atim: t, st_mtim: t, st_ctim: t, + #[cfg(feature = "syscall-fake-values")] + st_nlink: 1, + #[cfg(feature = "syscall-fake-values")] + st_blksize: 4096, + #[cfg(feature = "syscall-fake-values")] + st_ino: VFS_INO_NUM.fetch_add(1, Ordering::AcqRel), ..Default::default() }; diff --git a/src/syscalls/mod.rs b/src/syscalls/mod.rs index 98725cc32a..58719fee06 100644 --- a/src/syscalls/mod.rs +++ b/src/syscalls/mod.rs @@ -717,8 +717,8 @@ pub extern "C" fn sys_fcntl(fd: i32, cmd: i32, arg: i32) -> i32 { .map_or_else(|e| -i32::from(e), |()| 0) }, ) - } else if cfg!(feature = "syscall-fake-success") { - warn!("[syscall-fake-success] Unknown fcntl flag {cmd} {arg}, returning 0"); + } else if cfg!(feature = "syscall-fake-values") { + warn!("[syscall-fake-values] Unknown fcntl flag {cmd} {arg}, returning 0"); 0 } else { -i32::from(Errno::Inval) From 95cbe7bc674d725b511be2f8d1b67f0c78762217 Mon Sep 17 00:00:00 2001 From: Louis Vialar Date: Tue, 23 Sep 2025 09:15:09 +0200 Subject: [PATCH 5/6] fs/mem: make lstat compliant with POSIX --- src/fs/mem.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/fs/mem.rs b/src/fs/mem.rs index 25e30e92b7..14715dacc6 100644 --- a/src/fs/mem.rs +++ b/src/fs/mem.rs @@ -627,15 +627,17 @@ impl VfsNode for MemDirectory { block_on( async { let (component, rest) = path.split_once("/").unwrap_or((path, "")); + let inner = self.inner.read().await; + let node = inner.get(component).ok_or(Errno::Badf)?; if !rest.is_empty() { - let inner = self.inner.read().await; - let directory = inner.get(component).ok_or(Errno::Badf)?; - return directory.traverse_lstat(rest); + if node.get_kind() == NodeKind::File { + Err(Errno::Notdir)?; + } + + return node.traverse_lstat(rest); } - let inner = self.inner.read().await; - let node = inner.get(component).ok_or(Errno::Badf)?; node.get_file_attributes() }, None, @@ -646,15 +648,17 @@ impl VfsNode for MemDirectory { block_on( async { let (component, rest) = path.split_once("/").unwrap_or((path, "")); + let inner = self.inner.read().await; + let node = inner.get(component).ok_or(Errno::Badf)?; if !rest.is_empty() { - let inner = self.inner.read().await; - let directory = inner.get(component).ok_or(Errno::Badf)?; - return directory.traverse_stat(rest); + if node.get_kind() == NodeKind::File { + Err(Errno::Notdir)?; + } + + return node.traverse_stat(rest); } - let inner = self.inner.read().await; - let node = inner.get(component).ok_or(Errno::Badf)?; node.get_file_attributes() }, None, From 555421a92838badca32f1e749ae07249fccafb43 Mon Sep 17 00:00:00 2001 From: Louis Vialar Date: Tue, 29 Apr 2025 15:15:35 +0200 Subject: [PATCH 6/6] network: change implementation of sockopt The goal is to permit non boolean options in the future, and also to add the keepalive option which is required by some other software --- src/executor/network.rs | 2 + src/fd/delegate.rs | 4 +- src/fd/mod.rs | 104 ++++++++++++++++++++++++++--- src/fd/socket/tcp.rs | 63 ++++++++++++++---- src/fd/socket/udp.rs | 8 +-- src/syscalls/socket/mod.rs | 130 ++++++++++++++++++------------------- 6 files changed, 216 insertions(+), 95 deletions(-) diff --git a/src/executor/network.rs b/src/executor/network.rs index d30f648bd3..d4436214dc 100644 --- a/src/executor/network.rs +++ b/src/executor/network.rs @@ -314,6 +314,8 @@ pub(crate) fn init() { spawn(network_run()); #[cfg(feature = "dhcpv4")] spawn(dhcpv4_run()); + } else { + warn!("Network initialization failed."); } } diff --git a/src/fd/delegate.rs b/src/fd/delegate.rs index daff895b03..2e53d360a6 100644 --- a/src/fd/delegate.rs +++ b/src/fd/delegate.rs @@ -17,7 +17,7 @@ use crate::fd::socket::vsock; use crate::fd::stdio::{ConsoleStderr, ConsoleStdin, ConsoleStdout}; #[cfg(feature = "uhyve")] use crate::fd::stdio::{UhyveStderr, UhyveStdin, UhyveStdout}; -use crate::fd::{AccessPermission, ObjectInterface, PollEvent, StatusFlags}; +use crate::fd::{AccessPermission, ObjectInterface, PollEvent, SocketOptionValue, StatusFlags}; #[cfg(any(feature = "net", feature = "virtio-vsock"))] use crate::fd::{Endpoint, ListenEndpoint, SocketOption}; use crate::fs::mem::{MemDirectoryInterface, RamFileInterface, RomFileInterface}; @@ -158,7 +158,7 @@ impl ObjectInterface for Fd { #[cfg(any(feature = "net", feature = "virtio-vsock"))] async fn listen(&mut self, _backlog: i32) -> io::Result<()>; #[cfg(any(feature = "net", feature = "virtio-vsock"))] - async fn setsockopt(&self, _opt: SocketOption, _optval: bool) -> io::Result<()>; + async fn setsockopt(&self, _opt: SocketOption, _optval: SocketOptionValue) -> io::Result<()>; #[cfg(any(feature = "net", feature = "virtio-vsock"))] async fn getsockopt(&self, _opt: SocketOption) -> io::Result; #[cfg(any(feature = "net", feature = "virtio-vsock"))] diff --git a/src/fd/mod.rs b/src/fd/mod.rs index 15e7e6791c..1445d8a30a 100644 --- a/src/fd/mod.rs +++ b/src/fd/mod.rs @@ -1,5 +1,4 @@ use alloc::sync::Arc; -#[cfg(any(feature = "net", feature = "virtio-vsock"))] use core::ffi::c_int; use core::future; use core::mem::MaybeUninit; @@ -7,8 +6,7 @@ use core::pin::pin; use core::task::Poll::{Pending, Ready}; use core::time::Duration; -#[cfg(any(feature = "net", feature = "virtio-vsock"))] -use num_enum::TryFromPrimitive; +use num_enum::{IntoPrimitive, TryFromPrimitive}; #[cfg(feature = "net")] use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; @@ -18,6 +16,8 @@ use crate::errno::Errno; use crate::executor::block_on; use crate::fs::{FileAttr, SeekWhence}; use crate::io; +#[cfg(feature = "net")] +use crate::syscalls::socket::{Ipproto, SOL_SOCKET, socklen_t}; mod delegate; mod eventfd; @@ -48,12 +48,100 @@ pub(crate) enum ListenEndpoint { Vsock(socket::vsock::VsockListenEndpoint), } -#[cfg(any(feature = "net", feature = "virtio-vsock"))] -#[derive(TryFromPrimitive, PartialEq, Eq, Clone, Copy, Debug)] -#[repr(i32)] +#[allow(dead_code)] +#[derive(Debug, PartialEq, Eq)] pub(crate) enum SocketOption { - TcpNodelay = 1, + TcpOption(SocketOptionTcp), + SocketOption(SocketOptionSocket), +} + +#[cfg(feature = "net")] +impl SocketOption { + pub fn from_level_optname(level: i32, optname: i32) -> Option { + if level == SOL_SOCKET { + SocketOptionSocket::try_from(optname) + .ok() + .map(SocketOption::SocketOption) + } else { + let protocol = u8::try_from(level) + .ok() + .and_then(|proto| Ipproto::try_from(proto).ok())?; + + match protocol { + Ipproto::Tcp => SocketOptionTcp::try_from(optname) + .ok() + .map(SocketOption::TcpOption), + _ => None, + } + } + } +} + +#[cfg(feature = "net")] +pub struct SocketOptionValue { + optval: *const core::ffi::c_void, + optlen: socklen_t, +} + +#[cfg(not(feature = "net"))] +pub struct SocketOptionValue; + +unsafe impl Send for SocketOptionValue {} + +#[cfg(feature = "net")] +impl SocketOptionValue { + pub fn new(optval: *const core::ffi::c_void, optlen: socklen_t) -> Self { + Self { optval, optlen } + } +} + +#[cfg(feature = "net")] +impl TryFrom<&SocketOptionValue> for i32 { + type Error = Errno; + + fn try_from(value: &SocketOptionValue) -> Result { + if value.optval.is_null() { + return Err(Errno::Inval); + } + + if value.optlen != size_of::() as u32 { + return Err(Errno::Inval); + } + + let value = unsafe { *value.optval.cast::() }; + Ok(value) + } +} + +#[cfg(feature = "net")] +impl TryFrom<&SocketOptionValue> for bool { + type Error = Errno; + + fn try_from(value: &SocketOptionValue) -> Result { + let value: i32 = value.try_into()?; + Ok(value != 0) + } +} + +#[derive(TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Clone, Copy, Debug)] +#[repr(i32)] +#[non_exhaustive] +pub(crate) enum SocketOptionTcp { + #[doc(alias = "TCP_NODELAY")] + TcpNoDelay = 1, +} + +#[derive(TryFromPrimitive, IntoPrimitive, PartialEq, Eq, Clone, Copy, Debug)] +#[repr(i32)] +#[non_exhaustive] +pub(crate) enum SocketOptionSocket { + #[doc(alias = "SO_REUSEADDR")] + ReuseAddr = 1, + #[doc(alias = "SO_KEEPALIVE")] + KeepAlive = 8, + #[doc(alias = "SO_SNDBUF")] SoSndbuf = 0x1001, + #[doc(alias = "SO_RCVBUF")] SoRcvbuf = 0x1002, } @@ -265,7 +353,7 @@ pub(crate) trait ObjectInterface: Sync + Send { /// `setsockopt` sets options on sockets #[cfg(any(feature = "net", feature = "virtio-vsock"))] - async fn setsockopt(&self, _opt: SocketOption, _optval: bool) -> io::Result<()> { + async fn setsockopt(&self, _opt: SocketOption, _optval: SocketOptionValue) -> io::Result<()> { Err(Errno::Notsock) } diff --git a/src/fd/socket/tcp.rs b/src/fd/socket/tcp.rs index 671ab75e19..5db3b5ec4f 100644 --- a/src/fd/socket/tcp.rs +++ b/src/fd/socket/tcp.rs @@ -13,7 +13,10 @@ use smoltcp::wire::{IpEndpoint, Ipv4Address, Ipv6Address}; use crate::errno::Errno; use crate::executor::block_on; use crate::executor::network::{Handle, NIC, wake_network_waker}; -use crate::fd::{self, Endpoint, Fd, ListenEndpoint, ObjectInterface, PollEvent, SocketOption}; +use crate::fd::{ + self, Endpoint, Fd, ListenEndpoint, ObjectInterface, PollEvent, SocketOption, + SocketOptionSocket, SocketOptionTcp, SocketOptionValue, +}; use crate::syscalls::socket::Af; use crate::{DEFAULT_KEEP_ALIVE_INTERVAL, io}; @@ -426,19 +429,38 @@ impl ObjectInterface for Socket { Ok(()) } - async fn setsockopt(&self, opt: SocketOption, optval: bool) -> io::Result<()> { - if opt == SocketOption::TcpNodelay { - let mut guard = NIC.lock(); - let nic = guard.as_nic_mut().unwrap(); + async fn setsockopt(&self, opt: SocketOption, optval: SocketOptionValue) -> io::Result<()> { + let mut guard = NIC.lock(); + let nic = guard.as_nic_mut().unwrap(); - for handle in self.handle.iter().copied() { - let socket = nic.get_mut_socket::>(handle); - socket.set_nagle_enabled(optval); + match opt { + SocketOption::TcpOption(SocketOptionTcp::TcpNoDelay) => { + let is_enabled = (&optval).try_into()?; + for handle in self.handle.iter().copied() { + let socket = nic.get_mut_socket::>(handle); + socket.set_nagle_enabled(is_enabled); + } + Ok(()) } + SocketOption::SocketOption(SocketOptionSocket::KeepAlive) => { + let keepalive: bool = (&optval).try_into()?; + let keepalive = if keepalive { + Some(Duration::from_secs(120)) + } else { + None + }; - Ok(()) - } else { - Err(Errno::Inval) + for i in self.handle.iter() { + let socket = nic.get_mut_socket::>(*i); + socket.set_keep_alive(keepalive); + } + + Ok(()) + } + other => { + warn!("TCP: unsupported option {other:?}"); + Err(Errno::Inval) + } } } @@ -448,9 +470,22 @@ impl ObjectInterface for Socket { let socket = nic.get_mut_socket::>(*self.handle.first().unwrap()); match opt { - SocketOption::TcpNodelay => Ok(socket.nagle_enabled().into()), - SocketOption::SoSndbuf => Ok(c_int::try_from(socket.send_capacity()).unwrap()), - SocketOption::SoRcvbuf => Ok(c_int::try_from(socket.recv_capacity()).unwrap()), + SocketOption::TcpOption(SocketOptionTcp::TcpNoDelay) => { + Ok(socket.nagle_enabled().into()) + } + SocketOption::SocketOption(SocketOptionSocket::KeepAlive) => { + Ok(socket.keep_alive().is_some().into()) + } + SocketOption::SocketOption(SocketOptionSocket::SoSndbuf) => { + Ok(c_int::try_from(socket.send_capacity()).unwrap()) + } + SocketOption::SocketOption(SocketOptionSocket::SoRcvbuf) => { + Ok(c_int::try_from(socket.recv_capacity()).unwrap()) + } + other => { + warn!("TCP: unsupported option {other:?}"); + Err(Errno::Inval) + } } } diff --git a/src/fd/socket/udp.rs b/src/fd/socket/udp.rs index bb1aa72b1c..1dadf57f96 100644 --- a/src/fd/socket/udp.rs +++ b/src/fd/socket/udp.rs @@ -10,7 +10,7 @@ use smoltcp::wire::{IpEndpoint, Ipv4Address, Ipv6Address}; use crate::errno::Errno; use crate::executor::block_on; use crate::executor::network::{Handle, NIC, wake_network_waker}; -use crate::fd::{self, Endpoint, ListenEndpoint, ObjectInterface, PollEvent, SocketOption}; +use crate::fd::{self, Endpoint, ListenEndpoint, ObjectInterface, PollEvent, SocketOption, SocketOptionSocket}; use crate::io; use crate::syscalls::socket::Af; @@ -252,9 +252,9 @@ impl ObjectInterface for Socket { .get_mut_socket::>(self.handle); match opt { - SocketOption::TcpNodelay => Err(Errno::Inval), - SocketOption::SoSndbuf => Ok(c_int::try_from(socket.payload_send_capacity()).unwrap()), - SocketOption::SoRcvbuf => Ok(c_int::try_from(socket.payload_recv_capacity()).unwrap()), + SocketOption::SocketOption(SocketOptionSocket::SoSndbuf) => Ok(c_int::try_from(socket.payload_send_capacity()).unwrap()), + SocketOption::SocketOption(SocketOptionSocket::SoRcvbuf) => Ok(c_int::try_from(socket.payload_recv_capacity()).unwrap()), + _ => Err(Errno::Inval) } } } diff --git a/src/syscalls/socket/mod.rs b/src/syscalls/socket/mod.rs index 6bfb169239..368e3d4b37 100644 --- a/src/syscalls/socket/mod.rs +++ b/src/syscalls/socket/mod.rs @@ -25,7 +25,8 @@ use crate::fd::socket::udp; #[cfg(feature = "virtio-vsock")] use crate::fd::socket::vsock::{self, VsockEndpoint, VsockListenEndpoint}; use crate::fd::{ - self, Endpoint, ListenEndpoint, ObjectInterface, SocketOption, get_object, insert_object, + self, Endpoint, ListenEndpoint, ObjectInterface, SocketOption, SocketOptionSocket, + SocketOptionValue, get_object, insert_object, }; use crate::syscalls::block_on; @@ -73,9 +74,12 @@ pub const SO_REUSEADDR: i32 = 0x0004; pub const SO_KEEPALIVE: i32 = 0x0008; pub const SO_BROADCAST: i32 = 0x0020; pub const SO_LINGER: i32 = 0x0080; +pub const SO_SNDBUF: i32 = 0x1001; +pub const SO_RCVBUF: i32 = 0x1002; pub const SO_SNDTIMEO: i32 = 0x1005; pub const SO_RCVTIMEO: i32 = 0x1006; pub const SO_ERROR: i32 = 0x1007; +pub const TCP_NODELAY: i32 = 1; pub const MSG_PEEK: i32 = 1; pub type sa_family_t = u8; pub type socklen_t = u32; @@ -622,7 +626,8 @@ pub extern "C" fn sys_socket(domain: i32, type_: i32, protocol: i32) -> i32 { } #[cfg(feature = "net")] - if domain == Af::Inet && matches!(sock, Sock::Stream | Sock::Dgram) { + if (domain == Af::Inet || domain == Af::Inet6) && (sock == Sock::Stream || sock == Sock::Dgram) + { let mut guard = NIC.lock(); let NetworkState::Initialized(nic) = &mut *guard else { @@ -920,43 +925,36 @@ pub unsafe extern "C" fn sys_setsockopt( optval: *const c_void, optlen: socklen_t, ) -> i32 { - if level == SOL_SOCKET && optname == SO_REUSEADDR { - return 0; - } - - let Ok(Ok(level)) = u8::try_from(level).map(Ipproto::try_from) else { - return -i32::from(Errno::Inval); - }; - - let Ok(optname) = SocketOption::try_from(optname) else { - return -i32::from(Errno::Inval); + let option = SocketOption::from_level_optname(level, optname); + let Some(option) = option else { + return if cfg!(feature = "syscall-fake-values") { + warn!("setsockopt: unsupported option level={level:x} optname={optname:x}, faking success."); + 0 + } else { + -i32::from(Errno::Inval) + }; }; - debug!("sys_setsockopt: {fd}, level {level:?}, optname {optname:?}"); - - if level == Ipproto::Tcp - && optname == SocketOption::TcpNodelay - && optlen == u32::try_from(size_of::()).unwrap() - { - if optval.is_null() { - return -i32::from(Errno::Inval); - } - - let value = unsafe { *optval.cast::() }; - let obj = get_object(fd); - obj.map_or_else( - |e| -i32::from(e), - |v| { - block_on( - async { v.read().await.setsockopt(optname, value != 0).await }, - None, - ) - .map_or_else(|e| -i32::from(e), |()| 0) - }, - ) - } else { - -i32::from(Errno::Inval) + if option == SocketOption::SocketOption(SocketOptionSocket::ReuseAddr) { + return 0; } + + let obj = get_object(fd); + obj.map_or_else( + |e| -i32::from(e), + |v| { + block_on( + async { + v.read() + .await + .setsockopt(option, SocketOptionValue::new(optval, optlen)) + .await + }, + None, + ) + .map_or_else(|e| -i32::from(e), |()| 0) + }, + ) } #[hermit_macro::system(errno)] @@ -968,40 +966,38 @@ pub unsafe extern "C" fn sys_getsockopt( optval: *mut c_void, optlen: *mut socklen_t, ) -> i32 { - let Ok(optname) = SocketOption::try_from(optname) else { - return -i32::from(Errno::Inval); + let option = SocketOption::from_level_optname(level, optname); + let optval = unsafe { &mut *optval.cast::() }; + let optlen = unsafe { &mut *optlen }; + + let Some(option) = option else { + return if cfg!(feature = "syscall-fake-values") { + warn!("getsockopt: unsupported option level={level:x} optname={optname:x}, faking success."); + *optlen = 0; + 0 + } else { + -i32::from(Errno::Inval) + }; }; - debug!("sys_getsockopt: {fd}, level {level}, optname {optname:?}"); - - if level == Ipproto::Tcp as i32 && optname == SocketOption::TcpNodelay - || level == SOL_SOCKET - && (optname == SocketOption::SoSndbuf || optname == SocketOption::SoRcvbuf) - { - if optval.is_null() || optlen.is_null() { - return -i32::from(Errno::Inval); - } - - let optval = unsafe { &mut *optval.cast::() }; - let optlen = unsafe { &mut *optlen }; - let obj = get_object(fd); - obj.map_or_else( - |e| -i32::from(e), - |v| { - block_on(async { v.read().await.getsockopt(optname).await }, None).map_or_else( - |e| -i32::from(e), - |value| { - *optval = value; - *optlen = size_of::().try_into().unwrap(); + debug!("sys_getsockopt: {fd}, level {level:?}, optname {optname}"); + let obj = get_object(fd); + let result = obj.map_or_else( + |e| -i32::from(e), + |v| { + block_on(async { v.read().await.getsockopt(option).await }, None).map_or_else( + |e| -i32::from(e), + |value| { + *optval = value; + *optlen = size_of::().try_into().unwrap(); + 0 + }, + ) + }, + ); - 0 - }, - ) - }, - ) - } else { - -i32::from(Errno::Inval) - } + *optlen = 0; + result } #[hermit_macro::system(errno)]