From 2c40c5acbf06502b88bd7a268dc1205b397550e2 Mon Sep 17 00:00:00 2001 From: Nikolay Arhipov Date: Wed, 18 Oct 2023 15:30:29 +0300 Subject: [PATCH] Added fcntl shim (#8) --- .github/workflows/ci.yml | 4 +- .gitignore | 6 +- .vscode/settings.json | 3 +- Cargo.toml | 1 + build.rs | 2 +- src/lib.rs | 151 +++++++++++++++++++++++++++++++++------ 6 files changed, 136 insertions(+), 31 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e498918..0362a67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,9 +21,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: "`cargo check`" - run: cargo check --all-targets --all-features + run: cargo hack check -Z build-std=std,panic_abort --feature-powerset --examples --bins --tests --target armv7-sony-vita-newlibeabihf - name: "`cargo clippy`" - run: cargo clippy --all-targets --all-features + run: cargo hack clippy -Z build-std=std,panic_abort --feature-powerset --examples --bins --tests --target armv7-sony-vita-newlibeabihf - name: Run cargo doc run: DOCS_RS=1 RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features --target armv7-sony-vita-newlibeabihf -Z build-std - name: Upload docs diff --git a/.gitignore b/.gitignore index 23d0cf9..a172caf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,5 @@ -.vscode/* -!.vscode/settings.json - +.vscode target/ .DS_Store .tmp -.envrc \ No newline at end of file +.envrc diff --git a/.vscode/settings.json b/.vscode/settings.json index 64cb31d..f6abe0e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "rust-analyzer.cargo.target": "armv7-sony-vita-newlibeabihf" + "rust-analyzer.cargo.target": "armv7-sony-vita-newlibeabihf", + "rust-analyzer.cargo.features": ["fcntl", "pipe2", "socketpair"] } diff --git a/Cargo.toml b/Cargo.toml index ba4290b..f06f0ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ keywords = ["vitasdk", "psvita", "vita", "homebrew", "shim"] [features] pipe2 = [] socketpair = [] +fcntl = [] [dependencies] libc = "0.2.149" diff --git a/build.rs b/build.rs index 18bedc8..27dfc1e 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, path::Path, process::Command}; -const FEATURES: &[&str] = &["socketpair", "pipe2"]; +const FEATURES: &[&str] = &["socketpair", "pipe2", "fcntl"]; fn main() { if std::env::var("DOCS_RS").is_ok() { diff --git a/src/lib.rs b/src/lib.rs index c1ee8ce..37b99ba 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,8 @@ #![no_std] +#![feature(c_variadic)] #[no_mangle] +#[allow(clippy::missing_safety_doc)] #[cfg(all(target_os = "vita", feature = "socketpair"))] pub unsafe extern "C" fn socketpair( _domain: libc::c_int, @@ -22,12 +24,16 @@ pub unsafe extern "C" fn socketpair( server_addr.sin_addr.s_addr = libc::INADDR_LOOPBACK.to_be(); if libc::bind(listener, &mut server_addr as *mut _ as *mut _, addr_len) == -1 { - libc::close(listener); + with_errno(|| { + libc::close(listener); + }); return -1; } if libc::listen(listener, 1) == -1 { - libc::close(listener); + with_errno(|| { + libc::close(listener); + }); return -1; } @@ -37,13 +43,17 @@ pub unsafe extern "C" fn socketpair( &mut addr_len, ) == -1 { - libc::close(listener); + with_errno(|| { + libc::close(listener); + }); return -1; } let client_socket: libc::c_int = libc::socket(libc::AF_INET, r#type, protocol); if client_socket == -1 { - libc::close(listener); + with_errno(|| { + libc::close(listener); + }); return -1; } @@ -53,8 +63,10 @@ pub unsafe extern "C" fn socketpair( addr_len, ) == -1 { - libc::close(client_socket); - libc::close(listener); + with_errno(|| { + libc::close(client_socket); + libc::close(listener); + }); return -1; } @@ -64,8 +76,10 @@ pub unsafe extern "C" fn socketpair( &mut addr_len, ); if peer_socket == -1 { - libc::close(client_socket); - libc::close(listener); + with_errno(|| { + libc::close(client_socket); + libc::close(listener); + }); return -1; } @@ -78,36 +92,127 @@ pub unsafe extern "C" fn socketpair( } #[no_mangle] +#[allow(clippy::missing_safety_doc)] #[cfg(all(target_os = "vita", feature = "pipe2"))] pub unsafe extern "C" fn pipe2(pipefd: &mut [libc::c_int; 2], flags: libc::c_int) -> libc::c_int { #[cfg(not(feature = "socketpair"))] use libc::socketpair; - if flags & libc::O_NONBLOCK != 0 { - if socketpair(libc::AF_INET, libc::SOCK_STREAM, 0, pipefd.as_mut_ptr()) == -1 { + if socketpair(libc::AF_INET, libc::SOCK_STREAM, 0, pipefd.as_mut_ptr()) == -1 { + return -1; + } + + let pipefd = *pipefd; + for fd in pipefd { + let linger = libc::linger { + l_onoff: 1, + l_linger: 0, + }; + if setsockopt(fd, libc::SOL_SOCKET, libc::SO_LINGER, linger) == -1 { + with_errno(|| { + libc::close(pipefd[0]); + libc::close(pipefd[1]); + }); return -1; } + } - let val: libc::c_int = 1; - if set_nonblocking(pipefd[0], val) == -1 || set_nonblocking(pipefd[1], val) == -1 { - libc::close(pipefd[0]); - libc::close(pipefd[1]); - return -1; + if flags & libc::O_NONBLOCK != 0 { + for fd in pipefd { + if setsockopt(fd, libc::SOL_SOCKET, libc::SO_NONBLOCK, 1) == -1 { + with_errno(|| { + libc::close(pipefd[0]); + libc::close(pipefd[1]); + }); + return -1; + } } + } - 0 - } else { - libc::pipe(pipefd.as_mut_ptr()) + 0 +} + +#[cfg(all( + target_os = "vita", + any(feature = "pipe2", feature = "socketpair", feature = "fcntl") +))] +extern "C" { + #[cfg_attr(target_os = "vita", link_name = "__errno")] + fn errno_location() -> *mut libc::c_int; +} + +#[cfg(all(target_os = "vita", any(feature = "pipe2", feature = "socketpair")))] +unsafe fn with_errno(mut f: impl FnMut()) { + let errno = *errno_location(); + f(); + *errno_location() = errno as libc::c_int; +} + +#[no_mangle] +#[allow(clippy::missing_safety_doc)] +#[cfg(all(target_os = "vita", feature = "fcntl"))] +pub unsafe extern "C" fn fcntl(fd: libc::c_int, cmd: libc::c_int, mut args: ...) -> libc::c_int { + let mut arg: libc::c_int = 0; + + if cmd == libc::F_SETFL { + arg = args.arg::(); + } + + match cmd { + libc::F_GETFD => 0, + libc::F_GETFL => { + let mut val: libc::c_int = 0; + + let res = getsockopt(fd, libc::SOL_SOCKET, libc::SO_NONBLOCK, &mut val); + if res == -1 { + return -1; + } + + match val { + 0 => 0, + _ => libc::O_NONBLOCK, + } + } + libc::F_SETFL => { + let val = (arg & libc::O_NONBLOCK) != 0; + setsockopt(fd, libc::SOL_SOCKET, libc::SO_NONBLOCK, val as libc::c_int) + } + _ => { + *errno_location() = libc::ENOTSUP; + -1 + } } } -#[cfg(all(target_os = "vita", feature = "pipe2"))] -unsafe fn set_nonblocking(fd: libc::c_int, val: libc::c_int) -> libc::c_int { +#[cfg(all(target_os = "vita", any(feature = "pipe2", feature = "fcntl")))] +unsafe fn setsockopt( + fd: libc::c_int, + level: libc::c_int, + name: libc::c_int, + val: T, +) -> libc::c_int { libc::setsockopt( fd, - libc::SOL_SOCKET, - libc::SO_NONBLOCK, + level, + name, &val as *const _ as *const _, - core::mem::size_of::() as u32, + core::mem::size_of::() as libc::socklen_t, + ) +} + +#[cfg(all(target_os = "vita", feature = "fcntl"))] +unsafe fn getsockopt( + fd: libc::c_int, + level: libc::c_int, + name: libc::c_int, + val: &mut T, +) -> libc::c_int { + let mut len: libc::socklen_t = core::mem::size_of::() as libc::socklen_t; + libc::getsockopt( + fd, + level, + name, + val as *mut _ as *mut _, + &mut len as *mut _ as *mut _, ) }