0.3.0: converge relibc to upstream 0.6.0 + Red Bear patches

This commit is contained in:
2026-07-06 19:13:08 +03:00
parent 1a0edd8eeb
commit 4ef7e57571
1466 changed files with 75236 additions and 13644 deletions
-6332
View File
File diff suppressed because it is too large Load Diff
-26
View File
@@ -1,26 +0,0 @@
#include <stdarg.h>
#include <sys/types_internal.h>
// TODO: Can be implemented in rust when cbindgen supports "..." syntax
int sys_open(const char* filename, int flags, mode_t mode);
int open(const char* filename, int flags, ...) {
mode_t mode = 0;
va_list ap;
va_start(ap, flags);
mode = va_arg(ap, mode_t);
va_end(ap);
return sys_open(filename, flags, mode);
}
int sys_fcntl(int fildes, int cmd, int args);
int fcntl(int fildes, int cmd, ...) {
int args = 0;
va_list ap;
va_start(ap, cmd);
args = va_arg(ap, int);
va_end(ap);
return sys_fcntl(fildes, cmd, args);
}
-13
View File
@@ -1,13 +0,0 @@
// TODO: Can be implemented in rust when cbindgen supports "..." syntax
#include <stdarg.h>
int sys_ptrace(int request, va_list ap);
int ptrace(int request, ...) {
va_list ap;
va_start(ap, request);
int ret = sys_ptrace(request, ap);
va_end(ap);
return ret;
}
-10
View File
@@ -1,10 +0,0 @@
#include <stdint.h>
void abort();
uintptr_t __stack_chk_guard = 0xd048c37519fcadfe;
__attribute__((noreturn))
void __stack_chk_fail(void) {
abort();
}
-94
View File
@@ -1,94 +0,0 @@
#include <stdarg.h>
#include <stddef.h>
typedef struct FILE FILE;
// TODO: Can be implemented in rust when cbindgen supports "..." syntax
int vasprintf(char ** strp, const char * fmt, va_list ap);
int asprintf(char ** strp, const char * fmt, ...) {
int ret;
va_list ap;
va_start(ap, fmt);
ret = vasprintf(strp, fmt, ap);
va_end(ap);
return ret;
}
int vfprintf(FILE * stream, const char * fmt, va_list ap);
int fprintf(FILE * stream, const char * fmt, ...) {
int ret;
va_list ap;
va_start(ap, fmt);
ret = vfprintf(stream, fmt, ap);
va_end(ap);
return ret;
}
int vprintf(const char * fmt, va_list ap);
int printf(const char * fmt, ...) {
int ret;
va_list ap;
va_start(ap, fmt);
ret = vprintf(fmt, ap);
va_end(ap);
return ret;
}
int vsnprintf(char * s, size_t n, const char * fmt, va_list ap);
int snprintf(char * s, size_t n, const char * fmt, ...) {
int ret;
va_list ap;
va_start(ap, fmt);
ret = vsnprintf(s, n, fmt, ap);
va_end(ap);
return ret;
}
int vsprintf(char * s, const char * fmt, va_list ap);
int sprintf(char *s, const char * fmt, ...) {
int ret;
va_list ap;
va_start(ap, fmt);
ret = vsprintf(s, fmt, ap);
va_end(ap);
return ret;
}
int vfscanf(FILE * stream, const char * fmt, va_list ap);
int fscanf(FILE * stream, const char * fmt, ...) {
int ret;
va_list ap;
va_start(ap, fmt);
ret = vfscanf(stream, fmt, ap);
va_end(ap);
return ret;
}
int vscanf(const char * fmt, va_list ap);
int scanf(const char * fmt, ...) {
int ret;
va_list ap;
va_start(ap, fmt);
ret = vscanf(fmt, ap);
va_end(ap);
return ret;
}
int vsscanf(const char * input, const char * fmt, va_list ap);
int sscanf(const char * input, const char * fmt, ...) {
int ret;
va_list ap;
va_start(ap, fmt);
ret = vsscanf(input, fmt, ap);
va_end(ap);
return ret;
}
+8
View File
@@ -3,3 +3,11 @@ double strtod(const char *nptr, char **endptr);
long double strtold(const char *nptr, char **endptr) {
return (long double)strtod(nptr, endptr);
}
double relibc_ldtod(const long double* val) {
return (double)(*val);
}
void relibc_dtold(double val, long double* out) {
*out = (long double)val;
}
-74
View File
@@ -1,74 +0,0 @@
#include <stdarg.h>
#include <stddef.h>
// TODO: Can be implemented in rust when cbindgen supports "..." syntax
int execv(const char *path, char *const *argv);
int execl(const char *path, const char* argv0, ...)
{
int argc;
va_list ap;
va_start(ap, argv0);
for (argc = 1; va_arg(ap, const char*); argc++);
va_end(ap);
{
int i;
char *argv[argc+1];
va_start(ap, argv0);
argv[0] = (char *)argv0;
for (i = 1; i < argc; i++) {
argv[i] = va_arg(ap, char *);
}
argv[i] = NULL;
va_end(ap);
return execv(path, argv);
}
}
int execve(const char *path, char *const *argv, char *const *envp);
int execle(const char *path, const char* argv0, ...)
{
int argc;
va_list ap;
va_start(ap, argv0);
for (argc = 1; va_arg(ap, const char *); argc++);
va_end(ap);
{
int i;
char *argv[argc+1];
char **envp;
va_start(ap, argv0);
argv[0] = (char *)argv0;
for (i = 1; i <= argc; i++) {
argv[i] = va_arg(ap, char *);
}
envp = va_arg(ap, char **);
va_end(ap);
return execve(path, argv, envp);
}
}
int execvp(const char *file, char *const *argv);
int execlp(const char *file, const char* argv0, ...)
{
int argc;
va_list ap;
va_start(ap, argv0);
for (argc = 1; va_arg(ap, const char*); argc++);
va_end(ap);
{
int i;
char *argv[argc+1];
va_start(ap, argv0);
argv[0] = (char *)argv0;
for (i = 1; i < argc; i++) {
argv[i] = va_arg(ap, char *);
}
argv[i] = NULL;
va_end(ap);
return execvp(file, argv);
}
}
+265 -1198
View File
File diff suppressed because it is too large Load Diff
+17 -12
View File
@@ -1,6 +1,8 @@
//! Equivalent of Rust's `Vec<T>`, but using relibc's own allocator.
use crate::{
io::{self, Write},
platform::{self, types::*, WriteByte},
platform::{self, WriteByte, types::*},
};
use core::{
cmp, fmt,
@@ -27,6 +29,7 @@ pub struct CVec<T> {
cap: usize,
}
impl<T> CVec<T> {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
ptr: NonNull::dangling(),
@@ -35,7 +38,7 @@ impl<T> CVec<T> {
}
}
fn check_bounds(i: usize) -> Result<usize, AllocError> {
if i > core::isize::MAX as usize {
if i > isize::MAX as usize {
Err(AllocError)
} else {
Ok(i)
@@ -51,7 +54,7 @@ impl<T> CVec<T> {
return Ok(Self::new());
}
let size = Self::check_mul(cap, mem::size_of::<T>())?;
let ptr = NonNull::new(unsafe { platform::alloc(size) as *mut T }).ok_or(AllocError)?;
let ptr = NonNull::new(unsafe { platform::alloc(size).cast::<T>() }).ok_or(AllocError)?;
Ok(Self { ptr, len: 0, cap })
}
unsafe fn resize(&mut self, cap: usize) -> Result<(), AllocError> {
@@ -59,21 +62,23 @@ impl<T> CVec<T> {
let ptr = if cap == 0 {
NonNull::dangling()
} else if self.cap > 0 {
NonNull::new(platform::realloc(self.ptr.as_ptr() as *mut c_void, size) as *mut T)
.ok_or(AllocError)?
NonNull::new(
unsafe { platform::realloc(self.ptr.as_ptr().cast::<c_void>(), size) }.cast::<T>(),
)
.ok_or(AllocError)?
} else {
NonNull::new((platform::alloc(size)) as *mut T).ok_or(AllocError)?
NonNull::new((unsafe { platform::alloc(size) }).cast::<T>()).ok_or(AllocError)?
};
self.ptr = ptr;
self.cap = cap;
Ok(())
}
unsafe fn drop_range(&mut self, start: usize, end: usize) {
let mut start = self.ptr.as_ptr().add(start);
let end = self.ptr.as_ptr().add(end);
let mut start = unsafe { self.ptr.as_ptr().add(start) };
let end = unsafe { self.ptr.as_ptr().add(end) };
while start < end {
ptr::drop_in_place(start);
start = start.add(1);
unsafe { ptr::drop_in_place(start) };
start = unsafe { start.add(1) };
}
}
@@ -86,7 +91,7 @@ impl<T> CVec<T> {
.ok_or(AllocError)
.and_then(Self::check_bounds)?;
if required_len > self.cap {
let new_cap = cmp::min(required_len.next_power_of_two(), core::isize::MAX as usize);
let new_cap = cmp::min(required_len.next_power_of_two(), isize::MAX as usize);
unsafe {
self.resize(new_cap)?;
}
@@ -249,7 +254,7 @@ mod tests {
}
#[test]
fn extend_from_slice() {
use core_io::Write;
use crate::io::Write;
let mut vec = CVec::new();
vec.extend_from_slice(&[1, 2, 3]).unwrap();
+4
View File
@@ -2,7 +2,11 @@
name = "crt0"
version = "0.1.0"
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
edition = "2024"
[lib]
name = "crt0"
crate-type = ["staticlib"]
[lints]
workspace = true
+94 -28
View File
@@ -1,39 +1,105 @@
//! crt0
#![no_std]
#![feature(asm)]
#![feature(linkage)]
#![feature(llvm_asm)]
#![feature(naked_functions)]
#[no_mangle]
#[naked]
pub unsafe extern "C" fn _start() {
#[cfg(target_arch = "x86_64")]
llvm_asm!("mov rdi, rsp
and rsp, 0xFFFFFFFFFFFFFFF0
call relibc_start"
:
:
:
: "intel", "volatile"
);
#[cfg(target_arch = "aarch64")]
llvm_asm!("mov x0, sp
bl relibc_start"
:
:
:
: "volatile"
);
use core::{
arch::global_asm,
ffi::{c_char, c_int},
};
#[cfg(target_arch = "aarch64")]
global_asm!(
"
.globl _start
_start:
mov x0, sp
and sp, x0, #0xfffffffffffffff0 //align sp
bl relibc_crt0
"
);
#[cfg(target_arch = "x86")]
global_asm!(
"
.globl _start
.type _start, @function
_start:
sub esp, 8
mov DWORD PTR [esp], 0x00001F80
# ldmxcsr [esp]
mov WORD PTR [esp], 0x037F
fldcw [esp]
add esp, 8
push esp
call relibc_crt0
.size _start, . - _start
"
);
#[cfg(target_arch = "x86_64")]
global_asm!(
"
.globl _start
.type _start, @function
_start:
mov rdi, rsp
and rsp, 0xFFFFFFFFFFFFFFF0
sub rsp, 8
mov DWORD PTR [rsp], 0x00001F80
ldmxcsr [rsp]
mov WORD PTR [rsp], 0x037F
fldcw [rsp]
add rsp, 8
call relibc_crt0
.size _start, . - _start
"
);
#[cfg(target_arch = "riscv64")]
global_asm!(
"
.globl _start
_start:
mv a0, sp
la t0, relibc_crt0
jalr ra, t0
"
);
#[unsafe(no_mangle)]
pub unsafe extern "C" fn relibc_crt0(sp: usize) -> ! {
// This wrapper ensures a dynamic libc.so can access a hidden main function
//TODO: common definition of types
unsafe extern "C" {
fn main(argc: isize, argv: *mut *mut c_char, envp: *mut *mut c_char) -> c_int;
fn relibc_start_v1(
sp: usize,
main: unsafe extern "C" fn(
argc: isize,
argv: *mut *mut c_char,
envp: *mut *mut c_char,
) -> c_int,
) -> !;
}
unsafe { relibc_start_v1(sp, main) }
}
#[linkage = "weak"]
#[unsafe(no_mangle)]
pub extern "C" fn relibc_panic(_pi: &::core::panic::PanicInfo) -> ! {
loop {}
}
#[panic_handler]
#[linkage = "weak"]
#[no_mangle]
pub unsafe extern "C" fn rust_begin_unwind(pi: &::core::panic::PanicInfo) -> ! {
extern "C" {
fn relibc_panic(pi: &::core::panic::PanicInfo) -> !;
}
pub unsafe fn rust_begin_unwind(pi: &::core::panic::PanicInfo) -> ! {
relibc_panic(pi)
}
+3 -5
View File
@@ -2,9 +2,7 @@
name = "crti"
version = "0.1.0"
authors = ["jD91mZM2 <me@krake.one>"]
edition = "2024"
[lib]
name = "crti"
crate-type = ["staticlib"]
[dependencies]
[lints]
workspace = true
+1 -58
View File
@@ -1,60 +1,3 @@
//! crti
#![no_std]
#![feature(global_asm)]
#![feature(linkage)]
// https://wiki.osdev.org/Creating_a_C_Library#crtbegin.o.2C_crtend.o.2C_crti.o.2C_and_crtn.o
#[cfg(target_arch = "x86_64")]
global_asm!(
r#"
.section .init
.global _init
_init:
push %rbp
movq %rsp, %rbp
// Created a new stack frame and updated the stack pointer
// Body will be filled in by gcc and ended by crtn.o
.section .fini
.global _fini
_fini:
push %rbp
movq %rsp, %rbp
// Created a new stack frame and updated the stack pointer
// Body will be filled in by gcc and ended by crtn.o
"#
);
// https://git.musl-libc.org/cgit/musl/tree/crt/aarch64/crti.s
#[cfg(target_arch = "aarch64")]
global_asm!(
r#"
.section .init
.global _init
.type _init,%function
_init:
stp x29,x30,[sp,-16]!
mov x29,sp
// stp: "stores two doublewords from the first and second argument to memory addressed by addr"
// Body will be filled in by gcc and ended by crtn.o
.section .fini
.global _fini
.type _fini,%function
_fini:
stp x29,x30,[sp,-16]!
mov x29,sp
// stp: "stores two doublewords from the first and second argument to memory addressed by addr"
// Body will be filled in by gcc and ended by crtn.o
"#
);
#[panic_handler]
#[linkage = "weak"]
#[no_mangle]
pub unsafe extern "C" fn rust_begin_unwind(pi: &::core::panic::PanicInfo) -> ! {
extern "C" {
fn relibc_panic(pi: &::core::panic::PanicInfo) -> !;
}
relibc_panic(pi)
}
// we don't support _init/_fini functions, only init/fini arrays
+3 -5
View File
@@ -2,9 +2,7 @@
name = "crtn"
version = "0.1.0"
authors = ["jD91mZM2 <me@krake.one>"]
edition = "2024"
[lib]
name = "crtn"
crate-type = ["staticlib"]
[dependencies]
[lints]
workspace = true
+1 -48
View File
@@ -1,50 +1,3 @@
//! crti
#![no_std]
#![feature(global_asm)]
#![feature(linkage)]
// https://wiki.osdev.org/Creating_a_C_Library#crtbegin.o.2C_crtend.o.2C_crti.o.2C_and_crtn.o
#[cfg(target_arch = "x86_64")]
global_asm!(
r#"
.section .init
// This happens after crti.o and gcc has inserted code
// Pop the stack frame
pop %rbp
ret
.section .fini
// This happens after crti.o and gcc has inserted code
// Pop the stack frame
pop %rbp
ret
"#
);
// https://git.musl-libc.org/cgit/musl/tree/crt/aarch64/crtn.s
#[cfg(target_arch = "aarch64")]
global_asm!(
r#"
.section .init
// This happens after crti.o and gcc has inserted code
// ldp: "loads two doublewords from memory addressed by the third argument to the first and second"
ldp x29,x30,[sp],#16
ret
.section .fini
// This happens after crti.o and gcc has inserted code
// ldp: "loads two doublewords from memory addressed by the third argument to the first and second"
ldp x29,x30,[sp],#16
ret
"#
);
#[panic_handler]
#[linkage = "weak"]
#[no_mangle]
pub unsafe extern "C" fn rust_begin_unwind(pi: &::core::panic::PanicInfo) -> ! {
extern "C" {
fn relibc_panic(pi: &::core::panic::PanicInfo) -> !;
}
relibc_panic(pi)
}
// we don't support _init/_fini functions, only init/fini arrays
+88 -13
View File
@@ -1,28 +1,103 @@
use crate::platform::types::*;
// TODO: Implement cxa_finalize and uncomment this
use crate::platform::types::{c_int, c_void};
use alloc::vec::Vec;
use core::cell::RefCell;
use spin::Mutex;
#[derive(Clone, Copy)]
struct CxaAtExitFunc {
//func: extern "C" fn(*mut c_void),
//arg: *mut c_void,
//dso: *mut c_void,
func: extern "C" fn(*mut c_void),
arg: usize,
dso: usize,
}
static mut CXA_ATEXIT_FUNCS: [Option<CxaAtExitFunc>; 32] = [None; 32];
#[derive(Clone, Copy)]
struct CxaThreadAtExitFunc {
func: extern "C" fn(*mut c_void),
obj: *mut c_void,
dso: *mut c_void,
}
#[no_mangle]
static CXA_ATEXIT_FUNCS: Mutex<Vec<Option<CxaAtExitFunc>>> = Mutex::new(Vec::new());
#[thread_local]
static DTORS: RefCell<Vec<CxaThreadAtExitFunc>> = RefCell::new(Vec::new());
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __cxa_atexit(
func_opt: Option<extern "C" fn(*mut c_void)>,
func: Option<extern "C" fn(*mut c_void)>,
arg: *mut c_void,
dso: *mut c_void,
) -> c_int {
for item in &mut CXA_ATEXIT_FUNCS {
if item.is_none() {
*item = func_opt.map(|func| CxaAtExitFunc {} /*{ func, arg, dso }*/);
let Some(func) = func else {
return 0;
};
let entry = CxaAtExitFunc {
func,
arg: arg as usize,
dso: dso as usize,
};
let mut funcs = CXA_ATEXIT_FUNCS.lock();
for slot in funcs.iter_mut() {
if slot.is_none() {
*slot = Some(entry);
return 0;
}
}
-1
// No empty slots
funcs.push(Some(entry));
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __cxa_finalize(dso: *mut c_void) {
let mut funcs = CXA_ATEXIT_FUNCS.lock();
let dso_usize = dso as usize;
for slot in funcs.iter_mut().rev() {
if let Some(entry) = slot.as_ref()
&& (dso.is_null() || entry.dso == dso_usize)
&& let Some(entry_to_run) = slot.take()
{
(entry_to_run.func)(entry_to_run.arg as *mut c_void);
}
}
// clean up remaining list
if dso.is_null() {
funcs.clear();
} else {
funcs.retain(|opt| opt.is_some());
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __cxa_thread_atexit_impl(
func: extern "C" fn(*mut c_void),
obj: *mut c_void,
dso: *mut c_void,
) {
let entry = CxaThreadAtExitFunc { func, obj, dso };
DTORS.borrow_mut().push(entry);
}
// called internally
pub unsafe fn __cxa_thread_finalize() {
let mut dtors = DTORS.borrow_mut();
while let Some(entry) = dtors.pop() {
(entry.func)(entry.obj);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn _ITM_deregisterTMCloneTable(_ptr: *mut c_void) {
// No-op
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn _ITM_registerTMCloneTable(_ptr: *mut c_void, _len: usize) {
// No-op
}
+1 -1
View File
@@ -44,7 +44,7 @@ impl<R: BufRead> Db<R> {
pub type FileDb = Db<BufReader<File>>;
impl FileDb {
pub fn open(path: &CStr, separator: Separator) -> io::Result<Self> {
pub fn open(path: CStr, separator: Separator) -> io::Result<Self> {
let file = File::open(path, fcntl::O_RDONLY | fcntl::O_CLOEXEC)?;
Ok(Db::new(BufReader::new(file), separator))
}
+90
View File
@@ -0,0 +1,90 @@
use alloc::boxed::Box;
use crate::{header::errno::STR_ERROR, platform::types::c_int};
/// Positive error codes (EINVAL, not -EINVAL).
#[derive(Debug, Eq, PartialEq)]
// TODO: Move to a more generic place.
pub struct Errno(pub c_int);
impl Errno {
pub fn sync(self) -> Self {
crate::platform::ERRNO.set(self.0);
self
}
}
pub type Result<T, E = Errno> = core::result::Result<T, E>;
#[cfg(target_os = "redox")]
impl From<syscall::Error> for Errno {
#[inline]
fn from(value: syscall::Error) -> Self {
Errno(value.errno)
}
}
#[cfg(target_os = "redox")]
impl From<Errno> for syscall::Error {
#[inline]
fn from(value: Errno) -> Self {
syscall::Error::new(value.0)
}
}
impl From<Errno> for crate::io::Error {
#[inline]
fn from(Errno(errno): Errno) -> Self {
Self::from_raw_os_error(errno)
}
}
// TODO: core::error::Error
impl core::fmt::Display for Errno {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match usize::try_from(self.0).ok().and_then(|i| STR_ERROR.get(i)) {
Some(desc) => write!(f, "{desc}"),
None => write!(f, "unknown error ({})", self.0),
}
}
}
pub trait ResultExt<T> {
fn or_minus_one_errno(self) -> T;
}
impl<T: From<i8>> ResultExt<T> for Result<T, Errno> {
fn or_minus_one_errno(self) -> T {
match self {
Self::Ok(v) => v,
Self::Err(Errno(errno)) => {
crate::platform::ERRNO.set(errno);
T::from(-1)
}
}
}
}
pub trait ResultExtPtrMut<T> {
fn or_errno_null_mut(self) -> *mut T;
}
impl<T> ResultExtPtrMut<T> for Result<*mut T, Errno> {
fn or_errno_null_mut(self) -> *mut T {
match self {
Self::Ok(ptr) => ptr,
Self::Err(Errno(errno)) => {
crate::platform::ERRNO.set(errno);
core::ptr::null_mut()
}
}
}
}
impl<T> ResultExtPtrMut<T> for Result<Box<T>, Errno> {
fn or_errno_null_mut(self) -> *mut T {
match self {
Self::Ok(ptr) => Box::into_raw(ptr),
Self::Err(Errno(errno)) => {
crate::platform::ERRNO.set(errno);
core::ptr::null_mut()
}
}
}
}
+41 -31
View File
@@ -1,11 +1,14 @@
use crate::{
c_str::CStr,
error::{Errno, ResultExt},
header::{
fcntl::O_CREAT,
sys_stat::stat,
unistd::{SEEK_CUR, SEEK_END, SEEK_SET},
},
io,
platform::{types::*, Pal, Sys},
out::Out,
platform::{Pal, Sys, types::*},
};
use core::ops::Deref;
@@ -24,39 +27,46 @@ impl File {
}
}
pub fn open(path: &CStr, oflag: c_int) -> io::Result<Self> {
match Sys::open(path, oflag, 0) {
-1 => Err(io::last_os_error()),
ok => Ok(Self::new(ok)),
}
pub fn open(path: CStr, oflag: c_int) -> Result<Self, Errno> {
Sys::open(path, oflag, 0)
.map(Self::new)
.map_err(Errno::sync)
}
pub fn create(path: &CStr, oflag: c_int, mode: mode_t) -> io::Result<Self> {
match Sys::open(path, oflag | O_CREAT, mode) {
-1 => Err(io::last_os_error()),
ok => Ok(Self::new(ok)),
}
pub fn openat(dirfd: c_int, path: CStr, oflag: c_int) -> Result<Self, Errno> {
Sys::openat(dirfd, path, oflag, 0)
.map(Self::new)
.map_err(Errno::sync)
}
pub fn sync_all(&self) -> io::Result<()> {
match Sys::fsync(self.fd) {
-1 => Err(io::last_os_error()),
_ok => Ok(()),
}
pub fn create(path: CStr, oflag: c_int, mode: mode_t) -> Result<Self, Errno> {
Sys::open(path, oflag | O_CREAT, mode)
.map(Self::new)
.map_err(Errno::sync)
}
pub fn set_len(&self, size: u64) -> io::Result<()> {
match Sys::ftruncate(self.fd, size as off_t) {
-1 => Err(io::last_os_error()),
_ok => Ok(()),
}
pub fn createat(dirfd: c_int, path: CStr, oflag: c_int, mode: mode_t) -> Result<Self, Errno> {
Sys::openat(dirfd, path, oflag | O_CREAT, mode)
.map(Self::new)
.map_err(Errno::sync)
}
pub fn sync_all(&self) -> Result<(), Errno> {
Sys::fsync(self.fd).map_err(Errno::sync)
}
pub fn set_len(&self, size: u64) -> Result<(), Errno> {
Sys::ftruncate(self.fd, size as off_t).map_err(Errno::sync)
}
pub fn fstat(&self) -> Result<stat, Errno> {
let mut file_st = stat::default();
Sys::fstat(self.fd, Out::from_mut(&mut file_st)).map_err(Errno::sync)?;
Ok(file_st)
}
pub fn try_clone(&self) -> io::Result<Self> {
match Sys::dup(self.fd) {
-1 => Err(io::last_os_error()),
ok => Ok(Self::new(ok)),
}
Ok(Self::new(Sys::dup(self.fd)?))
}
/// Create a new file pointing to the same underlying descriptor. This file
@@ -72,7 +82,7 @@ impl File {
impl io::Read for &File {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match Sys::read(self.fd, buf) {
match Sys::read(self.fd, buf).map(|read| read as ssize_t).or_minus_one_errno() /* TODO */ {
-1 => Err(io::last_os_error()),
ok => Ok(ok as usize),
}
@@ -81,7 +91,10 @@ impl io::Read for &File {
impl io::Write for &File {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
match Sys::write(self.fd, buf) {
match Sys::write(self.fd, buf)
.map(|read| read as ssize_t)
.or_minus_one_errno()
{
-1 => Err(io::last_os_error()),
ok => Ok(ok as usize),
}
@@ -100,10 +113,7 @@ impl io::Seek for &File {
io::SeekFrom::End(end) => (end as off_t, SEEK_END),
};
match Sys::lseek(self.fd, offset, whence) {
-1 => Err(io::last_os_error()),
ok => Ok(ok as u64),
}
Ok(Sys::lseek(self.fd, offset, whence)? as u64)
}
}
+23 -10
View File
@@ -1,8 +1,13 @@
//! `aio.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/aio.h.html>.
use crate::{
header::time::{sigevent, timespec},
platform::types::*,
header::{bits_timespec::timespec, signal::sigevent},
platform::types::{c_int, c_void},
};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/aio.h.html>.
pub struct aiocb {
pub aio_fildes: c_int,
pub aio_lio_opcode: c_int,
@@ -12,17 +17,20 @@ pub struct aiocb {
pub aio_sigevent: sigevent,
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_read.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_read(aiocbp: *mut aiocb) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_write.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_write(aiocbp: *mut aiocb) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/lio_listio.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn lio_listio(
mode: c_int,
list: *const *const aiocb,
@@ -32,22 +40,26 @@ pub extern "C" fn lio_listio(
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_error.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_error(aiocbp: *const aiocb) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_return.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_return(aiocbp: *mut aiocb) -> usize {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_cancel.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_cancel(fildes: c_int, aiocbp: *mut aiocb) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_suspend.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_suspend(
list: *const *const aiocb,
nent: c_int,
@@ -56,7 +68,8 @@ pub extern "C" fn aio_suspend(
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/aio_fsync.html>.
// #[unsafe(no_mangle)]
pub extern "C" fn aio_fsync(operation: c_int, aiocbp: *mut aiocb) -> c_int {
unimplemented!();
}
+31 -15
View File
@@ -1,69 +1,85 @@
//! fenv.h implementation for Redox, following
//! http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/fenv.h.html
//! `fenv.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
use crate::platform::types::*;
use crate::platform::types::c_int;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
pub const FE_ALL_EXCEPT: c_int = 0;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
pub const FE_TONEAREST: c_int = 0;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
pub type fexcept_t = u64;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fenv.h.html>.
#[repr(C)]
pub struct fenv_t {
pub cw: u64,
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feclearexcept.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn feclearexcept(excepts: c_int) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub unsafe extern "C" fn fegenenv(envp: *mut fenv_t) -> c_int {
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetenv.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fegetenv(envp: *mut fenv_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetexceptflag.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fegetexceptflag(flagp: *mut fexcept_t, excepts: c_int) -> c_int {
unimplemented!();
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetround.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fegetround() -> c_int {
FE_TONEAREST
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feholdexcept.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn feholdexcept(envp: *mut fenv_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feraiseexcept.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn feraiseexcept(except: c_int) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fesetenv.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fesetenv(envp: *const fenv_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fesetexceptflag.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fesetexceptflag(flagp: *const fexcept_t, excepts: c_int) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fegetround.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fesetround(round: c_int) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fetestexcept.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn fetestexcept(excepts: c_int) -> c_int {
unimplemented!();
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/feupdateenv.html>.
// #[unsafe(no_mangle)]
pub unsafe extern "C" fn feupdateenv(envp: *const fenv_t) -> c_int {
unimplemented!();
}
+5
View File
@@ -0,0 +1,5 @@
include_guard = "_RELIBC_PATHS_H"
language = "C"
style = "Type"
no_includes = true
cpp_compat = true
+29
View File
@@ -0,0 +1,29 @@
//! Linux specific constants for paths.h.
//! Entirely borrowed from musl as there isn't a list of what to include.
pub const _PATH_DEFPATH: &str = "/usr/local/bin:/bin:/usr/bin";
pub const _PATH_STDPATH: &str = "/bin:/usr/bin:/sbin:/usr/sbin";
pub const _PATH_BSHELL: &str = "/bin/sh";
pub const _PATH_CONSOLE: &str = "/dev/console";
pub const _PATH_DEVNULL: &str = "/dev/null";
pub const _PATH_KLOG: &str = "/proc/kmsg";
pub const _PATH_LASTLOG: &str = "/var/log/lastlog";
pub const _PATH_MAILDIR: &str = "/var/mail";
pub const _PATH_MAN: &str = "/usr/share/man";
pub const _PATH_MNTTAB: &str = "/etc/fstab";
pub const _PATH_NOLOGIN: &str = "/etc/nologin";
pub const _PATH_SENDMAIL: &str = "/usr/sbin/sendmail";
pub const _PATH_SHADOW: &str = "/etc/shadow";
pub const _PATH_SHELLS: &str = "/etc/shells";
pub const _PATH_TTY: &str = "/dev/tty";
pub const _PATH_UTMP: &str = "/var/run/utmp";
pub const _PATH_WTMP: &str = "/var/log/wtmp";
pub const _PATH_VI: &str = "/usr/bin/vi";
// Trailing backslash intentional as these are dir paths.
pub const _PATH_DEV: &str = "/dev/";
pub const _PATH_TMP: &str = "/tmp/";
pub const _PATH_VARDB: &str = "/var/lib/misc/";
pub const _PATH_VARRUN: &str = "/var/run/";
pub const _PATH_VARTMP: &str = "/var/tmp/";
+11
View File
@@ -0,0 +1,11 @@
//! Implementation specific, non-standard path aliases
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
mod sys;
#[cfg(target_os = "redox")]
#[path = "redox.rs"]
mod sys;
pub use sys::*;
+13
View File
@@ -0,0 +1,13 @@
//! Redox specific constants for paths.h.
// NOTE: Cross check that new entries correspond to files on Redox before adding.
pub const _PATH_BSHELL: &str = "/bin/sh";
pub const _PATH_DEVNULL: &str = "/dev/null";
pub const _PATH_MAN: &str = "/usr/share/man";
pub const _PATH_TTY: &str = "/dev/tty";
// Trailing backslash intentional as these are dir paths.
pub const _PATH_DEV: &str = "/dev/";
pub const _PATH_TMP: &str = "/tmp/";
pub const _PATH_VARTMP: &str = "/var/tmp/";
+1 -1
View File
@@ -3,7 +3,7 @@
use platform::types::*;
/*
#[no_mangle]
#[unsafe(no_mangle)]
pub extern "C" fn func(args) -> c_int {
unimplemented!();
}
-78
View File
@@ -1,78 +0,0 @@
//! wctype implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/wctype.h.html
use crate::platform::types::*;
// #[no_mangle]
pub extern "C" fn iswalnum(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswalpha(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswcntrl(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswdigit(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswgraph(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswlower(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswprint(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswpunct(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswspace(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswupper(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswxdigit(wc: wint_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn iswctype(wc: wint_t, charclass: wctype_t) -> c_int {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn towlower(wc: wint_t) -> wint_t {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn towupper(wc: wint_t) -> wint_t {
unimplemented!();
}
// #[no_mangle]
pub extern "C" fn wctype(property: *const c_char) -> c_int {
unimplemented!();
}
+4 -4
View File
@@ -1,4 +1,4 @@
use crate::platform::types::*;
use crate::platform::types::{c_double, c_uint, c_ulong, c_ulonglong};
#[repr(C)]
pub struct user_regs_struct {
@@ -16,11 +16,11 @@ pub struct user_fpsimd_struct {
}
pub type elf_greg_t = c_ulong;
pub type elf_gregset_t = [c_ulong; 34];
pub type elf_gregset_t = *mut [c_ulong; 34];
pub type elf_fpregset_t = user_fpsimd_struct;
#[no_mangle]
pub extern "C" fn _cbindgen_only_generates_structs_if_they_are_mentioned_which_is_dumb_aarch64_user(
#[unsafe(no_mangle)]
pub extern "C" fn _cbindgen_export_aarch64_user(
a: user_regs_struct,
b: user_fpsimd_struct,
c: elf_gregset_t,
@@ -0,0 +1,7 @@
sys_includes = []
include_guard = "_RISCV64_USER_H"
language = "C"
style = "Tag"
[enum]
prefix_with_name = true
+39
View File
@@ -0,0 +1,39 @@
use crate::platform::types::{c_double, c_float, c_uint, c_ulong};
#[repr(C)]
pub struct user_regs_struct {
pub regs: [c_ulong; 31], // x1-x31
pub pc: c_ulong,
}
#[repr(C)]
pub struct user_fpregs_f_struct {
pub fpregs: [c_float; 32],
pub fcsr: c_uint,
}
#[repr(C)]
pub struct user_fpregs_g_struct {
pub fpregs: [c_double; 32],
pub fcsr: c_uint,
}
#[repr(C)]
pub struct user_fpregs_struct {
pub f_regs: user_fpregs_f_struct,
pub g_regs: user_fpregs_g_struct,
}
pub type elf_greg_t = c_ulong;
pub type elf_gregset_t = user_regs_struct;
pub type elf_fpregset_t = user_fpregs_struct;
#[unsafe(no_mangle)]
pub extern "C" fn _cbindgen_only_generates_structs_if_they_are_mentioned_which_is_dumb_riscv64_user(
a: user_regs_struct,
b: user_fpregs_struct,
c: elf_gregset_t,
d: elf_greg_t,
e: elf_fpregset_t,
) {
}
+4 -4
View File
@@ -1,6 +1,6 @@
//! A part of the ptrace compatibility for Redox OS
use crate::platform::types::*;
use crate::platform::types::{c_char, c_int, c_long, c_ulong};
#[repr(C)]
pub struct user_fpregs_struct {
@@ -50,7 +50,7 @@ pub struct user_regs_struct {
pub type elf_greg_t = c_ulong;
pub type elf_gregset_t = [c_ulong; 27];
pub type elf_gregset_t = *mut [c_ulong; 27];
pub type elf_fpregset_t = user_fpregs_struct;
#[repr(C)]
pub struct user {
@@ -71,8 +71,8 @@ pub struct user {
pub u_debugreg: [c_ulong; 8],
}
#[no_mangle]
pub extern "C" fn _cbindgen_only_generates_structs_if_they_are_mentioned_which_is_dumb_x86_user(
#[unsafe(no_mangle)]
pub extern "C" fn _cbindgen_export_x86_user(
a: user_fpregs_struct,
b: user_regs_struct,
c: user,
+20 -1
View File
@@ -1,5 +1,24 @@
sys_includes = ["stddef.h", "sys/socket.h", "netinet/in.h"]
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/arpa_inet.h.html
#
# Spec quotations relating to includes:
# - "The <arpa/inet.h> header shall define the in_port_t and in_addr_t types as described in <netinet/in.h> and the socklen_t type as defined in <sys/socket.h>."
# - "The <arpa/inet.h> header shall define the in_addr structure as described in <netinet/in.h>."
# - "The <arpa/inet.h> header shall define the INET_ADDRSTRLEN and INET6_ADDRSTRLEN macros as described in <netinet/in.h>."
# - "The <arpa/inet.h> header shall define the uint32_t and uint16_t types as described in <inttypes.h>."
# - "Inclusion of the <arpa/inet.h> header may also make visible all symbols from <netinet/in.h> and <inttypes.h>."
#
# Possible cycle between arpa/inet.h and netinet/in.h solved by:
# - including netinet/in.h in arpa/inet.h
# - splitting out functions (htonl, htons, ntohl, ntohs) into bits/arpainet.h
#
# netinet/in.h brings in sys/types.h which brings in features.h (needed for #[deprecated] annotation)
# bits/arpainet.h brings in inttypes.h
sys_includes = ["netinet/in.h"]
include_guard = "_ARPA_INET_H"
after_includes = """
#include <bits/arpainet.h> // for htonl, htons, ntohl, ntohs
#include <bits/socklen-t.h> // for socklen_t
"""
language = "C"
style = "Tag"
no_includes = true
+407 -118
View File
@@ -1,5 +1,8 @@
//! arpa/inet implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xns/arpainet.h.html
//! `arpa/inet.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/arpa_inet.h.html>.
use alloc::{string::String, vec::Vec};
use core::{
ptr, slice,
str::{self, FromStr},
@@ -8,151 +11,437 @@ use core::{
use crate::{
c_str::CStr,
header::{
errno::*,
netinet_in::{in_addr, in_addr_t, INADDR_NONE},
sys_socket::{constants::*, socklen_t},
bits_arpainet::ntohl,
bits_socklen_t::socklen_t,
errno::{EAFNOSUPPORT, ENOSPC},
netinet_in::{INADDR_NONE, INET6_ADDRSTRLEN, in6_addr, in_addr, in_addr_t},
sys_socket::constants::{AF_INET, AF_INET6},
},
platform::{self, types::*},
platform::{
self,
types::{c_char, c_int, c_void},
},
raw_cell::RawCell,
};
#[no_mangle]
pub extern "C" fn htonl(hostlong: uint32_t) -> uint32_t {
hostlong.to_be()
}
#[no_mangle]
pub extern "C" fn htons(hostshort: uint16_t) -> uint16_t {
hostshort.to_be()
}
#[no_mangle]
pub extern "C" fn ntohl(netlong: uint32_t) -> uint32_t {
u32::from_be(netlong)
}
#[no_mangle]
pub extern "C" fn ntohs(netshort: uint16_t) -> uint16_t {
u16::from_be(netshort)
}
#[no_mangle]
pub unsafe extern "C" fn inet_aton(cp: *const c_char, inp: *mut in_addr) -> c_int {
// TODO: octal/hex
inet_pton(AF_INET, cp, inp as *mut c_void)
}
#[no_mangle]
pub unsafe extern "C" fn inet_ntoa(addr: in_addr) -> *const c_char {
static mut NTOA_ADDR: [c_char; 16] = [0; 16];
inet_ntop(
AF_INET,
&addr as *const in_addr as *const c_void,
NTOA_ADDR.as_mut_ptr(),
16,
)
}
#[no_mangle]
pub unsafe extern "C" fn inet_pton(domain: c_int, src: *const c_char, dest: *mut c_void) -> c_int {
if domain != AF_INET {
platform::errno = EAFNOSUPPORT;
-1
} else {
let s_addr = slice::from_raw_parts_mut(
&mut (*(dest as *mut in_addr)).s_addr as *mut _ as *mut u8,
4,
);
let src_cstr = CStr::from_ptr(src);
let mut octets = str::from_utf8_unchecked(src_cstr.to_bytes()).split('.');
for i in 0..4 {
if let Some(n) = octets.next().and_then(|x| u8::from_str(x).ok()) {
s_addr[i] = n;
} else {
return 0;
}
}
if octets.next() == None {
1 // Success
} else {
0
}
}
}
#[no_mangle]
pub unsafe extern "C" fn inet_ntop(
domain: c_int,
src: *const c_void,
dest: *mut c_char,
size: socklen_t,
) -> *const c_char {
if domain != AF_INET {
platform::errno = EAFNOSUPPORT;
ptr::null()
} else if size < 16 {
platform::errno = ENOSPC;
ptr::null()
} else {
let s_addr = slice::from_raw_parts(
&(*(src as *const in_addr)).s_addr as *const _ as *const u8,
4,
);
let addr = format!("{}.{}.{}.{}\0", s_addr[0], s_addr[1], s_addr[2], s_addr[3]);
ptr::copy(addr.as_ptr() as *const c_char, dest, addr.len());
dest
}
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_addr.html>.
///
/// # Deprecated
/// The `inet_addr()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 8.
#[deprecated]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn inet_addr(cp: *const c_char) -> in_addr_t {
let mut val: in_addr = in_addr { s_addr: 0 };
if inet_aton(cp, &mut val) > 0 {
if unsafe { inet_aton(cp, &raw mut val) } > 0 {
val.s_addr
} else {
INADDR_NONE
}
}
#[no_mangle]
pub extern "C" fn inet_lnaof(input: in_addr) -> in_addr_t {
if input.s_addr >> 24 < 128 {
input.s_addr & 0xff_ffff
} else if input.s_addr >> 24 < 192 {
input.s_addr & 0xffff
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/inet_aton.3.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn inet_aton(cp: *const c_char, inp: *mut in_addr) -> c_int {
let cp_cstr = unsafe { CStr::from_ptr(cp) };
let mut four_parts_decimal_only = false;
if cp_cstr.contains(b'.') {
// 2, 3 or 4 part address
let parts = unsafe { str::from_utf8_unchecked(cp_cstr.to_bytes()).split('.') };
let mut count = 0;
for part in parts {
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"),
}
} else {
count += 1;
}
}
if count == 4 {
four_parts_decimal_only = true;
}
} 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");
unsafe {
(*inp.cast::<in_addr>()).s_addr = in_addr_t::from_ne_bytes(s_addr_bytes);
}
return 1; // successful
}
if four_parts_decimal_only {
unsafe { inet_pton(AF_INET, cp, inp.cast::<c_void>()) }
} else {
input.s_addr & 0xff
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
}
}
#[no_mangle]
pub extern "C" fn inet_makeaddr(net: in_addr_t, host: in_addr_t) -> in_addr {
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_lnaof.html>.
///
/// # Deprecation
/// The `inet_lnaof()` function was specified in Networking Services Issue 5,
/// but not in the Open Group Base Specifications Issue 6 and later.
#[deprecated]
#[unsafe(no_mangle)]
pub extern "C" fn inet_lnaof(r#in: in_addr) -> in_addr_t {
if r#in.s_addr >> 24 < 128 {
r#in.s_addr & 0xff_ffff
} else if r#in.s_addr >> 24 < 192 {
r#in.s_addr & 0xffff
} else {
r#in.s_addr & 0xff
}
}
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_makeaddr.html>.
///
/// # Deprecation
/// The `inet_makeaddr()` function was specified in Networking Services Issue
/// 5, but not in the Open Group Base Specifications Issue 6 and later.
#[deprecated]
#[unsafe(no_mangle)]
pub extern "C" fn inet_makeaddr(net: in_addr_t, lna: in_addr_t) -> in_addr {
let mut output: in_addr = in_addr { s_addr: 0 };
if net < 256 {
output.s_addr = host | net << 24;
output.s_addr = lna | net << 24;
} else if net < 65536 {
output.s_addr = host | net << 16;
output.s_addr = lna | net << 16;
} else {
output.s_addr = host | net << 8;
output.s_addr = lna | net << 8;
}
output
}
#[no_mangle]
pub extern "C" fn inet_netof(input: in_addr) -> in_addr_t {
if input.s_addr >> 24 < 128 {
input.s_addr & 0xff_ffff
} else if input.s_addr >> 24 < 192 {
input.s_addr & 0xffff
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_netof.html>.
///
/// # Deprecation
/// The `inet_netof()` function was specified in Networking Services Issue 5,
/// but not in the Open Group Base Specifications Issue 6 and later.
#[deprecated]
#[unsafe(no_mangle)]
pub extern "C" fn inet_netof(r#in: in_addr) -> in_addr_t {
if r#in.s_addr >> 24 < 128 {
r#in.s_addr & 0xff_ffff
} else if r#in.s_addr >> 24 < 192 {
r#in.s_addr & 0xffff
} else {
input.s_addr & 0xff
r#in.s_addr & 0xff
}
}
#[no_mangle]
pub unsafe extern "C" fn inet_network(cp: *mut c_char) -> in_addr_t {
ntohl(inet_addr(cp))
/// See <https://pubs.opengroup.org/onlinepubs/7908799/xns/inet_network.html>.
///
/// # Deprecation
/// The `inet_network()` function was specified in Networking Services Issue 5,
/// but not in the Open Group Base Specifications Issue 6 and later.
#[deprecated]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn inet_network(cp: *const c_char) -> in_addr_t {
ntohl(unsafe {
#[allow(deprecated)]
inet_addr(cp)
})
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_addr.html>.
///
/// # Deprecation
/// The `inet_ntoa()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 8.
#[deprecated]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn inet_ntoa(r#in: in_addr) -> *mut c_char {
static NTOA_ADDR: RawCell<[c_char; 16]> = RawCell::new([0; 16]);
unsafe {
let ptr = inet_ntop(
AF_INET,
ptr::from_ref::<in_addr>(&r#in).cast::<c_void>(),
NTOA_ADDR.unsafe_mut().as_mut_ptr(),
NTOA_ADDR.unsafe_ref().len() as socklen_t,
);
// Mutable pointer is required, inet_ntop returns destination as const pointer
ptr.cast_mut()
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_ntop.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn inet_ntop(
af: c_int,
src: *const c_void,
dst: *mut c_char,
size: socklen_t,
) -> *const c_char {
if af == AF_INET6 {
if size < INET6_ADDRSTRLEN as socklen_t {
platform::ERRNO.set(ENOSPC);
return ptr::null();
}
let s6_addr = unsafe { &(*(src.cast::<in6_addr>())).s6_addr };
let output = inet_ntop6(s6_addr);
let bytes = output.as_bytes();
unsafe {
ptr::copy(bytes.as_ptr().cast::<c_char>(), dst, bytes.len());
*dst.add(bytes.len()) = 0;
}
dst
} else if af == AF_INET {
if size < 16 {
platform::ERRNO.set(ENOSPC);
ptr::null()
} else {
let s_addr = unsafe {
slice::from_raw_parts(
ptr::from_ref(&(*(src.cast::<in_addr>())).s_addr).cast::<u8>(),
4,
)
};
let addr = format!("{}.{}.{}.{}\0", s_addr[0], s_addr[1], s_addr[2], s_addr[3]);
unsafe {
ptr::copy(addr.as_ptr().cast::<c_char>(), dst, addr.len());
}
dst
}
} else {
platform::ERRNO.set(EAFNOSUPPORT);
ptr::null()
}
}
fn inet_ntop6(addr: &[u8; 16]) -> String {
let groups: [u16; 8] = core::array::from_fn(|i| {
u16::from_be_bytes([addr[i * 2], addr[i * 2 + 1]])
});
let mut best_start = 8usize;
let mut best_len = 1usize;
let mut cur_start = 8usize;
let mut cur_len = 0usize;
for i in 0..8 {
if groups[i] == 0 {
if cur_len == 0 {
cur_start = i;
}
cur_len += 1;
} else {
if cur_len > best_len {
best_start = cur_start;
best_len = cur_len;
}
cur_len = 0;
}
}
if cur_len > best_len {
best_start = cur_start;
best_len = cur_len;
}
let mut parts = Vec::new();
let mut i = 0usize;
while i < 8 {
if i == best_start && best_len > 1 {
if i == 0 {
parts.push(String::new());
}
if i + best_len == 8 {
parts.push(String::new());
}
i += best_len;
} else {
parts.push(format!("{:x}", groups[i]));
i += 1;
}
}
if best_len == 8 {
return String::from("::");
}
parts.join(":")
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/inet_ntop.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn inet_pton(af: c_int, src: *const c_char, dst: *mut c_void) -> c_int {
if af == AF_INET6 {
let src_cstr = unsafe { CStr::from_ptr(src) };
let src_str = match src_cstr.to_str() {
Ok(s) => s,
Err(_) => return 0,
};
let out = unsafe { &mut *(dst.cast::<in6_addr>()) };
if inet_pton6(src_str, &mut out.s6_addr) {
1
} else {
0
}
} else if af == AF_INET {
let s_addr = unsafe {
slice::from_raw_parts_mut(
ptr::from_mut(&mut (*dst.cast::<in_addr>()).s_addr).cast::<u8>(),
4,
)
};
let src_cstr = unsafe { CStr::from_ptr(src) };
let mut octets = unsafe { str::from_utf8_unchecked(src_cstr.to_bytes()).split('.') };
for part in s_addr.iter_mut().take(4) {
if let Some(n) = octets
.next()
.filter(|x| !x.len() > 3)
.and_then(|x| u8::from_str(x).ok())
{
*part = n;
} else {
return 0;
}
}
if octets.next().is_none() {
1 // Success
} else {
0
}
} else {
platform::ERRNO.set(EAFNOSUPPORT);
-1
}
}
fn inet_pton6(src: &str, dst: &mut [u8; 16]) -> bool {
dst.fill(0);
let double_colon_pos = src.find("::");
let second_double = if let Some(pos) = double_colon_pos {
src[pos + 2..].find("::").map(|p| p + pos + 2)
} else {
None
};
if second_double.is_some() {
return false;
}
let (left_str, right_str) = match double_colon_pos {
Some(pos) => (&src[..pos], &src[pos + 2..]),
None => (src, ""),
};
let left_groups: Vec<&str> = if left_str.is_empty() {
Vec::new()
} else {
left_str.split(':').collect()
};
let right_groups: Vec<&str> = if right_str.is_empty() {
Vec::new()
} else {
right_str.split(':').collect()
};
let right_has_ipv4 = right_groups.last().is_some_and(|g| g.contains('.'));
let mut left_count = left_groups.len();
let mut right_count = right_groups.len();
if right_has_ipv4 {
right_count -= 1;
left_count += 1;
}
let gap = 8 - left_count - right_count;
if double_colon_pos.is_none() && gap != 0 {
return false;
}
if double_colon_pos.is_some() && gap < 0 {
return false;
}
if double_colon_pos.is_none() && left_groups.len() + right_groups.len() != 8 {
return false;
}
let mut idx = 0usize;
for group in &left_groups {
if idx >= 16 {
return false;
}
let val = match parse_hex_group(group) {
Some(v) => v,
None => return false,
};
dst[idx] = (val >> 8) as u8;
dst[idx + 1] = val as u8;
idx += 2;
}
if double_colon_pos.is_some() {
for _ in 0..gap {
if idx >= 16 {
return false;
}
dst[idx] = 0;
dst[idx + 1] = 0;
idx += 2;
}
}
let right_hex_count = if right_has_ipv4 {
right_groups.len().saturating_sub(1)
} else {
right_groups.len()
};
for group in &right_groups[..right_hex_count] {
if idx >= 16 {
return false;
}
let val = match parse_hex_group(group) {
Some(v) => v,
None => return false,
};
dst[idx] = (val >> 8) as u8;
dst[idx + 1] = val as u8;
idx += 2;
}
if right_has_ipv4 {
if idx != 12 {
return false;
}
let ipv4_str = right_groups[right_groups.len() - 1];
let parts: Vec<&str> = ipv4_str.split('.').collect();
if parts.len() != 4 {
return false;
}
for (i, part) in parts.iter().enumerate() {
match u8::from_str(part) {
Ok(v) => dst[12 + i] = v,
Err(_) => return false,
}
}
idx += 4;
}
idx == 16
}
fn parse_hex_group(s: &str) -> Option<u16> {
if s.is_empty() || s.len() > 4 {
return None;
}
let mut val: u16 = 0;
for c in s.chars() {
val = val.checked_mul(16)?;
match c.to_digit(16) {
Some(d) => val = val.checked_add(d as u16)?,
None => return None,
}
}
Some(val)
}
+20 -1
View File
@@ -1,5 +1,24 @@
sys_includes = ["bits/assert.h"]
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/assert.h.html
#
# There are no spec quotations relating to includes
#
# features.h included for Rust never type (no return)
sys_includes = ["features.h"]
include_guard = "_RELIBC_ASSERT_H"
# trailer is placed outside include_guard
trailer = """
// Do not use include guard, to ensure assert is always defined
#ifdef assert
#undef assert
#endif
#ifdef NDEBUG
# define assert(cond) (void) 0
#else
# define assert(cond) \
((void)((cond) || (__assert_fail(__func__, __FILE__, __LINE__, #cond), 0)))
#endif
"""
language = "C"
style = "Tag"
no_includes = true
+12 -19
View File
@@ -1,31 +1,24 @@
//! assert implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/assert.h.html
//! `assert.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/assert.h.html>.
use crate::{
c_str::CStr,
header::{stdio, stdlib},
platform::types::*,
platform::types::{c_char, c_int},
};
use core::fmt::Write;
#[no_mangle]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __assert_fail(
func: *const c_char,
file: *const c_char,
line: c_int,
cond: *const c_char,
) {
let func = CStr::from_ptr(func).to_str().unwrap();
let file = CStr::from_ptr(file).to_str().unwrap();
let cond = CStr::from_ptr(cond).to_str().unwrap();
) -> ! {
let func = unsafe { CStr::from_ptr(func) }.to_string_lossy();
let file = unsafe { CStr::from_ptr(file) }.to_string_lossy();
let cond = unsafe { CStr::from_ptr(cond) }.to_string_lossy();
writeln!(
*stdio::stderr,
"{}: {}:{}: Assertion `{}` failed.",
func,
file,
line,
cond
)
.unwrap();
stdlib::abort();
eprintln!("{}: {}:{}: Assertion `{}` failed.", func, file, line, cond);
unsafe { crate::header::stdlib::abort() };
}
+19
View File
@@ -0,0 +1,19 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/arpa_inet.h.html
#
# The arpa/inet header should define the following functions:
# - htonl
# - htons
# - ntohl
# - ntohs
#
# They are also meant to be available in the netinet/in header.
# The arpa/inet and netinet/in headers both say that they may include each other which creates a cycle.
# To break the cycle we include netinet/in in the arpa/inet header and split out the above functions.
#
# include inttypes.h to get uint16_t and uint32_t
sys_includes = ["inttypes.h"]
include_guard = "_RELIBC_BITS_ARPAINET_H"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
+25
View File
@@ -0,0 +1,25 @@
use crate::platform::types::{uint16_t, uint32_t};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/htonl.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htonl(hostlong: uint32_t) -> uint32_t {
hostlong.to_be()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/htonl.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htons(hostshort: uint16_t) -> uint16_t {
hostshort.to_be()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/htonl.html>.
#[unsafe(no_mangle)]
pub extern "C" fn ntohl(netlong: uint32_t) -> uint32_t {
u32::from_be(netlong)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/htonl.html>.
#[unsafe(no_mangle)]
pub extern "C" fn ntohs(netshort: uint16_t) -> uint16_t {
u16::from_be(netshort)
}
+4
View File
@@ -0,0 +1,4 @@
//! `bits/eventfd.h` — eventfd counter type.
use crate::platform::types::uint64_t;
pub type eventfd_t = uint64_t;
+18
View File
@@ -0,0 +1,18 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_uio.h.html
#
# Split out to avoid including all of sys/uio.h in sys/socket.h.
#
# POSIX headers that require iovec:
# - sys/socket.h
# - sys/uio.h (where it should be defined)
#
# sys/types.h included for c_void and size_t
sys_includes = ["sys/types.h"]
include_guard = "_RELIBC_BITS_IOVEC_H"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
+38
View File
@@ -0,0 +1,38 @@
use alloc::vec::Vec;
use core::slice;
use crate::platform::types::{c_void, size_t};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_uio.h.html>.
#[repr(C)]
#[derive(Debug, CheckVsLibcCrate)]
pub struct iovec {
pub iov_base: *mut c_void,
pub iov_len: size_t,
}
impl iovec {
unsafe fn to_slice(&self) -> &mut [u8] {
unsafe { slice::from_raw_parts_mut(self.iov_base.cast::<u8>(), self.iov_len) }
}
}
pub unsafe fn gather(iovs: &[iovec]) -> Vec<u8> {
let mut vec = Vec::new();
for iov in iovs.iter() {
vec.extend_from_slice(unsafe { iov.to_slice() });
}
vec
}
pub unsafe fn scatter(iovs: &[iovec], vec: Vec<u8>) {
let mut i = 0;
for iov in iovs.iter() {
let slice = unsafe { iov.to_slice() };
slice.copy_from_slice(&vec[i..][..slice.len()]);
i += slice.len();
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cbindgen_alias_iovec(_: iovec) {}
+29
View File
@@ -0,0 +1,29 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/locale.h.html
#
# The locale header should define this type but multiple headers make use of it.
# This type is split out from the locale header to avoid including all of locale in the other headers.
#
# POSIX headers that require locale_t:
# - ctype.h
# - langinfo.h
# - libintl.h (Redox does not have this header, it seems to be supplied externally)
# - monetary.h (TODO note exists, not currently imported)
# - string.h
# - strings.h (TODO note exists, not currently imported)
# - time.h (TODO note exists, not currently imported)
# - wchar.h (required by *_l functions, no TODO note present)
# - wctype.h (required by *_l functions, TODO note exists, not currently imported)
sys_includes = []
include_guard = "_RELIBC_BITS_LOCALE_T_H"
language = "C"
style = "type"
no_includes = true
cpp_compat = true
[export]
include = [
"locale_t"
]
[enum]
prefix_with_name = true
+1
View File
@@ -0,0 +1 @@
pub type locale_t = *mut core::ffi::c_void;
+20
View File
@@ -0,0 +1,20 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html
#
# This type is split out to prevent importing all of stddef.h into other headers.
include_guard = "_RELIBC_BITS_NULL_T_H"
language = "C"
no_includes = true
after_includes = """
// Null pointer constant.
// Any pointer object whose representation has all bits set to zero, perhaps by
// memset() to 0 or by calloc(), shall be treated as a null pointer.
#ifdef __cplusplus
#if __cplusplus >= 201103L
#define NULL nullptr
#else
#define NULL 0L
#endif
#else
#define NULL ((void *)0)
#endif
"""
+5
View File
@@ -0,0 +1,5 @@
//! `NULL` from `stddef.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html>.
//!
//! Implemented via ifdefs and defines in cbindgen.
+53
View File
@@ -0,0 +1,53 @@
sys_includes = []
include_guard = "_RELIBC_BITS_PTHREAD_H"
language = "C"
style = "type"
no_includes = true
cpp_compat = true
# TODO: Any better way to implement pthread_cleanup_push/pthread_cleanup_pop?
after_includes = """
#define PTHREAD_COND_INITIALIZER ((pthread_cond_t){0})
#define PTHREAD_MUTEX_INITIALIZER ((pthread_mutex_t){0})
#define PTHREAD_ONCE_INIT ((pthread_once_t){0})
#define PTHREAD_RWLOCK_INITIALIZER ((pthread_rwlock_t){0})
#define pthread_cleanup_push(ROUTINE, ARG) do { \\
struct { \\
void (*routine)(void *); \\
void *arg; \\
void *prev; \\
} __relibc_internal_pthread_ll_entry = { \\
.routine = (void (*)(void *))(ROUTINE), \\
.arg = (void *)(ARG), \\
}; \\
__relibc_internal_pthread_cleanup_push(&__relibc_internal_pthread_ll_entry);
#define pthread_cleanup_pop(EXECUTE) \\
__relibc_internal_pthread_cleanup_pop((EXECUTE)); \\
} while(0)
"""
[export.rename]
"AtomicInt" = "int"
"AtomicUint" = "unsigned"
[export]
include = [
"pthread_attr_t",
"pthread_rwlockattr_t",
"pthread_rwlock_t",
"pthread_barrier_t",
"pthread_barrierattr_t",
"pthread_mutex_t",
"pthread_mutexattr_t",
"pthread_condattr_t",
"pthread_cond_t",
"pthread_spinlock_t",
"pthread_once_t",
"pthread_t",
"pthread_key_t",
]
[enum]
prefix_with_name = true
+123
View File
@@ -0,0 +1,123 @@
#![allow(non_camel_case_types)]
use crate::platform::types::{c_int, c_uchar, c_ulong, c_void, size_t};
// XXX: https://github.com/eqrion/cbindgen/issues/685
//
// We need to write the opaque types ourselves, and apparently cbindgen doesn't even support
// expanding macros! Instead, we rely on checking that the lengths are correct, when these headers
// are parsed in the regular compilation phase.
/// The `pthread_attr_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_attr_t {
__relibc_internal_size: [c_uchar; 32],
__relibc_internal_align: size_t,
}
/// The `pthread_rwlockattr_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_rwlockattr_t {
__relibc_internal_size: [c_uchar; 1],
__relibc_internal_align: c_uchar,
}
/// The `pthread_rwlock_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_rwlock_t {
__relibc_internal_size: [c_uchar; 4],
__relibc_internal_align: c_int,
}
/// The `pthread_barrier_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_barrier_t {
__relibc_internal_size: [c_uchar; 24],
__relibc_internal_align: c_int,
}
/// The `pthread_barrierattr_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_barrierattr_t {
__relibc_internal_size: [c_uchar; 4],
__relibc_internal_align: c_int,
}
/// The `pthread_mutex_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_mutex_t {
__relibc_internal_size: [c_uchar; 12],
__relibc_internal_align: c_int,
}
/// The `pthread_mutexattr_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_mutexattr_t {
__relibc_internal_size: [c_uchar; 20],
__relibc_internal_align: c_int,
}
/// 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_align: c_int,
}
/// The `pthread_condattr_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_condattr_t {
__relibc_internal_size: [c_uchar; 8],
__relibc_internal_align: c_int,
}
/// The `pthread_spinlock_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_spinlock_t {
__relibc_internal_size: [c_uchar; 4],
__relibc_internal_align: c_int,
}
/// The `pthread_once_t` type provided in [`sys/types.h`](crate::header::sys_types).
#[repr(C)]
pub union pthread_once_t {
__relibc_internal_size: [c_uchar; 4],
__relibc_internal_align: c_int,
}
macro_rules! assert_equal_size(
($export:ident, $wrapped:ident) => {
const _: () = unsafe {
type Wrapped = crate::header::pthread::$wrapped;
// Fail at compile-time if sizes differ.
// TODO: Is this UB?
let export = $export { __relibc_internal_align: 0 };
let _: Wrapped = core::mem::transmute(export.__relibc_internal_size);
// Fail at compile-time if alignments differ.
let a = [0_u8; core::mem::align_of::<$export>()];
#[allow(clippy::useless_transmute)]
let b: [u8; core::mem::align_of::<Wrapped>()] = core::mem::transmute(a);
};
// TODO: Turn into a macro?
#[cfg(all(target_os = "redox", feature = "check_against_libc_crate"))]
const _: () = unsafe {
use ::__libc_only_for_layout_checks as libc;
let export = $export { __relibc_internal_align: 0 };
let _: libc::$export = core::mem::transmute(export.__relibc_internal_size);
let a = [0_u8; core::mem::align_of::<$export>()];
let b: [u8; core::mem::align_of::<libc::$export>()] = core::mem::transmute(a);
};
}
);
assert_equal_size!(pthread_attr_t, RlctAttr);
assert_equal_size!(pthread_rwlock_t, RlctRwlock);
assert_equal_size!(pthread_rwlockattr_t, RlctRwlockAttr);
assert_equal_size!(pthread_barrier_t, RlctBarrier);
assert_equal_size!(pthread_barrierattr_t, RlctBarrierAttr);
assert_equal_size!(pthread_mutex_t, RlctMutex);
assert_equal_size!(pthread_mutexattr_t, RlctMutexAttr);
assert_equal_size!(pthread_cond_t, RlctCond);
assert_equal_size!(pthread_condattr_t, RlctCondAttr);
assert_equal_size!(pthread_spinlock_t, RlctSpinlock);
assert_equal_size!(pthread_once_t, RlctOnce);
/// The `pthread_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type pthread_t = *mut c_void;
/// The `pthread_key_t` type provided in [`sys/types.h`](crate::header::sys_types).
pub type pthread_key_t = c_ulong;
+21
View File
@@ -0,0 +1,21 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_socket.h.html
#
# The sys/socket header should define this type.
# It is split out to avoid other headers from including all of sys/socket.
#
# POSIX headers that require sa_family_t:
# - netinet/in.h
# - sys/socket.h
# - sys/un.h
sys_includes = []
include_guard = "_RELIBC_BITS_SA_FAMILY_T_H"
language = "C"
style = "type"
no_includes = true
cpp_compat = true
[export]
include = ["sa_family_t"]
[enum]
prefix_with_name = true
+3
View File
@@ -0,0 +1,3 @@
use crate::platform::types::c_ushort;
pub type sa_family_t = c_ushort;
+18
View File
@@ -0,0 +1,18 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/signal.h.html
#
# This type is split out to prevent importing all of signal.h into other headers.
#
# POSIX headers that require sigset_t:
# - poll.h
# - signal.h (where it should be defined)
# - sys/select.h
sys_includes = []
include_guard = "_RELIBC_BITS_SIGSET_T_H"
language = "C"
style = "type"
no_includes = true
cpp_compat = true
[export]
include = ["sigset_t"]
[enum]
prefix_with_name = true
+4
View File
@@ -0,0 +1,4 @@
use crate::platform::types::c_ulonglong;
#[allow(non_camel_case_types)]
pub type sigset_t = c_ulonglong;
+30
View File
@@ -0,0 +1,30 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html
#
# This type is split out to prevent importing all of stddef.h into other headers.
#
# C++ compatibility: in C++, `size_t` is defined by the standard library
# in namespace `std::`. A bare `typedef unsigned long size_t;` at file
# scope in a C++ translation unit collides with `std::size_t` whenever
# a host stdlib header (e.g. `<string>`, `<bits/basic_string.h>`) is
# transitively included — the libstdc++ internal code references
# unqualified `size_t` and resolves it to the first visible one, which
# would otherwise be this typedef. The `__cplusplus` guard makes the
# typedef a no-op under C++ compilation; the libc++/libstdc++ headers
# then provide their own `std::size_t` via `__SIZE_TYPE__` or the
# standard type machinery.
include_guard = "_RELIBC_BITS_SIZE_T_H"
after_includes = """
/**
* Unsigned integer type of the result of the sizeof operator.
*/
#ifndef __cplusplus
typedef unsigned long size_t;
#endif
"""
language = "C"
no_includes = true
cpp_compat = true
[export]
include = ["size_t"]
[enum]
prefix_with_name = true
+9
View File
@@ -0,0 +1,9 @@
//! `size_t` from `stddef.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html>.
use crate::platform::types::c_ulong;
/// Unsigned integer type of the result of the sizeof operator.
#[allow(non_camel_case_types)]
pub type size_t = c_ulong;
+12
View File
@@ -0,0 +1,12 @@
sys_includes = []
include_guard = "_RELIBC_BITS_SOCKLEN_T_H"
language = "C"
style = "type"
no_includes = true
cpp_compat = true
[export]
include = [
"socklen_t"
]
[enum]
prefix_with_name = true
+1
View File
@@ -0,0 +1 @@
pub type socklen_t = u32;
+25
View File
@@ -0,0 +1,25 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/time.h.html
#
# The time header should define timespec but multiple headers make use of it.
# This type is split out from the time header to avoid including all of time in the other headers.
#
# POSIX headers that require timespec:
# - aio.h (all functions currently unimplemented but timespec imported)
# - poll.h
# - sched.h
# - semaphore.h
# - signal.h
# - sys/select.h (not currently imported, needed for pselect, no TODO)
# - sys/stat.h
# - sys/time.h
#
# include sys/types.h to get time_t for timespec
sys_includes = ["sys/types.h"]
include_guard = "_RELIBC_BITS_TIME_H"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
+64
View File
@@ -0,0 +1,64 @@
use crate::{
header::time::NANOSECONDS,
platform::types::{c_long, time_t},
};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/time.h.html>.
#[repr(C)]
#[derive(Clone, Default, Debug)]
pub struct timespec {
pub tv_sec: time_t,
pub tv_nsec: c_long,
}
impl timespec {
// TODO: Write test
/// similar logic with timeradd
#[allow(clippy::should_implement_trait)] // not to confuse std::ops::Add
pub fn add(base: timespec, interval: timespec) -> Option<timespec> {
let delta_sec = base.tv_sec.checked_add(interval.tv_sec)?;
let delta_nsec = base.tv_nsec.checked_add(interval.tv_nsec)?;
if delta_sec < 0 || delta_nsec < 0 {
return None;
}
Some(Self {
tv_sec: delta_sec + (delta_nsec / NANOSECONDS) as time_t,
tv_nsec: delta_nsec % NANOSECONDS,
})
}
/// similar logic with timersub
pub fn subtract(later: timespec, earlier: timespec) -> Option<timespec> {
let delta_sec = later.tv_sec.checked_sub(earlier.tv_sec)?;
let delta_nsec = later.tv_nsec.checked_sub(earlier.tv_nsec)?;
let time = if delta_nsec < 0 {
let roundup_sec = -delta_nsec / NANOSECONDS + 1;
timespec {
tv_sec: delta_sec - (roundup_sec as time_t),
tv_nsec: roundup_sec * NANOSECONDS - delta_nsec,
}
} else {
timespec {
tv_sec: delta_sec + (delta_nsec / NANOSECONDS) as time_t,
tv_nsec: delta_nsec % NANOSECONDS,
}
};
if time.tv_sec < 0 {
// https://man7.org/linux/man-pages/man2/settimeofday.2.html
// caller should return EINVAL
return None;
}
Some(time)
}
pub fn is_default(&self) -> bool {
self.tv_nsec == 0 && self.tv_sec == 0
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cbindgen_stupid_alias_timespec(_: timespec) {}
+21
View File
@@ -0,0 +1,21 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html
#
# This type is split out to prevent importing all of stddef.h into inttypes.h
include_guard = "_RELIBC_BITS_WCHAR_T_H"
after_includes = """
// Integer type whose range of values can represent distinct codes for all
// members of the largest extended character set specified among the supported
// locales; the null character shall have the code value zero.
#ifndef _WCHAR_T
#define _WCHAR_T
#ifndef __cplusplus
#ifndef __WCHAR_TYPE__
#define __WCHAR_TYPE__ int
#endif
typedef __WCHAR_TYPE__ wchar_t;
#endif // __cplusplus
#endif // _WCHAR_T
"""
language = "C"
no_includes = true
cpp_compat = true
+5
View File
@@ -0,0 +1,5 @@
//! `wchar_t` from `stddef.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/stddef.h.html>.
//!
//! Implemented via ifdefs and defines in cbindgen.
+16
View File
@@ -0,0 +1,16 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/cpio.h.html
#
# There are no spec quotations relating to includes
sys_includes = []
include_guard = "_RELIBC_CPIO_H"
# cbindgen still can't handle exporting CStr
after_includes = """
#define MAGIC "070707"
"""
language = "C"
style = "Type"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
+48
View File
@@ -0,0 +1,48 @@
//! `cpio.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/cpio.h.html>.
use crate::platform::types::c_int;
/// Read by owner.
pub const C_IRUSR: c_int = 0o0000400;
/// Write by owner.
pub const C_IWUSR: c_int = 0o0000200;
/// Execute by owner.
pub const C_IXUSR: c_int = 0o0000100;
/// Read by group.
pub const C_IRGRP: c_int = 0o0000040;
/// Write by group.
pub const C_IWGRP: c_int = 0o0000020;
/// Execute by group.
pub const C_IXGRP: c_int = 0o0000010;
/// Read by others.
pub const C_IROTH: c_int = 0o0000004;
/// Write by others.
pub const C_IWOTH: c_int = 0o0000002;
/// Execute by others.
pub const C_IXOTH: c_int = 0o0000001;
/// Set user ID.
pub const C_ISUID: c_int = 0o0004000;
/// Set group ID.
pub const C_ISGID: c_int = 0o0002000;
/// On directories, restricted deletion flag.
pub const C_ISVTX: c_int = 0o0001000;
/// Directory.
pub const C_ISDIR: c_int = 0o0040000;
/// FIFO.
pub const C_ISFIFO: c_int = 0o0010000;
/// Regular file.
pub const C_ISREG: c_int = 0o0100000;
/// Block special.
pub const C_ISBLK: c_int = 0o0060000;
/// Character special.
pub const C_ISCHR: c_int = 0o0020000;
/// Reserved.
pub const C_ISCTG: c_int = 0o0110000;
/// Symbolic link.
pub const C_ISLNK: c_int = 0o0120000;
/// Socket.
pub const C_ISSOCK: c_int = 0o0140000;
+16
View File
@@ -0,0 +1,16 @@
use alloc::string::{String, ToString};
use argon2::{
Argon2,
password_hash::{PasswordHash, PasswordVerifier},
};
pub fn crypt_argon2(key: &str, setting: &str) -> Option<String> {
let hash = PasswordHash::new(setting).ok()?;
let argon2 = Argon2::default();
if argon2.verify_password(key.as_bytes(), &hash).is_ok() {
Some(setting.to_string())
} else {
None
}
}
+90
View File
@@ -0,0 +1,90 @@
use crate::platform::types::{c_uchar, c_uint};
use alloc::{string::String, vec::Vec};
use base64ct::{Base64Bcrypt, Encoding};
use bcrypt_pbkdf::bcrypt_pbkdf;
use core::str;
const MIN_COST: u32 = 4;
const MAX_COST: u32 = 31;
const BHASH_WORDS: usize = 8;
const BHASH_OUTPUT_SIZE: usize = BHASH_WORDS * 4;
/// Inspired by https://github.com/Keats/rust-bcrypt/blob/87fc59e917bcb6cf3f3752fc7f2b4c659d415597/src/lib.rs#L135
fn split_with_prefix(hash: &str) -> Option<(&str, &str, c_uint)> {
let valid_prefixes = ["2y", "2b", "2a", "2x"];
// Should be [prefix, cost, hash]
let raw_parts: Vec<_> = hash.split('$').skip(1).collect();
if raw_parts.len() != 3 {
return None;
}
let prefix = raw_parts[0];
let setting = raw_parts[2];
if !valid_prefixes.contains(&prefix) {
return None;
}
raw_parts[1]
.parse::<c_uint>()
.ok()
.map(|cost| (prefix, setting, cost))
}
/// Performs Blowfish key derivation on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the Blowfish key derivation. It must be a string slice (`&str`)
/// and should follow the format `$<prefix>$<cost>$<setting>`, where `<prefix>` is a string that
/// indicates the type of the hash (e.g., "$2a$"), `<cost>` is a decimal number representing
/// the cost factor for the Blowfish operation, and `<setting>` is a base64-encoded string
/// representing the salt to be used for the Blowfish function.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the Blowfish operation was successful, where the
/// returned string is the result of the Blowfish operation formatted according to the Modular
/// Crypt Format (MCF). If the Blowfish operation failed, it returns `None`.
///
/// # Errors
/// * If the cost factor is outside the range `[MIN_COST, MAX_COST]`.
///
/// # Example
/// ```
/// let password = "correctbatteryhorsestapler";
/// let setting = "$2y$12$L6Bc/AlTQHyd9liGgGEZyO";
/// let result = crypt_blowfish(password, setting);
/// assert!(result.is_some());
///```
///
/// # Note
/// The `crypt_blowfish` function uses the Blowfish block cipher for hashing.
/// The output of the Blowfish operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_blowfish(passw: &str, setting: &str) -> Option<String> {
if let Some((prefix, setting, cost)) = split_with_prefix(setting) {
if !(MIN_COST..=MAX_COST).contains(&cost) {
return None;
}
// Passwords need to be null terminated
let mut vec = Vec::with_capacity(passw.len() + 1);
vec.extend_from_slice(passw.as_bytes());
vec.push(0);
// We only consider the first 72 chars; truncate if necessary.
let passw_t = if vec.len() > 72 { &vec[..72] } else { &vec };
let passw: &[c_uchar] = passw_t;
let setting = setting.as_bytes();
let mut output = vec![0; BHASH_OUTPUT_SIZE + 1];
bcrypt_pbkdf(passw, setting, cost, &mut output).ok()?;
Some(format!(
"${}${}${}",
prefix,
cost,
Base64Bcrypt::encode_string(&output),
))
} else {
None
}
}
+9
View File
@@ -0,0 +1,9 @@
include_guard = "_RELIBC_CRYPT_H"
language = "C"
style = "Type"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
+146
View File
@@ -0,0 +1,146 @@
use crate::platform::types::c_uchar;
use alloc::string::String;
use base64ct::{Base64ShaCrypt, Encoding};
use core::str;
use md5_crypto::{Digest, Md5};
// Block size for MD5
const BLOCK_SIZE: usize = 16;
// PWD part length of the password string
const PW_SIZE_MD5: usize = 22;
// Maximum length of a setting
const SALT_MAX: usize = 8;
// Inverse encoding map for MD5.
const MAP_MD5: [c_uchar; BLOCK_SIZE] = [12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 5, 10, 4, 11];
const KEY_MAX: usize = 30000;
fn encode_md5(source: &[c_uchar]) -> Option<[c_uchar; PW_SIZE_MD5]> {
let mut transposed = [0; BLOCK_SIZE];
for (i, &ti) in MAP_MD5.iter().enumerate() {
transposed[i] = source[ti as usize];
}
let mut buf = [0; PW_SIZE_MD5];
Base64ShaCrypt::encode(&transposed, &mut buf).ok()?;
Some(buf)
}
/// Function taken from PR: https://github.com/RustCrypto/password-hashes/pull/351
/// This won't be needed once the PR is merged
fn inner_md5(passw: &str, setting: &str) -> Option<String> {
let mut digest_b = Md5::default();
digest_b.update(passw);
digest_b.update(setting);
digest_b.update(passw);
let hash_b = digest_b.finalize();
let mut digest_a = Md5::default();
digest_a.update(passw);
digest_a.update("$1$");
digest_a.update(setting);
let mut pw_len = passw.len();
let rounds = pw_len / BLOCK_SIZE;
for _ in 0..rounds {
digest_a.update(hash_b);
}
// leftover passw
digest_a.update(&hash_b[..(pw_len - rounds * BLOCK_SIZE)]);
while pw_len > 0 {
match pw_len & 1 {
0 => digest_a.update(&passw[..1]),
1 => digest_a.update([0u8]),
_ => unreachable!(),
}
pw_len >>= 1;
}
let mut hash_a = digest_a.finalize();
// Repeatedly run the collected hash value through MD5 to burn
// CPU cycles
for i in 0..1000_usize {
// new hasher
let mut hasher = Md5::default();
// Add key or last result
if (i & 1) != 0 {
hasher.update(passw);
} else {
hasher.update(hash_a);
}
// Add setting for numbers not divisible by 3
if i % 3 != 0 {
hasher.update(setting);
}
// Add key for numbers not divisible by 7
if i % 7 != 0 {
hasher.update(passw);
}
// Add key or last result
if (i & 1) != 0 {
hasher.update(hash_a);
} else {
hasher.update(passw);
}
// digest_c.clone_from_slice(&hasher.finalize());
hash_a = hasher.finalize();
}
encode_md5(hash_a.as_slice())
.map(|encstr| format!("$1${}${}", setting, str::from_utf8(&encstr).unwrap()))
}
/// Performs MD5 hashing on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the MD5 hashing. It must be a string slice (`&str`)
/// and should start with "$1$". The rest of the string should represent the salt
/// to be used for the MD5 hashing.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the MD5 operation was successful, where the
/// returned string is the result of the MD5 operation formatted according to the Modular
/// Crypt Format (MCF). If the MD5 operation failed, it returns `None`.
///
/// # Errors
/// * If the `passw` length exceeds `KEY_MAX`.
/// * If the `setting` does not start with "$1$".
///
/// # Example
/// ```
/// let password = "my_password";
/// let setting = "$1$saltstring";
/// let result = crypt_md5(password, setting);
/// assert!(result.is_some());
/// ```
///
/// # Note
/// The `crypt_md5` function uses the MD5 hashing algorithm for hashing.
/// The output of the MD5 operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_md5(passw: &str, setting: &str) -> Option<String> {
/* reject large keys */
if passw.len() > KEY_MAX {
return None;
}
if &setting[0..3] != "$1$" {
return None;
}
let cursor = 3;
let slen = cursor
+ setting[cursor..cursor + SALT_MAX]
.chars()
.take_while(|c| *c != '$')
.count();
let setting = &setting[cursor..slen];
inner_md5(passw, setting)
}
+121
View File
@@ -0,0 +1,121 @@
//! `crypt.h` implementation.
//!
//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/crypt.3.html>.
use ::scrypt::password_hash::{Salt, SaltString};
use alloc::{
ffi::CString,
string::{String, ToString},
};
use core::ptr;
use rand::{Rng, SeedableRng, rngs::SmallRng};
use crate::{
c_str::CStr,
header::{errno::EINVAL, stdlib::rand},
platform::{
self,
types::{c_char, c_int},
},
};
mod argon2;
mod blowfish;
mod md5;
mod pbkdf2;
mod scrypt;
mod sha;
use self::{
argon2::crypt_argon2,
blowfish::crypt_blowfish,
md5::crypt_md5,
pbkdf2::crypt_pbkdf2,
scrypt::crypt_scrypt,
sha::{
ShaType::{Sha256, Sha512},
crypt_sha,
},
};
/// See <https://www.man7.org/linux/man-pages/man3/crypt.3.html>.
#[repr(C)]
pub struct crypt_data {
initialized: c_int,
buff: [c_char; 256],
}
impl crypt_data {
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
crypt_data {
initialized: 1,
buff: [0; 256],
}
}
}
fn gen_salt() -> Option<String> {
let mut rng = SmallRng::seed_from_u64(unsafe { rand() as u64 });
let mut bytes = [0u8; Salt::RECOMMENDED_LENGTH];
rng.fill_bytes(&mut bytes);
Some(SaltString::encode_b64(&bytes).ok()?.as_str().to_string())
}
/// See <https://www.man7.org/linux/man-pages/man3/crypt.3.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn crypt_r(
key: *const c_char,
setting: *const c_char,
data: *mut crypt_data,
) -> *mut c_char {
if unsafe { (*data).initialized } == 0 {
unsafe { *data = crypt_data::new() };
}
let key = unsafe { CStr::from_ptr(key) }
.to_str()
.expect("key must be utf-8");
let setting = unsafe { CStr::from_ptr(setting) }
.to_str()
.expect("setting must be utf-8");
let encoded = if setting.starts_with('$') {
if setting.starts_with("$1$") {
crypt_md5(key, setting)
} else if setting.starts_with("$2") && setting.as_bytes().get(3) == Some(&b'$') {
crypt_blowfish(key, setting)
} else if setting.starts_with("$5$") {
crypt_sha(key, setting, Sha256)
} else if setting.starts_with("$6$") {
crypt_sha(key, setting, Sha512)
} else if setting.starts_with("$7$") {
crypt_scrypt(key, setting)
} else if setting.starts_with("$8$") {
crypt_pbkdf2(key, setting)
} else if setting.starts_with("$argon2") {
crypt_argon2(key, setting)
} else {
platform::ERRNO.set(EINVAL);
return ptr::null_mut();
}
} else {
None
};
if let Some(inner) = encoded {
let len = inner.len();
if let Ok(ret) = CString::new(inner) {
let ret_ptr = ret.into_raw();
unsafe {
let dst = (*data).buff.as_mut_ptr();
ptr::copy_nonoverlapping(ret_ptr, dst.cast(), len);
}
ret_ptr.cast()
} else {
ptr::null_mut()
}
} else {
ptr::null_mut()
}
}
+64
View File
@@ -0,0 +1,64 @@
use super::gen_salt;
use alloc::string::{String, ToString};
use base64ct::{Base64Bcrypt, Encoding};
use core::str;
use pbkdf2::pbkdf2_hmac;
use sha2::Sha256;
/// Performs PBKDF2 key derivation on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the PBKDF2 key derivation. It must be a string slice (`&str`)
/// and should follow the format `$<iter>$<salt>`. The `<iter>` part should be a hexadecimal
/// number representing the iteration count for the PBKDF2 function. The `<salt>` part should
/// be a base64-encoded string representing the salt to be used for the PBKDF2 function.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the PBKDF2 operation was successful, where the
/// returned string is the result of the PBKDF2 operation formatted according to the Modular
/// Crypt Format (MCF). If the PBKDF2 operation failed, it returns `None`.
///
/// # Errors
/// * If the `setting` does not contain a '$' character.
/// * If the `setting` contains another '$' character after the first one.
/// * If the `<salt>` part of the `setting` is empty.
/// * If the `<iter>` part of the `setting` cannot be converted into a `u32` integer.
///
/// # Example
/// ```
/// let password = "my_password";
/// let setting = "$8$3e8$salt";
/// let result = crypt_pbkdf2(password, setting);
/// assert!(result.is_some());
/// ```
///
/// # Note
/// The `crypt_pbkdf2` function uses the SHA256 hashing algorithm for the PBKDF2 operation.
/// The output of the PBKDF2 operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_pbkdf2(passw: &str, setting: &str) -> Option<String> {
if let Some((iter_str, salt)) = &setting[3..].split_once('$') {
if salt.contains('$') {
return None;
}
let actual_salt = if !salt.is_empty() {
salt.to_string()
} else {
gen_salt()?
};
let iter = u32::from_str_radix(iter_str, 16).ok()?;
let mut buffer = [0u8; 32];
pbkdf2_hmac::<Sha256>(passw.as_bytes(), actual_salt.as_bytes(), iter, &mut buffer);
Some(format!(
"$8${}${}${}",
iter_str,
salt,
Base64Bcrypt::encode_string(&buffer)
))
} else {
None
}
}
+113
View File
@@ -0,0 +1,113 @@
use super::gen_salt;
use crate::platform::types::{c_uchar, c_uint};
use alloc::string::{String, ToString};
use base64ct::{Base64Bcrypt, Encoding};
use core::str;
use scrypt::{Params, scrypt};
/// Map for encoding and decoding
#[inline(always)]
fn to_digit(c: char, radix: u32) -> Option<u32> {
match c {
'.' => Some(0),
'/' => Some(1),
_ => c.to_digit(radix).map(|d| d + 2),
}
}
/// Decodes a 5 character lengt str value to c_uint
///
/// # Arguments
///
/// * `value` - A string slice that represents a u32 value in base64
///
/// # Returns
///
/// * `Option<c_uint>` - Returns the decoded c_uint value if successful, otherwise None
fn dencode_uint(value: &str) -> Option<c_uint> {
if value.len() != 5 {
return None;
}
value
.chars()
.enumerate()
.try_fold(0 as c_uint, |acc, (i, c)| {
acc.checked_add((to_digit(c, 30)? as c_uint) << (i * 6))
})
}
/// Reads settings for password encryption
///
/// # Arguments
///
/// * `setting` - A string slice that represents the settings
///
/// # Returns
///
/// * `Option<(c_uchar, c_uint, c_uint, String)>` - Returns a tuple containing the settings if successful, otherwise None
fn read_setting(setting: &str) -> Option<(c_uchar, c_uint, c_uint, String)> {
let nlog2 = to_digit(setting.chars().next()?, 30)? as c_uchar;
let r = dencode_uint(&setting[1..6])?;
let p = dencode_uint(&setting[6..11])?;
let salt = &setting[11..];
let actual_salt = if !salt.is_empty() {
salt.to_string()
} else {
gen_salt()?
};
Some((nlog2, r, p, actual_salt))
}
/// Performs Scrypt key derivation on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the Scrypt key derivation. It must be a string slice (`&str`)
/// and should follow the format `$<Nlog2>$<r>$<p>$<salt>`. The `<Nlog2>` part should be a decimal
/// number representing the logarithm base 2 of the CPU/memory cost factor N for Scrypt. The `<r>`
/// part should be a decimal number representing the block size r. The `<p>` part should be a decimal
/// number representing the parallelization factor p. The `<salt>` part should be a base64-encoded
/// string representing the salt to be used for the Scrypt function.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the Scrypt operation was successful, where the
/// returned string is the result of the Scrypt operation formatted according to the Modular
/// Crypt Format (MCF). If the Scrypt operation failed, it returns `None`.
///
/// # Errors
/// * If the `setting` length is less than 14 characters.
/// * If the `scrypt` function fails to perform the Scrypt operation.
///
/// # Example
/// ```
/// let password = "my_password";
/// let setting = "$7$C6..../....SodiumChloride";
/// let result = crypt_scrypt(password, setting);
/// assert!(result.is_some());
/// ```
///
/// # Note
/// The `crypt_scrypt` function uses the Scrypt key derivation function for hashing.
/// The output of the Scrypt operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_scrypt(passw: &str, setting: &str) -> Option<String> {
if setting.len() < 14 {
return None;
}
let (nlog2, r, p, salt) = read_setting(&setting[3..])?;
let params = Params::new(nlog2, r, p, 32).ok()?;
let mut output = [0u8; 32];
scrypt(passw.as_bytes(), salt.as_bytes(), &params, &mut output).ok()?;
Some(format!(
"$7${}${}${}",
&setting[3..14],
salt,
Base64Bcrypt::encode_string(&output)
))
}
+141
View File
@@ -0,0 +1,141 @@
use alloc::string::{String, ToString};
use sha_crypt::{
ROUNDS_DEFAULT, ROUNDS_MAX, ROUNDS_MIN, Sha256Params, Sha512Params, sha256_crypt_b64,
sha512_crypt_b64,
};
use crate::platform::types::c_ulong;
// key limit is not part of the original design, added for DoS protection.
// rounds limit has been lowered (versus the reference/spec), also for DoS
// protection. runtime is O(klen^2 + klen*rounds)
const KEY_MAX: usize = 256;
const SALT_MAX: usize = 16;
const RSTRING: &str = "rounds=";
pub enum ShaType {
Sha256,
Sha512,
}
/// Performs SHA hashing on a given password with a specific setting.
///
/// # Parameters
/// * `passw`: The password to be hashed. It must be a string slice (`&str`).
/// * `setting`: The settings for the SHA hashing. It must be a string slice (`&str`)
/// and should start with "$5$" for SHA256 or "$6$" for SHA512. The rest of the string should represent the salt
/// to be used for the SHA hashing.
/// * `cipher`: The type of SHA algorithm to use. It should be either `ShaType::Sha256` or `ShaType::Sha512`.
///
/// # Returns
/// * `Option<String>`: Returns `Some(String)` if the SHA operation was successful, where the
/// returned string is the result of the SHA operation formatted according to the Modular
/// Crypt Format (MCF). If the SHA operation failed, it returns `None`.
///
/// # Errors
/// * If the `passw` length exceeds `KEY_MAX`.
/// * If the `setting` does not start with "$5$" or "$6$".
/// * If the `setting` does not contain a '$' character.
/// * If the `setting` contains another '$' character after the first one.
/// * If the `setting` contains invalid characters.
/// * If the `setting` contains an invalid number of rounds.
/// * If the `sha256_crypt_b64` or `sha512_crypt_b64` function fails to hash the password.
///
/// # Example
/// ```
/// let password = "my_password";
/// let setting = "$5$rounds=1400$anotherlongsaltstringg";
/// let result = crypt_sha(password, setting, ShaType::Sha256);
/// assert!(result.is_some());
/// ```
///
/// # Note
/// The `crypt_sha` function uses the SHA256 or SHA512 hashing algorithm for hashing.
/// The output of the SHA operation is base64-encoded using the BCrypt variant of base64.
pub fn crypt_sha(passw: &str, setting: &str, cipher: ShaType) -> Option<String> {
let mut cursor = 3;
let rounds;
/* reject large keys */
if passw.len() > KEY_MAX {
return None;
}
// SHA256
// setting: $5$rounds=n$setting$ (rounds=n$ and closing $ are optional)
// SHA512
// setting: $6$rounds=n$setting$ (rounds=n$ and closing $ are optional)
let param = match cipher {
ShaType::Sha256 => "$5$",
ShaType::Sha512 => "$6$",
};
if &setting[0..3] != param {
return None;
}
let has_round;
// 7 is len("rounds=")
if &setting[cursor..cursor + 7] == RSTRING {
cursor += 7;
has_round = true;
if let Some(c_end) = setting[cursor..].chars().position(|r| r == '$') {
if let Ok(u) = setting[cursor..cursor + c_end].parse::<c_ulong>() {
cursor += c_end + 1;
rounds = u.min(ROUNDS_MAX as c_ulong).max(ROUNDS_MIN as c_ulong);
} else {
return None;
}
} else {
return None;
}
} else {
has_round = false;
rounds = ROUNDS_DEFAULT as c_ulong;
}
let mut slen = cursor;
for i in 0..SALT_MAX.min(setting.len() - cursor) {
let idx = cursor + i;
if &setting[idx..idx + 1] == "$" {
break;
}
// reject characters that interfere with /etc/shadow parsing
if &setting[idx..idx + 1] == "\n" || &setting[idx..idx + 1] == ":" {
return None;
}
slen += 1;
}
let setting = &setting[cursor..slen];
if let Ok(enc) = match cipher {
ShaType::Sha256 => {
let params = Sha256Params::new(rounds as usize)
.unwrap_or(Sha256Params::new(ROUNDS_DEFAULT).unwrap());
sha256_crypt_b64(passw.as_bytes(), setting.as_bytes(), &params)
}
ShaType::Sha512 => {
let params = Sha512Params::new(rounds as usize)
.unwrap_or(Sha512Params::new(ROUNDS_DEFAULT).unwrap());
sha512_crypt_b64(passw.as_bytes(), setting.as_bytes(), &params)
}
} {
let (r_slice, rn_slice) = if has_round {
(RSTRING, rounds.to_string() + "$")
} else {
("", String::new())
};
Some(format!(
"{}{}{}{}${}",
param, r_slice, rn_slice, setting, enc
))
} else {
None
}
}
+14 -2
View File
@@ -1,8 +1,20 @@
sys_includes = ["bits/ctype.h"]
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/ctype.h.html
#
# Spec quotations relating to includes:
# - "The <ctype.h> header shall define the locale_t type as described in <locale.h>, representing a locale object."
#
# features.h included for #[deprecated] annotation
sys_includes = ["features.h", "stddef.h"]
include_guard = "_RELIBC_CTYPE_H"
after_includes = """
#include <bits/locale-t.h> // for locale_t
#define _tolower(c) tolower(c)
#define _toupper(c) toupper(c)
"""
language = "C"
style = "Tag"
no_includes = true
no_includes = false
cpp_compat = true
[enum]
+137 -37
View File
@@ -1,53 +1,117 @@
//! ctype implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/ctype.h.html
//! `ctype.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/ctype.h.html>.
use crate::platform::types::*;
use crate::{header::bits_locale_t::locale_t, platform::types::c_int};
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isalnum.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isalnum(c: c_int) -> c_int {
c_int::from(isdigit(c) != 0 || isalpha(c) != 0)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isalnum_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isalnum_l(c: c_int, _loc: locale_t) -> c_int {
isalnum(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isalpha.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isalpha(c: c_int) -> c_int {
c_int::from(islower(c) != 0 || isupper(c) != 0)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isalpha_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isalpha_l(c: c_int, _loc: locale_t) -> c_int {
isalpha(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/isascii.html>.
///
/// The `isascii()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 7, and removed in Issue 8.
#[deprecated]
#[unsafe(no_mangle)]
pub extern "C" fn isascii(c: c_int) -> c_int {
c_int::from((c & !0x7f) == 0)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isblank.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isblank(c: c_int) -> c_int {
c_int::from(c == c_int::from(b' ') || c == c_int::from(b'\t'))
}
#[no_mangle]
pub extern "C" fn iscntrl(c: c_int) -> c_int {
c_int::from((c >= 0x00 && c <= 0x1f) || c == 0x7f)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isblank_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isblank_l(c: c_int, _loc: locale_t) -> c_int {
isblank(c)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/iscntrl.html>.
#[unsafe(no_mangle)]
pub extern "C" fn iscntrl(c: c_int) -> c_int {
c_int::from((0x00..=0x1f).contains(&c) || c == 0x7f)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/iscntrl_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn iscntrl_l(c: c_int, _loc: locale_t) -> c_int {
iscntrl(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isdigit.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isdigit(c: c_int) -> c_int {
c_int::from(c >= c_int::from(b'0') && c <= c_int::from(b'9'))
}
#[no_mangle]
pub extern "C" fn isgraph(c: c_int) -> c_int {
c_int::from(c >= 0x21 && c <= 0x7e)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isdigit_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isdigit_l(c: c_int, _loc: locale_t) -> c_int {
isdigit(c)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isgraph.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isgraph(c: c_int) -> c_int {
c_int::from((0x21..=0x7e).contains(&c))
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isgraph_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isgraph_l(c: c_int, _loc: locale_t) -> c_int {
isgraph(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/islower.html>.
#[unsafe(no_mangle)]
pub extern "C" fn islower(c: c_int) -> c_int {
c_int::from(c >= c_int::from(b'a') && c <= c_int::from(b'z'))
}
#[no_mangle]
pub extern "C" fn isprint(c: c_int) -> c_int {
c_int::from(c >= 0x20 && c < 0x7f)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/islower_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn islower_l(c: c_int, _loc: locale_t) -> c_int {
islower(c)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isprint.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isprint(c: c_int) -> c_int {
c_int::from((0x20..0x7f).contains(&c))
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isprint_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isprint_l(c: c_int, _loc: locale_t) -> c_int {
isprint(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ispunct.html>.
#[unsafe(no_mangle)]
pub extern "C" fn ispunct(c: c_int) -> c_int {
c_int::from(
(c >= c_int::from(b'!') && c <= c_int::from(b'/'))
@@ -57,7 +121,14 @@ pub extern "C" fn ispunct(c: c_int) -> c_int {
)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/ispunct_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn ispunct_l(c: c_int, _loc: locale_t) -> c_int {
ispunct(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isspace.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isspace(c: c_int) -> c_int {
c_int::from(
c == c_int::from(b' ')
@@ -69,37 +140,66 @@ pub extern "C" fn isspace(c: c_int) -> c_int {
)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isspace_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isspace_l(c: c_int, _loc: locale_t) -> c_int {
isspace(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isupper.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isupper(c: c_int) -> c_int {
c_int::from(c >= c_int::from(b'A') && c <= c_int::from(b'Z'))
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isupper_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isupper_l(c: c_int, _loc: locale_t) -> c_int {
isupper(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isxdigit.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isxdigit(c: c_int) -> c_int {
c_int::from(isdigit(c) != 0 || (c | 32 >= c_int::from(b'a') && c | 32 <= c_int::from(b'f')))
}
#[no_mangle]
/// The comment in musl:
/// "nonsense function that should NEVER be used!"
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/isxdigit_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn isxdigit_l(c: c_int, _loc: locale_t) -> c_int {
isxdigit(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9699919799/functions/toascii.html>.
///
/// The `toascii()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 7, and removed in Issue 8.
#[deprecated]
#[unsafe(no_mangle)]
pub extern "C" fn toascii(c: c_int) -> c_int {
c & 0x7f
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tolower.html>.
#[unsafe(no_mangle)]
pub extern "C" fn tolower(c: c_int) -> c_int {
if isupper(c) != 0 {
c | 0x20
} else {
c
}
if isupper(c) != 0 { c | 0x20 } else { c }
}
#[no_mangle]
pub extern "C" fn toupper(c: c_int) -> c_int {
if islower(c) != 0 {
c & !0x20
} else {
c
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/tolower_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn tolower_l(c: c_int, _loc: locale_t) -> c_int {
tolower(c)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/toupper.html>.
#[unsafe(no_mangle)]
pub extern "C" fn toupper(c: c_int) -> c_int {
if islower(c) != 0 { c & !0x20 } else { c }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/toupper_l.html>.
#[unsafe(no_mangle)]
pub extern "C" fn toupper_l(c: c_int, _loc: locale_t) -> c_int {
toupper(c)
}
+12 -2
View File
@@ -1,9 +1,19 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html
#
# Spec quotations relating to includes:
# - "The <dirent.h> header shall define the ino_t, reclen_t, size_t, and ssize_t types as described in <sys/types.h>."
sys_includes = ["sys/types.h"]
include_guard = "_RELIBC_DIRENT_H"
language = "C"
style = "Both"
trailer = "#include <bits/dirent.h>"
no_includes = true
after_includes = """
// Shamelessly stolen from musl
// Macros to convert between struct dirent and struct stat types.
#define IFTODT(x) ((x)>>12 & 017)
#define DTTOIF(x) ((x)<<12)
"""
no_includes = false
cpp_compat = true
[enum]
+314 -113
View File
@@ -1,31 +1,171 @@
//! dirent implementation following http://pubs.opengroup.org/onlinepubs/009695399/basedefs/dirent.h.html
//! `dirent.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html>.
use alloc::boxed::Box;
use core::{mem, ptr};
use alloc::{boxed::Box, vec::Vec};
use core::{mem, ptr, slice};
use crate::{
c_str::CStr,
c_vec::CVec,
error::{Errno, ResultExt, ResultExtPtrMut},
fs::File,
header::{errno, fcntl, stdlib, string},
io::{Seek, SeekFrom},
platform::{self, types::*, Pal, Sys},
header::{
errno::{EINVAL, EIO, ENOMEM, ENOTDIR},
fcntl, stdlib, string, sys_stat,
},
out::Out,
platform::{
self, Pal, Sys,
types::{
c_char, c_int, c_long, c_uchar, c_ushort, c_void, ino_t, off_t, reclen_t, size_t,
ssize_t,
},
},
};
const DIR_BUF_SIZE: usize = mem::size_of::<dirent>() * 3;
// values of below constants taken from musl
// No repr(C) needed, C won't see the content
/// Unknown file type.
pub const DT_UNKNOWN: c_int = 0;
/// FIFO special.
pub const DT_FIFO: c_int = 1;
/// Character special.
pub const DT_CHR: c_int = 2;
/// Directory.
pub const DT_DIR: c_int = 4;
/// Block special.
pub const DT_BLK: c_int = 6;
/// Regular.
pub const DT_REG: c_int = 8;
/// Symbolic link.
pub const DT_LNK: c_int = 10;
/// Socket.
pub const DT_SOCK: c_int = 12;
/// Non-POSIX.
/// Whiteout inode.
pub const DT_WHT: c_int = 14;
// values of above constants taken from musl
/// cbindgen:ignore
const INITIAL_BUFSIZE: usize = 512;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html>.
// No repr(C) needed, as this is a completely opaque struct. Being accessed as a pointer, in C it's
// just defined as `struct DIR`.
pub struct DIR {
file: File,
buf: [c_char; DIR_BUF_SIZE],
// index and len are specified in bytes
index: usize,
len: usize,
buf: Vec<u8>,
buf_offset: usize,
// The last value of d_off, used by telldir
offset: usize,
opaque_offset: u64,
}
impl DIR {
pub fn new(path: CStr) -> Result<Box<Self>, Errno> {
Ok(Box::new(Self {
file: File::open(
path,
fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC,
)?,
buf: Vec::with_capacity(INITIAL_BUFSIZE),
buf_offset: 0,
opaque_offset: 0,
}))
}
pub fn from_fd(fd: c_int) -> Result<Box<Self>, Errno> {
let mut stat = sys_stat::stat::default();
Sys::fstat(fd, Out::from_mut(&mut stat))?;
if (stat.st_mode & sys_stat::S_IFMT) != sys_stat::S_IFDIR {
return Err(Errno(ENOTDIR));
}
Sys::fcntl(fd, fcntl::F_SETFD, fcntl::FD_CLOEXEC as _)?;
// Take ownership now but not earlier so we don't close the fd on error.
let file = File::new(fd);
Ok(Self {
file,
buf: Vec::with_capacity(INITIAL_BUFSIZE),
buf_offset: 0,
opaque_offset: 0,
}
.into())
}
fn next_dirent(&mut self) -> Result<*mut dirent, Errno> {
let mut this_dent = self.buf.get(self.buf_offset..).ok_or(Errno(EIO))?;
if this_dent.is_empty() {
let size = loop {
self.buf.resize(self.buf.capacity(), 0_u8);
// TODO: uninitialized memory?
match Sys::getdents(*self.file, &mut self.buf, self.opaque_offset) {
Ok(size) => break size,
Err(Errno(EINVAL)) => {
self.buf
.try_reserve_exact(self.buf.len())
.map_err(|_| Errno(ENOMEM))?;
continue;
}
Err(Errno(other)) => return Err(Errno(other)),
}
};
self.buf.truncate(size);
self.buf_offset = 0;
if size == 0 {
return Ok(core::ptr::null_mut());
}
this_dent = &self.buf;
}
let (this_reclen, this_next_opaque) =
unsafe { Sys::dent_reclen_offset(this_dent, self.buf_offset).ok_or(Errno(EIO))? };
//println!("CDENT {} {}+{}", self.opaque_offset, self.buf_offset, this_reclen);
let next_off = self
.buf_offset
.checked_add(usize::from(this_reclen))
.ok_or(Errno(EIO))?;
if next_off > self.buf.len() {
return Err(Errno(EIO));
}
if this_dent.len() < usize::from(this_reclen) {
// Don't want memory corruption if a scheme is adversarial.
return Err(Errno(EIO));
}
let dent_ptr = this_dent.as_ptr() as *mut dirent;
self.opaque_offset = this_next_opaque;
self.buf_offset = next_off;
Ok(dent_ptr)
}
fn seek(&mut self, off: u64) {
let Ok(_) = Sys::dir_seek(*self.file, off) else {
return;
};
self.buf.clear();
self.buf_offset = 0;
self.opaque_offset = off;
}
fn rewind(&mut self) {
self.opaque_offset = 0;
let Ok(_) = Sys::dir_seek(*self.file, 0) else {
return;
};
self.buf.clear();
self.buf_offset = 0;
self.opaque_offset = 0;
}
fn close(mut self) -> Result<(), Errno> {
// Reference files aren't closed when dropped
self.file.reference = true;
// TODO: result
Sys::close(*self.file)
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html>.
#[repr(C)]
#[derive(Clone)]
pub struct dirent {
@@ -36,69 +176,114 @@ pub struct dirent {
pub d_name: [c_char; 256],
}
#[no_mangle]
pub unsafe extern "C" fn opendir(path: *const c_char) -> *mut DIR {
let path = CStr::from_ptr(path);
let file = match File::open(
path,
fcntl::O_RDONLY | fcntl::O_DIRECTORY | fcntl::O_CLOEXEC,
) {
Ok(file) => file,
Err(_) => return ptr::null_mut(),
};
Box::into_raw(Box::new(DIR {
file,
buf: [0; DIR_BUF_SIZE],
index: 0,
len: 0,
offset: 0,
}))
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dirent.h.html>.
/// must have the same struct layout as dirent
#[repr(C)]
#[derive(Clone)]
pub struct posix_dent {
pub d_ino: ino_t,
pub d_off: off_t, // not specified by posix
pub d_reclen: reclen_t,
pub d_type: c_uchar,
pub d_name: [c_char; 256],
}
#[no_mangle]
pub unsafe extern "C" fn closedir(dir: *mut DIR) -> c_int {
let mut dir = Box::from_raw(dir);
/// cbindgen:ignore
#[cfg(target_os = "redox")]
const _: () = {
use core::mem::{offset_of, size_of};
use syscall::dirent::DirentHeader;
let ret = Sys::close(*dir.file);
// Reference files aren't closed when dropped
dir.file.reference = true;
ret
}
#[no_mangle]
pub unsafe extern "C" fn dirfd(dir: *mut DIR) -> c_int {
*((*dir).file)
}
#[no_mangle]
pub unsafe extern "C" fn readdir(dir: *mut DIR) -> *mut dirent {
if (*dir).index >= (*dir).len {
let read = Sys::getdents(
*(*dir).file,
(*dir).buf.as_mut_ptr() as *mut dirent,
(*dir).buf.len(),
);
if read <= 0 {
if read != 0 && read != -errno::ENOENT {
platform::errno = -read;
}
return ptr::null_mut();
}
(*dir).index = 0;
(*dir).len = read as usize;
if offset_of!(dirent, d_ino) != offset_of!(DirentHeader, inode) {
panic!("struct dirent layout mismatch (inode)");
}
if offset_of!(dirent, d_off) != offset_of!(DirentHeader, next_opaque_id) {
panic!("struct dirent layout mismatch (inode)");
}
if offset_of!(dirent, d_reclen) != offset_of!(DirentHeader, record_len) {
panic!("struct dirent layout mismatch (len)");
}
if offset_of!(dirent, d_type) != offset_of!(DirentHeader, kind) {
panic!("struct dirent layout mismatch (kind)");
}
if offset_of!(dirent, d_name) != size_of::<DirentHeader>() {
panic!("struct dirent layout mismatch (name)");
}
};
let ptr = (*dir).buf.as_mut_ptr().add((*dir).index) as *mut dirent;
(*dir).offset = (*ptr).d_off as usize;
(*dir).index += (*ptr).d_reclen as usize;
ptr
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/alphasort.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn alphasort(first: *mut *const dirent, second: *mut *const dirent) -> c_int {
unsafe { string::strcoll((**first).d_name.as_ptr(), (**second).d_name.as_ptr()) }
}
// #[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/closedir.html>.
#[unsafe(no_mangle)]
pub extern "C" fn closedir(dir: Box<DIR>) -> c_int {
dir.close().map(|()| 0).or_minus_one_errno()
}
/// See <https://man.freebsd.org/cgi/man.cgi?query=fdopendir&sektion=3>
///
/// FreeBSD extension that transfers ownership of the directory file descriptor to the user.
///
/// It doesn't matter if DIR was opened with [`opendir`] or [`fdopendir`].
#[unsafe(no_mangle)]
pub extern "C" fn fdclosedir(dir: Box<DIR>) -> c_int {
let mut file = dir.file;
file.reference = true;
*file
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dirfd.html>.
#[unsafe(no_mangle)]
pub extern "C" fn dirfd(dir: &mut DIR) -> c_int {
*dir.file
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdopendir.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn opendir(path: *const c_char) -> *mut DIR {
let path = unsafe { CStr::from_ptr(path) };
DIR::new(path).or_errno_null_mut()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdopendir.html>.
#[unsafe(no_mangle)]
pub extern "C" fn fdopendir(fd: c_int) -> *mut DIR {
DIR::from_fd(fd).or_errno_null_mut()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_getdents.html>.
#[unsafe(no_mangle)]
pub extern "C" fn posix_getdents(
fildes: c_int,
buf: *mut c_void,
nbyte: size_t,
_flags: c_int,
) -> ssize_t {
let slice = unsafe { slice::from_raw_parts_mut(buf.cast::<u8>(), nbyte) };
Sys::posix_getdents(fildes, slice)
.map(|s| s as ssize_t)
.or_minus_one_errno()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/readdir.html>.
#[unsafe(no_mangle)]
pub extern "C" fn readdir(dir: &mut DIR) -> *mut dirent {
dir.next_dirent().or_errno_null_mut()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/readdir.html>.
///
/// # Deprecation
/// The `readdir_r()` function was marked obsolescent in the Open Group Base
/// Specifications Issue 8.
#[deprecated]
// #[unsafe(no_mangle)]
pub extern "C" fn readdir_r(
_dir: *mut DIR,
_entry: *mut dirent,
@@ -107,35 +292,21 @@ pub extern "C" fn readdir_r(
unimplemented!(); // plus, deprecated
}
#[no_mangle]
pub unsafe extern "C" fn telldir(dir: *mut DIR) -> c_long {
(*dir).offset as c_long
}
#[no_mangle]
pub unsafe extern "C" fn seekdir(dir: *mut DIR, off: c_long) {
let _ = (*dir).file.seek(SeekFrom::Start(off as u64));
(*dir).offset = off as usize;
(*dir).index = 0;
(*dir).len = 0;
}
#[no_mangle]
pub unsafe extern "C" fn rewinddir(dir: *mut DIR) {
seekdir(dir, 0)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/rewinddir.html>.
#[unsafe(no_mangle)]
pub extern "C" fn rewinddir(dir: &mut DIR) {
dir.rewind();
}
#[no_mangle]
pub unsafe extern "C" fn alphasort(first: *mut *const dirent, second: *mut *const dirent) -> c_int {
string::strcoll((**first).d_name.as_ptr(), (**second).d_name.as_ptr())
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/alphasort.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn scandir(
dirp: *const c_char,
namelist: *mut *mut *mut dirent,
filter: Option<extern "C" fn(_: *const dirent) -> c_int>,
compare: Option<extern "C" fn(_: *mut *const dirent, _: *mut *const dirent) -> c_int>,
) -> c_int {
let dir = opendir(dirp);
let dir = unsafe { opendir(dirp) };
if dir.is_null() {
return -1;
}
@@ -145,54 +316,84 @@ pub unsafe extern "C" fn scandir(
Err(err) => return -1,
};
let old_errno = platform::errno;
platform::errno = 0;
let old_errno = platform::ERRNO.get();
platform::ERRNO.set(0);
loop {
let entry: *mut dirent = readdir(dir);
let entry: *mut dirent = readdir(unsafe { &mut *dir });
if entry.is_null() {
break;
}
if let Some(filter) = filter {
if filter(entry) == 0 {
continue;
}
if let Some(filter) = filter
&& filter(entry) == 0
{
continue;
}
let copy = platform::alloc(mem::size_of::<dirent>()) as *mut dirent;
let copy = unsafe { platform::alloc(mem::size_of::<dirent>()) }.cast::<dirent>();
if copy.is_null() {
break;
}
ptr::write(copy, (*entry).clone());
if let Err(_) = vec.push(copy) {
unsafe { ptr::write(copy, (*entry).clone()) };
if vec.push(copy).is_err() {
break;
}
}
closedir(dir);
closedir(unsafe { Box::from_raw(dir) });
let len = vec.len();
if let Err(_) = vec.shrink_to_fit() {
if vec.shrink_to_fit().is_err() {
return -1;
}
if platform::errno != 0 {
if platform::ERRNO.get() != 0 {
for ptr in &mut vec {
platform::free(*ptr as *mut c_void);
unsafe { platform::free((*ptr).cast::<c_void>()) };
}
-1
} else {
*namelist = vec.leak();
unsafe {
// Empty CVecs use a dangling pointer which cannot be freed, return null instead
if vec.is_empty() {
*namelist = ptr::null_mut();
} else {
*namelist = vec.leak();
}
}
platform::errno = old_errno;
stdlib::qsort(
*namelist as *mut c_void,
len as size_t,
mem::size_of::<*mut dirent>(),
mem::transmute(compare),
);
platform::ERRNO.set(old_errno);
unsafe {
stdlib::qsort(
(*namelist).cast::<c_void>(),
len as size_t,
mem::size_of::<*mut dirent>(),
#[allow(clippy::missing_transmute_annotations)] // too verbose
mem::transmute(compare),
)
};
len as c_int
}
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/seekdir.html>.
#[unsafe(no_mangle)]
pub extern "C" fn seekdir(dir: &mut DIR, off: c_long) {
let Ok(off) = off.try_into() else {
platform::ERRNO.set(EINVAL);
return;
};
dir.seek(off);
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/telldir.html>.
#[unsafe(no_mangle)]
pub extern "C" fn telldir(dir: &mut DIR) -> c_long {
dir.opaque_offset as c_long
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_posix_dent(_: posix_dent) {}
+90 -32
View File
@@ -1,44 +1,102 @@
//! dl-tls implementation for Redox
use crate::{ld_so::tcb::Tcb, platform::types::*};
// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is
// dropped from src/lib.rs.
#![warn(warnings, unused_variables)]
#[cfg(target_arch = "x86")]
use core::arch::global_asm;
use alloc::alloc::alloc;
use core::{alloc::Layout, ptr};
use crate::{ld_so::tcb::Tcb, platform::types::c_void};
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct dl_tls_index {
pub ti_module: u64,
pub ti_offset: u64,
pub ti_module: usize,
pub ti_offset: usize,
}
#[no_mangle]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __tls_get_addr(ti: *mut dl_tls_index) -> *mut c_void {
trace!(
"__tls_get_addr({:p}: {:#x}, {:#x})",
let tcb = unsafe { Tcb::current().unwrap() };
let ti = unsafe { &*ti };
let masters = tcb.masters().unwrap();
#[cfg(feature = "trace_tls")]
log::trace!(
"__tls_get_addr({:p}: {:#x}, {:#x}, masters_len={}, dtv_len={})",
ti,
(*ti).ti_module,
(*ti).ti_offset
ti.ti_module,
ti.ti_offset,
masters.len(),
tcb.dtv_mut().len()
);
if let Some(tcb) = Tcb::current() {
if let Some(tls) = tcb.tls() {
if let Some(masters) = tcb.masters() {
if let Some(master) = masters.get((*ti).ti_module as usize) {
let addr = tls
.as_mut_ptr()
.add(master.offset + (*ti).ti_offset as usize);
trace!(
"__tls_get_addr({:p}: {:#x}, {:#x}) = {:p}",
ti,
(*ti).ti_module,
(*ti).ti_offset,
addr
);
return addr as *mut c_void;
}
}
}
if tcb.dtv_mut().len() < masters.len() {
// Reallocate DTV.
tcb.setup_dtv(masters.len());
}
panic!(
"__tls_get_addr({:p}: {:#x}, {:#x}) failed",
ti,
(*ti).ti_module,
(*ti).ti_offset
);
let dtv_index = ti.ti_module - 1;
if tcb.dtv_mut()[dtv_index].is_null() {
// Allocate TLS for module.
let master = &masters[dtv_index];
let module_tls = unsafe {
// FIXME(andypython): master.align
let layout = Layout::from_size_align_unchecked(master.segment_size, 16);
let ptr = alloc(layout);
ptr::copy_nonoverlapping(master.ptr, ptr, master.image_size);
ptr::write_bytes(
ptr.add(master.image_size),
0,
master.segment_size - master.image_size,
);
ptr
};
// Set the DTV entry.
tcb.dtv_mut()[dtv_index] = module_tls;
}
let mut ptr = tcb.dtv_mut()[dtv_index];
if ptr.is_null() {
panic!(
"__tls_get_addr({ti:p}: {:#x}, {:#x})",
ti.ti_module, ti.ti_offset
);
}
if cfg!(target_arch = "riscv64") {
ptr = unsafe { ptr.add(0x800 + ti.ti_offset) }; // dynamic offsets are 0x800-based on risc-v
} else {
ptr = unsafe { ptr.add(ti.ti_offset) };
}
ptr.cast::<c_void>()
}
// x86 can define a version that passes a pointer to dl_tls_index in eax
#[cfg(target_arch = "x86")]
global_asm!(
"
.globl ___tls_get_addr
.type ___tls_get_addr, @function
___tls_get_addr:
push ebp
mov ebp, esp
push eax
call __tls_get_addr
add esp, 4
pop ebp
ret
.size ___tls_get_addr, . - ___tls_get_addr
"
);
+3
View File
@@ -1,3 +1,6 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dlfcn.h.html
#
# There are no spec quotations relating to includes
sys_includes = []
include_guard = "_RELIBC_DLFCN_H"
language = "C"
+103 -67
View File
@@ -1,161 +1,197 @@
//! dlfcn implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/dlfcn.h.html
//! `dlfcn.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dlfcn.h.html>.
// FIXME(andypython): remove this when #![allow(warnings, unused_variables)] is
// dropped from src/lib.rs.
#![warn(warnings, unused_variables)]
use core::{
ptr, str,
sync::atomic::{AtomicUsize, Ordering},
};
use crate::{c_str::CStr, ld_so::tcb::Tcb, platform::types::*};
use crate::{
c_str::CStr,
ld_so::{
linker::{DlError, ObjectHandle, Resolve, ScopeKind},
tcb::Tcb,
},
platform::types::{c_char, c_int, c_void},
};
pub const RTLD_LAZY: c_int = 0x0001;
pub const RTLD_NOW: c_int = 0x0002;
pub const RTLD_GLOBAL: c_int = 0x0100;
pub const RTLD_LAZY: c_int = 1 << 0;
pub const RTLD_NOW: c_int = 1 << 1;
pub const RTLD_NOLOAD: c_int = 1 << 2;
pub const RTLD_GLOBAL: c_int = 1 << 8;
pub const RTLD_LOCAL: c_int = 0x0000;
static ERROR_NOT_SUPPORTED: &'static CStr = c_str!("dlfcn not supported");
#[allow(clippy::zero_ptr)] // related cbindgen issue: https://github.com/mozilla/cbindgen/issues/948
pub const RTLD_DEFAULT: *mut c_void = 0 as *mut c_void; // XXX: cbindgen doesn't like ptr::null_mut() for publically exported constants
static ERROR_NOT_SUPPORTED: &core::ffi::CStr = c"dlfcn not supported";
#[thread_local]
static ERROR: AtomicUsize = AtomicUsize::new(0);
fn set_last_error(error: DlError) {
ERROR.store(error.repr().as_ptr() as usize, Ordering::SeqCst);
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/dlfcn.h.html>.
#[repr(C)]
pub struct Dl_info {
#[allow(non_camel_case_types)]
pub struct Dl_info_t {
dli_fname: *const c_char,
dli_fbase: *mut c_void,
dli_sname: *const c_char,
dli_saddr: *mut c_void,
}
#[no_mangle]
pub unsafe extern "C" fn dladdr(addr: *mut c_void, info: *mut Dl_info) -> c_int {
/// alias as per spec update: <https://www.austingroupbugs.net/view.php?id=1847>
pub type Dl_info = Dl_info_t;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dladdr.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dladdr(_addr: *const c_void, info: *mut Dl_info_t) -> c_int {
//TODO
(*info).dli_fname = ptr::null();
(*info).dli_fbase = ptr::null_mut();
(*info).dli_sname = ptr::null();
(*info).dli_saddr = ptr::null_mut();
unsafe {
(*info).dli_fname = ptr::null();
(*info).dli_fbase = ptr::null_mut();
(*info).dli_sname = ptr::null();
(*info).dli_saddr = ptr::null_mut();
}
0
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dlopen.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dlopen(cfilename: *const c_char, flags: c_int) -> *mut c_void {
//TODO support all sort of flags
let resolve = if flags & RTLD_NOW == RTLD_NOW {
Resolve::Now
} else {
Resolve::Lazy
};
let scope = if flags & RTLD_GLOBAL == RTLD_GLOBAL {
ScopeKind::Global
} else {
ScopeKind::Local
};
let noload = flags & RTLD_NOLOAD == RTLD_NOLOAD;
let filename = if cfilename.is_null() {
None
} else {
Some(str::from_utf8_unchecked(
CStr::from_ptr(cfilename).to_bytes(),
))
unsafe {
Some(str::from_utf8_unchecked(
CStr::from_ptr(cfilename).to_bytes(),
))
}
};
let tcb = match Tcb::current() {
let tcb = match unsafe { Tcb::current() } {
Some(tcb) => tcb,
None => {
eprintln!("dlopen: tcb not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
};
if tcb.linker_ptr.is_null() {
eprintln!("dlopen: linker not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
let mut linker = (&*tcb.linker_ptr).lock();
let mut linker = unsafe { (*tcb.linker_ptr).lock() };
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
let id = match (cbs.load_library)(&mut linker, filename) {
Err(err) => {
eprintln!("dlopen: failed to load {:?}", filename);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
match (cbs.load_library)(&mut linker, filename, resolve, scope, noload) {
Ok(handle) => handle.as_ptr().cast_mut(),
Err(error) => {
set_last_error(error);
ptr::null_mut()
}
Ok(id) => id,
};
if let Some(fname) = filename {
if let Err(err) = (cbs.link)(&mut linker, None, None, Some(id)) {
(cbs.unload)(&mut linker, id);
eprintln!("dlopen: failed to link '{}': {}", fname, err);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
};
if let Err(err) = (cbs.run_init)(&mut linker, Some(id)) {
(cbs.unload)(&mut linker, id);
eprintln!("dlopen: failed to link '{}': {}", fname, err);
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
};
}
id as *mut c_void
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dlsym.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void {
let handle = ObjectHandle::from_ptr(handle);
if symbol.is_null() {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
let symbol_str = str::from_utf8_unchecked(CStr::from_ptr(symbol).to_bytes());
let symbol_str = unsafe { str::from_utf8_unchecked(CStr::from_ptr(symbol).to_bytes()) };
let tcb = match Tcb::current() {
// FIXME(andypython): just call obj.scope.get_sym() directly or search the
// global scope. The rest is unnecessary as Linker::get_sym() does not
// depend on the Linker state.
let tcb = match unsafe { Tcb::current() } {
Some(tcb) => tcb,
None => {
eprintln!("dlsym: tcb not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
};
if tcb.linker_ptr.is_null() {
eprintln!("dlsym: linker not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return ptr::null_mut();
}
let linker = (&*tcb.linker_ptr).lock();
let linker = unsafe { (*tcb.linker_ptr).lock() };
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
if let Some(global) = (cbs.get_sym)(&linker, symbol_str, Some(handle as usize)) {
global.as_ptr()
} else {
eprintln!("dlsym: symbol not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
ptr::null_mut()
match (cbs.get_sym)(&linker, handle, symbol_str) {
Some(sym) => sym,
_ => {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
ptr::null_mut()
}
}
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dlclose.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn dlclose(handle: *mut c_void) -> c_int {
let tcb = match Tcb::current() {
let tcb = match unsafe { Tcb::current() } {
Some(tcb) => tcb,
None => {
eprintln!("dlclose: tcb not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
}
};
if tcb.linker_ptr.is_null() {
eprintln!("dlclose: linker not found");
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
};
let mut linker = (&*tcb.linker_ptr).lock();
let Some(handle) = ObjectHandle::from_ptr(handle) else {
set_last_error(DlError::InvalidHandle);
return -1;
};
let mut linker = unsafe { (*tcb.linker_ptr).lock() };
let cbs_c = linker.cbs.clone();
let cbs = cbs_c.borrow();
if let Err(err) = (cbs.run_fini)(&mut linker, Some(handle as usize)) {
ERROR.store(ERROR_NOT_SUPPORTED.as_ptr() as usize, Ordering::SeqCst);
return -1;
};
(cbs.unload)(&mut linker, handle as usize);
(cbs.unload)(&mut linker, handle);
0
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/dlerror.html>.
#[unsafe(no_mangle)]
pub extern "C" fn dlerror() -> *mut c_char {
ERROR.swap(0, Ordering::SeqCst) as *mut c_char
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cbindgen_stupid_alias_dlinfo_for_dladdr(_: Dl_info) {}
+38 -1
View File
@@ -1,4 +1,41 @@
sys_includes = ["bits/elf.h"]
after_includes = """
#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4)
#define ELF32_ST_TYPE(val) ((val) & 0xf)
#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf))
#define ELF64_ST_BIND(val) ELF32_ST_BIND (val)
#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val)
#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type))
#define ELF32_ST_VISIBILITY(o) ((o) & 0x03)
#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o)
#define ELF32_R_SYM(val) ((val) >> 8)
#define ELF32_R_TYPE(val) ((val) & 0xff)
#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff))
#define ELF64_R_SYM(i) ((i) >> 32)
#define ELF64_R_TYPE(i) ((i) & 0xffffffff)
#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type))
#define DT_VALRNGHI 0x6ffffdff
#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag))
#define DT_ADDRRNGHI 0x6ffffeff
#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag))
#define DT_VERNEEDNUM 0x6fffffff
#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag))
#define ELF32_M_SYM(info) ((info) >> 8)
#define ELF32_M_SIZE(info) ((unsigned char) (info))
#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size))
#define ELF64_M_SYM(info) ELF32_M_SYM (info)
#define ELF64_M_SIZE(info) ELF32_M_SIZE (info)
#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size)
"""
include_guard = "_ELF_H"
language = "C"
style = "Tag"
+28 -8
View File
@@ -1,12 +1,16 @@
use crate::platform::types::*;
//! `elf.h` implementation.
//!
//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
use crate::platform::types::{c_char, c_uchar, int32_t, int64_t, uint16_t, uint32_t, uint64_t};
pub type Elf32_Half = uint16_t;
pub type Elf64_Half = uint16_t;
pub type Elf32_Word = uint32_t;
pub type Elf32_Sword = int32_t;
pub type Elf64_Word = uint64_t;
pub type Elf64_Sword = int64_t;
pub type Elf64_Word = uint32_t;
pub type Elf64_Sword = int32_t;
pub type Elf32_Xword = uint64_t;
pub type Elf32_Sxword = int64_t;
@@ -27,6 +31,7 @@ pub type Elf64_Versym = Elf64_Half;
pub const EI_NIDENT: usize = 16;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Ehdr {
pub e_ident: [c_uchar; EI_NIDENT],
@@ -45,6 +50,7 @@ pub struct Elf32_Ehdr {
pub e_shstrndx: Elf32_Half,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Ehdr {
pub e_ident: [c_uchar; EI_NIDENT],
@@ -71,7 +77,7 @@ pub const EI_MAG2: usize = 2;
pub const ELFMAG2: c_char = 'L' as c_char;
pub const EI_MAG3: usize = 3;
pub const ELFMAG3: c_char = 'F' as c_char;
pub const ELFMAG: &'static str = "\x7fELF";
pub const ELFMAG: &str = "\x7fELF";
pub const SELFMAG: usize = 4;
pub const EI_CLASS: usize = 4;
pub const ELFCLASSNONE: usize = 0;
@@ -196,6 +202,7 @@ pub const EV_NONE: usize = 0;
pub const EV_CURRENT: usize = 1;
pub const EV_NUM: usize = 2;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Shdr {
pub sh_name: Elf32_Word,
@@ -210,6 +217,7 @@ pub struct Elf32_Shdr {
pub sh_entsize: Elf32_Word,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Shdr {
pub sh_name: Elf64_Word,
@@ -289,6 +297,7 @@ pub const SHF_ORDERED: usize = 1 << 30;
pub const SHF_EXCLUDE: usize = 1 << 31;
pub const GRP_COMDAT: usize = 0x1;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Sym {
pub st_name: Elf32_Word,
@@ -299,6 +308,7 @@ pub struct Elf32_Sym {
pub st_shndx: Elf32_Section,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Sym {
pub st_name: Elf64_Word,
@@ -361,18 +371,21 @@ pub const STV_INTERNAL: usize = 1;
pub const STV_HIDDEN: usize = 2;
pub const STV_PROTECTED: usize = 3;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Rel {
pub r_offset: Elf32_Addr,
pub r_info: Elf32_Word,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Rel {
pub r_offset: Elf64_Addr,
pub r_info: Elf64_Xword,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Rela {
pub r_offset: Elf32_Addr,
@@ -380,6 +393,7 @@ pub struct Elf32_Rela {
pub r_addend: Elf32_Sword,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Rela {
pub r_offset: Elf64_Addr,
@@ -387,6 +401,7 @@ pub struct Elf64_Rela {
pub r_addend: Elf64_Sxword,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Phdr {
pub p_type: Elf32_Word,
@@ -399,6 +414,7 @@ pub struct Elf32_Phdr {
pub p_align: Elf32_Word,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Phdr {
pub p_type: Elf64_Word,
@@ -487,6 +503,7 @@ pub union Elf32_Dyn_Union {
d_ptr: Elf32_Addr,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Dyn {
pub d_tag: Elf32_Sword,
@@ -499,6 +516,7 @@ pub union Elf64_Dyn_Union {
d_ptr: Elf64_Addr,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Dyn {
pub d_tag: Elf64_Sxword,
@@ -740,6 +758,7 @@ pub const AT_L1D_CACHESHAPE: usize = 35;
pub const AT_L2_CACHESHAPE: usize = 36;
pub const AT_L3_CACHESHAPE: usize = 37;
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf32_Nhdr {
pub n_namesz: Elf32_Word,
@@ -747,6 +766,7 @@ pub struct Elf32_Nhdr {
pub n_type: Elf32_Word,
}
/// See <https://www.man7.org/linux/man-pages/man5/elf.5.html>.
#[repr(C)]
pub struct Elf64_Nhdr {
pub n_namesz: Elf64_Word,
@@ -754,8 +774,8 @@ pub struct Elf64_Nhdr {
pub n_type: Elf64_Word,
}
pub const ELF_NOTE_SOLARIS: &'static str = "SUNW Solaris";
pub const ELF_NOTE_GNU: &'static str = "GNU";
pub const ELF_NOTE_SOLARIS: &str = "SUNW Solaris";
pub const ELF_NOTE_GNU: &str = "GNU";
pub const ELF_NOTE_PAGESIZE_HINT: usize = 1;
@@ -943,8 +963,8 @@ pub const R_X86_64_IRELATIVE: usize = 37;
pub const R_X86_64_RELATIVE64: usize = 38;
pub const R_X86_64_NUM: usize = 39;
#[no_mangle]
pub extern "C" fn stupid_cbindgen_needs_a_function_that_holds_all_elf_structs(
#[unsafe(no_mangle)]
pub extern "C" fn _cbindgen_export_elf(
a: Elf32_Ehdr,
b: Elf64_Ehdr,
c: Elf32_Shdr,
+12
View File
@@ -0,0 +1,12 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/endian.h.html
#
# Spec quotations relating to includes:
# - "The <endian.h> header shall define the uint16_t, uint32_t, and uint64_t types as described in <stdint.h>."
# - "Inclusion of the <endian.h> header may also make visible all symbols from <stdint.h>."
sys_includes = ["stdint.h"]
include_guard = "_RELIBC_ENDIAN_H"
trailer = "#include <machine/endian.h>"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
+77
View File
@@ -0,0 +1,77 @@
//! `endian.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/endian.h.html>.
use crate::platform::types::{uint16_t, uint32_t, uint64_t};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn be16toh(x: uint16_t) -> uint16_t {
uint16_t::from_be(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn be32toh(x: uint32_t) -> uint32_t {
uint32_t::from_be(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn be64toh(x: uint64_t) -> uint64_t {
uint64_t::from_be(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htobe16(x: uint16_t) -> uint16_t {
x.to_be()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htobe32(x: uint32_t) -> uint32_t {
x.to_be()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htobe64(x: uint64_t) -> uint64_t {
x.to_be()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htole16(x: uint16_t) -> uint16_t {
x.to_le()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htole32(x: uint32_t) -> uint32_t {
x.to_le()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn htole64(x: uint64_t) -> uint64_t {
x.to_le()
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn le16toh(x: uint16_t) -> uint16_t {
uint16_t::from_le(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn le32toh(x: uint32_t) -> uint32_t {
uint32_t::from_le(x)
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/be16toh.html>.
#[unsafe(no_mangle)]
pub extern "C" fn le64toh(x: uint64_t) -> uint64_t {
uint64_t::from_le(x)
}
+5
View File
@@ -0,0 +1,5 @@
sys_includes = ["stdarg.h", "stdio.h", "features.h"]
include_guard = "_RELIBC_ERR_H"
language = "C"
no_includes = false
cpp_compat = true
+204
View File
@@ -0,0 +1,204 @@
//! `err.h` implementation.
//!
//! See <https://man.freebsd.org/cgi/man.cgi?err>
//!
//! `err.h` is a BSD extension to the C library which provides functions for printing formatted
//! errors. Errors are printed to [`stdio::stderr`] by default or to a file set by
//! [`err_set_file`]. This family of functions is non-portable, but it is also supported by `glibc`
//! and `musl`.
//!
//! The functions come in sets of three. Each of them print the program binary name (the last path
//! segment of `argv[0]`) and an optional user message along with these differences:
//! * No suffix: Prints an error message for ERRNO based on [`strerror`]
//! * `c` suffix: Prints an error message for an arbitrary error code
//! * `x` suffix: Does not print an error code
//!
//! For example, `err` does not have a suffix so it would print the program name, the user message,
//! and an error string for ERRNO. `errc` would operate in the same way except the functions takes
//! an error code for which to print an error string.
// Allow is intentional. Almost every line of the simple functions below are unsafe.
// unsafe_op_in_unsafe_fn only adds visual noise or a needless indentation here.
#![allow(unsafe_op_in_unsafe_fn)]
use core::{
ffi::{VaList as va_list, c_char, c_int},
ptr,
};
use crate::{
header::{
stdio::{self, FILE, fprintf, fputc, fputs, vfprintf},
stdlib::exit,
string::strerror,
},
platform::{self, ERRNO},
};
// Optional callback from user invoked on exit.
type ExitCallback = Option<unsafe extern "C" fn(c_int)>;
static mut on_exit: ExitCallback = None;
// Messages from this module are written to this sink.
static mut error_sink: *mut FILE = ptr::null_mut();
/// Set global [`FILE`] sink to write errors and warnings.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn err_set_file(fp: *mut FILE) {
if fp.is_null() {
error_sink = stdio::stderr;
} else {
error_sink = fp;
}
}
/// Set or remove a callback to invoke before exiting on error.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn err_set_exit(ef: ExitCallback) {
on_exit = ef;
}
/// Print a user message then an error message for [`ERRNO`] followed by exiting with `eval`.
///
/// The message format is `progname: fmt: strerror(ERRNO)`
///
/// # Return
/// Does not return. Exits with `eval` as an error code.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn err(eval: c_int, fmt: *const c_char, va_list: ...) -> ! {
let code = Some(ERRNO.get());
err_exit(eval, code, fmt, va_list)
}
/// Print a user message then an error message for `code` before exiting with `eval` as a return.
///
/// The message format is `progname: fmt: strerror(code)`
///
/// # Return
/// Exits with `eval` as an error code.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn errc(eval: c_int, code: c_int, fmt: *const c_char, va_list: ...) -> ! {
err_exit(eval, Some(code), fmt, va_list)
}
/// Print a user message then exits with `eval` as a return.
///
/// The message format is `progname: fmt`
///
/// # Return
/// Exits with `eval` as an error code.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn errx(eval: c_int, fmt: *const c_char, va_list: ...) -> ! {
err_exit(eval, None, fmt, va_list)
}
/// Print a user message and then an error message for [`ERRNO`].
///
/// The message format is `progname: fmt: strerror(ERRNO)`
#[unsafe(no_mangle)]
pub unsafe extern "C" fn warn(fmt: *const c_char, va_list: ...) {
let code = Some(ERRNO.get());
display_message(code, fmt, va_list);
}
/// Print a user message then an error message for `code`.
///
/// The message format is `progname: fmt: strerror(code)`
#[unsafe(no_mangle)]
pub unsafe extern "C" fn warnc(code: c_int, fmt: *const c_char, va_list: ...) {
display_message(Some(code), fmt, va_list);
}
/// Print a user message as a warning.
///
/// The message format is `progname: fmt`
#[unsafe(no_mangle)]
pub unsafe extern "C" fn warnx(fmt: *const c_char, va_list: ...) {
display_message(None, fmt, va_list);
}
/// See [`err`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn verr(eval: c_int, fmt: *const c_char, args: va_list) -> ! {
let code = Some(ERRNO.get());
err_exit(eval, code, fmt, args);
}
/// See [`errc`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn verrc(eval: c_int, code: c_int, fmt: *const c_char, args: va_list) -> ! {
err_exit(eval, Some(code), fmt, args)
}
/// See [`errx`];
#[unsafe(no_mangle)]
pub unsafe extern "C" fn verrx(eval: c_int, fmt: *const c_char, args: va_list) -> ! {
err_exit(eval, None, fmt, args)
}
/// See [`warn`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn vwarn(fmt: *const c_char, args: va_list) {
let code = Some(ERRNO.get());
display_message(code, fmt, args);
}
/// See [`warnc`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn vwarnc(code: c_int, fmt: *const c_char, args: va_list) {
display_message(Some(code), fmt, args);
}
/// See [`warnx`].
#[unsafe(no_mangle)]
pub unsafe extern "C" fn vwarnx(fmt: *const c_char, args: va_list) {
display_message(None, fmt, args);
}
// Write error messages for err and warn to the currently set sink.
unsafe fn display_message(code: Option<c_int>, fmt: *const c_char, args: va_list) {
// SAFETY:
// * error_sink is only null once on start but otherwise always stderr or a user set file
// * User is trusted to pass in a valid file pointer if err_set_file is used
if error_sink.is_null() {
error_sink = stdio::stderr;
}
let sink = error_sink;
// "progname:" is always printed
// SAFETY:
// * program_invocation_short_name is never null as it is set on start
// * program_invocation_short_name is not globally mutable so the user can't mangle it
fprintf(
sink,
c"%s".as_ptr(),
platform::program_invocation_short_name,
);
// Print user message if any
if !fmt.is_null() {
fputs(c": ".as_ptr(), sink);
vfprintf(sink, fmt, args);
}
// Print error message for non-x functions
if let Some(code) = code {
let message = strerror(code);
fprintf(sink, c": %s".as_ptr(), message);
}
// Always write new line
fputc(b'\n'.into(), sink);
}
// Write an error message as per err and then exit.
unsafe fn err_exit(eval: c_int, code: Option<c_int>, fmt: *const c_char, args: va_list) -> ! {
display_message(code, fmt, args);
if let Some(callback) = on_exit {
// errx will hit the unwrap.
callback(code.unwrap_or_else(|| ERRNO.get()));
}
exit(eval);
}
+9 -1
View File
@@ -1,4 +1,12 @@
sys_includes = ["bits/errno.h"]
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/errno.h.html
#
# There are no spec quotations relating to includes
sys_includes = []
after_includes = """
#define errno (*__errno_location())
#define program_invocation_name (*__program_invocation_name())
#define program_invocation_short_name (*__program_invocation_short_name())
"""
include_guard = "_RELIBC_ERRNO_H"
language = "C"
style = "Tag"
+65 -24
View File
@@ -1,38 +1,47 @@
//! errno implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/errno.h.html
//! `errno.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/errno.h.html>.
use crate::platform::{self, types::*};
use crate::platform::{
self,
types::{c_char, c_int},
};
//TODO: Consider removing, provided for compatibility with newlib
#[no_mangle]
pub unsafe extern "C" fn __errno() -> *mut c_int {
#[unsafe(no_mangle)]
pub extern "C" fn __errno() -> *mut c_int {
__errno_location()
}
#[no_mangle]
pub unsafe extern "C" fn __errno_location() -> *mut c_int {
&mut platform::errno
/// Provide a pointer to relibc's internal `errno` variable.
///
/// This is the basis of the C-facing macro definition of `errno`, and should
/// not be used directly.
#[unsafe(no_mangle)]
pub extern "C" fn __errno_location() -> *mut c_int {
platform::ERRNO.as_ptr()
}
#[no_mangle]
/// Get the name used to invoke the program, similar to `argv[0]`.
///
/// This provides the basis for the C-facing macro definition of
/// `program_invocation_name`, and should not be used directly.
///
/// The `program_invocation_name` variable is a GNU extension.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __program_invocation_name() -> *mut *mut c_char {
&mut platform::inner_argv[0]
&raw mut platform::program_invocation_name
}
#[no_mangle]
/// Get the directory-less name used to invoke the program.
///
/// This provides the basis for the C-facing macro definition of
/// `program_invocation_short_name`, and should not be used directly.
///
/// The `program_invocation_short_name` variable is a GNU extension.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn __program_invocation_short_name() -> *mut *mut c_char {
let mut ptr = platform::inner_argv[0];
let mut slash_ptr = ptr;
loop {
let b = *ptr as u8;
if b == 0 {
return &mut slash_ptr;
} else {
ptr = ptr.add(1);
if b == b'/' {
slash_ptr = ptr;
}
}
}
&raw mut platform::program_invocation_short_name
}
pub const EPERM: c_int = 1; /* Operation not permitted */
@@ -130,6 +139,7 @@ pub const ENOPROTOOPT: c_int = 92; /* Protocol not available */
pub const EPROTONOSUPPORT: c_int = 93; /* Protocol not supported */
pub const ESOCKTNOSUPPORT: c_int = 94; /* Socket type not supported */
pub const EOPNOTSUPP: c_int = 95; /* Operation not supported on transport endpoint */
pub const ENOTSUP: c_int = EOPNOTSUPP; /* Not supported */
pub const EPFNOSUPPORT: c_int = 96; /* Protocol family not supported */
pub const EAFNOSUPPORT: c_int = 97; /* Address family not supported by protocol */
pub const EADDRINUSE: c_int = 98; /* Address already in use */
@@ -167,7 +177,8 @@ pub const EKEYREJECTED: c_int = 129; /* Key was rejected by service */
pub const EOWNERDEAD: c_int = 130; /* Owner died */
pub const ENOTRECOVERABLE: c_int = 131; /* State not recoverable */
pub static STR_ERROR: [&'static str; 132] = [
/// String representations for the respective `errno` values.
pub(crate) const STR_ERROR: [&str; 132] = [
"Success",
"Operation not permitted",
"No such file or directory",
@@ -301,3 +312,33 @@ pub static STR_ERROR: [&'static str; 132] = [
"Owner died",
"State not recoverable",
];
/// Longest error message length
pub(crate) const STRERROR_MAX: usize = {
// Number of digits of the max value of this platform's c_int
let digits = {
let mut len = 0;
let mut i = c_int::MAX;
while i > 0 {
i /= 10;
len += 1;
}
len
};
let mut longest: usize = "Unknown error: ".len() + digits + 1;
let mut i = 0;
while i < STR_ERROR.len() {
let len = STR_ERROR[i].len() + 1;
if len > longest {
longest = len;
}
i += 1;
}
longest
};
+1 -6
View File
@@ -1,14 +1,9 @@
sys_includes = ["stdarg.h", "sys/types.h"]
sys_includes = ["stdarg.h", "sys/stat.h", "unistd.h"]
include_guard = "_RELIBC_FCNTL_H"
trailer = "#include <bits/fcntl.h>"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
[defines]
"target_os=linux" = "__linux__"
"target_os=redox" = "__redox__"
[enum]
prefix_with_name = true
+15 -1
View File
@@ -1,4 +1,4 @@
use crate::platform::types::*;
use crate::platform::types::c_int;
pub const O_RDONLY: c_int = 0x0000;
pub const O_WRONLY: c_int = 0x0001;
@@ -6,6 +6,7 @@ pub const O_RDWR: c_int = 0x0002;
pub const O_ACCMODE: c_int = 0x0003;
pub const O_CREAT: c_int = 0x0040;
pub const O_EXCL: c_int = 0x0080;
pub const O_NOCTTY: c_int = 0x0100;
pub const O_TRUNC: c_int = 0x0200;
pub const O_APPEND: c_int = 0x0400;
pub const O_NONBLOCK: c_int = 0x0800;
@@ -15,3 +16,16 @@ pub const O_CLOEXEC: c_int = 0x8_0000;
pub const O_PATH: c_int = 0x20_0000;
pub const FD_CLOEXEC: c_int = 0x8_0000;
// Defined for compatibility
pub const O_NDELAY: c_int = O_NONBLOCK;
// Flags for capability based "at" functions
pub const AT_FDCWD: c_int = -100;
pub const AT_SYMLINK_NOFOLLOW: c_int = 0x100;
// AT_EACCESS only used for faccessat
pub const AT_EACCESS: c_int = 0x200;
// AT_REMOVEDIR only used for unlinkat
pub const AT_REMOVEDIR: c_int = 0x200;
pub const AT_SYMLINK_FOLLOW: c_int = 0x400;
pub const AT_EMPTY_PATH: c_int = 0x1000;
+100 -13
View File
@@ -1,12 +1,23 @@
//! fcntl implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/fcntl.h.html
//! `fcntl.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fcntl.h.html>.
use core::num::NonZeroU64;
use crate::{
c_str::CStr,
platform::{types::*, Pal, Sys},
error::ResultExt,
header::unistd::close,
platform::{
Pal, Sys,
types::{c_char, c_int, c_short, c_ulonglong, mode_t, off_t, pid_t},
},
};
pub use self::sys::*;
use super::errno::EINVAL;
#[cfg(target_os = "linux")]
#[path = "linux.rs"]
pub mod sys;
@@ -23,16 +34,29 @@ pub const F_SETFL: c_int = 4;
pub const F_GETLK: c_int = 5;
pub const F_SETLK: c_int = 6;
pub const F_SETLKW: c_int = 7;
pub const F_OFD_GETLK: c_int = 36;
pub const F_OFD_SETLK: c_int = 37;
pub const F_OFD_SETLKW: c_int = 38;
pub const F_DUPFD_CLOEXEC: c_int = 1030;
pub const F_RDLCK: c_int = 0;
pub const F_WRLCK: c_int = 1;
pub const F_UNLCK: c_int = 2;
#[no_mangle]
pub const F_ULOCK: c_int = 0;
pub const F_LOCK: c_int = 1;
pub const F_TLOCK: c_int = 2;
pub const F_TEST: c_int = 3;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/creat.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn creat(path: *const c_char, mode: mode_t) -> c_int {
sys_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode)
unsafe { open(path, O_WRONLY | O_CREAT | O_TRUNC, mode) }
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fcntl.h.html>.
#[repr(C)]
#[derive(Clone, Copy, Default)]
pub struct flock {
pub l_type: c_short,
pub l_whence: c_short,
@@ -40,16 +64,79 @@ pub struct flock {
pub l_len: off_t,
pub l_pid: pid_t,
}
#[no_mangle]
pub extern "C" fn sys_fcntl(fildes: c_int, cmd: c_int, arg: c_int) -> c_int {
Sys::fcntl(fildes, cmd, arg)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fcntl.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn fcntl(fildes: c_int, cmd: c_int, mut __valist: ...) -> c_int {
// c_ulonglong
let arg = match cmd {
F_DUPFD | F_SETFD | F_SETFL | F_GETLK | F_SETLK | F_SETLKW | F_OFD_GETLK | F_OFD_SETLK
| F_OFD_SETLKW | F_DUPFD_CLOEXEC => unsafe { __valist.next_arg::<c_ulonglong>() },
_ => 0,
};
if cmd == F_DUPFD_CLOEXEC {
let new_fd = Sys::fcntl(fildes, F_DUPFD_CLOEXEC, arg).or_minus_one_errno();
if new_fd >= 0 {
return new_fd;
}
let new_fd = Sys::fcntl(fildes, F_DUPFD, arg).or_minus_one_errno();
if new_fd < 0 {
return -1;
}
if Sys::fcntl(new_fd, F_SETFD, FD_CLOEXEC as c_ulonglong).or_minus_one_errno() < 0 {
let _ = close(new_fd);
return -1;
}
return new_fd;
}
Sys::fcntl(fildes, cmd, arg).or_minus_one_errno()
}
#[no_mangle]
pub unsafe extern "C" fn sys_open(path: *const c_char, oflag: c_int, mode: mode_t) -> c_int {
let path = CStr::from_ptr(path);
Sys::open(path, oflag, mode)
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/open.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn open(path: *const c_char, oflag: c_int, mut __valist: ...) -> c_int {
unsafe { openat(AT_FDCWD, path, oflag, __valist) }
}
#[no_mangle]
pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_fcntl(a: flock) {}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/openat.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn openat(
fd: c_int,
path: *const c_char,
oflag: c_int,
mut __valist: ...
) -> c_int {
let mode = if oflag & O_CREAT == O_CREAT
/* || oflag & O_TMPFILE == O_TMPFILE */
{
unsafe { __valist.next_arg::<mode_t>() }
} else {
0
};
let path = unsafe { CStr::from_ptr(path) };
Sys::openat(fd, path, oflag, mode).or_minus_one_errno()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn cbindgen_stupid_struct_user_for_fcntl(_: flock) {}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn posix_fallocate(fd: c_int, offset: off_t, length: off_t) -> c_int {
// Length can't be zero and offset must be positive.
let Ok(offset) = offset.try_into() else {
return EINVAL;
};
let Some(length) = length.try_into().ok().and_then(NonZeroU64::new) else {
return EINVAL;
};
// posix_fallocate does not set errno but instead returns it.
Sys::posix_fallocate(fd, offset, length)
.err()
.map(|e| e.0)
.unwrap_or_default()
}
+18 -1
View File
@@ -1,4 +1,4 @@
use crate::platform::types::*;
use crate::platform::types::c_int;
pub const O_RDONLY: c_int = 0x0001_0000;
pub const O_WRONLY: c_int = 0x0002_0000;
@@ -10,6 +10,7 @@ pub const O_SHLOCK: c_int = 0x0010_0000;
pub const O_EXLOCK: c_int = 0x0020_0000;
pub const O_ASYNC: c_int = 0x0040_0000;
pub const O_FSYNC: c_int = 0x0080_0000;
pub const O_SYNC: c_int = O_FSYNC;
pub const O_CLOEXEC: c_int = 0x0100_0000;
pub const O_CREAT: c_int = 0x0200_0000;
pub const O_TRUNC: c_int = 0x0400_0000;
@@ -21,3 +22,19 @@ pub const O_SYMLINK: c_int = 0x4000_0000;
pub const O_NOFOLLOW: c_int = -0x8000_0000;
pub const FD_CLOEXEC: c_int = 0x0100_0000;
pub const O_NOCTTY: c_int = 0x00000200;
// Defined for compatibility
pub const O_NDELAY: c_int = O_NONBLOCK;
// Flags for capability based "at" functions
pub const AT_FDCWD: c_int = -100;
pub const AT_SYMLINK_NOFOLLOW: c_int = 0x200;
pub const AT_REMOVEDIR: c_int = 0x200;
// Used by linkat()
pub const AT_SYMLINK_FOLLOW: c_int = 0x2000;
// nonstandard extension, but likely to be in a future standard
pub const AT_EMPTY_PATH: c_int = 0x4000;
// only used for faccessat()
pub const AT_EACCESS: c_int = 0x400;
+60 -1
View File
@@ -1,5 +1,64 @@
sys_includes = ["sys/types.h", "bits/float.h"]
sys_includes = ["sys/types.h"]
include_guard = "_RELIBC_FLOAT_H"
after_includes = """
#ifndef _RELIBC_BITS_FLOAT_H
#define _RELIBC_BITS_FLOAT_H
#define FLT_ROUNDS (flt_rounds())
// Shamelessly copy pasted from musl:
#define FLT_TRUE_MIN 1.40129846432481707092e-45F
#define FLT_MIN 1.17549435082228750797e-38F
#define FLT_MAX 3.40282346638528859812e+38F
#define FLT_EPSILON 1.1920928955078125e-07F
#define FLT_MANT_DIG 24
#define FLT_MIN_EXP (-125)
#define FLT_MAX_EXP 128
#define FLT_HAS_SUBNORM 1
#define FLT_DIG 6
#define FLT_DECIMAL_DIG 9
#define FLT_MIN_10_EXP (-37)
#define FLT_MAX_10_EXP 38
#define DBL_TRUE_MIN 4.94065645841246544177e-324
#define DBL_MIN 2.22507385850720138309e-308
#define DBL_MAX 1.79769313486231570815e+308
#define DBL_EPSILON 2.22044604925031308085e-16
#define DBL_MANT_DIG 53
#define DBL_MIN_EXP (-1021)
#define DBL_MAX_EXP 1024
#define DBL_HAS_SUBNORM 1
#define DBL_DIG 15
#define DBL_DECIMAL_DIG 17
#define DBL_MIN_10_EXP (-307)
#define DBL_MAX_10_EXP 308
#define LDBL_HAS_SUBNORM 1
#define LDBL_DECIMAL_DIG DECIMAL_DIG
#define FLT_EVAL_METHOD 0
#define DECIMAL_DIG 21
// TODO: Support more architectures than x86_64 here:
#define LDBL_TRUE_MIN 3.6451995318824746025e-4951L
#define LDBL_MIN 3.3621031431120935063e-4932L
#define LDBL_MAX 1.1897314953572317650e+4932L
#define LDBL_EPSILON 1.0842021724855044340e-19L
#define LDBL_MANT_DIG 64
#define LDBL_MIN_EXP (-16381)
#define LDBL_MAX_EXP 16384
#define LDBL_DIG 18
#define LDBL_MIN_10_EXP (-4931)
#define LDBL_MAX_10_EXP 4932
#endif // _RELIBC_BITS_FLOAT_H
"""
language = "C"
style = "Tag"
no_includes = true
+9 -6
View File
@@ -1,16 +1,19 @@
//! float.h implementation for Redox, following
//! http://pubs.opengroup.org/onlinepubs/7908799/xsh/float.h.html
//! `float.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/float.h.html>.
use crate::{
header::_fenv::{fegetround, FE_TONEAREST},
platform::types::*,
header::_fenv::{FE_TONEAREST, fegetround},
platform::types::c_int,
};
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/float.h.html>.
pub const FLT_RADIX: c_int = 2;
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/float.h.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn flt_rounds() -> c_int {
match fegetround() {
match unsafe { fegetround() } {
FE_TONEAREST => 1,
_ => -1,
}
+3
View File
@@ -1,3 +1,6 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fnmatch.h.html
#
# There are no spec quotations relating to includes
include_guard = "_RELIBC_FNMATCH_H"
language = "C"
style = "Tag"
+46 -28
View File
@@ -1,12 +1,15 @@
//! fnmatch implementation
//! `fnmatch.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fnmatch.h.html>.
use alloc::{borrow::Cow, vec::Vec};
use core::slice;
use crate::platform::types::*;
use crate::platform::types::{c_char, c_int};
use posix_regex::{
compile::{Collation, Range, Token},
PosixRegex,
compile::{Collation, Range, Token},
tree::{Tree, TreeBuilder},
};
const ONCE: Range = Range(1, Some(1));
@@ -17,9 +20,10 @@ pub const FNM_NOESCAPE: c_int = 1;
pub const FNM_PATHNAME: c_int = 2;
pub const FNM_PERIOD: c_int = 4;
pub const FNM_CASEFOLD: c_int = 8;
pub const FNM_IGNORECASE: c_int = FNM_CASEFOLD;
// TODO: FNM_EXTMATCH
unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Vec<(Token, Range)> {
unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Tree {
fn any(leading: bool, flags: c_int) -> Token {
let mut list = Vec::new();
if flags & FNM_PATHNAME == FNM_PATHNAME {
@@ -38,24 +42,34 @@ unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Vec<(Token, Range)>
c == b'/' && flags & FNM_PATHNAME == FNM_PATHNAME
}
let mut tokens = Vec::new();
let mut leading = true;
let mut need_collapsing = false;
while *pattern != 0 {
let mut builder = TreeBuilder::default();
builder.start_internal(Token::Root, Range(1, Some(1)));
builder.start_internal(Token::Alternative, Range(1, Some(1)));
while unsafe { *pattern != 0 } {
let was_leading = leading;
leading = false;
let c = *pattern;
pattern = pattern.offset(1);
let c = unsafe { *pattern };
pattern = unsafe { pattern.offset(1) };
tokens.push(match c {
match (c == b'*', need_collapsing) {
(true, true) => continue,
(true, false) => need_collapsing = true,
(false, _) => need_collapsing = false,
}
let (token, range) = match c {
b'\\' if flags & FNM_NOESCAPE == FNM_NOESCAPE => {
let c = *pattern;
let c = unsafe { *pattern };
if c == 0 {
// Trailing backslash. Maybe error here?
break;
}
pattern = pattern.offset(1);
pattern = unsafe { pattern.offset(1) };
leading = is_leading(flags, c);
(Token::Char(c), ONCE)
}
@@ -63,24 +77,24 @@ unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Vec<(Token, Range)>
b'*' => (any(was_leading, flags), Range(0, None)),
b'[' => {
let mut list: Vec<Collation> = Vec::new();
let invert = if *pattern == b'!' {
pattern = pattern.offset(1);
let invert = if unsafe { *pattern == b'!' } {
pattern = unsafe { pattern.offset(1) };
true
} else {
false
};
loop {
let mut c = *pattern;
let mut c = unsafe { *pattern };
if c == 0 {
break;
}
pattern = pattern.offset(1);
pattern = unsafe { pattern.offset(1) };
match c {
b']' => break,
b'\\' => {
c = *pattern;
pattern = pattern.offset(1);
c = unsafe { *pattern };
pattern = unsafe { pattern.offset(1) };
if c == 0 {
// Trailing backslash. Maybe error?
break;
@@ -88,9 +102,9 @@ unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Vec<(Token, Range)>
}
_ => (),
}
if *pattern == b'-' && *pattern.offset(1) != 0 {
let end = *pattern.offset(1);
pattern = pattern.offset(2);
if unsafe { *pattern == b'-' && *pattern.offset(1) != 0 } {
let end = unsafe { *pattern.offset(1) };
pattern = unsafe { pattern.offset(2) };
for c in c..=end {
if can_push(was_leading, flags, c) {
list.push(Collation::Char(c));
@@ -108,12 +122,17 @@ unsafe fn tokenize(mut pattern: *const u8, flags: c_int) -> Vec<(Token, Range)>
leading = is_leading(flags, c);
(Token::Char(c), ONCE)
}
})
};
builder.leaf(token, range);
}
tokens
builder.leaf(Token::End, ONCE);
builder.finish_internal();
builder.finish_internal();
builder.finish()
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/fnmatch.html>.
#[unsafe(no_mangle)]
#[linkage = "weak"] // often redefined in GNU programs
pub unsafe extern "C" fn fnmatch(
pattern: *const c_char,
@@ -121,15 +140,14 @@ pub unsafe extern "C" fn fnmatch(
flags: c_int,
) -> c_int {
let mut len = 0;
while *input.offset(len) != 0 {
while unsafe { *input.offset(len) != 0 } {
len += 1;
}
let input = slice::from_raw_parts(input as *const u8, len as usize);
let input = unsafe { slice::from_raw_parts(input.cast::<u8>(), len as usize) };
let mut tokens = tokenize(pattern as *const u8, flags);
tokens.push((Token::End, ONCE));
let tokens = unsafe { tokenize(pattern.cast::<u8>(), flags) };
if PosixRegex::new(Cow::Owned(vec![tokens]))
if PosixRegex::new(Cow::Owned(tokens))
.case_insensitive(flags & FNM_CASEFOLD == FNM_CASEFOLD)
.matches_exact(input)
.is_some()
+98 -72
View File
@@ -1,20 +1,27 @@
//! getopt implementation for relibc
//! `getopt.h` implementation.
//!
//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getopt.3.html>.
use crate::{
header::{
stdio, string,
unistd::{optarg, opterr, optind, optopt},
},
platform::types::*,
platform::types::{c_char, c_int, size_t},
};
use core::ptr;
/// cbindgen:ignore
static mut CURRENT_OPT: *mut c_char = ptr::null_mut();
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getopt.3.html>.
pub const no_argument: c_int = 0;
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getopt.3.html>.
pub const required_argument: c_int = 1;
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getopt.3.html>.
pub const optional_argument: c_int = 2;
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getopt.3.html>.
#[repr(C)]
pub struct option {
name: *const c_char,
@@ -23,8 +30,10 @@ pub struct option {
val: c_int,
}
#[no_mangle]
#[linkage = "weak"] // often redefined in GNU programs
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getopt.3.html>.
///
/// Functions the same as `getopt` but also accepts long options.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getopt_long(
argc: c_int,
argv: *const *mut c_char,
@@ -33,94 +42,109 @@ pub unsafe extern "C" fn getopt_long(
longindex: *mut c_int,
) -> c_int {
// if optarg is not set, we still don't want the previous value leaking
optarg = ptr::null_mut();
// handle reinitialization request
if optind == 0 {
optind = 1;
CURRENT_OPT = ptr::null_mut();
unsafe {
optarg = ptr::null_mut();
}
if CURRENT_OPT.is_null() || *CURRENT_OPT == 0 {
if optind >= argc {
// handle reinitialization request
unsafe {
if optind == 0 {
optind = 1;
CURRENT_OPT = ptr::null_mut();
}
}
if unsafe { CURRENT_OPT.is_null() || *CURRENT_OPT == 0 } {
if unsafe { optind >= argc } {
-1
} else {
let current_arg = *argv.offset(optind as isize);
if current_arg.is_null()
|| *current_arg != b'-' as c_char
|| *current_arg.offset(1) == 0
{
let current_arg = unsafe { *argv.offset(optind as isize) };
if unsafe {
current_arg.is_null()
|| *current_arg != b'-' as c_char
|| *current_arg.offset(1) == 0
} {
-1
} else if string::strcmp(current_arg, c_str!("--").as_ptr()) == 0 {
optind += 1;
} else if unsafe { string::strcmp(current_arg, c"--".as_ptr()) == 0 } {
unsafe {
optind += 1;
}
-1
} else {
// remove the '-'
let current_arg = current_arg.offset(1);
let current_arg = unsafe { current_arg.offset(1) };
if *current_arg == b'-' as c_char && !longopts.is_null() {
let current_arg = current_arg.offset(1);
if unsafe { *current_arg == b'-' as c_char } && !longopts.is_null() {
let current_arg = unsafe { current_arg.offset(1) };
// is a long option
for i in 0.. {
let opt = &*longopts.offset(i);
let opt = unsafe { &*longopts.offset(i) };
if opt.name.is_null() {
break;
}
let mut end = 0;
while {
let c = *current_arg.offset(end);
let c = unsafe { *current_arg.offset(end) };
c != 0 && c != b'=' as c_char
} {
end += 1;
}
if string::strncmp(current_arg, opt.name, end as size_t) == 0 {
optind += 1;
*longindex = i as c_int;
if unsafe { string::strncmp(current_arg, opt.name, end as size_t) == 0 } {
unsafe {
optind += 1;
if !longindex.is_null() {
*longindex = i as c_int;
}
}
if opt.has_arg == optional_argument {
if *current_arg.offset(end) == b'=' as c_char {
optarg = current_arg.offset(end + 1);
unsafe {
if *current_arg.offset(end) == b'=' as c_char {
optarg = current_arg.offset(end + 1);
}
}
} else if opt.has_arg == required_argument {
if *current_arg.offset(end) == b'=' as c_char {
optarg = current_arg.offset(end + 1);
} else if optind < argc {
optarg = *argv.offset(optind as isize);
optind += 1;
} else if *optstring == b':' as c_char {
return b':' as c_int;
} else {
stdio::fputs(*argv as _, &mut *stdio::stderr);
stdio::fputs(
": option '--\0".as_ptr() as _,
&mut *stdio::stderr,
);
stdio::fputs(current_arg, &mut *stdio::stderr);
stdio::fputs(
"' requires an argument\n\0".as_ptr() as _,
&mut *stdio::stderr,
);
return b'?' as c_int;
unsafe {
if *current_arg.offset(end) == b'=' as c_char {
optarg = current_arg.offset(end + 1);
} else if optind < argc {
optarg = *argv.offset(optind as isize);
optind += 1;
} else if *optstring == b':' as c_char {
return c_int::from(b':');
} else {
stdio::fputs((*argv).cast_const(), &raw mut *stdio::stderr);
stdio::fputs(
c": option '--".as_ptr().cast(),
&raw mut *stdio::stderr,
);
stdio::fputs(current_arg, &raw mut *stdio::stderr);
stdio::fputs(
c"' requires an argument\n".as_ptr().cast(),
&raw mut *stdio::stderr,
);
return c_int::from(b'?');
}
}
}
if opt.flag.is_null() {
return opt.val;
} else {
*opt.flag = opt.val;
unsafe { *opt.flag = opt.val };
return 0;
}
}
}
}
parse_arg(argc, argv, current_arg, optstring)
unsafe { parse_arg(argc, argv, current_arg, optstring) }
}
}
} else {
parse_arg(argc, argv, CURRENT_OPT, optstring)
unsafe { parse_arg(argc, argv, CURRENT_OPT, optstring) }
}
}
@@ -130,35 +154,35 @@ unsafe fn parse_arg(
current_arg: *mut c_char,
optstring: *const c_char,
) -> c_int {
let update_current_opt = || {
let update_current_opt = || unsafe {
CURRENT_OPT = current_arg.offset(1);
if *CURRENT_OPT == 0 {
optind += 1;
}
};
let print_error = |desc: &[u8]| {
let print_error = |desc: &[u8]| unsafe {
// NOTE: we don't use fprintf to get around the usage of va_list
stdio::fputs(*argv as _, &mut *stdio::stderr);
stdio::fputs(desc.as_ptr() as _, &mut *stdio::stderr);
stdio::fputc(*current_arg as _, &mut *stdio::stderr);
stdio::fputc(b'\n' as _, &mut *stdio::stderr);
stdio::fputs((*argv).cast_const(), &raw mut *stdio::stderr);
stdio::fputs(desc.as_ptr().cast(), &raw mut *stdio::stderr);
stdio::fputc((*current_arg).into(), &raw mut *stdio::stderr);
stdio::fputc(b'\n'.into(), &raw mut *stdio::stderr);
};
match find_option(*current_arg, optstring) {
match unsafe { find_option(*current_arg, optstring) } {
Some(GetoptOption::Flag) => {
update_current_opt();
*current_arg as c_int
unsafe { c_int::from(*current_arg) }
}
Some(GetoptOption::OptArg) => {
CURRENT_OPT = b"\0".as_ptr() as _;
Some(GetoptOption::OptArg) => unsafe {
CURRENT_OPT = c"".as_ptr().cast_mut();
if *current_arg.offset(1) == 0 {
optind += 2;
if optind > argc {
CURRENT_OPT = ptr::null_mut();
optopt = *current_arg as c_int;
optopt = c_int::from(*current_arg);
let errch = if *optstring == b':' as c_char {
b':'
} else {
@@ -168,29 +192,31 @@ unsafe fn parse_arg(
b'?'
};
errch as c_int
c_int::from(errch)
} else {
optarg = *argv.offset(optind as isize - 1);
*current_arg as c_int
c_int::from(*current_arg)
}
} else {
optarg = current_arg.offset(1);
optind += 1;
*current_arg as c_int
c_int::from(*current_arg)
}
}
},
None => {
// couldn't find the given option in optstring
if opterr != 0 {
if unsafe { opterr != 0 } {
print_error(b": illegal option -- \0");
}
update_current_opt();
optopt = *current_arg as c_int;
b'?' as c_int
unsafe {
optopt = c_int::from(*current_arg);
}
c_int::from(b'?')
}
}
}
@@ -203,9 +229,9 @@ enum GetoptOption {
unsafe fn find_option(ch: c_char, optstring: *const c_char) -> Option<GetoptOption> {
let mut i = 0;
while *optstring.offset(i) != 0 {
if *optstring.offset(i) == ch {
let result = if *optstring.offset(i + 1) == b':' as c_char {
while unsafe { *optstring.offset(i) != 0 } {
if unsafe { *optstring.offset(i) == ch } {
let result = if unsafe { *optstring.offset(i + 1) == b':' as c_char } {
GetoptOption::OptArg
} else {
GetoptOption::Flag
+16
View File
@@ -0,0 +1,16 @@
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/glob.h.html
#
# Spec quotations relating to includes:
# - "The <glob.h> header shall define the size_t type as described in <sys/types.h>."
#
# size_t is defined in stddef.h (sys/types.h gets size_t by including stddef.h)
# just use stddef.h to avoid importing all of sys/types.h
sys_includes = ["stddef.h"]
include_guard = "_RELIBC_GLOB_H"
language = "C"
style = "type"
no_includes = true
cpp_compat = true
[enum]
prefix_with_name = true
+401
View File
@@ -0,0 +1,401 @@
//! `glob.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/glob.h.html>.
use core::ptr;
use alloc::{boxed::Box, vec::Vec};
use crate::{
c_str::{CStr, CString},
header::{
dirent::{closedir, opendir, readdir},
errno::*,
fnmatch::{FNM_NOESCAPE, FNM_PERIOD, fnmatch},
sys_stat::{S_IFDIR, S_IFMT, stat},
},
platform::{
self,
types::{c_char, c_int, c_uchar, c_void, size_t},
},
};
// Cause glob() to return on error
pub const GLOB_ERR: c_int = 0x0001;
// Each pathname that is a directory that matches pattern has a slash appended
pub const GLOB_MARK: c_int = 0x0002;
// Do not sort returned pathnames
pub const GLOB_NOSORT: c_int = 0x0004;
// Add gl_offs amount of null pointers to the beginning of `gl_pathv`
pub const GLOB_DOOFFS: c_int = 0x0008;
// If pattern does not match, return a list containing only pattern
pub const GLOB_NOCHECK: c_int = 0x0010;
// Append generated pathnames to those previously obtained
pub const GLOB_APPEND: c_int = 0x0020;
// Disable backslash escaping
pub const GLOB_NOESCAPE: c_int = 0x0040;
// Allow wildcards to match '.' (GNU extension)
pub const GLOB_PERIOD: c_int = 0x0080;
// Attempt to allocate memory failed
pub const GLOB_NOSPACE: c_int = 1;
// Scan was stopped because GLOB_ERR was set or `errfunc` returned non-zero
pub const GLOB_ABORTED: c_int = 2;
// Pattern does not match any existing pathname, and GLOB_NOCHECK was not set
pub const GLOB_NOMATCH: c_int = 3;
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/glob.h.html>.
#[derive(Debug)]
#[repr(C)]
pub struct glob_t {
pub gl_pathc: size_t, // Count of paths matched by pattern (POSIX required field)
pub gl_offs: size_t, // Slots to reserve at the beginning of gl_pathv (POSIX required field)
pub gl_pathv: *mut *mut c_char, // Pointer to list of matched pathnames (POSIX required field)
// Opaque pointer to allocation data
__opaque: *mut c_void, // Vec<*mut c_char>
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/glob.html>.
#[linkage = "weak"] // GNU prefers its own glob e.g. in Make
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glob(
pattern: *const c_char,
flags: c_int,
errfunc: Option<unsafe extern "C" fn(epath: *const c_char, eerrno: c_int) -> c_int>,
pglob: *mut glob_t,
) -> c_int {
if flags & GLOB_APPEND != GLOB_APPEND {
unsafe {
(*pglob).gl_pathc = 0;
(*pglob).gl_pathv = ptr::null_mut();
(*pglob).__opaque = ptr::null_mut();
}
}
let glob_expr = unsafe { CStr::from_ptr(pattern) };
if glob_expr.to_bytes() == b"" {
return GLOB_NOMATCH;
}
let base_path = unsafe {
CStr::from_bytes_with_nul_unchecked(if glob_expr.to_bytes().first() == Some(&b'/') {
b"/\0"
} else {
b"\0"
})
};
let errfunc = match errfunc {
Some(f) => f,
None => default_errfunc,
};
// Do the globbing
let mut results = match inner_glob(&base_path, &glob_expr, flags, errfunc) {
Ok(res) => res,
Err(e) => return e,
};
// Handle GLOB_NOCHECK and no matches
if results.is_empty() {
if flags & GLOB_NOCHECK == GLOB_NOCHECK {
results.push(glob_expr.to_owned_cstring());
} else {
return GLOB_NOMATCH;
}
}
// Handle GLOB_NOSORT
if flags & GLOB_NOSORT != GLOB_NOSORT {
results.sort();
}
// Set gl_pathc
if flags & GLOB_APPEND == GLOB_APPEND {
unsafe {
(*pglob).gl_pathc += results.len();
}
} else {
unsafe {
(*pglob).gl_pathc = results.len();
}
}
let mut pathv: Box<Vec<*mut c_char>>;
if flags & GLOB_APPEND == GLOB_APPEND {
pathv = unsafe { Box::from_raw((*pglob).__opaque.cast()) };
pathv.pop(); // Remove NULL from end
} else {
pathv = Box::new(Vec::new());
if flags & GLOB_DOOFFS == GLOB_DOOFFS {
let gl_offs = unsafe { (*pglob).gl_offs };
pathv.reserve(gl_offs);
for _ in 0..gl_offs {
pathv.push(ptr::null_mut());
}
}
}
pathv.reserve_exact(results.len() + 1);
pathv.extend(results.into_iter().map(|s| s.into_raw()));
pathv.push(ptr::null_mut());
unsafe {
(*pglob).gl_pathv = pathv.as_ptr().cast_mut();
(*pglob).__opaque = Box::into_raw(pathv).cast();
}
0
}
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/glob.html>.
#[linkage = "weak"] // GNU prefers its own glob e.g. in Make
#[unsafe(no_mangle)]
pub unsafe extern "C" fn globfree(pglob: *mut glob_t) {
// Retake ownership
if unsafe { !(*pglob).__opaque.is_null() } {
let pathv: Box<Vec<*mut c_char>> = unsafe { Box::from_raw((*pglob).__opaque.cast()) };
for (idx, path) in pathv.into_iter().enumerate() {
if unsafe { idx < (*pglob).gl_offs } {
continue;
}
if !path.is_null() {
unsafe {
drop(CString::from_raw(path));
}
}
}
unsafe {
(*pglob).gl_pathv = ptr::null_mut();
}
}
}
type GlobErrorFunc = unsafe extern "C" fn(epath: *const c_char, eerrno: c_int) -> c_int;
struct DirEntry {
name: CString,
is_dir: bool,
}
unsafe extern "C" fn default_errfunc(epath: *const c_char, eerrno: c_int) -> c_int {
0
}
fn list_dir(
path: &CStr,
errfunc: GlobErrorFunc,
abort_on_error: bool,
) -> Result<Vec<DirEntry>, c_int> {
const DT_DIR: c_uchar = 4; // From dirent.h
const DT_LNK: c_uchar = 10; // From dirent.h
let old_errno = platform::ERRNO.get();
let mut results: Vec<DirEntry> = Vec::new();
let open_path = if path.to_bytes().is_empty() {
unsafe { &CStr::from_bytes_with_nul_unchecked(b".\0") }
} else {
path
};
let dir = unsafe { opendir(open_path.as_ptr()) };
if dir.is_null() {
let new_errno = platform::ERRNO.get();
platform::ERRNO.set(old_errno);
if unsafe { errfunc(path.as_ptr(), new_errno) } != 0 || abort_on_error {
return Err(GLOB_ABORTED);
}
return Ok(results);
}
platform::ERRNO.set(0);
loop {
let entry = unsafe { readdir(&mut *dir) };
if entry.is_null() {
break;
}
let name = unsafe { CStr::from_ptr((*entry).d_name.as_ptr()).to_owned_cstring() };
if name.as_bytes() == b"." || name.as_bytes() == b".." {
continue;
}
let is_dir: bool = unsafe {
if (*entry).d_type == DT_DIR {
true
} else if (*entry).d_type == DT_LNK {
// Resolve symbolic link
let mut full_path = path.to_owned_cstring().into_string().unwrap();
if !full_path.ends_with('/') {
full_path.push('/');
}
full_path.push_str(name.to_str().unwrap());
full_path.push('\0');
let mut link_info = stat::default();
if stat(
full_path.as_ptr().cast::<c_char>(),
ptr::from_mut(&mut link_info),
) != 0
{
let errno = platform::ERRNO.get();
platform::ERRNO.set(old_errno);
if errfunc(full_path.as_ptr().cast::<c_char>(), errno) != 0 || abort_on_error {
return Err(GLOB_ABORTED);
}
}
link_info.st_mode & S_IFMT == S_IFDIR
} else {
false
}
};
results.push(DirEntry { name, is_dir });
}
// Check if entry == NULL because of an error
let errno = platform::ERRNO.get();
unsafe { closedir(Box::from_raw(dir)) };
// Restore the old errno
platform::ERRNO.set(old_errno);
if errno != 0 && (unsafe { errfunc(path.as_ptr(), errno) } != 0 || abort_on_error) {
return Err(GLOB_ABORTED);
}
Ok(results)
}
fn inner_glob(
current_dir: &CStr,
glob_expr: &CStr,
flags: c_int,
errfunc: GlobErrorFunc,
) -> Result<Vec<CString>, c_int> {
let mut pattern: Vec<u8> = Vec::new();
// Remove any '/' chars at the start of the expression
let glob_expr = {
let mut expr = glob_expr.to_bytes_with_nul();
while expr.first() == Some(&b'/') {
expr = &expr[1..];
}
unsafe { CStr::from_bytes_with_nul_unchecked(expr) }
};
// Get the next section of the glob expression (up to non-escaped '/')
let glob_iter = glob_expr.to_bytes();
let mut in_bracket = false;
let mut escaped = false;
let mut glob_consumed = 0;
for ch in glob_iter {
// Don't consume nul
if ch == &b'\0' {
break;
}
glob_consumed += 1;
if ch == &b'/' && !escaped {
break;
}
if ch == &b'[' && !escaped {
in_bracket = true;
} else if ch == &b']' {
// '\' is a normal character in brackets so doesn't escape
in_bracket = false;
}
escaped =
ch == &b'\\' && !in_bracket && !escaped && (flags & GLOB_NOESCAPE != GLOB_NOESCAPE);
pattern.push(*ch);
}
// Needs to be C-string
pattern.push(b'\0');
let new_glob_expr = unsafe {
CStr::from_bytes_with_nul_unchecked(&glob_expr.to_bytes_with_nul()[glob_consumed..])
};
// Handle special path sections
if pattern == b".\0" || pattern == b"..\0" {
let mut new_dir: Vec<u8> = Vec::new();
new_dir.extend_from_slice(current_dir.to_bytes());
new_dir.extend_from_slice(&pattern);
let new_dir_c = unsafe { CStr::from_bytes_with_nul_unchecked(&new_dir) };
return inner_glob(&new_dir_c, &new_glob_expr, flags, errfunc);
}
let mut fnmatch_flags = 0;
if flags & GLOB_NOESCAPE == GLOB_NOESCAPE {
fnmatch_flags |= FNM_NOESCAPE;
}
if flags & GLOB_PERIOD == GLOB_PERIOD {
fnmatch_flags |= FNM_PERIOD;
}
let mut matches: Vec<CString> = Vec::new();
for entry in list_dir(current_dir, errfunc, flags & GLOB_ERR == GLOB_ERR)? {
// If we still have pattern to match ignore non-directories
if !new_glob_expr.to_bytes().is_empty() && !entry.is_dir {
continue;
}
let mut path = current_dir.to_bytes().to_vec();
if path != b"" && !path.ends_with(b"/") {
path.push(b'/');
}
path.extend_from_slice(entry.name.as_bytes());
if flags & GLOB_MARK == GLOB_MARK && new_glob_expr.to_bytes() == b"" && entry.is_dir {
path.push(b'/');
}
// This shouldn't ever panic, we know the vec has no nul bytes
let path = CString::new(path).unwrap();
if unsafe {
fnmatch(
pattern.as_ptr().cast::<c_char>(),
entry.name.as_ptr(),
fnmatch_flags,
)
} == 0
{
if entry.is_dir && new_glob_expr.to_bytes() != b"" {
let new_matches = inner_glob(&CStr::borrow(&path), &new_glob_expr, flags, errfunc)?;
matches.extend(new_matches);
} else {
matches.push(path);
}
}
}
// It is an error if we don't find a directory when we expect one
if matches.is_empty() && !new_glob_expr.to_bytes().is_empty() {
let mut path = current_dir.to_bytes().to_vec();
path.extend_from_slice(&pattern);
if unsafe { errfunc(path.as_ptr().cast::<c_char>(), ENOENT) } != 0
|| flags & GLOB_ERR == GLOB_ERR
{
return Err(GLOB_ABORTED);
}
}
Ok(matches)
}
+7 -2
View File
@@ -1,8 +1,13 @@
sys_includes = []
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/grp.h.html
#
# Spec quotations relating to includes:
# - "The <grp.h> header shall define the gid_t and size_t types as described in <sys/types.h>."
sys_includes = ["sys/types.h"]
include_guard = "_RELIBC_GRP_H"
language = "C"
style = "Tag"
no_includes = true
usize_is_size_t = true
no_includes = false
cpp_compat = true
[enum]
+525 -32
View File
@@ -1,8 +1,91 @@
//! grp implementation for Redox, following http://pubs.opengroup.org/onlinepubs/7908799/xsh/grp.h.html
//! `grp.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/grp.h.html>.
use crate::platform::types::*;
use core::{
cell::SyncUnsafeCell,
convert::TryInto,
mem::{self, MaybeUninit},
num::ParseIntError,
ops::{Deref, DerefMut},
pin::Pin,
ptr, slice,
};
use alloc::{
borrow::ToOwned,
string::{FromUtf8Error, String},
};
use crate::{
error::ResultExt,
fs::File,
header::{errno, fcntl, limits, string::strlen},
io::{self, BufReader, Lines, prelude::*},
platform::{
self, Pal, Sys,
types::{c_char, c_int, c_void, gid_t, size_t},
},
};
use super::{errno::*, string::strncmp};
#[cfg(target_os = "linux")]
const SEPARATOR: char = ':';
#[cfg(target_os = "redox")]
const SEPARATOR: char = ';';
const GROUP_FILE: &core::ffi::CStr = c"/etc/group";
#[derive(Clone, Copy, Debug)]
struct DestBuffer {
ptr: *mut u8,
len: usize,
}
// Shamelessly stolen from pwd/mod.rs
#[derive(Debug)]
enum MaybeAllocated {
Owned(Pin<Box<[u8]>>),
Borrowed(DestBuffer),
}
impl Deref for MaybeAllocated {
type Target = [u8];
fn deref(&self) -> &Self::Target {
match self {
MaybeAllocated::Owned(boxed) => boxed,
MaybeAllocated::Borrowed(dst) => unsafe {
core::slice::from_raw_parts(dst.ptr, dst.len)
},
}
}
}
impl DerefMut for MaybeAllocated {
fn deref_mut(&mut self) -> &mut Self::Target {
match self {
MaybeAllocated::Owned(boxed) => boxed,
MaybeAllocated::Borrowed(dst) => unsafe {
core::slice::from_raw_parts_mut(dst.ptr, dst.len)
},
}
}
}
static mut GROUP_BUF: Option<MaybeAllocated> = None;
static mut GROUP: group = group {
gr_name: ptr::null_mut(),
gr_passwd: ptr::null_mut(),
gr_gid: 0,
gr_mem: ptr::null_mut(),
};
static LINE_READER: SyncUnsafeCell<Option<Lines<BufReader<File>>>> = SyncUnsafeCell::new(None);
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/grp.h.html>.
#[repr(C)]
#[derive(Debug)]
pub struct group {
pub gr_name: *mut c_char,
pub gr_passwd: *mut c_char,
@@ -10,56 +93,466 @@ pub struct group {
pub gr_mem: *mut *mut c_char,
}
// #[no_mangle]
pub extern "C" fn getgrgid(gid: gid_t) -> *mut group {
unimplemented!();
#[derive(Debug)]
enum Error {
EOF,
SyntaxError,
BufTooSmall,
Misc(io::Error),
FromUtf8Error(FromUtf8Error),
ParseIntError(ParseIntError),
Other,
}
// #[no_mangle]
pub extern "C" fn getgrnam(name: *const c_char) -> *mut group {
unimplemented!();
#[derive(Debug)]
struct OwnedGrp {
buffer: MaybeAllocated,
reference: group,
}
// #[no_mangle]
pub extern "C" fn getgrgid_r(
impl OwnedGrp {
fn into_global(self) -> *mut group {
unsafe {
GROUP_BUF = Some(self.buffer);
GROUP = self.reference;
&raw mut GROUP
}
}
}
fn split(buf: &mut [u8]) -> Option<group> {
let gr_gid = match buf[0..mem::size_of::<gid_t>()].try_into() {
Ok(buf) => gid_t::from_ne_bytes(buf),
Err(err) => return None,
};
// Get address of buffer for fixing up gr_mem
let buf_addr = buf.as_ptr() as usize;
// We moved the gid to the beginning of the byte buffer so we can do this.
let mut parts = buf[mem::size_of::<gid_t>()..].split_mut(|&c| c == b'\0');
let gr_name = parts.next()?.as_mut_ptr().cast::<c_char>();
let gr_passwd = parts.next()?.as_mut_ptr().cast::<c_char>();
let gr_mem = parts.next()?.as_mut_ptr().cast::<usize>();
// Adjust gr_mem address by buffer base address
// TODO: max group members length?
for i in 0..4096 {
unsafe {
if *gr_mem.add(i) == 0 {
// End of gr_mem pointer array
break;
}
*gr_mem.add(i) += buf_addr;
}
}
Some(group {
gr_name,
gr_passwd,
gr_gid,
gr_mem: gr_mem.cast::<*mut c_char>(),
})
}
fn parse_grp(line: String, destbuf: Option<DestBuffer>) -> Result<OwnedGrp, Error> {
let buffer = line.to_owned().into_bytes();
let mut buffer = buffer
.into_iter()
.map(|i| if i == SEPARATOR as u8 { b'\0' } else { i })
.chain([b'\0'])
.collect::<Vec<_>>();
let mut buffer = buffer.split_mut(|i| *i == b'\0');
let strings = {
let mut vec: Vec<u8> = Vec::new();
let gr_name = buffer.next().ok_or(Error::EOF)?.to_vec();
let gr_passwd = buffer.next().ok_or(Error::EOF)?.to_vec();
let gr_gid = String::from_utf8(buffer.next().ok_or(Error::EOF)?.to_vec())
.map_err(Error::FromUtf8Error)?
.parse::<gid_t>()
.map_err(Error::ParseIntError)?;
// Place the gid at the beginning of the byte buffer to make getting it back out again later, much faster.
vec.extend(gr_gid.to_ne_bytes());
vec.extend(gr_name);
vec.push(0);
vec.extend(gr_passwd);
vec.push(0);
let members = buffer.next().ok_or(Error::EOF)?;
// Get the offset of the members array
let member_array_start = vec.len();
// Push enough null pointers to fit all members
for _member in members
.split(|b| *b == b',')
.filter(|member| !member.is_empty())
{
vec.extend(0usize.to_ne_bytes());
}
let member_array_end = vec.len();
// Push a null pointer to terminate the members array
vec.extend(0usize.to_ne_bytes());
// Fill in member names
for (i, member) in members
.split(|b| *b == b',')
.filter(|member| !member.is_empty())
.enumerate()
{
let cur_offset = vec.len();
// This must be recomputed each time, because `vec` is undergoing extensions and so
// its backing memory might be reallocated and moved and its old memory deallocated.
let member_array = &mut vec[member_array_start..member_array_end];
let member_ptr = {
const SIZEOF_PTR: usize = mem::size_of::<*mut c_void>();
let start = i * SIZEOF_PTR;
let end = start + SIZEOF_PTR;
&mut member_array[start..end]
};
// Store offset to start of member, MUST BE ADJUSTED LATER BASED ON THE ADDRESS OF THE BUFFER
member_ptr.copy_from_slice(&cur_offset.to_ne_bytes());
vec.extend(member);
vec.push(0);
}
vec
};
let mut buffer = match destbuf {
None => MaybeAllocated::Owned(Box::into_pin(strings.into_boxed_slice())),
Some(buf) => {
let mut buf = MaybeAllocated::Borrowed(buf);
if buf.len() < strings.len() {
platform::ERRNO.set(errno::ERANGE);
return Err(Error::BufTooSmall);
}
buf[..strings.len()].copy_from_slice(&strings);
buf
}
};
let reference = split(&mut buffer).ok_or(Error::Other)?;
Ok(OwnedGrp { buffer, reference })
}
/// MT-Unsafe race:grgid locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgrgid.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getgrgid(gid: gid_t) -> *mut group {
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ptr::null_mut();
};
for line in BufReader::new(db).lines() {
let Ok(line) = line else {
return ptr::null_mut();
};
let Ok(grp) = parse_grp(line, None) else {
return ptr::null_mut();
};
if grp.reference.gr_gid == gid {
return grp.into_global();
}
}
ptr::null_mut()
}
/// MT-Unsafe race:grnam locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgrnam.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getgrnam(name: *const c_char) -> *mut group {
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ptr::null_mut();
};
for line in BufReader::new(db).lines() {
let Ok(line) = line else {
return ptr::null_mut();
};
let Ok(grp) = parse_grp(line, None) else {
return ptr::null_mut();
};
// Attempt to prevent BO vulnerabilities
if unsafe {
strncmp(
grp.reference.gr_name,
name,
strlen(grp.reference.gr_name).min(strlen(name)),
) == 0
} {
return grp.into_global();
}
}
ptr::null_mut()
}
/// MT-Safe locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgrgid_r.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getgrgid_r(
gid: gid_t,
grp: *mut group,
result_buf: *mut group,
buffer: *mut c_char,
bufsize: usize,
buflen: usize,
result: *mut *mut group,
) -> c_int {
unimplemented!();
// In case of error or the requested entry is not found.
unsafe {
*result = ptr::null_mut();
}
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ENOENT;
};
for line in BufReader::new(db).lines() {
let Ok(line) = line else { return EINVAL };
let grp = match parse_grp(
line,
Some(DestBuffer {
ptr: buffer.cast::<u8>(),
len: buflen,
}),
) {
Ok(grp) => grp,
Err(err) => {
return match err {
Error::BufTooSmall => ERANGE,
Error::EOF
| Error::SyntaxError
| Error::FromUtf8Error(_)
| Error::ParseIntError(_)
| Error::Other => EINVAL,
Error::Misc(io_err) => match io_err.kind() {
io::ErrorKind::InvalidData | io::ErrorKind::UnexpectedEof => EINVAL,
io::ErrorKind::NotFound => ENOENT,
_ => EIO,
},
};
}
};
if grp.reference.gr_gid == gid {
unsafe {
*result_buf = grp.reference;
*result = result_buf;
}
return 0;
}
}
// The requested entry was not found.
0
}
// #[no_mangle]
pub extern "C" fn getgrnam_r(
/// MT-Safe locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/getgrnam_r.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getgrnam_r(
name: *const c_char,
grp: *mut group,
result_buf: *mut group,
buffer: *mut c_char,
bufsize: usize,
buflen: usize,
result: *mut *mut group,
) -> c_int {
unimplemented!();
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ENOENT;
};
for line in BufReader::new(db).lines() {
let Ok(line) = line else { return EINVAL };
let Ok(grp) = parse_grp(
line,
Some(DestBuffer {
ptr: buffer.cast::<u8>(),
len: buflen,
}),
) else {
return EINVAL;
};
if unsafe {
strncmp(
grp.reference.gr_name,
name,
strlen(grp.reference.gr_name).min(strlen(name)),
) == 0
} {
unsafe {
*result_buf = grp.reference;
*result = result_buf;
}
return 0;
}
}
ENOENT
}
// #[no_mangle]
pub extern "C" fn getgrent() -> *mut group {
unimplemented!();
/// MT-Unsafe race:grent race:grentbuf locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endgrent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getgrent() -> *mut group {
let mut line_reader = unsafe { &mut *LINE_READER.get() };
if line_reader.is_none() {
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return ptr::null_mut();
};
*line_reader = Some(BufReader::new(db).lines());
}
if let Some(lines) = line_reader.deref_mut() {
let Some(line) = lines.next() else {
return ptr::null_mut();
};
let Ok(line) = line else {
return ptr::null_mut();
};
if let Ok(grp) = parse_grp(line, None) {
grp.into_global()
} else {
ptr::null_mut()
}
} else {
ptr::null_mut()
}
}
// #[no_mangle]
pub extern "C" fn endgrent() {
unimplemented!();
/// MT-Unsafe race:grent locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endgrent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn endgrent() {
unsafe {
*(&mut *LINE_READER.get()) = None;
}
}
// #[no_mangle]
pub extern "C" fn setgrent() {
unimplemented!();
/// MT-Unsafe race:grent locale
///
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/endgrent.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn setgrent() {
let line_reader = unsafe { &mut *LINE_READER.get() };
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return;
};
*line_reader = Some(BufReader::new(db).lines());
}
/*
#[no_mangle]
pub extern "C" fn func(args) -> c_int {
unimplemented!();
/// MT-Safe locale
/// Not POSIX
///
/// See <https://www.man7.org/linux/man-pages/man3/getgrouplist.3.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getgrouplist(
user: *const c_char,
group: gid_t,
groups: *mut gid_t,
ngroups: *mut c_int,
) -> c_int {
let grps = unsafe {
slice::from_raw_parts_mut(groups.cast::<MaybeUninit<gid_t>>(), ngroups.read() as usize)
};
// FIXME: This API probably expects the group database to already exist in memory, as it
// doesn't seem to have any documented error handling.
let Ok(user) = (unsafe { crate::c_str::CStr::from_ptr(user).to_str() }) else {
return 0;
};
let Ok(db) = File::open(GROUP_FILE.into(), fcntl::O_RDONLY) else {
return 0;
};
let mut groups_found: c_int = 0;
for line in BufReader::new(db).lines() {
let Ok(line) = line else {
return 0;
};
let mut parts = line.split(SEPARATOR);
let group_name = parts.next().unwrap_or("");
let group_password = parts.next().unwrap_or("");
let group_id = parts.next().unwrap_or("-1").parse::<c_int>().unwrap();
let members = parts
.next()
.unwrap_or("")
.split(",")
.map(|i| i.trim())
.collect::<Vec<_>>();
if !members.contains(&user) {
continue;
}
if let Some(dst) = grps.get_mut(groups_found as usize) {
dst.write(group_id);
}
groups_found = match groups_found.checked_add(1) {
Some(g) => g,
None => break,
};
}
unsafe {
ngroups.write(groups_found);
}
if groups_found as usize > grps.len() {
-1
} else {
groups_found
}
}
/// MT-Safe locale
/// Not POSIX
///
/// See <https://www.man7.org/linux/man-pages/man3/initgroups.3.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn initgroups(user: *const c_char, gid: gid_t) -> c_int {
let mut groups = [0; limits::NGROUPS_MAX];
let mut count = groups.len() as c_int;
if unsafe { getgrouplist(user, gid, groups.as_mut_ptr(), &raw mut count) < 0 } {
return -1;
}
unsafe { setgroups(count as size_t, groups.as_ptr()) }
}
/// Non-POSIX, see <https://www.man7.org/linux/man-pages/man2/setgroups.2.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn setgroups(size: size_t, list: *const gid_t) -> c_int {
unsafe { Sys::setgroups(size, list) }
.map(|()| 0)
.or_minus_one_errno()
}
*/
+17
View File
@@ -0,0 +1,17 @@
# netinet/in.h brings in sys/socket.h
sys_includes = ["features.h", "netinet/in.h"]
include_guard = "_RELIBC_IFADDRS_H"
language = "C"
style = "Tag"
no_includes = true
cpp_compat = true
trailer = """
#define ifa_broadaddr ifa_ifu.ifu_broadaddr
#define ifa_dstaddr ifa_ifu.ifu_dstaddr
"""
[export.rename]
"sockaddr" = "struct sockaddr"
[enum]
prefix_with_name = true
+44
View File
@@ -0,0 +1,44 @@
//! `ifaddrs.h` implementation.
//!
//! Non-POSIX, see <https://www.man7.org/linux/man-pages/man3/getifaddrs.3.html>.
use crate::{
header::{errno, stdlib, sys_socket::sockaddr},
platform::{
self,
types::{c_char, c_int, c_uint, c_void},
},
};
#[repr(C)]
union ifaddrs_ifa_ifu {
ifu_broadaddr: *mut sockaddr,
ifu_dstaddr: *mut sockaddr,
}
#[repr(C)]
pub struct ifaddrs {
ifa_next: *mut ifaddrs,
ifa_name: *mut c_char,
ifa_flags: c_uint,
ifa_addr: *mut sockaddr,
ifa_netmask: *mut sockaddr,
ifa_ifu: ifaddrs_ifa_ifu,
ifa_data: *mut c_void,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn freeifaddrs(mut ifa: *mut ifaddrs) {
while !ifa.is_null() {
let next = unsafe { (*ifa).ifa_next };
unsafe { stdlib::free(ifa.cast()) };
ifa = next;
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn getifaddrs(ifap: *mut *mut ifaddrs) -> c_int {
//TODO: implement getifaddrs
platform::ERRNO.set(errno::ENOSYS);
-1
}
+207 -2
View File
@@ -1,6 +1,211 @@
sys_includes = ["stdint.h", "wchar.h"]
# POSIX header spec: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/inttypes.h.html
#
# Spec quotations relating to includes:
# - "The <inttypes.h> header shall include the <stdint.h> header."
# - "wchar_t As described in <stddef.h>."
#
# Include stdint.h and stddef.h directly per POSIX spec.
# Do NOT include wchar.h — it creates a circular dependency:
# wchar.h → stdint.h → gnulib inttypes.h → inttypes.h → wchar.h
sys_includes = ["stdint.h", "stddef.h"]
include_guard = "_RELIBC_INTTYPES_H"
trailer = "#include <bits/inttypes.h>"
trailer = """
#ifndef _RELIBC_BITS_INTTYPES_H
#define _RELIBC_BITS_INTTYPES_H
#define PRId8 "hhd"
#define PRId16 "hd"
#define PRId32 "d"
#define PRId64 "ld"
#define PRIdLEAST8 "hhd"
#define PRIdLEAST16 "hd"
#define PRIdLEAST32 "d"
#define PRIdLEAST64 "ld"
#define PRIdFAST8 "hhd"
#define PRIdFAST16 "hd"
#define PRIdFAST32 "d"
#define PRIdFAST64 "ld"
#define PRIi8 "hhi"
#define PRIi16 "hi"
#define PRIi32 "i"
#define PRIi64 "li"
#define PRIiLEAST8 "hhi"
#define PRIiLEAST16 "hi"
#define PRIiLEAST32 "i"
#define PRIiLEAST64 "li"
#define PRIiFAST8 "hhi"
#define PRIiFAST16 "hi"
#define PRIiFAST32 "i"
#define PRIiFAST64 "li"
#define PRIo8 "hho"
#define PRIo16 "ho"
#define PRIo32 "o"
#define PRIo64 "lo"
#define PRIoLEAST8 "hho"
#define PRIoLEAST16 "ho"
#define PRIoLEAST32 "o"
#define PRIoLEAST64 "lo"
#define PRIoFAST8 "hho"
#define PRIoFAST16 "ho"
#define PRIoFAST32 "o"
#define PRIoFAST64 "lo"
#define PRIu8 "hhu"
#define PRIu16 "hu"
#define PRIu32 "u"
#define PRIu64 "lu"
#define PRIuLEAST8 "hhu"
#define PRIuLEAST16 "hu"
#define PRIuLEAST32 "u"
#define PRIuLEAST64 "lu"
#define PRIuFAST8 "hhu"
#define PRIuFAST16 "hu"
#define PRIuFAST32 "u"
#define PRIuFAST64 "lu"
#define PRIx8 "hhx"
#define PRIx16 "hx"
#define PRIx32 "x"
#define PRIx64 "lx"
#define PRIxLEAST8 "hhx"
#define PRIxLEAST16 "hx"
#define PRIxLEAST32 "x"
#define PRIxLEAST64 "lx"
#define PRIxFAST8 "hhx"
#define PRIxFAST16 "hx"
#define PRIxFAST32 "x"
#define PRIxFAST64 "lx"
#define PRIX8 "hhX"
#define PRIX16 "hX"
#define PRIX32 "X"
#define PRIX64 "lX"
#define PRIXLEAST8 "hhX"
#define PRIXLEAST16 "hX"
#define PRIXLEAST32 "X"
#define PRIXLEAST64 "lX"
#define PRIXFAST8 "hhX"
#define PRIXFAST16 "hX"
#define PRIXFAST32 "X"
#define PRIXFAST64 "lX"
#define PRIdMAX "jd"
#define PRIiMAX "ji"
#define PRIoMAX "jo"
#define PRIuMAX "ju"
#define PRIxMAX "jx"
#define PRIXMAX "jX"
#define PRIdPTR "td"
#define PRIiPTR "ti"
#define PRIoPTR "to"
#define PRIuPTR "tu"
#define PRIxPTR "tx"
#define PRIXPTR "tX"
#define SCNd8 PRId8
#define SCNd16 PRId16
#define SCNd32 PRId32
#define SCNd64 PRId64
#define SCNdLEAST8 PRIdLEAST8
#define SCNdLEAST16 PRIdLEAST16
#define SCNdLEAST32 PRIdLEAST32
#define SCNdLEAST64 PRIdLEAST64
#define SCNdFAST8 PRIdFAST8
#define SCNdFAST16 PRIdFAST16
#define SCNdFAST32 PRIdFAST32
#define SCNdFAST64 PRIdFAST64
#define SCNi8 PRIi8
#define SCNi16 PRIi16
#define SCNi32 PRIi32
#define SCNi64 PRIi64
#define SCNiLEAST8 PRIiLEAST8
#define SCNiLEAST16 PRIiLEAST16
#define SCNiLEAST32 PRIiLEAST32
#define SCNiLEAST64 PRIiLEAST64
#define SCNiFAST8 PRIiFAST8
#define SCNiFAST16 PRIiFAST16
#define SCNiFAST32 PRIiFAST32
#define SCNiFAST64 PRIiFAST64
#define SCNo8 PRIo8
#define SCNo16 PRIo16
#define SCNo32 PRIo32
#define SCNo64 PRIo64
#define SCNoLEAST8 PRIoLEAST8
#define SCNoLEAST16 PRIoLEAST16
#define SCNoLEAST32 PRIoLEAST32
#define SCNoLEAST64 PRIoLEAST64
#define SCNoFAST8 PRIoFAST8
#define SCNoFAST16 PRIoFAST16
#define SCNoFAST32 PRIoFAST32
#define SCNoFAST64 PRIoFAST64
#define SCNu8 PRIu8
#define SCNu16 PRIu16
#define SCNu32 PRIu32
#define SCNu64 PRIu64
#define SCNuLEAST8 PRIuLEAST8
#define SCNuLEAST16 PRIuLEAST16
#define SCNuLEAST32 PRIuLEAST32
#define SCNuLEAST64 PRIuLEAST64
#define SCNuFAST8 PRIuFAST8
#define SCNuFAST16 PRIuFAST16
#define SCNuFAST32 PRIuFAST32
#define SCNuFAST64 PRIuFAST64
#define SCNx8 PRIx8
#define SCNx16 PRIx16
#define SCNx32 PRIx32
#define SCNx64 PRIx64
#define SCNxLEAST8 PRIxLEAST8
#define SCNxLEAST16 PRIxLEAST16
#define SCNxLEAST32 PRIxLEAST32
#define SCNxLEAST64 PRIxLEAST64
#define SCNxFAST8 PRIxFAST8
#define SCNxFAST16 PRIxFAST16
#define SCNxFAST32 PRIxFAST32
#define SCNxFAST64 PRIxFAST64
#define SCNdMAX PRIdMAX
#define SCNiMAX PRIiMAX
#define SCNoMAX PRIoMAX
#define SCNuMAX PRIuMAX
#define SCNxMAX PRIxMAX
#define SCNdPTR PRIdPTR
#define SCNiPTR PRIiPTR
#define SCNoPTR PRIoPTR
#define SCNuPTR PRIuPTR
#define SCNxPTR PRIxPTR
#endif // _RELIBC_BITS_INTTYPES_H
"""
language = "C"
style = "Type"
no_includes = true
+48 -23
View File
@@ -1,21 +1,34 @@
//! `inttypes.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/inttypes.h.html>.
use crate::{
header::{ctype, errno::*, stdlib::*},
platform::{self, types::*},
header::{
ctype::{self, isspace},
errno::{EINVAL, ERANGE},
stdlib::{convert_hex, convert_integer, convert_octal, detect_base, is_positive},
},
platform::{
self,
types::{c_char, c_int, c_long, intmax_t, uintmax_t, wchar_t},
},
};
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/imaxabs.html>.
#[unsafe(no_mangle)]
pub extern "C" fn imaxabs(i: intmax_t) -> intmax_t {
i.abs()
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/inttypes.h.html>.
#[repr(C)]
pub struct imaxdiv_t {
quot: intmax_t,
rem: intmax_t,
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/imaxdiv.html>.
#[unsafe(no_mangle)]
pub extern "C" fn imaxdiv(i: intmax_t, j: intmax_t) -> imaxdiv_t {
imaxdiv_t {
quot: i / j,
@@ -23,7 +36,8 @@ pub extern "C" fn imaxdiv(i: intmax_t, j: intmax_t) -> imaxdiv_t {
}
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtoimax.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn strtoimax(
s: *const c_char,
endptr: *mut *mut c_char,
@@ -32,15 +46,16 @@ pub unsafe extern "C" fn strtoimax(
strto_impl!(
intmax_t,
false,
intmax_t::max_value(),
intmax_t::min_value(),
intmax_t::MAX,
intmax_t::MIN,
s,
endptr,
base
)
}
#[no_mangle]
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtoimax.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn strtoumax(
s: *const c_char,
endptr: *mut *mut c_char,
@@ -49,30 +64,40 @@ pub unsafe extern "C" fn strtoumax(
strto_impl!(
uintmax_t,
false,
uintmax_t::max_value(),
uintmax_t::min_value(),
uintmax_t::MAX,
uintmax_t::MIN,
s,
endptr,
base
)
}
#[allow(unused)]
// #[no_mangle]
pub extern "C" fn wcstoimax(
nptr: *const wchar_t,
endptr: *mut *mut wchar_t,
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcstoimax.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wcstoimax(
mut ptr: *const wchar_t,
end: *mut *mut wchar_t,
base: c_int,
) -> intmax_t {
unimplemented!();
skipws!(ptr);
let result = strto_impl!(intmax_t, ptr, base);
if !end.is_null() {
unsafe { *end = ptr.cast_mut() };
}
result
}
#[allow(unused)]
// #[no_mangle]
pub extern "C" fn wcstoumax(
nptr: *const wchar_t,
endptr: *mut *mut wchar_t,
/// See <https://pubs.opengroup.org/onlinepubs/9799919799/functions/wcstoimax.html>.
#[unsafe(no_mangle)]
pub unsafe extern "C" fn wcstoumax(
mut ptr: *const wchar_t,
end: *mut *mut wchar_t,
base: c_int,
) -> uintmax_t {
unimplemented!();
skipws!(ptr);
let result = strtou_impl!(uintmax_t, ptr, base);
if !end.is_null() {
unsafe { *end = ptr.cast_mut() };
}
result
}

Some files were not shown because too many files have changed in this diff Show More