0.3.0: converge relibc to upstream 0.6.0 + Red Bear patches
This commit is contained in:
@@ -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) }
|
||||
}
|
||||
@@ -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()) }
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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<×pec>,
|
||||
) -> 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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]]>) {}
|
||||
|
||||
@@ -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
@@ -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<×pec>) -> 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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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<×pec>,
|
||||
) -> Result<c_int>;
|
||||
}
|
||||
|
||||
+54
-17
@@ -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<()>;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
}
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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(¤t_path_string, flags) {
|
||||
Ok(fd) => return Ok(fd),
|
||||
Err(e) if e == Error::new(EXDEV) => {
|
||||
let link_target = read_link_content(¤t_path_string, false)?;
|
||||
|
||||
current_path_string = calc_next_abs_path(¤t_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(¤t_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(¤t_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)
|
||||
}
|
||||
@@ -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
@@ -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<×pec>,
|
||||
) -> 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
|
||||
}
|
||||
|
||||
+993
-248
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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 +0,0 @@
|
||||
use crate::platform::{PalEpoll, Sys};
|
||||
@@ -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
@@ -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],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user