diff --git a/litebox_shim_linux/src/lib.rs b/litebox_shim_linux/src/lib.rs index 6a9d9977d..769a90ca7 100644 --- a/litebox_shim_linux/src/lib.rs +++ b/litebox_shim_linux/src/lib.rs @@ -22,7 +22,6 @@ use alloc::sync::Arc; use core::cell::{Cell, RefCell}; use litebox::{ LiteBox, - fd::TypedFd, mm::{PageManager, linux::PAGE_SIZE}, net::Network, pipes::Pipes, @@ -52,8 +51,6 @@ pub mod syscalls; pub mod transport; mod wait; -use crate::syscalls::file::get_file_descriptor_flags; - pub type DefaultFS = LinuxFS; pub(crate) type LinuxFS = @@ -436,8 +433,9 @@ impl Task { let files = self.files.borrow(); let alive_fds: Vec = files.raw_descriptor_store.read().iter_alive().collect(); for raw_fd in alive_fds { - if let Ok(flags) = get_file_descriptor_flags(raw_fd, &self.global, &files) - && flags.contains(litebox_common_linux::FileDescriptorFlags::FD_CLOEXEC) + if let Ok(fd) = files.typed_fd_from_raw(raw_fd) + && syscalls::file::get_file_descriptor_flags(&fd, &self.global) + .contains(litebox_common_linux::FileDescriptorFlags::FD_CLOEXEC) { let _ = self.do_close(raw_fd); } @@ -446,42 +444,31 @@ impl Task { } impl syscalls::file::FilesState { - #[expect(clippy::too_many_arguments)] - pub(crate) fn run_on_raw_fd( + /// Resolve a userland fd number, rejecting negative values with `EBADF`. + pub(crate) fn typed_fd(&self, fd: i32) -> Result, Errno> { + self.typed_fd_from_raw(usize::try_from(fd).map_err(|_| Errno::EBADF)?) + } + + pub(crate) fn typed_fd_from_raw( &self, fd: usize, - fs: impl FnOnce(&FileFd) -> R, - net: impl FnOnce(&TypedFd>) -> R, - pipes: impl FnOnce(&TypedFd>) -> R, - eventfd: impl FnOnce(&TypedFd>) -> R, - epoll: impl FnOnce(&TypedFd>) -> R, - unix: impl FnOnce(&TypedFd>) -> R, - ) -> Result { + ) -> Result, Errno> { let rds = self.raw_descriptor_store.read(); - if let Ok(fd) = rds.fd_from_raw_integer(fd) { - drop(rds); - return Ok(fs(&fd)); - } - if let Ok(fd) = rds.fd_from_raw_integer(fd) { - drop(rds); - return Ok(net(&fd)); - } - if let Ok(fd) = rds.fd_from_raw_integer(fd) { - drop(rds); - return Ok(pipes(&fd)); - } - if let Ok(fd) = rds.fd_from_raw_integer(fd) { - drop(rds); - return Ok(eventfd(&fd)); - } - if let Ok(fd) = rds.fd_from_raw_integer(fd) { - drop(rds); - return Ok(epoll(&fd)); - } - if let Ok(fd) = rds.fd_from_raw_integer(fd) { - drop(rds); - return Ok(unix(&fd)); + + macro_rules! resolve_fd { + ($subsystem:ty, $variant:ident) => { + if let Ok(fd) = rds.fd_from_raw_integer::<$subsystem>(fd) { + return Ok(syscalls::file::AnyTypedFd::$variant(fd)); + } + }; } + + resolve_fd!(LinuxFS, Fs); + resolve_fd!(Network, Network); + resolve_fd!(Pipes, Pipes); + resolve_fd!(syscalls::eventfd::EventfdSubsystem, Eventfd); + resolve_fd!(syscalls::epoll::EpollSubsystem, Epoll); + resolve_fd!(syscalls::unix::UnixSocketSubsystem, Unix); Err(Errno::EBADF) } } @@ -513,23 +500,33 @@ impl ToSyscallResult for Result { } impl Task { - /// A wrapper function around `sys_pread64` that copies data in chunks to avoid OOMing. + /// A wrapper function around `do_pread_with_user_buf` that copies data in chunks to avoid OOMing. fn pread_with_user_buf( &self, fd: i32, buf: UserPtrMut, count: usize, offset: i64, + ) -> Result { + self.with_typed_fd(fd, |fd| self.do_pread_with_user_buf(fd, buf, count, offset)) + } + + fn do_pread_with_user_buf( + &self, + fd: &syscalls::file::AnyTypedFd, + buf: UserPtrMut, + count: usize, + offset: i64, ) -> Result { let mut kernel_buf = vec![0u8; count.min(MAX_KERNEL_BUF_SIZE)]; let mut read_total = 0; while read_total < count { let to_read = (count - read_total).min(kernel_buf.len()); - match self.sys_pread64( - fd, - &mut kernel_buf[..to_read], - offset + (read_total.reinterpret_as_signed() as i64), - ) { + let read_offset = offset + .checked_add(read_total.reinterpret_as_signed() as i64) + .and_then(|offset| usize::try_from(offset).ok()) + .ok_or(Errno::EINVAL)?; + match self.do_read(fd, &mut kernel_buf[..to_read], Some(read_offset)) { Ok(0) => break, // EOF Ok(size) => { buf.copy_from_slice::(read_total, &kernel_buf[..size]) @@ -598,26 +595,36 @@ impl Task { } else { // If the read size is too large, we need to do some extra work to avoid OOMing. // We read data in chunks and update the file offset ourselves only if the read succeeds. - self.sys_lseek(fd, 0, litebox::fs::SeekWhence::RelativeToCurrentOffset) - .inspect_err(|e| { - match *e { - Errno::EBADF => (), // safe errors to return - Errno::ESPIPE => { - unimplemented!("read on non-seekable fds with large buffers"); - } - Errno::EINVAL => { - unreachable!("seekable file should not return EINVAL when getting current offset"); - } - _ => { - unimplemented!("unexpected error from lseek: {}", e); + self.with_typed_fd(fd, |fd| { + self.do_seek( + fd, + 0, + litebox::fs::SeekWhence::RelativeToCurrentOffset, + ) + .inspect_err(|e| { + match *e { + Errno::EBADF => (), // safe errors to return + Errno::ESPIPE => { + unimplemented!("read on non-seekable fds with large buffers"); + } + Errno::EINVAL => { + unreachable!("seekable file should not return EINVAL when getting current offset"); + } + _ => { + unimplemented!("unexpected error from lseek: {}", e); + } } - } - }) - .and_then(|cur_loc| { - self.pread_with_user_buf(fd, buf, count, i64::try_from(cur_loc).unwrap()) + }) + .and_then(|cur_loc| { + self.do_pread_with_user_buf( + fd, + buf, + count, + i64::try_from(cur_loc).unwrap(), + ) .inspect(|read_total| { // Update the file offset to reflect the read we just did. - self.sys_lseek( + self.do_seek( fd, (cur_loc + read_total).reinterpret_as_signed(), litebox::fs::SeekWhence::RelativeToBeginning, @@ -625,6 +632,7 @@ impl Task { // Given that previous lseek and pread succeeded, this lseek should also succeed. .expect("lseek failed"); }) + }) }) } } diff --git a/litebox_shim_linux/src/syscalls/file.rs b/litebox_shim_linux/src/syscalls/file.rs index b1a92bcd2..8bf5bafdb 100644 --- a/litebox_shim_linux/src/syscalls/file.rs +++ b/litebox_shim_linux/src/syscalls/file.rs @@ -167,6 +167,87 @@ impl FilesState { } } +/// A raw fd resolved once into the subsystem that owns it. +pub(crate) enum AnyTypedFd { + Fs(alloc::sync::Arc>), + Network(alloc::sync::Arc>>), + Pipes(alloc::sync::Arc>>), + Eventfd(alloc::sync::Arc>>), + Epoll(alloc::sync::Arc>>), + Unix(alloc::sync::Arc>>), +} + +/// Apply one subsystem-generic expression to whichever subsystem an [`AnyTypedFd`] holds. +/// +/// Needed because a closure cannot be generic over the subsystem type. +macro_rules! on_any_fd { + ($any:expr, |$fd:ident| $body:expr) => { + match $any { + AnyTypedFd::Fs($fd) => $body, + AnyTypedFd::Network($fd) => $body, + AnyTypedFd::Pipes($fd) => $body, + AnyTypedFd::Eventfd($fd) => $body, + AnyTypedFd::Epoll($fd) => $body, + AnyTypedFd::Unix($fd) => $body, + } + }; +} + +impl AnyTypedFd { + /// The subsystem backing this fd, for diagnostics. + pub(crate) fn subsystem_name(&self) -> &'static str { + match self { + Self::Fs(_) => "fs", + Self::Network(_) => "net", + Self::Pipes(_) => "pipes", + Self::Eventfd(_) => "eventfd", + Self::Epoll(_) => "epoll", + Self::Unix(_) => "unix", + } + } + + /// The filesystem fd behind this descriptor, or `None` for every other subsystem. + pub(crate) fn as_fs(&self) -> Option<&FileFd> { + match self { + Self::Fs(fd) => Some(fd), + _ => None, + } + } + + /// Like [`Self::as_fs`], but fails with `otherwise` for non-filesystem descriptors. + pub(crate) fn fs_only(&self, otherwise: Errno) -> Result<&FileFd, Errno> { + self.as_fs().ok_or(otherwise) + } + + /// Run the handler matching this fd's subsystem. + pub(crate) fn dispatch( + &self, + fs: impl FnOnce(&FileFd) -> R, + net: impl FnOnce(&TypedFd>) -> R, + pipes: impl FnOnce(&TypedFd>) -> R, + eventfd: impl FnOnce(&TypedFd>) -> R, + epoll: impl FnOnce(&TypedFd>) -> R, + unix: impl FnOnce(&TypedFd>) -> R, + ) -> R { + match self { + Self::Fs(fd) => fs(fd), + Self::Network(fd) => net(fd), + Self::Pipes(fd) => pipes(fd), + Self::Eventfd(fd) => eventfd(fd), + Self::Epoll(fd) => epoll(fd), + Self::Unix(fd) => unix(fd), + } + } +} + +impl core::fmt::Debug for AnyTypedFd { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_tuple("AnyTypedFd") + .field(&self.subsystem_name()) + .finish() + } +} + /// Path in the file system #[derive(Debug)] enum FsPath { @@ -176,9 +257,9 @@ enum FsPath { Cwd, /// Path is relative to a file descriptor #[expect(dead_code, reason = "currently unused, might want to use later")] - FdRelative { fd: u32, path: CString }, + FdRelative { fd: i32, path: CString }, /// Fd - Fd(u32), + Fd(i32), } /// Maximum size of a file path @@ -201,7 +282,6 @@ impl FsPath { let cpath = path.to_c_str()?.into_owned(); FsPath::Absolute { path: cpath } } else if dirfd >= 0 { - let dirfd = u32::try_from(dirfd).expect("dirfd >= 0"); if path_str.is_empty() { FsPath::Fd(dirfd) } else { @@ -353,21 +433,16 @@ impl Task { /// Handle syscall `ftruncate` pub(crate) fn sys_ftruncate(&self, fd: i32, length: usize) -> Result<(), Errno> { - let Ok(raw_fd) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; let files = self.files.borrow(); - files - .run_on_raw_fd( - raw_fd, - |fd| files.fs.truncate(fd, length, false).map_err(Errno::from), - |_fd| todo!("net"), - |_fd| todo!("pipes"), - |_fd| Err(Errno::EINVAL), - |_fd| Err(Errno::EINVAL), - |_fd| Err(Errno::EINVAL), - ) - .flatten() + let fd = files.typed_fd(fd)?; + fd.dispatch( + |fd| files.fs.truncate(fd, length, false).map_err(Errno::from), + |_fd| todo!("net"), + |_fd| todo!("pipes"), + |_fd| Err(Errno::EINVAL), + |_fd| Err(Errno::EINVAL), + |_fd| Err(Errno::EINVAL), + ) } /// Handle syscall `mknodat` — create a filesystem node. @@ -437,14 +512,13 @@ impl Task { /// `offset` is an optional offset to read from. If `None`, it will read from the current file position. /// If `Some`, it will read from the specified offset without changing the current file position. pub fn sys_read(&self, fd: i32, buf: &mut [u8], offset: Option) -> Result { - let Ok(raw_fd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; - self.do_read(raw_fd, buf, offset) + let fd = self.typed_fd(fd)?; + self.do_read(&fd, buf, offset) } + pub(crate) fn do_read( &self, - fd: u32, + fd: &AnyTypedFd, buf: &mut [u8], offset: Option, ) -> Result { @@ -452,72 +526,68 @@ impl Task { // We need to do this cell dance because otherwise Rust can't recognize that the two // closures are mutually exclusive. let buf: core::cell::RefCell<&mut [u8]> = core::cell::RefCell::new(buf); - let n = files - .run_on_raw_fd( - fd as usize, - |fd| { - files - .fs - .read(fd, &mut buf.borrow_mut(), offset) - .map_err(Errno::from) - }, - |fd| { - espipe_for_non_seekable_offset(offset)?; - self.global.receive( + let result = fd.dispatch( + |fd| { + files + .fs + .read(fd, &mut buf.borrow_mut(), offset) + .map_err(Errno::from) + }, + |fd| { + espipe_for_non_seekable_offset(offset)?; + self.global.receive( + &self.wait_cx(), + fd, + &mut buf.borrow_mut(), + litebox_common_linux::ReceiveFlags::empty(), + None, + ) + }, + |fd| { + espipe_for_non_seekable_offset(offset)?; + self.global + .read_linux_pipe(&self.wait_cx(), fd, &mut buf.borrow_mut()) + }, + |fd| { + espipe_for_non_seekable_offset(offset)?; + let handle = self + .global + .litebox + .descriptor_table() + .entry_handle(fd) + .ok_or(Errno::EBADF)?; + handle.with_entry(|file| { + let buf = &mut buf.borrow_mut(); + if buf.len() < size_of::() { + return Err(Errno::EINVAL); + } + let value = file.read(&self.wait_cx())?; + buf[..size_of::()].copy_from_slice(&value.to_le_bytes()); + Ok(size_of::()) + }) + }, + |_fd| Err(Errno::EINVAL), + |fd| { + espipe_for_non_seekable_offset(offset)?; + let handle = self + .global + .litebox + .descriptor_table() + .entry_handle(fd) + .ok_or(Errno::EBADF)?; + handle.with_entry(|file| { + file.recvfrom( &self.wait_cx(), - fd, &mut buf.borrow_mut(), litebox_common_linux::ReceiveFlags::empty(), None, ) - }, - |fd| { - espipe_for_non_seekable_offset(offset)?; - self.global - .read_linux_pipe(&self.wait_cx(), fd, &mut buf.borrow_mut()) - }, - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - espipe_for_non_seekable_offset(offset)?; - handle.with_entry(|file| { - let buf = &mut buf.borrow_mut(); - if buf.len() < size_of::() { - return Err(Errno::EINVAL); - } - let value = file.read(&self.wait_cx())?; - buf[..size_of::()].copy_from_slice(&value.to_le_bytes()); - Ok(size_of::()) - }) - }, - |_fd| Err(Errno::EINVAL), - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - espipe_for_non_seekable_offset(offset)?; - handle.with_entry(|file| { - file.recvfrom( - &self.wait_cx(), - &mut buf.borrow_mut(), - litebox_common_linux::ReceiveFlags::empty(), - None, - ) - }) - }, - ) - .flatten()?; + }) + }, + ); // For datagrams, the returned size represents the actual size of the message, // which may be larger than the buffer size. - let capped_size = n.min(buf.borrow().len()); - Ok(capped_size) + result.map(|size| size.min(buf.borrow().len())) } /// Handle syscall `write` @@ -525,73 +595,71 @@ impl Task { /// `offset` is an optional offset to write to. If `None`, it will write to the current file position. /// If `Some`, it will write to the specified offset without changing the current file position. pub fn sys_write(&self, fd: i32, buf: &[u8], offset: Option) -> Result { - let Ok(raw_fd) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; + let fd = self.typed_fd(fd)?; + self.do_write(&fd, buf, offset) + } + + fn do_write( + &self, + fd: &AnyTypedFd, + buf: &[u8], + offset: Option, + ) -> Result { let files = self.files.borrow(); - let res = files - .run_on_raw_fd( - raw_fd, - |fd| files.fs.write(fd, buf, offset).map_err(Errno::from), - |fd| { - espipe_for_non_seekable_offset(offset)?; - self.global.sendto( - &self.wait_cx(), - fd, - buf, - litebox_common_linux::SendFlags::empty(), - None, - ) - }, - |fd| { - espipe_for_non_seekable_offset(offset)?; - self.global.write_linux_pipe(&self.wait_cx(), fd, buf) - }, - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - espipe_for_non_seekable_offset(offset)?; - handle.with_entry(|file| { - if buf.len() < size_of::() { - return Err(Errno::EINVAL); - } - let value: u64 = u64::from_le_bytes( - buf[..size_of::()] - .try_into() - .map_err(|_| Errno::EINVAL)?, - ); - file.write(&self.wait_cx(), value) - }) - }, - |_fd| Err(Errno::EINVAL), - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - espipe_for_non_seekable_offset(offset)?; - handle.with_entry(|file| { - file.sendto(self, buf, litebox_common_linux::SendFlags::empty(), None) - }) - }, - ) - .flatten(); - if let Err(Errno::EPIPE) = res { + let result = fd.dispatch( + |fd| files.fs.write(fd, buf, offset).map_err(Errno::from), + |fd| { + espipe_for_non_seekable_offset(offset)?; + self.global.sendto( + &self.wait_cx(), + fd, + buf, + litebox_common_linux::SendFlags::empty(), + None, + ) + }, + |fd| { + espipe_for_non_seekable_offset(offset)?; + self.global.write_linux_pipe(&self.wait_cx(), fd, buf) + }, + |fd| { + espipe_for_non_seekable_offset(offset)?; + let handle = self + .global + .litebox + .descriptor_table() + .entry_handle(fd) + .ok_or(Errno::EBADF)?; + handle.with_entry(|file| { + if buf.len() < size_of::() { + return Err(Errno::EINVAL); + } + let value = u64::from_le_bytes( + buf[..size_of::()] + .try_into() + .map_err(|_| Errno::EINVAL)?, + ); + file.write(&self.wait_cx(), value) + }) + }, + |_fd| Err(Errno::EINVAL), + |fd| { + espipe_for_non_seekable_offset(offset)?; + let handle = self + .global + .litebox + .descriptor_table() + .entry_handle(fd) + .ok_or(Errno::EBADF)?; + handle.with_entry(|file| { + file.sendto(self, buf, litebox_common_linux::SendFlags::empty(), None) + }) + }, + ); + if let Err(Errno::EPIPE) = result { self.send_signal(Signal::SIGPIPE, signal::siginfo_kill(Signal::SIGPIPE)); } - res - } - - /// Handle syscall `pread64` - pub fn sys_pread64(&self, fd: i32, buf: &mut [u8], offset: i64) -> Result { - let pos = usize::try_from(offset).map_err(|_| Errno::EINVAL)?; - self.sys_read(fd, buf, Some(pos)) + result } /// Handle syscall `pwrite64` @@ -600,30 +668,23 @@ impl Task { self.sys_write(fd, buf, Some(pos)) } - fn rewind_sendfile_in_fd(&self, in_raw_fd: usize, unread_n: usize) -> Result<(), Errno> { + fn rewind_sendfile_in_fd( + &self, + in_fd: &AnyTypedFd, + unread_n: usize, + ) -> Result<(), Errno> { if unread_n == 0 { return Ok(()); } let rewind = isize::try_from(unread_n).map_err(|_| Errno::EOVERFLOW)?; + let fd = in_fd.fs_only(Errno::EINVAL)?; let files = self.files.borrow(); files - .run_on_raw_fd( - in_raw_fd, - |fd| { - files - .fs - .seek(fd, -rewind, SeekWhence::RelativeToCurrentOffset) - .map(|_| ()) - .map_err(Errno::from) - }, - |_fd| Err(Errno::EINVAL), - |_fd| Err(Errno::EINVAL), - |_fd| Err(Errno::EINVAL), - |_fd| Err(Errno::EINVAL), - |_fd| Err(Errno::EINVAL), - ) - .flatten() + .fs + .seek(fd, -rewind, SeekWhence::RelativeToCurrentOffset) + .map(|_| ()) + .map_err(Errno::from) } /// Handle syscall `sendfile` @@ -634,12 +695,10 @@ impl Task { offset_ptr: Option>, count: usize, ) -> Result { - let Ok(in_raw_fd) = u32::try_from(in_fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; - // TODO: Linux rejects `sendfile` with `EINVAL` when `out_fd` has `O_APPEND` set. - self.check_raw_fd_exists(out_fd)?; + let typed_in_fd = self.typed_fd(in_fd)?; + let typed_out_fd = self.typed_fd(out_fd)?; + // TODO: Linux rejects `sendfile` with `EINVAL` when `out_fd` has `O_APPEND` set. let mut cur_off = offset_ptr .map(|p| { let off = p.read_at_offset::(0).ok_or(Errno::EFAULT)?; @@ -652,6 +711,7 @@ impl Task { let mut kernel_buf = vec![0u8; count.min(PAGE_SIZE)]; let mut total: usize = 0; + let files = self.files.borrow(); while total < count { let to_read = (count - total).min(kernel_buf.len()); @@ -663,20 +723,12 @@ impl Task { } else { Errno::EINVAL }; - let read_result = { - let buf_slice = &mut kernel_buf[..to_read]; - let files = self.files.borrow(); - files - .run_on_raw_fd( - in_raw_fd, - |fd| files.fs.read(fd, buf_slice, cur_off).map_err(Errno::from), - |_fd| Err(non_fs_err), - |_fd| Err(non_fs_err), - |_fd| Err(non_fs_err), - |_fd| Err(non_fs_err), - |_fd| Err(non_fs_err), - ) - .flatten() + let read_result = match typed_in_fd.as_fs() { + Some(fd) => files + .fs + .read(fd, &mut kernel_buf[..to_read], cur_off) + .map_err(Errno::from), + None => Err(non_fs_err), }; let read_n = match read_result { Ok(0) => break, @@ -685,12 +737,12 @@ impl Task { Err(_) => break, }; - let write_result = self.sys_write(out_fd, &kernel_buf[..read_n], None); + let write_result = self.do_write(&typed_out_fd, &kernel_buf[..read_n], None); let write_n = match write_result { Ok(n) => n, Err(e) => { if offset_ptr.is_none() { - self.rewind_sendfile_in_fd(in_raw_fd, read_n)?; + self.rewind_sendfile_in_fd(&typed_in_fd, read_n)?; } if total == 0 { return Err(e); @@ -701,11 +753,11 @@ impl Task { total += write_n; if let Some(ref mut off) = cur_off { - *off += write_n; + *off = off.checked_add(write_n).ok_or(Errno::EOVERFLOW)?; } if write_n < read_n { if offset_ptr.is_none() { - self.rewind_sendfile_in_fd(in_raw_fd, read_n - write_n)?; + self.rewind_sendfile_in_fd(&typed_in_fd, read_n - write_n)?; } break; } @@ -744,44 +796,40 @@ pub(crate) fn try_into_whence(value: i16) -> Result { impl Task { /// Handle syscall `lseek` pub fn sys_lseek(&self, fd: i32, offset: isize, whence: SeekWhence) -> Result { - let Ok(raw_fd) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; + let fd = self.typed_fd(fd)?; + self.do_seek(&fd, offset, whence) + } + + pub(crate) fn do_seek( + &self, + fd: &AnyTypedFd, + offset: isize, + whence: SeekWhence, + ) -> Result { + let fd = fd.fs_only(Errno::ESPIPE)?; let files = self.files.borrow(); - files - .run_on_raw_fd( - raw_fd, - |fd| match files.fs.seek(fd, offset, whence) { - Ok(pos) => Ok(pos), - Err(litebox::fs::errors::SeekError::NotAFile) => { - let base: usize = match whence { - SeekWhence::RelativeToBeginning => 0, - SeekWhence::RelativeToCurrentOffset => self - .global - .litebox - .descriptor_table() - .with_metadata(fd, |off: &Diroff| off.0) - .unwrap_or(0), - SeekWhence::RelativeToEnd => { - return Err(Errno::EINVAL); - } - }; - let new_pos = base.checked_add_signed(offset).ok_or(Errno::EINVAL)?; - self.global - .litebox - .descriptor_table_mut() - .set_entry_metadata(fd, Diroff(new_pos)); - Ok(new_pos) - } - Err(e) => Err(Errno::from(e)), - }, - |_| Err(Errno::ESPIPE), - |_| Err(Errno::ESPIPE), - |_| Err(Errno::ESPIPE), - |_| Err(Errno::ESPIPE), - |_| Err(Errno::ESPIPE), - ) - .flatten() + match files.fs.seek(fd, offset, whence) { + Ok(pos) => Ok(pos), + Err(litebox::fs::errors::SeekError::NotAFile) => { + let base = match whence { + SeekWhence::RelativeToBeginning => 0, + SeekWhence::RelativeToCurrentOffset => self + .global + .litebox + .descriptor_table() + .with_metadata(fd, |off: &Diroff| off.0) + .unwrap_or(0), + SeekWhence::RelativeToEnd => return Err(Errno::EINVAL), + }; + let new_pos = base.checked_add_signed(offset).ok_or(Errno::EINVAL)?; + self.global + .litebox + .descriptor_table_mut() + .set_entry_metadata(fd, Diroff(new_pos)); + Ok(new_pos) + } + Err(error) => Err(Errno::from(error)), + } } fn do_mkdir(&self, pathname: impl path::Arg, mode: Mode) -> Result<(), Errno> { @@ -827,21 +875,12 @@ impl Task { raw_fd: usize, replace: Option>, ) -> Result<(), Errno> { - enum ConsumedFd { - Fs(alloc::sync::Arc>), - Network(alloc::sync::Arc>>), - Pipes(alloc::sync::Arc>>), - Eventfd(alloc::sync::Arc>>), - Epoll(alloc::sync::Arc>>), - Unix(alloc::sync::Arc>>), - } - let files = self.files.borrow(); let mut rds = files.raw_descriptor_store.write(); - let consumed: ConsumedFd = match rds + let consumed: AnyTypedFd = match rds .fd_consume_raw_integer::>(raw_fd) { - Ok(fd) => ConsumedFd::Fs(fd), + Ok(fd) => AnyTypedFd::Fs(fd), Err(litebox::fd::ErrRawIntFd::NotFound) => { if let Some(new_fd) = replace { let success = rds.fd_into_specific_raw_integer(new_fd, raw_fd); @@ -853,23 +892,23 @@ impl Task { if let Ok(fd) = rds.fd_consume_raw_integer::>(raw_fd) { - ConsumedFd::Network(fd) + AnyTypedFd::Network(fd) } else if let Ok(fd) = rds.fd_consume_raw_integer::>(raw_fd) { - ConsumedFd::Pipes(fd) + AnyTypedFd::Pipes(fd) } else if let Ok(fd) = rds.fd_consume_raw_integer::>(raw_fd) { - ConsumedFd::Eventfd(fd) + AnyTypedFd::Eventfd(fd) } else if let Ok(fd) = rds.fd_consume_raw_integer::>(raw_fd) { - ConsumedFd::Epoll(fd) + AnyTypedFd::Epoll(fd) } else if let Ok(fd) = rds.fd_consume_raw_integer::>(raw_fd) { - ConsumedFd::Unix(fd) + AnyTypedFd::Unix(fd) } else { unreachable!("all subsystems covered") } @@ -887,23 +926,23 @@ impl Task { drop(rds); match consumed { - ConsumedFd::Fs(fd) => { + AnyTypedFd::Fs(fd) => { if let Ok(raw_fd) = i32::try_from(raw_fd) { self.finalize_elf_patch(raw_fd); } files.fs.close(&fd).map_err(Errno::from) } - ConsumedFd::Network(fd) => self.global.close_socket(&self.wait_cx(), fd), - ConsumedFd::Pipes(fd) => self.global.close_linux_pipe(&fd), - ConsumedFd::Eventfd(fd) => { + AnyTypedFd::Network(fd) => self.global.close_socket(&self.wait_cx(), fd), + AnyTypedFd::Pipes(fd) => self.global.close_linux_pipe(&fd), + AnyTypedFd::Eventfd(fd) => { self.remove_and_drop_descriptor(&fd); Ok(()) } - ConsumedFd::Epoll(fd) => { + AnyTypedFd::Epoll(fd) => { self.remove_and_drop_descriptor(&fd); Ok(()) } - ConsumedFd::Unix(fd) => { + AnyTypedFd::Unix(fd) => { self.remove_and_drop_descriptor(&fd); Ok(()) } @@ -912,12 +951,23 @@ impl Task { /// Handle syscall `close` pub(crate) fn sys_close(&self, fd: i32) -> Result<(), Errno> { - let Ok(raw_fd) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; + let raw_fd = usize::try_from(fd).map_err(|_| Errno::EBADF)?; self.do_close(raw_fd) } + /// Resolve a userland fd number into the subsystem that owns it. + pub(crate) fn typed_fd(&self, fd: i32) -> Result, Errno> { + self.files.borrow().typed_fd(fd) + } + + pub(crate) fn with_typed_fd( + &self, + fd: i32, + f: impl FnOnce(&AnyTypedFd) -> Result, + ) -> Result { + f(&self.typed_fd(fd)?) + } + /// Handle syscall `preadv` pub(crate) fn sys_preadv( &self, @@ -927,15 +977,16 @@ impl Task { offset: i64, ) -> Result { let base_offset = usize::try_from(offset).map_err(|_| Errno::EINVAL)?; - self.check_raw_fd_exists(fd)?; - check_iovcnt(iovcnt)?; - let iovs: &[IoReadVec] = &iovec - .to_owned_slice::(iovcnt) - .ok_or(Errno::EFAULT)?; - let mut kernel_buffer = vec![0u8; PAGE_SIZE]; - read_from_iovec::<_, Platform>(iovs, &mut kernel_buffer, |buf, total| { - let cur_offset = base_offset.checked_add(total).ok_or(Errno::EOVERFLOW)?; - self.sys_read(fd, buf, Some(cur_offset)) + self.with_typed_fd(fd, |fd| { + check_iovcnt(iovcnt)?; + let iovs: &[IoReadVec] = &iovec + .to_owned_slice::(iovcnt) + .ok_or(Errno::EFAULT)?; + let mut kernel_buffer = vec![0u8; PAGE_SIZE]; + read_from_iovec::<_, Platform>(iovs, &mut kernel_buffer, |buf, total| { + let cur_offset = base_offset.checked_add(total).ok_or(Errno::EOVERFLOW)?; + self.do_read(fd, buf, Some(cur_offset)) + }) }) } @@ -948,15 +999,16 @@ impl Task { offset: i64, ) -> Result { let base_offset = usize::try_from(offset).map_err(|_| Errno::EINVAL)?; - self.check_raw_fd_exists(fd)?; - check_iovcnt(iovcnt)?; - let iovs: &[IoWriteVec] = &iovec - .to_owned_slice::(iovcnt) - .ok_or(Errno::EFAULT)?; - // TODO: Linux ignores pwritev's offset for O_APPEND files; see the O_APPEND bug documented in pwrite(2). - write_to_iovec::<_, Platform>(iovs, |buf, total| { - let cur_offset = base_offset.checked_add(total).ok_or(Errno::EOVERFLOW)?; - self.sys_write(fd, buf, Some(cur_offset)) + self.with_typed_fd(fd, |fd| { + check_iovcnt(iovcnt)?; + let iovs: &[IoWriteVec] = &iovec + .to_owned_slice::(iovcnt) + .ok_or(Errno::EFAULT)?; + // TODO: Linux ignores pwritev's offset for O_APPEND files; see the O_APPEND bug documented in pwrite(2). + write_to_iovec::<_, Platform>(iovs, |buf, total| { + let cur_offset = base_offset.checked_add(total).ok_or(Errno::EOVERFLOW)?; + self.do_write(fd, buf, Some(cur_offset)) + }) }) } @@ -967,38 +1019,22 @@ impl Task { iovec: UserPtr, iovcnt: usize, ) -> Result { - self.check_raw_fd_exists(fd)?; - check_iovcnt(iovcnt)?; - let iovs: &[IoReadVec] = &iovec - .to_owned_slice::(iovcnt) - .ok_or(Errno::EFAULT)?; - let mut kernel_buffer = vec![0u8; PAGE_SIZE]; - // TODO: The data transfers performed by readv() and writev() are atomic: the data - // written by writev() is written as a single block that is not intermingled with - // output from writes in other processes - read_from_iovec::<_, Platform>(iovs, &mut kernel_buffer, |buf, _total| { - self.sys_read(fd, buf, None) + self.with_typed_fd(fd, |fd| { + check_iovcnt(iovcnt)?; + let iovs: &[IoReadVec] = &iovec + .to_owned_slice::(iovcnt) + .ok_or(Errno::EFAULT)?; + let mut kernel_buffer = vec![0u8; PAGE_SIZE]; + // TODO: The data transfers performed by readv() and writev() are atomic: the data + // written by writev() is written as a single block that is not intermingled with + // output from writes in other processes + read_from_iovec::<_, Platform>(iovs, &mut kernel_buffer, |buf, _total| { + self.do_read(fd, buf, None) + }) }) } } -impl Task { - fn check_raw_fd_exists(&self, fd: i32) -> Result<(), Errno> { - let raw_fd = usize::try_from(fd).map_err(|_| Errno::EBADF)?; - if self - .files - .borrow() - .raw_descriptor_store - .read() - .is_alive(raw_fd) - { - Ok(()) - } else { - Err(Errno::EBADF) - } - } -} - /// Linux's `IOV_MAX` / `UIO_MAXIOV`: the kernel rejects iovec counts above this /// with `EINVAL` for `readv`/`writev`/`preadv`/`pwritev`. const IOV_MAX: usize = 1024; @@ -1128,15 +1164,16 @@ impl Task { iovec: UserPtr, iovcnt: usize, ) -> Result { - self.check_raw_fd_exists(fd)?; - check_iovcnt(iovcnt)?; - let iovs: &[IoWriteVec] = &iovec - .to_owned_slice::(iovcnt) - .ok_or(Errno::EFAULT)?; - // TODO: The data transfers performed by readv() and writev() are atomic: the data - // written by writev() is written as a single block that is not intermingled with - // output from writes in other processes - write_to_iovec::<_, Platform>(iovs, |buf, _total| self.sys_write(fd, buf, None)) + self.with_typed_fd(fd, |fd| { + check_iovcnt(iovcnt)?; + let iovs: &[IoWriteVec] = &iovec + .to_owned_slice::(iovcnt) + .ok_or(Errno::EFAULT)?; + // TODO: The data transfers performed by readv() and writev() are atomic: the data + // written by writev() is written as a single block that is not intermingled with + // output from writes in other processes + write_to_iovec::<_, Platform>(iovs, |buf, _total| self.do_write(fd, buf, None)) + }) } fn validate_access_mode(mode: &AccessFlags) -> Result<(), Errno> { @@ -1242,7 +1279,7 @@ impl Task { self.do_access(cwd, mode, caller) } FsPath::Fd(fd) if flags.contains(AtFlags::AT_EMPTY_PATH) => { - let stat: FileStat = descriptor_stat(fd as usize, self)?; + let stat: FileStat = self.with_typed_fd(fd, |fd| self.do_stat(fd))?; let owner = AccessUserInfo { user: stat.st_uid, group: stat.st_gid, @@ -1304,34 +1341,56 @@ impl Task { } } -fn descriptor_stat( - raw_fd: usize, - task: &Task, -) -> Result -where - T: From + From, -{ - // TODO: give correct values for the synthesized branches. - let synthetic = |mode_bits: u32, blksize: usize| FileStat { - st_dev: 0, - st_ino: 0, - st_nlink: 1, - st_mode: mode_bits.trunc(), - st_uid: 0, - st_gid: 0, - st_rdev: 0, - st_size: 0, - st_blksize: blksize, - st_blocks: 0, - ..Default::default() - }; - let socket_mode = litebox_common_linux::InodeType::Socket as u32 - | (Mode::RWXU | Mode::RWXG | Mode::RWXO).bits(); - let rw_user_mode = (Mode::RUSR | Mode::WUSR).bits(); - let files = task.files.borrow(); - files - .run_on_raw_fd( - raw_fd, +pub(crate) fn get_file_descriptor_flags( + fd: &AnyTypedFd, + global: &GlobalState, +) -> FileDescriptorFlags { + // Currently, only one such flag is defined: FD_CLOEXEC, the close-on-exec flag. + // See https://www.man7.org/linux/man-pages/man2/F_GETFD.2const.html + on_any_fd!(fd, |fd| global + .litebox + .descriptor_table() + .with_metadata(fd, |flags: &FileDescriptorFlags| *flags) + .unwrap_or(FileDescriptorFlags::empty())) +} + +fn set_file_descriptor_flags( + fd: &AnyTypedFd, + global: &GlobalState, + flags: FileDescriptorFlags, +) { + on_any_fd!(fd, |fd| { + let _old = global + .litebox + .descriptor_table_mut() + .set_fd_metadata(fd, flags); + }); +} + +impl Task { + pub(crate) fn do_stat(&self, fd: &AnyTypedFd) -> Result + where + T: From + From, + { + // TODO: give correct values for the synthesized branches. + let synthetic = |mode_bits: u32, blksize: usize| FileStat { + st_dev: 0, + st_ino: 0, + st_nlink: 1, + st_mode: mode_bits.trunc(), + st_uid: 0, + st_gid: 0, + st_rdev: 0, + st_size: 0, + st_blksize: blksize, + st_blocks: 0, + ..Default::default() + }; + let socket_mode = litebox_common_linux::InodeType::Socket as u32 + | (Mode::RWXU | Mode::RWXG | Mode::RWXO).bits(); + let rw_user_mode = (Mode::RUSR | Mode::WUSR).bits(); + let files = self.files.borrow(); + fd.dispatch( |fd| { files .fs @@ -1342,7 +1401,7 @@ where |_fd| Ok(T::from(synthetic(socket_mode, 4096))), |fd| { Ok(T::from(synthetic( - task.global.linux_pipe_mode_bits(fd)?, + self.global.linux_pipe_mode_bits(fd)?, 4096, ))) }, @@ -1350,71 +1409,12 @@ where |_fd| Ok(T::from(synthetic(rw_user_mode, 0))), |_fd| Ok(T::from(synthetic(socket_mode, 4096))), ) - .flatten() -} - -pub(crate) fn get_file_descriptor_flags( - raw_fd: usize, - global: &GlobalState, - files: &FilesState, -) -> Result { - // Currently, only one such flag is defined: FD_CLOEXEC, the close-on-exec flag. - // See https://www.man7.org/linux/man-pages/man2/F_GETFD.2const.html - fn get_flags( - global: &GlobalState, - fd: &TypedFd, - ) -> FileDescriptorFlags { - global - .litebox - .descriptor_table() - .with_metadata(fd, |flags: &FileDescriptorFlags| *flags) - .unwrap_or(FileDescriptorFlags::empty()) - } - files.run_on_raw_fd( - raw_fd, - |fd| get_flags(global, fd), - |fd| get_flags(global, fd), - |fd| get_flags(global, fd), - |fd| get_flags(global, fd), - |fd| get_flags(global, fd), - |fd| get_flags(global, fd), - ) -} - -fn set_file_descriptor_flags( - raw_fd: usize, - global: &GlobalState, - files: &FilesState, - flags: FileDescriptorFlags, -) -> Result<(), Errno> { - fn set_flags( - global: &GlobalState, - fd: &TypedFd, - flags: FileDescriptorFlags, - ) { - let _old = global - .litebox - .descriptor_table_mut() - .set_fd_metadata(fd, flags); } - files.run_on_raw_fd( - raw_fd, - |fd| set_flags(global, fd, flags), - |fd| set_flags(global, fd, flags), - |fd| set_flags(global, fd, flags), - |fd| set_flags(global, fd, flags), - |fd| set_flags(global, fd, flags), - |fd| set_flags(global, fd, flags), - )?; - Ok(()) -} - -impl Task { /// Get the file status of `pathname`. /// /// The `pathname` must be absolute. - fn do_stat>( + fn do_path_stat>( &self, pathname: impl path::Arg, follow_symlink: bool, @@ -1438,7 +1438,7 @@ impl Task { /// Handle syscall `stat` pub fn sys_stat(&self, pathname: impl path::Arg) -> Result { let pathname = self.resolve_path(pathname)?; - self.do_stat(pathname, true) + self.do_path_stat(pathname, true) } /// Handle syscall `lstat` @@ -1448,15 +1448,12 @@ impl Task { /// TODO: we do not support symbolic links yet. pub fn sys_lstat(&self, pathname: impl path::Arg) -> Result { let pathname = self.resolve_path(pathname)?; - self.do_stat(pathname, false) + self.do_path_stat(pathname, false) } /// Handle syscall `fstat` pub fn sys_fstat(&self, fd: i32) -> Result { - let Ok(raw_fd) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; - descriptor_stat(raw_fd, self) + self.with_typed_fd(fd, |fd| self.do_stat(fd)) } fn do_fstatat( @@ -1472,7 +1469,7 @@ impl Task { let fs_path = FsPath::new(dirfd, pathname, get_cwd)?; match fs_path { FsPath::Absolute { path } => { - self.do_stat(path, !flags.contains(AtFlags::AT_SYMLINK_NOFOLLOW)) + self.do_path_stat(path, !flags.contains(AtFlags::AT_SYMLINK_NOFOLLOW)) } FsPath::Cwd if flags.contains(AtFlags::AT_EMPTY_PATH) => { // Take the cwd before locking the context: this lock is not recursive, so a @@ -1484,7 +1481,7 @@ impl Task { Ok(T::from(files.fs.file_status(&context, cwd)?)) } FsPath::Fd(fd) if flags.contains(AtFlags::AT_EMPTY_PATH) => { - descriptor_stat(fd as usize, self) + self.with_typed_fd(fd, |fd| self.do_stat(fd)) } FsPath::Cwd | FsPath::Fd(_) => Err(Errno::ENOENT), FsPath::FdRelative { .. } => { @@ -1545,15 +1542,13 @@ impl Task { } pub(crate) fn sys_fcntl(&self, fd: i32, arg: FcntlArg) -> Result { - let Ok(desc) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; - let files = self.files.borrow(); + let fd = files.typed_fd(fd)?; match arg { - FcntlArg::GETFD => Ok(get_file_descriptor_flags(desc, &self.global, &files)?.bits()), + FcntlArg::GETFD => Ok(get_file_descriptor_flags(&fd, &self.global).bits()), FcntlArg::SETFD(flags) => { - set_file_descriptor_flags(desc, &self.global, &files, flags).map(|()| 0) + set_file_descriptor_flags(&fd, &self.global, flags); + Ok(0) } FcntlArg::GETFL => { macro_rules! getfl_from_metadata { @@ -1580,17 +1575,15 @@ impl Task { handle.with_entry(|file| Ok(file.get_status())) }}; } - Ok(files - .run_on_raw_fd( - desc, + Ok(fd + .dispatch( |fd| getfl_from_metadata!(fd, crate::StdioStatusFlags), |fd| getfl_from_metadata!(fd, crate::syscalls::net::SocketOFlags), |fd| self.global.linux_pipe_status_flags(fd), |fd| getfl_from_handle!(fd), |fd| getfl_from_handle!(fd), |fd| getfl_from_handle!(fd), - ) - .flatten()? + )? .bits()) } FcntlArg::SETFL(flags) => { @@ -1642,8 +1635,7 @@ impl Task { }) }; } - files.run_on_raw_fd( - desc, + fd.dispatch( |fd| { setfl_in_metadata!( fd, @@ -1671,64 +1663,55 @@ impl Task { toggle_flags!(fd); Ok(()) }, - )??; + )?; Ok(0) } FcntlArg::GETLK(lock) => { - self.files - .borrow() - .run_on_raw_fd( - desc, - |_fd| { - let mut flock = - lock.read_at_offset::(0).ok_or(Errno::EFAULT)?; - let lock_type = litebox_common_linux::FlockType::try_from(flock.type_) - .map_err(|_| Errno::EINVAL)?; - if let litebox_common_linux::FlockType::Unlock = lock_type { - return Err(Errno::EINVAL); - } + fd.dispatch( + |_fd| { + let mut flock = lock.read_at_offset::(0).ok_or(Errno::EFAULT)?; + let lock_type = litebox_common_linux::FlockType::try_from(flock.type_) + .map_err(|_| Errno::EINVAL)?; + if let litebox_common_linux::FlockType::Unlock = lock_type { + return Err(Errno::EINVAL); + } - // Note LiteBox does not support multiple processes yet, and one process - // can always acquire the lock it owns, so return `Unlock` unconditionally. - flock.type_ = litebox_common_linux::FlockType::Unlock as i16; - lock.write_at_offset::(0, flock) - .ok_or(Errno::EFAULT)?; - Ok(0) - }, - |_fd| todo!("net"), - |_fd| todo!("pipes"), - |_fd| Err(Errno::EBADF), - |_fd| Err(Errno::EBADF), - |_fd| Err(Errno::EBADF), - ) - .flatten() + // Note LiteBox does not support multiple processes yet, and one process + // can always acquire the lock it owns, so return `Unlock` unconditionally. + flock.type_ = litebox_common_linux::FlockType::Unlock as i16; + lock.write_at_offset::(0, flock) + .ok_or(Errno::EFAULT)?; + Ok(0) + }, + |_fd| todo!("net"), + |_fd| todo!("pipes"), + |_fd| Err(Errno::EBADF), + |_fd| Err(Errno::EBADF), + |_fd| Err(Errno::EBADF), + ) } FcntlArg::SETLK(lock) | FcntlArg::SETLKW(lock) => { - self.files - .borrow() - .run_on_raw_fd( - desc, - |_fd| { - let flock = lock.read_at_offset::(0).ok_or(Errno::EFAULT)?; - let _ = litebox_common_linux::FlockType::try_from(flock.type_) - .map_err(|_| Errno::EINVAL)?; - - // Note LiteBox does not support multiple processes yet, and one process - // can always acquire the lock it owns, so we don't need to maintain anything. - Ok(0) - }, - |_fd| todo!("net"), - |_fd| todo!("pipes"), - |_fd| Err(Errno::EBADF), - |_fd| Err(Errno::EBADF), - |_fd| Err(Errno::EBADF), - ) - .flatten() + fd.dispatch( + |_fd| { + let flock = lock.read_at_offset::(0).ok_or(Errno::EFAULT)?; + let _ = litebox_common_linux::FlockType::try_from(flock.type_) + .map_err(|_| Errno::EINVAL)?; + + // Note LiteBox does not support multiple processes yet, and one process + // can always acquire the lock it owns, so we don't need to maintain anything. + Ok(0) + }, + |_fd| todo!("net"), + |_fd| todo!("pipes"), + |_fd| Err(Errno::EBADF), + |_fd| Err(Errno::EBADF), + |_fd| Err(Errno::EBADF), + ) } FcntlArg::DUPFD { cloexec, min_fd } => { let new_file = self .do_dup_inner( - desc, + &fd, if cloexec { OFlags::CLOEXEC } else { @@ -1915,163 +1898,112 @@ impl Task { /// Handle syscall `ioctl` pub fn sys_ioctl(&self, fd: i32, arg: IoctlArg) -> Result { - let Ok(desc) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; - let files = self.files.borrow(); match arg { IoctlArg::FIONBIO(arg) => { let val = arg.read_at_offset::(0).ok_or(Errno::EFAULT)?; - self.files - .borrow() - .run_on_raw_fd( - desc, - |_file_fd| { - // TODO: stdio NONBLOCK? - #[cfg(debug_assertions)] - litebox_util_log::debug!("set non-blocking on raw fd unimplemented"); - Ok(()) - }, - |socket_fd| { - if let Err(e) = self - .global - .litebox - .descriptor_table_mut() - .with_metadata_mut( - socket_fd, - |crate::syscalls::net::SocketOFlags(flags)| { - flags.set(OFlags::NONBLOCK, val != 0); - }, - ) - { - match e { - MetadataError::ClosedFd => return Err(Errno::EBADF), - MetadataError::NoSuchMetadata => unreachable!(), - } + let fd = files.typed_fd(fd)?; + macro_rules! set_nonblock_on_entry { + ($fd:ident) => {{ + let handle = self + .global + .litebox + .descriptor_table() + .entry_handle($fd) + .ok_or(Errno::EBADF)?; + handle.with_entry(|file| { + file.set_status(OFlags::NONBLOCK, val != 0); + }); + Ok(()) + }}; + } + fd.dispatch( + |_file_fd| { + // TODO: stdio NONBLOCK? + #[cfg(debug_assertions)] + litebox_util_log::debug!("set non-blocking on raw fd unimplemented"); + Ok(()) + }, + |socket_fd| { + if let Err(e) = self + .global + .litebox + .descriptor_table_mut() + .with_metadata_mut( + socket_fd, + |crate::syscalls::net::SocketOFlags(flags)| { + flags.set(OFlags::NONBLOCK, val != 0); + }, + ) + { + match e { + MetadataError::ClosedFd => return Err(Errno::EBADF), + MetadataError::NoSuchMetadata => unreachable!(), } - Ok(()) - }, - |fd| { - self.global - .pipes - .update_flags(fd, litebox::pipes::Flags::NON_BLOCKING, val != 0) - .map_err(Errno::from) - }, - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - handle.with_entry(|file| { - file.set_status(OFlags::NONBLOCK, val != 0); - }); - Ok(()) - }, - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - handle.with_entry(|file| { - file.set_status(OFlags::NONBLOCK, val != 0); - }); - Ok(()) - }, - |fd| { - let handle = self - .global - .litebox - .descriptor_table() - .entry_handle(fd) - .ok_or(Errno::EBADF)?; - handle.with_entry(|file| { - file.set_status(OFlags::NONBLOCK, val != 0); - }); - Ok(()) - }, - ) - .flatten()?; + } + Ok(()) + }, + |fd| { + self.global + .pipes + .update_flags(fd, litebox::pipes::Flags::NON_BLOCKING, val != 0) + .map_err(Errno::from) + }, + |fd| set_nonblock_on_entry!(fd), + |fd| set_nonblock_on_entry!(fd), + |fd| set_nonblock_on_entry!(fd), + )?; Ok(0) } - IoctlArg::FIOCLEX => files.run_on_raw_fd( - desc, - |fd| { - let _old = self - .global - .litebox - .descriptor_table_mut() - .set_fd_metadata(fd, FileDescriptorFlags::FD_CLOEXEC); - Ok(0) - }, - |_fd| todo!("net"), - |_fd| todo!("pipes"), - |fd| { - let _old = self - .global - .litebox - .descriptor_table_mut() - .set_fd_metadata(fd, FileDescriptorFlags::FD_CLOEXEC); - Ok(0) - }, - |fd| { - let _old = self - .global - .litebox - .descriptor_table_mut() - .set_fd_metadata(fd, FileDescriptorFlags::FD_CLOEXEC); - Ok(0) - }, - |fd| { - let _old = self - .global - .litebox - .descriptor_table_mut() - .set_fd_metadata(fd, FileDescriptorFlags::FD_CLOEXEC); - Ok(0) - }, - )?, + IoctlArg::FIOCLEX => { + let fd = files.typed_fd(fd)?; + macro_rules! set_cloexec { + ($fd:ident) => {{ + let _old = self + .global + .litebox + .descriptor_table_mut() + .set_fd_metadata($fd, FileDescriptorFlags::FD_CLOEXEC); + Ok(0) + }}; + } + fd.dispatch( + |fd| set_cloexec!(fd), + |_fd| todo!("net"), + |_fd| todo!("pipes"), + |fd| set_cloexec!(fd), + |fd| set_cloexec!(fd), + |fd| set_cloexec!(fd), + ) + } IoctlArg::TCGETS(..) | IoctlArg::TCSETS(..) | IoctlArg::TIOCGPTN(..) - | IoctlArg::TIOCGWINSZ(..) => files.run_on_raw_fd( - desc, - |fd| { - if self.is_stdio(&files.fs, fd)? { - let stream = self - .global - .litebox - .descriptor_table() - .with_metadata(fd, |stream: &StdioStream| *stream) - .map_err(|_| { - // TODO: Handle missing `StdioStream` metadata (could happen if - // `/dev/stdin`, `/dev/stdout`, or `/dev/stderr` was reopened). - // XXX(jayb): likely we might want to have some backend-specific - // metadata layer in our file system? - litebox_util_log::error!( - "standard stream is missing StdioStream metadata" - ); - Errno::ENOTTY - })?; - if self.global.platform.is_a_tty(stream) { - self.stdio_ioctl(&arg) - } else { - Err(Errno::ENOTTY) - } - } else { - Err(Errno::ENOTTY) - } - }, - |_fd| Err(Errno::ENOTTY), - |_fd| Err(Errno::ENOTTY), - |_fd| Err(Errno::ENOTTY), - |_fd| Err(Errno::ENOTTY), - |_fd| Err(Errno::ENOTTY), - )?, + | IoctlArg::TIOCGWINSZ(..) => { + let fd = files.typed_fd(fd)?; + let fd = fd.fs_only(Errno::ENOTTY)?; + if !self.is_stdio(&files.fs, fd)? { + return Err(Errno::ENOTTY); + } + let stream = self + .global + .litebox + .descriptor_table() + .with_metadata(fd, |stream: &StdioStream| *stream) + .map_err(|_| { + // TODO: Handle missing `StdioStream` metadata (could happen if + // `/dev/stdin`, `/dev/stdout`, or `/dev/stderr` was reopened). + // XXX(jayb): likely we might want to have some backend-specific + // metadata layer in our file system? + litebox_util_log::error!("standard stream is missing StdioStream metadata"); + Errno::ENOTTY + })?; + if self.global.platform.is_a_tty(stream) { + self.stdio_ioctl(&arg) + } else { + Err(Errno::ENOTTY) + } + } _ => { log_unsupported!("ioctl with arg {:?}", arg); Err(Errno::EINVAL) @@ -2435,13 +2367,13 @@ impl Task { Ok(count) } - fn do_dup(&self, file: usize, flags: OFlags) -> Result { + fn do_dup(&self, file: &AnyTypedFd, flags: OFlags) -> Result { self.do_dup_inner(file, flags, DupFdRequest::LowestAvailable) } fn do_dup_inner( &self, - file: usize, + file: &AnyTypedFd, flags: OFlags, target: DupFdRequest, ) -> Result { @@ -2503,43 +2435,40 @@ impl Task { let close_on_exec = flags.contains(OFlags::CLOEXEC); let files = self.files.borrow(); - files - .run_on_raw_fd( - file, - |fd| { - dup(self, &files, fd, close_on_exec, target, |fd| { - let _ = files.fs.close(&fd); - }) - }, - |fd| { - dup(self, &files, fd, close_on_exec, target, |fd| { - let _ = self - .global - .close_socket(&self.wait_cx(), alloc::sync::Arc::new(fd)); - }) - }, - |fd| { - dup(self, &files, fd, close_on_exec, target, |fd| { - let _ = self.global.close_linux_pipe(&fd); - }) - }, - |fd| { - dup(self, &files, fd, close_on_exec, target, |fd| { - self.remove_and_drop_descriptor(&fd); - }) - }, - |fd| { - dup(self, &files, fd, close_on_exec, target, |fd| { - self.remove_and_drop_descriptor(&fd); - }) - }, - |fd| { - dup(self, &files, fd, close_on_exec, target, |fd| { - self.remove_and_drop_descriptor(&fd); - }) - }, - ) - .map_err(|_| DupFdError::BadFd)? + file.dispatch( + |fd| { + dup(self, &files, fd, close_on_exec, target, |fd| { + let _ = files.fs.close(&fd); + }) + }, + |fd| { + dup(self, &files, fd, close_on_exec, target, |fd| { + let _ = self + .global + .close_socket(&self.wait_cx(), alloc::sync::Arc::new(fd)); + }) + }, + |fd| { + dup(self, &files, fd, close_on_exec, target, |fd| { + let _ = self.global.close_linux_pipe(&fd); + }) + }, + |fd| { + dup(self, &files, fd, close_on_exec, target, |fd| { + self.remove_and_drop_descriptor(&fd); + }) + }, + |fd| { + dup(self, &files, fd, close_on_exec, target, |fd| { + self.remove_and_drop_descriptor(&fd); + }) + }, + |fd| { + dup(self, &files, fd, close_on_exec, target, |fd| { + self.remove_and_drop_descriptor(&fd); + }) + }, + ) } /// Handle syscall `dup/dup2/dup3` @@ -2553,9 +2482,8 @@ impl Task { newfd: Option, flags: Option, ) -> Result { - self.check_raw_fd_exists(oldfd)?; + let typed_oldfd = self.typed_fd(oldfd)?; let oldfd = u32::try_from(oldfd).map_err(|_| Errno::EBADF)?; - let oldfd_usize = usize::try_from(oldfd).or(Err(Errno::EBADF))?; if let Some(newfd) = newfd { // dup2/dup3 let Ok(newfd) = u32::try_from(newfd) else { @@ -2574,13 +2502,13 @@ impl Task { } let newfd_usize = usize::try_from(newfd).or(Err(Errno::EBADF))?; self.do_dup_inner( - oldfd_usize, + &typed_oldfd, flags.unwrap_or(OFlags::empty()), DupFdRequest::Exact(newfd_usize), ) } else { // dup - self.do_dup(oldfd_usize, flags.unwrap_or(OFlags::empty())) + self.do_dup(&typed_oldfd, flags.unwrap_or(OFlags::empty())) } .map_err(|e| match e { DupFdError::BadFd | DupFdError::TargetFdExceedsLimit => Errno::EBADF, @@ -2622,78 +2550,67 @@ impl Task { dirp: UserPtrMut, count: usize, ) -> Result { - let Ok(fd) = u32::try_from(fd).and_then(usize::try_from) else { - return Err(Errno::EBADF); - }; let files = self.files.borrow(); - files.run_on_raw_fd( - fd, - |file| { - let dir_off: Diroff = self - .global - .litebox - .descriptor_table() - .with_metadata(file, |off: &Diroff| *off) - .unwrap_or_default(); - let mut dir_off = dir_off.0; - let mut nbytes = 0; - - let mut entries = files.fs.read_dir(file)?; - entries.sort_by(|a, b| a.name.cmp(&b.name)); - - for entry in entries.iter().skip(dir_off) { - // include null terminator and make it aligned - let len = (DIRENT_STRUCT_BYTES_WITHOUT_NAME + entry.name.len() + 1) - .next_multiple_of(align_of::()); - if nbytes + len > count { - // not enough space - if nbytes == 0 { - // not enough space for even a single entry - return Err(Errno::EINVAL); - } - break; - } - let dirent64 = litebox_common_linux::LinuxDirent64 { - ino: entry.ino_info.as_ref().map_or(0, |node_info| node_info.ino) as u64, - off: dir_off as u64, - len: len.trunc(), - typ: litebox_common_linux::DirentType::from(entry.file_type.clone()) as u8, - __name: [0; 0], - }; - let hdr_ptr = UserPtrMut::from_usize(dirp.as_usize() + nbytes); - hdr_ptr - .write_at_offset::(0, dirent64) - .ok_or(Errno::EFAULT)?; - let name_ptr = UserPtrMut::from_usize( - hdr_ptr.as_usize() + DIRENT_STRUCT_BYTES_WITHOUT_NAME, - ); - name_ptr - .write_slice_at_offset::(0, entry.name.as_bytes()) - .ok_or(Errno::EFAULT)?; - // set the null terminator and padding - let zeros_len = len - (DIRENT_STRUCT_BYTES_WITHOUT_NAME + entry.name.len()); - name_ptr - .write_slice_at_offset::( - isize::try_from(entry.name.len()).unwrap(), - &vec![0; zeros_len], - ) - .ok_or(Errno::EFAULT)?; - nbytes += len; - dir_off += 1; + let fd = files.typed_fd(fd)?; + let file = fd.fs_only(Errno::ENOTDIR)?; + + let dir_off: Diroff = self + .global + .litebox + .descriptor_table() + .with_metadata(file, |off: &Diroff| *off) + .unwrap_or_default(); + let mut dir_off = dir_off.0; + let mut nbytes = 0; + + let mut entries = files.fs.read_dir(file)?; + entries.sort_by(|a, b| a.name.cmp(&b.name)); + + for entry in entries.iter().skip(dir_off) { + // include null terminator and make it aligned + let len = (DIRENT_STRUCT_BYTES_WITHOUT_NAME + entry.name.len() + 1) + .next_multiple_of(align_of::()); + if nbytes + len > count { + // not enough space + if nbytes == 0 { + // not enough space for even a single entry + return Err(Errno::EINVAL); } - let _old = self - .global - .litebox - .descriptor_table_mut() - .set_entry_metadata(file, Diroff(dir_off)); - Ok(nbytes) - }, - |_fd| Err(Errno::ENOTDIR), - |_fd| Err(Errno::ENOTDIR), - |_fd| Err(Errno::ENOTDIR), - |_fd| Err(Errno::ENOTDIR), - |_fd| Err(Errno::ENOTDIR), - )? + break; + } + let dirent64 = litebox_common_linux::LinuxDirent64 { + ino: entry.ino_info.as_ref().map_or(0, |node_info| node_info.ino) as u64, + off: dir_off as u64, + len: len.trunc(), + typ: litebox_common_linux::DirentType::from(entry.file_type.clone()) as u8, + __name: [0; 0], + }; + let hdr_ptr = UserPtrMut::from_usize(dirp.as_usize() + nbytes); + hdr_ptr + .write_at_offset::(0, dirent64) + .ok_or(Errno::EFAULT)?; + let name_ptr = + UserPtrMut::from_usize(hdr_ptr.as_usize() + DIRENT_STRUCT_BYTES_WITHOUT_NAME); + name_ptr + .write_slice_at_offset::(0, entry.name.as_bytes()) + .ok_or(Errno::EFAULT)?; + // set the null terminator and padding + let zeros_len = len - (DIRENT_STRUCT_BYTES_WITHOUT_NAME + entry.name.len()); + name_ptr + .write_slice_at_offset::( + isize::try_from(entry.name.len()).unwrap(), + &vec![0; zeros_len], + ) + .ok_or(Errno::EFAULT)?; + nbytes += len; + dir_off += 1; + } + let _old = self + .global + .litebox + .descriptor_table_mut() + .set_entry_metadata(file, Diroff(dir_off)); + Ok(nbytes) } } @@ -2702,7 +2619,7 @@ mod tests { use super::*; use alloc::string::String; use core::cell::Cell; - use litebox::fs::Mode; + use litebox::fs::{Mode, OFlags}; extern crate std; diff --git a/litebox_shim_linux/src/syscalls/mm.rs b/litebox_shim_linux/src/syscalls/mm.rs index 79c255c60..7f2696c76 100644 --- a/litebox_shim_linux/src/syscalls/mm.rs +++ b/litebox_shim_linux/src/syscalls/mm.rs @@ -17,6 +17,7 @@ use litebox_common_linux::{MRemapFlags, MapFlags, ProtFlags, errno::Errno}; use crate::ShimPlatform; use crate::Task; use crate::UserPtrMut; +use crate::syscalls::file::AnyTypedFd; use litebox::utils::TruncateExt as _; use object::elf::{ET_DYN, FileHeader64, PT_LOAD, ProgramHeader64}; use object::endian::LittleEndian; @@ -117,14 +118,15 @@ impl Task { offset: usize, ) -> Result, MappingError> { let is_exec = prot.contains(ProtFlags::PROT_EXEC); + let typed_fd = self.typed_fd(fd).map_err(|_| MappingError::BadFD(fd))?; // Perform the normal mmap first (CoW or memcpy fallback). let result = if let Some(cow_result) = - self.try_cow_mmap_file(suggested_addr, len, &prot, &flags, fd, offset) + self.try_cow_mmap_file(suggested_addr, len, &prot, &flags, &typed_fd, offset) { cow_result? } else { - self.do_mmap_file_memcpy(suggested_addr, len, prot, flags, fd, offset)? + self.do_mmap_file_memcpy(suggested_addr, len, prot, flags, &typed_fd, offset)? }; // Runtime syscall rewriting: patch PROT_EXEC segments in-place. @@ -170,31 +172,15 @@ impl Task { len: usize, prot: &ProtFlags, flags: &MapFlags, - fd: i32, + fd: &AnyTypedFd, offset: usize, ) -> Option, MappingError>> { if !len.is_multiple_of(PAGE_SIZE) { return None; } - let Ok(fd) = u32::try_from(fd).and_then(usize::try_from) else { - return None; - }; - let files = self.files.borrow(); - let raw_fd = fd; - - let static_data = files - .run_on_raw_fd( - raw_fd, - |typed_fd| files.fs.get_static_backing_data(typed_fd), - |_| None, - |_| None, - |_| None, - |_| None, - |_| None, - ) - .ok()??; + let static_data = files.fs.get_static_backing_data(fd.as_fs()?)?; if offset > static_data.len() { return None; @@ -273,7 +259,7 @@ impl Task { len: usize, prot: ProtFlags, flags: MapFlags, - fd: i32, + fd: &AnyTypedFd, offset: usize, ) -> Result, MappingError> { let op = |ptr: UserPtrMut| -> Result { @@ -286,9 +272,11 @@ impl Task { let mut copied = 0; while copied < len { let size = - self.sys_read(fd, &mut buffer, Some(file_offset)) + self.do_read(fd, &mut buffer, Some(file_offset)) .map_err(|e| match e { - Errno::EBADF => MappingError::BadFD(fd), + // The raw fd was resolved once at syscall entry and is intentionally + // not retained; this payload is discarded when converted to EBADF. + Errno::EBADF => MappingError::BadFD(-1), Errno::EISDIR => MappingError::NotAFile, Errno::EACCES => MappingError::NotForReading, _ => unimplemented!(), diff --git a/litebox_shim_linux/src/syscalls/net.rs b/litebox_shim_linux/src/syscalls/net.rs index 7a0de63bc..33cad6935 100644 --- a/litebox_shim_linux/src/syscalls/net.rs +++ b/litebox_shim_linux/src/syscalls/net.rs @@ -33,7 +33,10 @@ use litebox_common_linux::{ }; use zerocopy::{FromBytes, Immutable, IntoBytes}; -use crate::syscalls::unix::{CSockUnixAddr, UnixSocket, UnixSocketAddr}; +use crate::syscalls::{ + file::AnyTypedFd, + unix::{CSockUnixAddr, UnixSocket, UnixSocketAddr}, +}; use crate::{GlobalState, ShimPlatform, Task}; use crate::{UserPtr, UserPtrMut, syscalls::signal}; @@ -58,45 +61,33 @@ macro_rules! convert_flags { pub(crate) type SocketFd = litebox::net::SocketFd; impl super::file::FilesState { - /// Helper to dispatch socket operations based on socket type (INET vs Unix). - /// - /// This method handles the common pattern of: - /// 1. Looking up the file descriptor - /// 2. Matching on descriptor type - /// 3. Dropping the file table lock before potentially-blocking operations - /// 4. Dispatching to the appropriate handler - /// - /// For `LiteBoxRawFd` sockets, the `inet_op` closure is called with the socket fd. - /// For Unix sockets, the `unix_op` closure is called with a cloned Arc to the socket. - fn with_socket( + fn socket_from_raw(&self, sockfd: i32) -> Result, Errno> { + let fd = self.typed_fd(sockfd)?; + match fd { + AnyTypedFd::Network(_) | AnyTypedFd::Unix(_) => Ok(fd), + _ => Err(Errno::ENOTSOCK), + } + } + + fn with_typed_socket( &self, global: &GlobalState, - sockfd: u32, + socket: &AnyTypedFd, inet_op: impl FnOnce(&SocketFd) -> Result, unix_op: impl FnOnce(&UnixSocket) -> Result, ) -> Result { - let raw_fd = sockfd as usize; - let inet_fd = { - let rds = self.raw_descriptor_store.read(); - rds.fd_from_raw_integer(raw_fd).ok() - }; - if let Some(fd) = inet_fd { - return inet_op(&fd); + match socket { + AnyTypedFd::Network(fd) => inet_op(fd), + AnyTypedFd::Unix(fd) => { + let handle = global + .litebox + .descriptor_table() + .entry_handle(fd) + .ok_or(Errno::EBADF)?; + handle.with_entry(unix_op) + } + _ => Err(Errno::ENOTSOCK), } - let unix = self - .raw_descriptor_store - .read() - .fd_from_raw_integer::>(raw_fd) - .map_err(|err| match err { - litebox::fd::ErrRawIntFd::NotFound => Errno::EBADF, - litebox::fd::ErrRawIntFd::InvalidSubsystem => Errno::ENOTSOCK, - })?; - let handle = global - .litebox - .descriptor_table() - .entry_handle(&unix) - .ok_or(Errno::EBADF)?; - handle.with_entry(|entry| unix_op(entry)) } } @@ -1260,11 +1251,9 @@ impl Task { addrlen: Option>, flags: SockFlags, ) -> Result { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(sockfd)?; let mut remote_addr = addr.is_some().then(SocketAddress::default); - let fd = self.do_accept(sockfd, remote_addr.as_mut(), flags)?; + let fd = self.do_accept(&socket, remote_addr.as_mut(), flags)?; if let (Some(addr), Some(remote_addr)) = (addr, remote_addr) { let addrlen = addrlen.ok_or(Errno::EFAULT)?; if let Err(err) = write_sockaddr_to_user::(remote_addr, addr, addrlen) { @@ -1278,15 +1267,15 @@ impl Task { } fn do_accept( &self, - sockfd: u32, + socket: &AnyTypedFd, peer: Option<&mut SocketAddress>, flags: SockFlags, ) -> Result { let files = self.files.borrow(); let want_peer = peer.is_some(); - let (file, peer_addr) = files.with_socket( + let (file, peer_addr) = files.with_typed_socket( &self.global, - sockfd, + socket, |fd| { let sock_type = self.global.get_socket_type(fd)?; let mut socket_addr = @@ -1339,16 +1328,18 @@ impl Task { sockaddr: UserPtr, addrlen: usize, ) -> Result<(), Errno> { - let Ok(fd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(fd)?; let sockaddr = read_sockaddr_from_user::(sockaddr, addrlen)?; - self.do_connect(fd, sockaddr) + self.do_connect(&socket, sockaddr) } - fn do_connect(&self, sockfd: u32, sockaddr: SocketAddress) -> Result<(), Errno> { - self.files.borrow().with_socket( + fn do_connect( + &self, + socket: &AnyTypedFd, + sockaddr: SocketAddress, + ) -> Result<(), Errno> { + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| { let addr = sockaddr.clone().inet().ok_or(Errno::EAFNOSUPPORT)?; self.global.connect(&self.wait_cx(), fd, addr) @@ -1367,16 +1358,14 @@ impl Task { sockaddr: UserPtr, addrlen: usize, ) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(sockfd)?; let sockaddr = read_sockaddr_from_user::(sockaddr, addrlen)?; - self.do_bind(sockfd, sockaddr) + self.do_bind(&socket, sockaddr) } - fn do_bind(&self, sockfd: u32, sockaddr: SocketAddress) -> Result<(), Errno> { - self.files.borrow().with_socket( + fn do_bind(&self, socket: &AnyTypedFd, sockaddr: SocketAddress) -> Result<(), Errno> { + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| { let addr = sockaddr.clone().inet().ok_or(Errno::EAFNOSUPPORT)?; self.global.bind(fd, addr) @@ -1390,15 +1379,13 @@ impl Task { /// Handle syscall `listen` pub(crate) fn sys_listen(&self, sockfd: i32, backlog: u16) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; - self.do_listen(sockfd, backlog) + let socket = self.files.borrow().socket_from_raw(sockfd)?; + self.do_listen(&socket, backlog) } - fn do_listen(&self, sockfd: u32, backlog: u16) -> Result<(), Errno> { - self.files.borrow().with_socket( + fn do_listen(&self, socket: &AnyTypedFd, backlog: u16) -> Result<(), Errno> { + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| self.global.listen(fd, backlog), |file| file.listen(backlog, &self.global), ) @@ -1414,25 +1401,23 @@ impl Task { addr: Option>, addrlen: u32, ) -> Result { - let Ok(fd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(fd)?; let sockaddr = addr .map(|addr| read_sockaddr_from_user::(addr, addrlen as usize)) .transpose()?; let buf = buf.to_owned_slice::(len).ok_or(Errno::EFAULT)?; - self.do_sendto(fd, &buf, flags, sockaddr) + self.do_sendto(&socket, &buf, flags, sockaddr) } fn do_sendto( &self, - sockfd: u32, + socket: &AnyTypedFd, buf: &[u8], flags: SendFlags, sockaddr: Option, ) -> Result { - let res = self.files.borrow().with_socket( + let res = self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| { let sockaddr = sockaddr .clone() @@ -1464,15 +1449,14 @@ impl Task { msg: UserPtr, flags: SendFlags, ) -> Result { - let Ok(fd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(fd)?; let msg = msg.read_at_offset::(0).ok_or(Errno::EFAULT)?; - self.do_sendmsg(fd, &msg, flags) + self.do_sendmsg(&socket, &msg, flags) } + fn do_sendmsg( &self, - sockfd: u32, + socket: &AnyTypedFd, msg: &litebox_common_linux::UserMsgHdr, flags: SendFlags, ) -> Result { @@ -1501,9 +1485,9 @@ impl Task { .ok_or(Errno::EFAULT)?, ) }; - let res = self.files.borrow().with_socket( + let res = self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| { let sock_addr = sock_addr .clone() @@ -1538,20 +1522,11 @@ impl Task { vlen: u32, flags: SendFlags, ) -> Result { - let Ok(sockfd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; - let vlen = (vlen as usize).min(UIO_MAXIOV); // Linux looks up the fd before touching vlen/msgvec, so a bogus fd // takes priority over a bogus msgvec pointer or vlen == 0. - self.files.borrow().with_socket( - &self.global, - sockfd, - |_| Ok::<(), Errno>(()), - |_| Ok::<(), Errno>(()), - )?; + let socket = self.files.borrow().socket_from_raw(fd)?; if vlen == 0 { return Ok(0); @@ -1567,7 +1542,7 @@ impl Task { return bail(Errno::EFAULT); }; let inner = mmh.msg_hdr; - let n = match self.do_sendmsg(sockfd, &inner, flags) { + let n = match self.do_sendmsg(&socket, &inner, flags) { Ok(n) => n, Err(e) => return bail(e), }; @@ -1595,14 +1570,12 @@ impl Task { addrlen: UserPtrMut, ) -> Result { const MAX_LEN: usize = 4096; - let Ok(sockfd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(fd)?; let mut source_addr = None; let mut buffer = [0u8; MAX_LEN]; let recv_buf = &mut buffer[..MAX_LEN.min(len)]; let size = self.do_recvfrom( - sockfd, + &socket, recv_buf, flags, if addr.is_some() { @@ -1636,21 +1609,20 @@ impl Task { /// this may be larger than the provided buffer length as the excessive data will be truncated. fn do_recvfrom( &self, - sockfd: u32, + socket: &AnyTypedFd, buf: &mut [u8], flags: ReceiveFlags, source_addr: Option<&mut Option>, ) -> Result { let want_source = source_addr.is_some(); let files = self.files.borrow(); - let raw_fd = usize::try_from(sockfd).or(Err(Errno::EBADF))?; let (size, addr) = { // We need to do this cell dance because otherwise Rust can't recognize that the two // closures are mutually exclusive. let buf: core::cell::RefCell<&mut [u8]> = core::cell::RefCell::new(buf); - files.with_socket( + files.with_typed_socket( &self.global, - raw_fd.trunc(), + socket, |fd| { let mut addr = None; let size = self.global.receive( @@ -1690,21 +1662,19 @@ impl Task { msg_ptr: UserPtrMut, flags: ReceiveFlags, ) -> Result { - let Ok(sockfd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; - let supported_flags = ReceiveFlags::DONTWAIT | ReceiveFlags::TRUNC; if flags.intersects(supported_flags.complement()) { log_unsupported!("Unsupported recvmsg flags: {:?}", flags); return Err(Errno::EINVAL); } - self.do_recvmsg(sockfd, msg_ptr, flags) + let socket = self.files.borrow().socket_from_raw(fd)?; + self.do_recvmsg(&socket, msg_ptr, flags) } + fn do_recvmsg( &self, - sockfd: u32, + socket: &AnyTypedFd, msg_ptr: UserPtrMut, flags: ReceiveFlags, ) -> Result { @@ -1744,7 +1714,7 @@ impl Task { buffer.resize(total_iov_capacity, 0); let recv_buf = &mut buffer[..]; let size = self.do_recvfrom( - sockfd, + socket, recv_buf, flags, if want_source { @@ -1838,20 +1808,21 @@ impl Task { // so a bad timeout takes precedence over EBADF. let timeout_duration = timeout.read::()?; - let Ok(sockfd) = u32::try_from(fd) else { - return Err(Errno::EBADF); - }; - let vlen = vlen as usize; // Linux looks up the fd before touching vlen/msgvec, so a bogus fd // takes priority over a bogus msgvec pointer or vlen == 0. - let inet_proxy = self.files.borrow().with_socket( - &self.global, - sockfd, - |fd| self.global.get_proxy(fd).map(Some), - |_| Ok(None), - )?; + let (socket, inet_proxy) = { + let files = self.files.borrow(); + let socket = files.socket_from_raw(fd)?; + let inet_proxy = files.with_typed_socket( + &self.global, + &socket, + |fd| self.global.get_proxy(fd).map(Some), + |_| Ok(None), + )?; + (socket, inet_proxy) + }; if vlen == 0 { return Ok(0); @@ -1878,7 +1849,7 @@ impl Task { for i in 0..vlen { let base = msgvec_base + i * stride; let inner_ptr = UserPtrMut::::from_usize(base); - let n = match self.do_recvmsg(sockfd, inner_ptr, iter_flags) { + let n = match self.do_recvmsg(&socket, inner_ptr, iter_flags) { Ok(n) => n, Err(e) => { if received > 0 { @@ -1940,25 +1911,23 @@ impl Task { optval: UserPtr, optlen: usize, ) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(sockfd)?; let optname = SocketOptionName::try_from(level, optname).ok_or_else(|| { log_unsupported!("setsockopt(level = {level}, optname = {optname})"); Errno::EINVAL })?; - self.do_setsockopt(sockfd, optname, optval, optlen) + self.do_setsockopt(&socket, optname, optval, optlen) } fn do_setsockopt( &self, - sockfd: u32, + socket: &AnyTypedFd, optname: SocketOptionName, optval: UserPtr, optlen: usize, ) -> Result<(), Errno> { - self.files.borrow().with_socket( + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| self.global.setsockopt(fd, optname, optval, optlen), |file| file.setsockopt(&self.global, optname, optval, optlen), ) @@ -1973,9 +1942,7 @@ impl Task { optval: UserPtrMut, optlen: UserPtrMut, ) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; + let socket = self.files.borrow().socket_from_raw(sockfd)?; let optname = SocketOptionName::try_from(level, optname).ok_or_else(|| { log_unsupported!("setsockopt(level = {level}, optname = {optname})"); Errno::EINVAL @@ -1984,7 +1951,7 @@ impl Task { if len > i32::MAX as u32 { return Err(Errno::EINVAL); } - let new_len = self.do_getsockopt(sockfd, optname, optval, len)?; + let new_len = self.do_getsockopt(&socket, optname, optval, len)?; optlen .write_at_offset::(0, new_len.trunc()) .ok_or(Errno::EFAULT)?; @@ -1995,14 +1962,14 @@ impl Task { /// Returns the length of the option value written to `optval` on success. fn do_getsockopt( &self, - sockfd: u32, + socket: &AnyTypedFd, optname: SocketOptionName, optval: UserPtrMut, len: u32, ) -> Result { - self.files.borrow().with_socket( + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| self.global.getsockopt(fd, optname, optval, len), |file| file.getsockopt(&self.global, optname, optval, len), ) @@ -2015,16 +1982,14 @@ impl Task { addr: UserPtrMut, addrlen: UserPtrMut, ) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; - let sockaddr = self.do_getsockname(sockfd)?; + let socket = self.files.borrow().socket_from_raw(sockfd)?; + let sockaddr = self.do_getsockname(&socket)?; write_sockaddr_to_user::(sockaddr, addr, addrlen) } - fn do_getsockname(&self, sockfd: u32) -> Result { - self.files.borrow().with_socket( + fn do_getsockname(&self, socket: &AnyTypedFd) -> Result { + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| { self.global .net @@ -2044,16 +2009,14 @@ impl Task { addr: UserPtrMut, addrlen: UserPtrMut, ) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; - let sockaddr = self.do_getpeername(sockfd)?; + let socket = self.files.borrow().socket_from_raw(sockfd)?; + let sockaddr = self.do_getpeername(&socket)?; write_sockaddr_to_user::(sockaddr, addr, addrlen) } - fn do_getpeername(&self, sockfd: u32) -> Result { - self.files.borrow().with_socket( + fn do_getpeername(&self, socket: &AnyTypedFd) -> Result { + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |fd| { self.global .net @@ -2072,18 +2035,16 @@ impl Task { /// Handle syscall `shutdown` pub(crate) fn sys_shutdown(&self, sockfd: i32, how: i32) -> Result<(), Errno> { - let Ok(sockfd) = u32::try_from(sockfd) else { - return Err(Errno::EBADF); - }; - self.do_shutdown(sockfd, how) + let socket = self.files.borrow().socket_from_raw(sockfd)?; + self.do_shutdown(&socket, how) } - fn do_shutdown(&self, sockfd: u32, how: i32) -> Result<(), Errno> { + fn do_shutdown(&self, socket: &AnyTypedFd, how: i32) -> Result<(), Errno> { // Linux validates the fd (EBADF, ENOTSOCK) before `how` (EINVAL), - // so resolve the socket through `with_socket` first and validate `how` + // so resolve the socket first and validate `how` // only inside the matching branch. - self.files.borrow().with_socket( + self.files.borrow().with_typed_socket( &self.global, - sockfd, + socket, |_fd| { ShutdownHow::try_from(how).map_err(|_| Errno::EINVAL)?; log_unsupported!("shutdown on inet socket"); @@ -2142,9 +2103,22 @@ mod tests { .expect("close socket failed"); } + fn typed_socket( + task: &TestTask, + fd: u32, + ) -> crate::syscalls::file::AnyTypedFd { + task.files + .borrow() + .socket_from_raw(i32::try_from(fd).unwrap()) + .unwrap() + } + /// Helper to read SO_ERROR from a socket via getsockopt. /// Returns the errno integer value (0 means no error). - fn get_so_error(task: &TestTask, sockfd: u32) -> u32 { + fn get_so_error( + task: &TestTask, + sockfd: &crate::syscalls::file::AnyTypedFd, + ) -> u32 { let mut optval: u32 = 0xDEAD; let len = task .do_getsockopt( @@ -2192,7 +2166,7 @@ mod tests { test_trunc: bool, option: &'static str, ) { - let server = task + let raw_server = task .do_socket( AddressFamily::INET, SockType::Stream, @@ -2204,13 +2178,14 @@ mod tests { 0, ) .unwrap(); + let server = typed_socket(task, raw_server); let server_sockaddr = SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from(ip), port, ))); - task.do_bind(server, server_sockaddr.clone()) + task.do_bind(&server, server_sockaddr.clone()) .expect("Failed to bind socket"); - task.do_listen(server, 1) + task.do_listen(&server, 1) .expect("Failed to listen on socket"); // Create an epoll instance and register the server fd for EPOLLIN @@ -2218,7 +2193,7 @@ mod tests { .sys_epoll_create(litebox_common_linux::EpollCreateFlags::empty()) .expect("failed to create epoll"); let epfd = i32::try_from(epfd).unwrap(); - epoll_add(task, epfd, server, litebox::event::Events::IN); + epoll_add(task, epfd, raw_server, litebox::event::Events::IN); let buf = "Hello, world!"; let child_handle = std::thread::spawn(move || { @@ -2257,9 +2232,9 @@ mod tests { } let mut remote_addr = super::SocketAddress::default(); - let client_fd = task + let raw_client_fd = task .do_accept( - server, + &server, Some(&mut remote_addr), if is_nonblocking { SockFlags::NONBLOCK @@ -2268,8 +2243,9 @@ mod tests { }, ) .expect("Failed to accept connection"); - assert_eq!(server_sockaddr, task.do_getsockname(client_fd).unwrap()); - assert_eq!(remote_addr, task.do_getpeername(client_fd).unwrap()); + let client_fd = typed_socket(task, raw_client_fd); + assert_eq!(server_sockaddr, task.do_getsockname(&client_fd).unwrap()); + assert_eq!(remote_addr, task.do_getpeername(&client_fd).unwrap()); let super::SocketAddress::Inet(SocketAddr::V4(remote_addr)) = remote_addr else { panic!("Expected IPv4 address"); }; @@ -2279,7 +2255,7 @@ mod tests { match option { "sendto" => { let n = task - .do_sendto(client_fd, buf.as_bytes(), SendFlags::empty(), None) + .do_sendto(&client_fd, buf.as_bytes(), SendFlags::empty(), None) .expect("Failed to send data"); assert_eq!(n, buf.len()); let output = child_handle @@ -2309,7 +2285,7 @@ mod tests { h }; assert_eq!( - task.do_sendmsg(client_fd, &hdr, SendFlags::empty()) + task.do_sendmsg(&client_fd, &hdr, SendFlags::empty()) .expect("Failed to sendmsg"), buf1.len() + buf2.len() ); @@ -2322,13 +2298,13 @@ mod tests { } "recvfrom" | "recvmsg" => { if is_nonblocking { - epoll_add(task, epfd, client_fd, litebox::event::Events::IN); + epoll_add(task, epfd, raw_client_fd, litebox::event::Events::IN); let mut events = [litebox_common_linux::EpollEvent { events: 0, data: 0 }; 2]; let n = epoll_wait(task, epfd, &mut events); for ev in &events[..n] { assert!(ev.events & litebox::event::Events::IN.bits() != 0); let fd = u32::try_from(ev.data).unwrap(); - assert_eq!(fd, client_fd); + assert_eq!(fd, raw_client_fd); } } let mut recv_buf = [0u8; 48]; @@ -2339,7 +2315,7 @@ mod tests { }; let n = match option { "recvfrom" => task - .do_recvfrom(client_fd, &mut recv_buf, flags, None) + .do_recvfrom(&client_fd, &mut recv_buf, flags, None) .expect("Failed to receive data"), "recvmsg" => { let iovec = [litebox_common_linux::IoVec { @@ -2352,7 +2328,7 @@ mod tests { msg_hdr.msg_iov = UserPtr::from_usize(iovec.as_ptr() as usize); msg_hdr.msg_iovlen = iovec.len(); let msg_ptr = UserPtrMut::from_usize(&raw mut msg_hdr as usize); - task.sys_recvmsg(i32::try_from(client_fd).unwrap(), msg_ptr, flags) + task.sys_recvmsg(i32::try_from(raw_client_fd).unwrap(), msg_ptr, flags) .expect("failed to recvmsg") } _ => unreachable!(), @@ -2368,8 +2344,8 @@ mod tests { _ => panic!("Unknown option"), } - close_socket(task, client_fd); - close_socket(task, server); + close_socket(task, raw_client_fd); + close_socket(task, raw_server); } fn test_tcp_socket_with_external_client(port: u16, is_nonblocking: bool, test_trunc: bool) { @@ -2440,17 +2416,18 @@ mod tests { #[test] fn test_tun_tcp_connection_refused() { let task = init_platform(Some(TUN_DEVICE_NAME)); - let socket_fd = task + let raw_socket_fd = task .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) .expect("failed to create socket"); - let socket_fd2 = task - .sys_dup(i32::try_from(socket_fd).unwrap(), None, None) + let raw_socket_fd2 = task + .sys_dup(i32::try_from(raw_socket_fd).unwrap(), None, None) .unwrap(); + let socket_fd2 = typed_socket(&task, raw_socket_fd2); - close_socket(&task, socket_fd); + close_socket(&task, raw_socket_fd); let err = task .do_connect( - socket_fd2, + &socket_fd2, SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from([10, 0, 0, 1]), SERVER_PORT, @@ -2459,11 +2436,11 @@ mod tests { .unwrap_err(); assert_eq!(err, litebox_common_linux::errno::Errno::ECONNREFUSED); - let so_err = get_so_error(&task, socket_fd2); + let so_err = get_so_error(&task, &socket_fd2); assert_eq!(so_err, i32::from(Errno::ECONNREFUSED).cast_unsigned()); // Second read should be cleared (self-clearing semantics) - assert_eq!(get_so_error(&task, socket_fd2), 0); + assert_eq!(get_so_error(&task, &socket_fd2), 0); } #[test] @@ -2484,17 +2461,18 @@ mod tests { std::thread::sleep(core::time::Duration::from_secs(1)); // Client socket - let client_fd = task + let raw_client_fd = task .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) .expect("failed to create client socket"); + let client_fd = typed_socket(&task, raw_client_fd); let server_addr = SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from([10, 0, 0, 1]), SERVER_PORT, ))); - task.do_connect(client_fd, server_addr) + task.do_connect(&client_fd, server_addr) .expect("failed to connect to server"); - let so_error = get_so_error(&task, client_fd); + let so_error = get_so_error(&task, &client_fd); assert_eq!( so_error, 0, "SO_ERROR should be 0 after successful connect, got {so_error}" @@ -2502,7 +2480,7 @@ mod tests { let buf = "Hello, world!"; let n = task - .do_sendto(client_fd, buf.as_bytes(), SendFlags::empty(), None) + .do_sendto(&client_fd, buf.as_bytes(), SendFlags::empty(), None) .unwrap(); assert_eq!(n, buf.len()); @@ -2512,14 +2490,14 @@ mod tests { }; let optval = UserPtr::from_usize((&raw const linger).cast::() as usize); task.do_setsockopt( - client_fd, + &client_fd, SocketOptionName::Socket(SocketOption::LINGER), optval, core::mem::size_of::(), ) .expect("Failed to set SO_LINGER"); - close_socket(&task, client_fd); + close_socket(&task, raw_client_fd); let output = child_handle .join() @@ -2537,7 +2515,7 @@ mod tests { op: &str, ) { // Server socket and bind - let server_fd = task + let raw_server_fd = task .do_socket( AddressFamily::INET, SockType::Datagram, @@ -2549,15 +2527,16 @@ mod tests { litebox_common_linux::IPProtocol::UDP as u8, ) .expect("failed to create server socket"); + let server_fd = typed_socket(task, raw_server_fd); let server_addr = SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from(TUN_IP_ADDR), SERVER_PORT, ))); - task.do_bind(server_fd, server_addr.clone()) + task.do_bind(&server_fd, server_addr.clone()) .expect("failed to bind server"); assert_eq!( server_addr, - task.do_getsockname(server_fd).expect("getsockname failed") + task.do_getsockname(&server_fd).expect("getsockname failed") ); // Create an epoll instance and register the server fd for EPOLLIN @@ -2565,7 +2544,7 @@ mod tests { .sys_epoll_create(litebox_common_linux::EpollCreateFlags::empty()) .expect("failed to create epoll"); let epfd = i32::try_from(epfd).unwrap(); - epoll_add(task, epfd, server_fd, litebox::event::Events::IN); + epoll_add(task, epfd, raw_server_fd, litebox::event::Events::IN); let msg = "Hello from client"; let mut child = std::process::Command::new("nc") @@ -2605,7 +2584,7 @@ mod tests { for ev in &events[..n] { assert!(ev.events & litebox::event::Events::IN.bits() != 0); let fd = u32::try_from(ev.data).unwrap(); - assert_eq!(fd, server_fd); + assert_eq!(fd, raw_server_fd); } } let recv_len = if test_trunc { @@ -2618,7 +2597,7 @@ mod tests { "recvfrom" => { let mut addrlen = core::mem::size_of::(); task.sys_recvfrom( - i32::try_from(server_fd).unwrap(), + i32::try_from(raw_server_fd).unwrap(), UserPtrMut::from_usize(recv_buf.as_mut_ptr() as usize), recv_len, recv_flags, @@ -2639,7 +2618,7 @@ mod tests { msg_hdr.msg_namelen = source_addr.len().trunc(); let msg_ptr = UserPtrMut::from_usize(&raw mut msg_hdr as usize); let n = task - .sys_recvmsg(i32::try_from(server_fd).unwrap(), msg_ptr, recv_flags) + .sys_recvmsg(i32::try_from(raw_server_fd).unwrap(), msg_ptr, recv_flags) .expect("recvmsg failed"); if test_trunc { let flags = msg_hdr.msg_flags; @@ -2671,7 +2650,7 @@ mod tests { }; assert_eq!(sender_addr.port(), CLIENT_PORT); - close_socket(task, server_fd); + close_socket(task, raw_server_fd); child.wait().expect("Failed to wait for client"); } @@ -2705,7 +2684,7 @@ mod tests { let task = init_platform(Some(TUN_DEVICE_NAME)); // Client socket and explicit bind - let client_fd = task + let raw_client_fd = task .do_socket( AddressFamily::INET, SockType::Datagram, @@ -2713,6 +2692,7 @@ mod tests { litebox_common_linux::IPProtocol::UDP as u8, ) .expect("failed to create client socket"); + let client_fd = typed_socket(&task, raw_client_fd); let server_addr = SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from([127, 0, 0, 1]), @@ -2722,7 +2702,7 @@ mod tests { // Send from client to server let msg = "Hello without connect()"; task.do_sendto( - client_fd, + &client_fd, msg.as_bytes(), SendFlags::empty(), Some(server_addr.clone()), @@ -2731,35 +2711,36 @@ mod tests { // Client implicitly bound to an ephemeral port via sendto let SocketAddress::Inet(client_addr) = - task.do_getsockname(client_fd).expect("getsockname failed") + task.do_getsockname(&client_fd).expect("getsockname failed") else { panic!("Expected Inet socket address"); }; assert_ne!(client_addr.port(), 0); // Client connects to server address - task.do_connect(client_fd, server_addr.clone()) + task.do_connect(&client_fd, server_addr.clone()) .expect("failed to connect"); // Now client can send without specifying addr let msg = "Hello with connect()"; - task.do_sendto(client_fd, msg.as_bytes(), SendFlags::empty(), None) + task.do_sendto(&client_fd, msg.as_bytes(), SendFlags::empty(), None) .expect("failed to sendto"); - close_socket(&task, client_fd); + close_socket(&task, raw_client_fd); } #[test] fn test_tun_tcp_sockopt() { let task = init_platform(Some(TUN_DEVICE_NAME)); - let sockfd = task + let raw_sockfd = task .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) .expect("failed to create socket"); + let sockfd = typed_socket(&task, raw_sockfd); let mut congestion_name = [0u8; 16]; let optlen = task .do_getsockopt( - sockfd, + &sockfd, SocketOptionName::TCP(TcpOption::CONGESTION), UserPtrMut::from_usize(congestion_name.as_mut_ptr() as usize), congestion_name.len().trunc(), @@ -2772,7 +2753,7 @@ mod tests { ); task.do_setsockopt( - sockfd, + &sockfd, SocketOptionName::TCP(TcpOption::CONGESTION), UserPtr::from_usize(congestion_name.as_ptr() as usize), optlen, @@ -2782,7 +2763,7 @@ mod tests { let congestion_name = b"cubic\0"; let err = task .do_setsockopt( - sockfd, + &sockfd, SocketOptionName::TCP(TcpOption::CONGESTION), UserPtr::from_usize(congestion_name.as_ptr() as usize), congestion_name.len(), @@ -2793,7 +2774,7 @@ mod tests { let val: u32 = 1; let optval = UserPtr::from_usize((&raw const val).cast::() as usize); task.do_setsockopt( - sockfd, + &sockfd, SocketOptionName::Socket(SocketOption::KEEPALIVE), optval, core::mem::size_of::(), @@ -2805,7 +2786,7 @@ mod tests { let optval_out = UserPtrMut::from_usize((&raw mut result).cast::() as usize); let len = task .do_getsockopt( - sockfd, + &sockfd, SocketOptionName::Socket(SocketOption::KEEPALIVE), optval_out, core::mem::size_of::().trunc(), @@ -2819,16 +2800,17 @@ mod tests { #[test] fn test_tun_tcp_so_error_network_unreachable() { let task = init_platform(Some(TUN_DEVICE_NAME)); - let sockfd = task + let raw_sockfd = task .do_socket(AddressFamily::INET, SockType::Stream, SockFlags::empty(), 0) .expect("failed to create socket"); + let sockfd = typed_socket(&task, raw_sockfd); // Connect to an off-subnet IP (TEST-NET, 192.0.2.1). // smoltcp does not report errors when route table lookup fails. Instead, it just dicards the packets. // Our current implementation returns `ETIMEDOUT` instead of `ENETUNREACH`. let err = task .do_connect( - sockfd, + &sockfd, SocketAddress::Inet(SocketAddr::V4(core::net::SocketAddrV4::new( core::net::Ipv4Addr::from([192, 0, 2, 1]), SERVER_PORT, @@ -2837,10 +2819,10 @@ mod tests { .unwrap_err(); assert_eq!(err, Errno::ETIMEDOUT); - let so_err = get_so_error(&task, sockfd); + let so_err = get_so_error(&task, &sockfd); assert_eq!(so_err, i32::from(Errno::ETIMEDOUT).cast_unsigned()); - close_socket(&task, sockfd); + close_socket(&task, raw_sockfd); } #[test] @@ -2891,13 +2873,14 @@ mod unix_tests { addr: &str, flags: SockFlags, ) -> Result { - let server_fd = create_unix_socket(task, SockType::Stream, flags); + let raw_server_fd = create_unix_socket(task, SockType::Stream, flags); + let server_fd = typed_socket(task, raw_server_fd); task.do_bind( - server_fd, + &server_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), )?; - task.do_listen(server_fd, 1)?; - Ok(server_fd) + task.do_listen(&server_fd, 1)?; + Ok(raw_server_fd) } fn close_socket(task: &TestTask, fd: u32) { @@ -2905,6 +2888,16 @@ mod unix_tests { .expect("close socket failed"); } + fn typed_socket( + task: &TestTask, + fd: u32, + ) -> crate::syscalls::file::AnyTypedFd { + task.files + .borrow() + .socket_from_raw(i32::try_from(fd).unwrap()) + .unwrap() + } + fn ppoll(task: &TestTask, fd: u32, events: Events) { let fd = i32::try_from(fd).unwrap(); let mut pollfd = [litebox_common_linux::Pollfd { @@ -2933,20 +2926,22 @@ mod unix_tests { for _ in 0..10 { let server_path = "/unix_stream_socket_server.sock"; let client_path = "/unix_stream_socket_client.sock"; - let server_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); - let client_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_server_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_client_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let server_fd = typed_socket(&task, raw_server_fd); + let client_fd = typed_socket(&task, raw_client_fd); let server_addr = SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())); let client_addr = SocketAddress::Unix(UnixSocketAddr::Path(client_path.to_string())); - task.do_bind(server_fd, server_addr.clone()) + task.do_bind(&server_fd, server_addr.clone()) .expect("server bind failed"); - task.do_bind(client_fd, client_addr.clone()) + task.do_bind(&client_fd, client_addr.clone()) .expect("client bind failed"); // send message from server to client let msg1 = "Hello from server"; let n = task .do_sendto( - server_fd, + &server_fd, msg1.as_bytes(), SendFlags::empty(), Some(client_addr.clone()), @@ -2958,7 +2953,7 @@ mod unix_tests { let mut source = None; let n = task .do_recvfrom( - client_fd, + &client_fd, &mut buf, ReceiveFlags::empty(), Some(&mut source), @@ -2972,7 +2967,7 @@ mod unix_tests { let msg2 = "Hello from client"; let n = task .do_sendto( - client_fd, + &client_fd, msg2.as_bytes(), SendFlags::empty(), Some(server_addr), @@ -2984,7 +2979,7 @@ mod unix_tests { let mut source = None; let n = task .do_recvfrom( - server_fd, + &server_fd, &mut buf, ReceiveFlags::empty(), Some(&mut source), @@ -2994,8 +2989,8 @@ mod unix_tests { assert_eq!(&buf[..n], b"Hello from client"); assert_eq!(source, Some(client_addr)); - close_socket(&task, server_fd); - close_socket(&task, client_fd); + close_socket(&task, raw_server_fd); + close_socket(&task, raw_client_fd); task.sys_unlinkat(-1, server_path, AtFlags::empty()) .unwrap(); task.sys_unlinkat(-1, client_path, AtFlags::empty()) @@ -3009,42 +3004,45 @@ mod unix_tests { for _ in 0..10 { let addr = "/unix_stream_socket.sock"; - let server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); - let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); + let raw_client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let server_fd = typed_socket(&task, raw_server_fd); + let client_fd = typed_socket(&task, raw_client_fd); task.do_connect( - client_fd, + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); let mut peer_addr = SocketAddress::default(); - let server_conn = task - .do_accept(server_fd, Some(&mut peer_addr), SockFlags::empty()) + let raw_server_conn = task + .do_accept(&server_fd, Some(&mut peer_addr), SockFlags::empty()) .unwrap(); + let server_conn = typed_socket(&task, raw_server_conn); assert!(matches!( peer_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) )); let msg1 = "Hello, "; let n = task - .do_sendto(server_conn, msg1.as_bytes(), SendFlags::empty(), None) + .do_sendto(&server_conn, msg1.as_bytes(), SendFlags::empty(), None) .expect("sendto failed"); assert_eq!(n, msg1.len()); let msg2 = "world!"; let n = task - .do_sendto(server_conn, msg2.as_bytes(), SendFlags::empty(), None) + .do_sendto(&server_conn, msg2.as_bytes(), SendFlags::empty(), None) .expect("sendto failed"); assert_eq!(n, msg2.len()); let mut buf = [0u8; 64]; let n = task - .do_recvfrom(client_fd, &mut buf, ReceiveFlags::empty(), None) + .do_recvfrom(&client_fd, &mut buf, ReceiveFlags::empty(), None) .expect("recvfrom failed"); assert_eq!(n, msg1.len() + msg2.len()); assert_eq!(&buf[..n], b"Hello, world!"); - close_socket(&task, server_fd); - close_socket(&task, client_fd); + close_socket(&task, raw_server_fd); + close_socket(&task, raw_client_fd); task.sys_unlinkat(-1, addr, AtFlags::empty()).unwrap(); } } @@ -3052,56 +3050,60 @@ mod unix_tests { #[test] fn test_unix_stream_socket_refused() { let task = init_platform(None); - let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let client_fd = typed_socket(&task, raw_client_fd); let addr = "/unix_stream_socket_refused.sock"; let result = task.do_connect( - client_fd, + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ); assert_eq!(result.unwrap_err(), Errno::ECONNREFUSED); - close_socket(&task, client_fd); + close_socket(&task, raw_client_fd); - let server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); - let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); + let raw_client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let client_fd = typed_socket(&task, raw_client_fd); let result = task.do_connect( - client_fd, + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ); assert!(result.is_ok()); // close the server socket - close_socket(&task, server_fd); + close_socket(&task, raw_server_fd); - let another_client = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_another_client = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let another_client = typed_socket(&task, raw_another_client); let result = task.do_connect( - another_client, + &another_client, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ); assert_eq!(result.unwrap_err(), Errno::ECONNREFUSED); - close_socket(&task, another_client); - close_socket(&task, client_fd); + close_socket(&task, raw_another_client); + close_socket(&task, raw_client_fd); let addr = "/unix_stream_socket_refused2.sock"; - let server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); - let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_server_fd = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap(); + let raw_client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let client_fd = typed_socket(&task, raw_client_fd); // remove the sock file task.sys_unlinkat(-1, addr, AtFlags::empty()).unwrap(); let result = task.do_connect( - client_fd, + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ); assert_eq!(result.unwrap_err(), Errno::ENOENT); - close_socket(&task, server_fd); - close_socket(&task, client_fd); + close_socket(&task, raw_server_fd); + close_socket(&task, raw_client_fd); } fn test_multiple_unix_stream_connections(is_nonblocking: bool) { let task = init_platform(None); let addr = "/unix_multi_stream_socket.sock"; - let server_fd = create_unix_server_socket( + let raw_server_fd = create_unix_server_socket( &task, addr, if is_nonblocking { @@ -3111,11 +3113,12 @@ mod unix_tests { }, ) .unwrap(); + let server_fd = typed_socket(&task, raw_server_fd); let client = task.spawn_clone_for_test(move |task| { let mut client_fds = Vec::new(); for _ in 0..10 { - let client_fd = create_unix_socket( + let raw_client_fd = create_unix_socket( &task, SockType::Stream, if is_nonblocking { @@ -3124,38 +3127,39 @@ mod unix_tests { SockFlags::empty() }, ); + let client_fd = typed_socket(&task, raw_client_fd); if is_nonblocking { - ppoll(&task, server_fd, Events::OUT); + ppoll(&task, raw_server_fd, Events::OUT); } task.do_connect( - client_fd, + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); - client_fds.push(client_fd); + client_fds.push((raw_client_fd, client_fd)); } - for (i, client_fd) in client_fds.iter().enumerate() { + for (i, (_, client_fd)) in client_fds.iter().enumerate() { let msg = alloc::format!("message from connection {i}"); let n = task - .do_sendto(*client_fd, msg.as_bytes(), SendFlags::empty(), None) + .do_sendto(client_fd, msg.as_bytes(), SendFlags::empty(), None) .expect("sendto failed"); assert_eq!(n, msg.len()); } - for client_fd in client_fds { - close_socket(&task, client_fd); + for (raw_client_fd, _) in client_fds { + close_socket(&task, raw_client_fd); } }); - let mut server_conn_fds = Vec::new(); + let mut raw_server_conn_fds = Vec::new(); for _ in 0..10 { if is_nonblocking { - ppoll(&task, server_fd, Events::IN); + ppoll(&task, raw_server_fd, Events::IN); } - let server_conn = task + let raw_server_conn_fd = task .do_accept( - server_fd, + &server_fd, None, if is_nonblocking { SockFlags::NONBLOCK @@ -3164,26 +3168,27 @@ mod unix_tests { }, ) .unwrap(); - server_conn_fds.push(server_conn); + raw_server_conn_fds.push(raw_server_conn_fd); } - for (i, server_conn_fd) in server_conn_fds.iter().enumerate() { + for (i, raw_server_conn_fd) in raw_server_conn_fds.iter().enumerate() { let msg = alloc::format!("message from connection {i}"); let mut buf = [0u8; 64]; if is_nonblocking { - ppoll(&task, *server_conn_fd, Events::IN); + ppoll(&task, *raw_server_conn_fd, Events::IN); } + let server_conn_fd = typed_socket(&task, *raw_server_conn_fd); let n = task - .do_recvfrom(*server_conn_fd, &mut buf, ReceiveFlags::empty(), None) + .do_recvfrom(&server_conn_fd, &mut buf, ReceiveFlags::empty(), None) .expect("recvfrom failed"); assert_eq!(n, msg.len()); assert_eq!(&buf[..n], msg.as_bytes()); } - for server_conn_fd in server_conn_fds { - close_socket(&task, server_conn_fd); + for raw_server_conn_fd in raw_server_conn_fds { + close_socket(&task, raw_server_conn_fd); } - close_socket(&task, server_fd); + close_socket(&task, raw_server_fd); client.join().unwrap(); } @@ -3202,43 +3207,49 @@ mod unix_tests { let task = init_platform(None); for _ in 0..10 { let addr = "/unix_stream_socket_server.sock"; - let server1_fd = create_unix_server_socket(&task, addr, SockFlags::NONBLOCK).unwrap(); + let raw_server1_fd = + create_unix_server_socket(&task, addr, SockFlags::NONBLOCK).unwrap(); + let server1_fd = typed_socket(&task, raw_server1_fd); let err = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap_err(); assert_eq!(err, Errno::EADDRINUSE); // remove the socket file to allow another server to bind to the same address task.sys_unlinkat(-1, addr, AtFlags::empty()).unwrap(); - let server2_fd = create_unix_server_socket(&task, addr, SockFlags::NONBLOCK).unwrap(); + let raw_server2_fd = + create_unix_server_socket(&task, addr, SockFlags::NONBLOCK).unwrap(); + let server2_fd = typed_socket(&task, raw_server2_fd); - let client1_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_client1_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let client1_fd = typed_socket(&task, raw_client1_fd); task.do_connect( - client1_fd, + &client1_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); // server one is still alive but cannot accept connections let err = task - .do_accept(server1_fd, None, SockFlags::empty()) + .do_accept(&server1_fd, None, SockFlags::empty()) .unwrap_err(); assert_eq!(err, Errno::EAGAIN); let conn_fd = task - .do_accept(server2_fd, None, SockFlags::empty()) + .do_accept(&server2_fd, None, SockFlags::empty()) .unwrap(); close_socket(&task, conn_fd); - close_socket(&task, client1_fd); + close_socket(&task, raw_client1_fd); // close server one and connect again - close_socket(&task, server1_fd); - let client2_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + close_socket(&task, raw_server1_fd); + let raw_client2_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let client2_fd = typed_socket(&task, raw_client2_fd); task.do_connect( - client2_fd, + &client2_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); - close_socket(&task, client2_fd); - close_socket(&task, server2_fd); + close_socket(&task, raw_client2_fd); + close_socket(&task, raw_server2_fd); // still fail after we close the server let err = create_unix_server_socket(&task, addr, SockFlags::empty()).unwrap_err(); @@ -3253,32 +3264,35 @@ mod unix_tests { let task = init_platform(None); for _ in 0..10 { let addr = "/unix_datagram_socket_server.sock"; - let server_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_server_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let server_fd = typed_socket(&task, raw_server_fd); task.do_bind( - server_fd, + &server_fd, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); - let server_fd2 = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_server_fd2 = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let server_fd2 = typed_socket(&task, raw_server_fd2); let err = task .do_bind( - server_fd2, + &server_fd2, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap_err(); assert_eq!(err, Errno::EADDRINUSE); task.sys_unlinkat(-1, addr, AtFlags::empty()).unwrap(); - let server_fd2 = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_server_fd2 = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let server_fd2 = typed_socket(&task, raw_server_fd2); task.do_bind( - server_fd2, + &server_fd2, SocketAddress::Unix(UnixSocketAddr::Path(addr.to_string())), ) .unwrap(); - close_socket(&task, server_fd); - close_socket(&task, server_fd2); + close_socket(&task, raw_server_fd); + close_socket(&task, raw_server_fd2); task.sys_unlinkat(-1, addr, AtFlags::empty()).unwrap(); } } @@ -3296,17 +3310,20 @@ mod unix_tests { task.sys_socketpair(AddressFamily::UNIX as u32, ty_and_flags, 0, sv_mut_ptr) .unwrap(); - let sock1 = sv_ptr[0]; - let sock2 = sv_ptr[1]; + let raw_sock1 = sv_ptr[0]; + let raw_sock2 = sv_ptr[1]; + let sock1 = typed_socket(&task, raw_sock1); + let sock2 = typed_socket(&task, raw_sock2); // Receive on sock2 (from sock1) let receiver2 = task.spawn_clone_for_test(move |task| { let mut buf = [0u8; 64]; if is_nonblocking { - ppoll(&task, sock2, Events::IN); + ppoll(&task, raw_sock2, Events::IN); } + let sock2 = typed_socket(&task, raw_sock2); let n = task - .do_recvfrom(sock2, &mut buf, ReceiveFlags::empty(), None) + .do_recvfrom(&sock2, &mut buf, ReceiveFlags::empty(), None) .expect("recvfrom failed"); assert_eq!(&buf[..n], b"Message from sock1"); }); @@ -3314,17 +3331,18 @@ mod unix_tests { std::thread::sleep(core::time::Duration::from_millis(100)); // Send from sock1 to sock2 let msg1 = "Message from sock1"; - task.do_sendto(sock1, msg1.as_bytes(), SendFlags::empty(), None) + task.do_sendto(&sock1, msg1.as_bytes(), SendFlags::empty(), None) .expect("sendto failed"); let receiver1 = task.spawn_clone_for_test(move |task| { // Receive on sock1 (from sock2) let mut buf = [0u8; 64]; if is_nonblocking { - ppoll(&task, sock1, Events::IN); + ppoll(&task, raw_sock1, Events::IN); } + let sock1 = typed_socket(&task, raw_sock1); let n = task - .do_recvfrom(sock1, &mut buf, ReceiveFlags::empty(), None) + .do_recvfrom(&sock1, &mut buf, ReceiveFlags::empty(), None) .expect("recvfrom failed"); assert_eq!(&buf[..n], b"Message from sock2"); }); @@ -3332,12 +3350,12 @@ mod unix_tests { std::thread::sleep(core::time::Duration::from_millis(100)); // Send from sock2 to sock1 let msg2 = "Message from sock2"; - task.do_sendto(sock2, msg2.as_bytes(), SendFlags::empty(), None) + task.do_sendto(&sock2, msg2.as_bytes(), SendFlags::empty(), None) .expect("sendto failed"); std::thread::sleep(core::time::Duration::from_millis(500)); - close_socket(&task, sock1); - close_socket(&task, sock2); + close_socket(&task, raw_sock1); + close_socket(&task, raw_sock2); receiver2.join().unwrap(); receiver1.join().unwrap(); } @@ -3378,14 +3396,15 @@ mod unix_tests { fn unix_socket_recv_timeout(ty: SockType) { let task = init_platform(None); - let (sock1, _sock2) = task + let (raw_sock1, _raw_sock2) = task .do_socketpair(AddressFamily::UNIX, ty, SockFlags::empty(), 0) .expect("socketpair failed"); + let sock1 = typed_socket(&task, raw_sock1); let timeout = Duration::from_millis(200); let tv = litebox_common_linux::TimeVal::from(timeout); let optval = UserPtr::from_usize((&raw const tv).cast::() as usize); task.do_setsockopt( - sock1, + &sock1, SocketOptionName::Socket(SocketOption::RCVTIMEO), optval, core::mem::size_of::(), @@ -3394,7 +3413,7 @@ mod unix_tests { let mut buf = [0u8; 16]; let start = std::time::Instant::now(); let err = task - .do_recvfrom(sock1, &mut buf, ReceiveFlags::empty(), None) + .do_recvfrom(&sock1, &mut buf, ReceiveFlags::empty(), None) .unwrap_err(); let elapsed = start.elapsed(); // Linux returns EAGAIN (not ETIMEDOUT) when SO_RCVTIMEO expires on a blocking recv. @@ -3416,20 +3435,23 @@ mod unix_tests { fn test_unix_stream_addr() { let task = init_platform(None); let server_path = "/unix_stream_sockname.sock"; - let server_fd = create_unix_server_socket(&task, server_path, SockFlags::empty()).unwrap(); + let raw_server_fd = + create_unix_server_socket(&task, server_path, SockFlags::empty()).unwrap(); + let server_fd = typed_socket(&task, raw_server_fd); // Server socket should have its bound address - let server_addr = task.do_getsockname(server_fd).unwrap(); + let server_addr = task.do_getsockname(&server_fd).unwrap(); assert_eq!( server_addr, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) ); // Create client and connect - let client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let raw_client_fd = create_unix_socket(&task, SockType::Stream, SockFlags::empty()); + let client_fd = typed_socket(&task, raw_client_fd); // Before connect, client should have unnamed address - let client_addr = task.do_getsockname(client_fd).unwrap(); + let client_addr = task.do_getsockname(&client_fd).unwrap(); assert!(matches!( client_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) @@ -3437,45 +3459,48 @@ mod unix_tests { // Connect client to server task.do_connect( - client_fd, + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())), ) .unwrap(); // After connect, client's getsockname should still be unnamed - let client_local_addr = task.do_getsockname(client_fd).unwrap(); + let client_local_addr = task.do_getsockname(&client_fd).unwrap(); assert!(matches!( client_local_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) )); // Client's getpeername should return server's address - let client_peer_addr = task.do_getpeername(client_fd).unwrap(); + let client_peer_addr = task.do_getpeername(&client_fd).unwrap(); assert_eq!( client_peer_addr, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) ); // Accept connection on server - let server_conn = task.do_accept(server_fd, None, SockFlags::empty()).unwrap(); + let raw_server_conn = task + .do_accept(&server_fd, None, SockFlags::empty()) + .unwrap(); + let server_conn = typed_socket(&task, raw_server_conn); // Server connection's local address should be the server's bound address - let server_conn_local = task.do_getsockname(server_conn).unwrap(); + let server_conn_local = task.do_getsockname(&server_conn).unwrap(); assert_eq!( server_conn_local, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) ); // Server connection's peer address should be unnamed (client didn't bind) - let server_conn_peer = task.do_getpeername(server_conn).unwrap(); + let server_conn_peer = task.do_getpeername(&server_conn).unwrap(); assert!(matches!( server_conn_peer, SocketAddress::Unix(UnixSocketAddr::Unnamed) )); - close_socket(&task, client_fd); - close_socket(&task, server_conn); - close_socket(&task, server_fd); + close_socket(&task, raw_client_fd); + close_socket(&task, raw_server_conn); + close_socket(&task, raw_server_fd); task.sys_unlinkat(-1, server_path, AtFlags::empty()) .unwrap(); } @@ -3486,17 +3511,19 @@ mod unix_tests { let server_path = "/unix_datagram_sockname_server.sock"; let client_path = "/unix_datagram_sockname_client.sock"; - let server_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); - let client_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_server_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let raw_client_fd = create_unix_socket(&task, SockType::Datagram, SockFlags::empty()); + let server_fd = typed_socket(&task, raw_server_fd); + let client_fd = typed_socket(&task, raw_client_fd); // Before bind, both should have unnamed addresses - let server_addr = task.do_getsockname(server_fd).unwrap(); + let server_addr = task.do_getsockname(&server_fd).unwrap(); assert!(matches!( server_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) )); - let client_addr = task.do_getsockname(client_fd).unwrap(); + let client_addr = task.do_getsockname(&client_fd).unwrap(); assert!(matches!( client_addr, SocketAddress::Unix(UnixSocketAddr::Unnamed) @@ -3504,13 +3531,13 @@ mod unix_tests { // Bind server task.do_bind( - server_fd, + &server_fd, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())), ) .unwrap(); // After bind, server should have its bound address - let server_local = task.do_getsockname(server_fd).unwrap(); + let server_local = task.do_getsockname(&server_fd).unwrap(); assert_eq!( server_local, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) @@ -3518,13 +3545,13 @@ mod unix_tests { // Bind client task.do_bind( - client_fd, + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(client_path.to_string())), ) .unwrap(); // After bind, client should have its bound address - let client_local = task.do_getsockname(client_fd).unwrap(); + let client_local = task.do_getsockname(&client_fd).unwrap(); assert_eq!( client_local, SocketAddress::Unix(UnixSocketAddr::Path(client_path.to_string())) @@ -3532,31 +3559,31 @@ mod unix_tests { // Connect client to server task.do_connect( - client_fd, + &client_fd, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())), ) .unwrap(); // After connect, getsockname should still return client's bound address - let client_local_after_connect = task.do_getsockname(client_fd).unwrap(); + let client_local_after_connect = task.do_getsockname(&client_fd).unwrap(); assert_eq!( client_local_after_connect, SocketAddress::Unix(UnixSocketAddr::Path(client_path.to_string())) ); // getpeername should return server's address - let client_peer = task.do_getpeername(client_fd).unwrap(); + let client_peer = task.do_getpeername(&client_fd).unwrap(); assert_eq!( client_peer, SocketAddress::Unix(UnixSocketAddr::Path(server_path.to_string())) ); // Server hasn't connected, so getpeername should fail with ENOTCONN - let server_peer_result = task.do_getpeername(server_fd); + let server_peer_result = task.do_getpeername(&server_fd); assert_eq!(server_peer_result.unwrap_err(), Errno::ENOTCONN); - close_socket(&task, server_fd); - close_socket(&task, client_fd); + close_socket(&task, raw_server_fd); + close_socket(&task, raw_client_fd); task.sys_unlinkat(-1, server_path, AtFlags::empty()) .unwrap(); task.sys_unlinkat(-1, client_path, AtFlags::empty())