libc: extend libc I/O coverage

This commit is contained in:
Mark Poliakov 2025-03-09 11:59:38 +02:00
parent 69649f1cea
commit 7a9a0ce59e
24 changed files with 550 additions and 258 deletions

View File

@ -8,6 +8,8 @@ extern "C" {
struct __FILE;
typedef struct __FILE FILE;
#define P_tmpdir "/tmp"
extern FILE *stdin;
extern FILE *stdout;
extern FILE *stderr;

View File

@ -0,0 +1,19 @@
language = "C"
style = "Tag"
sys_includes = [
"stddef.h",
"stdint.h",
"time.h",
"signal.h",
"sys/types.h",
]
no_includes = true
include_guard = "_AIO_H"
usize_type = "size_t"
isize_type = "ssize_t"
[export]
include = ["aiocb", "__ygg_aiocb_t"]

View File

@ -0,0 +1,75 @@
use core::ffi::{c_int, c_void};
use super::{signal::__ygg_sigevent_t, sys_time::__ygg_timespec_t, sys_types::off_t};
#[repr(C)]
pub struct aiocb {
pub aio_fildes: c_int,
pub aio_offset: off_t,
pub aio_buf: *mut c_void,
pub aio_nbytes: usize,
pub aio_reqprio: c_int,
pub aio_sigevent: __ygg_sigevent_t,
pub aio_lio_opcode: c_int,
}
pub type __ygg_aiocb_t = aiocb;
pub const AIO_ALLDONE: c_int = 1 << 0;
pub const AIO_CANCELED: c_int = 1 << 1;
pub const AIO_NOTCANCELED: c_int = 1 << 2;
pub const LIO_NOP: c_int = 1;
pub const LIO_READ: c_int = 2;
pub const LIO_WRITE: c_int = 3;
pub const LIO_NOWAIT: c_int = 4;
pub const LIO_WAIT: c_int = 5;
#[no_mangle]
unsafe extern "C" fn aio_cancel(_fd: c_int, _aiocbp: *mut aiocb) -> c_int {
unimplemented!("aio_cancel(): <aio.h> is not implemented in Yggdrasil")
}
#[no_mangle]
unsafe extern "C" fn aio_error(_aiocbp: *const aiocb) -> c_int {
unimplemented!("aio_error(): <aio.h> is not implemented in Yggdrasil")
}
#[no_mangle]
unsafe extern "C" fn aio_fsync(_fd: c_int, _aiocbp: *mut aiocb) -> c_int {
unimplemented!("aio_fsync(): <aio.h> is not implemented in Yggdrasil")
}
#[no_mangle]
unsafe extern "C" fn aio_read(_aiocbp: *mut aiocb) -> c_int {
unimplemented!("aio_read(): <aio.h> is not implemented in Yggdrasil")
}
#[no_mangle]
unsafe extern "C" fn aio_return(_aiocbp: *mut aiocb) -> isize {
unimplemented!("aio_return(): <aio.h> is not implemented in Yggdrasil")
}
#[no_mangle]
unsafe extern "C" fn aio_suspend(
_aiocbps: *const *const aiocb,
_fd: c_int,
_timeout: *const __ygg_timespec_t,
) -> c_int {
unimplemented!("aio_suspend(): <aio.h> is not implemented in Yggdrasil")
}
#[no_mangle]
unsafe extern "C" fn aio_write(_aiocbp: *mut aiocb) -> c_int {
unimplemented!("aio_write(): <aio.h> is not implemented in Yggdrasil")
}
#[no_mangle]
unsafe extern "C" fn lio_listio(
_fd: c_int,
_aiocb: *const aiocb,
_opts: c_int,
_sigevent: *mut __ygg_sigevent_t,
) -> c_int {
unimplemented!("lio_listio(): <aio.h> is not implemented in Yggdrasil")
}

View File

@ -13,7 +13,7 @@ use crate::{
util::{PointerExt, PointerStrExt},
};
use super::{errno::Errno, sys_types::ino_t};
use super::{errno::Errno, fcntl::AT_FDCWD, sys_types::ino_t};
#[derive(Debug)]
#[repr(C)]
@ -118,46 +118,52 @@ unsafe extern "C" fn readdir(dir: *mut DIR) -> CPtrResult<dirent> {
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!()
todo!("<dirent.h>: seekdir()")
}
#[no_mangle]
unsafe extern "C" fn rewinddir(_dir: *mut DIR) {
todo!()
todo!("<dirent.h>: rewinddir()")
}
#[no_mangle]
unsafe extern "C" fn telldir(_dir: *mut DIR) -> c_long {
todo!()
todo!("<dirent.h>: telldir()")
}
// 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 {
scandirat(AT_FDCWD, dirp, namelist, filter, compar)
}
#[no_mangle]
unsafe extern "C" fn scandirat(
_dirfd: c_int,
_dirp: *const c_char,
_namelist: *mut *mut *mut dirent,
_filter: __scandir_filter_fn_t,
_compar: __scandir_compar_fn_t,
) -> c_int {
todo!()
todo!("<dirent.h>: scandirat()")
}
#[no_mangle]
unsafe extern "C" fn alphasort(_a: *const *const dirent, _b: *const *const dirent) -> c_int {
todo!()
todo!("<dirent.h>: alphasort()")
}
#[no_mangle]
unsafe extern "C" fn versionsort(_a: *const *const dirent, _b: *const *const dirent) -> c_int {
todo!("<dirent.h>: alphasort()")
}

