0.3.0: converge relibc to upstream 0.6.0 + Red Bear patches
This commit is contained in:
-6332
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#include <stdint.h>
|
||||
|
||||
void abort();
|
||||
|
||||
uintptr_t __stack_chk_guard = 0xd048c37519fcadfe;
|
||||
|
||||
__attribute__((noreturn))
|
||||
void __stack_chk_fail(void) {
|
||||
abort();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+17
-12
@@ -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();
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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!();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
include_guard = "_RELIBC_PATHS_H"
|
||||
language = "C"
|
||||
style = "Type"
|
||||
no_includes = true
|
||||
cpp_compat = true
|
||||
@@ -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/";
|
||||
@@ -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::*;
|
||||
@@ -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/";
|
||||
@@ -3,7 +3,7 @@
|
||||
use platform::types::*;
|
||||
|
||||
/*
|
||||
#[no_mangle]
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn func(args) -> c_int {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
@@ -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!();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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,
|
||||
) {
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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() };
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
//! `bits/eventfd.h` — eventfd counter type.
|
||||
|
||||
use crate::platform::types::uint64_t;
|
||||
pub type eventfd_t = uint64_t;
|
||||
@@ -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
|
||||
@@ -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) {}
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
pub type locale_t = *mut core::ffi::c_void;
|
||||
@@ -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
|
||||
"""
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -0,0 +1,3 @@
|
||||
use crate::platform::types::c_ushort;
|
||||
|
||||
pub type sa_family_t = c_ushort;
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
use crate::platform::types::c_ulonglong;
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type sigset_t = c_ulonglong;
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
pub type socklen_t = u32;
|
||||
@@ -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
|
||||
@@ -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) {}
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
include_guard = "_RELIBC_CRYPT_H"
|
||||
language = "C"
|
||||
style = "Type"
|
||||
no_includes = true
|
||||
cpp_compat = true
|
||||
|
||||
|
||||
[enum]
|
||||
prefix_with_name = true
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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(), ¶ms, &mut output).ok()?;
|
||||
|
||||
Some(format!(
|
||||
"$7${}${}${}",
|
||||
&setting[3..14],
|
||||
salt,
|
||||
Base64Bcrypt::encode_string(&output)
|
||||
))
|
||||
}
|
||||
@@ -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(), ¶ms)
|
||||
}
|
||||
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(), ¶ms)
|
||||
}
|
||||
} {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
"
|
||||
);
|
||||
|
||||
@@ -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
@@ -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) {}
|
||||
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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,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
|
||||
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
Reference in New Issue
Block a user