From 2d573ad1054534acc775ec5c3499628cac65d04e Mon Sep 17 00:00:00 2001 From: MarcusGrass <34198073+MarcusGrass@users.noreply.github.com> Date: Sun, 5 May 2024 12:08:31 +0200 Subject: [PATCH] Networking improvements (#26) * Rework networking a bit, add some functions * Update to new lints --- Cargo.lock | 4 +- Notes.md | 9 + Readme.md | 1 - rusl/Cargo.toml | 2 +- rusl/src/futex.rs | 2 +- rusl/src/io_uring/test.rs | 194 +++++- rusl/src/network.rs | 14 +- rusl/src/network/accept.rs | 56 +- rusl/src/network/bind.rs | 24 +- rusl/src/network/connect.rs | 25 +- rusl/src/network/socket.rs | 77 ++- rusl/src/network/test.rs | 280 +++++++++ rusl/src/platform/compat/io_uring.rs | 226 ++++++- rusl/src/platform/compat/socket.rs | 560 +++++++++++++++++- rusl/src/platform/compat/time.rs | 8 +- rusl/src/platform/compat/uio.rs | 7 +- rusl/src/process/signal.rs | 5 +- rusl/src/select/poll.rs | 4 +- rusl/src/string/unix_str.rs | 10 +- rusl/src/unistd.rs | 2 + rusl/src/unistd/seek.rs | 27 + .../io_uring/uring_recv_send_pass_fd.txt | 1 + rusl/test-files/socket/.gitkeep | 0 test-runners/alloc-st-main/Cargo.lock | 4 +- test-runners/no-alloc-main/Cargo.lock | 4 +- test-runners/threaded-main/Cargo.lock | 4 +- tiny-std/src/allocator/dlmalloc.rs | 1 + tiny-std/src/env.rs | 1 + tiny-std/src/fs.rs | 2 +- tiny-std/src/io/read_buf.rs | 2 +- tiny-std/src/net.rs | 12 +- tiny-std/src/thread/spawn.rs | 4 +- tiny-std/src/unix/passwd/getpw_r.rs | 2 +- 33 files changed, 1483 insertions(+), 91 deletions(-) create mode 100644 Notes.md create mode 100644 rusl/src/network/test.rs create mode 100644 rusl/src/unistd/seek.rs create mode 100644 rusl/test-files/io_uring/uring_recv_send_pass_fd.txt create mode 100644 rusl/test-files/socket/.gitkeep diff --git a/Cargo.lock b/Cargo.lock index 88309e0..17dc1ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "linux-rust-bindings" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88eeeb1a5ef57ab0d40fb7a256e1b211496f97d6a8664ef52c0a665ce69a4e0f" +checksum = "109280d660e557dfa6474e3b1bbe04272ba3fa5c5e5ce73b394c7b224f082caa" [[package]] name = "rusl" diff --git a/Notes.md b/Notes.md new file mode 100644 index 0000000..0100974 --- /dev/null +++ b/Notes.md @@ -0,0 +1,9 @@ +# General dev-notes + +Running multi-arch is difficult. + +## Qemu + +`qemu-aarch64` version 9.0.0 [seems to have an issue](https://gitlab.com/qemu-project/qemu/-/issues/2326). +Use a previous version, 0.7.2 works, manifests as VDSO-image address-alignment being zero, which +causes a div-by-zero. diff --git a/Readme.md b/Readme.md index f2ac4c4..c9bd526 100644 --- a/Readme.md +++ b/Readme.md @@ -47,7 +47,6 @@ At present, the minimal WM builds statically pie-linked at `790K`. both with and without an allocator/threading. - ## License This project and any contributions are licensed under [MPL-2.0](LICENSE). diff --git a/rusl/Cargo.toml b/rusl/Cargo.toml index 4c449ca..ae9801e 100644 --- a/rusl/Cargo.toml +++ b/rusl/Cargo.toml @@ -18,7 +18,7 @@ alloc = [] integration-test = [] [dependencies] -linux-rust-bindings = { version = "0.1.1", features = ["all"] } +linux-rust-bindings = { version = "0.1.3", features = ["all"] } sc = "0.2.7" [dev-dependencies] \ No newline at end of file diff --git a/rusl/src/futex.rs b/rusl/src/futex.rs index cb007c7..bdc77e9 100644 --- a/rusl/src/futex.rs +++ b/rusl/src/futex.rs @@ -23,7 +23,7 @@ pub fn futex_wait( val, timeout .as_ref() - .map_or_else(core::ptr::null, |ts| ts as *const TimeSpec), + .map_or_else(core::ptr::null, core::ptr::from_ref::), 0, 0 ) diff --git a/rusl/src/io_uring/test.rs b/rusl/src/io_uring/test.rs index a41d401..280e6ae 100644 --- a/rusl/src/io_uring/test.rs +++ b/rusl/src/io_uring/test.rs @@ -7,16 +7,15 @@ use crate::io_uring::{ io_uring_enter, io_uring_register_buffers, io_uring_register_files, io_uring_register_io_slices, io_uring_setup, setup_io_uring, }; -use crate::network::{bind, connect, listen, socket}; use crate::platform::{ AddressFamily, Fd, IoSlice, IoSliceMut, IoUring, IoUringCompletionQueueEntry, IoUringEnterFlags, IoUringParamFlags, IoUringParams, IoUringSQEFlags, - IoUringSubmissionQueueEntry, Mode, NonNegativeI32, OpenFlags, RenameFlags, SocketAddress, + IoUringSubmissionQueueEntry, Mode, OpenFlags, PollAddMultiFlags, PollEvents, RenameFlags, SocketFlags, SocketOptions, SocketType, StatxFlags, StatxMask, TimeSpec, STDERR, STDIN, STDOUT, }; use crate::string::unix_str::UnixStr; use crate::time::clock_get_monotonic_time; -use crate::unistd::{close, open, open_mode, read, stat, unlink, unlink_flags, UnlinkFlags}; +use crate::unistd::{close, open, open_mode, read, stat, unlink_flags, UnlinkFlags}; #[test] fn uring_setup() { @@ -364,7 +363,10 @@ fn uring_single_socket() { } #[test] -fn uring_single_accept() { +#[cfg(feature = "alloc")] +#[allow(clippy::cast_sign_loss, clippy::too_many_lines)] +fn uring_unix_accept_send_recv() { + use crate::network::{bind_unix, connect_unix, listen, socket}; let Some(mut uring) = setup_ignore_enosys(8, IoUringParamFlags::empty()) else { return; }; @@ -376,20 +378,25 @@ fn uring_single_accept() { .unwrap(); let sock_path = unsafe { UnixStr::from_str_unchecked("test-files/io_uring/test-sock-accept\0") }; - let addr = SocketAddress::try_from_unix(sock_path).unwrap(); + let addr = crate::platform::SocketAddressUnix::try_from_unix(sock_path).unwrap(); // Ensure socket doesn't exist before test if let Err(e) = stat(sock_path) { assert_eq!(Errno::ENOENT, e.code.unwrap()); } else { - unlink(sock_path).unwrap(); + crate::unistd::unlink(sock_path).unwrap(); } - bind(server_socket, &addr).unwrap(); - listen(server_socket, NonNegativeI32::comptime_checked_new(100)).unwrap(); + bind_unix(server_socket, &addr).unwrap(); + listen( + server_socket, + crate::platform::NonNegativeI32::comptime_checked_new(100), + ) + .unwrap(); let user_data = 10011; let entry = unsafe { - IoUringSubmissionQueueEntry::new_accept( + IoUringSubmissionQueueEntry::new_accept_unix( server_socket, - &addr, + core::ptr::null_mut(), + core::ptr::null_mut(), SocketFlags::SOCK_CLOEXEC | SocketFlags::SOCK_NONBLOCK, user_data, // Run as async since we know we won't be able to connect yet @@ -408,11 +415,142 @@ fn uring_single_accept() { 0, ) .unwrap(); - connect(conn_sock, &addr).unwrap(); + connect_unix(conn_sock, &addr).unwrap(); io_uring_enter(uring.fd, 0, 1, IoUringEnterFlags::IORING_ENTER_GETEVENTS).unwrap(); let cqe = uring.get_next_cqe().unwrap(); assert_eq!(user_data, cqe.0.user_data, "Bad user data in cqe {cqe:?}"); assert!(cqe.0.res >= 0, "Failed res for cqe: {cqe:?}"); + let client_to_server_socket = Fd::try_new(cqe.0.res).unwrap(); + let next_slot = uring.get_next_sqe_slot().unwrap(); + let msg_content = b"Ping!"; + let msg_fd = open( + unix_lit!("test-files/io_uring/uring_recv_send_pass_fd.txt"), + OpenFlags::O_RDONLY, + ) + .unwrap(); + let mut file_buf_orig = [0u8; 64]; + read(msg_fd, &mut file_buf_orig).unwrap(); + let msg_fds = [msg_fd]; + unsafe { + let io = [IoSlice::new(msg_content)]; + let msg = crate::platform::MsgHdrBorrow::create_send( + None, + &io, + Some(crate::platform::ControlMessageSend::ScmRights(&msg_fds)), + ); + *next_slot = IoUringSubmissionQueueEntry::new_sendmsg( + conn_sock, + &msg, + 0, + 99, + IoUringSQEFlags::empty(), + ); + uring.flush_submission_queue(); + io_uring_enter(uring.fd, 1, 1, IoUringEnterFlags::IORING_ENTER_GETEVENTS).unwrap(); + let cqe = uring.get_next_cqe().unwrap(); + assert_eq!(msg_content.len(), cqe.0.res as usize); + assert_eq!(99, cqe.0.user_data); + let next_slot = uring.get_next_sqe_slot().unwrap(); + let mut buf = [0u8; 32]; + let mut ctrl_buf = [0u8; 126]; + let mut iov = [IoSliceMut::new(&mut buf)]; + let mut recv_hdr = + crate::platform::MsgHdrBorrow::create_recv(&mut iov, Some(&mut ctrl_buf)); + *next_slot = IoUringSubmissionQueueEntry::new_recvmsg( + client_to_server_socket, + core::ptr::addr_of_mut!(recv_hdr).cast(), + 0, + 999, + IoUringSQEFlags::empty(), + ); + uring.flush_submission_queue(); + io_uring_enter(uring.fd, 1, 1, IoUringEnterFlags::IORING_ENTER_GETEVENTS).unwrap(); + let cqe = uring.get_next_cqe().unwrap(); + assert!(cqe.0.res > 0, "Got error result {}", cqe.0.res); + assert_eq!(msg_content.len(), cqe.0.res as usize); + assert_eq!(b"Ping!", &buf[..5]); + let mut ctrl = recv_hdr.control_messages(); + let fd = ctrl.next().unwrap(); + assert!(ctrl.next().is_none()); + match fd { + crate::platform::ControlMessageSend::ScmRights(fds) => { + assert_eq!(1, fds.len()); + assert!(msg_fd < fds[0]); + // Since it's the same fd a reseek is necessary here + let off = crate::unistd::lseek(fds[0], 0, crate::unistd::Whence::SET).unwrap(); + assert_eq!(0, off); + let mut file_buf_passed = [0u8; 64]; + read(fds[0], &mut file_buf_passed).unwrap(); + assert_eq!(file_buf_orig, file_buf_passed); + let expect = b"Text content!\n"; + assert_eq!(expect, &file_buf_passed[..14]); + } + } + } +} + +#[test] +#[cfg(feature = "alloc")] +#[allow(clippy::cast_sign_loss, clippy::too_many_lines)] +fn uring_tcp_accept() { + use crate::network::{bind_inet, listen, socket}; + const FIFTEEN: crate::platform::NonNegativeI32 = + crate::platform::NonNegativeI32::comptime_checked_new(15); + let Some(mut uring) = setup_ignore_enosys(8, IoUringParamFlags::empty()) else { + return; + }; + // Dynamically assign port + let mut addr = crate::platform::SocketAddressInet::new([0, 0, 0, 0], 0); + let srv_sock = socket( + AddressFamily::AF_INET, + SocketOptions::new(SocketType::SOCK_STREAM, SocketFlags::SOCK_CLOEXEC), + 6, + ) + .unwrap(); + bind_inet(srv_sock, &addr).unwrap(); + // Get port + let sockname = crate::network::get_inet_sock_name(srv_sock).unwrap(); + addr.0.sin_port = sockname.0.sin_port; + listen(srv_sock, FIFTEEN).unwrap(); + let user_data = 10012; + let entry = unsafe { + IoUringSubmissionQueueEntry::new_accept_inet( + srv_sock, + core::ptr::null_mut(), + core::ptr::null_mut(), + SocketFlags::SOCK_CLOEXEC | SocketFlags::SOCK_NONBLOCK, + user_data, + // Run as async since we know we won't be able to connect yet + IoUringSQEFlags::IOSQE_ASYNC, + ) + }; + // We actually have to handle this async since we're on a single thread and accept will block + // for a connect + let next_slot = uring.get_next_sqe_slot().unwrap(); + unsafe { next_slot.write(entry) } + uring.flush_submission_queue(); + io_uring_enter(uring.fd, 1, 0, IoUringEnterFlags::empty()).unwrap(); + let conn_sock = socket( + AddressFamily::AF_INET, + SocketOptions::new(SocketType::SOCK_STREAM, SocketFlags::SOCK_CLOEXEC), + 6, + ) + .unwrap(); + crate::network::connect_inet(conn_sock, &addr).unwrap(); + io_uring_enter(uring.fd, 0, 1, IoUringEnterFlags::IORING_ENTER_GETEVENTS).unwrap(); + let cqe = uring.get_next_cqe().unwrap(); + assert_eq!(user_data, cqe.0.user_data, "Bad user data in cqe {cqe:?}"); + assert!(cqe.0.res >= 0, "Failed res for cqe: {cqe:?}"); + let server_to_client_socket = Fd::try_new(cqe.0.res).unwrap(); + let msg_content = b"Ping!"; + crate::unistd::write(server_to_client_socket, msg_content).unwrap(); + let mut ret_buf = [0u8; 5]; + crate::unistd::read(conn_sock, &mut ret_buf).unwrap(); + assert_eq!(&ret_buf, msg_content); + // Todo: Better cleanup on test failures + let _ = crate::unistd::close(server_to_client_socket); + let _ = crate::unistd::close(conn_sock); + let _ = crate::unistd::close(srv_sock); } fn write_await_single_entry( @@ -781,3 +919,37 @@ fn uring_multi_linked_crud() { } } } + +#[test] +fn poll_add() { + let Some(mut uring) = setup_ignore_enosys(8, IoUringParamFlags::empty()) else { + return; + }; + let poll_file_path = unix_lit!("test-files/io_uring/tmp_uring_poll_add_1.txt"); + let poll_fd = open_mode( + poll_file_path, + OpenFlags::O_CREAT | OpenFlags::O_RDWR, + Mode::S_IRUSR | Mode::S_IWUSR | Mode::S_IRGRP | Mode::S_IROTH, + ) + .unwrap(); + unsafe { + let sqe = uring.get_next_sqe_slot().unwrap(); + *sqe = IoUringSubmissionQueueEntry::new_poll_add( + poll_fd, + PollEvents::POLLIN | PollEvents::POLLOUT, + PollAddMultiFlags::empty(), + 0, + IoUringSQEFlags::empty(), + ); + } + uring.flush_submission_queue(); + assert_eq!( + 1, + io_uring_enter(uring.fd, 1, 1, IoUringEnterFlags::IORING_ENTER_GETEVENTS).unwrap() + ); + let next = uring.get_next_cqe().unwrap(); + assert_eq!( + next.0.res, + i32::from((PollEvents::POLLIN | PollEvents::POLLOUT).0) + ); +} diff --git a/rusl/src/network.rs b/rusl/src/network.rs index 22f030f..4b97f51 100644 --- a/rusl/src/network.rs +++ b/rusl/src/network.rs @@ -1,11 +1,17 @@ -pub use accept::accept; -pub use bind::bind; -pub use connect::connect; +pub use accept::{accept_inet, accept_unix}; +pub use bind::{bind_inet, bind_unix}; +pub use connect::{connect_inet, connect_unix}; pub use listen::listen; -pub use socket::socket; + +#[cfg(feature = "alloc")] +pub use socket::{get_inet_sock_name, get_unix_sock_name, recvmsg, sendmsg, socket}; +#[cfg(not(feature = "alloc"))] +pub use socket::{get_inet_sock_name, get_unix_sock_name, socket}; mod accept; mod bind; mod connect; mod listen; mod socket; +#[cfg(all(test, feature = "alloc"))] +mod test; diff --git a/rusl/src/network/accept.rs b/rusl/src/network/accept.rs index 55e21e9..e611a10 100644 --- a/rusl/src/network/accept.rs +++ b/rusl/src/network/accept.rs @@ -1,19 +1,57 @@ +use core::mem::MaybeUninit; use sc::syscall; -use crate::platform::{Fd, SocketArg, SocketFlags}; +use crate::platform::{Fd, SocketAddressInet, SocketAddressUnix, SocketArgUnix, SocketFlags}; use crate::Result; -/// Accept a new connection and set flags on the new connection's `Fd` +/// Accept a new unix-connection and set flags on the new connection's `Fd` /// Accepted flags are 0, `SOCK_NONBLOCK` an `SOCK_CLOEXEC` -/// The `socket_address` is the peer address, if applicable /// See [Linux documentation for more details](https://man7.org/linux/man-pages/man2/accept.2.html) /// # Errors /// See above #[inline] -pub fn accept(sock_fd: Fd, socket_address: Option<&SocketArg>, flags: SocketFlags) -> Result { - let (addr, addr_len) = socket_address.map_or((0, 0), |addr| { - (core::ptr::addr_of!(addr.addr) as usize, addr.addr_len) - }); - let res = unsafe { syscall!(ACCEPT4, sock_fd.0, addr, addr_len, flags.0) }; - Fd::coerce_from_register(res, "`ACCEPT4` syscall failed") +pub fn accept_unix(sock_fd: Fd, flags: SocketFlags) -> Result<(Fd, SocketArgUnix)> { + let mut addr = MaybeUninit::zeroed(); + let mut addr_len = core::mem::size_of::(); + let res = unsafe { + syscall!( + ACCEPT4, + sock_fd.0, + core::ptr::addr_of_mut!(addr), + core::ptr::addr_of_mut!(addr_len), + flags.0 + ) + }; + let fd = Fd::coerce_from_register(res, "`ACCEPT4` syscall failed")?; + unsafe { + Ok(( + fd, + SocketArgUnix { + addr: addr.assume_init(), + addr_len, + }, + )) + } +} + +/// Accept a new tcp-connection and set flags on the new connection's `Fd` +/// Accepted flags are 0, `SOCK_NONBLOCK` an `SOCK_CLOEXEC` +/// See [Linux documentation for more details](https://man7.org/linux/man-pages/man2/accept.2.html) +/// # Errors +/// See above +#[inline] +pub fn accept_inet(sock_fd: Fd, flags: SocketFlags) -> Result<(Fd, SocketAddressInet)> { + let mut addr = MaybeUninit::zeroed(); + let mut addr_len = core::mem::size_of::(); + let res = unsafe { + syscall!( + ACCEPT4, + sock_fd.0, + core::ptr::addr_of_mut!(addr), + core::ptr::addr_of_mut!(addr_len), + flags.0 + ) + }; + let fd = Fd::coerce_from_register(res, "`ACCEPT4` syscall failed")?; + unsafe { Ok((fd, addr.assume_init())) } } diff --git a/rusl/src/network/bind.rs b/rusl/src/network/bind.rs index 434dfff..bce4a46 100644 --- a/rusl/src/network/bind.rs +++ b/rusl/src/network/bind.rs @@ -1,15 +1,15 @@ use sc::syscall; -use crate::platform::{Fd, SocketArg}; +use crate::platform::{Fd, SocketAddressInet, SocketArgUnix}; use crate::Result; -/// Bind the socket with the fd `sock_fd` to the address `socket_address` +/// Bind the unix-socket with the fd `sock_fd` to the address `socket_address` /// See the [Linux documentation for details](https://man7.org/linux/man-pages/man2/connect.2.html) /// Similar to `connect` but on the 'server'-side /// # Errors /// See above #[inline] -pub fn bind(sock_fd: Fd, socket_address: &SocketArg) -> Result<()> { +pub fn bind_unix(sock_fd: Fd, socket_address: &SocketArgUnix) -> Result<()> { let res = unsafe { syscall!( BIND, @@ -21,3 +21,21 @@ pub fn bind(sock_fd: Fd, socket_address: &SocketArg) -> Result<()> { bail_on_below_zero!(res, "`BIND` syscall failed"); Ok(()) } + +/// Bind the tcp-socket with the fd `sock_fd` to the address `socket_address` +/// See the [Linux documentation for details](https://man7.org/linux/man-pages/man2/connect.2.html) +/// Similar to `connect` but on the 'server'-side +/// # Errors +/// See above +pub fn bind_inet(sock_fd: Fd, socket_address_inet: &SocketAddressInet) -> Result<()> { + let res = unsafe { + syscall!( + BIND, + sock_fd.0, + socket_address_inet as *const SocketAddressInet, + SocketAddressInet::LENGTH + ) + }; + bail_on_below_zero!(res, "`BIND` syscall failed"); + Ok(()) +} diff --git a/rusl/src/network/connect.rs b/rusl/src/network/connect.rs index 7b89461..132b27f 100644 --- a/rusl/src/network/connect.rs +++ b/rusl/src/network/connect.rs @@ -1,15 +1,15 @@ use sc::syscall; use crate::error::Result; -use crate::platform::{Fd, SocketArg}; +use crate::platform::{Fd, SocketAddressInet, SocketArgUnix}; -/// Connect the socket with the fd `sock_fd` to the address `socket_address` +/// Connect the unix-socket with the fd `sock_fd` to the address `socket_address` /// See the [Linux documentation for details](https://man7.org/linux/man-pages/man2/connect.2.html) /// Similar to `bind` but on the 'client'-side /// # Errors /// See above #[inline] -pub fn connect(sock_fd: Fd, socket_address: &SocketArg) -> Result<()> { +pub fn connect_unix(sock_fd: Fd, socket_address: &SocketArgUnix) -> Result<()> { let res = unsafe { syscall!( CONNECT, @@ -21,3 +21,22 @@ pub fn connect(sock_fd: Fd, socket_address: &SocketArg) -> Result<()> { bail_on_below_zero!(res, "`CONNECT` syscall failed"); Ok(()) } + +/// Connect the tcp-socket with the fd `sock_fd` to the address `socket_address` +/// See the [Linux documentation for details](https://man7.org/linux/man-pages/man2/connect.2.html) +/// Similar to `bind` but on the 'client'-side +/// # Errors +/// See above +#[inline] +pub fn connect_inet(sock_fd: Fd, addr: &SocketAddressInet) -> Result<()> { + let res = unsafe { + syscall!( + CONNECT, + sock_fd.0, + addr as *const SocketAddressInet, + SocketAddressInet::LENGTH + ) + }; + bail_on_below_zero!(res, "`CONNECT` syscall failed"); + Ok(()) +} diff --git a/rusl/src/network/socket.rs b/rusl/src/network/socket.rs index 4864add..6fda8d7 100644 --- a/rusl/src/network/socket.rs +++ b/rusl/src/network/socket.rs @@ -1,6 +1,9 @@ +use core::mem::MaybeUninit; use sc::syscall; -use crate::platform::{AddressFamily, Fd, SocketOptions}; +use crate::platform::{ + AddressFamily, Fd, SocketAddressInet, SocketAddressUnix, SocketArgUnix, SocketOptions, +}; use crate::Result; /// Create a socket with the specified `Domain`, `SocketType`, and `protocol` @@ -12,3 +15,75 @@ pub fn socket(domain: AddressFamily, options: SocketOptions, protocol: i32) -> R let res = unsafe { syscall!(SOCKET, domain.0, options.0, protocol) }; Fd::coerce_from_register(res, "`SOCKET` syscall failed") } + +/// Get the socket name of the provided Unix socket [`Fd`]. +/// See [Linux docs for details](https://man7.org/linux/man-pages/man2/getsockname.2.html) +/// # Errors +/// See above +pub fn get_unix_sock_name(sock_fd: Fd) -> Result { + let mut addr = MaybeUninit::zeroed(); + let mut addr_len = core::mem::size_of::(); + let res = unsafe { + syscall!( + GETSOCKNAME, + sock_fd.into_usize(), + core::ptr::addr_of_mut!(addr), + core::ptr::addr_of_mut!(addr_len) + ) + }; + bail_on_below_zero!(res, "`GETSOCKNAME` syscall failed"); + unsafe { + Ok(SocketArgUnix { + addr: addr.assume_init(), + addr_len, + }) + } +} + +/// Get the socket name of the provided Inet socket [`Fd`]. +/// See [Linux docs for details](https://man7.org/linux/man-pages/man2/getsockname.2.html) +/// # Errors +/// See above +pub fn get_inet_sock_name(sock_fd: Fd) -> Result { + let mut addr = MaybeUninit::zeroed(); + let mut addr_len = core::mem::size_of::(); + let res = unsafe { + syscall!( + GETSOCKNAME, + sock_fd.into_usize(), + core::ptr::addr_of_mut!(addr), + core::ptr::addr_of_mut!(addr_len) + ) + }; + bail_on_below_zero!(res, "`GETSOCKNAME` syscall failed"); + unsafe { Ok(addr.assume_init()) } +} + +/// Send a message on a socket, [`crate::unistd::write`] should be prefered if not sending fds. +/// See [linux docs for details](https://man7.org/linux/man-pages/man2/send.2.html) +/// # Errors +/// See above +#[cfg(feature = "alloc")] +pub fn sendmsg(sock_fd: Fd, send: &crate::platform::SendDropGuard, flags: i32) -> Result { + let res = unsafe { syscall!(SENDMSG, sock_fd.0, core::ptr::addr_of!(send.msghdr), flags) }; + bail_on_below_zero!(res, "`SENDMSG` syscall failed"); + Ok(res) +} + +/// Read a message from a socket, [`crate::unistd::read`] should be prefered if not expecting to receive fds. +/// See [linux docs for details](https://man7.org/linux/man-pages/man2/recv.2.html) +/// # Errors +/// See above +#[cfg(feature = "alloc")] +pub fn recvmsg(sock_fd: Fd, recv: &mut crate::platform::MsgHdrBorrow, flags: i32) -> Result { + let res = unsafe { + syscall!( + RECVMSG, + sock_fd.0, + recv as *mut crate::platform::MsgHdrBorrow, + flags + ) + }; + bail_on_below_zero!(res, "`RECVMSG` syscall failed"); + Ok(res) +} diff --git a/rusl/src/network/test.rs b/rusl/src/network/test.rs new file mode 100644 index 0000000..0c9a285 --- /dev/null +++ b/rusl/src/network/test.rs @@ -0,0 +1,280 @@ +use crate::error::Errno; +use crate::network::{get_inet_sock_name, get_unix_sock_name}; +use crate::platform::{ + AddressFamily, ControlMessageSend, IoSlice, IoSliceMut, MsgHdrBorrow, NonNegativeI32, + OpenFlags, PollEvents, PollFd, SocketAddressInet, SocketAddressUnix, SocketFlags, + SocketOptions, SocketType, +}; +use crate::unistd::{close, open, unlink}; +use core::sync::atomic::{AtomicBool, Ordering}; +use core::time::Duration; + +#[test] +fn get_dynamic_inet_sock_name() { + let srv_sock = super::socket( + AddressFamily::AF_INET, + SocketOptions::new(SocketType::SOCK_STREAM, SocketFlags::SOCK_CLOEXEC), + 6, + ) + .unwrap(); + let addr = SocketAddressInet::new([0, 0, 0, 0], 0); + super::bind_inet(srv_sock, &addr).unwrap(); + // Dynamic port should have changed + let assigned = get_inet_sock_name(srv_sock).unwrap(); + assert_ne!(addr.0.sin_port, assigned.0.sin_port); + let _ = close(srv_sock); +} + +#[test] +fn read_unix_sock_name() { + let addr_raw = unix_lit!("test-files/socket/unix-sockname-test"); + let _ = unlink(addr_raw); + let srv_sock = super::socket( + AddressFamily::AF_UNIX, + SocketOptions::new(SocketType::SOCK_STREAM, SocketFlags::SOCK_CLOEXEC), + 0, + ) + .unwrap(); + let addr = SocketAddressUnix::try_from_unix(addr_raw).unwrap(); + super::bind_unix(srv_sock, &addr).unwrap(); + // Should be the same path as the socket address supplied to bind + let assigned = get_unix_sock_name(srv_sock).unwrap(); + assert_eq!(addr.addr.0.sun_path, assigned.addr.0.sun_path); + assert_eq!(addr.addr.0.sun_family, assigned.addr.0.sun_family); + assert_eq!(addr.addr_len, assigned.addr_len); + let _ = close(srv_sock); +} + +#[test] +fn send_recv_msg() { + const FIFTEEN: NonNegativeI32 = NonNegativeI32::comptime_checked_new(15); + let addr_raw = unix_lit!("test-files/socket/tmp-recvmsg"); + let _ = unlink(addr_raw); + let srv_sock = super::socket( + AddressFamily::AF_UNIX, + SocketOptions::new(SocketType::SOCK_STREAM, SocketFlags::SOCK_CLOEXEC), + 0, + ) + .unwrap(); + let addr = SocketAddressUnix::try_from_unix(addr_raw).unwrap(); + super::bind_unix(srv_sock, &addr).unwrap(); + let cl_sock = super::socket( + AddressFamily::AF_UNIX, + SocketOptions::new(SocketType::SOCK_STREAM, SocketFlags::SOCK_CLOEXEC), + 0, + ) + .unwrap(); + //let server_to_client_con = std::sync::Arc::new(std::sync::Mutex::new(None)); + //let stc_clone = server_to_client_con.clone(); + let listening = std::sync::Arc::new(AtomicBool::new(false)); + let lc = listening.clone(); + let listen_thread = std::thread::spawn(move || { + super::listen(srv_sock, FIFTEEN).unwrap(); + lc.store(true, Ordering::SeqCst); + let client = super::accept_unix(srv_sock, SocketFlags::SOCK_CLOEXEC) + .unwrap() + .0; + let mut poll_fds = [PollFd::new(client, PollEvents::POLLIN)]; + crate::select::ppoll(&mut poll_fds, None, None).unwrap(); + assert_eq!(PollEvents::POLLIN, poll_fds[0].received_events()); + let mut space = [0u8; 64]; + let io = &mut [IoSliceMut::new(&mut space)]; + let mut ctrl_space = [0u8; 64]; + let mut hdr = MsgHdrBorrow::create_recv(io, Some(&mut ctrl_space)); + let re = super::recvmsg(client, &mut hdr, 0).unwrap(); + assert_eq!(5, re); + assert_eq!(b"Hello", &space[..5]); + let mut ctrl = hdr.control_messages(); + assert!(ctrl.next().is_none()); + }); + while !listening.load(Ordering::SeqCst) {} + super::connect_unix(cl_sock, &addr).unwrap(); + let io_out = &[IoSlice::new(b"Hello")]; + let snd = MsgHdrBorrow::create_send(None, io_out, None); + let send = super::sendmsg(cl_sock, &snd, 0).unwrap(); + assert_eq!(5, send); + listen_thread.join().unwrap(); +} +#[test] +fn send_recv_msg_with_control_single() { + const FIFTEEN: NonNegativeI32 = NonNegativeI32::comptime_checked_new(15); + let addr_raw = unix_lit!("test-files/socket/tmp-recvmsg-crtl"); + let _ = unlink(addr_raw); + let srv_sock = super::socket( + AddressFamily::AF_UNIX, + SocketOptions::new(SocketType::SOCK_STREAM, SocketFlags::SOCK_CLOEXEC), + 0, + ) + .unwrap(); + let addr = SocketAddressUnix::try_from_unix(addr_raw).unwrap(); + super::bind_unix(srv_sock, &addr).unwrap(); + let cl_sock = super::socket( + AddressFamily::AF_UNIX, + SocketOptions::new(SocketType::SOCK_STREAM, SocketFlags::SOCK_CLOEXEC), + 0, + ) + .unwrap(); + //let server_to_client_con = std::sync::Arc::new(std::sync::Mutex::new(None)); + //let stc_clone = server_to_client_con.clone(); + let listening = std::sync::Arc::new(AtomicBool::new(false)); + let lc = listening.clone(); + let fd1 = open(unix_lit!("/proc/mounts"), OpenFlags::O_RDONLY).unwrap(); + let fds = [fd1]; + let listen_thread = std::thread::spawn(move || { + super::listen(srv_sock, FIFTEEN).unwrap(); + lc.store(true, Ordering::SeqCst); + let client = super::accept_unix(srv_sock, SocketFlags::SOCK_CLOEXEC) + .unwrap() + .0; + let mut poll_fds = [PollFd::new(client, PollEvents::POLLIN)]; + crate::select::ppoll(&mut poll_fds, None, None).unwrap(); + assert_eq!(PollEvents::POLLIN, poll_fds[0].received_events()); + let mut space = [0u8; 64]; + let io = &mut [IoSliceMut::new(&mut space)]; + let mut ctrl_space = [0u8; 64]; + let mut hdr = MsgHdrBorrow::create_recv(io, Some(&mut ctrl_space)); + let re = super::recvmsg(client, &mut hdr, 0).unwrap(); + assert_eq!(5, re); + assert_eq!(b"Hello", &space[..5]); + let mut ctrl = hdr.control_messages(); + let scm_next = ctrl.next().unwrap(); + match scm_next { + ControlMessageSend::ScmRights(recv) => { + // Sending the fds over is in this case, since it's the same process, equivalent to a dup-call. It's the same file but different fds. + // If it would have been the exact same, this would be a failure, just data serialization not actual fd-passing. + assert_eq!(1, recv.len()); + assert!(recv[0] > fds[0]); + } + } + assert!(ctrl.next().is_none()); + }); + while !listening.load(Ordering::SeqCst) {} + super::connect_unix(cl_sock, &addr).unwrap(); + let io_out = &[IoSlice::new(b"Hello")]; + let snd = MsgHdrBorrow::create_send(None, io_out, Some(ControlMessageSend::ScmRights(&fds))); + let send = super::sendmsg(cl_sock, &snd, 0).unwrap(); + assert_eq!(5, send); + listen_thread.join().unwrap(); +} + +#[test] +fn send_recv_msg_with_control_multi() { + const FIFTEEN: NonNegativeI32 = NonNegativeI32::comptime_checked_new(15); + let addr_raw = unix_lit!("test-files/socket/tmp-recvmsg-crtl"); + let _ = unlink(addr_raw); + let srv_sock = super::socket( + AddressFamily::AF_UNIX, + SocketOptions::new(SocketType::SOCK_STREAM, SocketFlags::SOCK_CLOEXEC), + 0, + ) + .unwrap(); + let addr = SocketAddressUnix::try_from_unix(addr_raw).unwrap(); + super::bind_unix(srv_sock, &addr).unwrap(); + let cl_sock = super::socket( + AddressFamily::AF_UNIX, + SocketOptions::new(SocketType::SOCK_STREAM, SocketFlags::SOCK_CLOEXEC), + 0, + ) + .unwrap(); + //let server_to_client_con = std::sync::Arc::new(std::sync::Mutex::new(None)); + //let stc_clone = server_to_client_con.clone(); + let listening = std::sync::Arc::new(AtomicBool::new(false)); + let lc = listening.clone(); + let fd1 = open(unix_lit!("/proc/mounts"), OpenFlags::O_RDONLY).unwrap(); + let fd2 = open(unix_lit!("/proc/mounts"), OpenFlags::O_RDONLY).unwrap(); + let fds = [fd1, fd2]; + let listen_thread = std::thread::spawn(move || { + super::listen(srv_sock, FIFTEEN).unwrap(); + lc.store(true, Ordering::SeqCst); + let client = super::accept_unix(srv_sock, SocketFlags::SOCK_CLOEXEC) + .unwrap() + .0; + let mut poll_fds = [PollFd::new(client, PollEvents::POLLIN)]; + crate::select::ppoll(&mut poll_fds, None, None).unwrap(); + assert_eq!(PollEvents::POLLIN, poll_fds[0].received_events()); + let mut space = [0u8; 64]; + let io = &mut [IoSliceMut::new(&mut space)]; + let mut ctrl_space = [0u8; 64]; + let mut hdr = MsgHdrBorrow::create_recv(io, Some(&mut ctrl_space)); + let re = super::recvmsg(client, &mut hdr, 0).unwrap(); + assert_eq!(5, re); + assert_eq!(b"Hello", &space[..5]); + let mut ctrl = hdr.control_messages(); + let scm_next = ctrl.next().unwrap(); + match scm_next { + ControlMessageSend::ScmRights(recv) => { + // Sending the fds over is in this case, since it's the same process, equivalent to a dup-call. It's the same file but different fds. + // If it would have been the exact same, this would be a failure, just data serialization not actual fd-passing. + assert_eq!(2, recv.len()); + assert!(recv[1] > recv[0]); + assert!(recv[0] > fds[1]); + } + } + assert!(ctrl.next().is_none()); + }); + while !listening.load(Ordering::SeqCst) {} + super::connect_unix(cl_sock, &addr).unwrap(); + let io_out = &[IoSlice::new(b"Hello")]; + let snd = MsgHdrBorrow::create_send(None, io_out, Some(ControlMessageSend::ScmRights(&fds))); + let send = super::sendmsg(cl_sock, &snd, 0).unwrap(); + assert_eq!(5, send); + listen_thread.join().unwrap(); +} + +#[test] +fn test_tcp() { + const FIFTEEN: NonNegativeI32 = NonNegativeI32::comptime_checked_new(15); + const EXPECT_REQ: &[u8] = b"Hello server!"; + const EXPECT_RES: &[u8] = b"Hello client!"; + let srv_sock = super::socket( + AddressFamily::AF_INET, + SocketOptions::new(SocketType::SOCK_STREAM, SocketFlags::SOCK_CLOEXEC), + 6, + ) + .unwrap(); + let mut addr = SocketAddressInet::new([0, 0, 0, 0], 0); + super::bind_inet(srv_sock, &addr).unwrap(); + // Get dynamically assigned port + addr.0.sin_port = get_inet_sock_name(srv_sock).unwrap().0.sin_port; + super::listen(srv_sock, FIFTEEN).unwrap(); + let _srv = std::thread::spawn(move || { + let (fd, _addr) = super::accept_inet(srv_sock, SocketFlags::SOCK_CLOEXEC).unwrap(); + let mut buf = [0u8; EXPECT_REQ.len()]; + let mut read = 0; + while read < EXPECT_REQ.len() { + let count = crate::unistd::read(fd, &mut buf); + match count { + Ok(bytes) => { + read += bytes; + } + Err(e) => { + if e.code.unwrap() == Errno::EAGAIN { + continue; + } + panic!("Error reading {e}"); + } + } + } + assert_eq!(EXPECT_REQ, &buf); + let written = crate::unistd::write(fd, EXPECT_RES).unwrap(); + assert_eq!(EXPECT_RES.len(), written); + }); + std::thread::sleep(Duration::from_millis(10)); + let clnt = super::socket( + AddressFamily::AF_INET, + SocketOptions::new(SocketType::SOCK_STREAM, SocketFlags::SOCK_CLOEXEC), + 6, + ) + .unwrap(); + super::connect_inet(clnt, &addr).unwrap(); + assert_eq!( + EXPECT_REQ.len(), + crate::unistd::write(clnt, EXPECT_REQ).unwrap() + ); + let mut buf = [0u8; EXPECT_RES.len()]; + assert_eq!( + EXPECT_RES.len(), + crate::unistd::read(clnt, &mut buf).unwrap() + ); + assert_eq!(EXPECT_RES, buf); +} diff --git a/rusl/src/platform/compat/io_uring.rs b/rusl/src/platform/compat/io_uring.rs index 695f8fc..1b1bc24 100644 --- a/rusl/src/platform/compat/io_uring.rs +++ b/rusl/src/platform/compat/io_uring.rs @@ -11,9 +11,9 @@ use linux_rust_bindings::io_uring::{ }; use crate::platform::{ - comptime_i32_to_u32, comptime_u32_to_u8, AddressFamily, Fd, Mode, OpenFlags, RenameFlags, - SocketArg, SocketFlags, SocketOptions, Statx, StatxFlags, StatxMask, TimeSpec, AT_FDCWD, - AT_REMOVEDIR, + comptime_i32_to_u32, comptime_u32_to_u8, AddressFamily, Fd, Mode, OpenFlags, PollEvents, + RenameFlags, SocketAddressInet, SocketAddressUnix, SocketArgUnix, SocketFlags, SocketOptions, + Statx, StatxFlags, StatxMask, TimeSpec, AT_FDCWD, AT_REMOVEDIR, }; use crate::string::unix_str::UnixStr; use crate::unistd::munmap; @@ -90,7 +90,7 @@ pub enum IoUringOp { ReadFixed = comptime_u32_to_u8(linux_rust_bindings::io_uring::io_uring_op_IORING_OP_READ_FIXED), WriteFixed = comptime_u32_to_u8(linux_rust_bindings::io_uring::io_uring_op_IORING_OP_WRITE_FIXED), - PollFdd = comptime_u32_to_u8(linux_rust_bindings::io_uring::io_uring_op_IORING_OP_POLL_ADD), + PollAdd = comptime_u32_to_u8(linux_rust_bindings::io_uring::io_uring_op_IORING_OP_POLL_ADD), PollRemove = comptime_u32_to_u8(linux_rust_bindings::io_uring::io_uring_op_IORING_OP_POLL_REMOVE), SyncFileRange = @@ -144,6 +144,15 @@ pub enum IoUringOp { Last = comptime_u32_to_u8(linux_rust_bindings::io_uring::io_uring_op_IORING_OP_LAST), } +transparent_bitflags! { + pub struct PollAddMultiFlags: u32 { + const DEFAULT = 0; + const ADD_MULTI = comptime_i32_to_u32(linux_rust_bindings::io_uring::IORING_POLL_ADD_MULTI); + const UPDATE_EVENTS = comptime_i32_to_u32(linux_rust_bindings::io_uring::IORING_POLL_UPDATE_EVENTS); + const UPDATE_USER_DATA = comptime_i32_to_u32(linux_rust_bindings::io_uring::IORING_POLL_UPDATE_USER_DATA); + } +} + #[repr(transparent)] pub struct IoUringSubmissionQueueEntry(pub io_uring_sqe); @@ -572,9 +581,9 @@ impl IoUringSubmissionQueueEntry { /// `sockaddr` needs to live until this entry is passed to the kernel #[inline] #[must_use] - pub unsafe fn new_connect( + pub unsafe fn new_connect_unix( socket: Fd, - sockaddr: &SocketArg, + sockaddr: &SocketArgUnix, user_data: u64, sqe_flags: IoUringSQEFlags, ) -> Self { @@ -605,12 +614,14 @@ impl IoUringSubmissionQueueEntry { /// Creates a new socket. Will execute an equivalent to an `accept4` syscall. /// # Safety - /// `sockaddr` needs to live until this entry is passed to the kernel + /// `sockaddr` and `addr_len` needs to live until the kernel has processed this accept call + /// (accepted a client and returned a CQE). #[inline] #[must_use] - pub unsafe fn new_accept( + pub unsafe fn new_accept_unix( socket: Fd, - sockaddr: &SocketArg, + sockaddr: *mut SocketAddressUnix, + addr_len: *mut u64, socket_flags: SocketFlags, user_data: u64, sqe_flags: IoUringSQEFlags, @@ -621,10 +632,51 @@ impl IoUringSubmissionQueueEntry { ioprio: 0, fd: socket.0, __bindgen_anon_1: io_uring_sqe__bindgen_ty_1 { - addr2: core::ptr::addr_of!(sockaddr.addr_len) as u64, + addr2: sockaddr as u64, }, __bindgen_anon_2: io_uring_sqe__bindgen_ty_2 { - addr: core::ptr::addr_of!(sockaddr.addr) as u64, + addr: addr_len as u64, + }, + len: 0, + __bindgen_anon_3: io_uring_sqe__bindgen_ty_3 { + accept_flags: socket_flags.0, + }, + user_data, + __bindgen_anon_4: io_uring_sqe__bindgen_ty_4 { buf_index: 0 }, + personality: 0, + __bindgen_anon_5: io_uring_sqe__bindgen_ty_5 { file_index: 0 }, + __bindgen_anon_6: io_uring_sqe__bindgen_ty_6 { + __bindgen_anon_1: __BindgenUnionField::default(), + cmd: __BindgenUnionField::default(), + bindgen_union_field: [0; 2], + }, + }) + } + + /// Accepts a new inet socket connection. Will execute an equivalent to an `accept4` syscall. + /// # Safety + /// `sockaddr` and `addr_len` needs to live until the kernel has processed this accept call + /// (accepted a client and returned a CQE). + #[inline] + #[must_use] + pub unsafe fn new_accept_inet( + socket: Fd, + sockaddr: *mut SocketAddressInet, + addr_len: *mut u64, + socket_flags: SocketFlags, + user_data: u64, + sqe_flags: IoUringSQEFlags, + ) -> Self { + Self(io_uring_sqe { + opcode: IoUringOp::Accept as u8, + flags: sqe_flags.bits(), + ioprio: 0, + fd: socket.0, + __bindgen_anon_1: io_uring_sqe__bindgen_ty_1 { + addr2: sockaddr as u64, + }, + __bindgen_anon_2: io_uring_sqe__bindgen_ty_2 { + addr: addr_len as u64, }, len: 0, __bindgen_anon_3: io_uring_sqe__bindgen_ty_3 { @@ -669,7 +721,7 @@ impl IoUringSubmissionQueueEntry { }, }, __bindgen_anon_2: io_uring_sqe__bindgen_ty_2 { - addr: ts as *const TimeSpec as u64, + addr: core::ptr::from_ref::(ts) as u64, }, len: 1, __bindgen_anon_3: io_uring_sqe__bindgen_ty_3 { @@ -690,6 +742,156 @@ impl IoUringSubmissionQueueEntry { }, }) } + + /// Create a new sendmsg SQE + /// # Safety + /// `send_drop_guard` needs to live until this SQE is processed by the kernel. + #[inline] + #[must_use] + #[cfg(feature = "alloc")] + #[allow(clippy::cast_sign_loss)] + pub unsafe fn new_sendmsg( + socket: Fd, + send_drop_guard: &crate::platform::SendDropGuard, + sendmsg_flags: i32, + user_data: u64, + sqe_flags: IoUringSQEFlags, + ) -> Self { + Self(io_uring_sqe { + opcode: IoUringOp::Sendmsg as u8, + flags: sqe_flags.bits(), + ioprio: 0, + fd: socket.value(), + __bindgen_anon_1: io_uring_sqe__bindgen_ty_1 { off: 0 }, + __bindgen_anon_2: io_uring_sqe__bindgen_ty_2 { + addr: core::ptr::addr_of!(send_drop_guard.msghdr) as u64, + }, + len: 0, + __bindgen_anon_3: io_uring_sqe__bindgen_ty_3 { + msg_flags: sendmsg_flags as u32, + }, + user_data, + __bindgen_anon_4: io_uring_sqe__bindgen_ty_4 { buf_index: 0 }, + personality: 0, + __bindgen_anon_5: io_uring_sqe__bindgen_ty_5 { file_index: 0 }, + __bindgen_anon_6: io_uring_sqe__bindgen_ty_6 { + __bindgen_anon_1: __BindgenUnionField::new(), + cmd: __BindgenUnionField::new(), + bindgen_union_field: [0; 2], + }, + }) + } + + /// Create a new raw pointer sendmsg SQE + /// # Safety + /// `msghdr` needs to live until this SQE is processed by the kernel. + #[inline] + #[must_use] + #[cfg(feature = "alloc")] + #[allow(clippy::cast_sign_loss)] + pub unsafe fn new_sendmsg_raw( + socket: Fd, + msghdr: *const crate::platform::MsgHdr, + sendmsg_flags: i32, + user_data: u64, + sqe_flags: IoUringSQEFlags, + ) -> Self { + Self(io_uring_sqe { + opcode: IoUringOp::Sendmsg as u8, + flags: sqe_flags.bits(), + ioprio: 0, + fd: socket.value(), + __bindgen_anon_1: io_uring_sqe__bindgen_ty_1 { off: 0 }, + __bindgen_anon_2: io_uring_sqe__bindgen_ty_2 { + addr: msghdr as u64, + }, + len: 0, + __bindgen_anon_3: io_uring_sqe__bindgen_ty_3 { + msg_flags: sendmsg_flags as u32, + }, + user_data, + __bindgen_anon_4: io_uring_sqe__bindgen_ty_4 { buf_index: 0 }, + personality: 0, + __bindgen_anon_5: io_uring_sqe__bindgen_ty_5 { file_index: 0 }, + __bindgen_anon_6: io_uring_sqe__bindgen_ty_6 { + __bindgen_anon_1: __BindgenUnionField::new(), + cmd: __BindgenUnionField::new(), + bindgen_union_field: [0; 2], + }, + }) + } + + /// Create a new sendmsg SQE + /// # Safety + /// Instant UB if the pointer, or the internal pointers in the pointed to struct become invalid + /// before the kernel has finished with the entry. + #[inline] + #[must_use] + #[cfg(feature = "alloc")] + #[allow(clippy::cast_sign_loss)] + pub unsafe fn new_recvmsg( + socket: Fd, + msghdr: *mut crate::platform::MsgHdr, + sendmsg_flags: i32, + user_data: u64, + sqe_flags: IoUringSQEFlags, + ) -> Self { + Self(io_uring_sqe { + opcode: IoUringOp::Recvmsg as u8, + flags: sqe_flags.bits(), + ioprio: 0, + fd: socket.value(), + __bindgen_anon_1: io_uring_sqe__bindgen_ty_1 { off: 0 }, + __bindgen_anon_2: io_uring_sqe__bindgen_ty_2 { + addr: msghdr as u64, + }, + len: 0, + __bindgen_anon_3: io_uring_sqe__bindgen_ty_3 { + msg_flags: sendmsg_flags as u32, + }, + user_data, + __bindgen_anon_4: io_uring_sqe__bindgen_ty_4 { buf_index: 0 }, + personality: 0, + __bindgen_anon_5: io_uring_sqe__bindgen_ty_5 { file_index: 0 }, + __bindgen_anon_6: io_uring_sqe__bindgen_ty_6 { + __bindgen_anon_1: __BindgenUnionField::new(), + cmd: __BindgenUnionField::new(), + bindgen_union_field: [0; 2], + }, + }) + } + + #[must_use] + #[allow(clippy::cast_sign_loss)] + pub fn new_poll_add( + fd: Fd, + poll_events: PollEvents, + flags: PollAddMultiFlags, + user_data: u64, + sqe_flags: IoUringSQEFlags, + ) -> Self { + Self(io_uring_sqe { + opcode: IoUringOp::PollAdd as u8, + flags: sqe_flags.bits(), + ioprio: 0, + fd: fd.value(), + __bindgen_anon_1: io_uring_sqe__bindgen_ty_1 { off: 0 }, + __bindgen_anon_2: io_uring_sqe__bindgen_ty_2 { addr: 0 }, + len: flags.bits(), + __bindgen_anon_3: io_uring_sqe__bindgen_ty_3 { + poll_events: poll_events.bits() as u16, + }, + user_data, + __bindgen_anon_4: io_uring_sqe__bindgen_ty_4 { buf_index: 0 }, + personality: 0, + __bindgen_anon_5: io_uring_sqe__bindgen_ty_5 { file_index: 0 }, + __bindgen_anon_6: io_uring_sqe__bindgen_ty_6 { + __bindgen_anon_1: __BindgenUnionField::new(), + cmd: __BindgenUnionField::new(), + bindgen_union_field: [0; 2], + }, + }) + } } #[inline] diff --git a/rusl/src/platform/compat/socket.rs b/rusl/src/platform/compat/socket.rs index 949c818..bb58ce7 100644 --- a/rusl/src/platform/compat/socket.rs +++ b/rusl/src/platform/compat/socket.rs @@ -1,4 +1,5 @@ //! These are not defined in the uapi which is a bit hairy, if they change, that's obviously +use crate::platform::Fd; use crate::string::unix_str::UnixStr; /// a problem. use crate::Error; @@ -97,17 +98,39 @@ transparent_bitflags!( } ); +#[repr(transparent)] #[derive(Debug, Copy, Clone)] -pub struct SocketArg { - pub(crate) addr: SocketAddress, +pub struct SocketAddressInet(pub(crate) linux_rust_bindings::socket::sockaddr_in); + +impl SocketAddressInet { + pub const LENGTH: usize = core::mem::size_of::(); + + #[must_use] + pub const fn new(ip_addr: [u8; 4], port: u16) -> Self { + let s_addr = u32::from_be_bytes(ip_addr); + let port_bytes = port.to_ne_bytes(); + let sin_port = u16::from_be_bytes(port_bytes); + let i = linux_rust_bindings::socket::sockaddr_in { + sin_family: AddressFamily::AF_INET.0, + sin_port, + sin_addr: linux_rust_bindings::socket::in_addr { s_addr }, + __pad: [0u8; 8], + }; + Self(i) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct SocketArgUnix { + pub(crate) addr: SocketAddressUnix, pub(crate) addr_len: usize, } #[repr(transparent)] #[derive(Debug, Copy, Clone)] -pub struct SocketAddress(linux_rust_bindings::socket::sockaddr_un); +pub struct SocketAddressUnix(pub(crate) linux_rust_bindings::socket::sockaddr_un); -impl SocketAddress { +impl SocketAddressUnix { /// Get the `AddressFamily` of the socket address #[inline] #[must_use] @@ -126,7 +149,7 @@ impl SocketAddress { /// # Errors /// The path is longer than 108 bytes (null termination included). /// The path contains byte values out of the 7-bit ASCII range. - pub fn try_from_unix(path: &UnixStr) -> crate::Result { + pub fn try_from_unix(path: &UnixStr) -> crate::Result { let mut ind = 0; let buf = unsafe { let mut buf = [0; 108]; @@ -153,9 +176,532 @@ impl SocketAddress { sun_family: AddressFamily::AF_UNIX.0, sun_path: buf, }); - Ok(SocketArg { + Ok(SocketArgUnix { addr, - addr_len: ind + 2, + addr_len: ind + + core::mem::size_of::(), }) } } + +// #define CMSG_LEN(len) (CMSG_ALIGN (sizeof (struct cmsghdr)) + (len)) +#[cfg(feature = "alloc")] +macro_rules! cmsg_len { + ($len: expr) => { + cmsg_align!(core::mem::size_of::()) + $len + }; +} + +// #define CMSG_SPACE(len) (CMSG_ALIGN (len) + CMSG_ALIGN (sizeof (struct cmsghdr))) +#[cfg(feature = "alloc")] +macro_rules! cmsg_space { + ($len: expr) => { + cmsg_align!($len) + cmsg_align!(core::mem::size_of::()) + }; +} + +// #define CMSG_ALIGN(len) (((len) + sizeof (size_t) - 1) & (size_t) ~(sizeof (size_t) - 1)) +#[cfg(feature = "alloc")] +macro_rules! cmsg_align { + ($len: expr) => { + ($len + core::mem::size_of::() - 1) & !(core::mem::size_of::() - 1) + }; +} + +// #define CMSG_FIRSTHDR(mhdr) ((size_t) (mhdr)->msg_controllen >= sizeof (struct cmsghdr) ? (struct cmsghdr *) (mhdr)->msg_control : (struct cmsghdr *) 0) +#[cfg(feature = "alloc")] +macro_rules! cmsg_firsthdr { + ($mhdr: expr) => { + if $mhdr.msg_controllen >= core::mem::size_of::() { + $mhdr.msg_control + } else { + core::ptr::null_mut() + } + }; +} + +/* +#define CMSG_NXTHDR(mhdr, cmsg) ((cmsg)->cmsg_len < sizeof (struct cmsghdr) || \ + __CMSG_LEN(cmsg) + sizeof(struct cmsghdr) >= __MHDR_END(mhdr) - (unsigned char *)(cmsg) \ + ? 0 : (struct cmsghdr *)__CMSG_NEXT(cmsg)) + */ +#[cfg(feature = "alloc")] +macro_rules! cmsg_nxthdr { + ($mhdr: expr, $cmsg: expr) => { + if ((*$cmsg).cmsg_len) < core::mem::size_of::() + || __cmsg_len!($cmsg) + core::mem::size_of::() + >= __mhdr_end!($mhdr) - core::ptr::addr_of!($cmsg) as usize + { + core::ptr::null() + } else { + __cmsg_next!($cmsg) as *const CmsgHdr + } + }; +} + +// #define CMSG_DATA(cmsg) ((unsigned char *) (((struct cmsghdr *)(cmsg)) + 1)) +#[cfg(feature = "alloc")] +macro_rules! cmsg_data { + ($cmsg: expr) => { + ($cmsg.cast::()).add(1).cast::() + }; +} + +// #define __CMSG_LEN(cmsg) (((cmsg)->cmsg_len + sizeof(long) - 1) & ~(long)(sizeof(long) - 1)) +#[cfg(feature = "alloc")] +macro_rules! __cmsg_len { + ($cmsg: expr) => { + ((*$cmsg).cmsg_len + 8usize - 1usize) & !((8u64 - 1u64) as usize) + }; +} + +// #define __CMSG_NEXT(cmsg) ((unsigned char *)(cmsg) + __CMSG_LEN(cmsg)) +#[cfg(feature = "alloc")] +macro_rules! __cmsg_next { + ($cmsg: expr) => { + $cmsg as usize + __cmsg_len!($cmsg) + }; +} +// #define __MHDR_END(mhdr) ((unsigned char *)(mhdr)->msg_control + (mhdr)->msg_controllen) +#[cfg(feature = "alloc")] +macro_rules! __mhdr_end { + ($mhdr: expr) => { + core::ptr::addr_of!($mhdr.msg_control) as usize + $mhdr.msg_controllen + }; +} + +#[derive(Debug)] +#[cfg(feature = "alloc")] +pub enum ControlMessageSend<'a> { + ScmRights(&'a [crate::platform::Fd]), +} + +#[repr(C)] +#[derive(Debug)] +#[cfg(feature = "alloc")] +pub struct MsgHdrBorrow<'a> { + msg_name: *const u8, + msg_namelen: u32, + msg_iov: *mut linux_rust_bindings::uio::iovec, + msg_iovlen: usize, + msg_control: *mut CmsgHdrSend<'a>, + msg_controllen: usize, + msg_flags: i32, +} + +#[cfg(feature = "alloc")] +pub struct SendDropGuard<'a> { + pub(crate) msghdr: MsgHdrBorrow<'a>, + _dealloc_spc: alloc::vec::Vec, +} +#[cfg(feature = "alloc")] +#[allow(clippy::cast_possible_truncation)] +impl<'a> MsgHdrBorrow<'a> { + #[must_use] + pub fn create_send( + name: Option<&'a UnixStr>, + io: &'a [crate::platform::IoSlice<'a>], + control: Option>, + ) -> SendDropGuard<'a> { + let (name, name_len) = if let Some(name) = name { + (name.as_ptr(), name.len() as u32) + } else { + (core::ptr::null(), 0) + }; + unsafe { + if let Some(ctrl) = control { + match ctrl { + ControlMessageSend::ScmRights(fds) => { + let spc = cmsg_space!(core::mem::size_of_val(fds)); + let mut cmsg_raw = alloc::vec![0u8; spc]; + let cmsg_ptr = cmsg_raw.as_mut_ptr(); + let mhdr = MsgHdrBorrow { + msg_name: name, + msg_namelen: name_len, + msg_iov: io.as_ptr().cast_mut().cast(), + msg_iovlen: io.len(), + msg_control: cmsg_ptr.cast(), + msg_controllen: spc, + msg_flags: 0, + }; + let cmhdr: *mut CmsgHdr = cmsg_firsthdr!(mhdr).cast::(); + // Space was just created for this. + let mut_cm = cmhdr.as_mut().unwrap_unchecked(); + let cmsg_len = cmsg_len!(core::mem::size_of_val(fds)); + mut_cm.cmsg_level = 1; + mut_cm.cmsg_type = 0x01; + mut_cm.cmsg_len = cmsg_len; + let data = cmsg_data!(cmhdr); + core::ptr::copy_nonoverlapping( + fds.as_ptr().cast::(), + data, + core::mem::size_of_val(fds), + ); + SendDropGuard { + msghdr: mhdr, + _dealloc_spc: cmsg_raw, + } + } + } + } else { + SendDropGuard { + msghdr: MsgHdrBorrow { + msg_name: name, + msg_namelen: name_len, + msg_iov: io.as_ptr().cast_mut().cast(), + msg_iovlen: io.len(), + msg_control: core::ptr::null_mut(), + msg_controllen: 0, + msg_flags: 0, + }, + _dealloc_spc: alloc::vec::Vec::new(), + } + } + } + } + + #[must_use] + pub fn create_recv( + io: &'a mut [crate::platform::IoSliceMut<'a>], + cmsg_buf: Option<&'a mut [u8]>, + ) -> Self { + let (ctrl, ctrl_len) = if let Some(cm_buf) = cmsg_buf { + (cm_buf.as_mut_ptr(), cm_buf.len()) + } else { + (core::ptr::null_mut(), 0) + }; + MsgHdrBorrow { + msg_name: core::ptr::null(), + msg_namelen: 0, + msg_iov: io.as_mut_ptr().cast(), + msg_iovlen: io.len(), + msg_control: ctrl.cast(), + msg_controllen: ctrl_len, + msg_flags: 0, + } + } + + #[must_use] + pub fn control_messages(&'a self) -> ControlMessageIterator<'a> { + let first = cmsg_firsthdr!(self); + let cmsg_prev = if first.is_null() { + None + } else { + Some(first.cast()) + }; + ControlMessageIterator { + msghdr: self, + cmsg_prev, + } + } +} + +#[cfg(feature = "alloc")] +pub struct ControlMessageIterator<'a> { + msghdr: &'a MsgHdrBorrow<'a>, + cmsg_prev: Option<*mut CmsgHdr>, +} + +#[cfg(feature = "alloc")] +impl<'a> Iterator for ControlMessageIterator<'a> { + type Item = ControlMessageSend<'a>; + + fn next(&mut self) -> Option { + let cmsg = self.cmsg_prev?; + unsafe { + let r = cmsg.as_mut()?; + if r.cmsg_type == 1 && r.cmsg_level == 1 { + let data = cmsg_data!(cmsg); + let len = cmsg.cast_const() as usize + r.cmsg_len - data as usize; + let len = len / core::mem::size_of::(); + self.cmsg_prev = Some(cmsg_nxthdr!(self.msghdr, cmsg).cast_mut()); + // Here on x86_64 and aarch64 data is 8-byte aligned + #[allow(clippy::cast_ptr_alignment)] + return Some(ControlMessageSend::ScmRights(core::slice::from_raw_parts( + data as *const crate::platform::NonNegativeI32, + len, + ))); + } + self.cmsg_prev = Some(cmsg_nxthdr!(self.msghdr, cmsg).cast_mut()); + self.next() + } + } +} + +/// A control message, metadata about the message type, followed by bytes of data, +/// this struct can be thought of as a protocol for an array of bytes, the control-message itself is functionally a variable length array of bytes, this should never go on the stack, since +/// the actual payload would go outside of the bounds of this struct. Allocating bytes [`[u8; N]`] on the stack, then constructing a cmsg-pointer to that is alright though, use with caution. +#[repr(C)] +#[derive(Debug)] +#[cfg(feature = "alloc")] +pub struct CmsgHdrSend<'a> { + cmsg_len: usize, + cmsg_level: i32, + cmsg_type: i32, + /// Hope this is actually not compiled into to the struct... + _pd: core::marker::PhantomData<&'a ()>, + // Data is written to the end here, it's not a pointer, it's variable size data directly in the struct, this can never be put on the stack. +} + +#[repr(C)] +#[cfg(feature = "alloc")] +pub struct MsgHdr { + pub msg_name: *const u8, + pub msg_namelen: u32, + pub msg_iov: *mut linux_rust_bindings::uio::iovec, + pub msg_iovlen: usize, + pub msg_control: *mut CmsgHdr, + pub msg_controllen: usize, + pub msg_flags: i32, +} + +#[derive(Debug)] +pub enum ControlMessageRaw { + ScmRights(*mut Fd, usize), +} + +#[cfg(feature = "alloc")] +impl MsgHdr { + /// Update control message + /// # Safety + /// `cmsg_ptr` is valid and has enough space to write the cmsg + /// `cmsg_ptr` needs to be alive as long as this struct is alive. + #[cfg(feature = "alloc")] + pub unsafe fn update_control( + &mut self, + control: Option, + cmsg_ptr: *mut u8, + ) { + unsafe { + if let Some(ctrl) = control { + match ctrl { + ControlMessageSend::ScmRights(fds) => { + let spc = cmsg_space!(core::mem::size_of_val(fds)); + core::ptr::write_bytes(cmsg_ptr, 0, spc); + self.msg_control = cmsg_ptr.cast(); + self.msg_controllen = spc; + let cmhdr: *mut CmsgHdr = cmsg_firsthdr!(self).cast::(); + // Space was just created for this. + let mut_cm = cmhdr.as_mut().unwrap_unchecked(); + let cmsg_len = cmsg_len!(core::mem::size_of_val(fds)); + mut_cm.cmsg_level = 1; + mut_cm.cmsg_type = 0x01; + mut_cm.cmsg_len = cmsg_len; + let data = cmsg_data!(cmhdr); + core::ptr::copy_nonoverlapping( + fds.as_ptr().cast::(), + data, + core::mem::size_of_val(fds), + ); + } + } + } else { + self.msg_control = core::ptr::null_mut(); + self.msg_controllen = 0; + } + } + } + + /// Create a [`MsgHdr`] ready to send using a sendmsg + /// # Safety + /// All pointers must be valid. + /// All pointers must live at least as long as this struct. + /// + #[must_use] + #[cfg(feature = "alloc")] + pub unsafe fn create_send( + msg_iov: *mut linux_rust_bindings::uio::iovec, + msg_iovlen: usize, + control: Option, + cmsg_ptr: *mut u8, + ) -> Self { + unsafe { + if let Some(ctrl) = control { + match ctrl { + ControlMessageRaw::ScmRights(fds, len) => { + let fd_buf = core::slice::from_raw_parts_mut(fds, len); + let spc = cmsg_space!(core::mem::size_of_val(fd_buf)); + core::ptr::write_bytes(cmsg_ptr, 0, spc); + let mhdr = Self { + msg_name: core::ptr::null(), + msg_namelen: 0, + msg_iov, + msg_iovlen: msg_iovlen as _, + msg_control: cmsg_ptr.cast(), + msg_controllen: spc, + msg_flags: 0, + }; + let cmhdr: *mut CmsgHdr = cmsg_firsthdr!(mhdr).cast::(); + // Space was just created for this. + let mut_cm = cmhdr.as_mut().unwrap_unchecked(); + let cmsg_len = cmsg_len!(core::mem::size_of_val(fd_buf)); + mut_cm.cmsg_level = 1; + mut_cm.cmsg_type = 0x01; + mut_cm.cmsg_len = cmsg_len; + let data = cmsg_data!(cmhdr); + core::ptr::copy_nonoverlapping( + fds.cast_const().cast::(), + data, + core::mem::size_of_val(fd_buf), + ); + mhdr + } + } + } else { + Self { + msg_name: core::ptr::null(), + msg_namelen: 0, + msg_iov, + msg_iovlen: msg_iovlen as _, + msg_control: core::ptr::null_mut(), + msg_controllen: 0, + msg_flags: 0, + } + } + } + } +} + +/// A control message, metadata about the message type, followed by bytes of data, +/// this struct can be thought of as a protocol for an array of bytes, the control-message itself is functionally a variable length array of bytes, this should never go on the stack, since +/// the actual payload would go outside of the bounds of this struct. Allocating bytes [`[u8; N]`] on the stack, then constructing a cmsg-pointer to that is alright though, use with caution. +#[repr(C)] +#[derive(Debug)] +#[cfg(feature = "alloc")] +pub struct CmsgHdr { + cmsg_len: usize, + cmsg_level: i32, + cmsg_type: i32, + // Data is written to the end here, it's not a pointer, it's variable size data directly in the struct, this can never be put on the stack. +} + +#[cfg(test)] +#[cfg(feature = "alloc")] +#[allow( + clippy::cast_ptr_alignment, + clippy::ptr_cast_constness, + clippy::ptr_as_ptr, + clippy::cast_possible_truncation +)] +mod tests { + use crate::platform::{CmsgHdr, Fd, IoSlice, MsgHdr, OpenFlags}; + use crate::string::unix_str::UnixStr; + use crate::unistd::open; + use alloc::vec; + use core::ptr::null_mut; + use linux_rust_bindings::uio::iovec; + + #[test] + fn cmsg_macros_on_empty() { + unsafe { + let mut cmsg = CmsgHdr { + cmsg_len: 16, + cmsg_level: 0, + cmsg_type: 0, + }; + let cmsg_ptr = core::ptr::addr_of_mut!(cmsg); + let len = __cmsg_len!(cmsg_ptr); + assert_eq!(16, len); + let _ = __cmsg_next!(cmsg_ptr); + let mhdr = MsgHdr { + msg_name: "abc".as_ptr(), + msg_namelen: 0, + msg_iov: null_mut(), + msg_iovlen: 0, + msg_control: cmsg_ptr, + msg_controllen: 0, + msg_flags: 0, + }; + let _ = __mhdr_end!(mhdr); + assert!(cmsg_firsthdr!(mhdr).is_null()); + } + } + + /// pretty unsafe, references need to live until this [`MsgHdr`] goes out of scope, cba for this test though + /// Also leaks the cap vector + unsafe fn msg_hdr(name: &UnixStr, iovec: &[iovec], fds: &[Fd]) -> MsgHdr { + let spc = cmsg_space!(core::mem::size_of_val(fds)); + let mut cap = vec![0u8; spc]; + let cmsg_ptr = cap.as_mut_ptr(); + let mhdr = MsgHdr { + msg_name: name.as_ptr(), + msg_namelen: name.len() as _, + msg_iov: iovec.as_ptr().cast_mut(), + msg_iovlen: iovec.len(), + msg_control: cmsg_ptr.cast(), + msg_controllen: spc, + msg_flags: 0, + }; + let cmhdr: *mut CmsgHdr = cmsg_firsthdr!(mhdr); + assert!(!cmhdr.is_null()); + let mut_cm = cmhdr.as_mut().unwrap(); + let cmsg_len = cmsg_len!(core::mem::size_of_val(fds)); + mut_cm.cmsg_level = 1; + mut_cm.cmsg_type = 0x01; + mut_cm.cmsg_len = cmsg_len; + let data = cmsg_data!(cmhdr); + core::ptr::copy_nonoverlapping( + core::ptr::from_ref(fds) as *const u8, + data, + core::mem::size_of_val(fds), + ); + core::mem::forget(cap); + mhdr + } + + #[test] + fn cmsg_macros_has_single_fd() { + let name = unix_lit!("name"); + let io = IoSlice::new(b"hello"); + let fd1 = open(unix_lit!("/proc/mounts"), OpenFlags::O_RDONLY).unwrap(); + let v = &[io.vec]; + let f = &[fd1]; + let mhdr = unsafe { msg_hdr(name, v, f) }; + let first_hdr = cmsg_firsthdr!(mhdr); + assert!(!first_hdr.is_null()); + let first_hdr_deref = unsafe { first_hdr.as_ref().unwrap() }; + assert_eq!(1, first_hdr_deref.cmsg_level); + assert_eq!(1, first_hdr_deref.cmsg_type); + // 8 bytes length, 4 bytes level, 4 bytes type, 4 bytes [`Fd`] 4 bytes alignment padding + assert_eq!(20, first_hdr_deref.cmsg_len); + unsafe { + let data = cmsg_data!(first_hdr); + let _slice = core::slice::from_raw_parts(data as _, 8); + // From the back + let len = first_hdr as *const _ as usize + first_hdr_deref.cmsg_len - data as usize; + let _gross_count = len / core::mem::size_of::(); + let null_term_ptr = data as *mut Fd; + let first = null_term_ptr.read_unaligned(); + assert_eq!(fd1, first); + assert_eq!(0, cmsg_nxthdr!(mhdr, first_hdr) as usize); + } + } + + #[test] + fn cmsg_macros_has_two_fd() { + let name = unix_lit!("name"); + let io = IoSlice::new(b"hello"); + let fd1 = open(unix_lit!("/proc/mounts"), OpenFlags::O_RDONLY).unwrap(); + let v = &[io.vec]; + let f = &[fd1]; + let mhdr = unsafe { msg_hdr(name, v, f) }; + let first_hdr = cmsg_firsthdr!(mhdr); + assert!(!first_hdr.is_null()); + let first_hdr_deref = unsafe { first_hdr.as_ref().unwrap() }; + assert_eq!(1, first_hdr_deref.cmsg_level); + assert_eq!(1, first_hdr_deref.cmsg_type); + // 8 bytes length, 4 bytes level, 4 bytes type, 4 bytes [`Fd`] 4 bytes alignment padding + assert_eq!(20, first_hdr_deref.cmsg_len); + unsafe { + let data = cmsg_data!(first_hdr); + let _slice = core::slice::from_raw_parts(data as _, 8); + // From the back + let len = first_hdr as *const _ as usize + first_hdr_deref.cmsg_len - data as usize; + let _gross_count = len / core::mem::size_of::(); + let null_term_ptr = data as *mut Fd; + let first = null_term_ptr.read_unaligned(); + assert_eq!(fd1, first); + assert_eq!(0, cmsg_nxthdr!(mhdr, first_hdr) as usize); + } + } +} diff --git a/rusl/src/platform/compat/time.rs b/rusl/src/platform/compat/time.rs index a7d3195..b6d33f4 100644 --- a/rusl/src/platform/compat/time.rs +++ b/rusl/src/platform/compat/time.rs @@ -95,13 +95,7 @@ impl TryFrom for TimeSpec { tv_sec: d.as_secs().try_into().map_err(|_| { crate::Error::no_code("Failed to fit duration u64 secs into tv_sec i64") })?, - tv_nsec: d - .subsec_nanos() - .try_into() - // This one doesn't make a lot of sense - .map_err(|_| { - crate::Error::no_code("Failed to fit duration u32 secs into tv_sec i32") - })?, + tv_nsec: d.subsec_nanos().into(), })) } } diff --git a/rusl/src/platform/compat/uio.rs b/rusl/src/platform/compat/uio.rs index bd9ac91..18cb9d8 100644 --- a/rusl/src/platform/compat/uio.rs +++ b/rusl/src/platform/compat/uio.rs @@ -1,11 +1,11 @@ use core::marker::PhantomData; -use linux_rust_bindings::uio::iovec; +pub use linux_rust_bindings::uio::iovec; #[repr(transparent)] pub struct IoSlice<'a> { #[allow(dead_code)] - vec: iovec, + pub vec: iovec, _p: PhantomData<&'a [u8]>, } @@ -24,8 +24,7 @@ impl<'a> IoSlice<'a> { #[repr(transparent)] pub struct IoSliceMut<'a> { - #[allow(dead_code)] - pub(crate) vec: iovec, + pub vec: iovec, _p: PhantomData<&'a mut [u8]>, } diff --git a/rusl/src/process/signal.rs b/rusl/src/process/signal.rs index a9acb9d..5a976be 100644 --- a/rusl/src/process/signal.rs +++ b/rusl/src/process/signal.rs @@ -8,6 +8,7 @@ use crate::platform::{NonNegativeI32, SaMask, SigSetT, SIG_DFL, SIG_IGN}; /// This struct can differ between architectures, it's the same on aarch64 and `x86_64` though. #[repr(C)] +#[allow(clippy::struct_field_names)] struct Sigaction { // Fn pointer to an extern C Fn(int) -> () sa_sigaction: SaSigaction, @@ -44,8 +45,8 @@ pub struct SigInfo { pub si_code: i32, pub si_trapno: i32, pub si_pid: i32, - pub _pad: [i32; 27], - pub _align: [u64; 0], + _pad: [i32; 27], + _align: [u64; 0], } /// We don't have to do much here, just return from the handler diff --git a/rusl/src/select/poll.rs b/rusl/src/select/poll.rs index 6b8c5ef..12d3884 100644 --- a/rusl/src/select/poll.rs +++ b/rusl/src/select/poll.rs @@ -20,8 +20,8 @@ pub fn ppoll( PPOLL, poll_fds.as_mut_ptr(), poll_fds.len(), - timespec.map_or_else(core::ptr::null, |ts| ts as *const TimeSpec), - sigset.map_or_else(core::ptr::null, |ss_t| ss_t as *const SigSetT) + timespec.map_or_else(core::ptr::null, core::ptr::from_ref::), + sigset.map_or_else(core::ptr::null, core::ptr::from_ref::) ) }; bail_on_below_zero!(res, "`PPOLL` syscall failed"); diff --git a/rusl/src/string/unix_str.rs b/rusl/src/string/unix_str.rs index ce8f99a..e31ce20 100644 --- a/rusl/src/string/unix_str.rs +++ b/rusl/src/string/unix_str.rs @@ -120,7 +120,7 @@ impl core::ops::Deref for UnixString { type Target = UnixStr; fn deref(&self) -> &Self::Target { - unsafe { &*(self.0.as_slice() as *const [u8] as *const UnixStr) } + unsafe { &*(core::ptr::from_ref::<[u8]>(self.0.as_slice()) as *const UnixStr) } } } @@ -183,7 +183,7 @@ impl UnixStr { for (ind, byte) in s.iter().enumerate() { if *byte == NULL_BYTE { return if ind == len - 1 { - unsafe { Ok(&*(s as *const [u8] as *const UnixStr)) } + unsafe { Ok(&*(core::ptr::from_ref::<[u8]>(s) as *const UnixStr)) } } else { Err(Error::no_code("Tried to instantiate UnixStr from an invalid &str, a null byte was found but out of place")) }; @@ -200,7 +200,7 @@ impl UnixStr { pub unsafe fn from_ptr<'a>(s: *const u8) -> &'a Self { let non_null_len = strlen(s); let slice = core::slice::from_raw_parts(s, non_null_len + 1); - &*(slice as *const [u8] as *const Self) + &*(core::ptr::from_ref::<[u8]>(slice) as *const Self) } /// Try to convert this `&UnixStr` to a utf8 `&str` @@ -336,7 +336,9 @@ impl UnixStr { for (ind, byte) in self.0.iter().enumerate().rev() { if *byte == b'/' { return if ind + 2 < self.len() { - unsafe { Some(&*(&self.0[ind + 1..] as *const [u8] as *const Self)) } + unsafe { + Some(&*(core::ptr::from_ref::<[u8]>(&self.0[ind + 1..]) as *const Self)) + } } else { None }; diff --git a/rusl/src/unistd.rs b/rusl/src/unistd.rs index 93a8157..c916910 100644 --- a/rusl/src/unistd.rs +++ b/rusl/src/unistd.rs @@ -12,6 +12,7 @@ pub use open::{open, open_at, open_at_mode, open_mode, open_raw}; pub use pipe::{pipe, pipe2}; pub use read::read; pub use rename::{rename, rename_at, rename_at2, rename_flags}; +pub use seek::{lseek, Whence}; pub use setgid::setgid; pub use setpgid::setpgid; pub use setsid::setsid; @@ -37,6 +38,7 @@ mod open; mod pipe; mod read; mod rename; +mod seek; mod setgid; mod setpgid; mod setsid; diff --git a/rusl/src/unistd/seek.rs b/rusl/src/unistd/seek.rs new file mode 100644 index 0000000..2fa8c25 --- /dev/null +++ b/rusl/src/unistd/seek.rs @@ -0,0 +1,27 @@ +use crate::platform::{Fd, NonNegativeI32, OffT}; +use crate::Error; +use sc::syscall; + +transparent_bitflags! { + pub struct Whence: NonNegativeI32 { + const DEFAULT = NonNegativeI32::ZERO; + const SET = NonNegativeI32::comptime_checked_new(linux_rust_bindings::fs::SEEK_SET); + const CUR = NonNegativeI32::comptime_checked_new(linux_rust_bindings::fs::SEEK_CUR); + const END = NonNegativeI32::comptime_checked_new(linux_rust_bindings::fs::SEEK_END); + const DATA = NonNegativeI32::comptime_checked_new(linux_rust_bindings::fs::SEEK_DATA); + const HOLE = NonNegativeI32::comptime_checked_new(linux_rust_bindings::fs::SEEK_HOLE); + } +} + +/// Seek a fd +/// See details in the [linux docs here](https://man7.org/linux/man-pages/man2/lseek.2.html) +/// # Errors +/// See above +#[allow(clippy::cast_possible_wrap)] +pub fn lseek(fd: Fd, off_t: OffT, whence: Whence) -> Result { + unsafe { + let res = syscall!(LSEEK, fd.0, off_t, whence.bits().0); + bail_on_below_zero!(res, "`LSEEK syscall failed`"); + Ok(res as OffT) + } +} diff --git a/rusl/test-files/io_uring/uring_recv_send_pass_fd.txt b/rusl/test-files/io_uring/uring_recv_send_pass_fd.txt new file mode 100644 index 0000000..24fc7fd --- /dev/null +++ b/rusl/test-files/io_uring/uring_recv_send_pass_fd.txt @@ -0,0 +1 @@ +Text content! diff --git a/rusl/test-files/socket/.gitkeep b/rusl/test-files/socket/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test-runners/alloc-st-main/Cargo.lock b/test-runners/alloc-st-main/Cargo.lock index f6abf79..475a6cd 100644 --- a/test-runners/alloc-st-main/Cargo.lock +++ b/test-runners/alloc-st-main/Cargo.lock @@ -12,9 +12,9 @@ dependencies = [ [[package]] name = "linux-rust-bindings" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88eeeb1a5ef57ab0d40fb7a256e1b211496f97d6a8664ef52c0a665ce69a4e0f" +checksum = "109280d660e557dfa6474e3b1bbe04272ba3fa5c5e5ce73b394c7b224f082caa" [[package]] name = "rusl" diff --git a/test-runners/no-alloc-main/Cargo.lock b/test-runners/no-alloc-main/Cargo.lock index 5cec56a..33a045c 100644 --- a/test-runners/no-alloc-main/Cargo.lock +++ b/test-runners/no-alloc-main/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "linux-rust-bindings" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88eeeb1a5ef57ab0d40fb7a256e1b211496f97d6a8664ef52c0a665ce69a4e0f" +checksum = "109280d660e557dfa6474e3b1bbe04272ba3fa5c5e5ce73b394c7b224f082caa" [[package]] name = "no-alloc-main" diff --git a/test-runners/threaded-main/Cargo.lock b/test-runners/threaded-main/Cargo.lock index cd19d4d..f495fa4 100644 --- a/test-runners/threaded-main/Cargo.lock +++ b/test-runners/threaded-main/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "linux-rust-bindings" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88eeeb1a5ef57ab0d40fb7a256e1b211496f97d6a8664ef52c0a665ce69a4e0f" +checksum = "109280d660e557dfa6474e3b1bbe04272ba3fa5c5e5ce73b394c7b224f082caa" [[package]] name = "rusl" diff --git a/tiny-std/src/allocator/dlmalloc.rs b/tiny-std/src/allocator/dlmalloc.rs index 21140db..283b8e4 100644 --- a/tiny-std/src/allocator/dlmalloc.rs +++ b/tiny-std/src/allocator/dlmalloc.rs @@ -192,6 +192,7 @@ const fn leftshift_for_tree_index(x: u32) -> u32 { } } +#[allow(clippy::new_without_default)] impl Dlmalloc { #[must_use] pub const fn new() -> Dlmalloc { diff --git a/tiny-std/src/env.rs b/tiny-std/src/env.rs index 368e4cb..827dcc8 100644 --- a/tiny-std/src/env.rs +++ b/tiny-std/src/env.rs @@ -12,6 +12,7 @@ pub(crate) static mut ENV: Env = Env { env_p: core::ptr::null(), }; +#[allow(clippy::struct_field_names)] pub(crate) struct Env { pub(crate) arg_c: u64, pub(crate) arg_v: *const *const u8, diff --git a/tiny-std/src/fs.rs b/tiny-std/src/fs.rs index 04da4e0..2ebe33c 100644 --- a/tiny-std/src/fs.rs +++ b/tiny-std/src/fs.rs @@ -562,7 +562,7 @@ impl<'a> DirEntry<'a> { // ie. we just did a range check. let tgt = self.inner.d_name.get_unchecked(..=len); // Safety: `&UnixStr` and `&[u8]` have the same layout - Ok(&*(tgt as *const [u8] as *const UnixStr)) + Ok(&*(core::ptr::from_ref::<[u8]>(tgt) as *const UnixStr)) } } diff --git a/tiny-std/src/io/read_buf.rs b/tiny-std/src/io/read_buf.rs index dfc02de..53cfffd 100644 --- a/tiny-std/src/io/read_buf.rs +++ b/tiny-std/src/io/read_buf.rs @@ -167,5 +167,5 @@ pub(crate) unsafe fn slice_assume_init_mut(slice: &mut [MaybeUninit]) -> & // `slice` is initialized, and `MaybeUninit` is guaranteed to have the same layout as `T`. // The pointer obtained is valid since it refers to memory owned by `slice` which is a // reference and thus guaranteed to be valid for reads. - &mut *(slice as *mut [MaybeUninit] as *mut [T]) + &mut *(core::ptr::from_mut::<[MaybeUninit]>(slice) as *mut [T]) } diff --git a/tiny-std/src/net.rs b/tiny-std/src/net.rs index 658c443..a1b9a53 100644 --- a/tiny-std/src/net.rs +++ b/tiny-std/src/net.rs @@ -1,5 +1,5 @@ use rusl::platform::{ - AddressFamily, NonNegativeI32, SocketAddress, SocketFlags, SocketOptions, SocketType, + AddressFamily, NonNegativeI32, SocketAddressUnix, SocketFlags, SocketOptions, SocketType, }; use rusl::string::unix_str::UnixStr; @@ -26,9 +26,9 @@ impl UnixStream { SocketOptions::new(SocketType::SOCK_STREAM, block), 0, )?; - let addr = SocketAddress::try_from_unix(path)?; + let addr = SocketAddressUnix::try_from_unix(path)?; - rusl::network::connect(fd, &addr)?; + rusl::network::connect_unix(fd, &addr)?; Ok(Self(OwnedFd(fd))) } } @@ -74,8 +74,8 @@ impl UnixListener { SocketOptions::new(SocketType::SOCK_STREAM, block), 0, )?; - let addr = SocketAddress::try_from_unix(path)?; - rusl::network::bind(fd, &addr)?; + let addr = SocketAddressUnix::try_from_unix(path)?; + rusl::network::bind_unix(fd, &addr)?; rusl::network::listen(fd, NonNegativeI32::MAX)?; Ok(Self(OwnedFd(fd))) } @@ -89,7 +89,7 @@ impl UnixListener { let block = blocking .then(SocketFlags::empty) .unwrap_or(SocketFlags::SOCK_NONBLOCK); - let client = rusl::network::accept(self.0 .0, None, SocketFlags::SOCK_CLOEXEC | block)?; + let client = rusl::network::accept_unix(self.0 .0, SocketFlags::SOCK_CLOEXEC | block)?.0; Ok(UnixStream(OwnedFd(client))) } } diff --git a/tiny-std/src/thread/spawn.rs b/tiny-std/src/thread/spawn.rs index c6dc3a9..c9b46a6 100644 --- a/tiny-std/src/thread/spawn.rs +++ b/tiny-std/src/thread/spawn.rs @@ -258,9 +258,9 @@ pub(crate) struct ThreadDealloc { /// Spawn a thread that will run the provided function /// # Errors /// Failure to mmap the thread's stack. -pub fn spawn T>(func: F) -> Result> +pub fn spawn(func: F) -> Result> where - F: Send + 'static, + F: FnOnce() -> T + Send + 'static, T: Send + 'static, { let flags = CloneFlags::CLONE_VM diff --git a/tiny-std/src/unix/passwd/getpw_r.rs b/tiny-std/src/unix/passwd/getpw_r.rs index baede31..51ad3dc 100644 --- a/tiny-std/src/unix/passwd/getpw_r.rs +++ b/tiny-std/src/unix/passwd/getpw_r.rs @@ -19,7 +19,7 @@ pub struct Passwd<'a> { /// `/etc/passwd` isn't readable. pub fn getpwuid_r(uid: UidT, buf: &mut [u8]) -> Result> { let fd = - unsafe { rusl::unistd::open_raw("/etc/passwd\0".as_ptr() as usize, OpenFlags::O_RDONLY)? }; + unsafe { rusl::unistd::open_raw(c"/etc/passwd".as_ptr() as usize, OpenFlags::O_RDONLY)? }; search_pwd_fd(fd, uid, buf) }