libc: add pwd.h, dirent.h, fcntl.h, setjmp.h, signal.h

This commit is contained in:
Mark Poliakov 2024-11-12 12:19:56 +02:00
parent 4519e5385a
commit 457e82d2e3
22 changed files with 901 additions and 19 deletions

View File

@ -0,0 +1,8 @@
#ifndef _YGGDRASIL_DIRENT
#define _YGGDRASIL_DIRENT 1
struct __DIR;
typedef struct __DIR DIR;
#endif

View File

@ -0,0 +1,8 @@
#ifndef _YGGDRASIL_FCNTL_H
#define _YGGDRASIL_FCNTL_H 1
int fcntl(int fd, int cmd, ...);
int open(const char *pathname, int opts, ...);
int openat(int atfd, const char *pathname, int opts, ...);
#endif

View File

@ -0,0 +1,7 @@
#ifndef _YGGDRASIL_SETJMP_H
#define _YGGDRASIL_SETJMP_H 1
int setjmp(jmp_buf env);
[[noreturn]] void longjmp(jmp_buf env, int val);
#endif

View File

@ -0,0 +1,16 @@
#ifndef _YGGDRASIL_SIGNAL_H
#define _YGGDRASIL_SIGNAL_H 1
#include <stdatomic.h>
typedef _Atomic int sig_atomic_t;
#define SIG_IGN __sig_ignore
// TODO
#define SIG_ERR __sig_terminate
// TODO
#define SIG_HOLD __sig_terminate
#define SIG_DFL ((sig_handler_t) 0)
#endif

View File

@ -0,0 +1,47 @@
#ifndef _INTTYPES_H
#define _INTTYPES_H 1
#include <stddef.h>
#include <stdint.h>
#define PRIo64 "lo"
#define PRId64 "ld"
#define PRIu64 "lu"
#define PRIx64 "lx"
#define PRIo32 "o"
#define PRId32 "d"
#define PRIu32 "u"
#define PRIx32 "x"
#define PRIo16 "ho"
#define PRId16 "hd"
#define PRIu16 "hu"
#define PRIx16 "hx"
#define PRIo8 "hho"
#define PRId8 "hhd"
#define PRIu8 "hhu"
#define PRIx8 "hhx"
#define SCNo64 PRIo64
#define SCNd64 PRId64
#define SCNu64 PRIu64
#define SCNx64 PRIx64
#define SCNo32 PRIo32
#define SCNd32 PRId32
#define SCNu32 PRIu32
#define SCNx32 PRIx32
#define SCNo16 PRIo16
#define SCNd16 PRId16
#define SCNu16 PRIu16
#define SCNx16 PRIx16
#define SCNo8 PRIo8
#define SCNd8 PRId8
#define SCNu8 PRIu8
#define SCNx8 PRIx8
#endif

View File

