absorb: 27 orphaned relibc patches re-applied (Phase 1.0A)
Per local/docs/PATCH-PRESERVATION-AUDIT-2026-07-12.md the relibc
fork was carrying only 34 of 90 patches in local/patches/relibc/.
The other 56 patches' content was silently missing from the fork.
This commit re-applies 27 patches that genuinely still apply
cleanly. Recovery covers:
- eventfd implementation (sys/eventfd.h + eventfd.rs)
- signalfd implementation (sys/signalfd.h + signalfd.rs)
- timerfd implementation (sys/timerfd.h + timerfd.rs)
- bits/eventfd.h header
- spawn() function: cbindgen + stdint fix
- P3-timerfd-cbindgen-fix
- cbindgen language=C fixes for sys/{timerfd,semaphore}
- stdint include chain fixes
- strtold implementation
- dns aaaa getaddrinfo ipv6
- various stack/threading/header threading fixes
- dup3 syscalls
- waitid implementation
- bits/timespec reverse_from
- open_memstream integration
24 files changed.
This commit is contained in:
@@ -21,7 +21,7 @@ use super::ForkScratchpad;
|
||||
|
||||
// Setup a stack starting from the very end of the address space, and then growing downwards.
|
||||
pub const STACK_TOP: usize = 1 << 47;
|
||||
pub const STACK_SIZE: usize = 1024 * 1024;
|
||||
pub const STACK_SIZE: usize = 8 * 1024 * 1024;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(C)]
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
//! `bits/eventfd.h` — eventfd counter type.
|
||||
|
||||
use crate::platform::types::uint64_t;
|
||||
pub type eventfd_t = uint64_t;
|
||||
@@ -51,6 +51,14 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
|
||||
let layout = Layout::from_size_align_unchecked(master.segment_size, 16);
|
||||
let ptr = alloc(layout);
|
||||
|
||||
if ptr.is_null() {
|
||||
eprintln!(
|
||||
"__tls_get_addr: TLS allocation failed for module {:#x} (size {})",
|
||||
ti.ti_module, master.segment_size
|
||||
);
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
ptr::copy_nonoverlapping(master.ptr, ptr, master.image_size);
|
||||
ptr::write_bytes(
|
||||
ptr.add(master.image_size),
|
||||
@@ -68,10 +76,11 @@ pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
|
||||
let mut ptr = tcb.dtv_mut()[dtv_index];
|
||||
|
||||
if ptr.is_null() {
|
||||
panic!(
|
||||
"__tls_get_addr({ti:p}: {:#x}, {:#x})",
|
||||
ti.ti_module, ti.ti_offset
|
||||
eprintln!(
|
||||
"__tls_get_addr({:p}: {:#x}, {:#x}): DTV entry is null, module {} TLS not available",
|
||||
ti, ti.ti_module, ti.ti_offset, ti.ti_module
|
||||
);
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
if cfg!(target_arch = "riscv64") {
|
||||
|
||||
@@ -9,8 +9,8 @@ pub type Elf64_Half = uint16_t;
|
||||
|
||||
pub type Elf32_Word = uint32_t;
|
||||
pub type Elf32_Sword = int32_t;
|
||||
pub type Elf64_Word = uint64_t;
|
||||
pub type Elf64_Sword = int64_t;
|
||||
pub type Elf64_Word = uint32_t;
|
||||
pub type Elf64_Sword = int32_t;
|
||||
|
||||
pub type Elf32_Xword = uint64_t;
|
||||
pub type Elf32_Sxword = int64_t;
|
||||
|
||||
@@ -7,6 +7,7 @@ use core::num::NonZeroU64;
|
||||
use crate::{
|
||||
c_str::CStr,
|
||||
error::ResultExt,
|
||||
header::unistd::close,
|
||||
platform::{
|
||||
Pal, Sys,
|
||||
types::{c_char, c_int, c_short, c_ulonglong, mode_t, off_t, pid_t},
|
||||
@@ -73,6 +74,23 @@ pub struct flock {
|
||||
/// Type of lock; `F_RDLCK`, `F_WRLCK`, `F_UNLCK`.
|
||||
pub l_type: c_short,
|
||||
/// Flag for starting offset.
|
||||
|
||||
if cmd == F_DUPFD_CLOEXEC {
|
||||
let new_fd = Sys::fcntl(fildes, F_DUPFD_CLOEXEC, arg).or_minus_one_errno();
|
||||
if new_fd >= 0 {
|
||||
return new_fd;
|
||||
}
|
||||
|
||||
let new_fd = Sys::fcntl(fildes, F_DUPFD, arg).or_minus_one_errno();
|
||||
if new_fd < 0 {
|
||||
return -1;
|
||||
}
|
||||
if Sys::fcntl(new_fd, F_SETFD, FD_CLOEXEC as c_ulonglong).or_minus_one_errno() < 0 {
|
||||
let _ = close(new_fd);
|
||||
return -1;
|
||||
}
|
||||
return new_fd;
|
||||
}
|
||||
pub l_whence: c_short,
|
||||
/// Relative offset in bytes.
|
||||
pub l_start: off_t,
|
||||
|
||||
@@ -111,6 +111,36 @@ pub unsafe extern "C" fn uselocale(newloc: locale_t) -> locale_t {
|
||||
old_loc
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn getlocalename_l(category: c_int, loc: locale_t) -> *const c_char {
|
||||
if loc.is_null() {
|
||||
return ptr::null();
|
||||
}
|
||||
|
||||
if loc == LC_GLOBAL_LOCALE {
|
||||
if unsafe { GLOBAL_LOCALE.is_null() } {
|
||||
return (&raw const C_LOCALE).cast::<c_char>();
|
||||
}
|
||||
|
||||
let Some(global) = (unsafe { GLOBAL_LOCALE.as_ref() }) else {
|
||||
return ptr::null();
|
||||
};
|
||||
return global
|
||||
.get_name(category)
|
||||
.map_or(ptr::null(), |name| name.as_ptr());
|
||||
}
|
||||
|
||||
if !(LC_COLLATE..=LC_ALL).contains(&category) {
|
||||
return ptr::null();
|
||||
}
|
||||
|
||||
let loc = loc.cast_const().cast::<LocaleData>();
|
||||
let Some(loc) = (unsafe { loc.as_ref() }) else {
|
||||
return ptr::null();
|
||||
};
|
||||
loc.name.as_ptr()
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/newlocale.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn newlocale(mask: c_int, locale: *const c_char, base: locale_t) -> locale_t {
|
||||
|
||||
@@ -314,6 +314,13 @@ pub unsafe extern "C" fn pthread_testcancel() {
|
||||
unsafe { pthread::testcancel() };
|
||||
}
|
||||
|
||||
/// <https://man7.org/linux/man-pages/man3/pthread_yield.3.html>
|
||||
///
|
||||
/// Non-standard GNU extension. Prefer `sched_yield()` instead.
|
||||
pub extern "C" fn pthread_yield() {
|
||||
let _ = Sys::sched_yield();
|
||||
}
|
||||
|
||||
// Must be the same struct as defined in the pthread_cleanup_push macro.
|
||||
#[repr(C)]
|
||||
pub(crate) struct CleanupLinkedListEntry {
|
||||
|
||||
+82
-15
@@ -37,43 +37,110 @@ pub const SCHED_RR: c_int = 1;
|
||||
pub const SCHED_OTHER: c_int = 2;
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_get_priority_max.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn sched_get_priority_max(policy: c_int) -> c_int {
|
||||
todo!()
|
||||
match policy {
|
||||
SCHED_FIFO | SCHED_RR => 99,
|
||||
SCHED_OTHER => 0,
|
||||
_ => {
|
||||
crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||
-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_get_priority_max.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_get_priority_min.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn sched_get_priority_min(policy: c_int) -> c_int {
|
||||
todo!()
|
||||
match policy {
|
||||
SCHED_FIFO | SCHED_RR => 1,
|
||||
SCHED_OTHER => 0,
|
||||
_ => {
|
||||
crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||
-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_getparam.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sched_getparam(pid: pid_t, param: *mut sched_param) -> c_int {
|
||||
todo!()
|
||||
if pid != 0 {
|
||||
crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||
return -1;
|
||||
}
|
||||
crate::platform::ERRNO.set(crate::header::errno::ENOSYS);
|
||||
-1
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_getscheduler.html>.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn sched_getscheduler(pid: pid_t) -> c_int {
|
||||
if pid != 0 {
|
||||
crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||
return -1;
|
||||
}
|
||||
crate::platform::ERRNO.set(crate::header::errno::ENOSYS);
|
||||
-1
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_rr_get_interval.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub extern "C" fn sched_rr_get_interval(pid: pid_t, time: *const timespec) -> c_int {
|
||||
todo!()
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn sched_rr_get_interval(pid: pid_t, tp: *mut timespec) -> c_int {
|
||||
if pid != 0 {
|
||||
crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||
return -1;
|
||||
}
|
||||
if tp.is_null() {
|
||||
crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||
return -1;
|
||||
}
|
||||
unsafe {
|
||||
(*tp).tv_sec = 0;
|
||||
(*tp).tv_nsec = 100_000_000; // 100ms default SCHED_RR quantum
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_setparam.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sched_setparam(pid: pid_t, param: *const sched_param) -> c_int {
|
||||
todo!()
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn sched_setparam(pid: pid_t, _param: *const sched_param) -> c_int {
|
||||
if pid != 0 {
|
||||
crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||
return -1;
|
||||
}
|
||||
crate::platform::ERRNO.set(crate::header::errno::ENOSYS);
|
||||
-1
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_setscheduler.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn sched_setscheduler(
|
||||
pid: pid_t,
|
||||
policy: c_int,
|
||||
param: *const sched_param,
|
||||
) -> c_int {
|
||||
todo!()
|
||||
if pid != 0 {
|
||||
crate::platform::ERRNO.set(crate::header::errno::ESRCH);
|
||||
return -1;
|
||||
}
|
||||
match policy {
|
||||
SCHED_OTHER => {
|
||||
if !param.is_null() && unsafe { (*param).sched_priority } != 0 {
|
||||
crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||
return -1;
|
||||
}
|
||||
SCHED_OTHER
|
||||
}
|
||||
SCHED_FIFO | SCHED_RR => {
|
||||
crate::platform::ERRNO.set(crate::header::errno::ENOSYS);
|
||||
-1
|
||||
}
|
||||
_ => {
|
||||
crate::platform::ERRNO.set(crate::header::errno::EINVAL);
|
||||
-1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/sched_yield.html>.
|
||||
|
||||
+23
-11
@@ -2,7 +2,10 @@
|
||||
//!
|
||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html>.
|
||||
|
||||
use core::{mem, ptr};
|
||||
use core::{
|
||||
mem, ptr,
|
||||
sync::atomic::Ordering,
|
||||
};
|
||||
|
||||
use cbitset::BitSet;
|
||||
|
||||
@@ -34,6 +37,12 @@ pub mod sys;
|
||||
#[path = "redox.rs"]
|
||||
pub mod sys;
|
||||
|
||||
mod signalfd;
|
||||
pub use self::signalfd::*;
|
||||
|
||||
mod signalfd;
|
||||
pub use self::signalfd::*;
|
||||
|
||||
type SigSet = BitSet<[u64; 1]>;
|
||||
|
||||
/// cbindgen:ignore
|
||||
@@ -246,10 +255,15 @@ pub extern "C" fn killpg(pgrp: pid_t, sig: c_int) -> c_int {
|
||||
/// not send the signal.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn pthread_kill(thread: pthread_t, sig: c_int) -> c_int {
|
||||
let os_tid = {
|
||||
let pthread = unsafe { &*(thread as *const crate::pthread::Pthread) };
|
||||
unsafe { pthread.os_tid.get().read() }
|
||||
};
|
||||
let pthread = unsafe { &*(thread as *const crate::pthread::Pthread) };
|
||||
let os_tid = unsafe { pthread.os_tid.get().read() };
|
||||
let flags = crate::pthread::PthreadFlags::from_bits_retain(
|
||||
pthread.flags.load(Ordering::Acquire),
|
||||
);
|
||||
if flags.contains(crate::pthread::PthreadFlags::FINISHED) {
|
||||
return errno::ESRCH;
|
||||
}
|
||||
|
||||
crate::header::pthread::e(unsafe { Sys::rlct_kill(os_tid, sig as usize) })
|
||||
}
|
||||
|
||||
@@ -264,12 +278,10 @@ pub unsafe extern "C" fn pthread_sigmask(
|
||||
set: *const sigset_t,
|
||||
oldset: *mut sigset_t,
|
||||
) -> c_int {
|
||||
// On Linux and Redox, pthread_sigmask and sigprocmask are equivalent
|
||||
if unsafe { sigprocmask(how, set, oldset) } == 0 {
|
||||
0
|
||||
} else {
|
||||
//TODO: Fix race
|
||||
platform::ERRNO.get()
|
||||
let filtered_set = unsafe { set.as_ref().map(|&block| block & !RLCT_SIGNAL_MASK) };
|
||||
match unsafe { Sys::sigprocmask(how, filtered_set.as_ref(), oldset.as_mut()) } {
|
||||
Ok(()) => 0,
|
||||
Err(errno) => errno.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
use core::{mem, ptr};
|
||||
|
||||
use crate::{
|
||||
error::{Errno, ResultExt},
|
||||
header::fcntl::{
|
||||
FD_CLOEXEC, F_GETFL, F_SETFD, F_SETFL, O_CLOEXEC, O_NONBLOCK, O_RDWR, fcntl,
|
||||
},
|
||||
platform::{
|
||||
ERRNO, Pal, Sys,
|
||||
types::{c_int, c_ulonglong},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{SIG_BLOCK, sigprocmask, sigset_t};
|
||||
|
||||
pub const SFD_CLOEXEC: c_int = 0x80000;
|
||||
pub const SFD_NONBLOCK: c_int = 0x800;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct signalfd_siginfo {
|
||||
pub ssi_signo: u32,
|
||||
pub ssi_errno: i32,
|
||||
pub ssi_code: i32,
|
||||
pub ssi_pid: u32,
|
||||
pub ssi_uid: u32,
|
||||
pub ssi_fd: i32,
|
||||
pub ssi_tid: u32,
|
||||
pub ssi_band: u32,
|
||||
pub ssi_overrun: u32,
|
||||
pub ssi_trapno: u32,
|
||||
pub ssi_status: i32,
|
||||
pub ssi_int: i32,
|
||||
pub ssi_ptr: u64,
|
||||
pub ssi_utime: u64,
|
||||
pub ssi_stime: u64,
|
||||
pub ssi_addr: u64,
|
||||
pub ssi_addr_lsb: u16,
|
||||
pub __pad2: u16,
|
||||
pub ssi_syscall: i32,
|
||||
pub ssi_call_addr: u64,
|
||||
pub ssi_arch: u32,
|
||||
pub __pad: [u8; 28],
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _cbindgen_export_signalfd_siginfo(siginfo: signalfd_siginfo) {}
|
||||
|
||||
fn signalfd4_inner(fd: c_int, mask: *const sigset_t, masksize: usize, flags: c_int) -> Result<c_int, Errno> {
|
||||
let supported = SFD_CLOEXEC | SFD_NONBLOCK;
|
||||
if flags & !supported != 0 || masksize != mem::size_of::<sigset_t>() {
|
||||
return Err(Errno(crate::header::errno::EINVAL));
|
||||
}
|
||||
if mask.is_null() {
|
||||
return Err(Errno(crate::header::errno::EFAULT));
|
||||
}
|
||||
|
||||
let new_fd = if fd == -1 {
|
||||
let mut oflag = O_RDWR;
|
||||
if flags & SFD_CLOEXEC == SFD_CLOEXEC {
|
||||
oflag |= O_CLOEXEC;
|
||||
}
|
||||
if flags & SFD_NONBLOCK == SFD_NONBLOCK {
|
||||
oflag |= O_NONBLOCK;
|
||||
}
|
||||
Sys::open(c"/scheme/event".into(), oflag, 0)?
|
||||
} else {
|
||||
if flags & SFD_CLOEXEC == SFD_CLOEXEC
|
||||
&& unsafe { fcntl(fd, F_SETFD, FD_CLOEXEC as c_ulonglong) } < 0
|
||||
{
|
||||
return Err(Errno(ERRNO.get()));
|
||||
}
|
||||
if flags & SFD_NONBLOCK == SFD_NONBLOCK {
|
||||
let current = unsafe { fcntl(fd, F_GETFL, 0 as c_ulonglong) };
|
||||
if current < 0 {
|
||||
return Err(Errno(ERRNO.get()));
|
||||
}
|
||||
if unsafe { fcntl(fd, F_SETFL, (current | O_NONBLOCK) as c_ulonglong) } < 0 {
|
||||
return Err(Errno(ERRNO.get()));
|
||||
}
|
||||
}
|
||||
fd
|
||||
};
|
||||
|
||||
if unsafe { sigprocmask(SIG_BLOCK, mask, ptr::null_mut()) } < 0 {
|
||||
if fd == -1 {
|
||||
let _ = Sys::close(new_fd);
|
||||
}
|
||||
return Err(Errno(ERRNO.get()));
|
||||
}
|
||||
|
||||
Ok(new_fd)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn signalfd4(fd: c_int, mask: *const sigset_t, masksize: usize, flags: c_int) -> c_int {
|
||||
signalfd4_inner(fd, mask, masksize, flags).or_minus_one_errno()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn signalfd(fd: c_int, mask: *const sigset_t, masksize: usize) -> c_int {
|
||||
unsafe { signalfd4(fd, mask, masksize, 0) }
|
||||
}
|
||||
@@ -49,6 +49,9 @@ mod default;
|
||||
pub use self::getdelim::*;
|
||||
mod getdelim;
|
||||
|
||||
pub use self::open_memstream::*;
|
||||
mod open_memstream;
|
||||
|
||||
mod ext;
|
||||
mod helpers;
|
||||
pub mod printf;
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
use alloc::{boxed::Box, vec, vec::Vec};
|
||||
use core::ptr;
|
||||
|
||||
use super::{
|
||||
Buffer, FILE,
|
||||
constants::{BUFSIZ, F_NORD},
|
||||
};
|
||||
use crate::{
|
||||
error::{Errno, ResultExtPtrMut},
|
||||
fs::File,
|
||||
header::{
|
||||
errno::{EFAULT, ENOMEM},
|
||||
fcntl, pthread, stdlib, unistd,
|
||||
},
|
||||
io::{self, BufWriter, Write},
|
||||
platform::{
|
||||
ERRNO,
|
||||
types::{c_char, size_t},
|
||||
},
|
||||
};
|
||||
|
||||
struct MemstreamWriter {
|
||||
bufp: *mut *mut c_char,
|
||||
sizep: *mut size_t,
|
||||
current: *mut c_char,
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
unsafe impl Send for MemstreamWriter {}
|
||||
|
||||
impl MemstreamWriter {
|
||||
fn new(bufp: *mut *mut c_char, sizep: *mut size_t) -> Self {
|
||||
Self {
|
||||
bufp,
|
||||
sizep,
|
||||
current: ptr::null_mut(),
|
||||
buffer: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn sync_output(&mut self) -> io::Result<()> {
|
||||
let size = self.buffer.len();
|
||||
let alloc_size = size
|
||||
.checked_add(1)
|
||||
.ok_or_else(|| io::Error::from_raw_os_error(ENOMEM))?;
|
||||
|
||||
let raw = if self.current.is_null() {
|
||||
unsafe { stdlib::malloc(alloc_size) }
|
||||
} else {
|
||||
unsafe { stdlib::realloc(self.current.cast(), alloc_size) }
|
||||
};
|
||||
if raw.is_null() {
|
||||
return Err(io::Error::from_raw_os_error(ENOMEM));
|
||||
}
|
||||
|
||||
let raw = raw.cast::<c_char>();
|
||||
if size != 0 {
|
||||
unsafe { ptr::copy_nonoverlapping(self.buffer.as_ptr(), raw.cast::<u8>(), size) };
|
||||
}
|
||||
unsafe {
|
||||
*raw.add(size) = 0;
|
||||
*self.bufp = raw;
|
||||
*self.sizep = size;
|
||||
}
|
||||
self.current = raw;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for MemstreamWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.buffer
|
||||
.try_reserve(buf.len())
|
||||
.map_err(|_| io::Error::from_raw_os_error(ENOMEM))?;
|
||||
self.buffer.extend_from_slice(buf);
|
||||
Ok(buf.len())
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.sync_output()
|
||||
}
|
||||
}
|
||||
|
||||
fn create_memstream(bufp: *mut *mut c_char, sizep: *mut size_t) -> Result<Box<FILE>, Errno> {
|
||||
if bufp.is_null() || sizep.is_null() {
|
||||
return Err(Errno(EFAULT));
|
||||
}
|
||||
|
||||
unsafe {
|
||||
*bufp = ptr::null_mut();
|
||||
*sizep = 0;
|
||||
}
|
||||
|
||||
let mut fds = [0; 2];
|
||||
if unsafe { unistd::pipe2(fds.as_mut_ptr(), fcntl::O_CLOEXEC) } != 0 {
|
||||
return Err(Errno(ERRNO.get()));
|
||||
}
|
||||
let _ = unistd::close(fds[0]);
|
||||
|
||||
let file = File::new(fds[1]);
|
||||
let writer = Box::new(BufWriter::new(MemstreamWriter::new(bufp, sizep)));
|
||||
let mutex_attr = pthread::RlctMutexAttr {
|
||||
ty: pthread::PTHREAD_MUTEX_RECURSIVE,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
Ok(Box::new(FILE {
|
||||
lock: pthread::RlctMutex::new(&mutex_attr).unwrap(),
|
||||
file,
|
||||
flags: F_NORD,
|
||||
read_buf: Buffer::Owned(vec![0; BUFSIZ as usize]),
|
||||
read_pos: 0,
|
||||
read_size: 0,
|
||||
unget: Vec::new(),
|
||||
writer,
|
||||
pid: None,
|
||||
orientation: 0,
|
||||
}))
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn open_memstream(bufp: *mut *mut c_char, sizep: *mut size_t) -> *mut FILE {
|
||||
create_memstream(bufp, sizep).or_errno_null_mut()
|
||||
}
|
||||
@@ -1247,7 +1247,11 @@ pub unsafe extern "C" fn realpath(pathname: *const c_char, resolved: *mut c_char
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getenv.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn secure_getenv(name: *const c_char) -> *mut c_char {
|
||||
unimplemented!();
|
||||
// Redox does not support setuid/setgid binaries, so there is no
|
||||
// elevated-privilege case to guard against. Always delegate to getenv.
|
||||
// If setuid support is ever added, this must check real vs effective
|
||||
// uid/gid and return NULL when they differ.
|
||||
unsafe { getenv(name) }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/drand48.html>.
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
sys_includes = ["stdint.h"]
|
||||
after_includes = """
|
||||
typedef uint64_t eventfd_t;
|
||||
"""
|
||||
include_guard = "_SYS_EVENTFD_H"
|
||||
language = "C"
|
||||
style = "Tag"
|
||||
no_includes = true
|
||||
cpp_compat = true
|
||||
[enum]
|
||||
prefix_with_name = true
|
||||
@@ -0,0 +1,176 @@
|
||||
//! `sys/sem.h` implementation.
|
||||
|
||||
use alloc::{collections::BTreeMap, ffi::CString, format};
|
||||
use core::{mem, ptr, sync::atomic::{AtomicI32, Ordering}};
|
||||
|
||||
use crate::{
|
||||
header::{
|
||||
errno::{EFAULT, EINVAL, ENOENT},
|
||||
fcntl::{O_CREAT, O_EXCL, O_RDWR},
|
||||
sys_ipc::{IPC_CREAT, IPC_EXCL, IPC_NOWAIT, IPC_PRIVATE, IPC_RMID, ipc_perm, key_t},
|
||||
sys_mman::{MAP_FAILED, MAP_SHARED, PROT_READ, PROT_WRITE, mmap, munmap, shm_open, shm_unlink},
|
||||
unistd::close,
|
||||
},
|
||||
out::Out,
|
||||
platform::{ERRNO, Pal, Sys, types::{c_int, c_short, c_ushort, c_void}},
|
||||
sync::{Mutex, Semaphore},
|
||||
};
|
||||
|
||||
pub const GETVAL: c_int = 12;
|
||||
pub const SETVAL: c_int = 16;
|
||||
pub const SEM_UNDO: c_short = 0x1000;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct sembuf {
|
||||
pub sem_num: c_ushort,
|
||||
pub sem_op: c_short,
|
||||
pub sem_flg: c_short,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct semid_ds {
|
||||
pub sem_perm: ipc_perm,
|
||||
pub sem_nsems: c_ushort,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub union semun {
|
||||
pub val: c_int,
|
||||
pub buf: *mut semid_ds,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct SharedSemSet {
|
||||
sem: Semaphore,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SemMeta {
|
||||
path: CString,
|
||||
nsems: c_ushort,
|
||||
}
|
||||
|
||||
static NEXT_SEMID: AtomicI32 = AtomicI32::new(1);
|
||||
static SEM_REGISTRY: Mutex<BTreeMap<c_int, SemMeta>> = Mutex::new(BTreeMap::new());
|
||||
static SEM_ATTACHMENTS: Mutex<BTreeMap<c_int, (usize, c_int)>> = Mutex::new(BTreeMap::new());
|
||||
|
||||
fn key_path(key: key_t) -> CString { CString::new(format!("/relibc-sysv-sem-{key:x}")).unwrap() }
|
||||
fn id_path(id: c_int) -> CString { CString::new(format!("/relibc-sysv-sem-id-{id:x}")).unwrap() }
|
||||
|
||||
fn open_flags_from_ipc(semflg: c_int) -> c_int {
|
||||
let mut flags = O_RDWR;
|
||||
if semflg & IPC_CREAT == IPC_CREAT { flags |= O_CREAT; }
|
||||
if semflg & IPC_EXCL == IPC_EXCL { flags |= O_EXCL; }
|
||||
flags
|
||||
}
|
||||
|
||||
unsafe fn map_sem(path: &CString, create: bool, initial: c_int) -> Result<*mut SharedSemSet, c_int> {
|
||||
let fd = unsafe { shm_open(path.as_ptr(), if create { O_CREAT | O_RDWR } else { O_RDWR }, 0o600) };
|
||||
if fd < 0 { return Err(ERRNO.get()); }
|
||||
let mut st = crate::header::sys_stat::stat::default();
|
||||
if Sys::fstat(fd, Out::from_mut(&mut st)).is_err() { let _ = close(fd); return Err(ERRNO.get()); }
|
||||
if st.st_size == 0 {
|
||||
if Sys::ftruncate(fd, mem::size_of::<SharedSemSet>() as i64).is_err() { let _ = close(fd); return Err(ERRNO.get()); }
|
||||
}
|
||||
let ptr = unsafe { mmap(ptr::null_mut(), mem::size_of::<SharedSemSet>(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) };
|
||||
let _ = close(fd);
|
||||
if ptr == MAP_FAILED { return Err(ERRNO.get()); }
|
||||
let set = ptr.cast::<SharedSemSet>();
|
||||
if create && st.st_size == 0 { unsafe { set.write(SharedSemSet { sem: Semaphore::new(initial as u32) }) }; }
|
||||
Ok(set)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn semget(key: key_t, nsems: c_int, semflg: c_int) -> c_int {
|
||||
if nsems != 1 { ERRNO.set(EINVAL); return -1; }
|
||||
let semid = NEXT_SEMID.fetch_add(1, Ordering::SeqCst);
|
||||
let path = if key == IPC_PRIVATE { id_path(semid) } else { key_path(key) };
|
||||
let fd = unsafe { shm_open(path.as_ptr(), open_flags_from_ipc(semflg), (semflg & 0o777) as c_int) };
|
||||
if fd < 0 { return -1; }
|
||||
let _ = close(fd);
|
||||
SEM_REGISTRY.lock().insert(semid, SemMeta { path, nsems: 1 });
|
||||
semid
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn semop(semid: c_int, sops: *mut sembuf, nsops: usize) -> c_int {
|
||||
if sops.is_null() { ERRNO.set(EFAULT); return -1; }
|
||||
if nsops == 0 { return 0; }
|
||||
let meta = { let registry = SEM_REGISTRY.lock(); registry.get(&semid).cloned() };
|
||||
let Some(meta) = meta else { ERRNO.set(ENOENT); return -1; };
|
||||
let set = {
|
||||
let mut attachments = SEM_ATTACHMENTS.lock();
|
||||
if let Some((ptr, _)) = attachments.get(&semid) { *ptr as *mut SharedSemSet } else {
|
||||
let ptr = match unsafe { map_sem(&meta.path, false, 0) } { Ok(ptr) => ptr, Err(_) => return -1 };
|
||||
attachments.insert(semid, (ptr as usize, 1));
|
||||
ptr
|
||||
}
|
||||
};
|
||||
let ops = unsafe { core::slice::from_raw_parts(sops, nsops) };
|
||||
for op in ops {
|
||||
if op.sem_num != 0 { ERRNO.set(EINVAL); return -1; }
|
||||
if op.sem_op > 0 { unsafe { (*set).sem.post(op.sem_op as u32) }; continue; }
|
||||
if op.sem_op == 0 {
|
||||
if op.sem_flg & IPC_NOWAIT as c_short == IPC_NOWAIT as c_short {
|
||||
if unsafe { (*set).sem.value() } != 0 { ERRNO.set(crate::header::errno::EAGAIN); return -1; }
|
||||
} else if unsafe { (*set).sem.value() } != 0 {
|
||||
ERRNO.set(crate::header::errno::EAGAIN);
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for _ in 0..(-op.sem_op) {
|
||||
if op.sem_flg & IPC_NOWAIT as c_short == IPC_NOWAIT as c_short {
|
||||
if unsafe { (*set).sem.try_wait() } == 0 { ERRNO.set(crate::header::errno::EAGAIN); return -1; }
|
||||
} else if unsafe { (*set).sem.wait(None, crate::header::time::CLOCK_MONOTONIC) }.is_err() {
|
||||
ERRNO.set(crate::header::errno::EINTR);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn semctl(semid: c_int, semnum: c_int, cmd: c_int, mut args: ...) -> c_int {
|
||||
if semnum != 0 { ERRNO.set(EINVAL); return -1; }
|
||||
let meta = { let registry = SEM_REGISTRY.lock(); registry.get(&semid).cloned() };
|
||||
let Some(meta) = meta else { ERRNO.set(ENOENT); return -1; };
|
||||
match cmd {
|
||||
IPC_RMID => {
|
||||
let _ = unsafe { shm_unlink(meta.path.as_ptr()) };
|
||||
SEM_REGISTRY.lock().remove(&semid);
|
||||
if let Some((ptr, _)) = SEM_ATTACHMENTS.lock().remove(&semid) {
|
||||
let _ = unsafe { munmap((ptr as *mut SharedSemSet).cast::<c_void>(), mem::size_of::<SharedSemSet>()) };
|
||||
}
|
||||
0
|
||||
}
|
||||
GETVAL => {
|
||||
let set = match unsafe { map_sem(&meta.path, false, 0) } { Ok(set) => set, Err(errno) => { ERRNO.set(errno); return -1; } };
|
||||
let value = unsafe { (*set).sem.value() as c_int };
|
||||
let _ = unsafe { munmap(set.cast::<c_void>(), mem::size_of::<SharedSemSet>()) };
|
||||
value
|
||||
}
|
||||
SETVAL => {
|
||||
let val = unsafe { args.arg::<c_int>() };
|
||||
let set = match unsafe { map_sem(&meta.path, false, 0) } { Ok(set) => set, Err(errno) => { ERRNO.set(errno); return -1; } };
|
||||
let current = unsafe { (*set).sem.value() as c_int };
|
||||
if val > current { unsafe { (*set).sem.post((val - current) as u32) }; } else {
|
||||
for _ in 0..(current - val) {
|
||||
if unsafe { (*set).sem.try_wait() } == 0 { break; }
|
||||
}
|
||||
}
|
||||
let _ = unsafe { munmap(set.cast::<c_void>(), mem::size_of::<SharedSemSet>()) };
|
||||
0
|
||||
}
|
||||
crate::header::sys_ipc::IPC_STAT => {
|
||||
let buf = unsafe { args.arg::<*mut semid_ds>() };
|
||||
if buf.is_null() { ERRNO.set(EFAULT); return -1; }
|
||||
unsafe { *buf = semid_ds { sem_perm: ipc_perm::default(), sem_nsems: meta.nsems }; }
|
||||
0
|
||||
}
|
||||
_ => { ERRNO.set(EINVAL); -1 }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
sys_includes = ["signal.h", "stdint.h", "stddef.h"]
|
||||
include_guard = "_SYS_SIGNALFD_H"
|
||||
trailer = """
|
||||
#ifndef SFD_CLOEXEC
|
||||
#define SFD_CLOEXEC 0x80000
|
||||
#endif
|
||||
|
||||
#ifndef SFD_NONBLOCK
|
||||
#define SFD_NONBLOCK 0x800
|
||||
#endif
|
||||
|
||||
int signalfd(int fd, const sigset_t *mask, size_t masksize);
|
||||
int signalfd4(int fd, const sigset_t *mask, size_t masksize, int flags);
|
||||
"""
|
||||
language = "C"
|
||||
style = "Tag"
|
||||
no_includes = true
|
||||
cpp_compat = true
|
||||
|
||||
[enum]
|
||||
prefix_with_name = true
|
||||
|
||||
[export.rename]
|
||||
"signalfd_siginfo" = "struct signalfd_siginfo"
|
||||
@@ -0,0 +1,14 @@
|
||||
//! `sys/signalfd.h` implementation.
|
||||
|
||||
use crate::{
|
||||
header::signal::{self, signalfd_siginfo},
|
||||
platform::types::c_int,
|
||||
};
|
||||
|
||||
pub const SFD_CLOEXEC: c_int = signal::SFD_CLOEXEC;
|
||||
pub const SFD_NONBLOCK: c_int = signal::SFD_NONBLOCK;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn _cbindgen_export_sys_signalfd_siginfo(siginfo: signalfd_siginfo) {
|
||||
let _ = siginfo;
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
//! `sys/timerfd.h` implementation.
|
||||
//!
|
||||
//! Non-POSIX, see <https://man7.org/linux/man-pages/man2/timerfd_create.2.html>.
|
||||
|
||||
use alloc::{collections::BTreeMap, format};
|
||||
use core::{mem, slice, sync::atomic::{AtomicBool, Ordering}};
|
||||
|
||||
use crate::{
|
||||
c_str::{CStr, CString},
|
||||
error::{Errno, ResultExt},
|
||||
header::{
|
||||
bits_timespec::timespec,
|
||||
errno::{EBADF, EFAULT, EINVAL, EIO},
|
||||
fcntl::{O_CLOEXEC, O_NONBLOCK, O_RDWR},
|
||||
},
|
||||
out::Out,
|
||||
platform::{
|
||||
Pal, Sys,
|
||||
types::{c_int, clockid_t},
|
||||
},
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
pub use crate::header::time::itimerspec;
|
||||
|
||||
pub const TFD_CLOEXEC: c_int = 0x80000;
|
||||
pub const TFD_NONBLOCK: c_int = 0x800;
|
||||
pub const TFD_TIMER_ABSTIME: c_int = 0x1;
|
||||
pub const TFD_TIMER_CANCEL_ON_SET: c_int = 0x2;
|
||||
|
||||
const NSEC_PER_SEC: i64 = 1_000_000_000;
|
||||
|
||||
static MAP_INIT: AtomicBool = AtomicBool::new(false);
|
||||
static TIMERFD_CLOCKIDS: Mutex<Option<BTreeMap<c_int, clockid_t>>> = Mutex::new(None);
|
||||
|
||||
fn with_map<R>(f: impl FnOnce(&mut BTreeMap<c_int, clockid_t>) -> R) -> R {
|
||||
let mut guard = TIMERFD_CLOCKIDS.lock();
|
||||
if !MAP_INIT.load(Ordering::Acquire) {
|
||||
*guard = Some(BTreeMap::new());
|
||||
MAP_INIT.store(true, Ordering::Release);
|
||||
}
|
||||
f(guard.as_mut().unwrap())
|
||||
}
|
||||
|
||||
fn add_timespec(a: ×pec, b: ×pec) -> timespec {
|
||||
let total_nsec = a.tv_nsec as i64 + b.tv_nsec as i64;
|
||||
let mut sec = a.tv_sec + b.tv_sec + (total_nsec / NSEC_PER_SEC) as i64;
|
||||
let mut nsec = (total_nsec % NSEC_PER_SEC) as i64;
|
||||
if nsec < 0 {
|
||||
sec -= 1;
|
||||
nsec += NSEC_PER_SEC;
|
||||
}
|
||||
timespec { tv_sec: sec, tv_nsec: nsec }
|
||||
}
|
||||
|
||||
fn read_exact(fd: c_int, buf: &mut [u8]) -> Result<(), Errno> {
|
||||
match Sys::read(fd, buf)? {
|
||||
n if n == buf.len() => Ok(()),
|
||||
_ => Err(Errno(EIO)),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_exact(fd: c_int, buf: &[u8]) -> Result<(), Errno> {
|
||||
match Sys::write(fd, buf)? {
|
||||
n if n == buf.len() => Ok(()),
|
||||
_ => Err(Errno(EIO)),
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn timerfd_create(clockid: clockid_t, flags: c_int) -> c_int {
|
||||
let supported = TFD_CLOEXEC | TFD_NONBLOCK;
|
||||
if flags & !supported != 0 {
|
||||
return Err::<c_int, _>(Errno(EINVAL)).or_minus_one_errno();
|
||||
}
|
||||
|
||||
let mut oflag = O_RDWR;
|
||||
if flags & TFD_CLOEXEC == TFD_CLOEXEC {
|
||||
oflag |= O_CLOEXEC;
|
||||
}
|
||||
if flags & TFD_NONBLOCK == TFD_NONBLOCK {
|
||||
oflag |= O_NONBLOCK;
|
||||
}
|
||||
|
||||
let path = match CString::new(format!("/scheme/time/{clockid}")) {
|
||||
Ok(path) => path,
|
||||
Err(_) => return Err::<c_int, _>(Errno(EINVAL)).or_minus_one_errno(),
|
||||
};
|
||||
let fd = match Sys::open(CStr::borrow(&path), oflag, 0) {
|
||||
Ok(fd) => fd,
|
||||
Err(Errno(e)) => { return Err::<c_int, _>(Errno(e)).or_minus_one_errno(); }
|
||||
};
|
||||
|
||||
// Register the clockid for this fd so timerfd_settime can convert relative times
|
||||
with_map(|map| { map.insert(fd, clockid); });
|
||||
|
||||
fd
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn timerfd_settime(fd: c_int, flags: c_int, new: *const itimerspec, old: *mut itimerspec) -> c_int {
|
||||
let supported = TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET;
|
||||
if flags & !supported != 0 {
|
||||
return Err::<c_int, _>(Errno(EINVAL)).or_minus_one_errno();
|
||||
}
|
||||
if new.is_null() {
|
||||
return Err::<c_int, _>(Errno(EFAULT)).or_minus_one_errno();
|
||||
}
|
||||
if !old.is_null() && unsafe { timerfd_gettime(fd, old) } < 0 {
|
||||
return -1;
|
||||
}
|
||||
let spec = unsafe { &*new };
|
||||
let mut value = spec.it_value.clone();
|
||||
|
||||
// Convert relative time to absolute if TFD_TIMER_ABSTIME is not set
|
||||
if flags & TFD_TIMER_ABSTIME == 0 {
|
||||
let clockid = with_map(|map| map.get(&fd).copied()).unwrap_or(0);
|
||||
let mut now = timespec::default();
|
||||
if Sys::clock_gettime(clockid, Out::from_mut(&mut now)).is_err() {
|
||||
return Err::<c_int, _>(Errno(EBADF)).or_minus_one_errno();
|
||||
}
|
||||
value = add_timespec(&now, &value);
|
||||
}
|
||||
|
||||
let buf = unsafe { slice::from_raw_parts((&raw const value).cast::<u8>(), mem::size_of::<timespec>()) };
|
||||
write_exact(fd, buf).map(|()| 0).or_minus_one_errno()
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn timerfd_gettime(fd: c_int, curr: *mut itimerspec) -> c_int {
|
||||
if curr.is_null() {
|
||||
return Err::<c_int, _>(Errno(EFAULT)).or_minus_one_errno();
|
||||
}
|
||||
let curr = unsafe { &mut *curr };
|
||||
curr.it_interval = timespec::default();
|
||||
let buf = unsafe { slice::from_raw_parts_mut((&raw mut curr.it_value).cast::<u8>(), mem::size_of::<timespec>()) };
|
||||
read_exact(fd, buf).map(|()| 0).or_minus_one_errno()
|
||||
}
|
||||
@@ -50,6 +50,16 @@ pub(crate) const NANOSECONDS: c_long = 1_000_000_000;
|
||||
/// cbindgen:ignore
|
||||
const UTC_STR: &core::ffi::CStr = c"UTC";
|
||||
|
||||
#[cfg(target_os = "redox")]
|
||||
impl<'a> From<&'a syscall::TimeSpec> for timespec {
|
||||
fn from(tp: &syscall::TimeSpec) -> Self {
|
||||
Self {
|
||||
tv_sec: tp.tv_sec as _,
|
||||
tv_nsec: tp.tv_nsec as _,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// timer_t internal data, ABI unstable
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
|
||||
@@ -287,7 +287,20 @@ pub extern "C" fn dup2(fildes: c_int, fildes2: c_int) -> c_int {
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dup.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub extern "C" fn dup3(fildes: c_int, fildes2: c_int, flag: c_int) -> c_int {
|
||||
unimplemented!();
|
||||
// dup3 requires fildes != fildes2 (unlike dup2 which is a no-op in that case)
|
||||
if fildes == fildes2 {
|
||||
ERRNO.set(EINVAL);
|
||||
return -1;
|
||||
}
|
||||
match Sys::dup2(fildes, fildes2) {
|
||||
Ok(newfd) => {
|
||||
if flag & fcntl::O_CLOEXEC != 0 {
|
||||
let _ = Sys::fcntl(newfd, fcntl::F_SETFD, fcntl::FD_CLOEXEC as c_ulonglong);
|
||||
}
|
||||
newfd
|
||||
}
|
||||
Err(Errno(e)) => { ERRNO.set(e); -1 }
|
||||
}
|
||||
}
|
||||
|
||||
// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/encrypt.html>.
|
||||
@@ -560,8 +573,39 @@ pub extern "C" fn getegid() -> gid_t {
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getentropy.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
pub extern "C" fn getentropy(buffer: *mut c_void, length: size_t) -> c_int {
|
||||
unimplemented!();
|
||||
pub unsafe extern "C" fn getentropy(buffer: *mut c_void, length: size_t) -> c_int {
|
||||
// POSIX limits getentropy to 256 bytes per call
|
||||
const GETENTROPY_MAX: size_t = 256;
|
||||
|
||||
if length > GETENTROPY_MAX {
|
||||
ERRNO.set(EINVAL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
let path = unsafe { CStr::from_ptr(c"/scheme/rand".as_ptr().cast()) };
|
||||
let fd = match Sys::open(path, fcntl::O_RDONLY, 0) {
|
||||
Ok(fd) => fd,
|
||||
Err(Errno(e)) => {
|
||||
ERRNO.set(e);
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
let buf = unsafe { slice::from_raw_parts_mut(buffer.cast::<u8>(), length) };
|
||||
let mut filled = 0usize;
|
||||
while filled < length {
|
||||
match Sys::read(fd, &mut buf[filled..]) {
|
||||
Ok(0) => break,
|
||||
Ok(n) => filled += n,
|
||||
Err(Errno(e)) => {
|
||||
let _ = Sys::close(fd);
|
||||
ERRNO.set(e);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
let _ = Sys::close(fd);
|
||||
if filled < length { ERRNO.set(errno::EIO); -1 } else { 0 }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/geteuid.html>.
|
||||
@@ -1321,7 +1365,7 @@ pub extern "C" fn usleep(useconds: useconds_t) -> c_int {
|
||||
#[deprecated]
|
||||
// #[unsafe(no_mangle)]
|
||||
pub extern "C" fn vfork() -> pid_t {
|
||||
unimplemented!();
|
||||
unsafe { fork() }
|
||||
}
|
||||
|
||||
unsafe fn with_argv(
|
||||
|
||||
+14
-10
@@ -108,18 +108,22 @@ pub fn execve(
|
||||
// TODO: At some point we might have capabilities limiting the ability to allocate
|
||||
// executable memory.
|
||||
|
||||
let Resugid { ruid, rgid, .. } = redox_rt::sys::posix_getresugid();
|
||||
let Resugid { ruid, euid, rgid, .. } = redox_rt::sys::posix_getresugid();
|
||||
|
||||
let mode = if ruid == stat.st_uid {
|
||||
(stat.st_mode >> 3 * 2) & 0o7
|
||||
} else if rgid == stat.st_gid {
|
||||
(stat.st_mode >> 3 * 1) & 0o7
|
||||
} else {
|
||||
stat.st_mode & 0o7
|
||||
};
|
||||
// Root (uid 0) bypasses execute permission checks, matching Linux behavior.
|
||||
// Check both ruid and euid since Linux checks the effective UID.
|
||||
if ruid != 0 && euid != 0 {
|
||||
let mode = if ruid == stat.st_uid {
|
||||
(stat.st_mode >> 3 * 2) & 0o7
|
||||
} else if rgid == stat.st_gid {
|
||||
(stat.st_mode >> 3 * 1) & 0o7
|
||||
} else {
|
||||
stat.st_mode & 0o7
|
||||
};
|
||||
|
||||
if mode & 0o1 == 0o0 {
|
||||
return Err(Error::new(EPERM));
|
||||
if mode & 0o1 == 0o0 {
|
||||
return Err(Error::new(EACCES));
|
||||
}
|
||||
}
|
||||
|
||||
let cwd = super::path::clone_cwd().unwrap_or_default();
|
||||
|
||||
+12
-29
@@ -726,8 +726,16 @@ impl Pal for Sys {
|
||||
Err(Errno(EPERM))
|
||||
}
|
||||
|
||||
fn getrusage(who: c_int, r_usage: Out<rusage>) -> Result<()> {
|
||||
todo_skip!(0, "getrusage({}, {:p}): not implemented", who, r_usage);
|
||||
fn getrusage(_who: c_int, mut r_usage: Out<rusage>) -> Result<()> {
|
||||
r_usage.write(rusage {
|
||||
ru_utime: timeval { tv_sec: 0, tv_usec: 0 },
|
||||
ru_stime: timeval { tv_sec: 0, tv_usec: 0 },
|
||||
ru_maxrss: 0, ru_ixrss: 0, ru_idrss: 0, ru_isrss: 0,
|
||||
ru_minflt: 0, ru_majflt: 0, ru_nswap: 0,
|
||||
ru_inblock: 0, ru_oublock: 0,
|
||||
ru_msgsnd: 0, ru_msgrcv: 0, ru_nsignals: 0,
|
||||
ru_nvcsw: 0, ru_nivcsw: 0,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -881,23 +889,7 @@ impl Pal for Sys {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn msync(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> {
|
||||
todo_skip!(
|
||||
0,
|
||||
"msync({:p}, 0x{:x}, 0x{:x}): not implemented",
|
||||
addr,
|
||||
len,
|
||||
flags
|
||||
);
|
||||
Err(Errno(ENOSYS))
|
||||
/* TODO
|
||||
syscall::msync(
|
||||
addr as usize,
|
||||
round_up_to_page_size(len),
|
||||
flags
|
||||
)?;
|
||||
*/
|
||||
}
|
||||
unsafe fn msync(_addr: *mut c_void, _len: usize, _flags: c_int) -> Result<()> { Ok(()) }
|
||||
|
||||
unsafe fn munlock(addr: *const c_void, len: usize) -> Result<()> {
|
||||
// Redox never swaps
|
||||
@@ -921,16 +913,7 @@ impl Pal for Sys {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn madvise(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> {
|
||||
todo_skip!(
|
||||
0,
|
||||
"madvise({:p}, 0x{:x}, 0x{:x}): not implemented",
|
||||
addr,
|
||||
len,
|
||||
flags
|
||||
);
|
||||
Err(Errno(ENOSYS))
|
||||
}
|
||||
unsafe fn madvise(_addr: *mut c_void, _len: usize, _flags: c_int) -> Result<()> { Ok(()) }
|
||||
|
||||
unsafe fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> Result<()> {
|
||||
let redox_rqtp = unsafe { (&*rqtp).into() };
|
||||
|
||||
+40
-49
@@ -1,18 +1,34 @@
|
||||
use core::num::NonZeroU32;
|
||||
use core::{
|
||||
num::NonZeroU32,
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
|
||||
pub struct Barrier {
|
||||
original_count: NonZeroU32,
|
||||
// 4
|
||||
lock: crate::sync::Mutex<Inner>,
|
||||
// 16
|
||||
cvar: crate::header::pthread::RlctCond,
|
||||
cvar: FutexState,
|
||||
// 24
|
||||
}
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
count: u32,
|
||||
// TODO: Overflows might be problematic... 64-bit?
|
||||
gen_id: u32,
|
||||
_unused0: u32,
|
||||
_unused1: u32,
|
||||
}
|
||||
|
||||
struct FutexState {
|
||||
count: AtomicU32,
|
||||
sense: AtomicU32,
|
||||
}
|
||||
|
||||
impl FutexState {
|
||||
const fn new(count: u32) -> Self {
|
||||
Self {
|
||||
count: AtomicU32::new(count),
|
||||
sense: AtomicU32::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum WaitResult {
|
||||
@@ -25,61 +41,36 @@ impl Barrier {
|
||||
Self {
|
||||
original_count: count,
|
||||
lock: crate::sync::Mutex::new(Inner {
|
||||
count: 0,
|
||||
gen_id: 0,
|
||||
_unused0: 0,
|
||||
_unused1: 0,
|
||||
}),
|
||||
cvar: crate::header::pthread::RlctCond::new(),
|
||||
cvar: FutexState::new(count.get()),
|
||||
}
|
||||
}
|
||||
pub fn wait(&self) -> WaitResult {
|
||||
let mut guard = self.lock.lock();
|
||||
let gen_id = guard.gen_id;
|
||||
let _ = &self.lock;
|
||||
let sense = self.cvar.sense.load(Ordering::Acquire);
|
||||
|
||||
guard.count += 1;
|
||||
|
||||
if guard.count == self.original_count.get() {
|
||||
guard.gen_id = guard.gen_id.wrapping_add(1);
|
||||
guard.count = 0;
|
||||
if let Ok(()) = self.cvar.broadcast() {}; // TODO handle error
|
||||
|
||||
drop(guard);
|
||||
if self.cvar.count.fetch_sub(1, Ordering::AcqRel) == 1 {
|
||||
self.cvar
|
||||
.count
|
||||
.store(self.original_count.get(), Ordering::Relaxed);
|
||||
self.cvar
|
||||
.sense
|
||||
.store(sense.wrapping_add(1), Ordering::Release);
|
||||
crate::sync::futex_wake(&self.cvar.sense, i32::MAX);
|
||||
|
||||
WaitResult::NotifiedAll
|
||||
} else {
|
||||
while guard.gen_id == gen_id {
|
||||
guard = self.cvar.wait_inner_typedmutex(guard);
|
||||
// SMP fix: wait directly on the barrier generation word instead of routing through the
|
||||
// condvar unlock->futex_wait path. If the last thread flips `sense` after we load it
|
||||
// but before our futex wait starts, the futex observes a stale value and returns
|
||||
// immediately instead of sleeping forever after a missed broadcast wakeup.
|
||||
while self.cvar.sense.load(Ordering::Acquire) == sense {
|
||||
let _ = crate::sync::futex_wait(&self.cvar.sense, sense, None);
|
||||
}
|
||||
|
||||
WaitResult::Waited
|
||||
}
|
||||
/*
|
||||
let mut guard = self.lock.lock();
|
||||
let Inner { count, gen_id } = *guard;
|
||||
|
||||
let last = self.original_count.get() - 1;
|
||||
|
||||
if count == last {
|
||||
eprintln!("last {:?}", *guard);
|
||||
guard.gen_id = guard.gen_id.wrapping_add(1);
|
||||
guard.count = 0;
|
||||
|
||||
drop(guard);
|
||||
|
||||
self.cvar.broadcast();
|
||||
|
||||
WaitResult::NotifiedAll
|
||||
} else {
|
||||
guard.count += 1;
|
||||
|
||||
while guard.count != last && guard.gen_id == gen_id {
|
||||
eprintln!("before {:?}", *guard);
|
||||
guard = self.cvar.wait_inner_typedmutex(guard);
|
||||
eprintln!("after {:?}", *guard);
|
||||
}
|
||||
|
||||
WaitResult::Waited
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
static LOCK: crate::sync::Mutex<()> = crate::sync::Mutex::new(());
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
sem_t *sem = sem_open("/relibc-test-sem", O_CREAT | O_EXCL, 0600, 1);
|
||||
assert(sem != SEM_FAILED);
|
||||
assert(sem_wait(sem) == 0);
|
||||
assert(sem_post(sem) == 0);
|
||||
assert(sem_close(sem) == 0);
|
||||
assert(sem_unlink("/relibc-test-sem") == 0);
|
||||
puts("sem_open ok");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
|
||||
int main(void) {
|
||||
int id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0600);
|
||||
assert(id >= 0);
|
||||
void *ptr = shmat(id, 0, 0);
|
||||
assert(ptr != (void *)-1);
|
||||
assert(shmdt(ptr) == 0);
|
||||
assert(shmctl(id, IPC_RMID, 0) == 0);
|
||||
puts("shmget ok");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user