View File

@ -1,12 +1,9 @@
use core::ffi::{c_char, c_int, c_short, VaList};
use yggdrasil_rt::{
io::{AccessMode, FileMode, OpenOptions},
sys as syscall,
};
use yggdrasil_rt::io::{FileMode, OpenOptions};
use crate::{
error::{CFdResult, CIntCountResult, CIntZeroResult, EResult, ResultExt, TryFromExt},
error::{CFdResult, CIntCountResult, EResult, TryFromExt},
io::{raw::RawFile, IntoRawFd},
util::{self, PointerStrExt},
};
@ -16,16 +13,6 @@ use super::{
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,
@ -81,10 +68,12 @@ 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;
pub const R_OK: c_int = 1 << 0;
pub const W_OK: c_int = 1 << 1;
pub const X_OK: c_int = 1 << 2;
pub const F_OK: c_int = 0;
pub const POSIX_FADV_DONTNEED: c_int = 1;
pub const POSIX_FADV_NOREUSE: c_int = 2;
pub const POSIX_FADV_NORMAL: c_int = 3;
pub const POSIX_FADV_RANDOM: c_int = 4;
pub const POSIX_FADV_SEQUENTIAL: c_int = 5;
pub const POSIX_FADV_WILLNEED: c_int = 6;
enum OpenMode {
File(OpenOptions, FileMode),
@ -155,6 +144,7 @@ unsafe extern "C" fn fcntl(fd: c_int, cmd: c_int, _args: ...) -> CIntCountResult
match cmd {
F_GETFD => CIntCountResult::success(0),
// The only defined option is FD_CLOEXEC, which is not supported
F_SETFD => CIntCountResult::success(0),
_ => {
todo!("fcntl({}, {}, ...)", fd, cmd);
@ -195,25 +185,18 @@ unsafe extern "C" fn openat(
}
#[no_mangle]
pub(crate) unsafe extern "C" fn faccessat(
atfd: c_int,
path: *const c_char,
mode: c_int,
_flags: c_int,
) -> CIntZeroResult {
let atfd = util::at_fd(atfd)?;
let path = path.ensure_str();
let mut access = AccessMode::empty();
if mode & R_OK != 0 {
access |= AccessMode::READ;
}
if mode & W_OK != 0 {
access |= AccessMode::WRITE;
}
if mode & X_OK != 0 {
access |= AccessMode::EXEC;
}
syscall::check_access(atfd, path, access).e_map_err(Errno::from)?;
CIntZeroResult::SUCCESS
unsafe extern "C" fn posix_fadvise(
_fd: c_int,
_offset: off_t,
_len: off_t,
_advice: c_int,
) -> c_int {
log::warn!("posix_fadvise() not implemented yet");
0
}
#[no_mangle]
unsafe extern "C" fn posix_fallocate(_fd: c_int, _offset: off_t, _size: off_t) -> c_int {
// Can be a truncate() call with some extra arg for future compat
todo!("posix_fallocate()")
}

View File

@ -0,0 +1,12 @@
language = "C"
style = "Type"
sys_includes = []
no_includes = true
include_guard = "_FNMATCH_H"
usize_type = "size_t"
isize_type = "ssize_t"
[export]

View File

@ -0,0 +1,15 @@
use core::ffi::{c_char, c_int};
pub const FNM_NOMATCH: c_int = 1;
pub const FNM_PATHNAME: c_int = 1 << 0;
pub const FNM_NOESCAPE: c_int = 1 << 1;
pub const FNM_PERIOD: c_int = 1 << 2;
#[no_mangle]
unsafe extern "C" fn fnmatch(
_pattern: *const c_char,
_name: *const c_char,
_flags: c_int,
) -> c_int {
todo!("fnmatch()")
}

View File

@ -0,0 +1,15 @@
language = "C"
style = "Tag"
sys_includes = [
"sys/stat.h",
]
no_includes = true
include_guard = "_FTW_H"
usize_type = "size_t"
isize_type = "ssize_t"
[export]
include = ["FTW"]

View File

@ -0,0 +1,41 @@
use core::ffi::{c_char, c_int};
use super::sys_stat::__ygg_stat_t;
#[repr(C)]
pub struct FTW {
pub base: c_int,
pub level: c_int,
}
pub const FTW_F: c_int = 1;
pub const FTW_D: c_int = 2;
pub const FTW_DNR: c_int = 3;
pub const FTW_DP: c_int = 4;
pub const FTW_NS: c_int = 5;
pub const FTW_SL: c_int = 6;
pub const FTW_SLN: c_int = 7;
pub const FTW_PHYS: c_int = 1;
pub const FTW_MOUNT: c_int = 2;
pub const FTW_DEPTH: c_int = 3;
pub const FTW_CHDIR: c_int = 4;
pub type __ftw_func_t = extern "C" fn(*const c_char, *const __ygg_stat_t, c_int) -> c_int;
pub type __nftw_func_t =
extern "C" fn(*const c_char, *const __ygg_stat_t, c_int, *mut FTW) -> c_int;
#[no_mangle]
unsafe extern "C" fn ftw(_dir: *const c_char, _func: __ftw_func_t, _fds: c_int) -> c_int {
todo!("ftw()")
}
#[no_mangle]
unsafe extern "C" fn nftw(
_dir: *const c_char,
_func: __nftw_func_t,
_fds: c_int,
_flag: c_int,
) -> c_int {
todo!("nftw()")
}

View File

@ -0,0 +1,14 @@
language = "C"
style = "Type"
sys_includes = [
"sys/types.h"
]
no_includes = true
include_guard = "_GLOB_H"
usize_type = "size_t"
isize_type = "ssize_t"
[export]

View File

@ -0,0 +1,35 @@
use core::ffi::{c_char, c_int};
#[repr(C)]
pub struct glob_t {
pub gl_pathc: usize,
pub gl_pathv: *mut *mut c_char,
pub gl_offs: usize,
}
pub const GLOB_APPEND: c_int = 1 << 0;
pub const GLOB_DOOFFS: c_int = 1 << 1;
pub const GLOB_ERR: c_int = 1 << 2;
pub const GLOB_MARK: c_int = 1 << 3;
pub const GLOB_NOCHECK: c_int = 1 << 4;
pub const GLOB_NOESCAPE: c_int = 1 << 5;
pub const GLOB_NOSORT: c_int = 1 << 6;
pub const GLOB_ABORTED: c_int = -1;
pub const GLOB_NOMATCH: c_int = -2;
pub const GLOB_NOSPACE: c_int = -3;
#[no_mangle]
unsafe extern "C" fn glob(
_pattern: *const c_char,
_flags: c_int,
_errfunc: extern "C" fn(*const c_char, c_int) -> c_int,
_pglob: *mut glob_t,
) -> c_int {
todo!("glob()")
}
#[no_mangle]
unsafe extern "C" fn globfree(_pglob: *mut glob_t) {
todo!("globfree()")
}

View File

@ -1,11 +1,47 @@
use core::ffi::c_char;
use crate::util::PointerStrExt;
const DOT: *mut c_char = c".".as_ptr().cast_mut();
#[no_mangle]
unsafe extern "C" fn basename(_path: *mut c_char) -> *mut c_char {
todo!()
unsafe extern "C" fn basename(path: *mut c_char) -> *mut c_char {
if path.is_null() {
return DOT;
}
let path_str = path.cast_const().ensure_str();
if path_str == "" {
DOT
} else if path_str == "/" {
path
} else {
let path_str = path_str.trim_end_matches('/');
if let Some(last_slash) = path_str.rfind('/') {
path.add(last_slash + 1)
} else {
path
}
}
}
#[no_mangle]
unsafe extern "C" fn dirname(_path: *mut c_char) -> *mut c_char {
todo!()
unsafe extern "C" fn dirname(path: *mut c_char) -> *mut c_char {
if path.is_null() {
return DOT;
}
let path_str = path.cast_const().ensure_str();
if path_str.is_empty() {
DOT
} else if path_str == "/" {
path
} else {
let path_str = path_str.trim_end_matches('/');
if let Some(last_slash) = path_str.rfind('/') {
// Turn last slash into '\0' to terminate dirname
path.add(last_slash).write(0);
path
} else {
DOT
}
}
}

View File

@ -1,102 +1,14 @@
#![allow(non_camel_case_types, non_upper_case_globals)]
// - not yet implemented
// = partially implemented
// + fully implemented
// ! out of scope for now
// Network
// <arpa/inet.h> !
// <ndbm.h> !
// <net/if.h> !
// <netdb.h> !
// <netinet/in.h> !
// <netinet/tcp.h> !
// <sys/socket.h> !
// <sys/un.h> !
// Math
// <complex.h> -
// <fenv.h> -
// <math.h> +
// I/O utilities
// <cpio.h> !
// <fmtmsg.h> !
// <fnmatch.h> !
// <ftw.h> !
// <glob.h> !
// <mqueue.h> !
// <stdlib.h> =
// <stropts.h> !
// <sys/uio.h> !
// <tar.h> !
// I/O
// <aio.h> !
// <dirent.h> +
// <fcntl.h> +
// <poll.h> =
// <stdio.h> +
// <sys/select.h> =
// <sys/shm.h> !
// <sys/stat.h> =
// <sys/statvfs.h> =
// <syslog.h> !
// <termios.h> =
// <trace.h> !
// <unistd.h> ~
// Misc utilities
// <assert.h> +
// <dlfcn.h> !
// <errno.h> +
// <grp.h> =
// <libgen.h> =
// <pwd.h> =
// <sched.h> =
// <search.h> !
// <sys/resource.h> =
// <sys/time.h> =
// <sys/times.h> =
// <sys/types.h> +
// <sys/utsname.h> =
// <time.h> =
// <utmpx.h> !
// Process utilities
// <pthread.h> !
// <semaphore.h> !
// <setjmp.h> +
// <signal.h> =
// <spawn.h> =
// <sys/ipc.h> !
// <sys/mman.h> =
// <sys/msg.h> !
// <sys/sem.h> !
// <sys/wait.h> =
// <ulimit.h> =
// <utime.h> =
// Locale & string utilities
// <ctype.h> +
// <iconv.h> =
// <langinfo.h> =
// <locale.h> =
// <monetary.h> =
// <nl_types.h> =
// <regex.h> !
// <string.h> +
// <strings.h> =
// <wchar.h> =
// <wctype.h> =
// <wordexp.h> !
pub mod aio;
pub mod ctype;
pub mod dirent;
pub mod dlfcn;
pub mod errno;
pub mod fcntl;
pub mod fnmatch;
pub mod ftw;
pub mod glob;
pub mod grp;
pub mod iconv;
pub mod langinfo;

View File

@ -24,5 +24,5 @@ pub const POLLWRBAND: c_short = 1 << 9;
#[no_mangle]
unsafe extern "C" fn poll(_fds: *mut pollfd, _nfds: nfds_t, _flags: c_int) -> c_int {
todo!()
todo!("poll()")
}

View File

@ -19,5 +19,5 @@ isize_type = "ssize_t"
enum_style = "define"
[export]
include = ["sig_handler_t", "sigaction", "sigevent", "SIGABRT"]
include = ["sig_handler_t", "sigaction", "sigevent", "__ygg_sigevent_t", "SIGABRT"]
exclude = []

View File

@ -40,6 +40,8 @@ pub struct sigevent {
pub sigev_notify_attributes: *mut c_void,
}
pub type __ygg_sigevent_t = sigevent;
#[repr(C)]
pub struct sigaction {
pub sa_handler: unsafe extern "C" fn(c_int),

View File

@ -183,8 +183,13 @@ unsafe extern "C" fn ftello(fp: *mut FILE) -> COffsetResult {
}
#[no_mangle]
unsafe extern "C" fn ftrylockfile(_fp: *mut FILE) -> c_int {
unimplemented!()
unsafe extern "C" fn ftrylockfile(fp: *mut FILE) -> c_int {
let fp = fp.ensure_mut();
if fp.try_lock() {
0
} else {
-1
}
}
#[no_mangle]
@ -203,12 +208,12 @@ unsafe extern "C" fn open_memstream(
#[no_mangle]
unsafe extern "C" fn pclose(_fp: *mut FILE) -> c_int {
todo!()
todo!("pclose()")
}
#[no_mangle]
unsafe extern "C" fn popen(_command: *const c_char, _ty: *const c_char) -> CPtrResult<FILE> {
todo!()
todo!("popen()")
}
#[no_mangle]
@ -244,21 +249,11 @@ unsafe extern "C" fn setvbuf(
CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn fpurge(_fp: *mut FILE) {
todo!("fpurge()")
}
#[no_mangle]
unsafe extern "C" fn __fpurge(_fp: *mut FILE) {
todo!("__fpurge()")
}
#[no_mangle]
unsafe extern "C" fn freading(fp: *mut FILE) -> c_int {
__freading(fp)
}
#[no_mangle]
unsafe extern "C" fn __freading(fp: *mut FILE) -> c_int {
let fp = fp.ensure_mut();
@ -302,6 +297,7 @@ unsafe extern "C" fn __fwriting(fp: *mut FILE) -> c_int {
}
#[no_mangle]
unsafe extern "C" fn __fseterr(_fp: *mut FILE) {
todo!("__fseterr()")
unsafe extern "C" fn __fseterr(fp: *mut FILE) {
let fp = fp.ensure_mut();
fp.set_error();
}

View File

@ -14,8 +14,8 @@ mod util;
// ssize_t from <sys/types.h>
//
// stdin, stdout, stderr as externs from <bits/stdio.h>
// TODO L_ctermid
pub const L_tmpnam: c_int = 256;
pub const L_tmpnam: c_int = 64;
pub const L_ctermid: c_int = 64;
pub const _IOFBF: c_int = 0;
pub const _IOLBF: c_int = 1;

View File

@ -1,98 +1,23 @@
use core::{
ffi::{c_char, c_int, VaList},
fmt,
ptr::NonNull,
};
use format::{FmtOpts, FmtRadix, FmtSign, FmtSize, FmtSpec};
use writer::{AllocatedStringWriter, FileWriter, FmtWriter, StringWriter};
use crate::{
error::{CIntCountResult, EResult, TryFromExt},
io::{
managed::{stdout, FILE},
raw::RawFile,
Write,
},
util::{PointerExt, PointerStrExt},
};
mod float;
mod format;
struct FileWriter<'w, W: Write>(&'w mut W);
struct StringWriter {
buffer: NonNull<c_char>,
position: usize,
capacity: usize,
}
trait FmtWriter: Write + fmt::Write {}
impl<W: Write> fmt::Write for FileWriter<'_, W> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.0
.write_all(s.as_bytes())
.into_result(|_| fmt::Error, true)
}
}
impl<W: Write> Write for FileWriter<'_, W> {
fn write(&mut self, data: &[u8]) -> EResult<usize> {
self.0.write(data)
}
fn flush(&mut self) -> EResult<()> {
self.0.flush()
}
}
impl<W: Write> FmtWriter for FileWriter<'_, W> {}
impl StringWriter {
pub fn new(buffer: NonNull<c_char>, capacity: usize) -> Self {
Self {
buffer,
capacity,
position: 0,
}
}
fn finish(&mut self) {
if self.position < self.capacity {
unsafe { self.buffer.add(self.position).write(0) };
}
}
}
impl fmt::Write for StringWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write(s.as_bytes()).into_result(|_| fmt::Error, true)?;
Ok(())
}
}
impl Write for StringWriter {
fn write(&mut self, data: &[u8]) -> EResult<usize> {
let count = core::cmp::min(self.capacity - self.position, data.len());
if count > 0 {
let dst = unsafe {
core::slice::from_raw_parts_mut(
self.buffer.add(self.position).as_ptr().cast(),
count,
)
};
dst.copy_from_slice(&data[..count]);
self.position += count;
}
EResult::Ok(count)
}
fn flush(&mut self) -> EResult<()> {
EResult::Ok(())
}
}
impl FmtWriter for StringWriter {}
mod writer;
fn printf_inner<W: FmtWriter>(output: &mut W, format: &[u8], mut ap: VaList) -> EResult<usize> {
let mut fmt = format.iter();
@ -196,17 +121,31 @@ fn printf_inner<W: FmtWriter>(output: &mut W, format: &[u8], mut ap: VaList) ->
}
#[no_mangle]
unsafe extern "C" fn asprintf(dst: *mut *mut c_char, fmt: *const c_char, mut args: ...) -> c_int {
unsafe extern "C" fn asprintf(
dst: *mut *mut c_char,
fmt: *const c_char,
mut args: ...
) -> CIntCountResult {
vasprintf(dst, fmt, args.as_va_list())
}
#[no_mangle]
unsafe extern "C" fn vasprintf(
_dst: *mut *mut c_char,
_fmt: *const c_char,
_args: VaList,
) -> c_int {
todo!()
dst: *mut *mut c_char,
fmt: *const c_char,
args: VaList,
) -> CIntCountResult {
let dst = dst.ensure_mut();
let fmt = fmt.ensure_cstr();
if !(*dst).is_null() {
// Not sure if the previous pointer needs to be free()d
todo!()
}
let mut writer = AllocatedStringWriter::new(usize::MAX);
let count = printf_inner(&mut writer, fmt.to_bytes(), args)?;
let ptr = writer.finish()?;
*dst = ptr.as_ptr();
CIntCountResult::success(count.try_into().unwrap())
}
#[no_mangle]

View File

@ -0,0 +1,145 @@
use core::{ffi::c_char, fmt, ptr::NonNull};
use alloc::vec::Vec;
use crate::{
allocator::c_alloc,
error::{self, EResult},
headers::errno::Errno,
io::Write,
};
pub(super) struct FileWriter<'w, W: Write>(pub(super) &'w mut W);
pub(super) struct StringWriter {
buffer: NonNull<c_char>,
position: usize,
capacity: usize,
}
pub(super) struct AllocatedStringWriter {
buffer: Vec<u8>,
limit: usize,
}
pub(super) trait FmtWriter: Write + fmt::Write {}
// File
impl<W: Write> fmt::Write for FileWriter<'_, W> {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.0
.write_all(s.as_bytes())
.into_result(|_| fmt::Error, true)
}
}
impl<W: Write> Write for FileWriter<'_, W> {
fn write(&mut self, data: &[u8]) -> EResult<usize> {
self.0.write(data)
}
fn flush(&mut self) -> EResult<()> {
self.0.flush()
}
}
impl<W: Write> FmtWriter for FileWriter<'_, W> {}
// String
impl StringWriter {
pub fn new(buffer: NonNull<c_char>, capacity: usize) -> Self {
Self {
buffer,
capacity,
position: 0,
}
}
pub fn finish(&mut self) {
if self.position < self.capacity {
unsafe { self.buffer.add(self.position).write(0) };
}
}
}
impl fmt::Write for StringWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
self.write(s.as_bytes()).into_result(|_| fmt::Error, true)?;
Ok(())
}
}
impl Write for StringWriter {
fn write(&mut self, data: &[u8]) -> EResult<usize> {
let count = core::cmp::min(self.capacity - self.position, data.len());
if count > 0 {
let dst = unsafe {
core::slice::from_raw_parts_mut(
self.buffer.add(self.position).as_ptr().cast(),
count,
)
};
dst.copy_from_slice(&data[..count]);
self.position += count;
}
EResult::Ok(count)
}
fn flush(&mut self) -> EResult<()> {
EResult::Ok(())
}
}
impl FmtWriter for StringWriter {}
// Allocated string
impl AllocatedStringWriter {
pub fn new(limit: usize) -> Self {
Self {
buffer: Vec::new(),
limit,
}
}
pub fn finish(self) -> EResult<NonNull<c_char>> {
assert!(self.buffer.len() < self.limit);
let data = c_alloc(self.buffer.len() + 1, 1, false)?.cast::<c_char>();
unsafe {
let dst_slice =
NonNull::slice_from_raw_parts(data.cast::<u8>(), self.buffer.len() + 1).as_mut();
dst_slice[..self.buffer.len()].copy_from_slice(&self.buffer[..]);
dst_slice[self.buffer.len()] = 0;
}
EResult::Ok(data)
}
}
impl Write for AllocatedStringWriter {
fn write(&mut self, data: &[u8]) -> EResult<usize> {
let amount = (self.limit - self.buffer.len()).min(data.len());
if self.buffer.try_reserve(amount).is_err() {
return EResult::Err(Errno::ENOMEM);
}
self.buffer.extend_from_slice(&data[..amount]);
EResult::Ok(data.len())
}
fn flush(&mut self) -> EResult<()> {
EResult::Ok(())
}
}
impl fmt::Write for AllocatedStringWriter {
fn write_str(&mut self, s: &str) -> fmt::Result {
match self.write_all(s.as_bytes()) {
EResult::Ok(()) => Ok(()),
EResult::Err(e) => {
unsafe { error::errno = e };
Err(fmt::Error)
}
}
}
}
impl FmtWriter for AllocatedStringWriter {}

View File

@ -16,4 +16,4 @@ usize_type = "size_t"
isize_type = "ssize_t"
[export]
include = ["stat"]
include = ["stat", "__ygg_stat_t"]

View File

@ -38,6 +38,8 @@ pub struct stat {
pub st_blocks: blkcnt_t,
}
pub type __ygg_stat_t = stat;
pub const S_IFMT: c_int = 0xF << 16;
pub const S_IFBLK: c_int = 1 << 16;
pub const S_IFCHR: c_int = 2 << 16;

View File

@ -3,13 +3,16 @@ use core::{
ptr::{null_mut, NonNull},
};
use yggdrasil_rt::io::RemoveFlags;
use yggdrasil_rt::{
io::{AccessMode, RemoveFlags},
sys as syscall,
};
use crate::{
error::{self, CIntZeroResult, CPtrResult, EResult, ResultExt, TryFromExt},
headers::{
errno::Errno,
fcntl::{faccessat, AT_FDCWD, AT_REMOVEDIR},
fcntl::{AT_FDCWD, AT_REMOVEDIR},
sys_types::{gid_t, off_t, uid_t},
},
io::{self, raw::RawFile, FromRawFd},
@ -18,6 +21,11 @@ use crate::{
pub const _PC_PATH_MAX: c_int = 0;
pub const R_OK: c_int = 1 << 0;
pub const W_OK: c_int = 1 << 1;
pub const X_OK: c_int = 1 << 2;
pub const F_OK: c_int = 0;
#[no_mangle]
unsafe extern "C" fn access(path: *const c_char, mode: c_int) -> CIntZeroResult {
faccessat(AT_FDCWD, path, mode, 0)
@ -35,6 +43,30 @@ unsafe extern "C" fn chown(path: *const c_char, uid: uid_t, gid: gid_t) -> c_int
todo!()
}
#[no_mangle]
pub(crate) unsafe extern "C" fn faccessat(
atfd: c_int,
path: *const c_char,
mode: c_int,
_flags: c_int,
) -> CIntZeroResult {
let atfd = util::at_fd(atfd)?;
let path = path.ensure_str();
let mut access = AccessMode::empty();
if mode & R_OK != 0 {
access |= AccessMode::READ;
}
if mode & W_OK != 0 {
access |= AccessMode::WRITE;
}
if mode & X_OK != 0 {
access |= AccessMode::EXEC;
}
syscall::check_access(atfd, path, access).e_map_err(Errno::from)?;
CIntZeroResult::SUCCESS
}
#[no_mangle]
unsafe extern "C" fn fchdir(fd: c_int) -> c_int {
todo!()

View File

@ -30,6 +30,7 @@ use super::{
};
macro locked_op($self:expr, $op:expr) {{
#![allow(unused_unsafe)]
$self.lock.lock();
let result = unsafe { $op };
unsafe { $self.lock.release() };
@ -286,6 +287,10 @@ impl FILE {
self.flags.remove(FileFlags::ERROR);
}
pub fn try_lock(&self) -> bool {
self.lock.try_lock()
}
pub unsafe fn lock(&mut self) {
self.lock.lock();
}
@ -348,6 +353,12 @@ impl FILE {
locked_op!(self, self.is_line_buffered_locked())
}
pub fn set_error(&mut self) {
locked_op!(self, {
self.flags |= FileFlags::ERROR;
});
}
// pub fn reset(&mut self) {
// if let Some(read_buffer) = self.read_buffer.as_mut() {
// read_buffer.reset();