@ -102,6 +102,13 @@ impl<T> EResult<T> {
}
}
pub fn map<U, F: FnOnce(T) -> U>(self, map: F) -> EResult<U> {
match self {
Self::Ok(value) => EResult::Ok(map(value)),
Self::Err(err) => EResult::Err(err)
}
}
pub fn unwrap_err(self) -> Errno {
match self {
Self::Ok(_) => panic!("Expected an error"),

View File

@ -0,0 +1,20 @@
language = "C"
style = "Tag"
sys_includes = [
"stddef.h",
"stdint.h",
"sys/types.h",
"bits/dirent.h",
"limits.h",
]
no_includes = true
include_guard = "_DIRENT_H"
usize_type = "size_t"
isize_type = "ssize_t"
[export]
include = ["dirent"]
exclude = ["DIR"]

View File

@ -0,0 +1,163 @@
use core::{
ffi::{c_char, c_int, c_long},
mem::MaybeUninit,
ptr::NonNull,
};
use alloc::boxed::Box;
use yggdrasil_rt::io::{DirectoryEntry, RawFd};
use crate::{
error::{CIntZeroResult, CPtrResult, EResult, TryFromExt},
io::{dir::DirReader, FromRawFd},
util::{PointerExt, PointerStrExt},
};
use super::{errno::ESUCCESS, sys_types::ino_t};
#[derive(Debug)]
#[repr(C)]
pub struct DIR {
reader: DirReader,
buffer: MaybeUninit<dirent>,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(C)]
pub struct dirent {
pub d_ino: ino_t,
pub d_name: [c_char; 256],
}
pub type __scandir_filter_fn_t = extern "C" fn(*const dirent) -> c_int;
pub type __scandir_compar_fn_t = extern "C" fn(*mut *const dirent, *mut *const dirent) -> c_int;
impl DIR {
unsafe fn close(mut dir_ptr: NonNull<Self>) -> EResult<()> {
let dir = dir_ptr.as_mut();
let result = dir.reader.close();
drop(Box::from_raw(dir_ptr.as_ptr()));
result
}
fn read_entry(&mut self) -> EResult<NonNull<dirent>> {
match self.reader.read_entry() {
EResult::Ok(Some(entry)) => {
self.buffer.write(dirent::from(entry));
EResult::Ok(unsafe { NonNull::new_unchecked(self.buffer.as_mut_ptr()) })
}
EResult::Ok(None) => EResult::Err(ESUCCESS),
EResult::Err(err) => EResult::Err(err),
}
}
}
impl From<DirectoryEntry> for dirent {
fn from(value: DirectoryEntry) -> Self {
let mut d_name = [0; 256];
let len = value.name.len();
if len >= 255 {
todo!("Name too long");
}
// SAFETY this is just to cast u8 to i8, so transmute should be safe
d_name[..len].copy_from_slice(unsafe { core::mem::transmute(value.name.as_bytes()) });
d_name[len] = 0;
Self {
// TODO
d_ino: 0,
d_name,
}
}
}
// Primary stuff
#[no_mangle]
unsafe extern "C" fn opendir(pathname: *const c_char) -> CPtrResult<DIR> {
let pathname = pathname.ensure_str();
let reader = DirReader::open_at(None, pathname)?;
let dir = NonNull::from(Box::leak(Box::new(DIR {
reader,
buffer: MaybeUninit::uninit(),
})));
CPtrResult::success(dir)
}
#[no_mangle]
unsafe extern "C" fn fdopendir(fd: c_int) -> CPtrResult<DIR> {
let fd = RawFd::e_try_from(fd)?;
let reader = DirReader::from_raw_fd(fd);
let dir = NonNull::from(Box::leak(Box::new(DIR {
reader,
buffer: MaybeUninit::uninit(),
})));
CPtrResult::success(dir)
}
#[no_mangle]
unsafe extern "C" fn closedir(dir: *mut DIR) -> CIntZeroResult {
let dir = NonNull::new(dir).unwrap();
DIR::close(dir)?;
CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn dirfd(dir: *mut DIR) -> c_int {
let dir = dir.ensure_mut();
dir.reader.as_raw_fd().into_raw().try_into().unwrap()
}
#[no_mangle]
unsafe extern "C" fn readdir(dir: *mut DIR) -> CPtrResult<dirent> {
let dir = dir.as_mut().unwrap();
let dirent = dir.read_entry()?;
CPtrResult::success(dirent)
}
// Deprecated
// #[no_mangle]
// unsafe extern "C" fn readdir_r(
// dir: *mut DIR,
// buffer: *mut dirent,
// outptr: *mut *mut dirent,
// ) -> c_int {
// todo!()
// }
// Seeking
#[no_mangle]
unsafe extern "C" fn seekdir(_dir: *mut DIR, _offset: c_long) {
todo!()
}
#[no_mangle]
unsafe extern "C" fn rewinddir(_dir: *mut DIR) {
todo!()
}
#[no_mangle]
unsafe extern "C" fn telldir(_dir: *mut DIR) -> c_long {
todo!()
}
// Directory scan and sorting
#[no_mangle]
unsafe extern "C" fn scandir(
_dirp: *const c_char,
_namelist: *mut *mut *mut dirent,
_filter: __scandir_filter_fn_t,
_compar: __scandir_compar_fn_t,
) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn alphasort(_a: *const *const dirent, _b: *const *const dirent) -> c_int {
todo!()
}

View File

@ -0,0 +1,23 @@
language = "C"
style = "Tag"
sys_includes = [
"stddef.h",
"stdint.h",
"sys/types.h",
"sys/stat.h",
"unistd.h",
"bits/fcntl.h"
]
no_includes = true
include_guard = "_FCNTL_H"
usize_type = "size_t"
isize_type = "ssize_t"
[export]
include = ["flock"]
# Varargs are broken for these
exclude = ["fcntl", "open", "openat"]

View File

@ -0,0 +1,178 @@
use core::ffi::{c_char, c_int, c_short, VaList};
use yggdrasil_rt::io::{FileMode, OpenOptions};
use crate::{
error::{CFdResult, CIntCountResult, EResult, TryFromExt},
io::{raw::RawFile, IntoRawFd},
util::{self, PointerStrExt},
};
use super::{
errno,
sys_types::{mode_t, off_t, pid_t},
};
// TODO:
// POSIX_FADV_DONTNEED
// POSIX_FADV_NOREUSE
// POSIX_FADV_NORMAL
// POSIX_FADV_RANDOM
// POSIX_FADV_SEQUENTIAL
// POSIX_FADV_WILLNEED
// int posix_fadvise(int, off_t, off_t, int);
// int posix_fallocate(int, off_t, off_t);
#[repr(C)]
pub struct flock {
pub l_type: c_short,
pub l_whence: c_short,
pub l_start: off_t,
pub l_len: off_t,
pub l_pid: pid_t,
}
pub const F_DUPFD: c_int = 1;
pub const F_DUPFD_CLOEXEC: c_int = 2;
pub const F_GETFD: c_int = 3;
pub const F_SETFD: c_int = 4;
pub const F_GETFL: c_int = 5;
pub const F_SETFL: c_int = 6;
pub const F_GETLK: c_int = 7;
pub const F_SETLK: c_int = 8;
pub const F_SETLKW: c_int = 9;
pub const F_GETOWN: c_int = 10;
pub const F_SETOWN: c_int = 11;
pub const FD_CLOEXEC: c_int = 1 << 0;
pub const F_RDLCK: c_int = 1;
pub const F_UNLCK: c_int = 2;
pub const F_WRLCK: c_int = 3;
pub const O_CLOEXEC: c_int = 1 << 16;
pub const O_CREAT: c_int = 1 << 17;
pub const O_DIRECTORY: c_int = 1 << 18;
pub const O_EXCL: c_int = 1 << 19;
pub const O_NOCTTY: c_int = 1 << 20;
pub const O_NOFOLLOW: c_int = 1 << 21;
pub const O_TRUNC: c_int = 1 << 22;
pub const O_TTY_INIT: c_int = 1 << 23;
pub const O_APPEND: c_int = 1 << 24;
pub const O_DSYNC: c_int = 1 << 25;
pub const O_NONBLOCK: c_int = 1 << 26;
pub const O_RSYNC: c_int = 1 << 27;
pub const O_SYNC: c_int = 1 << 28;
pub const O_ACCMODE: c_int = 0xFF;
pub const O_RDONLY: c_int = 1;
pub const O_WRONLY: c_int = 2;
pub const O_RDWR: c_int = 3;
pub const O_EXEC: c_int = 4;
pub const O_SEARCH: c_int = 5;
pub const AT_FDCWD: c_int = -65536;
pub const AT_EACCESS: c_int = 1 << 0;
pub const AT_SYMLINK_NOFOLLOW: c_int = 1 << 1;
pub const AT_REMOVEDIR: c_int = 1 << 2;
enum OpenMode {
File(OpenOptions, FileMode),
Directory,
}
fn open_opts(opts: c_int, ap: &mut VaList) -> EResult<OpenMode> {
if opts & O_DIRECTORY != 0 {
if opts & !O_DIRECTORY != 0 {
todo!();
}
return EResult::Ok(OpenMode::Directory);
}
let need_mode = opts & O_CREAT != 0;
let mut res = OpenOptions::empty();
match opts & O_ACCMODE {
O_RDONLY => res |= OpenOptions::READ,
O_WRONLY => res |= OpenOptions::WRITE,
O_RDWR => res |= OpenOptions::READ | OpenOptions::WRITE,
O_EXEC => todo!(),
O_SEARCH => todo!(),
_ => {
return EResult::Err(errno::EINVAL);
}
}
if opts & O_CREAT != 0 {
res |= OpenOptions::CREATE;
}
if opts & O_EXCL != 0 {
res |= OpenOptions::CREATE_EXCL;
}
if opts & O_APPEND != 0 {
res |= OpenOptions::APPEND;
}
if opts & O_TRUNC != 0 {
res |= OpenOptions::TRUNCATE;
}
if opts
& (O_DSYNC | O_RSYNC | O_SYNC | O_TTY_INIT | O_NONBLOCK | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY)
!= 0
{
todo!();
}
let mode = if need_mode {
let _raw = unsafe { ap.arg::<c_int>() };
todo!();
} else {
FileMode::empty()
};
EResult::Ok(OpenMode::File(res, mode))
}
#[no_mangle]
unsafe extern "C" fn fcntl(fd: c_int, cmd: c_int, _args: ...) -> CIntCountResult {
// TODO kernel support for fcntl
let _file = RawFile::e_try_from(fd)?;
match cmd {
_ => todo!("fcntl({}, {}, ...)", fd, cmd),
}
}
unsafe fn vopenat(atfd: c_int, pathname: *const c_char, opts: c_int, mut ap: VaList) -> CFdResult {
let atfd = util::at_fd(atfd)?;
let pathname = pathname.ensure_str();
let fd = match open_opts(opts, &mut ap)? {
OpenMode::File(opts, mode) => RawFile::open_at(atfd, pathname, opts, mode)?.into_raw_fd(),
OpenMode::Directory => todo!(),
};
CFdResult::success(fd)
}
#[no_mangle]
unsafe extern "C" fn creat(pathname: *const c_char, mode: mode_t) -> CFdResult {
openat(AT_FDCWD, pathname, O_CREAT | O_WRONLY | O_TRUNC, mode)
}
#[no_mangle]
unsafe extern "C" fn open(pathname: *const c_char, opts: c_int, mut args: ...) -> CFdResult {
vopenat(AT_FDCWD, pathname, opts, args.as_va_list())
}
#[no_mangle]
unsafe extern "C" fn openat(
atfd: c_int,
pathname: *const c_char,
opts: c_int,
mut args: ...
) -> CFdResult {
vopenat(atfd, pathname, opts, args.as_va_list())
}

View File

@ -6,10 +6,10 @@
// <complex.h> -
// <cpio.h> -
// <ctype.h> +
// <dirent.h> -
// <dirent.h> +
// <dlfcn.h> -
// <errno.h> +
// <fcntl.h> -
// <fcntl.h> +
// <fenv.h> -
// <float.h> -
// <fmtmsg.h> -
@ -18,12 +18,12 @@
// <glob.h> -
// <grp.h> -
// <iconv.h> -
// <inttypes.h> -
// <inttypes.h> +
// <iso646.h> -
// <langinfo.h> -
// <libgen.h> -
// <limits.h> -
// <locale.h> ~
// <limits.h> ?
// <locale.h> =
// <math.h> -
// <monetary.h> -
// <mqueue.h> -
@ -35,18 +35,18 @@
// <nl_types.h> -
// <poll.h> -
// <pthread.h> -
// <pwd.h> -
// <pwd.h> =
// <regex.h> -
// <sched.h> -
// <search.h> -
// <semaphore.h> -
// <setjmp.h> -
// <signal.h> -
// <setjmp.h> +
// <signal.h> =
// <spawn.h> -
// <stdarg.h> -
// <stdbool.h> -
// <stddef.h> -
// <stdint.h> -
// <stdarg.h> +
// <stdbool.h> +
// <stddef.h> +
// <stdint.h> +
// <stdio.h> +
// <stdlib.h> -
// <string.h> +
@ -84,12 +84,17 @@
// <wordexp.h> -
pub mod ctype;
pub mod dirent;
pub mod errno;
pub mod fcntl;
pub mod locale;
pub mod pwd;
pub mod setjmp;
pub mod stdio;
pub mod stdlib;
pub mod string;
pub mod strings;
pub mod unistd;
pub mod signal;
pub mod sys_types;

View File

@ -0,0 +1,14 @@
language = "C"
style = "Tag"
sys_includes = ["stddef.h", "sys/types.h"]
no_includes = true
include_guard = "_PWD_H"
usize_type = "size_t"
isize_type = "ssize_t"
[export]
include = ["passwd"]
exclude = []

View File

@ -0,0 +1,60 @@
use core::ffi::{c_char, c_int};
use super::sys_types::{gid_t, uid_t};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(C)]
pub struct passwd {
pub pw_name: *mut c_char,
pub pw_uid: uid_t,
pub pw_gid: gid_t,
pub pw_dir: *mut c_char,
pub pw_shell: *mut c_char,
}
#[no_mangle]
unsafe extern "C" fn endpwent() {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn getpwent() -> *mut passwd {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn getpwnam(_name: *const c_char) -> *mut passwd {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn getpwnam_r(
_name: *const c_char,
_pwd: *mut passwd,
_buf: *mut c_char,
_buflen: usize,
_result: *mut *mut passwd,
) -> c_int {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn getpwuid(_uid: uid_t) -> *mut passwd {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn getpwuid_r(
_uid: uid_t,
_pwd: *mut passwd,
_buf: *mut c_char,
_buflen: usize,
_result: *mut *mut passwd,
) -> c_int {
unimplemented!()
}
#[no_mangle]
unsafe extern "C" fn setpwent() {
unimplemented!()
}

View File

@ -0,0 +1,15 @@
language = "C"
style = "Type"
sys_includes = ["stdarg.h", "stddef.h"]
no_includes = true
include_guard = "_SETJMP_H"
trailer = "#include <bits/setjmp.h>"
usize_type = "size_t"
isize_type = "ssize_t"
[export]
include = ["jmp_buf"]
exclude = []

View File

@ -0,0 +1,5 @@
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
mod x86_64;
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
pub type jmp_buf = *mut x86_64::__jmp_buf;

View File

@ -0,0 +1,46 @@
.global setjmp
.global longjmp
.global _setjmp
.global _longjmp
.section .text
// args:
// %rdi -- __jmp_buf *env
setjmp:
_setjmp:
movq %rbx, 0(%rdi)
movq %rbp, 8(%rdi)
movq %r12, 16(%rdi)
movq %r13, 24(%rdi)
movq %r14, 32(%rdi)
movq %r15, 40(%rdi)
// Calculate return stack and rip
leaq 8(%rsp), %rax
movq %rax, 48(%rdi)
movq (%rsp), %rax
movq %rax, 56(%rdi)
movq $0, %rax
ret
// args:
// %rdi -- __jmp_buf *env
// %rsi -- int val
longjmp:
_longjmp:
// Restore registers
movq 0(%rdi), %rbx
movq 8(%rdi), %rbp
movq 16(%rdi), %r12
movq 24(%rdi), %r13
movq 32(%rdi), %r14
movq 40(%rdi), %r15
movq 48(%rdi), %rsp
movq 56(%rdi), %rax
pushq %rax
// Setup return value
movq %rsi, %rax
ret

View File

@ -0,0 +1,15 @@
use core::arch::global_asm;
#[repr(C)]
pub struct __jmp_buf {
rip: usize,
rsp: usize,
r15: usize,
r14: usize,
r13: usize,
r12: usize,
rbp: usize,
rbx: usize,
}
global_asm!(include_str!("x86_64.S"), options(att_syntax));

View File

@ -0,0 +1,19 @@
language = "C"
style = "Type"
sys_includes = [
"stddef.h",
"stdint.h",
"sys/types.h",
"bits/signal.h"
]
no_includes = true
include_guard = "_SIGNAL_H"
usize_type = "size_t"
isize_type = "ssize_t"
[export]
include = ["sig_handler_t", "SIGABRT"]
exclude = []

View File

@ -0,0 +1,106 @@
use core::ffi::c_int;
use yggdrasil_rt::process::Signal;
use super::sys_types::pid_t;
pub type sig_handler_t = unsafe extern "C" fn(SigNumber);
extern "C" {
fn __sig_terminate(_: SigNumber);
fn __sig_ignore(_: SigNumber);
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
#[repr(transparent)]
pub struct SigNumber(pub c_int);
// TODO generate these based on the ABI
pub const SIGSEGV: SigNumber = SigNumber(1);
pub const SIGABRT: SigNumber = SigNumber(2);
pub const SIGKILL: SigNumber = SigNumber(3);
pub const SIGINT: SigNumber = SigNumber(4);
// TODO not yet defined signals
pub const SIGALRM: SigNumber = SigNumber::INVALID;
pub const SIGBUS: SigNumber = SigNumber::INVALID;
pub const SIGCHLD: SigNumber = SigNumber::INVALID;
pub const SIGCONT: SigNumber = SigNumber::INVALID;
pub const SIGFPE: SigNumber = SigNumber::INVALID;
pub const SIGHUP: SigNumber = SigNumber::INVALID;
pub const SIGPIPE: SigNumber = SigNumber::INVALID;
pub const SIGQUIT: SigNumber = SigNumber::INVALID;
pub const SIGSTOP: SigNumber = SigNumber::INVALID;
pub const SIGTERM: SigNumber = SigNumber::INVALID;
pub const SIGTSTP: SigNumber = SigNumber::INVALID;
pub const SIGTTIN: SigNumber = SigNumber::INVALID;
pub const SIGTTOU: SigNumber = SigNumber::INVALID;
pub const SIGUSR1: SigNumber = SigNumber::INVALID;
pub const SIGUSR2: SigNumber = SigNumber::INVALID;
pub const SIGPOLL: SigNumber = SigNumber::INVALID;
pub const SIGPROF: SigNumber = SigNumber::INVALID;
pub const SIGSYS: SigNumber = SigNumber::INVALID;
pub const SIGTRAP: SigNumber = SigNumber::INVALID;
pub const SIGURG: SigNumber = SigNumber::INVALID;
pub const SIGVTALRM: SigNumber = SigNumber::INVALID;
pub const SIGXCPU: SigNumber = SigNumber::INVALID;
pub const SIGXFSZ: SigNumber = SigNumber::INVALID;
impl SigNumber {
pub const INVALID: Self = Self(-65536);
}
impl TryFrom<SigNumber> for Signal {
type Error = ();
fn try_from(value: SigNumber) -> Result<Self, Self::Error> {
match value {
SIGSEGV => Ok(Signal::MemoryAccessViolation),
SIGABRT => Ok(Signal::Aborted),
SIGKILL => Ok(Signal::Killed),
SIGINT => Ok(Signal::Interrupted),
_ => Err(()),
}
}
}
impl From<Signal> for SigNumber {
fn from(value: Signal) -> Self {
match value {
Signal::Aborted => SIGABRT,
Signal::Interrupted => SIGINT,
Signal::Killed => SIGKILL,
Signal::MemoryAccessViolation => SIGSEGV,
Signal::Debug => SIGTRAP,
}
}
}
#[no_mangle]
unsafe extern "C" fn kill(_pid: pid_t, _signum: SigNumber) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn killpg(_pid: pid_t, _signum: SigNumber) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn raise(_signum: SigNumber) -> c_int {
todo!()
}
#[no_mangle]
unsafe extern "C" fn signal(_signum: SigNumber, _handler: sig_handler_t) -> sig_handler_t {
todo!()
// // NOTE handler might be NULL, so check that
// let Ok(signal) = Signal::try_from(signum) else {
// // Ignore
// return __sig_terminate;
// };
// let handler_ptr = handler as usize;
// let handler = (handler_ptr != 0).then_some(handler);
// signal::set_handler(signal, handler)
}

View File

@ -0,0 +1,104 @@
use core::mem::MaybeUninit;
use alloc::boxed::Box;
use yggdrasil_rt::{
io::{DirectoryEntry, RawFd},
path::Path,
sys as syscall,
};
use crate::{error::EResult, headers::errno};
use super::{AsRawFd, FromRawFd};
#[derive(Debug)]
pub struct DirReader {
fd: Option<RawFd>,
// Read buffer
buffer: Box<[MaybeUninit<DirectoryEntry>]>,
position: usize,
len: usize,
}
impl DirReader {
const BUFFER_SIZE: usize = 16;
pub fn open_at<P: AsRef<Path>>(at: Option<RawFd>, path: P) -> EResult<Self> {
let fd = unsafe { syscall::open_directory(at, path.as_ref().as_str()) }?;
EResult::Ok(unsafe { Self::from_raw_fd(fd) })
}
pub unsafe fn close(&mut self) -> EResult<()> {
if let Some(fd) = self.fd.take() {
unsafe { syscall::close(fd) }?;
EResult::Ok(())
} else {
EResult::Err(errno::EBADF)
}
}
pub fn as_raw_fd(&self) -> RawFd {
self.fd.unwrap()
}
fn fill_buf(&mut self) -> EResult<&[DirectoryEntry]> {
let Some(fd) = self.fd else {
return EResult::Err(errno::EBADF);
};
if self.position == self.len {
let count = unsafe { syscall::read_directory_entries(fd, &mut self.buffer) }?;
self.position = 0;
self.len = count;
}
EResult::Ok(unsafe {
MaybeUninit::slice_assume_init_ref(&self.buffer[self.position..self.len])
})
}
fn consume(&mut self, count: usize) {
self.position = (self.position + count).min(self.len);
}
pub fn read_entry(&mut self) -> EResult<Option<DirectoryEntry>> {
let buf = self.fill_buf()?;
if let Some(&entry) = buf.get(0) {
self.consume(1);
EResult::Ok(Some(entry))
} else {
EResult::Ok(None)
}
}
}
impl AsRawFd for DirReader {
fn as_raw_fd(&self) -> RawFd {
self.fd.unwrap()
}
}
impl FromRawFd for DirReader {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self {
fd: Some(fd),
buffer: Box::new_uninit_slice(Self::BUFFER_SIZE),
position: 0,
len: 0,
}
}
}
impl Drop for DirReader {
fn drop(&mut self) {
if self.fd.is_some() {
unsafe {
self.close().ok();
}
}
}
}

View File

@ -1,6 +1,9 @@
use core::{ffi::{c_int, c_long}, mem::MaybeUninit};
use core::{ffi::c_int, mem::MaybeUninit};
use yggdrasil_rt::{io::{PipeOptions, RawFd, SeekFrom}, sys as syscall};
use yggdrasil_rt::{
io::{PipeOptions, RawFd, SeekFrom},
sys as syscall,
};
use crate::{
error::{EResult, TryFromExt},
@ -11,9 +14,10 @@ use crate::{
},
};
pub mod raw;
pub mod managed;
pub mod buffer;
pub mod managed;
pub mod raw;
pub mod dir;
pub trait Write {
fn write(&mut self, data: &[u8]) -> EResult<usize>;
@ -39,7 +43,7 @@ pub trait Write {
match self.write(data) {
EResult::Ok(n) if n == data.len() => EResult::Ok(()),
EResult::Ok(_) => todo!(),
EResult::Err(err) => EResult::Err(err)
EResult::Err(err) => EResult::Err(err),
}
}
}
@ -51,7 +55,7 @@ pub trait Read {
match self.read(buf) {
EResult::Ok(n) if n == buf.len() => EResult::Ok(()),
EResult::Ok(_) => todo!(),
EResult::Err(err) => EResult::Err(err)
EResult::Err(err) => EResult::Err(err),
}
}
}

View File

@ -1,4 +1,8 @@
use core::ffi::{c_char, CStr};
use core::ffi::{c_char, c_int, CStr};
use yggdrasil_rt::io::RawFd;
use crate::{error::{EResult, TryFromExt}, headers::{errno, fcntl::AT_FDCWD}};
pub trait PointerExt {
unsafe fn ensure(self: *const Self) -> &'static Self;
@ -48,3 +52,11 @@ impl PointerStrExt for c_char {
CStr::from_ptr(self)
}
}
pub fn at_fd(fd: c_int) -> EResult<Option<RawFd>> {
match fd {
AT_FDCWD => EResult::Ok(None),
0.. => RawFd::e_try_from(fd).map(Some),
_ => EResult::Err(errno::EBADF),
}
}