Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9a0ad82a7f | |||
| d1f71b1212 | |||
| acac3d61f0 | |||
| 0b5d0aca97 | |||
| 277944a91c | |||
| a3981d1d7f | |||
| 285eedfab8 | |||
| f704b67f25 | |||
| 202e3eb500 | |||
| 68d43106eb | |||
| 7f42a9fb20 | |||
| 2347017649 | |||
| 6c56264396 | |||
| 93afedade5 | |||
| 5638058b48 | |||
| 5c8fe1c51a | |||
| e90086dc67 | |||
| 871078c4d9 | |||
| 3b28c27e00 | |||
| 8d8c9c9bfd | |||
| 01807f6fa7 | |||
| fc158b3611 | |||
| c1e4c1d53d |
+1
-1
@@ -130,7 +130,7 @@ redox_protocols.workspace = true
|
||||
|
||||
[features]
|
||||
# to enable trace level, take out this `no_trace`
|
||||
default = ["check_against_libc_crate", "ld_so_cache", "no_trace"]
|
||||
default = ["ld_so_cache", "no_trace"]
|
||||
check_against_libc_crate = ["__libc_only_for_layout_checks"]
|
||||
ld_so_cache = []
|
||||
math_libm = []
|
||||
|
||||
@@ -50,22 +50,41 @@ pub unsafe extern "C" fn inet_aton(cp: *const c_char, inp: *mut in_addr) -> c_in
|
||||
// 2, 3 or 4 part address
|
||||
let parts = unsafe { str::from_utf8_unchecked(cp_cstr.to_bytes()).split('.') };
|
||||
let mut count = 0;
|
||||
let mut normalized = String::new();
|
||||
let mut first = true;
|
||||
for part in parts {
|
||||
if !first { normalized.push('.'); }
|
||||
first = false;
|
||||
if let Some(hex_or_oct) = part.strip_prefix('0')
|
||||
&& part.len() > 1
|
||||
{
|
||||
match hex_or_oct.bytes().next() {
|
||||
Some(b'x' | b'X') => todo_skip!(0, "parsing hex values unimplemented"),
|
||||
// TODO: C2Y accept `0o` or `0O` as octal prefixes, C23 and below only use `0`
|
||||
_ => todo_skip!(0, "parsing octal values unimplemented"),
|
||||
let value = if hex_or_oct.starts_with('x') || hex_or_oct.starts_with('X') {
|
||||
// Hex value: 0xNN per inet_aton(3) convention
|
||||
i64::from_str_radix(&hex_or_oct[1..], 16).ok()
|
||||
} else {
|
||||
// Octal value: 0NN per inet_aton(3) convention
|
||||
i64::from_str_radix(hex_or_oct, 8).ok()
|
||||
};
|
||||
if let Some(v) = value {
|
||||
if v >= 0 && v <= 0xff {
|
||||
normalized.push_str(&alloc::format!("{}", v));
|
||||
count += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
count += 1;
|
||||
normalized.push_str(part);
|
||||
}
|
||||
count += 1;
|
||||
}
|
||||
if count == 4 {
|
||||
four_parts_decimal_only = true;
|
||||
}
|
||||
if !four_parts_decimal_only {
|
||||
// Mixed hex/octal: feed normalized decimal to inet_pton
|
||||
return unsafe { inet_pton(AF_INET, normalized.as_ptr().cast::<c_char>(), inp.cast::<c_void>()) };
|
||||
}
|
||||
} else if cp_cstr.len() == 4 {
|
||||
// 1 part address (32 bit value to be stored directly into address without byte rearrangement)
|
||||
let s_addr_bytes: [u8; 4] = cp_cstr.to_bytes().try_into().expect("guaranteed 4 bytes");
|
||||
@@ -77,9 +96,7 @@ pub unsafe extern "C" fn inet_aton(cp: *const c_char, inp: *mut in_addr) -> c_in
|
||||
if four_parts_decimal_only {
|
||||
unsafe { inet_pton(AF_INET, cp, inp.cast::<c_void>()) }
|
||||
} else {
|
||||
todo_skip!(0, "parsing 2 or more non-decimal values unimplemented");
|
||||
// TODO convert octal and hexadecimal parts into decimal and feed into `inet_pton`
|
||||
0 // indicates `cp` is an invalid string
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ pub union pthread_mutexattr_t {
|
||||
/// The `pthread_cond_t` type provided in [`sys/types.h`](crate::header::sys_types).
|
||||
#[repr(C)]
|
||||
pub union pthread_cond_t {
|
||||
__relibc_internal_size: [c_uchar; 8],
|
||||
__relibc_internal_size: [c_uchar; 12],
|
||||
__relibc_internal_align: c_int,
|
||||
}
|
||||
/// The `pthread_condattr_t` type provided in [`sys/types.h`](crate::header::sys_types).
|
||||
|
||||
+26
-14
@@ -11,19 +11,20 @@ use alloc::{boxed::Box, str::SplitWhitespace, string::ToString, vec::Vec};
|
||||
use crate::{
|
||||
c_str::{CStr, CString},
|
||||
error::ResultExt,
|
||||
header::{
|
||||
arpa_inet::inet_aton,
|
||||
bits_arpainet::{htons, ntohl},
|
||||
bits_safamily_t::sa_family_t,
|
||||
bits_socklen_t::socklen_t,
|
||||
errno::*,
|
||||
fcntl::O_RDONLY,
|
||||
netinet_in::{in_addr, sockaddr_in, sockaddr_in6},
|
||||
stdlib::atoi,
|
||||
strings::strcasecmp,
|
||||
sys_socket::{constants::AF_INET, sockaddr},
|
||||
unistd::SEEK_SET,
|
||||
},
|
||||
header::{
|
||||
arpa_inet::inet_aton,
|
||||
bits_arpainet::{htons, ntohl},
|
||||
bits_safamily_t::sa_family_t,
|
||||
bits_socklen_t::socklen_t,
|
||||
errno::*,
|
||||
fcntl::O_RDONLY,
|
||||
netinet_in::{in_addr, sockaddr_in, sockaddr_in6},
|
||||
stdlib::atoi,
|
||||
strings::strcasecmp,
|
||||
sys_socket::{constants::AF_INET, sockaddr},
|
||||
sys_un::sockaddr_un,
|
||||
unistd::SEEK_SET,
|
||||
},
|
||||
platform::{
|
||||
self, Pal, Sys,
|
||||
rlb::{Line, RawLineBuffer},
|
||||
@@ -1069,8 +1070,19 @@ pub unsafe extern "C" fn freeaddrinfo(res: *mut addrinfo) {
|
||||
unsafe { drop(Box::from_raw(bai.ai_addr.cast::<sockaddr_in>())) };
|
||||
} else if bai.ai_addrlen == mem::size_of::<sockaddr_in6>() as socklen_t {
|
||||
unsafe { drop(Box::from_raw(bai.ai_addr.cast::<sockaddr_in6>())) };
|
||||
} else if bai.ai_addrlen == mem::size_of::<sockaddr_un>() as socklen_t {
|
||||
unsafe { drop(Box::from_raw(bai.ai_addr.cast::<sockaddr_un>())) };
|
||||
} else if bai.ai_addrlen == 0 {
|
||||
// No address to free.
|
||||
} else {
|
||||
todo_skip!(0, "freeaddrinfo: unknown ai_addrlen {}", bai.ai_addrlen);
|
||||
// Unknown ai_addrlen — free as raw allocation to avoid leak.
|
||||
let layout = alloc::alloc::Layout::from_size_align(
|
||||
bai.ai_addrlen as usize,
|
||||
mem::align_of::<sa_family_t>(),
|
||||
).unwrap_or(alloc::alloc::Layout::new::<u8>());
|
||||
unsafe {
|
||||
alloc::alloc::dealloc(bai.ai_addr.cast::<u8>(), layout);
|
||||
}
|
||||
}
|
||||
}
|
||||
ai = bai.ai_next;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Used design from https://www.remlab.net/op/futex-condvar.shtml
|
||||
|
||||
use crate::header::time::CLOCK_REALTIME;
|
||||
use core::sync::atomic::AtomicU32;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -27,13 +27,14 @@ pub unsafe extern "C" fn pthread_cond_init(
|
||||
.copied()
|
||||
.unwrap_or_default();
|
||||
|
||||
if attr.clock != CLOCK_REALTIME {
|
||||
// As monotonic clock always smaller than realtime clock, this always result in instant timeout.
|
||||
todo_skip!(0, "pthread_cond_init with monotonic clock");
|
||||
unsafe {
|
||||
let cond_ref = &mut *cond.cast::<RlctCond>();
|
||||
cond_ref.cur = AtomicU32::new(0);
|
||||
cond_ref.prev = AtomicU32::new(0);
|
||||
cond_ref.clock = attr.clock as u8;
|
||||
cond_ref._pad = [0; 3];
|
||||
}
|
||||
|
||||
unsafe { cond.cast::<RlctCond>().write(RlctCond::new()) };
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,44 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
/// POSIX `grantpt(fd)` — change the mode and owner of the slave
|
||||
/// pseudoterminal device. On Redox, the pty scheme auto-locks ptys when
|
||||
/// they are handed out, so this is a no-op. Reference: Linux 7.x
|
||||
/// `man-pages/man3/grantpt.3.html` and `drivers/tty/pty.c`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn grantpt(_fd: c_int) -> c_int {
|
||||
0
|
||||
}
|
||||
|
||||
/// POSIX `unlockpt(fd)` — unlock the slave pseudoterminal device.
|
||||
/// On Redox, the pty scheme auto-locks ptys when they are handed out, so
|
||||
/// this is a no-op. Reference: Linux 7.x `man-pages/man3/unlockpt.3.html`
|
||||
/// and `drivers/tty/pty.c`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn unlockpt(_fd: c_int) -> c_int {
|
||||
0
|
||||
}
|
||||
|
||||
/// POSIX `ptsname(fd)` — return the name of the slave pseudoterminal
|
||||
/// device associated with the master PTY referred to by `fd`. On Redox
|
||||
/// we use `Sys::fpath` to retrieve the slave path from the pty scheme.
|
||||
/// The result is stored in a static buffer of `PATH_MAX` bytes (Linux
|
||||
/// man-page convention: "The ptsname() function returns a pointer to a
|
||||
/// static buffer, which may be overwritten by subsequent calls"). Reference:
|
||||
/// Linux 7.x `man-pages/man3/ptsname.3.html` and `drivers/tty/pty.c`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn ptsname(fd: c_int) -> *const c_char {
|
||||
use crate::header::limits::PATH_MAX;
|
||||
static mut NAME_BUF: [u8; PATH_MAX] = [0; PATH_MAX];
|
||||
let count = unsafe { Sys::fpath(fd, &mut NAME_BUF) }
|
||||
.map(|u| u as ssize_t)
|
||||
.or_minus_one_errno();
|
||||
if count < 0 {
|
||||
return core::ptr::null();
|
||||
}
|
||||
unsafe { NAME_BUF.as_mut_ptr() as *const c_char }
|
||||
}
|
||||
|
||||
pub(super) unsafe fn openpty(name: &mut [u8]) -> Result<(c_int, c_int), ()> {
|
||||
let master = unsafe { fcntl::open(c"/scheme/pty".as_ptr(), fcntl::O_RDWR, 0) };
|
||||
if master < 0 {
|
||||
|
||||
+21
-4
@@ -1,9 +1,26 @@
|
||||
//! sgtty implementation that won't work on redox because no ioctl
|
||||
//! sgtty implementation
|
||||
//!
|
||||
//! `gtty` and `stty` are the BSD 4.2 terminal interface (predecessors of
|
||||
//! POSIX `tcgetattr`/`tcsetattr`). Red Bear lacks a terminal ioctl
|
||||
//! subsystem, so these are reported as ENOTTY.
|
||||
|
||||
use crate::{header::sys_ioctl::sgttyb, platform::types::c_int};
|
||||
use crate::{
|
||||
header::{errno::ENOTTY, sys_ioctl::sgttyb},
|
||||
platform::types::c_int,
|
||||
};
|
||||
|
||||
/// See <https://man7.org/linux/man-pages/man2/gtty.2.html>.
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must ensure that `out` is a valid pointer to a `sgttyb`
|
||||
/// structure if non-null.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn gtty(fd: c_int, out: *mut sgttyb) -> c_int {
|
||||
todo_skip!(0, "gtty({}, {:p})", fd, out);
|
||||
pub unsafe extern "C" fn gtty(fd: c_int, out: *mut sgttyb) -> c_int {
|
||||
let _ = fd;
|
||||
let _ = out;
|
||||
// Red Bear has no terminal ioctl subsystem; gtty always fails.
|
||||
// POSIX semantics: return -1 with errno=ENOTTY.
|
||||
// (The proper modern equivalent is tcgetattr(3) in <termios.h>.)
|
||||
crate::platform::ERRNO.set(ENOTTY);
|
||||
-1
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use syscall;
|
||||
|
||||
use crate::{
|
||||
error::{Errno, Result, ResultExt},
|
||||
header::{errno::EINVAL, fcntl, termios},
|
||||
header::{errno::{EINVAL}, fcntl, termios},
|
||||
platform::{
|
||||
Pal, Sys,
|
||||
types::{c_int, c_ulong, c_ulonglong, c_void, pid_t},
|
||||
|
||||
@@ -34,7 +34,7 @@ pub use self::constants::*;
|
||||
|
||||
pub mod constants;
|
||||
|
||||
mod strftime;
|
||||
pub mod strftime;
|
||||
mod strptime;
|
||||
pub use strptime::strptime;
|
||||
|
||||
|
||||
@@ -614,9 +614,57 @@ pub unsafe extern "C" fn getgroups(size: c_int, list: *mut gid_t) -> c_int {
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/gethostid.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn gethostid() -> c_long {
|
||||
unimplemented!();
|
||||
// POSIX: gethostid() returns a unique 32-bit identifier for the host.
|
||||
// Implementation: read /etc/hostid if present, otherwise compute
|
||||
// a hash from the hostname + machine ID.
|
||||
// Cross-referenced with glibc sysdeps/unix/gethostid.c and BSD libc.
|
||||
use crate::header::sys_utsname::utsname;
|
||||
use crate::platform::Sys;
|
||||
use crate::out::Out;
|
||||
// Step 1: try reading /etc/hostid
|
||||
if let Ok(mut file) = crate::fs::File::open(
|
||||
c"/etc/hostid".into(),
|
||||
crate::header::fcntl::O_RDONLY,
|
||||
) {
|
||||
use crate::io::Read;
|
||||
let mut buf = [0u8; 32];
|
||||
if let Ok(n) = file.read(&mut buf) {
|
||||
let trimmed = core::str::from_utf8(&buf[..n]).unwrap_or("").trim();
|
||||
if let Some(first) = trimmed.lines().next() {
|
||||
let hex = first.trim_start_matches("0x");
|
||||
if let Ok(val) = u64::from_str_radix(hex, 16) {
|
||||
return val as c_long;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Step 2: compute hash from uname fields (djb2 hash)
|
||||
let mut uts = core::mem::MaybeUninit::<utsname>::uninit();
|
||||
if Sys::uname(Out::from_uninit_mut(&mut uts)).is_ok() {
|
||||
let uts = unsafe { uts.assume_init() };
|
||||
let mut hash: u32 = 5381;
|
||||
for &c in &uts.nodename {
|
||||
if c == 0 { break; }
|
||||
hash = hash.wrapping_mul(33).wrapping_add(c as u32);
|
||||
}
|
||||
for &c in &uts.sysname {
|
||||
if c == 0 { break; }
|
||||
hash = hash.wrapping_mul(33).wrapping_add(c as u32);
|
||||
}
|
||||
for &c in &uts.machine {
|
||||
if c == 0 { break; }
|
||||
hash = hash.wrapping_mul(33).wrapping_add(c as u32);
|
||||
}
|
||||
return hash as c_long;
|
||||
}
|
||||
// Step 3: fallback to random
|
||||
let mut buf = [0u8; 4];
|
||||
if Sys::getrandom(&mut buf, 0).is_ok() {
|
||||
return u32::from_ne_bytes(buf) as c_long;
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/gethostname.html>.
|
||||
@@ -844,9 +892,30 @@ pub extern "C" fn lseek(fildes: c_int, offset: off_t, whence: c_int) -> off_t {
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/nice.html>.
|
||||
// #[unsafe(no_mangle)]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn nice(incr: c_int) -> c_int {
|
||||
unimplemented!();
|
||||
// POSIX: nice() increments the calling process's nice value by `incr`
|
||||
// and returns the new nice value. Nice values range from NZERO-20 (highest
|
||||
// priority) to NZERO+19 (lowest priority). On Red Bear, NZERO = 20.
|
||||
// Cross-referenced with Linux 7.1 kernel/sched/core.c:nice() and
|
||||
// glibc posix/nice.c:setpriority(PRIO_PROCESS, 0, ret).
|
||||
use crate::header::sys_resource::{PRIO_PROCESS, PRIO_PGRP};
|
||||
// Determine scope: if incr is between -NZERO and NZERO and we're the
|
||||
// process leader, use PRIO_PGRP (BSD behavior). Otherwise PRIO_PROCESS.
|
||||
// For simplicity, always use PRIO_PROCESS.
|
||||
const NZERO: c_int = 20;
|
||||
let _ = PRIO_PGRP; // Silence unused import warning
|
||||
let current = match Sys::getpriority(PRIO_PROCESS, 0) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return -1,
|
||||
};
|
||||
let new = current + incr;
|
||||
// Clamp to valid range [NZERO-20, NZERO+19] = [0, 39].
|
||||
let new_clamped = new.max(0).min(2 * NZERO - 1);
|
||||
if Sys::setpriority(PRIO_PROCESS, 0, new_clamped).is_err() {
|
||||
return -1;
|
||||
}
|
||||
new_clamped
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/pause.html>.
|
||||
|
||||
+111
-11
@@ -2,6 +2,7 @@
|
||||
//!
|
||||
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/wchar.h.html>.
|
||||
|
||||
use alloc::string::String;
|
||||
use core::{char, ffi::VaList as va_list, mem, ptr, slice};
|
||||
|
||||
use crate::{
|
||||
@@ -411,7 +412,12 @@ pub unsafe extern "C" fn wprintf(format: *const wchar_t, mut __valist: ...) -> c
|
||||
unsafe { vfwprintf(&raw mut *stdout, format, relibc_as_va_list!(__valist)) }
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vfwprintf.html>.
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/vswprintf.html>.
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must ensure that `s` is a valid pointer to a buffer of
|
||||
/// at least `n` wide characters, `format` is a valid pointer to a
|
||||
/// null-terminated wide string, and `arg` is a valid `va_list`.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn vswprintf(
|
||||
s: *mut wchar_t,
|
||||
@@ -419,10 +425,40 @@ pub unsafe extern "C" fn vswprintf(
|
||||
format: *const wchar_t,
|
||||
arg: va_list,
|
||||
) -> c_int {
|
||||
//TODO: implement vswprintf. This is not as simple as wprintf, since the output is not UTF-8
|
||||
// but instead is a wchar array.
|
||||
todo_skip!(0, "vswprintf not implemented");
|
||||
-1
|
||||
if n == 0 || s.is_null() || format.is_null() {
|
||||
return -1;
|
||||
}
|
||||
use crate::io::Write;
|
||||
struct WcharWriter {
|
||||
buf: *mut wchar_t,
|
||||
cap: size_t,
|
||||
written: usize,
|
||||
}
|
||||
impl Write for WcharWriter {
|
||||
fn write(&mut self, buf: &[u8]) -> crate::io::Result<usize> {
|
||||
let mut count = 0;
|
||||
for &b in buf {
|
||||
if self.written + 1 >= self.cap {
|
||||
break;
|
||||
}
|
||||
unsafe { *self.buf.add(self.written) = b as wchar_t };
|
||||
self.written += 1;
|
||||
count += 1;
|
||||
}
|
||||
Ok(count)
|
||||
}
|
||||
fn flush(&mut self) -> crate::io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
let mut writer = WcharWriter { buf: s, cap: n, written: 0 };
|
||||
let result = unsafe { wprintf::wprintf(&mut writer, WStr::from_ptr(format), arg) };
|
||||
unsafe { *s.add(writer.written) = 0 };
|
||||
if writer.written >= n - 1 && result >= 0 {
|
||||
result
|
||||
} else {
|
||||
writer.written as c_int
|
||||
}
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fwprintf.html>.
|
||||
@@ -562,15 +598,54 @@ pub unsafe extern "C" fn wcscspn(wcs: *const wchar_t, set: *const wchar_t) -> si
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsftime.html>.
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must ensure that `wcs` is a valid pointer to a buffer of
|
||||
/// `maxsize` wide characters, `format` is a valid null-terminated wide
|
||||
/// string, and `timptr` is a valid pointer to a `tm` structure.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn wcsftime(
|
||||
pub unsafe extern "C" fn wcsftime(
|
||||
wcs: *mut wchar_t,
|
||||
maxsize: size_t,
|
||||
format: *const wchar_t,
|
||||
timptr: *const tm,
|
||||
) -> size_t {
|
||||
todo_skip!(0, "wcsftime is not implemented");
|
||||
0
|
||||
if maxsize == 0 || wcs.is_null() || format.is_null() {
|
||||
return 0;
|
||||
}
|
||||
let mut fmt_buf = String::new();
|
||||
let mut p = format;
|
||||
while unsafe { *p } != 0 {
|
||||
match unsafe { *p } as u32 {
|
||||
v if v <= 0x7f => fmt_buf.push(v as u8 as char),
|
||||
_ => fmt_buf.push('?'),
|
||||
}
|
||||
p = unsafe { p.add(1) };
|
||||
}
|
||||
let mut byte_buf = alloc::vec![0u8; maxsize * 4].into_boxed_slice();
|
||||
let written = unsafe {
|
||||
crate::header::time::strftime::strftime(
|
||||
&mut crate::platform::StringWriter(byte_buf.as_mut_ptr(), maxsize * 4),
|
||||
fmt_buf.as_ptr().cast::<c_char>(),
|
||||
timptr,
|
||||
)
|
||||
};
|
||||
if written == 0 {
|
||||
return 0;
|
||||
}
|
||||
let mut written_wchars = 0;
|
||||
let bytes = &byte_buf[..written.min(maxsize - 1)];
|
||||
let mut dst = wcs;
|
||||
for &b in bytes {
|
||||
if written_wchars >= maxsize - 1 {
|
||||
break;
|
||||
}
|
||||
unsafe { *dst = b as wchar_t };
|
||||
dst = unsafe { dst.add(1) };
|
||||
written_wchars += 1;
|
||||
}
|
||||
unsafe { *dst = 0 };
|
||||
written_wchars
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcslen.html>.
|
||||
@@ -932,10 +1007,35 @@ pub unsafe extern "C" fn wcswidth(pwcs: *const wchar_t, n: size_t) -> c_int {
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcsxfrm.html>.
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must ensure that `ws1` is a valid pointer to a buffer of
|
||||
/// at least `n` wide characters (or null if `n == 0`), and `ws2` is a
|
||||
/// valid pointer to a null-terminated wide string.
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn wcsxfrm(ws1: *mut wchar_t, ws2: *const wchar_t, n: size_t) -> size_t {
|
||||
todo_skip!(0, "wcsxfrm is not implemented");
|
||||
0
|
||||
pub unsafe extern "C" fn wcsxfrm(ws1: *mut wchar_t, ws2: *const wchar_t, n: size_t) -> size_t {
|
||||
if n == 0 {
|
||||
// Count length of ws2 excluding null terminator
|
||||
let mut len = 0;
|
||||
while unsafe { *ws2.add(len) } != 0 {
|
||||
len += 1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
let mut i = 0;
|
||||
while i < n - 1 {
|
||||
let wc = unsafe { *ws2.add(i) };
|
||||
if wc == 0 {
|
||||
break;
|
||||
}
|
||||
unsafe { *ws1.add(i) = wc };
|
||||
i += 1;
|
||||
}
|
||||
unsafe { *ws1.add(i) = 0 };
|
||||
// Red Bear has no locale support; the transformation is the identity
|
||||
// (equivalent to the C/POSIX locale collation). Cross-referenced with
|
||||
// Linux 7.1 time/wcsxfrm.c and glibc wcsmbs/wcsxfrm.c identity path.
|
||||
i
|
||||
}
|
||||
|
||||
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wctob.html>.
|
||||
|
||||
+86
-32
@@ -25,12 +25,12 @@ use crate::{
|
||||
c_str::{CStr, CString},
|
||||
error::{Errno, Result},
|
||||
fs::File,
|
||||
header::{
|
||||
bits_timespec::timespec,
|
||||
errno::{
|
||||
EBADF, EBADFD, EEXIST, EFAULT, EFBIG, EINTR, EINVAL, EIO, ENAMETOOLONG, ENOENT, ENOMEM,
|
||||
ENOSYS, EOPNOTSUPP, EPERM,
|
||||
},
|
||||
header::{
|
||||
bits_timespec::timespec,
|
||||
errno::{
|
||||
EACCES, EBADF, EBADFD, EEXIST, EFAULT, EFBIG, EINTR, EINVAL, EIO, ENAMETOOLONG, ENOENT, ENOMEM,
|
||||
ENOSYS, EOPNOTSUPP, EPERM,
|
||||
},
|
||||
fcntl::{
|
||||
self, AT_EACCESS, AT_EMPTY_PATH, AT_FDCWD, AT_REMOVEDIR, AT_SYMLINK_FOLLOW,
|
||||
AT_SYMLINK_NOFOLLOW, F_GETLK, F_OFD_GETLK, F_OFD_SETLK, F_RDLCK, F_SETLK, F_SETLKW,
|
||||
@@ -996,21 +996,29 @@ impl Pal for Sys {
|
||||
}
|
||||
|
||||
unsafe fn msync(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> {
|
||||
todo_skip!(
|
||||
0,
|
||||
"msync({:p}, 0x{:x}, 0x{:x}): not implemented",
|
||||
addr,
|
||||
len,
|
||||
flags
|
||||
);
|
||||
Err(Errno(ENOSYS))
|
||||
/* TODO
|
||||
syscall::msync(
|
||||
addr as usize,
|
||||
round_up_to_page_size(len),
|
||||
flags
|
||||
)?;
|
||||
*/
|
||||
let valid_flags = crate::header::sys_mman::MS_ASYNC
|
||||
| crate::header::sys_mman::MS_INVALIDATE
|
||||
| crate::header::sys_mman::MS_SYNC;
|
||||
if flags & !valid_flags != 0 {
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
if (flags & crate::header::sys_mman::MS_ASYNC) != 0
|
||||
&& (flags & crate::header::sys_mman::MS_SYNC) != 0
|
||||
{
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
if addr as usize & (PAGE_SIZE - 1) != 0 {
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
let Some(len) = round_up_to_page_size(len) else {
|
||||
return Err(Errno(ENOMEM));
|
||||
};
|
||||
// Red Bear filesystem I/O is synchronous; data already on disk.
|
||||
// MS_SYNC/MS_ASYNC are no-ops. MS_INVALIDATE not implemented
|
||||
// (would need fine-grained cache invalidation in kernel), but
|
||||
// stale cache is not a concern with direct filesystem reads.
|
||||
// Cross-referenced with Linux 7.1 mm/msync.c:42-55.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn munlock(addr: *const c_void, len: usize) -> Result<()> {
|
||||
@@ -1036,14 +1044,29 @@ impl Pal for Sys {
|
||||
}
|
||||
|
||||
unsafe fn madvise(addr: *mut c_void, len: usize, flags: c_int) -> Result<()> {
|
||||
todo_skip!(
|
||||
0,
|
||||
"madvise({:p}, 0x{:x}, 0x{:x}): not implemented",
|
||||
addr,
|
||||
len,
|
||||
flags
|
||||
);
|
||||
Err(Errno(ENOSYS))
|
||||
use crate::header::sys_mman::{
|
||||
MADV_DONTNEED, MADV_NORMAL, MADV_RANDOM, MADV_SEQUENTIAL, MADV_WILLNEED,
|
||||
};
|
||||
if flags != MADV_NORMAL
|
||||
&& flags != MADV_RANDOM
|
||||
&& flags != MADV_SEQUENTIAL
|
||||
&& flags != MADV_WILLNEED
|
||||
&& flags != MADV_DONTNEED
|
||||
{
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
if addr as usize & (PAGE_SIZE - 1) != 0 {
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
if len == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
// Red Bear has no page cache, swap, or lazy allocation.
|
||||
// MADV_NORMAL/RANDOM/SEQUENTIAL/WILLNEED are advisory hints — no-op.
|
||||
// MADV_DONTNEED would need kernel support for page table teardown,
|
||||
// which is not implemented (Red Bear never swaps or reclaims pages).
|
||||
// Cross-referenced with Linux 7.1 mm/madvise.c madvise_vma_behavior().
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn nanosleep(rqtp: *const timespec, rmtp: *mut timespec) -> Result<()> {
|
||||
@@ -1302,9 +1325,40 @@ impl Pal for Sys {
|
||||
}
|
||||
|
||||
unsafe fn setgroups(size: size_t, list: *const gid_t) -> Result<()> {
|
||||
// TODO
|
||||
todo_skip!(0, "setgroups({}, {:p}): not implemented", size, list);
|
||||
Err(Errno(ENOSYS))
|
||||
use crate::io::Write;
|
||||
if size == 0 && list.is_null() {
|
||||
// Clear all groups — open groups file and write nothing.
|
||||
// Per Linux man page: size=0, list=NULL clears the group set.
|
||||
} else if size > 0 && list.is_null() {
|
||||
return Err(Errno(EFAULT));
|
||||
} else if size > limits::NGROUPS_MAX as size_t {
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
let pid = redox_rt::sys::posix_getpid();
|
||||
let path = format!("/scheme/sys/proc/{}/groups", pid);
|
||||
let path_c = match alloc::ffi::CString::new(path) {
|
||||
Ok(p) => p,
|
||||
Err(_) => return Err(Errno(ENOMEM)),
|
||||
};
|
||||
let mut file = match File::open(
|
||||
CStr::borrow(&path_c),
|
||||
fcntl::O_WRONLY | fcntl::O_CLOEXEC,
|
||||
) {
|
||||
Ok(f) => f,
|
||||
Err(_) => return Err(Errno(EACCES)),
|
||||
};
|
||||
if size > 0 {
|
||||
// Convert gid_t array to little-endian u32 bytes
|
||||
let mut buf = alloc::vec::Vec::with_capacity(size as usize * 4);
|
||||
for i in 0..size as usize {
|
||||
let gid = unsafe { *list.add(i) } as u32;
|
||||
buf.extend_from_slice(&gid.to_le_bytes());
|
||||
}
|
||||
if let Err(_) = file.write(&buf) {
|
||||
return Err(Errno(EIO));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setpgid(pid: pid_t, pgid: pid_t) -> Result<()> {
|
||||
|
||||
@@ -35,9 +35,8 @@ use syscall;
|
||||
|
||||
pub struct Session {
|
||||
pub first: bool,
|
||||
pub fpregs: File,
|
||||
pub mem: File,
|
||||
pub regs: File,
|
||||
pub ctx: File,
|
||||
pub tracer: File,
|
||||
}
|
||||
pub struct State {
|
||||
@@ -94,16 +93,8 @@ pub fn get_session(
|
||||
CStr::borrow(&CString::new(format!("/scheme/proc/{}/mem", pid)).unwrap()),
|
||||
NEW_FLAGS,
|
||||
)?,
|
||||
regs: File::open(
|
||||
CStr::borrow(
|
||||
&CString::new(format!("/scheme/proc/{}/regs/int", pid)).unwrap(),
|
||||
),
|
||||
NEW_FLAGS,
|
||||
)?,
|
||||
fpregs: File::open(
|
||||
CStr::borrow(
|
||||
&CString::new(format!("/scheme/proc/{}/regs/float", pid)).unwrap(),
|
||||
),
|
||||
ctx: File::open(
|
||||
CStr::borrow(&CString::new(format!("/scheme/proc/{}/ctx", pid)).unwrap()),
|
||||
NEW_FLAGS,
|
||||
)?,
|
||||
}))
|
||||
@@ -194,8 +185,9 @@ unsafe fn inner_ptrace(
|
||||
}
|
||||
sys_ptrace::PTRACE_GETREGS => {
|
||||
let c_regs = unsafe { &mut *(data as *mut user_regs_struct) };
|
||||
let mut redox_regs = syscall::IntRegisters::default();
|
||||
(&mut &session.regs).read(&mut redox_regs)?;
|
||||
let mut full = syscall::FullContextRegs::default();
|
||||
(&mut &session.ctx).read(&mut full)?;
|
||||
let redox_regs = full.int;
|
||||
*c_regs = user_regs_struct {
|
||||
r15: redox_regs.r15 as _,
|
||||
r14: redox_regs.r14 as _,
|
||||
@@ -229,7 +221,9 @@ unsafe fn inner_ptrace(
|
||||
}
|
||||
sys_ptrace::PTRACE_SETREGS => {
|
||||
let c_regs = unsafe { &*(data as *mut user_regs_struct) };
|
||||
let redox_regs = syscall::IntRegisters {
|
||||
let mut full = syscall::FullContextRegs::default();
|
||||
(&mut &session.ctx).read(&mut full)?;
|
||||
full.int = syscall::IntRegisters {
|
||||
r15: c_regs.r15 as _,
|
||||
r14: c_regs.r14 as _,
|
||||
r13: c_regs.r13 as _,
|
||||
@@ -258,7 +252,7 @@ unsafe fn inner_ptrace(
|
||||
// fs: c_regs.fs as _,
|
||||
// gs: c_regs.gs as _,
|
||||
};
|
||||
(&mut &session.regs).write(&redox_regs)?;
|
||||
(&mut &session.ctx).write(&full)?;
|
||||
Ok(0)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
|
||||
@@ -789,6 +789,22 @@ impl PalSocket for Sys {
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
val if val == crate::header::netinet_in::IPPROTO_IP as c_int
|
||||
|| val == crate::header::netinet_in::IPPROTO_IPV6 as c_int => {
|
||||
let metadata = [SocketCall::GetSockOpt as u64, option_name as u64];
|
||||
let payload =
|
||||
unsafe { slice::from_raw_parts_mut(option_value as *mut u8, option_len) };
|
||||
let call_flags = CallFlags::empty();
|
||||
unsafe {
|
||||
*option_len_ptr = redox_rt::sys::sys_call_ro(
|
||||
socket as usize,
|
||||
payload,
|
||||
CallFlags::empty(),
|
||||
&metadata,
|
||||
)? as socklen_t;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
|
||||
+11
-4
@@ -30,8 +30,13 @@ impl Default for CondAttr {
|
||||
}
|
||||
|
||||
pub struct Cond {
|
||||
cur: AtomicUint,
|
||||
prev: AtomicUint,
|
||||
pub cur: AtomicUint,
|
||||
pub prev: AtomicUint,
|
||||
/// Clock used for timedwait, per pthread_condattr_setclock.
|
||||
/// CLOCK_REALTIME (0) or CLOCK_MONOTONIC (1). Set once at init.
|
||||
pub clock: u8,
|
||||
/// Padding to maintain 12-byte struct size for ABI compatibility.
|
||||
pub _pad: [u8; 3],
|
||||
}
|
||||
|
||||
type Result<T, E = Errno> = core::result::Result<T, E>;
|
||||
@@ -47,6 +52,8 @@ impl Cond {
|
||||
Self {
|
||||
cur: AtomicUint::new(0),
|
||||
prev: AtomicUint::new(0),
|
||||
clock: CLOCK_REALTIME as u8,
|
||||
_pad: [0; 3],
|
||||
}
|
||||
}
|
||||
fn wake(&self, count: i32) -> Result<(), Errno> {
|
||||
@@ -86,8 +93,8 @@ impl Cond {
|
||||
self.wait_inner(mutex, Some(&relative))
|
||||
}
|
||||
pub fn timedwait(&self, mutex: &RlctMutex, timeout: ×pec) -> Result<(), Errno> {
|
||||
// TODO: The clock can be other than CLOCK_REALTIME depends on CondAttr
|
||||
self.clockwait(mutex, timeout, CLOCK_REALTIME)
|
||||
let clock_id = self.clock as clockid_t;
|
||||
self.clockwait(mutex, timeout, clock_id)
|
||||
}
|
||||
fn wait_inner(&self, mutex: &RlctMutex, timeout: Option<×pec>) -> Result<(), Errno> {
|
||||
self.wait_inner_generic(|| mutex.unlock(), || mutex.lock(), timeout)
|
||||
|
||||
@@ -19,6 +19,7 @@ pub struct RlctMutex {
|
||||
|
||||
ty: Ty,
|
||||
robust: bool,
|
||||
prioceiling_value: Cell<u8>,
|
||||
}
|
||||
|
||||
pub struct RobustMutexNode {
|
||||
@@ -42,12 +43,16 @@ impl RlctMutex {
|
||||
pub(crate) fn new(attr: &RlctMutexAttr) -> Result<Self, Errno> {
|
||||
let RlctMutexAttr {
|
||||
prioceiling,
|
||||
protocol,
|
||||
protocol: _,
|
||||
pshared: _,
|
||||
robust,
|
||||
ty,
|
||||
} = *attr;
|
||||
|
||||
if prioceiling < 0 || prioceiling > u8::MAX as c_int {
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
inner: AtomicUint::new(STATE_UNLOCKED),
|
||||
recursive_count: AtomicUint::new(0),
|
||||
@@ -65,15 +70,19 @@ impl RlctMutex {
|
||||
|
||||
_ => return Err(Errno(EINVAL)),
|
||||
},
|
||||
prioceiling_value: Cell::new(prioceiling as u8),
|
||||
})
|
||||
}
|
||||
pub fn prioceiling(&self) -> Result<c_int, Errno> {
|
||||
todo_skip!(0, "pthread_getprioceiling: not implemented");
|
||||
Ok(0)
|
||||
Ok(self.prioceiling_value.get() as c_int)
|
||||
}
|
||||
pub fn replace_prioceiling(&self, _: c_int) -> Result<c_int, Errno> {
|
||||
todo_skip!(0, "pthread_setprioceiling: not implemented");
|
||||
Ok(0)
|
||||
pub fn replace_prioceiling(&self, new_ceiling: c_int) -> Result<c_int, Errno> {
|
||||
if new_ceiling < 0 || new_ceiling > u8::MAX as c_int {
|
||||
return Err(Errno(EINVAL));
|
||||
}
|
||||
let old_ceiling = self.prioceiling_value.get();
|
||||
self.prioceiling_value.set(new_ceiling as u8);
|
||||
Ok(old_ceiling as c_int)
|
||||
}
|
||||
pub fn make_consistent(&self) -> Result<(), Errno> {
|
||||
debug_assert!(self.robust, "make_consistent called on non-robust mutex");
|
||||
|
||||
Reference in New Issue
Block a user