0.3.0: converge relibc to upstream 0.6.0 + Red Bear patches

This commit is contained in:
2026-07-06 19:13:08 +03:00
parent 1a0edd8eeb
commit 4ef7e57571
1466 changed files with 75236 additions and 13644 deletions
-65
View File
@@ -1,65 +0,0 @@
use crate::ALLOCATOR;
use core::{
alloc::{GlobalAlloc, Layout},
sync::atomic::{AtomicUsize, Ordering},
};
use super::types::*;
extern "C" {
fn create_mspace(capacity: size_t, locked: c_int) -> usize;
fn mspace_malloc(msp: usize, bytes: size_t) -> *mut c_void;
fn mspace_memalign(msp: usize, alignment: size_t, bytes: size_t) -> *mut c_void;
fn mspace_realloc(msp: usize, oldmem: *mut c_void, bytes: size_t) -> *mut c_void;
fn mspace_free(msp: usize, mem: *mut c_void);
//fn dlmalloc(bytes: size_t) -> *mut c_void;
//fn dlmemalign(alignment: size_t, bytes: size_t) -> *mut c_void;
//fn dlrealloc(oldmem: *mut c_void, bytes: size_t) -> *mut c_void;
//fn dlfree(mem: *mut c_void);
}
pub struct Allocator {
mstate: AtomicUsize,
}
pub const NEWALLOCATOR: Allocator = Allocator {
mstate: AtomicUsize::new(0),
};
impl Allocator {
pub fn set_book_keeper(&self, mstate: usize) {
self.mstate.store(mstate, Ordering::Relaxed);
}
pub fn get_book_keeper(&self) -> usize {
self.mstate.load(Ordering::Relaxed)
}
}
unsafe impl<'a> GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
alloc_align(layout.size(), layout.align()) as *mut u8
}
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
free(ptr as *mut c_void)
}
}
pub unsafe fn alloc(size: usize) -> *mut c_void {
mspace_malloc(ALLOCATOR.get_book_keeper(), size)
}
pub unsafe fn alloc_align(size: usize, alignment: usize) -> *mut c_void {
mspace_memalign(ALLOCATOR.get_book_keeper(), alignment, size)
}
pub unsafe fn realloc(ptr: *mut c_void, size: size_t) -> *mut c_void {
mspace_realloc(ALLOCATOR.get_book_keeper(), ptr, size)
}
pub unsafe fn free(ptr: *mut c_void) {
mspace_free(ALLOCATOR.get_book_keeper(), ptr)
}
pub fn new_mspace() -> usize {
unsafe { create_mspace(0, 0) }
}
+121
View File
@@ -0,0 +1,121 @@
use core::{
alloc::{GlobalAlloc, Layout},
cell::SyncUnsafeCell,
cmp,
mem::align_of,
ptr::{self, copy_nonoverlapping, write_bytes},
sync::atomic::{AtomicPtr, Ordering},
};
mod sys;
use super::types::*;
use crate::{ALLOCATOR, sync::Mutex};
use dlmalloc::DlmallocCApi;
pub type Dlmalloc = DlmallocCApi<sys::System>;
#[allow(clippy::declare_interior_mutable_const)]
pub const NEWALLOCATOR: Allocator = Allocator::new();
pub struct Allocator {
inner: SyncUnsafeCell<Mutex<Dlmalloc>>,
pub ptr: AtomicPtr<Mutex<Dlmalloc>>,
}
impl Allocator {
#[allow(clippy::new_without_default)]
pub const fn new() -> Self {
Allocator {
inner: SyncUnsafeCell::new(Mutex::new(Dlmalloc::new(sys::System::new()))),
ptr: AtomicPtr::new(ptr::null_mut()),
}
}
pub fn get(&self) -> *const Mutex<Dlmalloc> {
let ptr = self.ptr.load(Ordering::Acquire);
if !ptr.is_null() {
return ptr;
}
self.inner.get()
}
pub fn set(&self, mspace: *const Mutex<Dlmalloc>) {
self.ptr.store(mspace.cast_mut(), Ordering::Release);
}
}
unsafe impl GlobalAlloc for Allocator {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if layout.align() <= align_of::<max_align_t>() {
unsafe { (*self.get()).lock().malloc(layout.size()) }
} else {
unsafe { (*self.get()).lock().memalign(layout.align(), layout.size()) }
}
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
unsafe { (*self.get()).lock().free(ptr) }
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
let ptr = unsafe { self.alloc(layout) };
if !ptr.is_null() && unsafe { (*self.get()).lock().calloc_must_clear(ptr) } {
unsafe { write_bytes(ptr, 0, layout.size()) };
}
ptr
}
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
if layout.align() <= align_of::<max_align_t>() {
unsafe { (*self.get()).lock().realloc(ptr, new_size) }
} else {
let new =
unsafe { self.alloc(Layout::from_size_align_unchecked(new_size, layout.align())) };
let old_size = layout.size();
if !new.is_null() {
let size = cmp::min(old_size, new_size);
unsafe { copy_nonoverlapping(ptr, new, size) };
}
unsafe { (*self.get()).lock().free(ptr) };
new
}
}
}
pub unsafe fn alloc(size: size_t) -> *mut c_void {
unsafe { (*ALLOCATOR.get()).lock().malloc(size) }.cast()
}
pub unsafe fn alloc_align(size: size_t, alignment: size_t) -> *mut c_void {
unsafe { (*ALLOCATOR.get()).lock().memalign(alignment, size) }.cast()
}
pub unsafe fn realloc(ptr: *mut c_void, size: size_t) -> *mut c_void {
if ptr.is_null() {
unsafe { (*ALLOCATOR.get()).lock().malloc(size) }.cast()
} else {
unsafe { (*ALLOCATOR.get()).lock().realloc(ptr.cast(), size) }.cast()
}
}
pub unsafe fn free(ptr: *mut c_void) {
if ptr.is_null() {
return;
}
unsafe { (*ALLOCATOR.get()).lock().free(ptr.cast()) }
}
pub unsafe fn alloc_usable_size(ptr: *mut c_void) -> size_t {
if ptr.is_null() {
return 0;
}
unsafe { (*ALLOCATOR.get()).lock().usable_size(ptr.cast()) }
}
-48
View File
@@ -1,48 +0,0 @@
extern crate ralloc;
pub use ralloc::Allocator;
unsafe fn alloc_inner(size: usize, offset: usize, align: usize) -> *mut c_void {
let ptr = ralloc::alloc(size + offset, align);
if !ptr.is_null() {
*(ptr as *mut u64) = (size + offset) as u64;
*(ptr as *mut u64).offset(1) = align as u64;
ptr.offset(offset as isize) as *mut c_void
} else {
ptr as *mut c_void
}
}
pub unsafe fn alloc(size: usize) -> *mut c_void {
alloc_inner(size, 16, 8)
}
pub unsafe fn alloc_align(size: usize, alignment: usize) -> *mut c_void {
let mut align = 32;
while align <= alignment {
align *= 2;
}
alloc_inner(size, align / 2, align)
}
pub unsafe fn realloc(ptr: *mut c_void, size: size_t) -> *mut c_void {
let old_ptr = (ptr as *mut u8).offset(-16);
let old_size = *(old_ptr as *mut u64);
let align = *(old_ptr as *mut u64).offset(1);
let ptr = ralloc::realloc(old_ptr, old_size as usize, size + 16, align as usize);
if !ptr.is_null() {
*(ptr as *mut u64) = (size + 16) as u64;
*(ptr as *mut u64).offset(1) = align;
ptr.offset(16) as *mut c_void
} else {
ptr as *mut c_void
}
}
pub unsafe fn free(ptr: *mut c_void) {
let ptr = (ptr as *mut u8).offset(-16);
let size = *(ptr as *mut u64);
let _align = *(ptr as *mut u64).offset(1);
ralloc::free(ptr, size as usize);
}
+129
View File
@@ -0,0 +1,129 @@
use crate::{
header::{
pthread::pthread_atfork,
sys_mman::{self, MREMAP_MAYMOVE},
},
platform::{Pal, Sys},
sync::Mutex,
};
use core::ptr;
use dlmalloc::Allocator;
/// System setting for Redox/Linux
pub struct System {
_priv: (),
}
impl System {
pub const fn new() -> System {
System { _priv: () }
}
}
static LOCK: Mutex<()> = Mutex::new(());
unsafe impl Allocator for System {
fn alloc(&self, size: usize) -> (*mut u8, usize, u32) {
let Ok(addr) = (unsafe {
Sys::mmap(
ptr::null_mut(),
size,
sys_mman::PROT_WRITE | sys_mman::PROT_READ,
sys_mman::MAP_ANON | sys_mman::MAP_PRIVATE,
-1,
0,
)
}) else {
return (ptr::null_mut(), 0, 0);
};
(addr.cast::<u8>(), size, 0)
}
fn remap(&self, ptr: *mut u8, oldsize: usize, newsize: usize, can_move: bool) -> *mut u8 {
let flags = if can_move { MREMAP_MAYMOVE } else { 0 };
let Ok(ptr) =
(unsafe { Sys::mremap(ptr.cast(), oldsize, newsize, flags, ptr::null_mut()) })
else {
return ptr::null_mut();
};
ptr.cast::<u8>()
}
fn free_part(&self, ptr: *mut u8, oldsize: usize, newsize: usize) -> bool {
unsafe {
if Sys::mremap(ptr.cast(), oldsize, newsize, 0, ptr::null_mut()).is_ok() {
return true;
}
Sys::munmap(ptr.add(newsize).cast(), oldsize - newsize).is_ok()
}
}
fn free(&self, ptr: *mut u8, size: usize) -> bool {
unsafe { Sys::munmap(ptr.cast(), size).is_ok() }
}
fn can_release_part(&self, _flags: u32) -> bool {
true
}
fn allocates_zeros(&self) -> bool {
true
}
fn page_size(&self) -> usize {
4096
}
}
pub fn acquire_global_lock() {
unsafe {
// SAFETY: No data inside
LOCK.manual_lock();
}
}
pub(super) fn release_global_lock() {
unsafe {
// SAFETY: No data inside
LOCK.manual_unlock();
}
}
/// Allows the allocator to remain unsable in the child process,
/// after a call to `fork(2)`
///
/// # Safety
///
/// if used, this function must be called,
/// before any allocations are made with the global allocator.
pub unsafe fn enable_alloc_after_fork() {
// atfork must only be called once, to avoid a deadlock,
// where the handler attempts to acquire the global lock twice
static mut FORK_PROTECTED: bool = false;
extern "C" fn _acquire_global_lock() {
acquire_global_lock()
}
extern "C" fn _release_global_lock() {
release_global_lock()
}
acquire_global_lock();
// if a process forks,
// it will acquire the lock before any other thread,
// protecting it from deadlock,
// due to the child being created with only the calling thread.
unsafe {
if !FORK_PROTECTED {
pthread_atfork(
Some(_acquire_global_lock),
Some(_release_global_lock),
Some(_release_global_lock),
);
FORK_PROTECTED = true;
}
}
release_global_lock();
}
+60
View File
@@ -0,0 +1,60 @@
pub const AT_NULL: usize = 0; /* End of vector */
pub const AT_IGNORE: usize = 1; /* Entry should be ignored */
pub const AT_EXECFD: usize = 2; /* File descriptor of program */
pub const AT_PHDR: usize = 3; /* Program headers for program */
pub const AT_PHENT: usize = 4; /* Size of program header entry */
pub const AT_PHNUM: usize = 5; /* Number of program headers */
pub const AT_PAGESZ: usize = 6; /* System page size */
pub const AT_BASE: usize = 7; /* Base address of interpreter */
pub const AT_FLAGS: usize = 8; /* Flags */
pub const AT_ENTRY: usize = 9; /* Entry point of program */
pub const AT_NOTELF: usize = 10; /* Program is not ELF */
pub const AT_UID: usize = 11; /* Real uid */
pub const AT_EUID: usize = 12; /* Effective uid */
pub const AT_GID: usize = 13; /* Real gid */
pub const AT_EGID: usize = 14; /* Effective gid */
pub const AT_CLKTCK: usize = 17; /* Frequency of times() */
pub const AT_PLATFORM: usize = 15; /* String identifying platform. */
pub const AT_HWCAP: usize = 16; /* Machine-dependent hints about */
pub const AT_FPUCW: usize = 18; /* Used FPU control word. */
pub const AT_DCACHEBSIZE: usize = 19; /* Data cache block size. */
pub const AT_ICACHEBSIZE: usize = 20; /* Instruction cache block size. */
pub const AT_UCACHEBSIZE: usize = 21; /* Unified cache block size. */
pub const AT_IGNOREPPC: usize = 22; /* Entry should be ignored. */
pub const AT_BASE_PLATFORM: usize = 24; /* String identifying real platforms.*/
pub const AT_RANDOM: usize = 25; /* Address of 16 random bytes. */
pub const AT_HWCAP2: usize = 26; /* More machine-dependent hints about*/
pub const AT_EXECFN: usize = 31; /* Filename of executable. */
// TODO: Downgrade aux vectors to getauxval constants on Redox, and use a regular struct for
// passing important runtime info between exec (or posix_spawn in the future) calls.
#[cfg(target_os = "redox")]
// XXX: The name AT_CWD is already used in openat... for a completely different purpose.
pub const AT_REDOX_INITIAL_CWD_PTR: usize = 32;
#[cfg(target_os = "redox")]
pub const AT_REDOX_INITIAL_CWD_LEN: usize = 33;
#[cfg(target_os = "redox")]
pub const AT_REDOX_INHERITED_SIGIGNMASK: usize = 34;
#[cfg(all(target_os = "redox", target_pointer_width = "32"))]
pub const AT_REDOX_INHERITED_SIGIGNMASK_HI: usize = 35;
#[cfg(target_os = "redox")]
pub const AT_REDOX_INHERITED_SIGPROCMASK: usize = 36;
#[cfg(all(target_os = "redox", target_pointer_width = "32"))]
pub const AT_REDOX_INHERITED_SIGPROCMASK_HI: usize = 37;
#[cfg(target_os = "redox")]
pub const AT_REDOX_UMASK: usize = 40;
#[cfg(target_os = "redox")]
pub const AT_REDOX_PROC_FD: usize = 41;
#[cfg(target_os = "redox")]
pub const AT_REDOX_THR_FD: usize = 42;
#[cfg(target_os = "redox")]
pub const AT_REDOX_NS_FD: usize = 43;
#[cfg(target_os = "redox")]
pub const AT_REDOX_CWD_FD: usize = 44;
+24 -13
View File
@@ -1,34 +1,45 @@
use super::{
super::{types::*, PalEpoll},
e, Sys,
use core::mem;
use super::{Sys, e_raw};
use crate::{
error::Result,
header::{bits_sigset_t::sigset_t, sys_epoll::epoll_event},
platform::{
PalEpoll,
types::{c_int, size_t},
},
};
use crate::header::{signal::sigset_t, sys_epoll::epoll_event};
impl PalEpoll for Sys {
fn epoll_create1(flags: c_int) -> c_int {
unsafe { e(syscall!(EPOLL_CREATE1, flags)) as c_int }
fn epoll_create1(flags: c_int) -> Result<c_int> {
Ok(unsafe { e_raw(syscall!(EPOLL_CREATE1, flags))? as c_int })
}
fn epoll_ctl(epfd: c_int, op: c_int, fd: c_int, event: *mut epoll_event) -> c_int {
unsafe { e(syscall!(EPOLL_CTL, epfd, op, fd, event)) as c_int }
unsafe fn epoll_ctl(epfd: c_int, op: c_int, fd: c_int, event: *mut epoll_event) -> Result<()> {
unsafe {
e_raw(syscall!(EPOLL_CTL, epfd, op, fd, event))?;
}
Ok(())
}
fn epoll_pwait(
unsafe fn epoll_pwait(
epfd: c_int,
events: *mut epoll_event,
maxevents: c_int,
timeout: c_int,
sigmask: *const sigset_t,
) -> c_int {
) -> Result<usize> {
let sigsetsize: size_t = mem::size_of::<sigset_t>();
unsafe {
e(syscall!(
e_raw(syscall!(
EPOLL_PWAIT,
epfd,
events,
maxevents,
timeout,
sigmask
)) as c_int
sigmask,
sigsetsize
))
}
}
}
+631 -221
View File
File diff suppressed because it is too large Load Diff
+10 -4
View File
@@ -1,10 +1,16 @@
use super::{
super::{types::*, PalPtrace},
e, Sys,
super::{PalPtrace, types::*},
Sys, e_raw,
};
use crate::error::Result;
impl PalPtrace for Sys {
fn ptrace(request: c_int, pid: pid_t, addr: *mut c_void, data: *mut c_void) -> c_int {
unsafe { e(syscall!(PTRACE, request, pid, addr, data)) as c_int }
unsafe fn ptrace(
request: c_int,
pid: pid_t,
addr: *mut c_void,
data: *mut c_void,
) -> Result<c_int> {
Ok(unsafe { e_raw(syscall!(PTRACE, request, pid, addr, data))? as c_int })
}
}
+178 -30
View File
@@ -1,57 +1,205 @@
use core::mem;
use core::{
mem,
ptr::{self, addr_of},
};
use super::{
super::{types::*, PalSignal},
e, Sys,
super::{
PalSignal,
types::{c_int, c_uint, pid_t},
},
Sys, e_raw,
};
use crate::header::{
signal::{sigaction, sigset_t, stack_t},
sys_time::itimerval,
#[allow(deprecated)]
use crate::header::sys_time::itimerval;
use crate::{
error::{Errno, Result},
header::{
bits_sigset_t::sigset_t,
bits_timespec::timespec,
signal::{
SA_RESTORER, SI_QUEUE, SIGALRM, SIGEV_SIGNAL, sigaction, sigevent, siginfo_t, sigval,
stack_t,
},
time,
},
out::Out,
platform::{self, Pal, types::timer_t},
};
impl PalSignal for Sys {
fn getitimer(which: c_int, out: *mut itimerval) -> c_int {
e(unsafe { syscall!(GETITIMER, which, out) }) as c_int
#[allow(deprecated)]
fn getitimer(which: c_int, out: &mut itimerval) -> Result<()> {
unsafe {
e_raw(syscall!(GETITIMER, which, ptr::from_mut(out)))?;
}
Ok(())
}
fn kill(pid: pid_t, sig: c_int) -> c_int {
e(unsafe { syscall!(KILL, pid, sig) }) as c_int
fn kill(pid: pid_t, sig: c_int) -> Result<()> {
e_raw(unsafe { syscall!(KILL, pid, sig) })?;
Ok(())
}
fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> Result<()> {
let info = siginfo_t {
si_addr: core::ptr::null_mut(),
si_code: SI_QUEUE,
si_errno: 0,
si_pid: 0, // TODO: GETPID?
si_signo: sig,
si_status: 0,
si_uid: 0, // TODO: GETUID?
si_value: val,
};
e_raw(unsafe { syscall!(RT_SIGQUEUEINFO, pid, sig, addr_of!(info)) }).map(|_| ())
}
fn killpg(pgrp: pid_t, sig: c_int) -> c_int {
e(unsafe { syscall!(KILL, -(pgrp as isize) as pid_t, sig) }) as c_int
fn killpg(pgrp: pid_t, sig: c_int) -> Result<()> {
e_raw(unsafe { syscall!(KILL, -(pgrp as isize) as pid_t, sig) })?;
Ok(())
}
fn raise(sig: c_int) -> c_int {
let tid = e(unsafe { syscall!(GETTID) }) as pid_t;
if tid == !0 {
-1
fn raise(sig: c_int) -> Result<()> {
let tid = e_raw(unsafe { syscall!(GETTID) })? as pid_t;
e_raw(unsafe { syscall!(TKILL, tid, sig) })?;
Ok(())
}
#[allow(deprecated)]
fn setitimer(which: c_int, new: &itimerval, old: Option<&mut itimerval>) -> Result<()> {
e_raw(unsafe {
syscall!(
SETITIMER,
which,
ptr::from_ref(new),
old.map_or_else(ptr::null_mut, ptr::from_mut)
)
})?;
Ok(())
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/alarm.html>.
fn alarm(seconds: c_uint) -> c_uint {
let mut signal_event = sigevent {
sigev_value: sigval { sival_int: 0 },
sigev_signo: SIGALRM as c_int,
sigev_notify: SIGEV_SIGNAL,
sigev_notify_attributes: ptr::null_mut(),
sigev_notify_function: None,
};
let mut timerid: timer_t = Default::default();
let timer = unsafe {
time::timer_create(
time::CLOCK_REALTIME,
ptr::from_mut(&mut signal_event),
ptr::from_mut(&mut timerid),
)
};
let value = time::itimerspec {
it_value: timespec {
tv_sec: i64::from(seconds),
tv_nsec: 0,
},
it_interval: timespec {
tv_sec: 0,
tv_nsec: 0,
},
};
let mut ovalue = Default::default();
let errno_backup = platform::ERRNO.get();
if Sys::timer_settime(timerid, 0, &value, Some(Out::from_mut(&mut ovalue))).is_err() {
platform::ERRNO.set(errno_backup);
0
} else {
e(unsafe { syscall!(TKILL, tid, sig) }) as c_int
ovalue.it_value.tv_sec as c_uint + if ovalue.it_value.tv_nsec > 0 { 1 } else { 0 }
}
}
fn setitimer(which: c_int, new: *const itimerval, old: *mut itimerval) -> c_int {
e(unsafe { syscall!(SETITIMER, which, new, old) }) as c_int
}
fn sigaction(sig: c_int, act: Option<&sigaction>, oact: Option<&mut sigaction>) -> c_int {
e(unsafe {
fn sigaction(
sig: c_int,
act: Option<&sigaction>,
oact: Option<&mut sigaction>,
) -> Result<(), Errno> {
unsafe extern "C" {
fn __restore_rt();
}
let act = act.map(|act| {
let mut act_clone = act.clone();
act_clone.sa_flags |= SA_RESTORER as c_int;
act_clone.sa_restorer = Some(__restore_rt);
act_clone
});
e_raw(unsafe {
syscall!(
RT_SIGACTION,
sig,
act.map_or_else(core::ptr::null, |x| x as *const _),
oact.map_or_else(core::ptr::null_mut, |x| x as *mut _),
act.as_ref().map_or_else(ptr::null, ptr::from_ref),
oact.map_or_else(ptr::null_mut, ptr::from_mut),
mem::size_of::<sigset_t>()
)
}) as c_int
})
.map(|_| ())
}
fn sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) -> c_int {
e(unsafe { syscall!(SIGALTSTACK, ss, old_ss) }) as c_int
unsafe fn sigaltstack(ss: Option<&stack_t>, old_ss: Option<&mut stack_t>) -> Result<()> {
e_raw(syscall!(
SIGALTSTACK,
ss.map_or_else(ptr::null, ptr::from_ref),
old_ss.map_or_else(ptr::null_mut, ptr::from_mut)
))
.map(|_| ())
}
fn sigprocmask(how: c_int, set: *const sigset_t, oset: *mut sigset_t) -> c_int {
e(unsafe { syscall!(RT_SIGPROCMASK, how, set, oset, mem::size_of::<sigset_t>()) }) as c_int
fn sigpending(set: &mut sigset_t) -> Result<()> {
e_raw(unsafe {
syscall!(
RT_SIGPENDING,
ptr::from_mut::<sigset_t>(set) as usize,
mem::size_of::<sigset_t>()
)
})
.map(|_| ())
}
fn sigprocmask(how: c_int, set: Option<&sigset_t>, oset: Option<&mut sigset_t>) -> Result<()> {
e_raw(unsafe {
syscall!(
RT_SIGPROCMASK,
how,
set.map_or_else(ptr::null, ptr::from_ref),
oset.map_or_else(ptr::null_mut, ptr::from_mut),
mem::size_of::<sigset_t>()
)
})
.map(|_| ())
}
fn sigsuspend(mask: &sigset_t) -> Errno {
unsafe {
e_raw(syscall!(
RT_SIGSUSPEND,
ptr::from_ref::<sigset_t>(mask),
size_of::<sigset_t>()
))
.expect_err("must fail")
}
}
fn sigtimedwait(
set: &sigset_t,
sig: Option<&mut siginfo_t>,
tp: Option<&timespec>,
) -> Result<c_int> {
unsafe {
e_raw(syscall!(
RT_SIGTIMEDWAIT,
ptr::from_ref(set),
sig.map_or_else(ptr::null_mut, ptr::from_mut),
tp.map_or_else(ptr::null, ptr::from_ref),
size_of::<sigset_t>()
))
.map(|s| s as c_int)
}
}
}
+67 -36
View File
@@ -1,46 +1,64 @@
use super::{
super::{types::*, PalSocket},
e, Sys,
use super::{Sys, e_raw};
use crate::{
error::Result,
header::{
bits_socklen_t::socklen_t,
sys_socket::{msghdr, sockaddr},
},
platform::{
PalSocket,
types::{c_int, c_void, size_t},
},
};
use crate::header::sys_socket::{sockaddr, socklen_t};
impl PalSocket for Sys {
unsafe fn accept(socket: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int {
e(syscall!(ACCEPT, socket, address, address_len)) as c_int
unsafe fn accept(
socket: c_int,
address: *mut sockaddr,
address_len: *mut socklen_t,
) -> Result<c_int> {
Ok(e_raw(syscall!(ACCEPT, socket, address, address_len))? as c_int)
}
unsafe fn bind(socket: c_int, address: *const sockaddr, address_len: socklen_t) -> c_int {
e(syscall!(BIND, socket, address, address_len)) as c_int
unsafe fn bind(socket: c_int, address: *const sockaddr, address_len: socklen_t) -> Result<()> {
e_raw(syscall!(BIND, socket, address, address_len))?;
Ok(())
}
unsafe fn connect(socket: c_int, address: *const sockaddr, address_len: socklen_t) -> c_int {
e(syscall!(CONNECT, socket, address, address_len)) as c_int
unsafe fn connect(
socket: c_int,
address: *const sockaddr,
address_len: socklen_t,
) -> Result<c_int> {
Ok(e_raw(syscall!(CONNECT, socket, address, address_len))? as c_int)
}
unsafe fn getpeername(
socket: c_int,
address: *mut sockaddr,
address_len: *mut socklen_t,
) -> c_int {
e(syscall!(GETPEERNAME, socket, address, address_len)) as c_int
) -> Result<()> {
e_raw(syscall!(GETPEERNAME, socket, address, address_len))?;
Ok(())
}
unsafe fn getsockname(
socket: c_int,
address: *mut sockaddr,
address_len: *mut socklen_t,
) -> c_int {
e(syscall!(GETSOCKNAME, socket, address, address_len)) as c_int
) -> Result<()> {
e_raw(syscall!(GETSOCKNAME, socket, address, address_len))?;
Ok(())
}
fn getsockopt(
unsafe fn getsockopt(
socket: c_int,
level: c_int,
option_name: c_int,
option_value: *mut c_void,
option_len: *mut socklen_t,
) -> c_int {
e(unsafe {
) -> Result<()> {
e_raw(unsafe {
syscall!(
GETSOCKOPT,
socket,
@@ -49,11 +67,13 @@ impl PalSocket for Sys {
option_value,
option_len
)
}) as c_int
})?;
Ok(())
}
fn listen(socket: c_int, backlog: c_int) -> c_int {
e(unsafe { syscall!(LISTEN, socket, backlog) }) as c_int
fn listen(socket: c_int, backlog: c_int) -> Result<()> {
e_raw(unsafe { syscall!(LISTEN, socket, backlog) })?;
Ok(())
}
unsafe fn recvfrom(
@@ -63,8 +83,8 @@ impl PalSocket for Sys {
flags: c_int,
address: *mut sockaddr,
address_len: *mut socklen_t,
) -> ssize_t {
e(syscall!(
) -> Result<usize> {
e_raw(syscall!(
RECVFROM,
socket,
buf,
@@ -72,7 +92,15 @@ impl PalSocket for Sys {
flags,
address,
address_len
)) as ssize_t
))
}
unsafe fn recvmsg(socket: c_int, msg: *mut msghdr, flags: c_int) -> Result<usize> {
e_raw(syscall!(RECVMSG, socket, msg, flags))
}
unsafe fn sendmsg(socket: c_int, msg: *const msghdr, flags: c_int) -> Result<usize> {
e_raw(syscall!(SENDMSG, socket, msg, flags))
}
unsafe fn sendto(
@@ -82,20 +110,20 @@ impl PalSocket for Sys {
flags: c_int,
dest_addr: *const sockaddr,
dest_len: socklen_t,
) -> ssize_t {
e(syscall!(
) -> Result<usize> {
e_raw(syscall!(
SENDTO, socket, buf, len, flags, dest_addr, dest_len
)) as ssize_t
))
}
fn setsockopt(
unsafe fn setsockopt(
socket: c_int,
level: c_int,
option_name: c_int,
option_value: *const c_void,
option_len: socklen_t,
) -> c_int {
e(unsafe {
) -> Result<()> {
e_raw(unsafe {
syscall!(
SETSOCKOPT,
socket,
@@ -104,18 +132,21 @@ impl PalSocket for Sys {
option_value,
option_len
)
}) as c_int
})?;
Ok(())
}
fn shutdown(socket: c_int, how: c_int) -> c_int {
e(unsafe { syscall!(SHUTDOWN, socket, how) }) as c_int
fn shutdown(socket: c_int, how: c_int) -> Result<()> {
e_raw(unsafe { syscall!(SHUTDOWN, socket, how) })?;
Ok(())
}
unsafe fn socket(domain: c_int, kind: c_int, protocol: c_int) -> c_int {
e(syscall!(SOCKET, domain, kind, protocol)) as c_int
unsafe fn socket(domain: c_int, kind: c_int, protocol: c_int) -> Result<c_int> {
Ok(e_raw(syscall!(SOCKET, domain, kind, protocol))? as c_int)
}
fn socketpair(domain: c_int, kind: c_int, protocol: c_int, sv: &mut [c_int; 2]) -> c_int {
e(unsafe { syscall!(SOCKETPAIR, domain, kind, protocol, sv.as_mut_ptr()) }) as c_int
fn socketpair(domain: c_int, kind: c_int, protocol: c_int, sv: &mut [c_int; 2]) -> Result<()> {
e_raw(unsafe { syscall!(SOCKETPAIR, domain, kind, protocol, sv.as_mut_ptr()) })?;
Ok(())
}
}
+293
View File
@@ -0,0 +1,293 @@
use core::{fmt, str::FromStr};
use crate::{c_str::CStr, io::prelude::*, sync::Mutex};
use alloc::string::{String, ToString};
use log::{Metadata, Record};
const DEFAULT_LOG_LEVEL: log::LevelFilter = log::LevelFilter::Info;
pub unsafe fn init() {
let mut logger = RedoxLogger::new();
let log_env = c"RELIBC_LOG_LEVEL".as_ptr();
#[cfg(feature = "no_trace")]
let mut trace_warn = false;
unsafe {
if let Some(env) = CStr::from_nullable_ptr(crate::header::stdlib::getenv(log_env))
&& let Ok(level) = log::LevelFilter::from_str(env.to_str().unwrap_or(""))
{
#[cfg(feature = "no_trace")]
if level == log::LevelFilter::Trace {
trace_warn = true;
}
logger = logger.with_output(OutputBuilder::stderr().with_filter(level).build());
}
if let Some(name) = CStr::from_nullable_ptr(crate::platform::program_invocation_short_name)
{
logger = logger.with_process_name(name.to_str().unwrap_or("").to_string());
}
}
if logger.enable().is_err() {
log::error!("Logger already initialized");
}
#[cfg(feature = "no_trace")]
if trace_warn {
log::warn!(
"The 'no_trace' feature is enabled but RELIBC_LOG_LEVEL=TRACE, there will be no trace logs"
);
}
}
/// Copied from redox_log crate with some modifications, in future we might use it instead?
/// An output that will be logged to. The two major outputs for most Redox system programs are
/// usually the log file, and the global stdout.
pub struct Output {
// the actual endpoint to write to.
endpoint: Mutex<Box<dyn fmt::Write + Send + 'static>>,
// useful for devices like BufWrite or BufRead. You don't want the log file to never but
// written until the program exists.
flush_on_newline: bool,
// specifies the maximum log level possible
filter: log::LevelFilter,
}
impl fmt::Debug for Output {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Output")
.field("endpoint", &"opaque")
.field("flush_on_newline", &self.flush_on_newline)
.field("filter", &self.filter)
.finish()
}
}
impl Default for Output {
fn default() -> Self {
// Uses default level of max_level_in_use == None a.k.a LogLevel::Info
OutputBuilder::stderr().build()
}
}
pub struct OutputBuilder {
endpoint: Box<dyn fmt::Write + Send + 'static>,
flush_on_newline: Option<bool>,
filter: Option<log::LevelFilter>,
ansi: Option<bool>,
}
impl OutputBuilder {
/*
pub fn in_redox_logging_scheme<A, B, C>(
category: A,
subcategory: B,
logfile: C,
) -> Result<Self, io::Error>
where
A: AsRef<OsStr>,
B: AsRef<OsStr>,
C: AsRef<OsStr>,
{
if !cfg!(target_os = "redox") {
return Ok(Self::with_endpoint(Vec::new()));
}
let mut path = PathBuf::from("/scheme/logging/");
path.push(category.as_ref());
path.push(subcategory.as_ref());
path.push(logfile.as_ref());
path.set_extension("log");
if let Some(parent) = path.parent() {
if !parent.exists() {
fs::create_dir_all(parent)?;
}
}
Ok(Self::with_endpoint(BufWriter::new(File::create(
path,
fcntl::O_CREAT | fcntl::O_CLOEXEC,
0,
)?)))
}
*/
pub fn stdout() -> Self {
Self::with_endpoint(crate::platform::FileWriter::new(1))
}
pub fn stderr() -> Self {
Self::with_endpoint(crate::platform::FileWriter::new(2))
}
pub fn with_endpoint<T>(endpoint: T) -> Self
where
T: fmt::Write + Send + 'static,
{
Self::with_dyn_endpoint(Box::new(endpoint))
}
pub fn with_dyn_endpoint(endpoint: Box<dyn fmt::Write + Send + 'static>) -> Self {
Self {
endpoint,
flush_on_newline: None,
filter: None,
ansi: None,
}
}
pub fn flush_on_newline(mut self, flush: bool) -> Self {
self.flush_on_newline = Some(flush);
self
}
pub fn with_filter(mut self, filter: log::LevelFilter) -> Self {
self.filter = Some(filter);
self
}
pub fn build(self) -> Output {
Output {
endpoint: Mutex::new(self.endpoint),
filter: self.filter.unwrap_or(DEFAULT_LOG_LEVEL),
flush_on_newline: self.flush_on_newline.unwrap_or(true),
}
}
}
#[derive(Debug, Default)]
pub struct RedoxLogger {
output: Output,
min_filter: Option<log::LevelFilter>,
max_filter: Option<log::LevelFilter>,
max_level_in_use: Option<log::LevelFilter>,
min_level_in_use: Option<log::LevelFilter>,
process_name: Option<String>,
}
impl RedoxLogger {
pub fn new() -> Self {
Self::default()
}
fn adjust_output_level(
max_filter: Option<log::LevelFilter>,
min_filter: Option<log::LevelFilter>,
max_in_use: &mut Option<log::LevelFilter>,
min_in_use: &mut Option<log::LevelFilter>,
output: &mut Output,
) {
if let Some(max) = max_filter {
output.filter = core::cmp::max(output.filter, max);
}
if let Some(min) = min_filter {
output.filter = core::cmp::min(output.filter, min);
}
match max_in_use {
&mut Some(ref mut max) => *max = core::cmp::max(output.filter, *max),
max @ &mut None => *max = Some(output.filter),
}
match min_in_use {
&mut Some(ref mut min) => *min = core::cmp::min(output.filter, *min),
min @ &mut None => *min = Some(output.filter),
}
}
pub fn with_output(mut self, mut output: Output) -> Self {
Self::adjust_output_level(
self.max_filter,
self.min_filter,
&mut self.max_level_in_use,
&mut self.min_level_in_use,
&mut output,
);
self.output = output;
self
}
pub fn with_min_level_override(mut self, min: log::LevelFilter) -> Self {
self.min_filter = Some(min);
let output = &mut self.output;
Self::adjust_output_level(
self.max_filter,
self.min_filter,
&mut self.max_level_in_use,
&mut self.min_level_in_use,
output,
);
self
}
pub fn with_max_level_override(mut self, max: log::LevelFilter) -> Self {
self.max_filter = Some(max);
let output = &mut self.output;
Self::adjust_output_level(
self.max_filter,
self.min_filter,
&mut self.max_level_in_use,
&mut self.min_level_in_use,
output,
);
self
}
pub fn with_process_name(mut self, name: String) -> Self {
self.process_name = Some(name);
self
}
pub fn enable(self) -> Result<&'static Self, log::SetLoggerError> {
let leak = Box::leak(Box::new(self));
log::set_logger(leak)?;
if let Some(max) = leak.max_level_in_use {
log::set_max_level(max);
} else {
log::set_max_level(DEFAULT_LOG_LEVEL);
}
Ok(leak)
}
fn write_record<W: fmt::Write + ?Sized>(
record: &Record,
process_name: Option<&str>,
writer: &mut W,
) -> fmt::Result {
let target = record.module_path().unwrap_or(record.target());
let level = record.level();
let message = record.args();
let show_lines = true;
let line_number = if show_lines { record.line() } else { None };
let process_name = process_name.unwrap_or("");
let line = &LineFmt(line_number);
writeln!(writer, "[{process_name}@{target}{line} {level}] {message}",)
}
}
impl log::Log for RedoxLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
self.max_level_in_use
.map(|min| metadata.level() >= min)
.unwrap_or(false)
&& self
.min_level_in_use
.map(|max| metadata.level() <= max)
.unwrap_or(false)
}
fn log(&self, record: &Record) {
let output = &self.output;
if record.metadata().level() <= output.filter {
let mut endpoint_guard = output.endpoint.lock();
let _ = Self::write_record(
record,
self.process_name.as_deref(),
endpoint_guard.as_mut(),
);
}
}
fn flush(&self) {
// no-op
}
}
struct LineFmt(Option<u32>);
impl fmt::Display for LineFmt {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(line) = self.0 {
write!(f, ":{line}")
} else {
write!(f, "")
}
}
}
+219 -35
View File
@@ -1,101 +1,143 @@
use crate::io::{self, Read, Write};
use alloc::vec::Vec;
use core::{fmt, ptr};
//! Platform abstractions and environment.
use crate::{
error::{Errno, ResultExt},
io::{self, Read, Write},
raw_cell::RawCell,
};
use alloc::{boxed::Box, vec::Vec};
use core::{cell::Cell, fmt, ptr};
pub use self::allocator::*;
#[cfg(not(feature = "ralloc"))]
#[path = "allocator/dlmalloc.rs"]
mod allocator;
#[cfg(feature = "ralloc")]
#[path = "allocator/ralloc.rs"]
mod allocator;
pub mod logger;
pub use self::pal::{Pal, PalEpoll, PalPtrace, PalSignal, PalSocket};
mod pal;
pub use self::sys::{e, Sys};
pub use self::sys::Sys;
#[cfg(all(not(feature = "no_std"), target_os = "linux"))]
#[cfg(target_os = "linux")]
#[path = "linux/mod.rs"]
mod sys;
pub(crate) mod sys;
#[cfg(all(not(feature = "no_std"), target_os = "redox"))]
#[cfg(target_os = "redox")]
#[path = "redox/mod.rs"]
mod sys;
#[cfg(test)]
mod test;
mod pte;
pub(crate) mod sys;
pub use self::rlb::{Line, RawLineBuffer};
pub mod rlb;
#[cfg(target_os = "linux")]
pub mod auxv_defs;
#[cfg(target_os = "redox")]
pub use redox_rt::auxv_defs;
use self::types::*;
pub mod types;
/// The global `errno` variable used internally in relibc.
#[thread_local]
#[allow(non_upper_case_globals)]
#[no_mangle]
pub static mut errno: c_int = 0;
pub static ERRNO: Cell<c_int> = Cell::new(0);
/// The `argv` argument available to a program's `main` function.
#[allow(non_upper_case_globals)]
pub static mut argv: *mut *mut c_char = ptr::null_mut();
#[allow(non_upper_case_globals)]
pub static mut inner_argv: Vec<*mut c_char> = Vec::new();
pub static inner_argv: RawCell<Vec<*mut c_char>> = RawCell::new(Vec::new());
#[allow(non_upper_case_globals)]
pub static mut program_invocation_name: *mut c_char = ptr::null_mut();
#[allow(non_upper_case_globals)]
pub static mut program_invocation_short_name: *mut c_char = ptr::null_mut();
#[allow(non_upper_case_globals)]
#[no_mangle]
#[unsafe(no_mangle)]
pub static mut environ: *mut *mut c_char = ptr::null_mut();
#[allow(non_upper_case_globals)]
pub static mut inner_environ: Vec<*mut c_char> = Vec::new();
pub static OUR_ENVIRON: RawCell<Vec<*mut c_char>> = RawCell::new(Vec::new());
pub fn environ_iter() -> impl Iterator<Item = *mut c_char> + 'static {
unsafe {
let mut ptrs = environ;
core::iter::from_fn(move || {
if ptrs.is_null() {
None
} else {
let ptr = ptrs.read();
if ptr.is_null() {
None
} else {
ptrs = ptrs.add(1);
Some(ptr)
}
}
})
}
}
pub trait WriteByte: fmt::Write {
fn write_u8(&mut self, byte: u8) -> fmt::Result;
}
impl<'a, W: WriteByte> WriteByte for &'a mut W {
impl<W: WriteByte> WriteByte for &mut W {
fn write_u8(&mut self, byte: u8) -> fmt::Result {
(**self).write_u8(byte)
}
}
pub struct FileWriter(pub c_int);
/// An implementation of [`core::fmt::Write`] for a file descriptor.
pub struct FileWriter(pub c_int, Option<Errno>);
impl FileWriter {
pub fn write(&mut self, buf: &[u8]) -> isize {
Sys::write(self.0, buf)
pub fn new(fd: c_int) -> Self {
Self(fd, None)
}
pub fn write(&mut self, buf: &[u8]) -> fmt::Result {
let _ = Sys::write(self.0, buf).map_err(|err| {
self.1 = Some(err);
fmt::Error
})?;
Ok(())
}
}
impl fmt::Write for FileWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write(s.as_bytes());
if let Ok(()) = self.write(s.as_bytes()) {}; // TODO handle error
Ok(())
}
}
impl WriteByte for FileWriter {
fn write_u8(&mut self, byte: u8) -> fmt::Result {
self.write(&[byte]);
if let Ok(()) = self.write(&[byte]) {}; // TODO handle error
Ok(())
}
}
/// An implementation of [`Read`] for a file descriptor.
pub struct FileReader(pub c_int);
impl FileReader {
// TODO: This is a bad interface. Rustify
pub fn read(&mut self, buf: &mut [u8]) -> isize {
Sys::read(self.0, buf)
.map(|u| u as isize)
.or_minus_one_errno()
}
}
impl Read for FileReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let i = Sys::read(self.0, buf);
let i = Sys::read(self.0, buf)
.map(|u| u as isize)
.or_minus_one_errno(); // TODO
if i >= 0 {
Ok(i as usize)
} else {
@@ -104,6 +146,7 @@ impl Read for FileReader {
}
}
/// An implementation of [`Write`]/[`core::fmt::Write`] for a byte array.
pub struct StringWriter(pub *mut u8, pub usize);
impl Write for StringWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
@@ -144,6 +187,8 @@ impl WriteByte for StringWriter {
}
}
/// An implementation of [`Write`]/[`core::fmt::Write`] for a byte array,
/// without buffer overflow protection.
pub struct UnsafeStringWriter(pub *mut u8);
impl Write for UnsafeStringWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
@@ -173,16 +218,18 @@ impl WriteByte for UnsafeStringWriter {
}
}
/// An implementation of [`Read`] for a byte array, without buffer over-read
/// protection.
pub struct UnsafeStringReader(pub *const u8);
impl Read for UnsafeStringReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
unsafe {
for i in 0..buf.len() {
for (i, inner) in buf.iter_mut().enumerate() {
if *self.0 == 0 {
return Ok(i);
}
buf[i] = *self.0;
*inner = *self.0;
self.0 = self.0.offset(1);
}
Ok(buf.len())
@@ -190,6 +237,8 @@ impl Read for UnsafeStringReader {
}
}
/// A wrapper that keeps track of the number of bytes written with the
/// underlying writer `T`.
pub struct CountingWriter<T> {
pub inner: T,
pub written: usize,
@@ -223,7 +272,7 @@ impl<T: Write> Write for CountingWriter<T> {
res
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
match self.inner.write_all(&buf) {
match self.inner.write_all(buf) {
Ok(()) => (),
Err(ref err) if err.kind() == io::ErrorKind::WriteZero => (),
Err(err) => return Err(err),
@@ -235,3 +284,138 @@ impl<T: Write> Write for CountingWriter<T> {
self.inner.flush()
}
}
// TODO: Set a global variable once get_auxvs is called, and then implement getauxval based on
// get_auxv.
#[cold]
pub unsafe fn auxv_iter<'a>(ptr: *const usize) -> impl Iterator<Item = [usize; 2]> + 'a {
struct St(*const usize);
impl Iterator for St {
type Item = [usize; 2];
fn next(&mut self) -> Option<Self::Item> {
unsafe {
if *self.0 == self::auxv_defs::AT_NULL {
return None;
}
let kind = *self.0;
let value = *self.0.add(1);
self.0 = self.0.add(2);
Some([kind, value])
}
}
}
St(ptr)
}
#[cold]
pub unsafe fn get_auxvs(ptr: *const usize) -> Box<[[usize; 2]]> {
//traverse the stack and collect argument environment variables
let mut auxvs = unsafe { auxv_iter(ptr) }.collect::<Vec<_>>();
auxvs.sort_unstable_by_key(|[kind, _]| *kind);
auxvs.into_boxed_slice()
}
// TODO: Find an auxv replacement for Redox's execv protocol
#[cold]
pub unsafe fn get_auxv_raw(ptr: *const usize, requested_kind: usize) -> Option<usize> {
unsafe { auxv_iter(ptr) }
.find_map(|[kind, value]| Some(value).filter(|_| kind == requested_kind))
}
pub fn get_auxv(auxvs: &[[usize; 2]], key: usize) -> Option<usize> {
auxvs
.binary_search_by_key(&key, |[entry_key, _]| *entry_key)
.ok()
.map(|idx| auxvs[idx][1])
}
#[cold]
#[cfg(target_os = "redox")]
// SAFETY: Must only be called when only one thread exists.
pub unsafe fn init(auxvs: Box<[[usize; 2]]>) {
use self::auxv_defs::*;
use redox_rt::proc::FdGuard;
let Some(proc_fd) = get_auxv(&auxvs, AT_REDOX_PROC_FD) else {
panic!("Missing proc and thread fd!");
};
let Some(ns_fd) = get_auxv(&auxvs, AT_REDOX_NS_FD) else {
panic!("Missing namespace fd!");
};
unsafe {
redox_rt::initialize(
FdGuard::new(proc_fd).to_upper().unwrap(),
if ns_fd == usize::MAX {
None
} else {
Some(FdGuard::new(ns_fd).to_upper().unwrap())
},
);
init_inner(auxvs)
}
}
#[cold]
#[cfg(target_os = "redox")]
pub unsafe fn init_inner(auxvs: Box<[[usize; 2]]>) {
use self::auxv_defs::*;
use crate::header::sys_stat::S_ISVTX;
use redox_rt::proc::FdGuard;
use syscall::MODE_PERM;
// TODO: Is it safe to assume setup_sighandler has been called at this point?
redox_rt::sys::this_proc_call(
&mut [],
syscall::CallFlags::empty(),
&[redox_protocols::protocol::ProcCall::SyncSigPctl as u64],
)
.expect("failed to sync signal pctl");
if let (Some(cwd_ptr), Some(cwd_len), Some(cwd_fd)) = (
get_auxv(&auxvs, AT_REDOX_INITIAL_CWD_PTR),
get_auxv(&auxvs, AT_REDOX_INITIAL_CWD_LEN),
get_auxv(&auxvs, AT_REDOX_CWD_FD),
) {
let cwd_bytes: &'static [u8] =
unsafe { core::slice::from_raw_parts(cwd_ptr as *const u8, cwd_len) };
if let (Ok(cwd_path), Some(cwd_fd)) = (
core::str::from_utf8(cwd_bytes),
(cwd_fd != usize::MAX).then(|| {
FdGuard::new(cwd_fd)
.to_upper()
.expect("failed to move cwd fd to upper table")
}),
) {
self::sys::path::set_cwd_manual(cwd_path.into(), cwd_fd);
}
}
let mut inherited_sigignmask = 0_u64;
if let Some(mask) = get_auxv(&auxvs, AT_REDOX_INHERITED_SIGIGNMASK) {
inherited_sigignmask |= mask as u64;
}
#[cfg(target_pointer_width = "32")]
if let Some(mask) = get_auxv(&auxvs, AT_REDOX_INHERITED_SIGIGNMASK_HI) {
inherited_sigignmask |= (mask as u64) << 32;
}
redox_rt::signal::apply_inherited_sigignmask(inherited_sigignmask);
let mut inherited_sigprocmask = 0_u64;
if let Some(mask) = get_auxv(&auxvs, AT_REDOX_INHERITED_SIGPROCMASK) {
inherited_sigprocmask |= mask as u64;
}
#[cfg(target_pointer_width = "32")]
if let Some(mask) = get_auxv(&auxvs, AT_REDOX_INHERITED_SIGPROCMASK_HI) {
inherited_sigprocmask |= (mask as u64) << 32;
}
redox_rt::signal::set_sigmask(Some(inherited_sigprocmask), None).unwrap();
if let Some(umask) = get_auxv(&auxvs, AT_REDOX_UMASK) {
let _ =
redox_rt::sys::swap_umask((umask as u32) & u32::from(MODE_PERM) & !(S_ISVTX as u32));
}
}
#[cfg(not(target_os = "redox"))]
pub unsafe fn init(auxvs: Box<[[usize; 2]]>) {}
+15 -6
View File
@@ -1,14 +1,23 @@
use super::super::{types::*, Pal};
use crate::header::{signal::sigset_t, sys_epoll::epoll_event};
use crate::{
error::Result,
header::{bits_sigset_t::sigset_t, sys_epoll::epoll_event},
platform::{Pal, types::c_int},
};
/// Platform abstraction for `epoll` functionality.
pub trait PalEpoll: Pal {
fn epoll_create1(flags: c_int) -> c_int;
fn epoll_ctl(epfd: c_int, op: c_int, fd: c_int, event: *mut epoll_event) -> c_int;
fn epoll_pwait(
/// Platform implementation of [`epoll_create1()`](crate::header::sys_epoll::epoll_create1) from [`sys/epoll.h`](crate::header::sys_epoll).
fn epoll_create1(flags: c_int) -> Result<c_int>;
/// Platform implementation of [`epoll_ctl()`](crate::header::sys_epoll::epoll_ctl) from [`sys/epoll.h`](crate::header::sys_epoll).
unsafe fn epoll_ctl(epfd: c_int, op: c_int, fd: c_int, event: *mut epoll_event) -> Result<()>;
/// Platform implementation of [`epoll_pwait()`](crate::header::sys_epoll::epoll_pwait) from [`sys/epoll.h`](crate::header::sys_epoll).
unsafe fn epoll_pwait(
epfd: c_int,
events: *mut epoll_event,
maxevents: c_int,
timeout: c_int,
sigmask: *const sigset_t,
) -> c_int;
) -> Result<usize>;
}
+354 -59
View File
@@ -1,15 +1,24 @@
use core::num::NonZeroU64;
use super::types::*;
use crate::{
c_str::CStr,
error::{Errno, Result},
header::{
dirent::dirent,
sys_resource::rlimit,
bits_timespec::timespec,
fcntl::{AT_EMPTY_PATH, AT_FDCWD},
signal::sigevent,
sys_resource::{rlimit, rusage},
sys_select::timeval,
sys_stat::stat,
sys_statvfs::statvfs,
sys_time::{timeval, timezone},
sys_time::timezone,
sys_utsname::utsname,
time::timespec,
time::itimerspec,
},
ld_so::tcb::OsSpecific,
out::Out,
pthread,
};
pub use self::epoll::PalEpoll;
@@ -24,91 +33,256 @@ mod signal;
pub use self::socket::PalSocket;
mod socket;
/// Platform abstraction layer, a platform-agnostic abstraction over syscalls.
pub trait Pal {
fn access(path: &CStr, mode: c_int) -> c_int;
/// Platform implementation of [`access()`](crate::header::unistd::access) from [`unistd.h`](crate::header::unistd).
fn access(path: CStr, mode: c_int) -> Result<()> {
Self::faccessat(AT_FDCWD, path, mode, 0)
}
fn brk(addr: *mut c_void) -> *mut c_void;
/// Platform implementation of [`faccessat()`](crate::header::unistd::faccessat) from [`unistd.h`](crate::header::unistd).
fn faccessat(fd: c_int, path: CStr, amode: c_int, flags: c_int) -> Result<()>;
fn chdir(path: &CStr) -> c_int;
/// Platform implementation of [`brk()`](crate::header::unistd::brk) from [`unistd.h`](crate::header::unistd).
unsafe fn brk(addr: *mut c_void) -> Result<*mut c_void>;
fn chmod(path: &CStr, mode: mode_t) -> c_int;
/// Platform implementation of [`chdir()`](crate::header::unistd::chdir) from [`unistd.h`](crate::header::unistd).
fn chdir(path: CStr) -> Result<()>;
fn chown(path: &CStr, owner: uid_t, group: gid_t) -> c_int;
/// Platform implementation of [`chmod()`](crate::header::sys_stat::chmod) from [`sys/stat.h`](crate::header::sys_stat).
fn chmod(path: CStr, mode: mode_t) -> Result<()> {
Self::fchmodat(AT_FDCWD, Some(path), mode, 0)
}
fn clock_gettime(clk_id: clockid_t, tp: *mut timespec) -> c_int;
/// Platform implementation of [`chown()`](crate::header::unistd::chown) from [`unistd.h`](crate::header::unistd).
fn chown(path: CStr, owner: uid_t, group: gid_t) -> Result<()> {
Self::fchownat(AT_FDCWD, path, owner, group, 0)
}
fn close(fildes: c_int) -> c_int;
/// Platform implementation of [`clock_getres()`](crate::header::time::clock_getres) from [`time.h`](crate::header::time).
fn clock_getres(clk_id: clockid_t, tp: Option<Out<timespec>>) -> Result<()>;
fn dup(fildes: c_int) -> c_int;
// TODO: maybe remove tp and change signature to -> Result<timespec>?
/// Platform implementation of [`clock_gettime()`](crate::header::time::clock_gettime) from [`time.h`](crate::header::time).
fn clock_gettime(clk_id: clockid_t, tp: Out<timespec>) -> Result<()>;
fn dup2(fildes: c_int, fildes2: c_int) -> c_int;
/// Platform implementation of [`clock_settime()`](crate::header::time::clock_settime) from [`time.h`](crate::header::time).
unsafe fn clock_settime(clk_id: clockid_t, tp: *const timespec) -> Result<()>;
unsafe fn execve(path: &CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> c_int;
/// Platform implementation of [`close()`](crate::header::unistd::close) from [`unistd.h`](crate::header::unistd).
fn close(fildes: c_int) -> Result<()>;
/// Platform implementation of [`dup()`](crate::header::unistd::dup) from [`unistd.h`](crate::header::unistd).
fn dup(fildes: c_int) -> Result<c_int>;
/// Platform implementation of [`dup2()`](crate::header::unistd::dup2) from [`unistd.h`](crate::header::unistd).
fn dup2(fildes: c_int, fildes2: c_int) -> Result<c_int>;
/// Platform implementation of [`execve()`](crate::header::unistd::execve) from [`unistd.h`](crate::header::unistd).
unsafe fn execve(path: CStr, argv: *const *mut c_char, envp: *const *mut c_char) -> Result<()>;
/// Platform implementation of [`fexecve()`](crate::header::unistd::fexecve) from [`unistd.h`](crate::header::unistd).
unsafe fn fexecve(
fildes: c_int,
argv: *const *mut c_char,
envp: *const *mut c_char,
) -> Result<()>;
/// Platform implementation of [`_Exit()`](crate::header::stdlib::_Exit) from [`stdlib.h`](crate::header::stdlib) (or the equivalent [`_exit()`](crate::header::unistd::_exit) from [`unistd.h`](crate::header::unistd)).
fn exit(status: c_int) -> !;
fn fchdir(fildes: c_int) -> c_int;
unsafe fn exit_thread(stack_base: *mut (), stack_size: usize) -> !;
fn fchmod(fildes: c_int, mode: mode_t) -> c_int;
/// Platform implementation of [`fchdir()`](crate::header::unistd::fchdir) from [`unistd.h`](crate::header::unistd).
fn fchdir(fildes: c_int) -> Result<()>;
fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> c_int;
/// Platform implementation of [`fchmod()`](crate::header::sys_stat::fchmod) from [`sys/stat.h`](crate::header::sys_stat).
fn fchmod(fildes: c_int, mode: mode_t) -> Result<()> {
Self::fchmodat(fildes, Some(c"".into()), mode, AT_EMPTY_PATH)
}
fn flock(fd: c_int, operation: c_int) -> c_int;
/// Platform implementation of [`fchmodat()`](crate::header::sys_stat::fchmodat) from [`sys/stat.h`](crate::header::sys_stat).
fn fchmodat(dirfd: c_int, path: Option<CStr>, mode: mode_t, flags: c_int) -> Result<()>;
fn fstat(fildes: c_int, buf: *mut stat) -> c_int;
/// Platform implementation of [`fchown()`](crate::header::unistd::fchown) from [`unistd.h`](crate::header::unistd).
fn fchown(fildes: c_int, owner: uid_t, group: gid_t) -> Result<()> {
Self::fchownat(fildes, c"".into(), owner, group, AT_EMPTY_PATH)
}
fn fstatvfs(fildes: c_int, buf: *mut statvfs) -> c_int;
/// Platform implementation of [`fchownat()`](crate::header::unistd::fchownat) from [`unistd.h`](crate::header::unistd).
fn fchownat(fildes: c_int, path: CStr, owner: uid_t, group: gid_t, flags: c_int) -> Result<()>;
fn fcntl(fildes: c_int, cmd: c_int, arg: c_int) -> c_int;
/// Platform implementation of [`fdatasync()`](crate::header::unistd::fdatasync) from [`unistd.h`](crate::header::unistd).
fn fdatasync(fildes: c_int) -> Result<()>;
fn fork() -> pid_t;
/// Platform implementation of [`flock()`](crate::header::sys_file::flock) from [`sys/file.h`](crate::header::sys_file).
fn flock(fd: c_int, operation: c_int) -> Result<()>;
fn fpath(fildes: c_int, out: &mut [u8]) -> ssize_t;
/// Platform implementation of [`fstat()`](crate::header::sys_stat::fstat) from [`sys/stat.h`](crate::header::sys_stat).
fn fstat(fildes: c_int, buf: Out<stat>) -> Result<()> {
Self::fstatat(fildes, Some(c"".into()), buf, 0)
}
fn fsync(fildes: c_int) -> c_int;
/// Platform implementation of [`fstatat()`](crate::header::sys_stat::fstatat) from [`sys/stat.h`](crate::header::sys_stat).
fn fstatat(fildes: c_int, path: Option<CStr>, buf: Out<stat>, flags: c_int) -> Result<()>;
fn ftruncate(fildes: c_int, length: off_t) -> c_int;
/// Platform implementation of [`fstatvfs()`](crate::header::sys_statvfs::fstatvfs) from [`sys/statvfs.h`](crate::header::sys_statvfs).
fn fstatvfs(fildes: c_int, buf: Out<statvfs>) -> Result<()>;
fn futex(addr: *mut c_int, op: c_int, val: c_int) -> c_int;
/// Platform implementation of [`fcntl()`](crate::header::fcntl::fcntl) from [`fcntl.h`](crate::header::fcntl).
fn fcntl(fildes: c_int, cmd: c_int, arg: c_ulonglong) -> Result<c_int>;
fn futimens(fd: c_int, times: *const timespec) -> c_int;
/// Platform implementation of [`_Fork()`](crate::header::unistd::_Fork) from [`unistd.h`](crate::header::unistd).
unsafe fn fork() -> Result<pid_t>;
fn utimens(path: &CStr, times: *const timespec) -> c_int;
fn fpath(fildes: c_int, out: &mut [u8]) -> Result<usize>;
fn getcwd(buf: *mut c_char, size: size_t) -> *mut c_char;
/// Platform implementation of [`fsync()`](crate::header::unistd::fsync) from [`unistd.h`](crate::header::unistd).
fn fsync(fildes: c_int) -> Result<()>;
fn getdents(fd: c_int, dirents: *mut dirent, bytes: usize) -> c_int;
/// Platform implementation of [`ftruncate()`](crate::header::unistd::ftruncate) from [`unistd.h`](crate::header::unistd).
fn ftruncate(fildes: c_int, length: off_t) -> Result<()>;
unsafe fn futex_wait(addr: *mut u32, val: u32, deadline: Option<&timespec>) -> Result<()>;
unsafe fn futex_wake(addr: *mut u32, num: u32) -> Result<u32>;
/// Platform implementation of [`futimens()`](crate::header::sys_stat::futimens) from [`sys/stat.h`](crate::header::sys_stat).
unsafe fn futimens(fd: c_int, times: *const timespec) -> Result<()>;
/// Platform implementation of `utimens()` (TODO) from [`sys/stat.h`](crate::header::sys_stat).
unsafe fn utimens(path: CStr, times: *const timespec) -> Result<()>;
/// Platform implementation of [`getcwd()`](crate::header::unistd::getcwd) from [`unistd.h`](crate::header::unistd).
fn getcwd(buf: Out<[u8]>) -> Result<()>;
fn getdents(fd: c_int, buf: &mut [u8], opaque_offset: u64) -> Result<usize>;
fn dir_seek(fd: c_int, opaque_offset: u64) -> Result<()>;
// SAFETY: This_dent must satisfy platform-specific size and alignment constraints. On Linux,
// this means the buffer came from a valid getdents64 invocation, whereas on Redox, every
// possible this_dent slice is safe (and will be validated).
unsafe fn dent_reclen_offset(this_dent: &[u8], offset: usize) -> Option<(u16, u64)>;
// Always successful
/// Platform implementation of [`getegid()`](crate::header::unistd::getegid) from [`unistd.h`](crate::header::unistd).
fn getegid() -> gid_t;
// Always successful
/// Platform implementation of [`geteuid()`](crate::header::unistd::geteuid) from [`unistd.h`](crate::header::unistd).
fn geteuid() -> uid_t;
// Always successful
/// Platform implementation of [`getgid()`](crate::header::unistd::getgid) from [`unistd.h`](crate::header::unistd).
fn getgid() -> gid_t;
fn getpgid(pid: pid_t) -> pid_t;
/// Platform implementation of [`getgroups()`](crate::header::unistd::getgroups) from [`unistd.h`](crate::header::unistd).
fn getgroups(list: Out<[gid_t]>) -> Result<c_int>;
/* Note that this is distinct from the legacy POSIX function
* getpagesize(), which returns a c_int. On some Linux platforms,
* page size may be determined through a syscall ("getpagesize"). */
fn getpagesize() -> usize;
/// Platform implementation of [`getpgid()`](crate::header::unistd::getpgid) from [`unistd.h`](crate::header::unistd).
fn getpgid(pid: pid_t) -> Result<pid_t>;
// Always successful
/// Platform implementation of [`getpid()`](crate::header::unistd::getpid) from [`unistd.h`](crate::header::unistd).
fn getpid() -> pid_t;
// Always successful
/// Platform implementation of [`getppid()`](crate::header::unistd::getppid) from [`unistd.h`](crate::header::unistd).
fn getppid() -> pid_t;
fn getrandom(buf: &mut [u8], flags: c_uint) -> ssize_t;
/// Platform implementation of [`getpriority()`](crate::header::sys_resource::getpriority) from [`sys/resource.h`](crate::header::sys_resource).
fn getpriority(which: c_int, who: id_t) -> Result<c_int>;
unsafe fn getrlimit(resource: c_int, rlim: *mut rlimit) -> c_int;
/// Platform implementation of [`getrandom()`](crate::header::sys_random::getrandom) from [`sys/random.h`](crate::header::sys_random).
fn getrandom(buf: &mut [u8], flags: c_uint) -> Result<usize>;
/// Platform implementation of [`getresgid()`](crate::header::unistd::getresgid) from [`unistd.h`](crate::header::unistd).
fn getresgid(
rgid: Option<Out<gid_t>>,
egid: Option<Out<gid_t>>,
sgid: Option<Out<gid_t>>,
) -> Result<()>;
/// Platform implementation of [`getresuid()`](crate::header::unistd::getresuid) from [`unistd.h`](crate::header::unistd).
fn getresuid(
ruid: Option<Out<uid_t>>,
euid: Option<Out<uid_t>>,
suid: Option<Out<uid_t>>,
) -> Result<()>;
/// Platform implementation of [`getrlimit()`](crate::header::sys_resource::getrlimit) from [`sys/resource.h`](crate::header::sys_resource).
fn getrlimit(resource: c_int, rlim: Out<rlimit>) -> Result<()>;
/// Platform implementation of [`setrlimit()`](crate::header::sys_resource::setrlimit) from [`sys/resource.h`](crate::header::sys_resource).
unsafe fn setrlimit(resource: c_int, rlim: *const rlimit) -> Result<()>;
/// Platform implementation of [`getrusage()`](crate::header::sys_resource::getrusage) from [`sys/resource.h`](crate::header::sys_resource).
fn getrusage(who: c_int, r_usage: Out<rusage>) -> Result<()>;
/// Platform implementation of [`getsid()`](crate::header::unistd::getsid) from [`unistd.h`](crate::header::unistd).
fn getsid(pid: pid_t) -> Result<pid_t>;
// Always successful
/// Platform implementation of `gettid()` (TODO) from [`unistd.h`](crate::header::unistd).
fn gettid() -> pid_t;
fn gettimeofday(tp: *mut timeval, tzp: *mut timezone) -> c_int;
/// Platform implementation of [`gettimeofday()`](crate::header::sys_time::gettimeofday) from [`sys/time.h`](crate::header::sys_time).
fn gettimeofday(tp: Out<timeval>, tzp: Option<Out<timezone>>) -> Result<()>;
/// Platform implementation of [`getuid()`](crate::header::unistd::getuid) from [`unistd.h`](crate::header::unistd).
fn getuid() -> uid_t;
fn link(path1: &CStr, path2: &CStr) -> c_int;
/// Platform implementation of [`lchown()`](crate::header::unistd::lchown) from [`unistd.h`](crate::header::unistd).
fn lchown(path: CStr, owner: uid_t, group: gid_t) -> Result<()>;
fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t;
/// Platform implementation of [`link()`](crate::header::unistd::link) from [`unistd.h`](crate::header::unistd).
fn link(path1: CStr, path2: CStr) -> Result<()> {
Self::linkat(AT_FDCWD, path1, AT_FDCWD, path2, 0)
}
fn mkdir(path: &CStr, mode: mode_t) -> c_int;
/// Platform implementation of [`linkat()`](crate::header::unistd::linkat) from [`unistd.h`](crate::header::unistd).
fn linkat(fd1: c_int, oldpath: CStr, fd2: c_int, newpath: CStr, flags: c_int) -> Result<()>;
fn mkfifo(path: &CStr, mode: mode_t) -> c_int;
/// Platform implementation of [`lseek()`](crate::header::unistd::lseek) from [`unistd.h`](crate::header::unistd).
fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> Result<off_t>;
/// Platform implementation of [`mkdirat()`](crate::header::sys_stat::mkdirat) from [`sys/stat.h`](crate::header::sys_stat).
fn mkdirat(fildes: c_int, path: CStr, mode: mode_t) -> Result<()>;
/// Platform implementation of [`mkdir()`](crate::header::sys_stat::mkdir) from [`sys/stat.h`](crate::header::sys_stat).
fn mkdir(path: CStr, mode: mode_t) -> Result<()> {
Self::mkdirat(AT_FDCWD, path, mode)
}
/// Platform implementation of [`mkfifoat()`](crate::header::sys_stat::mkfifoat) from [`sys/stat.h`](crate::header::sys_stat).
fn mkfifoat(dir_fd: c_int, path: CStr, mode: mode_t) -> Result<()>;
/// Platform implementation of [`mkfifo()`](crate::header::sys_stat::mkfifo) from [`sys/stat.h`](crate::header::sys_stat).
fn mkfifo(path: CStr, mode: mode_t) -> Result<()> {
Self::mkfifoat(AT_FDCWD, path, mode)
}
/// Platform implementation of [`mknodat()`](crate::header::sys_stat::mknodat) from [`sys/stat.h`](crate::header::sys_stat).
fn mknodat(fildes: c_int, path: CStr, mode: mode_t, dev: dev_t) -> Result<()>;
/// Platform implementation of [`mknod()`](crate::header::sys_stat::mknod) from [`sys/stat.h`](crate::header::sys_stat).
fn mknod(path: CStr, mode: mode_t, dev: dev_t) -> Result<()> {
Self::mknodat(AT_FDCWD, path, mode, dev)
}
/// Platform implementation of [`mlock()`](crate::header::sys_mman::mlock) from [`sys/mman.h`](crate::header::sys_mman).
unsafe fn mlock(addr: *const c_void, len: usize) -> Result<()>;
/// Platform implementation of [`mlockall()`](crate::header::sys_mman::mlockall) from [`sys/mman.h`](crate::header::sys_mman).
unsafe fn mlockall(flags: c_int) -> Result<()>;
/// Platform implementation of [`mmap()`](crate::header::sys_mman::mmap) from [`sys/mman.h`](crate::header::sys_mman).
unsafe fn mmap(
addr: *mut c_void,
len: usize,
@@ -116,49 +290,170 @@ pub trait Pal {
flags: c_int,
fildes: c_int,
off: off_t,
) -> *mut c_void;
) -> Result<*mut c_void>;
unsafe fn mprotect(addr: *mut c_void, len: usize, prot: c_int) -> c_int;
/// Platform implementation of [`mremap()`](crate::header::sys_mman::mremap) from [`sys/mman.h`](crate::header::sys_mman).
unsafe fn mremap(
addr: *mut c_void,
len: usize,
new_len: usize,
flags: c_int,
args: *mut c_void,
) -> Result<*mut c_void>;
unsafe fn msync(addr: *mut c_void, len: usize, flags: c_int) -> c_int;
/// Platform implementation of [`mprotect()`](crate::header::sys_mman::mprotect) from [`sys/mman.h`](crate::header::sys_mman).
unsafe fn mprotect(addr: *mut c_void, len: usize, prot: c_int) -> Result<()>;
unsafe fn munmap(addr: *mut c_void, len: usize) -> c_int;
/// Platform implementation of [`msync()`](crate::header::sys_mman::msync) from [`sys/mman.h`](crate::header::sys_mman).
unsafe fn msync(addr: *mut c_void, len: usize, flags: c_int) -> Result<()>;
fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> c_int;
/// Platform implementation of [`munlock()`](crate::header::sys_mman::munlock) from [`sys/mman.h`](crate::header::sys_mman).
unsafe fn munlock(addr: *const c_void, len: usize) -> Result<()>;
fn open(path: &CStr, oflag: c_int, mode: mode_t) -> c_int;
/// Platform implementation of [`madvise()`](crate::header::sys_mman::madvise) from [`sys/mman.h`](crate::header::sys_mman).
unsafe fn madvise(addr: *mut c_void, len: usize, flags: c_int) -> Result<()>;
fn pipe2(fildes: &mut [c_int], flags: c_int) -> c_int;
/// Platform implementation of [`munlockall()`](crate::header::sys_mman::munlockall) from [`sys/mman.h`](crate::header::sys_mman).
unsafe fn munlockall() -> Result<()>;
unsafe fn pte_clone(stack: *mut usize) -> pid_t;
/// Platform implementation of [`munmap()`](crate::header::sys_mman::munmap) from [`sys/mman.h`](crate::header::sys_mman).
unsafe fn munmap(addr: *mut c_void, len: usize) -> Result<()>;
fn read(fildes: c_int, buf: &mut [u8]) -> ssize_t;
/// Platform implementation of [`nanosleep()`](crate::header::time::nanosleep) from [`time.h`](crate::header::time).
unsafe fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> Result<()>;
fn readlink(pathname: &CStr, out: &mut [u8]) -> ssize_t;
/// Platform implementation of [`open()`](crate::header::fcntl::open) from [`fcntl.h`](crate::header::fcntl).
fn open(path: CStr, oflag: c_int, mode: mode_t) -> Result<c_int>;
fn rename(old: &CStr, new: &CStr) -> c_int;
/// Platform implementation of `openat()` (TODO) from [`fcntl.h`](crate::header::fcntl).
fn openat(dirfd: c_int, path: CStr, oflag: c_int, mode: mode_t) -> Result<c_int>;
fn rmdir(path: &CStr) -> c_int;
/// Platform implementation of [`pipe2()`](crate::header::unistd::pipe2) from [`unistd.h`](crate::header::unistd).
fn pipe2(fildes: Out<[c_int; 2]>, flags: c_int) -> Result<()>;
fn sched_yield() -> c_int;
/// Platform implementation of [`posix_fallocate()`](crate::header::fcntl::posix_fallocate) from [`fcntl.h`](crate::header::fcntl).
fn posix_fallocate(fd: c_int, offset: u64, length: NonZeroU64) -> Result<()>;
fn setpgid(pid: pid_t, pgid: pid_t) -> c_int;
/// Platform implementation of [`posix_getdents()`](crate::header::dirent::posix_getdents) from [`dirent.h`](crate::header::dirent).
fn posix_getdents(fildes: c_int, buf: &mut [u8]) -> Result<usize>;
fn setregid(rgid: gid_t, egid: gid_t) -> c_int;
unsafe fn rlct_clone(
stack: *mut usize,
os_specific: &mut OsSpecific,
) -> Result<pthread::OsTid, Errno>;
fn setreuid(ruid: uid_t, euid: uid_t) -> c_int;
unsafe fn rlct_kill(os_tid: pthread::OsTid, signal: usize) -> Result<()>;
fn symlink(path1: &CStr, path2: &CStr) -> c_int;
fn current_os_tid() -> pthread::OsTid;
/// Platform implementation of [`read()`](crate::header::unistd::read) from [`unistd.h`](crate::header::unistd).
fn read(fildes: c_int, buf: &mut [u8]) -> Result<usize>;
/// Platform implementation of [`pread()`](crate::header::unistd::pread) from [`unistd.h`](crate::header::unistd).
fn pread(fildes: c_int, buf: &mut [u8], offset: off_t) -> Result<usize>;
/// Platform implementation of [`readlink()`](crate::header::unistd::readlink) from [`unistd.h`](crate::header::unistd).
fn readlink(pathname: CStr, out: &mut [u8]) -> Result<usize> {
Self::readlinkat(AT_FDCWD, pathname, out)
}
/// Platform implementation of [`readlinkat()`](crate::header::unistd::readlinkat) from [`unistd.h`](crate::header::unistd).
fn readlinkat(dirfd: c_int, pathname: CStr, out: &mut [u8]) -> Result<usize>;
/// Platform implementation of [`rename()`](crate::header::stdio::rename) from [`stdio.h`](crate::header::stdio).
fn rename(old: CStr, new: CStr) -> Result<()> {
Self::renameat(AT_FDCWD, old, AT_FDCWD, new)
}
/// Platform implementation of [`renameat()`](crate::header::stdio::renameat) from [`stdio.h`](crate::header::stdio).
fn renameat(old_dir: c_int, old_path: CStr, new_dir: c_int, new_path: CStr) -> Result<()> {
Self::renameat2(old_dir, old_path, new_dir, new_path, 0)
}
/// Platform implementation of [`renameat2()`](crate::header::stdio::renameat2) from [`stdio.h`](crate::header::stdio).
fn renameat2(
old_dir: c_int,
old_path: CStr,
new_dir: c_int,
new_path: CStr,
flags: c_uint,
) -> Result<()>;
/// Platform implementation of [`rmdir()`](crate::header::unistd::rmdir) from [`unistd.h`](crate::header::unistd).
fn rmdir(path: CStr) -> Result<()>;
/// Platform implementation of [`sched_yield()`](crate::header::sched::sched_yield) from [`sched.h`](crate::header::sched).
fn sched_yield() -> Result<()>;
/// Platform implementation of [`setgroups()`](crate::header::grp::setgroups) from [`grp.h`](crate::header::grp).
unsafe fn setgroups(size: size_t, list: *const gid_t) -> Result<()>;
/// Platform implementation of [`setpgid()`](crate::header::unistd::setpgid) from [`unistd.h`](crate::header::unistd).
fn setpgid(pid: pid_t, pgid: pid_t) -> Result<()>;
/// Platform implementation of [`setpriority()`](crate::header::sys_resource::setpriority) from [`sys/resource.h`](crate::header::sys_resource).
fn setpriority(which: c_int, who: id_t, prio: c_int) -> Result<()>;
/// Platform implementation of [`setresgid()`](crate::header::unistd::setresgid) from [`unistd.h`](crate::header::unistd).
fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) -> Result<()>;
/// Platform implementation of [`setresuid()`](crate::header::unistd::setresuid) from [`unistd.h`](crate::header::unistd).
fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) -> Result<()>;
/// Platform implementation of [`setsid()`](crate::header::unistd::setsid) from [`unistd.h`](crate::header::unistd).
fn setsid() -> Result<c_int>;
/// Platform implementation of [`symlink()`](crate::header::unistd::symlink) from [`unistd.h`](crate::header::unistd).
fn symlink(path1: CStr, path2: CStr) -> Result<()> {
Self::symlinkat(path1, AT_FDCWD, path2)
}
/// Platform implementation of [`symlinkat()`](crate::header::unistd::symlinkat) from [`unistd.h`](crate::header::unistd).
fn symlinkat(path1: CStr, fd: c_int, path2: CStr) -> Result<()>;
/// Platform implementation of [`sync()`](crate::header::unistd::sync) from [`unistd.h`](crate::header::unistd).
fn sync() -> Result<()>;
/// Platform implementation of [`timer_create()`](crate::header::time::timer_create) from [`time.h`](crate::header::time).
fn timer_create(clock_id: clockid_t, evp: &sigevent, timerid: Out<timer_t>) -> Result<()>;
/// Platform implementation of [`timer_delete()`](crate::header::time::timer_delete) from [`time.h`](crate::header::time).
fn timer_delete(timerid: timer_t) -> Result<()>;
/// Platform implementation of [`timer_gettime()`](crate::header::time::timer_gettime) from [`time.h`](crate::header::time).
fn timer_gettime(timerid: timer_t, value: Out<itimerspec>) -> Result<()>;
/// Platform implementation of [`timer_settime()`](crate::header::time::timer_settime) from [`time.h`](crate::header::time).
fn timer_settime(
timerid: timer_t,
flags: c_int,
value: &itimerspec,
ovalue: Option<Out<itimerspec>>,
) -> Result<()>;
// Always successful
/// Platform implementation of [`umask()`](crate::header::sys_stat::umask) from [`sys/stat.h`](crate::header::sys_stat).
fn umask(mask: mode_t) -> mode_t;
fn uname(utsname: *mut utsname) -> c_int;
/// Platform implementation of [`uname()`](crate::header::sys_utsname::uname) from [`sys/utsname.h`](crate::header::sys_utsname).
fn uname(utsname: Out<utsname>) -> Result<()>;
fn unlink(path: &CStr) -> c_int;
/// Platform implementation of [`unlink()`](crate::header::unistd::unlink) from [`unistd.h`](crate::header::unistd).
fn unlink(path: CStr) -> Result<()> {
Self::unlinkat(AT_FDCWD, path, 0)
}
fn waitpid(pid: pid_t, stat_loc: *mut c_int, options: c_int) -> pid_t;
/// Platform implementation of [`unlinkat()`](crate::header::unistd::unlinkat) from [`unistd.h`](crate::header::unistd).
fn unlinkat(fd: c_int, path: CStr, flags: c_int) -> Result<()>;
fn write(fildes: c_int, buf: &[u8]) -> ssize_t;
/// Platform implementation of [`waitpid()`](crate::header::sys_wait::waitpid) from [`sys/wait.h`](crate::header::sys_wait).
fn waitpid(pid: pid_t, stat_loc: Option<Out<c_int>>, options: c_int) -> Result<pid_t>;
/// Platform implementation of [`write()`](crate::header::unistd::write) from [`unistd.h`](crate::header::unistd).
fn write(fildes: c_int, buf: &[u8]) -> Result<usize>;
/// Platform implementation of [`pwrite()`](crate::header::unistd::pwrite) from [`unistd.h`](crate::header::unistd).
fn pwrite(fildes: c_int, buf: &[u8], offset: off_t) -> Result<usize>;
fn verify() -> bool;
}
+12 -2
View File
@@ -1,5 +1,15 @@
use super::super::{types::*, Pal};
use crate::{
error::Result,
platform::{Pal, types::*},
};
/// Platform abstraction for `ptrace` functionality.
pub trait PalPtrace: Pal {
fn ptrace(request: c_int, pid: pid_t, addr: *mut c_void, data: *mut c_void) -> c_int;
/// Platform implementation of [`ptrace()`](crate::header::sys_ptrace::ptrace) from [`sys/ptrace.h`](crate::header::sys_ptrace).
unsafe fn ptrace(
request: c_int,
pid: pid_t,
addr: *mut c_void,
data: *mut c_void,
) -> Result<c_int>;
}
+51 -12
View File
@@ -1,23 +1,62 @@
use super::super::{types::*, Pal};
use crate::header::{
signal::{sigaction, sigset_t, stack_t},
sys_time::itimerval,
use super::super::{
Pal,
types::{c_int, c_uint, pid_t},
};
#[allow(deprecated)]
use crate::header::sys_time::itimerval;
use crate::{
error::{Errno, Result},
header::{
bits_sigset_t::sigset_t,
bits_timespec::timespec,
signal::{sigaction, siginfo_t, sigval, stack_t},
},
};
/// Platform abstraction of signal-related functionality.
pub trait PalSignal: Pal {
fn getitimer(which: c_int, out: *mut itimerval) -> c_int;
/// Platform implementation of [`getitimer()`](crate::header::sys_time::getitimer) from [`sys/time.h`](crate::header::sys_time).
#[allow(deprecated)]
fn getitimer(which: c_int, out: &mut itimerval) -> Result<()>;
fn kill(pid: pid_t, sig: c_int) -> c_int;
/// Platform implementation of [`kill()`](crate::header::signal::kill) from [`signal.h`](crate::header::signal).
fn kill(pid: pid_t, sig: c_int) -> Result<()>;
fn killpg(pgrp: pid_t, sig: c_int) -> c_int;
/// Platform implementation of [`sigqueue()`](crate::header::signal::sigqueue) from [`signal.h`](crate::header::signal).
fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> Result<()>;
fn raise(sig: c_int) -> c_int;
/// Platform implementation of [`killpg()`](crate::header::signal::killpg) from [`signal.h`](crate::header::signal).
fn killpg(pgrp: pid_t, sig: c_int) -> Result<()>;
fn setitimer(which: c_int, new: *const itimerval, old: *mut itimerval) -> c_int;
/// Platform implementation of [`raise()`](crate::header::signal::raise) from [`signal.h`](crate::header::signal).
fn raise(sig: c_int) -> Result<()>;
fn sigaction(sig: c_int, act: Option<&sigaction>, oact: Option<&mut sigaction>) -> c_int;
/// Platform implementation of [`setitimer()`](crate::header::sys_time::setitimer) from [`sys/time.h`](crate::header::sys_time).
#[allow(deprecated)]
fn setitimer(which: c_int, new: &itimerval, old: Option<&mut itimerval>) -> Result<()>;
fn sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) -> c_int;
/// Platform implementation of [`alarm()`](crate::header::unistd::alarm) from [`unistd.h`](crate::header::unistd).
fn alarm(seconds: c_uint) -> c_uint;
fn sigprocmask(how: c_int, set: *const sigset_t, oset: *mut sigset_t) -> c_int;
/// Platform implementation of [`sigaction()`](crate::header::signal::sigaction()) from [`signal.h`](crate::header::signal).
fn sigaction(sig: c_int, act: Option<&sigaction>, oact: Option<&mut sigaction>) -> Result<()>;
/// Platform implementation of [`sigaltstack()`](crate::header::signal::sigaltstack()) from [`signal.h`](crate::header::signal).
unsafe fn sigaltstack(ss: Option<&stack_t>, old_ss: Option<&mut stack_t>) -> Result<()>;
/// Platform implementation of [`sigpending()`](crate::header::signal::sigpending) from [`signal.h`](crate::header::signal).
fn sigpending(set: &mut sigset_t) -> Result<()>;
/// Platform implementation of [`sigprocmask()`](crate::header::signal::sigprocmask) from [`signal.h`](crate::header::signal).
fn sigprocmask(how: c_int, set: Option<&sigset_t>, oset: Option<&mut sigset_t>) -> Result<()>;
/// Platform implementation of [`sigsuspend()`](crate::header::signal::sigsuspend) from [`signal.h`](crate::header::signal).
fn sigsuspend(mask: &sigset_t) -> Errno; // always fails
/// Platform implementation of [`sigtimedwait()`](crate::header::signal::sigtimedwait) from [`signal.h`](crate::header::signal).
fn sigtimedwait(
set: &sigset_t,
sig: Option<&mut siginfo_t>,
tp: Option<&timespec>,
) -> Result<c_int>;
}
+54 -17
View File
@@ -1,35 +1,61 @@
use super::super::{types::*, Pal};
use crate::header::sys_socket::{sockaddr, socklen_t};
use crate::{
error::Result,
header::{
bits_socklen_t::socklen_t,
sys_socket::{msghdr, sockaddr},
},
platform::{
Pal,
types::{c_int, c_void, size_t},
},
};
/// Platform abstraction of socket functionality.
pub trait PalSocket: Pal {
unsafe fn accept(socket: c_int, address: *mut sockaddr, address_len: *mut socklen_t) -> c_int;
/// Platform implementation of [`accept()`](crate::header::sys_socket::accept) from [`sys/socket.h`](crate::header::sys_socket).
unsafe fn accept(
socket: c_int,
address: *mut sockaddr,
address_len: *mut socklen_t,
) -> Result<c_int>;
unsafe fn bind(socket: c_int, address: *const sockaddr, address_len: socklen_t) -> c_int;
/// Platform implementation of [`bind()`](crate::header::sys_socket::bind) from [`sys/socket.h`](crate::header::sys_socket).
unsafe fn bind(socket: c_int, address: *const sockaddr, address_len: socklen_t) -> Result<()>;
unsafe fn connect(socket: c_int, address: *const sockaddr, address_len: socklen_t) -> c_int;
/// Platform implementation of [`connect()`](crate::header::sys_socket::connect) from [`sys/socket.h`](crate::header::sys_socket).
unsafe fn connect(
socket: c_int,
address: *const sockaddr,
address_len: socklen_t,
) -> Result<c_int>;
/// Platform implementation of [`getpeername()`](crate::header::sys_socket::getpeername) from [`sys/socket.h`](crate::header::sys_socket).
unsafe fn getpeername(
socket: c_int,
address: *mut sockaddr,
address_len: *mut socklen_t,
) -> c_int;
) -> Result<()>;
/// Platform implementation of [`getsockname()`](crate::header::sys_socket::getsockname) from [`sys/socket.h`](crate::header::sys_socket).
unsafe fn getsockname(
socket: c_int,
address: *mut sockaddr,
address_len: *mut socklen_t,
) -> c_int;
) -> Result<()>;
fn getsockopt(
/// Platform implementation of [`getsockopt()`](crate::header::sys_socket::getsockopt) from [`sys/socket.h`](crate::header::sys_socket).
unsafe fn getsockopt(
socket: c_int,
level: c_int,
option_name: c_int,
option_value: *mut c_void,
option_len: *mut socklen_t,
) -> c_int;
) -> Result<()>;
fn listen(socket: c_int, backlog: c_int) -> c_int;
/// Platform implementation of [`listen()`](crate::header::sys_socket::listen) from [`sys/socket.h`](crate::header::sys_socket).
fn listen(socket: c_int, backlog: c_int) -> Result<()>;
/// Platform implementation of [`recvfrom()`](crate::header::sys_socket::recvfrom) from [`sys/socket.h`](crate::header::sys_socket).
unsafe fn recvfrom(
socket: c_int,
buf: *mut c_void,
@@ -37,8 +63,15 @@ pub trait PalSocket: Pal {
flags: c_int,
address: *mut sockaddr,
address_len: *mut socklen_t,
) -> ssize_t;
) -> Result<usize>;
/// Platform implementation of [`recvmsg()`](crate::header::sys_socket::recvmsg) from [`sys/socket.h`](crate::header::sys_socket).
unsafe fn recvmsg(socket: c_int, msg: *mut msghdr, flags: c_int) -> Result<usize>;
/// Platform implementation of [`sendmsg()`](crate::header::sys_socket::sendmsg) from [`sys/socket.h`](crate::header::sys_socket).
unsafe fn sendmsg(socket: c_int, msg: *const msghdr, flags: c_int) -> Result<usize>;
/// Platform implementation of [`sendto()`](crate::header::sys_socket::sendto) from [`sys/socket.h`](crate::header::sys_socket).
unsafe fn sendto(
socket: c_int,
buf: *const c_void,
@@ -46,19 +79,23 @@ pub trait PalSocket: Pal {
flags: c_int,
dest_addr: *const sockaddr,
dest_len: socklen_t,
) -> ssize_t;
) -> Result<usize>;
fn setsockopt(
/// Platform implementation of [`setsockopt()`](crate::header::sys_socket::setsockopt) from [`sys/socket.h`](crate::header::sys_socket).
unsafe fn setsockopt(
socket: c_int,
level: c_int,
option_name: c_int,
option_value: *const c_void,
option_len: socklen_t,
) -> c_int;
) -> Result<()>;
fn shutdown(socket: c_int, how: c_int) -> c_int;
/// Platform implementation of [`shutdown()`](crate::header::sys_socket::shutdown) from [`sys/socket.h`](crate::header::sys_socket).
fn shutdown(socket: c_int, how: c_int) -> Result<()>;
unsafe fn socket(domain: c_int, kind: c_int, protocol: c_int) -> c_int;
/// Platform implementation of [`socket()`](crate::header::sys_socket::socket) from [`sys/socket.h`](crate::header::sys_socket).
unsafe fn socket(domain: c_int, kind: c_int, protocol: c_int) -> Result<c_int>;
fn socketpair(domain: c_int, kind: c_int, protocol: c_int, sv: &mut [c_int; 2]) -> c_int;
/// Platform implementation of [`socketpair()`](crate::header::sys_socket::socketpair) from [`sys/socket.h`](crate::header::sys_socket).
fn socketpair(domain: c_int, kind: c_int, protocol: c_int, sv: &mut [c_int; 2]) -> Result<()>;
}
-410
View File
@@ -1,410 +0,0 @@
#![allow(non_snake_case)]
use alloc::{boxed::Box, collections::BTreeMap};
use core::{
intrinsics, ptr,
sync::atomic::{AtomicU32, Ordering},
};
use crate::{
header::{sys_mman, time::timespec},
ld_so::{
linker::Linker,
tcb::{Master, Tcb},
},
platform::{
types::{c_int, c_uint, c_void, pid_t, size_t},
Pal, Sys,
},
sync::{Mutex, Semaphore},
ALLOCATOR,
};
type pte_osThreadHandle = pid_t;
type pte_osMutexHandle = *mut Mutex<()>;
type pte_osSemaphoreHandle = *mut Semaphore;
type pte_osThreadEntryPoint = unsafe extern "C" fn(params: *mut c_void) -> *mut c_void;
#[repr(C)]
#[derive(Eq, PartialEq)]
#[allow(dead_code)]
pub enum pte_osResult {
PTE_OS_OK = 0,
PTE_OS_NO_RESOURCES,
PTE_OS_GENERAL_FAILURE,
PTE_OS_TIMEOUT,
PTE_OS_INTERRUPTED,
PTE_OS_INVALID_PARAM,
}
use self::pte_osResult::*;
static mut pid_mutexes: Option<BTreeMap<pte_osThreadHandle, pte_osMutexHandle>> = None;
static mut pid_mutexes_lock: Mutex<()> = Mutex::new(());
static mut pid_stacks: Option<BTreeMap<pte_osThreadHandle, (*mut c_void, size_t)>> = None;
static mut pid_stacks_lock: Mutex<()> = Mutex::new(());
#[thread_local]
static mut LOCALS: *mut BTreeMap<c_uint, *mut c_void> = ptr::null_mut();
static NEXT_KEY: AtomicU32 = AtomicU32::new(0);
unsafe fn locals() -> &'static mut BTreeMap<c_uint, *mut c_void> {
if LOCALS.is_null() {
LOCALS = Box::into_raw(Box::new(BTreeMap::new()));
}
&mut *LOCALS
}
// pte_osResult pte_osInit(void)
#[no_mangle]
pub unsafe extern "C" fn pte_osInit() -> pte_osResult {
PTE_OS_OK
}
/// A shim to wrap thread entry points in logic to set up TLS, for example
unsafe extern "C" fn pte_osThreadShim(
entryPoint: pte_osThreadEntryPoint,
argv: *mut c_void,
mutex: pte_osMutexHandle,
tls_size: usize,
tls_masters_ptr: *mut Master,
tls_masters_len: usize,
tls_linker_ptr: *const Mutex<Linker>,
tls_mspace: usize,
) {
// The kernel allocated TLS does not have masters set, so do not attempt to copy it.
// It will be copied by the kernel.
if !tls_masters_ptr.is_null() {
let tcb = Tcb::new(tls_size).unwrap();
tcb.masters_ptr = tls_masters_ptr;
tcb.masters_len = tls_masters_len;
tcb.linker_ptr = tls_linker_ptr;
tcb.mspace = tls_mspace;
tcb.copy_masters().unwrap();
tcb.activate();
}
// Wait until pte_osThreadStart
pte_osMutexLock(mutex);
entryPoint(argv);
pte_osThreadExit();
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadCreate(
entryPoint: pte_osThreadEntryPoint,
stackSize: c_int,
_initialPriority: c_int,
argv: *mut c_void,
ppte_osThreadHandle: *mut pte_osThreadHandle,
) -> pte_osResult {
// Create a locked mutex, unlocked by pte_osThreadStart
let mutex: pte_osMutexHandle = Box::into_raw(Box::new(Mutex::locked(())));
let stack_size = if stackSize == 0 {
1024 * 1024
} else {
stackSize as usize
};
let stack_base = sys_mman::mmap(
ptr::null_mut(),
stack_size,
sys_mman::PROT_READ | sys_mman::PROT_WRITE,
sys_mman::MAP_SHARED | sys_mman::MAP_ANONYMOUS,
-1,
0,
);
if stack_base as isize == -1 {
return PTE_OS_GENERAL_FAILURE;
}
ptr::write_bytes(stack_base as *mut u8, 0, stack_size);
let stack_end = stack_base.add(stack_size);
let mut stack = stack_end as *mut usize;
{
let mut push = |value: usize| {
stack = stack.offset(-1);
*stack = value;
};
//WARNING: Stack must be 128-bit aligned for SSE
if let Some(tcb) = Tcb::current() {
push(tcb.mspace as usize);
push(tcb.linker_ptr as usize);
push(tcb.masters_len);
push(tcb.masters_ptr as usize);
push(tcb.tls_len);
} else {
push(ALLOCATOR.get_book_keeper());
push(0);
push(0);
push(0);
push(0);
}
push(mutex as usize);
push(argv as usize);
push(entryPoint as usize);
push(pte_osThreadShim as usize);
}
let id = Sys::pte_clone(stack);
if id < 0 {
return PTE_OS_GENERAL_FAILURE;
}
pte_osMutexLock(&mut pid_mutexes_lock);
if pid_mutexes.is_none() {
pid_mutexes = Some(BTreeMap::new());
}
pid_mutexes.as_mut().unwrap().insert(id, mutex);
pte_osMutexUnlock(&mut pid_mutexes_lock);
pte_osMutexLock(&mut pid_stacks_lock);
if pid_stacks.is_none() {
pid_stacks = Some(BTreeMap::new());
}
pid_stacks
.as_mut()
.unwrap()
.insert(id, (stack_base, stack_size));
pte_osMutexUnlock(&mut pid_stacks_lock);
*ppte_osThreadHandle = id;
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadStart(handle: pte_osThreadHandle) -> pte_osResult {
let mut ret = PTE_OS_GENERAL_FAILURE;
pte_osMutexLock(&mut pid_mutexes_lock);
if let Some(ref mutexes) = pid_mutexes {
if let Some(mutex) = mutexes.get(&handle) {
pte_osMutexUnlock(*mutex);
ret = PTE_OS_OK;
}
}
pte_osMutexUnlock(&mut pid_mutexes_lock);
ret
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadExit() {
Sys::exit(0);
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadExitAndDelete(handle: pte_osThreadHandle) -> pte_osResult {
let res = pte_osThreadDelete(handle);
if res != PTE_OS_OK {
return res;
}
pte_osThreadExit();
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadDelete(handle: pte_osThreadHandle) -> pte_osResult {
pte_osMutexLock(&mut pid_mutexes_lock);
if let Some(ref mut mutexes) = pid_mutexes {
if let Some(mutex) = mutexes.remove(&handle) {
Box::from_raw(mutex);
}
}
pte_osMutexUnlock(&mut pid_mutexes_lock);
pte_osMutexLock(&mut pid_stacks_lock);
if let Some(ref mut stacks) = pid_stacks {
if let Some((stack_base, stack_size)) = stacks.remove(&handle) {
//TODO: this currently unmaps the thread's stack, while it is being used!
//sys_mman::munmap(stack_base, stack_size);
}
}
pte_osMutexUnlock(&mut pid_stacks_lock);
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadWaitForEnd(handle: pte_osThreadHandle) -> pte_osResult {
let mut status = 0;
Sys::waitpid(handle, &mut status, 0);
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadCancel(handle: pte_osThreadHandle) -> pte_osResult {
//TODO: allow cancel of thread
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadCheckCancel(handle: pte_osThreadHandle) -> pte_osResult {
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadSleep(msecs: c_uint) {
let tm = timespec {
tv_sec: msecs as i64 / 1000,
tv_nsec: (msecs % 1000) as i64 * 1000000,
};
Sys::nanosleep(&tm, ptr::null_mut());
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadGetHandle() -> pte_osThreadHandle {
Sys::gettid()
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadGetPriority(threadHandle: pte_osThreadHandle) -> c_int {
// XXX Shouldn't Redox support priorities?
1
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadSetPriority(
threadHandle: pte_osThreadHandle,
newPriority: c_int,
) -> pte_osResult {
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadGetMinPriority() -> c_int {
1
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadGetMaxPriority() -> c_int {
1
}
#[no_mangle]
pub unsafe extern "C" fn pte_osThreadGetDefaultPriority() -> c_int {
1
}
#[no_mangle]
pub unsafe extern "C" fn pte_osMutexCreate(pHandle: *mut pte_osMutexHandle) -> pte_osResult {
*pHandle = Box::into_raw(Box::new(Mutex::new(())));
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osMutexDelete(handle: pte_osMutexHandle) -> pte_osResult {
Box::from_raw(handle);
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osMutexLock(handle: pte_osMutexHandle) -> pte_osResult {
(*handle).manual_lock();
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osMutexUnlock(handle: pte_osMutexHandle) -> pte_osResult {
(*handle).manual_unlock();
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osSemaphoreCreate(
initialValue: c_int,
pHandle: *mut pte_osSemaphoreHandle,
) -> pte_osResult {
*pHandle = Box::into_raw(Box::new(Semaphore::new(initialValue)));
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osSemaphoreDelete(handle: pte_osSemaphoreHandle) -> pte_osResult {
Box::from_raw(handle);
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osSemaphorePost(
handle: pte_osSemaphoreHandle,
count: c_int,
) -> pte_osResult {
(*handle).post();
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osSemaphorePend(
handle: pte_osSemaphoreHandle,
pTimeout: *mut c_uint,
) -> pte_osResult {
//TODO: pTimeout
(*handle).wait();
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osSemaphoreCancellablePend(
handle: pte_osSemaphoreHandle,
pTimeout: *mut c_uint,
) -> pte_osResult {
//TODO
pte_osSemaphorePend(handle, pTimeout)
}
#[no_mangle]
pub unsafe extern "C" fn pte_osAtomicExchange(ptarg: *mut c_int, val: c_int) -> c_int {
intrinsics::atomic_xchg(ptarg, val)
}
#[no_mangle]
pub unsafe extern "C" fn pte_osAtomicCompareExchange(
pdest: *mut c_int,
exchange: c_int,
comp: c_int,
) -> c_int {
intrinsics::atomic_cxchg(pdest, comp, exchange).0
}
#[no_mangle]
pub unsafe extern "C" fn pte_osAtomicExchangeAdd(pAppend: *mut c_int, value: c_int) -> c_int {
intrinsics::atomic_xadd(pAppend, value)
}
#[no_mangle]
pub unsafe extern "C" fn pte_osAtomicDecrement(pdest: *mut c_int) -> c_int {
intrinsics::atomic_xadd(pdest, -1) - 1
}
#[no_mangle]
pub unsafe extern "C" fn pte_osAtomicIncrement(pdest: *mut c_int) -> c_int {
intrinsics::atomic_xadd(pdest, 1) + 1
}
#[no_mangle]
pub unsafe extern "C" fn pte_osTlsSetValue(index: c_uint, value: *mut c_void) -> pte_osResult {
locals().insert(index, value);
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osTlsGetValue(index: c_uint) -> *mut c_void {
locals().get_mut(&index).copied().unwrap_or(ptr::null_mut())
}
#[no_mangle]
pub unsafe extern "C" fn pte_osTlsAlloc(pKey: *mut c_uint) -> pte_osResult {
*pKey = NEXT_KEY.fetch_add(1, Ordering::SeqCst);
PTE_OS_OK
}
#[no_mangle]
pub unsafe extern "C" fn pte_osTlsFree(index: c_uint) -> pte_osResult {
// XXX free keys
PTE_OS_OK
}
+99 -54
View File
@@ -1,13 +1,13 @@
use super::{
super::{types::*, Pal, PalEpoll},
super::{Pal, PalEpoll, types::*},
Sys,
};
use crate::{
error::Errno,
fs::File,
header::{errno::*, fcntl::*, signal::sigset_t, sys_epoll::*},
header::{bits_sigset_t::sigset_t, errno::*, fcntl::*, sys_epoll::*},
io::prelude::*,
platform,
};
use core::{mem, slice};
use syscall::{
@@ -15,25 +15,63 @@ use syscall::{
flag::EVENT_READ,
};
impl PalEpoll for Sys {
fn epoll_create1(flags: c_int) -> c_int {
Sys::open(c_str!("event:"), O_RDWR | flags, 0)
fn epoll_to_event_flags(epoll: c_uint) -> syscall::EventFlags {
let mut event_flags = syscall::EventFlags::empty();
if epoll & EPOLLIN != 0 {
event_flags |= syscall::EventFlags::EVENT_READ;
}
fn epoll_ctl(epfd: c_int, op: c_int, fd: c_int, event: *mut epoll_event) -> c_int {
if epoll & EPOLLOUT != 0 {
event_flags |= syscall::EventFlags::EVENT_WRITE;
}
/*TODO: support more EPOLL flags */
let unsupported = !(EPOLLIN | EPOLLOUT);
if epoll & unsupported != 0 {
log::trace!("epoll unsupported flags 0x{:X}", epoll & unsupported);
}
event_flags
}
fn event_flags_to_epoll(flags: syscall::EventFlags) -> c_uint {
let mut epoll = 0;
if flags.contains(syscall::EventFlags::EVENT_READ) {
epoll |= EPOLLIN;
}
if flags.contains(syscall::EventFlags::EVENT_WRITE) {
epoll |= EPOLLOUT;
}
epoll
}
impl PalEpoll for Sys {
fn epoll_create1(flags: c_int) -> Result<c_int, Errno> {
Sys::open(c"/scheme/event".into(), O_RDWR | flags, 0)
}
unsafe fn epoll_ctl(
epfd: c_int,
op: c_int,
fd: c_int,
event: *mut epoll_event,
) -> Result<(), Errno> {
match op {
EPOLL_CTL_ADD | EPOLL_CTL_MOD => {
Sys::write(
epfd,
&Event {
id: fd as usize,
flags: syscall::EventFlags::from_bits(unsafe { (*event).events as usize })
.expect("epoll: invalid bit pattern"),
flags: unsafe { epoll_to_event_flags((*event).events) },
// NOTE: Danger when using something smaller than 64-bit
// systems. If this is needed, use a box or something
data: unsafe { (*event).data.u64 as usize },
},
) as c_int
)?;
}
EPOLL_CTL_DEL => {
Sys::write(
@@ -44,70 +82,77 @@ impl PalEpoll for Sys {
//TODO: Is data required?
data: 0,
},
) as c_int
}
_ => {
unsafe { platform::errno = EINVAL };
return -1;
)?;
}
_ => return Err(Errno(EINVAL)),
}
Ok(())
}
fn epoll_pwait(
unsafe fn epoll_pwait(
epfd: c_int,
events: *mut epoll_event,
maxevents: c_int,
timeout: c_int,
_sigset: *const sigset_t,
) -> c_int {
// TODO: sigset
sigset: *const sigset_t,
) -> Result<usize, Errno> {
assert_eq!(mem::size_of::<epoll_event>(), mem::size_of::<Event>());
if maxevents <= 0 {
return Err(Errno(EINVAL));
}
let timer_opt = if timeout != -1 {
match File::open(c_str!("time:4"), O_RDWR) {
Err(_) => return -1,
Ok(mut timer) => {
if Sys::write(
epfd,
&Event {
id: timer.fd as usize,
flags: EVENT_READ,
data: 0,
},
) == -1
{
return -1;
}
let mut timer = File::open(c"/scheme/time/4".into(), O_RDWR)?;
Sys::write(
epfd,
&Event {
id: timer.fd as usize,
flags: EVENT_READ,
data: 0,
},
)?;
let mut time = TimeSpec::default();
if let Err(err) = timer.read(&mut time) {
return -1;
}
time.tv_sec += (timeout as i64) / 1000;
time.tv_nsec += (timeout % 1000) * 1000000;
if let Err(err) = timer.write(&time) {
return -1;
}
let mut time = TimeSpec::default();
let _ = timer
.read(&mut time)
.map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?;
time.tv_sec += (timeout as i64) / 1000;
time.tv_nsec += (timeout % 1000) * 1000000;
let _ = timer
.write(&time)
.map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))?;
Some(timer)
}
}
Some(timer)
} else {
None
};
let bytes_read = Sys::read(epfd, unsafe {
slice::from_raw_parts_mut(events as *mut u8, maxevents as usize)
});
if bytes_read == -1 {
return -1;
}
let callback = || {
let res = syscall::read(epfd as usize, unsafe {
slice::from_raw_parts_mut(
events as *mut u8,
maxevents as usize * mem::size_of::<syscall::Event>(),
)
});
res
};
let bytes_read = if sigset.is_null() {
callback()
} else {
// Allowset is inverse of sigset mask
let allowset = !unsafe { *sigset };
redox_rt::signal::callback_or_signal_async(allowset, callback)
}?;
let read = bytes_read as usize / mem::size_of::<syscall::Event>();
let mut count = 0;
for i in 0..read {
unsafe {
let event_ptr = events.add(i);
let target_ptr = events.add(count);
let event = *(event_ptr as *mut Event);
if let Some(ref timer) = timer_opt {
if event.id as c_int == timer.fd {
@@ -115,8 +160,8 @@ impl PalEpoll for Sys {
continue;
}
}
*event_ptr = epoll_event {
events: event.flags.bits() as _,
*target_ptr = epoll_event {
events: event_flags_to_epoll(event.flags),
data: epoll_data {
u64: event.data as u64,
},
@@ -126,6 +171,6 @@ impl PalEpoll for Sys {
}
}
count as c_int
Ok(count)
}
}
+82
View File
@@ -0,0 +1,82 @@
use core::mem::size_of;
use crate::header::{
bits_sigset_t::sigset_t,
bits_timespec::timespec,
fcntl::{O_CLOEXEC, O_CREAT, O_RDWR},
};
use super::libredox::RawResult;
use syscall::{EINVAL, Error, Result};
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_event_queue_create_v1(flags: u32) -> RawResult {
Error::mux((|| {
if flags != 0 {
return Err(Error::new(EINVAL));
}
Ok(super::libredox::open("/scheme/event", O_CLOEXEC | O_CREAT | O_RDWR, 0o700)? as usize)
})())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_event_queue_get_events_v1(
queue: usize,
buf: *mut event::raw::RawEventV1,
buf_count: usize,
flags: u32,
_timeout: *const timespec,
_sigset: *const sigset_t,
) -> RawResult {
Error::mux((|| -> Result<usize> {
if flags != 0 || buf_count == 0 {
return Err(Error::new(EINVAL));
}
let mut event = syscall::Event::default();
let res = syscall::read(queue, &mut event)?;
assert_eq!(
res,
size_of::<syscall::Event>(),
"EOF not yet defined for event queue reads"
);
unsafe {
buf.write(event::raw::RawEventV1 {
fd: event.id,
flags: event::raw::EventFlags::from(event.flags).bits(),
user_data: event.data,
})
};
Ok(1)
})())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_event_queue_ctl_v1(
queue: usize,
fd: usize,
flags: u32,
user_data: usize,
) -> RawResult {
Error::mux((|| -> Result<usize> {
let res = syscall::write(
queue,
&syscall::Event {
id: fd,
flags: event::raw::EventFlags::from_bits(flags)
.ok_or(Error::new(EINVAL))?
.into(),
data: user_data,
},
)?;
assert_eq!(
res,
size_of::<syscall::Event>(),
"EOF not yet defined for event queue writes"
);
Ok(0)
})())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_event_queue_destroy_v1(queue: usize) -> RawResult {
Error::mux(syscall::close(queue))
}
+618
View File
@@ -0,0 +1,618 @@
use core::{
convert::Infallible,
num::{NonZeroU64, NonZeroUsize},
};
use crate::{
c_str::{CStr, CString},
fs::File,
header::{limits::PATH_MAX, string::strlen},
io::{BufReader, SeekFrom, prelude::*},
platform::types::*,
};
use redox_rt::{
RtTcb,
proc::{ExtraInfo, FdGuard, FdGuardUpper, FexecResult, InterpOverride},
sys::Resugid,
};
use syscall::{data::Stat, error::*, flag::*};
fn fexec_impl(
exec_file: FdGuardUpper,
path: &[u8],
args: &[&[u8]],
envs: &[&[u8]],
extrainfo: &ExtraInfo,
interp_override: Option<InterpOverride>,
) -> Result<Infallible> {
let FexecResult::Interp {
path,
interp_override: new_interp_override,
} = redox_rt::proc::fexec_impl(
exec_file,
&RtTcb::current().thread_fd(),
redox_rt::current_proc_fd(),
path,
args,
envs,
extrainfo,
interp_override,
)?;
// According to elf(5), PT_INTERP requires that the interpreter path be
// null-terminated. Violating this should therefore give the "format error" ENOEXEC.
let path_cstr = CStr::from_bytes_with_nul(&path).map_err(|_| Error::new(ENOEXEC))?;
return execve(
Executable::AtPath(path_cstr),
ArgEnv::Parsed { args, envs },
Some(new_interp_override),
);
}
pub enum ArgEnv<'a> {
C {
argv: *const *mut c_char,
envp: *const *mut c_char,
},
Parsed {
args: &'a [&'a [u8]],
envs: &'a [&'a [u8]],
},
}
pub enum Executable<'a> {
AtPath(CStr<'a>),
InFd { file: File, arg0: &'a [u8] },
}
pub fn execve(
exec: Executable<'_>,
arg_env: ArgEnv,
interp_override: Option<InterpOverride>,
) -> Result<Infallible> {
// NOTE: We must omit O_CLOEXEC and close manually, otherwise it will be closed before we
// have even read it!
let (mut image_file, stat, arg0) = match exec {
Executable::AtPath(path) => {
let Ok(src_fd) = File::open(path, O_RDONLY as c_int) else {
return Err(Error::new(ENOENT));
};
let mut src_stat = Stat::default();
redox_rt::sys::fstat(*src_fd as usize, &mut src_stat)?;
#[cfg(feature = "ld_so_cache")]
let src_fd = {
let mtime_sec = src_stat.st_mtime;
let mtime_nsec = src_stat.st_mtime_nsec;
let safe_path = path.to_str().unwrap_or("").replace('/', "_");
let shm_path_owned = format!(
"/scheme/shm/ld.so.cache.{}.{}.{}\0",
safe_path, mtime_sec, mtime_nsec
);
let shm_path =
unsafe { CStr::from_bytes_with_nul_unchecked(shm_path_owned.as_bytes()) };
let mut file_opt = None;
if let Ok(shm_fd) = File::open(shm_path, O_RDONLY as c_int) {
if let Ok(shm_stat) = shm_fd.fstat()
&& shm_stat.st_size > 0
{
file_opt = Some(shm_fd);
}
}
file_opt.unwrap_or(src_fd)
};
(src_fd, src_stat, path.to_bytes())
}
Executable::InFd { file, arg0 } => {
let mut stat = Stat::default();
redox_rt::sys::fstat(*file as usize, &mut stat)?;
(file, stat, arg0)
}
};
// With execve now being implemented in userspace, we need to check ourselves that this
// file is actually executable. While checking for read permission is unnecessary as the
// scheme will not allow us to read otherwise, the execute bit is completely unenforced.
//
// But we do (currently) have the permission to mmap executable memory and fill it with any
// program, even marked non-executable, so really the best we can do is check that nothing is
// executed by accident.
//
// TODO: At some point we might have capabilities limiting the ability to allocate
// executable memory.
let Resugid { ruid, euid, rgid, .. } = redox_rt::sys::posix_getresugid();
// 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(EACCES));
}
}
let cwd: Box<[u8]> = super::path::clone_cwd().unwrap_or_default().into();
// Path to interpreter binary and args if found
let (interpreter_path, interpreter_args) = { parse_interpreter(&mut image_file)? };
// Total number of arguments which includes the interpreter if interpreted and its args
let mut len = 0;
if interpreter_path.is_some() {
len = 1;
if interpreter_args.is_some() {
len = 2;
}
}
// Count arguments for `exec` which is different from the interpreter's args
//
// When there's an interpreter, we skip the original `argv[0]` and replace it with the script
// path (`arg0`).
match arg_env {
ArgEnv::C { argv, .. } => unsafe {
let mut count = 0;
let ptr = if interpreter_path.is_some() && !(*argv).is_null() {
argv.add(1)
} else {
argv
};
while !(*ptr.add(count)).is_null() {
count += 1;
}
len += count;
},
ArgEnv::Parsed { args, .. } => {
let skip = if interpreter_path.is_some() { 1 } else { 0 };
len += args.len().saturating_sub(skip);
}
}
let mut args: Vec<&[u8]> = Vec::with_capacity(len);
if let Some(interpreter) = &interpreter_path {
image_file = File::open(CStr::borrow(&interpreter), O_RDONLY as c_int)
.map_err(|_| Error::new(ENOENT))?;
// Push interpreter to arguments
args.push(interpreter.as_bytes());
// Push interpreter args, if any, to our main arguments
if let Some(args_ref) = interpreter_args.as_ref() {
args.push(args_ref.as_bytes());
}
} else {
image_file
.seek(SeekFrom::Start(0))
.map_err(|_| Error::new(EIO))?;
}
let (args, envs): (Vec<_>, Vec<_>) = match arg_env {
ArgEnv::C { mut argv, mut envp } => unsafe {
// Arguments
if interpreter_path.is_some() {
args.push(arg0);
if !(*argv).is_null() {
argv = argv.add(1);
}
}
while !argv.read().is_null() {
let arg = argv.read();
let len = strlen(arg);
args.push(core::slice::from_raw_parts(arg as *const u8, len));
argv = argv.add(1);
}
// Environment variables
let mut len = 0;
while !envp.add(len).read().is_null() {
len += 1;
}
let mut envs: Vec<&[u8]> = Vec::with_capacity(len);
while !envp.read().is_null() {
let env = envp.read();
let len = strlen(env);
envs.push(core::slice::from_raw_parts(env as *const u8, len));
envp = envp.add(1);
}
(args, envs)
},
ArgEnv::Parsed {
args: new_args,
envs,
} => {
if interpreter_path.is_some() {
args.push(arg0);
args.extend(new_args.iter().skip(1));
} else {
args.extend(new_args);
}
(args, Vec::from(envs))
}
};
// TODO: Convert image_file to FdGuard earlier?
let exec_fd_guard = FdGuard::new(image_file.fd as usize).to_upper().unwrap();
core::mem::forget(image_file);
let sigprocmask = redox_rt::signal::get_sigmask().unwrap();
let extrainfo = ExtraInfo {
cwd: Some(&cwd),
sigignmask: redox_rt::signal::get_sigignmask_to_inherit(),
sigprocmask,
umask: redox_rt::sys::get_umask(),
thr_fd: RtTcb::current().thread_fd().as_raw_fd(),
proc_fd: redox_rt::current_proc_fd().as_raw_fd(),
ns_fd: redox_rt::current_namespace_fd().ok(),
cwd_fd: super::path::current_dir()
.ok()
.map(|fd| fd.as_ref().unwrap().fd.as_raw_fd()),
};
fexec_impl(
exec_fd_guard,
arg0,
&args,
&envs,
&extrainfo,
interp_override,
)
}
// Parse the interpreter and its args if `reader` starts with a shebang (#!).
//
// # Return
// * Path to the interpreter and its args, if any
// * `None` if no shebang
// * An error if parsing failed
//
// # Errors
// * E2BIG: The full path of the shebang is greater than [`PATH_MAX`]
// * ENOEXEC: Invalid shebang line, such as a line of all whitespace
// * EIO: Failure reading from `reader`
fn parse_interpreter<R>(image_file: &mut R) -> Result<(Option<CString>, Option<CString>)>
where
R: Read + Seek,
{
// Read shebang (for example #!/bin/sh)
let mut read = 0;
let mut shebang = [0; 2];
while read < 2 {
match image_file
.read(&mut shebang)
.map_err(|_| Error::new(ENOEXEC))?
{
0 => break,
i => read += i,
}
}
if shebang != *b"#!" {
return Ok((None, None));
}
// BufReader is created after parsing the shebang because it doesn't make sense to buffer
// bytes to read two bytes especially if `image_file` is NOT a script.
let mut reader_ = BufReader::new(image_file);
let reader = &mut reader_;
// Skip prepended whitespace for interpreter
// Ex: #! /usr/bin/python
let pos = reader
.bytes()
.position(|byte| byte.ok().is_some_and(|byte| !byte.is_ascii_whitespace()))
.and_then(|pos| (pos + 2).try_into().ok())
// Fail if all whitespace or empty
.ok_or_else(|| Error::new(ENOEXEC))?;
// We read the non-whitespace character which sets reader position one past it.
// Seeking back to that position is essentially free since reads are buffered and it's
// unlikely that there was enough whitespace that we performed multiple reads.
reader
.seek(SeekFrom::Start(pos))
.map_err(|_| Error::new(EIO))?;
// Scan the first line once for the mandatory interpreter and optional args.
// This is nicer than using `read_until` or `read_line` because it avoids having to scan the
// data twice to check if there are args.
let mut interp_offset = None;
let mut args_offset = None;
for (i, byte) in reader.bytes().enumerate() {
let byte = byte.map_err(|_| Error::new(EIO))?;
match (byte, interp_offset, args_offset) {
// No args; only interpreter
(b'\n', None, None) => {
interp_offset = NonZeroUsize::new(i);
break;
}
// Interpreter found, so we're scanning for where the args ends
(b'\n', Some(_), None) => {
args_offset = NonZeroUsize::new(i);
break;
}
// Found args so interpreter ends at `i`
(b' ', None, None) => {
interp_offset = NonZeroUsize::new(i);
}
_ => {}
}
}
// Interpreter is mandatory since we found #! earlier
let Some(interp_offset) = interp_offset.map(NonZeroUsize::get) else {
return Err(Error::new(ENOEXEC));
};
// We need u64s and usizes; converting them now is easier
let Ok(interp_offset_u64) = interp_offset.try_into() else {
return Err(Error::new(E2BIG));
};
let args_offset_u64: Option<NonZeroU64> = args_offset
.map(|offset| offset.try_into())
.transpose()
.map_err(|_| Error::new(E2BIG))?;
// Spec: full length of the shebang can't exceed max path length
let shebang_len = pos
.checked_add(interp_offset_u64)
.and_then(|len| len.checked_add(args_offset_u64.map(NonZeroU64::get).unwrap_or_default()))
.ok_or_else(|| Error::new(E2BIG))?;
// PATH_MAX is a small number that fits into u64 so `as` is okay
if shebang_len > PATH_MAX as u64 {
return Err(Error::new(E2BIG));
}
// Rewind to the beginning of the interpreter.
// As above, this is essentially free because the internal buf size is several times larger
// than PATH_MAX by default, and our shebang_len < PATH_MAX as checked above.
reader
.seek(SeekFrom::Start(pos))
.map_err(|_| Error::new(E2BIG))?;
let mut interpreter = Vec::with_capacity(interp_offset);
reader
.take(interp_offset_u64)
.read_to_end(&mut interpreter)
.map_err(|_| Error::new(EIO))?;
// Read args, but treat as an opaque block to pass to the interpreter.
// Linux and FreeBSD both pass the args as is to the interpreter whereas macOS splits
// the args similar to `/usr/bin/env -S`.
// POSIX leaves the behavior up to the implementation.
// It's simpler to rely on env because well behaved, portable scripts will use
// it to ensure correct operation on Linux/FreeBSD.
// Splitting args ourselves gains little while reinventing env -S
let interpreter_args = if let (Some(offset), Some(offset_u64)) = (
args_offset.map(NonZeroUsize::get),
args_offset_u64.map(NonZeroU64::get),
) {
let len = offset - interp_offset;
let len_u64 = offset_u64 - interp_offset_u64;
let mut args = Vec::with_capacity(len);
reader
.take(len_u64)
.read_to_end(&mut args)
.map_err(|_| Error::new(E2BIG))?;
// Eat '\n'
reader.consume(1);
let mut arg_start = 0;
while arg_start < args.len() && (args[arg_start] == b' ' || args[arg_start] == b'\t') {
arg_start += 1;
}
if arg_start < args.len() {
let args = CString::new(&args[arg_start..]).map_err(|_| Error::new(ENOEXEC))?;
Some(args)
} else {
None
}
} else {
None
};
let interpreter = CString::new(interpreter).map_err(|_| Error::new(ENOEXEC))?;
Ok((Some(interpreter), interpreter_args))
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::parse_interpreter;
// Shebangs without a script attached
const NO_FRILLS: &str = "#!/bin/sh\n";
const NO_FRILLS_EXPECTED: &str = "/bin/sh";
const SPACE_B4_INTERP: &str = "#! /bin/sh\n";
const SPACE_B4_INTERP_EXPECTED: &str = "/bin/sh";
const NO_FRILLS_ENV: &str = "#!/usr/bin/env sh\n";
const NO_FRILLS_ENV_EXPECTED: &str = "/usr/bin/env";
const NO_FRILLS_ENV_EXPECTED_ARGS: &str = "sh";
const SPACE_B4_ENV: &str = "#! /usr/bin/env sh\n";
const SPACE_B4_EXPECTED: &str = NO_FRILLS_ENV_EXPECTED;
const SPACE_B4_EXPECTED_ARGS: &str = NO_FRILLS_ENV_EXPECTED_ARGS;
const MULT_SPACES_B4: &str = "#! /usr/bin/env sh\n";
const MULT_SPACES_B4_EXPECTED: &str = NO_FRILLS_ENV_EXPECTED;
const MULT_SPACES_B4_EXPECTED_ARGS: &str = NO_FRILLS_ENV_EXPECTED_ARGS;
// Shebangs with a script attached
// These test that the parser doesn't run off the first line
const NO_FRILLS_W_SCRIPT: &str = r#"#!/bin/sh
echo "Hello from Redox""#;
const NO_FRILLS_W_SCRIPT_EXPECTED: &str = NO_FRILLS_EXPECTED;
const SPACE_B4_INTERP_W_SCRIPT: &str = r#"#! /bin/sh
echo "Doctor Eigenvalue""#;
const SPACE_B4_INTERP_W_SCRIPT_EXPECTED: &str = NO_FRILLS_EXPECTED;
const MULT_ARGUMENTS: &str = r#"#! /usr/bin/env -S python -OO
assert False
print("This totally works")
"#;
const MULT_ARGUMENTS_EXPECTED: &str = NO_FRILLS_ENV_EXPECTED;
const MULT_ARGUMENTS_EXPECTED_ARGS: &str = "-S python -OO";
// No hashbang conditions
const NO_SHEBANG: &str = "/bin/sh";
const EMPTY: &str = "";
// Error conditions
const SHEBANG_NO_INTERP: &str = "#!";
const SHEBANG_NO_INTERP_SPACE: &str = "#! ";
const SHEBANG_NO_INTERP_SCRIPT: &str = "#!\necho ${PATH}";
fn success(input: &str, expected_interp: &str, expected_args: Option<&str>) {
let mut reader = Cursor::new(input);
let (actual_interp, actual_args) = parse_interpreter(&mut reader)
.unwrap_or_else(|e| panic!("Shebang ({input}) should parse\n\t{e}"));
let actual_interp = actual_interp
.expect("Expected an interpreter")
.into_string()
.expect("Interpreter is ASCII (valid UTF-8)");
assert_eq!(expected_interp, actual_interp);
if let Some(expected_args) = expected_args {
let actual_args = actual_args
.expect("Expected arguments to interpreter")
.into_string()
.expect("Args string is ASCII (valid UTF-8)");
assert_eq!(expected_args, actual_args);
}
}
#[test]
fn parse_interpreter_without_space() {
success(NO_FRILLS, NO_FRILLS_EXPECTED, None);
}
#[test]
fn parse_interpreter_with_space() {
success(SPACE_B4_INTERP, SPACE_B4_INTERP_EXPECTED, None);
}
#[test]
fn parse_interpreter_with_arg() {
success(
NO_FRILLS_ENV,
NO_FRILLS_ENV_EXPECTED,
Some(NO_FRILLS_ENV_EXPECTED_ARGS),
);
}
#[test]
fn parse_interpreter_with_arg_and_space() {
success(
SPACE_B4_ENV,
SPACE_B4_EXPECTED,
Some(SPACE_B4_EXPECTED_ARGS),
);
}
#[test]
fn parse_interpreter_with_multiple_spaces() {
success(
MULT_SPACES_B4,
MULT_SPACES_B4_EXPECTED,
Some(MULT_SPACES_B4_EXPECTED_ARGS),
);
}
#[test]
fn parse_interpreter_with_script() {
success(NO_FRILLS_W_SCRIPT, NO_FRILLS_W_SCRIPT_EXPECTED, None);
}
#[test]
fn parse_interpreter_with_script_and_space() {
success(
SPACE_B4_INTERP_W_SCRIPT,
SPACE_B4_INTERP_W_SCRIPT_EXPECTED,
None,
);
}
#[test]
fn parse_interpreter_with_script_args_space() {
success(
MULT_ARGUMENTS,
MULT_ARGUMENTS_EXPECTED,
Some(MULT_ARGUMENTS_EXPECTED_ARGS),
);
}
#[test]
fn parse_interpreter_no_shebang() {
let mut reader = Cursor::new(NO_SHEBANG);
let (interpreter, args) =
parse_interpreter(&mut reader).expect("Shouldn't fail if file doesn't have a shebang");
assert!(
interpreter.is_none(),
"Interpreter should be `None` if shebang isn't present"
);
assert!(
args.is_none(),
"Args should be empty without an interpreter."
);
}
#[test]
fn parse_interpreter_empty() {
let mut reader = Cursor::new(EMPTY);
let (interpreter, args) =
parse_interpreter(&mut reader).expect("Shouldn't fail if file doesn't have a shebang");
assert!(
interpreter.is_none(),
"Interpreter should be `None` for empty image"
);
assert!(args.is_none(), "Args should be empty for empty image");
}
#[test]
fn parse_interpreter_no_interpreter_fail() {
let mut reader = Cursor::new(SHEBANG_NO_INTERP);
parse_interpreter(&mut reader)
.expect_err("A hashbang without an interpreter should return an error");
}
#[test]
fn parse_interpreter_no_interpreter_space_fail() {
let mut reader = Cursor::new(SHEBANG_NO_INTERP_SPACE);
parse_interpreter(&mut reader)
.expect_err("A hashbang without an interpreter should return an error");
}
#[test]
fn parse_interpreter_no_interpreter_script_fail() {
let mut reader = Cursor::new(SHEBANG_NO_INTERP_SCRIPT);
parse_interpreter(&mut reader)
.expect_err("A hashbang without an interpreter should return an error");
}
}
+29 -40
View File
@@ -1,49 +1,38 @@
use core::{ptr, slice};
use core::slice;
use crate::platform::{sys::e, types::*};
use crate::{
error::{Errno, ResultExt},
platform::types::*,
};
use syscall::{F_SETFD, F_SETFL, O_RDONLY, O_WRONLY, error::*};
#[no_mangle]
pub use redox_rt::proc::FdGuard;
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_fpath(fd: c_int, buf: *mut c_void, count: size_t) -> ssize_t {
e(syscall::fpath(
fd as usize,
slice::from_raw_parts_mut(buf as *mut u8, count),
)) as ssize_t
syscall::fpath(fd as usize, unsafe {
slice::from_raw_parts_mut(buf as *mut u8, count)
})
.map_err(Errno::from)
.map(|l| l as ssize_t)
.or_minus_one_errno()
}
#[no_mangle]
pub unsafe extern "C" fn redox_physalloc(size: size_t) -> *mut c_void {
let res = e(syscall::physalloc(size));
if res == !0 {
return ptr::null_mut();
} else {
return res as *mut c_void;
}
}
pub fn pipe2(flags: usize) -> syscall::error::Result<[c_int; 2]> {
let read_flags = flags | O_RDONLY;
let write_flags = flags | O_WRONLY;
let read_fd = FdGuard::open("/scheme/pipe", read_flags)?;
let write_fd = read_fd.dup(b"write")?;
write_fd.fcntl(F_SETFL, write_flags)?;
write_fd.fcntl(F_SETFD, write_flags)?;
#[no_mangle]
pub unsafe extern "C" fn redox_physfree(physical_address: *mut c_void, size: size_t) -> c_int {
e(syscall::physfree(physical_address as usize, size)) as c_int
}
let fds = [
c_int::try_from(read_fd.as_raw_fd()).map_err(|_| Error::new(EMFILE))?,
c_int::try_from(write_fd.as_raw_fd()).map_err(|_| Error::new(EMFILE))?,
];
#[no_mangle]
pub unsafe extern "C" fn redox_physmap(
physical_address: *mut c_void,
size: size_t,
flags: c_int,
) -> *mut c_void {
let res = e(syscall::physmap(
physical_address as usize,
size,
syscall::PhysmapFlags::from_bits(flags as usize).expect("physmap: invalid bit pattern"),
));
if res == !0 {
return ptr::null_mut();
} else {
return res as *mut c_void;
}
}
read_fd.take();
write_fd.take();
#[no_mangle]
pub unsafe extern "C" fn redox_physunmap(virtual_address: *mut c_void) -> c_int {
e(syscall::physunmap(virtual_address as usize)) as c_int
Ok(fds)
}
+52
View File
@@ -0,0 +1,52 @@
use crate::{c_str::CStr, header::stdlib::getenv, platform::types::*};
use core::ptr;
use syscall::{EIO, ENOENT, Error, Result, flag::*};
pub const LIBC_SCHEME: &'static str = "libc:";
const ENV_MAX_LEN: i32 = i32::MAX;
macro_rules! env_str {
($lit:expr) => {
#[allow(unused_unsafe)]
{
let val_bytes = unsafe { getenv(concat!($lit, "\0").as_ptr() as *const c_char) };
if val_bytes != ptr::null_mut() {
if let Ok(val_str) = unsafe { CStr::from_ptr(val_bytes) }.to_str() {
Some(val_str)
} else {
None
}
} else {
None
}
}
};
}
pub fn open(path: &str, flags: usize) -> Result<usize> {
assert!(path.starts_with(LIBC_SCHEME));
if flags & O_SYMLINK != 0 {
return Err(Error::new(ENOENT));
}
let basename = match path.strip_prefix(LIBC_SCHEME) {
Some(path) => path.trim_matches('/'),
_ => return Err(Error::new(EIO)),
};
// Linux seems to allow you to read from or write to any of /dev/{stdin,stderr,stdout}
match basename {
"stderr" => syscall::dup(2, &[]),
"stdin" => syscall::dup(0, &[]),
"stdout" => syscall::dup(1, &[]),
"tty" => {
if let Some(tty) = env_str!("TTY") {
return redox_rt::sys::open(tty, flags);
}
Err(Error::new(ENOENT))
}
_ => Err(Error::new(ENOENT)),
}
}
+639
View File
@@ -0,0 +1,639 @@
use core::{mem, slice, str};
use alloc::vec::Vec;
use ioslice::IoSlice;
use redox_protocols::protocol::{ProcKillTarget, SocketCall, WaitFlags};
use redox_rt::sys::{WaitpidTarget, posix_read, posix_write, std_fs_call_ro, std_fs_call_wo};
use syscall::{
EMFILE, ENAMETOOLONG, ENOSYS, EOPNOTSUPP, Error, Result, StdFsCallKind,
data::StdFsCallMeta,
dirent::{DirentHeader, DirentKind},
};
use crate::{
header::{
bits_iovec::iovec, bits_timespec::timespec, errno::EINVAL, signal::sigaction,
sys_stat::UTIME_NOW,
},
out::Out,
platform::{PalSignal, pal::Pal, types::*},
};
use super::Sys;
pub type RawResult = usize;
pub fn open(path: &str, oflag: c_int, mode: mode_t) -> Result<usize> {
let usize_fd = super::path::open(
path,
((oflag as usize) & 0xFFFF_0000) | ((mode as usize) & 0xFFFF),
)?;
c_int::try_from(usize_fd)
.map_err(|_| {
let _ = syscall::close(usize_fd);
Error::new(EMFILE)
})
.map(|f| f as usize)
}
pub fn openat(dirfd: c_int, path: &str, oflag: c_int, mode: mode_t) -> Result<usize> {
let usize_fd = super::path::openat(
dirfd,
path,
((oflag as usize) & 0xFFFF_0000) | ((mode as usize) & 0xFFFF),
)?;
c_int::try_from(usize_fd)
.map_err(|_| {
let _ = syscall::close(usize_fd);
Error::new(EMFILE)
})
.map(|f| f as usize)
}
pub fn fchmod(fd: usize, new_mode: u16) -> Result<()> {
std_fs_call_wo(
fd,
&[],
&StdFsCallMeta::new(StdFsCallKind::Fchmod, new_mode as u64, 0),
)?;
Ok(())
}
pub fn fchown(fd: usize, new_uid: u32, new_gid: u32) -> Result<()> {
/* std_fs_call
Error::mux(std_fs_call_wo(
fd,
&[],
&StdFsCallMeta::new(
StdFsCallKind::Fchmod,
(new_uid as u64) | ((new_gid as u64) << 32),
0,
),
))
*/
syscall::fchown(fd, new_uid, new_gid)?;
Ok(())
}
pub fn getdents(fd: usize, buf: &mut [u8], opaque: u64) -> Result<usize> {
//println!("GETDENTS {} into ({:p}+{})", fd, buf.as_ptr(), buf.len());
const HEADER_SIZE: usize = mem::size_of::<DirentHeader>();
// Use syscall if it exists.
match std_fs_call_ro(
fd,
buf,
&StdFsCallMeta::new(StdFsCallKind::Getdents, opaque, HEADER_SIZE as u64),
) {
Err(Error {
errno: EOPNOTSUPP | ENOSYS,
}) => (),
other => {
//println!("REAL GETDENTS {:?}", other);
return Ok(other?);
}
}
// Otherwise, for legacy schemes, assume the buffer is pre-arranged (all schemes do this in
// practice), and just read the name. If multiple names appear, pretend it didn't happen
// and just use the first entry.
let (header, name) = buf.split_at_mut(mem::size_of::<DirentHeader>());
let bytes_read = Sys::pread(fd as c_int, name, opaque as i64)? as usize;
if bytes_read == 0 {
return Ok(0);
}
let (name_len, advance) = match name[..bytes_read].iter().position(|c| *c == b'\n') {
Some(idx) => (idx, idx + 1),
// Insufficient space for NUL byte, or entire entry was not read. Indicate we need a
// larger buffer.
None if bytes_read == name.len() => return Err(Error::new(EINVAL)),
None => (bytes_read, name.len()),
};
name[name_len] = b'\0';
let record_len = u16::try_from(mem::size_of::<DirentHeader>() + name_len + 1)
.map_err(|_| Error::new(ENAMETOOLONG))?;
header.copy_from_slice(&DirentHeader {
inode: 0,
next_opaque_id: opaque + advance as u64,
record_len,
kind: DirentKind::Unspecified as u8,
});
//println!("EMULATED GETDENTS");
Ok(record_len.into())
}
pub unsafe fn fstat(fd: usize, buf: *mut crate::header::sys_stat::stat) -> Result<()> {
let mut redox_buf: syscall::Stat = Default::default();
redox_rt::sys::fstat(fd, &mut redox_buf)?;
if let Some(buf) = unsafe { buf.as_mut() } {
buf.st_dev = redox_buf.st_dev as dev_t;
buf.st_ino = redox_buf.st_ino as ino_t;
buf.st_nlink = redox_buf.st_nlink as nlink_t;
buf.st_mode = redox_buf.st_mode as mode_t;
buf.st_uid = redox_buf.st_uid as uid_t;
buf.st_gid = redox_buf.st_gid as gid_t;
// TODO st_rdev
buf.st_rdev = 0;
buf.st_size = redox_buf.st_size as off_t;
buf.st_blksize = redox_buf.st_blksize as blksize_t;
buf.st_blocks = redox_buf.st_blocks as blkcnt_t;
buf.st_atim = timespec {
tv_sec: redox_buf.st_atime as time_t,
tv_nsec: redox_buf.st_atime_nsec as c_long,
};
buf.st_mtim = timespec {
tv_sec: redox_buf.st_mtime as time_t,
tv_nsec: redox_buf.st_mtime_nsec as c_long,
};
buf.st_ctim = timespec {
tv_sec: redox_buf.st_ctime as time_t,
tv_nsec: redox_buf.st_ctime_nsec as c_long,
};
}
Ok(())
}
pub unsafe fn fstatvfs(fd: usize, buf: *mut crate::header::sys_statvfs::statvfs) -> Result<()> {
let mut kbuf: syscall::StatVfs = Default::default();
std_fs_call_ro(
fd,
&mut kbuf,
&StdFsCallMeta::new(StdFsCallKind::Fstatvfs, 0, 0),
)?;
if !buf.is_null() {
unsafe {
(*buf).f_bsize = kbuf.f_bsize as c_ulong;
(*buf).f_frsize = kbuf.f_bsize as c_ulong;
(*buf).f_blocks = kbuf.f_blocks as c_ulong;
(*buf).f_bfree = kbuf.f_bfree as c_ulong;
(*buf).f_bavail = kbuf.f_bavail as c_ulong;
//TODO
(*buf).f_files = 0;
(*buf).f_ffree = 0;
(*buf).f_favail = 0;
(*buf).f_fsid = 0;
(*buf).f_flag = 0;
(*buf).f_namemax = 0;
}
}
Ok(())
}
pub fn fsync(fd: usize) -> Result<()> {
std_fs_call_wo(fd, &[], &StdFsCallMeta::new(StdFsCallKind::Fsync, 0, 0))?;
Ok(())
}
pub fn ftruncate(fd: usize, len: usize) -> Result<()> {
std_fs_call_wo(
fd,
&[],
&StdFsCallMeta::new(StdFsCallKind::Ftruncate, len as u64, 0),
)?;
Ok(())
}
pub unsafe fn futimens(fd: usize, times: *const timespec) -> Result<()> {
let times = if times.is_null() {
// null means set to current time using special UTIME_NOW value (tv_sec is ignored in that case)
[
syscall::TimeSpec {
tv_sec: 0,
tv_nsec: UTIME_NOW as c_int,
},
syscall::TimeSpec {
tv_sec: 0,
tv_nsec: UTIME_NOW as c_int,
},
]
} else {
unsafe { times.cast::<[timespec; 2]>().read() }.map(|ts| syscall::TimeSpec::from(&ts))
};
let redox_buf = unsafe {
slice::from_raw_parts(
times.as_ptr() as *const u8,
times.len() * mem::size_of::<syscall::TimeSpec>(),
)
};
std_fs_call_wo(
fd,
redox_buf,
&StdFsCallMeta::new(StdFsCallKind::Futimens, 0, 0),
)?;
Ok(())
}
pub fn clock_gettime(clock: usize, mut tp: Out<timespec>) -> Result<()> {
let mut redox_tp = syscall::TimeSpec::default();
syscall::clock_gettime(clock as usize, &mut redox_tp)?;
tp.write(timespec {
tv_sec: redox_tp.tv_sec as time_t,
tv_nsec: redox_tp.tv_nsec as c_long,
});
Ok(())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_open_v1(
path_base: *const u8,
path_len: usize,
flags: u32,
mode: u16,
) -> RawResult {
Error::mux(open(
unsafe { str::from_utf8_unchecked(slice::from_raw_parts(path_base, path_len)) },
flags as c_int,
mode as mode_t,
))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_openat_v1(
fd: usize,
path_base: *const u8,
path_len: usize,
flags: u32,
fcntl_flags: u32,
) -> RawResult {
Error::mux(syscall::openat(
fd,
unsafe { str::from_utf8_unchecked(slice::from_raw_parts(path_base, path_len)) },
flags as usize,
fcntl_flags as usize,
))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_dup_v1(fd: usize, buf: *const u8, len: usize) -> RawResult {
Error::mux(syscall::dup(fd, unsafe {
core::slice::from_raw_parts(buf, len)
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_dup2_v1(
old_fd: usize,
new_fd: usize,
buf: *const u8,
len: usize,
) -> RawResult {
Error::mux(syscall::dup2(old_fd, new_fd, unsafe {
core::slice::from_raw_parts(buf, len)
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_read_v1(fd: usize, dst_base: *mut u8, dst_len: usize) -> RawResult {
Error::mux(posix_read(fd, unsafe {
slice::from_raw_parts_mut(dst_base, dst_len)
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_write_v1(
fd: usize,
src_base: *const u8,
src_len: usize,
) -> RawResult {
Error::mux(posix_write(fd, unsafe {
slice::from_raw_parts(src_base, src_len)
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_fchmod_v1(fd: usize, new_mode: u16) -> RawResult {
Error::mux(fchmod(fd, new_mode).map(|()| 0))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_fchown_v1(fd: usize, new_uid: u32, new_gid: u32) -> RawResult {
Error::mux(fchown(fd, new_uid, new_gid).map(|()| 0))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_getdents_v0(
fd: usize,
buf: *mut u8,
buf_len: usize,
opaque: u64,
) -> RawResult {
Error::mux(
Sys::getdents(
fd as c_int,
unsafe { slice::from_raw_parts_mut(buf, buf_len) },
opaque,
)
.map_err(Into::into),
)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_fstat_v1(
fd: usize,
stat: *mut crate::header::sys_stat::stat,
) -> RawResult {
Error::mux(unsafe { fstat(fd, stat) }.map(|()| 0))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_fstatvfs_v1(
fd: usize,
stat: *mut crate::header::sys_statvfs::statvfs,
) -> RawResult {
Error::mux(unsafe { fstatvfs(fd, stat) }.map(|()| 0))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_fsync_v1(fd: usize) -> RawResult {
Error::mux(fsync(fd).map(|()| 0))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_fdatasync_v1(fd: usize) -> RawResult {
// TODO
Error::mux(fsync(fd).map(|()| 0))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_ftruncate_v0(fd: usize, len: usize) -> RawResult {
Error::mux(ftruncate(fd, len).map(|()| 0))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_futimens_v1(fd: usize, times: *const timespec) -> RawResult {
Error::mux(unsafe { futimens(fd, times) }.map(|()| 0))
}
/* TODO: Support unlinkat
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_unlinkat_v0(
fd: usize,
path_base: *const u8,
path_len: usize,
flags: u32,
) -> RawResult {
Error::mux(std_fs_call_wo(
fd,
unsafe { slice::from_raw_parts(path_base, path_len) },
&StdFsCallMeta::new(StdFsCallKind::Unlinkat, flags as u64, 0),
))
}
*/
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_fpath_v1(fd: usize, dst_base: *mut u8, dst_len: usize) -> RawResult {
Error::mux(syscall::fpath(fd, unsafe {
core::slice::from_raw_parts_mut(dst_base, dst_len)
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_close_v1(fd: usize) -> RawResult {
Error::mux(syscall::close(fd))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_fcntl_v0(fd: usize, cmd: usize, arg: usize) -> RawResult {
Error::mux(redox_rt::sys::fcntl(fd, cmd, arg))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_get_pid_v1() -> RawResult {
redox_rt::sys::posix_getpid() as _
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_get_euid_v1() -> RawResult {
redox_rt::sys::posix_getresugid().euid as _
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_get_ruid_v1() -> RawResult {
redox_rt::sys::posix_getresugid().ruid as _
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_get_egid_v1() -> RawResult {
redox_rt::sys::posix_getresugid().egid as _
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_get_rgid_v1() -> RawResult {
redox_rt::sys::posix_getresugid().rgid as _
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_get_ens_v0() -> RawResult {
Error::mux(redox_rt::sys::getens())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_get_ns_v0() -> RawResult {
Error::mux(redox_rt::sys::getns())
}
#[allow(improper_ctypes_definitions)]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_get_proc_credentials_v1(
cap_fd: usize,
target_pid: usize,
buf: &mut [u8], // not FFI safe
) -> RawResult {
Error::mux(redox_rt::sys::get_proc_credentials(cap_fd, target_pid, buf))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_setrens_v1(rns: usize, ens: usize) -> RawResult {
let _ = if ens == 0 {
let null_namespace: [IoSlice; 2] = [IoSlice::new(b"memory"), IoSlice::new(b"pipe")];
match redox_rt::sys::mkns(&null_namespace) {
Ok(new_ns_fd) => redox_rt::sys::setns(new_ns_fd.take()),
Err(e) => return Error::mux(Err(e)),
}
} else {
redox_rt::sys::setns(ens)
};
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_waitpid_v1(pid: usize, status: *mut i32, options: u32) -> RawResult {
let mut sts = 0_usize;
let res = Error::mux(redox_rt::sys::sys_waitpid(
WaitpidTarget::from_posix_arg(pid as isize),
&mut sts,
WaitFlags::from_bits_truncate(options as usize),
));
unsafe { status.write(sts as i32) };
res
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_kill_v1(pid: usize, signal: u32) -> RawResult {
Error::mux(
redox_rt::sys::posix_kill(ProcKillTarget::from_raw(pid), signal as usize).map(|()| 0),
)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_sigaction_v1(
signal: u32,
new: *const sigaction,
old: *mut sigaction,
) -> RawResult {
Error::mux(
Sys::sigaction(signal as c_int, unsafe { new.as_ref() }, unsafe {
old.as_mut()
})
.map(|()| 0)
.map_err(Into::into),
)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_sigprocmask_v1(
how: u32,
new: *const u64,
old: *mut u64,
) -> RawResult {
Error::mux(
Sys::sigprocmask(how as c_int, unsafe { new.as_ref() }, unsafe {
old.as_mut()
})
.map(|()| 0)
.map_err(Into::into),
)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_mmap_v1(
addr: *mut (),
unaligned_len: usize,
prot: u32,
flags: u32,
fd: usize,
offset: u64,
) -> RawResult {
Error::mux(unsafe {
syscall::fmap(
fd,
&syscall::Map {
address: addr as usize,
offset: offset as usize,
size: unaligned_len,
flags: syscall::MapFlags::from_bits_truncate(
((prot << 16) | (flags & 0xffff)) as usize,
),
},
)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_munmap_v1(addr: *mut (), unaligned_len: usize) -> RawResult {
Error::mux(unsafe { syscall::funmap(addr as usize, unaligned_len) })
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_clock_gettime_v1(clock: usize, ts: *mut timespec) -> RawResult {
Error::mux(clock_gettime(clock, unsafe { Out::nonnull(ts) }).map(|()| 0))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_strerror_v1(
buf: *mut u8,
buflen: *mut usize,
error: u32,
) -> RawResult {
let dst = unsafe { core::slice::from_raw_parts_mut(buf, buflen.read()) };
Error::mux((|| {
// TODO: Merge syscall::error::STR_ERROR into crate::header::error::?
let src = syscall::error::STR_ERROR
.get(error as usize)
.ok_or(Error::new(EINVAL))?;
// This API ensures that the returned buffer is proper UTF-8. Thus, it returns both the
// copied length and the actual length.
unsafe { buflen.write(src.len()) };
let raw_len = core::cmp::min(dst.len(), src.len());
let len = match core::str::from_utf8(&src.as_bytes()[..raw_len]) {
Ok(_valid) => raw_len,
Err(error) => error.valid_up_to(),
};
dst[..len].copy_from_slice(&src.as_bytes()[..len]);
Ok(len)
})())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_mkns_v1(
names: *const iovec,
num_names: usize,
flags: u32,
) -> RawResult {
Error::mux((|| {
if flags != 0 {
return Err(Error::new(EINVAL));
}
let raw_iovecs = unsafe { slice::from_raw_parts(names, num_names) };
let names_ioslice: Vec<IoSlice> = raw_iovecs
.iter()
.map(|iov| {
IoSlice::new(unsafe {
slice::from_raw_parts(iov.iov_base as *const u8, iov.iov_len)
})
})
.collect();
redox_rt::sys::mkns(&names_ioslice).map(|fd| fd.take())
})())
}
// ABI-UNSTABLE
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_cur_procfd_v0() -> usize {
redox_rt::current_proc_fd().as_raw_fd()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_cur_thrfd_v0() -> usize {
redox_rt::RtTcb::current().thread_fd().as_raw_fd()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_sys_call_v0(
fd: usize,
payload: *mut u8,
payload_len: usize,
flags: usize,
metadata: *const u64,
metadata_len: usize,
) -> RawResult {
Error::mux(redox_rt::sys::sys_call(
fd,
unsafe { slice::from_raw_parts_mut(payload, payload_len) },
syscall::CallFlags::from_bits_retain(flags),
unsafe { slice::from_raw_parts(metadata, metadata_len) },
))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_get_socket_token_v0(
fd: usize,
payload: *mut u8,
payload_len: usize,
) -> RawResult {
let metadata = [SocketCall::GetToken as u64];
Error::mux(redox_rt::sys::sys_call_ro(
fd,
unsafe { slice::from_raw_parts_mut(payload, payload_len) },
syscall::CallFlags::empty(),
&metadata,
))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_setns_v0(fd: usize) -> RawResult {
match redox_rt::sys::setns(fd) {
Some(guard) => guard.take(),
None => usize::MAX,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn redox_register_scheme_to_ns_v0(
ns_fd: usize,
name_base: *const u8,
name_len: usize,
cap_fd: usize,
) -> RawResult {
Error::mux(
redox_rt::sys::register_scheme_to_ns(
ns_fd,
unsafe { str::from_utf8_unchecked(slice::from_raw_parts(name_base, name_len)) },
cap_fd,
)
.map(|()| 0),
)
}
+1471 -793
View File
File diff suppressed because it is too large Load Diff
+522
View File
@@ -0,0 +1,522 @@
use alloc::{
boxed::Box,
collections::btree_set::BTreeSet,
ffi::CString,
string::{String, ToString},
vec::Vec,
};
use core::{ffi::c_int, str};
use redox_rt::{proc::FdGuardUpper, signal::tmp_disable_signals};
use syscall::{data::Stat, error::*, flag::*};
use super::{FdGuard, Pal, Sys, libcscheme};
use crate::{
error::Errno,
fs::File,
header::{fcntl, limits, sys_file},
out::Out,
sync::rwlock::{ReadGuard, RwLock},
};
pub use redox_path::{RedoxPath, canonicalize_using_cwd};
pub fn normalize_path<'a>(path: &'a str) -> Option<(bool, String)> {
let absolute = match RedoxPath::from_absolute(path) {
Some(absolute) => absolute,
None => return Some((true, partially_canonical(path)?)),
};
let canonical = absolute.canonical()?;
Some((false, canonical.to_string()))
}
pub fn normalize_scheme_rooted_path<'a>(path: &'a str) -> Option<(bool, String)> {
let absolute = match RedoxPath::from_absolute(path) {
Some(absolute) => absolute,
None => return Some((true, partially_canonical(path)?)),
};
let canonical = absolute.canonical()?;
Some((false, scheme_rooted_path(&canonical.to_string()).ok()?))
}
fn partially_canonical(path: &str) -> Option<String> {
let mut stack = Vec::new();
let mut paths_to_check = BTreeSet::new();
let mut up_counts = 0;
for part in path.split('/') {
if part.is_empty() || part == "." {
continue;
} else if part == ".." {
if let Some(part) = stack.pop() {
paths_to_check.insert(
core::iter::repeat("..")
.take(up_counts)
.chain(stack.clone())
.chain(core::iter::once(part))
.collect::<Vec<_>>()
.join("/"),
);
} else {
up_counts += 1;
}
} else {
stack.push(part);
}
}
for path in paths_to_check {
let _ = current_dir()
.ok()?
.as_ref()
.unwrap()
.fd
.openat(&path, O_STAT, 0)
.ok()?;
}
Some(
core::iter::repeat("..")
.take(up_counts)
.chain(stack)
.collect::<Vec<_>>()
.join("/"),
)
}
// POSIX states chdir is both thread-safe and signal-safe. Thus we need to synchronize access to CWD, but at the
// same time forbid signal handlers from running in the meantime, to avoid reentrant deadlock.
pub fn chdir(path: &str) -> Result<()> {
let _siglock = tmp_disable_signals();
let mut cwd_guard = CWD.write();
let (is_relative, path) = normalize_path(path).ok_or(Error::new(ENOENT))?;
if is_relative {
let fd = cwd_guard
.as_ref()
.unwrap()
.fd
.openat(&path, O_STAT, 0)?
.to_upper()
.unwrap();
let mut stat = Stat::default();
if fd.fstat(&mut stat).is_err() || (stat.st_mode & MODE_TYPE) != MODE_DIR {
return Err(Error::new(ENOTDIR));
}
let canon = canonicalize_using_cwd(cwd_guard.as_ref().map(|c| c.path.as_ref()), &path)
.ok_or(Error::new(ENOENT))?;
*cwd_guard = Some(Cwd {
path: canon.into_boxed_str(),
fd,
});
} else {
let canon_with_scheme = scheme_rooted_path(&path)?;
let fd = FdGuard::open(&canon_with_scheme, O_STAT)?
.to_upper()
.unwrap();
let mut stat = Stat::default();
if fd.fstat(&mut stat).is_err() || (stat.st_mode & MODE_TYPE) != MODE_DIR {
return Err(Error::new(ENOTDIR));
}
*cwd_guard = Some(Cwd {
path: path.into_boxed_str(),
fd,
});
}
Ok(())
}
pub fn fchdir(fd: c_int) -> Result<()> {
let mut buf = [0_u8; limits::PATH_MAX];
let res = Sys::fpath(fd, &mut buf)?;
let path = core::str::from_utf8(&buf[..res])
.map_err(|_| Errno(EINVAL))?
.to_string();
let fd = FdGuard::new(syscall::fcntl(
fd as usize,
syscall::F_DUPFD,
syscall::UPPER_FDTBL_TAG,
)?)
.to_upper()
.unwrap();
set_cwd_manual(path.into_boxed_str(), fd);
Ok(())
}
// getcwd is similarly both thread-safe and signal-safe.
pub fn getcwd(mut buf: Out<[u8]>) -> Result<usize> {
let _siglock = tmp_disable_signals();
let guard = CWD.read();
let cwd = guard.as_ref().ok_or(Error::new(ENOENT))?;
let path_bytes = cwd.path.as_bytes();
let [mut before, mut after] = buf
.split_at_checked(path_bytes.len())
.ok_or(Error::new(ERANGE))?;
before.copy_from_slice(path_bytes);
after.zero();
Ok(path_bytes.len())
}
// Get Cwd object
pub fn current_dir() -> Result<ReadGuard<'static, Option<Cwd>>> {
let _siglock = tmp_disable_signals();
let guard = CWD.read();
if guard.as_ref().is_none() {
return Err(Error::new(ENOENT));
}
Ok(guard)
}
fn scheme_rooted_path(path: &str) -> Result<String> {
let standard_scheme = path == "/scheme" || path.starts_with("/scheme/");
let legacy_scheme = path
.split("/")
.next()
.map(|c| c.contains(":"))
.unwrap_or(false);
Ok(if standard_scheme || legacy_scheme {
path.to_string()
} else {
let mut result = format!("/scheme/file{}", path);
// Trim trailing / to keep path canonical.
if result.as_bytes().last() == Some(&b'/') {
result.pop();
}
result
})
}
// TODO: How much of this logic should be in redox-path?
fn canonicalize_with_cwd_internal(cwd: Option<&str>, path: &str) -> Result<String> {
let path = canonicalize_using_cwd(cwd, path).ok_or(Error::new(ENOENT))?;
scheme_rooted_path(&path)
}
pub fn canonicalize(path: &str) -> Result<String> {
let _siglock = tmp_disable_signals();
let cwd_guard = CWD.read();
canonicalize_with_cwd_internal(cwd_guard.as_ref().map(|c| c.path.as_ref()), path)
}
pub struct Cwd {
pub path: Box<str>,
pub fd: FdGuardUpper,
}
// TODO: arraystring?
static CWD: RwLock<Option<Cwd>> = RwLock::new(None);
pub fn set_cwd_manual(path: Box<str>, fd: FdGuardUpper) {
let _siglock = tmp_disable_signals();
*CWD.write() = Some(Cwd { path, fd });
}
pub fn clone_cwd() -> Option<Box<str>> {
let _siglock = tmp_disable_signals();
CWD.read().as_ref().map(|cwd| cwd.path.clone())
}
fn open_absolute(path: &str, flags: usize) -> Result<usize> {
if path.starts_with(libcscheme::LIBC_SCHEME) {
libcscheme::open(path, flags)
} else {
redox_rt::sys::open(path, flags)
}
}
fn link_target(fd: FdGuard) -> Result<String> {
let mut resolve_buf = [0_u8; limits::PATH_MAX];
let count = fd.read(&mut resolve_buf)?;
if count == resolve_buf.len() {
// TODO: make resolve_buf PATH_MAX + 1 bytes?
return Err(Error::new(ENAMETOOLONG));
}
// If the symbolic link path is non-UTF8, it cannot be opened, and is thus
// considered a "dangling symbolic link".
core::str::from_utf8(&resolve_buf[..count])
.map_err(|_| Error::new(ENOENT))
.map(|s| s.to_string())
}
fn read_link_content(path: &str, is_relative: bool) -> Result<String> {
let resolve_flags = O_CLOEXEC | O_SYMLINK | O_RDONLY;
let fd = if is_relative {
current_dir()?
.as_ref()
.unwrap()
.fd
.openat(path, resolve_flags, 0)?
} else {
FdGuard::open(path, resolve_flags)?
};
link_target(fd)
}
fn calc_next_abs_path(current_abs: &str, link_target: &str) -> Result<String> {
let parent = get_parent_path(current_abs).ok_or(Error::new(ENOENT))?;
canonicalize_using_cwd(Some(&parent), link_target).ok_or(Error::new(ENOENT))
}
fn resolve_sym_links(mut current_path_string: String, flags: usize) -> Result<usize> {
// TODO: SYMLOOP_MAX
const MAX_LEVEL: usize = 64;
// Sym reolve loop
for _ in 0..(MAX_LEVEL - 1) {
match open_absolute(&current_path_string, flags) {
Ok(fd) => return Ok(fd),
Err(e) if e == Error::new(EXDEV) => {
let link_target = read_link_content(&current_path_string, false)?;
current_path_string = calc_next_abs_path(&current_path_string, &link_target)?;
}
Err(e) => return Err(e),
}
}
Err(Error::new(ELOOP))
}
// TODO: Move to redox-rt, or maybe part of it?
pub fn openat(dirfd: c_int, path: &str, flags: usize) -> Result<usize> {
if path.is_empty() && flags as i32 & fcntl::AT_EMPTY_PATH != fcntl::AT_EMPTY_PATH {
return Err(Error::new(ENOENT));
}
let (is_relative, path_to_open) = normalize_path(path).ok_or(Error::new(ENOENT))?;
if !is_relative {
return open(path, flags);
}
let _siglock = tmp_disable_signals();
let fcntl_flags = flags & syscall::O_FCNTL_MASK;
// First try
let initial_res = if dirfd == fcntl::AT_FDCWD {
current_dir()?
.as_ref()
.unwrap()
.fd
.openat(&path_to_open, flags, fcntl_flags)
.map(|fd: FdGuard| fd.take())
} else {
redox_rt::sys::openat(dirfd as usize, &path_to_open, flags, fcntl_flags)
};
let current_path_string = match initial_res {
Ok(fd) => return Ok(fd),
Err(e) if e == Error::new(EXDEV) => {
let resolve_flags = O_CLOEXEC | O_SYMLINK | O_RDONLY;
let fd = if dirfd == fcntl::AT_FDCWD {
current_dir()?
.as_ref()
.unwrap()
.fd
.openat(path, resolve_flags, 0)?
} else {
redox_rt::sys::openat(dirfd as usize, &path_to_open, flags, fcntl_flags)
.map(FdGuard::new)?
};
let link_target = link_target(fd)?;
let current_abs = openat2_path(dirfd, path, 0)?;
calc_next_abs_path(&current_abs, &link_target)?
}
Err(e) => return Err(e),
};
resolve_sym_links(current_path_string, flags)
}
// TODO: Move to redox-rt, or maybe part of it?
pub fn open(path: &str, flags: usize) -> Result<usize> {
let _siglock = tmp_disable_signals();
if path == "" {
return Err(Error::new(ENOENT));
}
let (is_relative, canon) = normalize_scheme_rooted_path(path).ok_or(Error::new(ENOENT))?;
// First try
let initial_res = if is_relative {
let fcntl_flags = flags & syscall::O_FCNTL_MASK;
current_dir()?
.as_ref()
.unwrap()
.fd
.openat(&canon, flags, fcntl_flags)
.map(|fd: FdGuard| fd.take())
} else {
open_absolute(&canon, flags)
};
let current_path_string = match initial_res {
Ok(fd) => return Ok(fd),
Err(e) if e == Error::new(EXDEV) => {
let link_target = read_link_content(&canon, is_relative)?;
let cwd_guard = CWD.read();
let current_abs =
canonicalize_using_cwd(cwd_guard.as_ref().map(|c| c.path.as_ref()), &canon)
.ok_or(Error::new(ENOENT))?;
calc_next_abs_path(&current_abs, &link_target)?
}
Err(e) => return Err(e),
};
// Sym reolve loop
resolve_sym_links(current_path_string, flags)
}
fn get_parent_path(path: &str) -> Option<String> {
let path = path.strip_suffix('/').unwrap_or(path);
fn parent_opt(path: &str) -> Option<&str> {
path.rfind('/').map(|index| {
if index == 0 {
// Path is something like "/file.txt" or the root "/".
// The parent is the root directory "/".
"/"
} else {
// Path is something like "/a/b/c.txt".
// Take the slice from the beginning up to the last '/'.
&path[..index]
}
})
}
match RedoxPath::from_absolute(path) {
Some(path) => {
let (scheme, reference) = path.as_parts()?;
let parent_ref = parent_opt(reference.as_ref()).unwrap_or("");
Some(format!("/scheme/{}/{}", scheme.as_ref(), parent_ref))
}
None => parent_opt(path).map(String::from),
}
}
pub fn dir_path_and_fd_path(socket_path: &str) -> Result<(String, String)> {
let _siglock = tmp_disable_signals();
let cwd_guard = CWD.read();
let cwd_path = cwd_guard.as_ref().map(|c| c.path.as_ref());
let full_path = canonicalize_with_cwd_internal(cwd_path, socket_path)?;
let redox_path = RedoxPath::from_absolute(&full_path).ok_or(Error::new(EINVAL))?;
let (_, ref_path) = redox_path.as_parts().ok_or(Error::new(EINVAL))?;
if ref_path.as_ref().is_empty() {
return Err(Error::new(EINVAL));
}
if redox_path.is_default_scheme() {
let dir_to_open = get_parent_path(&full_path).ok_or(Error::new(EINVAL))?;
Ok((dir_to_open, ref_path.as_ref().to_string()))
} else {
let full_path = canonicalize_with_cwd_internal(cwd_path, ref_path.as_ref())?;
let redox_path = RedoxPath::from_absolute(&full_path).ok_or(Error::new(EINVAL))?;
let (_, path) = redox_path.as_parts().ok_or(Error::new(EINVAL))?;
let dir_to_open = get_parent_path(&full_path).ok_or(Error::new(EINVAL))?;
Ok((dir_to_open, path.as_ref().to_string()))
}
}
pub struct FileLock(c_int);
impl FileLock {
pub fn lock(fd: c_int, op: c_int) -> Result<Self> {
if op & sys_file::LOCK_SH | sys_file::LOCK_EX == 0 {
return Err(Error::new(EINVAL));
}
Sys::flock(fd, op)?;
Ok(Self(fd))
}
pub fn unlock(self) -> Result<()> {
Sys::flock(self.0, sys_file::LOCK_UN).map_err(Into::into)
}
}
impl Drop for FileLock {
fn drop(&mut self) {
let fd = self.0;
self.0 = -1;
let _ = Sys::flock(self.0, sys_file::LOCK_UN);
}
}
/// Resolve `path` under `dirfd`.
///
/// See [`openat2`] for more information.
pub(super) fn openat2_path(dirfd: c_int, path: &str, at_flags: c_int) -> Result<String, Errno> {
// Ideally, the function calling this fn would check AT_EMPTY_PATH and just call fstat or
// whatever with the fd.
if path.is_empty() && at_flags & fcntl::AT_EMPTY_PATH != fcntl::AT_EMPTY_PATH {
return Err(Errno(ENOENT));
}
// Absolute paths are passed without processing unless RESOLVE_BENEATH is used.
// canonicalize_using_cwd checks that path is absolute so a third branch that does so here
// isn't needed.
if dirfd == fcntl::AT_FDCWD {
// The special constant AT_FDCWD indicates that we should use the cwd.
let cwd_guard = CWD.read();
canonicalize_using_cwd(cwd_guard.as_ref().map(|c| c.path.as_ref()), path)
.ok_or(Errno(EBADF))
} else {
let mut buf = [0; limits::PATH_MAX];
let len = Sys::fpath(dirfd, &mut buf)?;
// SAFETY: fpath checks then copies valid UTF8.
let dir = unsafe { str::from_utf8_unchecked(&buf[..len]) };
canonicalize_using_cwd(Some(dir), path).ok_or(Errno(EBADF))
}
}
/// Canonicalize and open `path` with respect to `dirfd`.
///
/// This unexported openat2 is similar to the Linux syscall but with a different interface. The
/// naming is mostly for convenience - it's not a drop in replacement for openat2.
///
/// # Arguments
/// * `dirfd` is a directory descriptor to which `path` is resolved.
/// * `path` is a relative or absolute path. Relative paths are resolved in relation to `dirfd`
/// while absolute paths skip `dirfd`.
/// * `at_flags` constrains how `path` is resolved.
/// * `oflags` are flags that are passed to open.
///
/// # Constants
/// `at_flags`:
/// * AT_EMPTY_PATH returns the path at `dirfd` itself if `path` is empty. If `path` is not
/// empty, it's resolved w.r.t `dirfd` like normal.
///
/// `dirfd`:
/// `AT_FDCWD` is a special constant for `dirfd` that resolves `path` under the current working
/// directory.
pub(super) fn openat2(
dirfd: c_int,
path: &str,
at_flags: c_int,
oflags: c_int,
) -> Result<File, Errno> {
// Translate at flags into open flags; openat will do this on its own most likely.
let oflags = if at_flags & fcntl::AT_SYMLINK_NOFOLLOW == fcntl::AT_SYMLINK_NOFOLLOW {
fcntl::O_CLOEXEC | fcntl::O_NOFOLLOW | fcntl::O_PATH | fcntl::O_SYMLINK | oflags
} else {
fcntl::O_CLOEXEC | oflags
};
let c_path = CString::new(path).map_err(|_| Errno(EINVAL))?;
File::openat(dirfd, c_path.as_c_str().into(), oflags)
}
+95 -26
View File
@@ -4,21 +4,33 @@
//! we are NOT going to bend our API for the sake of
//! compatibility. So, this module will be a hellhole.
use super::super::{errno, types::*, Pal, PalPtrace, PalSignal, Sys};
#[cfg(target_arch = "aarch64")]
use crate::header::arch_aarch64_user::user_regs_struct;
use super::super::{ERRNO, PalPtrace, Sys, types::*};
#[cfg(target_arch = "x86_64")]
use super::super::{Pal, PalSignal};
#[cfg(target_arch = "x86_64")]
use crate::header::arch_x64_user::user_regs_struct;
use crate::{
c_str::CString,
c_str::{CStr, CString},
error::Errno,
fs::File,
header::{errno as errnoh, fcntl, signal, sys_ptrace},
io::{self, prelude::*},
sync::{Mutex, Once},
header::{
errno::{self as errnoh, EIO},
fcntl,
},
io,
raw_cell::RawCell,
sync::Mutex,
};
#[cfg(target_arch = "x86_64")]
use crate::{
header::{signal, sys_ptrace},
io::prelude::*,
};
use alloc::collections::{btree_map::Entry, BTreeMap};
use alloc::collections::{BTreeMap, btree_map::Entry};
#[cfg(target_arch = "x86_64")]
use core::mem;
#[cfg(target_arch = "x86_64")]
use syscall;
pub struct Session {
@@ -39,14 +51,26 @@ impl State {
}
}
static STATE: Once<State> = Once::new();
#[thread_local]
static STATE: RawCell<Option<State>> = RawCell::new(None);
pub fn init_state() -> &'static State {
STATE.call_once(|| State::new())
// Safe due to STATE being thread_local (TODO: is it though?)
unsafe {
if STATE.unsafe_ref().is_none() {
STATE.unsafe_set(Some(State::new()));
}
let state_ptr = STATE.unsafe_ref().as_ref().unwrap() as *const State;
&*state_ptr
}
}
pub fn is_traceme(pid: pid_t) -> bool {
// Skip special PIDs (<=0)
if pid <= 0 {
return false;
}
File::open(
&CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap(),
CStr::borrow(&CString::new(format!("/scheme/chan/ptrace-relibc/{}/traceme", pid)).unwrap()),
fcntl::O_PATH,
)
.is_ok()
@@ -63,26 +87,28 @@ pub fn get_session(
Ok(entry.insert(Session {
first: true,
tracer: File::open(
&CString::new(format!("proc:{}/trace", pid)).unwrap(),
CStr::borrow(&CString::new(format!("/scheme/proc/{}/trace", pid)).unwrap()),
NEW_FLAGS,
)?,
mem: File::open(
&CString::new(format!("proc:{}/mem", pid)).unwrap(),
CStr::borrow(&CString::new(format!("/scheme/proc/{}/mem", pid)).unwrap()),
NEW_FLAGS,
)?,
regs: File::open(
&CString::new(format!("proc:{}/regs/int", pid)).unwrap(),
CStr::borrow(
&CString::new(format!("/scheme/proc/{}/regs/int", pid)).unwrap(),
),
NEW_FLAGS,
)?,
fpregs: File::open(
&CString::new(format!("proc:{}/regs/float", pid)).unwrap(),
CStr::borrow(
&CString::new(format!("/scheme/proc/{}/regs/float", pid)).unwrap(),
),
NEW_FLAGS,
)?,
}))
} else {
unsafe {
errno = errnoh::ESRCH;
}
ERRNO.set(errnoh::ESRCH);
Err(io::last_os_error())
}
}
@@ -90,7 +116,30 @@ pub fn get_session(
}
}
fn inner_ptrace(
#[cfg(target_arch = "aarch64")]
unsafe fn inner_ptrace(
request: c_int,
pid: pid_t,
addr: *mut c_void,
data: *mut c_void,
) -> io::Result<c_int> {
//TODO: aarch64
unimplemented!("inner_ptrace not implemented on aarch64");
}
#[cfg(target_arch = "x86")]
unsafe fn inner_ptrace(
request: c_int,
pid: pid_t,
addr: *mut c_void,
data: *mut c_void,
) -> io::Result<c_int> {
//TODO: x86
unimplemented!("inner_ptrace not implemented on x86");
}
#[cfg(target_arch = "x86_64")]
unsafe fn inner_ptrace(
request: c_int,
pid: pid_t,
addr: *mut c_void,
@@ -102,7 +151,9 @@ fn inner_ptrace(
// Mark this child as traced, parent will check for this marker file
let pid = Sys::getpid();
mem::forget(File::open(
&CString::new(format!("chan:ptrace-relibc/{}/traceme", pid)).unwrap(),
CStr::borrow(
&CString::new(format!("/scheme/chan/ptrace-relibc/{}/traceme", pid)).unwrap(),
),
fcntl::O_CREAT | fcntl::O_PATH | fcntl::O_EXCL,
)?);
return Ok(0);
@@ -117,7 +168,7 @@ fn inner_ptrace(
| sys_ptrace::PTRACE_SYSCALL
| sys_ptrace::PTRACE_SYSEMU
| sys_ptrace::PTRACE_SYSEMU_SINGLESTEP => {
Sys::kill(pid, signal::SIGCONT as _);
if let Ok(()) = Sys::kill(pid, signal::SIGCONT as _) {}; // TODO handle error
// TODO: Translate errors
let syscall = syscall::PTRACE_STOP_PRE_SYSCALL | syscall::PTRACE_STOP_POST_SYSCALL;
@@ -171,8 +222,8 @@ fn inner_ptrace(
gs_base: 0, // gs_base: redox_regs.gs_base as _,
ds: 0, // ds: redox_regs.ds as _,
es: 0, // es: redox_regs.es as _,
fs: redox_regs.fs as _,
gs: 0, // gs: redox_regs.gs as _,
fs: 0, // fs: redox_regs.fs as _,
gs: 0, // gs: redox_regs.gs as _,
};
Ok(0)
}
@@ -204,7 +255,7 @@ fn inner_ptrace(
// gs_base: c_regs.gs_base as _,
// ds: c_regs.ds as _,
// es: c_regs.es as _,
fs: c_regs.fs as _,
// fs: c_regs.fs as _,
// gs: c_regs.gs as _,
};
(&mut &session.regs).write(&redox_regs)?;
@@ -214,8 +265,26 @@ fn inner_ptrace(
}
}
#[cfg(target_arch = "riscv64")]
fn inner_ptrace(
request: c_int,
pid: pid_t,
addr: *mut c_void,
data: *mut c_void,
) -> io::Result<c_int> {
//TODO: riscv64
unimplemented!("inner_ptrace not implemented on riscv64");
}
impl PalPtrace for Sys {
fn ptrace(request: c_int, pid: pid_t, addr: *mut c_void, data: *mut c_void) -> c_int {
inner_ptrace(request, pid, addr, data).unwrap_or(-1)
#[allow(unused_unsafe)] // keeping inner unsafe fails x86_64, removing fails riscv cross-build
unsafe fn ptrace(
request: c_int,
pid: pid_t,
addr: *mut c_void,
data: *mut c_void,
) -> Result<c_int, Errno> {
unsafe { inner_ptrace(request, pid, addr, data) }
.map_err(|err| Errno(err.raw_os_error().unwrap_or(EIO)))
}
}
+330 -123
View File
@@ -1,155 +1,362 @@
use core::mem;
use syscall;
use super::{
super::{types::*, Pal, PalSignal},
e, Sys,
super::{Pal, PalSignal, types::*},
Sys,
};
#[allow(deprecated)]
use crate::header::sys_time::{ITIMER_REAL, itimerval};
use crate::{
error::{Errno, Result},
header::{
errno::EINVAL,
signal::{sigaction, sigset_t, stack_t},
sys_time::{itimerval, ITIMER_REAL},
bits_sigset_t::sigset_t,
bits_timespec::timespec,
errno::{EINVAL, ENOSYS},
signal::{
SIG_BLOCK, SIG_DFL, SIG_IGN, SIG_SETMASK, SIG_UNBLOCK, SIGALRM, SIGEV_SIGNAL,
SS_DISABLE, SS_ONSTACK, sigaction, sigevent, siginfo_t, sigval, stack_t, ucontext_t,
},
time::{itimerspec, timer_internal_t},
},
platform::errno,
out::Out,
sync::Mutex,
};
use core::mem::offset_of;
use redox_protocols::protocol::ProcKillTarget;
use redox_rt::signal::{
PosixStackt, SigStack, Sigaction, SigactionFlags, SigactionKind, Sigaltstack, SignalHandler,
};
/// Wrapper for timer_t that implements Send (the timer_t pointer is a process-
/// wide mmap'd allocation that outlives any single thread).
struct AlarmTimer(timer_t);
// SAFETY: The timer_t pointer refers to an mmap'd timer_internal_t that is
// only accessed under the ALARM_TIMER mutex lock.
unsafe impl Send for AlarmTimer {}
/// Process-global singleton timer used by alarm(). Protected by a mutex to
/// ensure only one alarm is active at a time (POSIX requirement).
static ALARM_TIMER: Mutex<Option<AlarmTimer>> = Mutex::new(None);
const _: () = {
#[track_caller]
const fn assert_eq(a: usize, b: usize) {
if a != b {
panic!("compile-time struct verification failed");
}
}
assert_eq(offset_of!(ucontext_t, uc_link), offset_of!(SigStack, link));
assert_eq(
offset_of!(ucontext_t, uc_stack),
offset_of!(SigStack, old_stack),
);
assert_eq(
offset_of!(ucontext_t, uc_sigmask),
offset_of!(SigStack, old_mask),
);
assert_eq(
offset_of!(ucontext_t, uc_mcontext),
offset_of!(SigStack, regs),
);
};
impl PalSignal for Sys {
fn getitimer(which: c_int, out: *mut itimerval) -> c_int {
#[allow(deprecated)]
fn getitimer(which: c_int, out: &mut itimerval) -> Result<()> {
let path = match which {
ITIMER_REAL => "itimer:1",
_ => unsafe {
errno = EINVAL;
return -1;
},
ITIMER_REAL => "/scheme/itimer/1",
_ => return Err(Errno(EINVAL)),
};
// TODO: implement setitimer
// let fd = FdGuard::new(redox_rt::sys::open(path, syscall::O_RDONLY | syscall::O_CLOEXEC)?);
// let count = syscall::read(*fd, &mut spec)?;
let fd = e(syscall::open(path, syscall::O_RDONLY | syscall::O_CLOEXEC));
if fd == !0 {
return -1;
}
let spec = syscall::ITimerSpec::default();
out.it_interval.tv_sec = spec.it_interval.tv_sec as time_t;
out.it_interval.tv_usec = spec.it_interval.tv_nsec / 1000;
out.it_value.tv_sec = spec.it_value.tv_sec as time_t;
out.it_value.tv_usec = spec.it_value.tv_nsec / 1000;
let mut spec = syscall::ITimerSpec::default();
let count = e(syscall::read(fd, &mut spec));
let _ = syscall::close(fd);
if count == !0 {
return -1;
}
unsafe {
(*out).it_interval.tv_sec = spec.it_interval.tv_sec;
(*out).it_interval.tv_usec = spec.it_interval.tv_nsec / 1000;
(*out).it_value.tv_sec = spec.it_value.tv_sec;
(*out).it_value.tv_usec = spec.it_value.tv_nsec / 1000;
}
0
Ok(())
}
fn kill(pid: pid_t, sig: c_int) -> c_int {
e(syscall::kill(pid as usize, sig as usize)) as c_int
fn kill(pid: pid_t, sig: c_int) -> Result<()> {
redox_rt::sys::posix_kill(ProcKillTarget::from_raw(pid as usize), sig as usize)?;
Ok(())
}
fn sigqueue(pid: pid_t, sig: c_int, val: sigval) -> Result<()> {
Ok(redox_rt::sys::posix_sigqueue(
pid as usize,
sig as usize,
unsafe { val.sival_ptr } as usize,
)?)
}
fn killpg(pgrp: pid_t, sig: c_int) -> c_int {
e(syscall::kill(-(pgrp as isize) as usize, sig as usize)) as c_int
}
fn raise(sig: c_int) -> c_int {
Self::kill(Self::getpid(), sig)
}
fn setitimer(which: c_int, new: *const itimerval, old: *mut itimerval) -> c_int {
let path = match which {
ITIMER_REAL => "itimer:1",
_ => unsafe {
errno = EINVAL;
return -1;
},
};
let fd = e(syscall::open(path, syscall::O_RDWR | syscall::O_CLOEXEC));
if fd == !0 {
return -1;
fn killpg(pgrp: pid_t, sig: c_int) -> Result<()> {
if pgrp == 1 {
return Err(Errno(EINVAL));
}
Self::kill(-pgrp, sig)
}
let mut spec = syscall::ITimerSpec::default();
fn raise(sig: c_int) -> Result<()> {
// TODO: Bypass kernel?
unsafe { Self::rlct_kill(Self::current_os_tid(), sig as _) }
}
let mut count = e(syscall::read(fd, &mut spec));
#[allow(deprecated)]
fn setitimer(which: c_int, _new: &itimerval, old: Option<&mut itimerval>) -> Result<()> {
// TODO: setitimer is no longer part of POSIX and should not be implemented in Redox
// Change the platform-independent implementation to use POSIX timers.
// For Redox, the timer should probably use "/scheme/time"
todo_skip!(0, "setitimer not implemented");
Err(Errno(ENOSYS))
}
if count != !0 {
unsafe {
if !old.is_null() {
(*old).it_interval.tv_sec = spec.it_interval.tv_sec;
(*old).it_interval.tv_usec = spec.it_interval.tv_nsec / 1000;
(*old).it_value.tv_sec = spec.it_value.tv_sec;
(*old).it_value.tv_usec = spec.it_value.tv_nsec / 1000;
fn sigaction(
sig: c_int,
c_act: Option<&sigaction>,
c_oact: Option<&mut sigaction>,
) -> Result<(), Errno> {
let sig = u8::try_from(sig).map_err(|_| syscall::Error::new(syscall::EINVAL))?;
let new_action = c_act.map(|c_act| {
let handler = c_act.sa_handler.map_or(0, |f| f as usize);
let kind = if handler == SIG_DFL {
SigactionKind::Default
} else if handler == SIG_IGN {
SigactionKind::Ignore
} else {
SigactionKind::Handled {
handler: if c_act.sa_flags & crate::header::signal::SA_SIGINFO as c_ulong != 0 {
SignalHandler {
sigaction: unsafe { core::mem::transmute(c_act.sa_handler) },
}
} else {
SignalHandler {
handler: c_act.sa_handler,
}
},
}
};
spec.it_interval.tv_sec = (*new).it_interval.tv_sec;
spec.it_interval.tv_nsec = (*new).it_interval.tv_usec * 1000;
spec.it_value.tv_sec = (*new).it_value.tv_sec;
spec.it_value.tv_nsec = (*new).it_value.tv_usec * 1000;
}
count = e(syscall::write(fd, &spec));
}
let _ = syscall::close(fd);
if count == !0 {
return -1;
}
0
}
fn sigaction(sig: c_int, act: Option<&sigaction>, oact: Option<&mut sigaction>) -> c_int {
let new_opt = act.map(|act| {
let m = act.sa_mask;
let sa_handler = unsafe { mem::transmute(act.sa_handler) };
syscall::SigAction {
sa_handler,
sa_mask: [m as u64, 0],
sa_flags: syscall::SigActionFlags::from_bits(act.sa_flags as usize)
.expect("sigaction: invalid bit pattern"),
Sigaction {
kind,
mask: c_act.sa_mask,
flags: SigactionFlags::from_bits_retain(c_act.sa_flags as u32),
}
});
let mut old_opt = oact.as_ref().map(|_| syscall::SigAction::default());
let ret = e(syscall::sigaction(
sig as usize,
new_opt.as_ref(),
old_opt.as_mut(),
)) as c_int;
if let (Some(old), Some(oact)) = (old_opt, oact) {
oact.sa_handler = unsafe { mem::transmute(old.sa_handler) };
let m = old.sa_mask;
oact.sa_mask = m[0] as c_ulong;
oact.sa_flags = old.sa_flags.bits() as c_ulong;
let mut old_action = c_oact.as_ref().map(|_| Sigaction::default());
redox_rt::signal::sigaction(sig, new_action.as_ref(), old_action.as_mut())?;
if let (Some(c_oact), Some(old_action)) = (c_oact, old_action) {
*c_oact = match old_action.kind {
SigactionKind::Ignore => sigaction {
sa_handler: unsafe { core::mem::transmute(SIG_IGN) },
sa_flags: 0,
sa_restorer: None,
sa_mask: 0,
},
SigactionKind::Default => sigaction {
sa_handler: unsafe { core::mem::transmute(SIG_DFL) },
sa_flags: 0,
sa_restorer: None,
sa_mask: 0,
},
SigactionKind::Handled { handler } => sigaction {
sa_handler: if old_action.flags.contains(SigactionFlags::SIGINFO) {
unsafe { core::mem::transmute(handler.sigaction) }
} else {
unsafe { handler.handler }
},
sa_restorer: None,
sa_flags: old_action.flags.bits() as c_ulong,
sa_mask: old_action.mask,
},
};
}
ret
Ok(())
}
fn sigaltstack(ss: *const stack_t, old_ss: *mut stack_t) -> c_int {
unimplemented!()
unsafe fn sigaltstack(
new_c: Option<&stack_t>,
old_c: Option<&mut stack_t>,
) -> Result<(), Errno> {
let new = new_c
.map(|c_stack| {
let flags = usize::try_from(c_stack.ss_flags).map_err(|_| Errno(EINVAL))?;
if flags != flags & (SS_DISABLE | SS_ONSTACK) {
return Err(Errno(EINVAL));
}
Ok(if flags & SS_DISABLE == SS_DISABLE {
Sigaltstack::Disabled
} else {
Sigaltstack::Enabled {
onstack: false,
base: c_stack.ss_sp.cast(),
size: c_stack.ss_size,
}
})
})
.transpose()?;
let mut old = old_c.as_ref().map(|_| Sigaltstack::default());
(unsafe { redox_rt::signal::sigaltstack(new.as_ref(), old.as_mut()) })?;
if let (Some(old_c_stack), Some(old)) = (old_c, old) {
let c_stack = PosixStackt::from(old);
*old_c_stack = stack_t {
ss_sp: c_stack.sp.cast(),
ss_size: c_stack.size,
ss_flags: c_stack.flags,
};
}
Ok(())
}
fn sigprocmask(how: c_int, set: *const sigset_t, oset: *mut sigset_t) -> c_int {
let new_opt = if set.is_null() {
None
} else {
Some([unsafe { *set as u64 }, 0])
};
let mut old_opt = if oset.is_null() { None } else { Some([0, 0]) };
let ret = e(syscall::sigprocmask(
how as usize,
new_opt.as_ref(),
old_opt.as_mut(),
)) as c_int;
if let Some(old) = old_opt {
unsafe { *oset = old[0] as sigset_t };
fn sigpending(set: &mut sigset_t) -> Result<(), Errno> {
*set = redox_rt::signal::currently_pending_blocked();
Ok(())
}
fn sigprocmask(
how: c_int,
set: Option<&sigset_t>,
oset: Option<&mut sigset_t>,
) -> Result<(), Errno> {
match how {
_ if set.is_none() => {
if let Some(oset) = oset {
*oset = redox_rt::signal::get_sigmask()?;
}
}
SIG_SETMASK => redox_rt::signal::set_sigmask(set.copied(), oset)?,
SIG_BLOCK => redox_rt::signal::or_sigmask(set.copied(), oset)?,
SIG_UNBLOCK => redox_rt::signal::andn_sigmask(set.copied(), oset)?,
_ => return Err(Errno(EINVAL)),
}
ret
Ok(())
}
fn sigsuspend(mask: &sigset_t) -> Errno {
match redox_rt::signal::await_signal_async(!*mask) {
Ok(_) => unreachable!(),
Err(err) => err.into(),
}
}
fn sigtimedwait(
set: &sigset_t,
info_out: Option<&mut siginfo_t>,
timeout: Option<&timespec>,
) -> Result<c_int, Errno> {
// TODO: deadline-based API
let timeout = timeout.map(|timeout| syscall::TimeSpec {
tv_sec: timeout.tv_sec,
tv_nsec: timeout.tv_nsec as _,
});
let info = redox_rt::signal::await_signal_sync(*set, timeout.as_ref())?.into();
if let Some(out) = info_out {
*out = info;
}
Ok(info.si_signo)
}
/// Thread-based alarm() that Recycles the existing POSIX timer machinery
/// (timerfd + eventfd + pthread) with process-level signal delivery.
///
/// Internally works with a timespec to allow sub-second timers in the
/// future (e.g. ualarm) as i've been asked, though the public API only exposes whole seconds.
fn alarm(seconds: c_uint) -> c_uint {
alarm_timespec(timespec {
tv_sec: seconds as time_t,
tv_nsec: 0,
})
}
}
/// Internal helper that arms/disarms the process-global alarm timer.
/// Accepts a full timespec so sub-second timers (ualarm) can reuse this later.
/// Returns the number of seconds remaining on the previous alarm (rounded up),
/// or 0 if there was no previous alarm.
///
/// TODO: This implementation does not survive `exec()`. POSIX requires that a
/// pending alarm be preserved across exec (the timer continues counting down
/// in the new process image as i understand).
fn alarm_timespec(duration: timespec) -> c_uint {
let mut guard = ALARM_TIMER.lock();
// Determine remaining time on any existing alarm
let remaining = if let Some(ref alarm) = *guard {
let mut cur = itimerspec::default();
if Sys::timer_gettime(alarm.0, Out::from_mut(&mut cur)).is_ok() {
let secs = cur.it_value.tv_sec as c_uint;
if cur.it_value.tv_nsec > 0 {
secs + 1 // POSIX: round up
} else {
secs
}
} else {
0
}
} else {
0
};
let disarm = duration.tv_sec == 0 && duration.tv_nsec == 0;
if disarm {
// alarm(0): cancel any pending alarm
if let Some(ref alarm) = *guard {
let zero = itimerspec::default();
let _ = Sys::timer_settime(alarm.0, 0, &zero, None);
}
return remaining;
}
// Lazily create the singleton timer if it doesn't exist yet
if guard.is_none() {
let evp = sigevent {
sigev_value: sigval {
sival_ptr: core::ptr::null_mut(),
},
sigev_signo: SIGALRM as c_int,
sigev_notify: SIGEV_SIGNAL,
sigev_notify_function: None,
sigev_notify_attributes: core::ptr::null_mut(),
};
let mut timer_id: timer_t = core::ptr::null_mut();
if Sys::timer_create(
crate::header::time::CLOCK_REALTIME,
&evp,
Out::from_mut(&mut timer_id),
)
.is_err()
{
return remaining;
}
// Enable process-wide signal delivery instead of thread-specific
let timer_st = unsafe { &mut *(timer_id as *mut timer_internal_t) };
timer_st.process_pid = Sys::getpid();
*guard = Some(AlarmTimer(timer_id));
}
let timer_id = guard
.as_ref()
.expect("alarm timer must exist after lazy init")
.0;
// Arm the timer as a one-shot (no interval)
let spec = itimerspec {
it_value: duration,
it_interval: timespec::default(),
};
let _ = Sys::timer_settime(timer_id, 0, &spec, None);
remaining
}
File diff suppressed because it is too large Load Diff
+103
View File
@@ -0,0 +1,103 @@
use syscall::Error;
use crate::{
error::{Errno, Result},
header::{
bits_timespec::timespec,
errno::EIO,
signal::{SIGEV_SIGNAL, SIGEV_THREAD},
time::timer_internal_t,
},
out::Out,
platform::{Pal, Sys, sys::event, types::c_void},
};
use core::{
mem::{MaybeUninit, size_of},
ptr, slice,
};
pub extern "C" fn timer_routine(arg: *mut c_void) -> *mut c_void {
let timer_st = unsafe { &mut *(arg as *mut timer_internal_t) };
loop {
let mut buf = MaybeUninit::uninit();
let res = Error::demux(unsafe {
event::redox_event_queue_get_events_v1(
timer_st.eventfd,
buf.as_mut_ptr(),
1,
0,
core::ptr::null(),
core::ptr::null(),
)
});
if let Ok(res) = res {
assert_eq!(res, 1, "EOF is not yet well defined for event queues");
} else {
break;
}
if timer_st.evp.sigev_notify == SIGEV_THREAD {
if let Some(fun) = timer_st.evp.sigev_notify_function {
fun(timer_st.evp.sigev_value);
}
} else if timer_st.evp.sigev_notify == SIGEV_SIGNAL {
// TODO: This will deliver signal to process, which is required for alarm()
// Until it can bypass the exec() boundary, do not uncomment this code
// if timer_st.process_pid != 0 && Sys::kill(timer_st.process_pid, timer_st.evp.sigev_signo).is_err() { break; } else
if unsafe { Sys::rlct_kill(timer_st.caller_thread, timer_st.evp.sigev_signo as _) }
.is_err()
{
break;
}
}
if timer_next_event(timer_st).is_err() {
break;
}
}
timer_st.thread = ptr::null_mut();
ptr::null_mut()
}
fn timer_next_event(timer_st: &mut timer_internal_t) -> Result<()> {
timer_update_wake_time(timer_st)?;
let buf_to_write = unsafe {
Error::demux(event::redox_event_queue_ctl_v1(
timer_st.eventfd,
timer_st.timerfd,
1,
0,
))?;
slice::from_raw_parts(
&timer_st.next_wake_time.it_value as *const _ as *const u8,
size_of::<timespec>(),
)
};
let bytes_written = redox_rt::sys::posix_write(timer_st.timerfd, buf_to_write)?;
if bytes_written < size_of::<timespec>() {
return Err(Errno(EIO));
}
Ok(())
}
pub(crate) fn timer_update_wake_time(timer_st: &mut timer_internal_t) -> Result<()> {
timer_st.next_wake_time.it_value = if timer_st.next_wake_time.it_interval.is_default() {
timespec::default()
} else {
let mut now = timespec::default();
Sys::clock_gettime(timer_st.clockid, Out::from_mut(&mut now))?;
let next_time = match timespec::add(now, timer_st.next_wake_time.it_interval.clone()) {
Some(a) => a,
None => timespec::default(),
};
next_time
};
if timer_st.next_wake_time.it_value.is_default() {
return Err(Errno(0));
}
Ok(())
}
+10 -5
View File
@@ -1,8 +1,11 @@
use alloc::vec::Vec;
use crate::platform::{types::*, Pal, Sys};
use crate::platform::{Pal, Sys, types::*};
use crate::header::unistd::{lseek, SEEK_SET};
use crate::{
error::ResultExt,
header::unistd::{SEEK_SET, lseek},
};
/// Implements an `Iterator` which returns on either newline or EOF.
#[derive(Clone)]
pub struct RawLineBuffer {
@@ -32,7 +35,7 @@ impl RawLineBuffer {
// Can't use iterators because we want to return a reference.
// See https://stackoverflow.com/a/30422716/5069285
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Line {
pub fn next(&mut self) -> Line<'_> {
// Remove last line
if let Some(newline) = self.newline {
self.buf.drain(..=newline);
@@ -58,7 +61,9 @@ impl RawLineBuffer {
self.buf.set_len(capacity);
}
let read = Sys::read(self.fd, &mut self.buf[len..]);
let read = Sys::read(self.fd, &mut self.buf[len..])
.map(|u| u as isize)
.or_minus_one_errno();
let read_usize = read.max(0) as usize;
@@ -92,7 +97,7 @@ impl RawLineBuffer {
/// Seek to a byte position in the file
pub fn seek(&mut self, pos: usize) -> off_t {
let ret = lseek(self.fd, pos as i64, SEEK_SET);
let ret = lseek(self.fd, pos as off_t, SEEK_SET);
if ret != !0 {
self.read = pos;
}
-1
View File
@@ -1 +0,0 @@
use crate::platform::{PalEpoll, Sys};
-87
View File
@@ -1,87 +0,0 @@
use crate::platform::{Pal, Sys};
// Stub for call used in exit
#[no_mangle]
pub extern "C" fn pthread_terminate() {}
mod epoll;
#[test]
fn access() {
use crate::header::{errno, unistd};
//TODO: create test files
assert_eq!(Sys::access(c_str!("not a file!"), unistd::F_OK), !0);
assert_eq!(Sys::access(c_str!("README.md"), unistd::F_OK), 0);
assert_eq!(Sys::access(c_str!("README.md"), unistd::R_OK), 0);
assert_eq!(Sys::access(c_str!("README.md"), unistd::W_OK), 0);
assert_eq!(Sys::access(c_str!("README.md"), unistd::X_OK), !0);
}
#[test]
fn brk() {
use core::ptr;
let current = Sys::brk(ptr::null_mut());
assert_ne!(current, ptr::null_mut());
let request = unsafe { current.add(4096) };
let next = Sys::brk(request);
assert_eq!(next, request);
}
#[test]
fn chdir() {
//TODO: create test files
assert_eq!(Sys::chdir(c_str!("src")), 0);
}
//TODO: chmod
//TODO: chown
#[test]
fn clock_gettime() {
use crate::header::time;
{
let mut timespec = time::timespec {
tv_sec: -1,
tv_nsec: -1,
};
assert_eq!(Sys::clock_gettime(time::CLOCK_REALTIME, &mut timespec), 0);
assert_ne!(timespec.tv_sec, -1);
assert_ne!(timespec.tv_nsec, -1);
}
{
let mut timespec = time::timespec {
tv_sec: -1,
tv_nsec: -1,
};
assert_eq!(Sys::clock_gettime(time::CLOCK_MONOTONIC, &mut timespec), 0);
assert_ne!(timespec.tv_sec, -1);
assert_ne!(timespec.tv_nsec, -1);
}
}
//TDOO: everything else
#[test]
fn getrandom() {
use crate::{header::sys_random, platform::types::ssize_t};
let mut arrays = [[0; 32]; 32];
for i in 1..arrays.len() {
assert_eq!(
Sys::getrandom(&mut arrays[i], 0),
arrays[i].len() as ssize_t
);
for j in 0..arrays.len() {
if i != j {
assert_ne!(&arrays[i], &arrays[j]);
}
}
}
}
+96 -8
View File
@@ -1,8 +1,9 @@
use core::i32;
//! C data types for this platform.
// Use repr(u8) as LLVM expects `void*` to be the same as `i8*` to help enable
// more optimization opportunities around it recognizing things like
// malloc/free.
/// The `void` type in C.
#[repr(u8)]
pub enum c_void {
// Two dummy variants so the #[repr] attribute can be used.
@@ -12,62 +13,149 @@ pub enum c_void {
__variant2,
}
/// The `int8_t` type provided in `stdint.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdint.h.html>.
pub type int8_t = i8;
/// The `int16_t` type provided in `stdint.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdint.h.html>.
pub type int16_t = i16;
/// The `int32_t` type provided in `stdint.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdint.h.html>.
pub type int32_t = i32;
/// The `int64_t` type provided in `stdint.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdint.h.html>.
pub type int64_t = i64;
/// The `uint8_t` type provided in `stdint.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdint.h.html>.
pub type uint8_t = u8;
/// The `uint16_t` type provided in `stdint.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdint.h.html>.
pub type uint16_t = u16;
/// The `uint32_t` type provided in `stdint.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdint.h.html>.
pub type uint32_t = u32;
/// The `uint64_t` type provided in `stdint.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdint.h.html>.
pub type uint64_t = u64;
/// The `signed char` type in C.
pub type c_schar = i8;
/// The `unsigned char` type in C.
pub type c_uchar = u8;
/// The `short` type in C.
pub type c_short = i16;
/// The `unsigned short` type in C.
pub type c_ushort = u16;
/// The `int` type in C.
pub type c_int = i32;
/// The `unsigned int` type in C.
pub type c_uint = u32;
/// The `float` type in C.
pub type c_float = f32;
/// The `double` type in C.
pub type c_double = f64;
/// The `long long` type in C.
pub type c_longlong = i64;
/// The `unsigned long long` type in C.
pub type c_ulonglong = u64;
/// The `intmax_t` type provided in `stdint.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdint.h.html>.
pub type intmax_t = i64;
/// The `uintmax_t` type provided in `stdint.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdint.h.html>.
pub type uintmax_t = u64;
/// The `size_t` type provided in `stddef.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html>.
pub type size_t = usize;
/// The `ptrdiff_t` type provided in `stddef.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html>.
pub type ptrdiff_t = isize;
/// The `intptr_t` type provided in `stdint.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdint.h.html>.
pub type intptr_t = isize;
/// The `uintptr_t` type provided in `stdint.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stdint.h.html>.
pub type uintptr_t = usize;
/// The `ssize_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type ssize_t = isize;
pub type c_char = i8;
/// The `char` type in C.
pub type c_char = core::ffi::c_char;
/// The `long` type in C.
#[cfg(target_pointer_width = "32")]
pub type c_long = i32;
/// The `unsigned long` type in C.
#[cfg(target_pointer_width = "32")]
pub type c_ulong = u32;
/// The `long` type in C.
#[cfg(target_pointer_width = "64")]
pub type c_long = i64;
/// The `unsigned long` type in C.
#[cfg(target_pointer_width = "64")]
pub type c_ulong = u64;
/// The `wchar_t` type provided in `stddef.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html>.
pub type wchar_t = i32;
/// The `wint_t` type provided in [`wchar.h`](crate::header::wchar).
pub type wint_t = u32;
pub type wctype_t = i64;
/// The `regoff_t` type provided in [`regex.h`](crate::header::regex).
pub type regoff_t = size_t;
pub type off_t = c_long;
/// The `off_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type off_t = c_longlong;
/// The `mode_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type mode_t = c_int;
pub type time_t = c_long;
/// The `time_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type time_t = c_longlong;
/// The `pid_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type pid_t = c_int;
/// The `id_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type id_t = c_uint;
/// The `gid_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type gid_t = c_int;
/// The `uid_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type uid_t = c_int;
pub type dev_t = c_long;
pub type ino_t = c_ulong;
/// The `dev_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type dev_t = c_ulonglong;
/// The `ino_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type ino_t = c_ulonglong;
/// The `reclen_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type reclen_t = c_ushort;
/// The `nlink_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type nlink_t = c_ulong;
/// The `blksize_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type blksize_t = c_long;
pub type blkcnt_t = c_ulong;
/// The `blkcnt_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type blkcnt_t = c_longlong;
/// The `fsblkcnt_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type fsblkcnt_t = c_ulong;
/// The `fsfilcnt_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type fsfilcnt_t = c_ulong;
/// The `useconds_t` type provided in [`sys/types.h`](crate::header::sys_types) prior to Issue 7.
#[deprecated]
pub type useconds_t = c_uint;
/// The `suseconds_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[cfg(target_os = "linux")]
pub type suseconds_t = c_long;
// TODO: Should we break this to c_long as well? This also breaks timeval as well
// but it will be consistent with timespec.tv_nsec (note that syscall already uses c_int)
/// The `suseconds_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[cfg(not(target_os = "linux"))]
pub type suseconds_t = c_int;
/// The `clock_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type clock_t = c_long;
/// The `clockid_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type clockid_t = c_int;
/// The `timer_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type timer_t = *mut c_void;
// A C long double is 96 bit in x86, 128 bit in other 64-bit targets
// However, both in x86 and x86_64 is actually f80 padded which rust has no underlying support,
// while aarch64 (and possibly riscv64) support full f128 type but behind a feature gate.
// Until rust supporting them, relibc will lose precision to get them working, plus:
// All read operation to this type must be converted from "relibc_ldtod".
// All write operation to this type must be converted with "relibc_dtold".
/// The `long double` type in C.
#[cfg(target_pointer_width = "64")]
pub type c_longdouble = u128;
/// The `long double` type in C.
#[cfg(target_pointer_width = "32")]
pub type c_longdouble = [u32; 3];
pub use crate::header::bits_pthread::*;
/// The `max_align_t` type provided in `stddef.h`, see <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html>.
#[repr(C, align(16))]
pub struct max_align_t {
_priv: [f64; 4],
}