From 7a9a0ce59ec284fae3896fef30885704b2b741a3 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Sun, 9 Mar 2025 11:59:38 +0200 Subject: [PATCH] libc: extend libc I/O coverage --- userspace/lib/ygglibc/include/bits/stdio.h | 2 + .../lib/ygglibc/src/headers/aio/cbindgen.toml | 19 +++ userspace/lib/ygglibc/src/headers/aio/mod.rs | 75 +++++++++ .../lib/ygglibc/src/headers/dirent/mod.rs | 38 +++-- .../lib/ygglibc/src/headers/fcntl/mod.rs | 63 +++----- .../ygglibc/src/headers/fnmatch/cbindgen.toml | 12 ++ .../lib/ygglibc/src/headers/fnmatch/mod.rs | 15 ++ .../lib/ygglibc/src/headers/ftw/cbindgen.toml | 15 ++ userspace/lib/ygglibc/src/headers/ftw/mod.rs | 41 +++++ .../ygglibc/src/headers/glob/cbindgen.toml | 14 ++ userspace/lib/ygglibc/src/headers/glob/mod.rs | 35 +++++ .../lib/ygglibc/src/headers/libgen/mod.rs | 44 +++++- userspace/lib/ygglibc/src/headers/mod.rs | 96 +----------- userspace/lib/ygglibc/src/headers/poll/mod.rs | 2 +- .../ygglibc/src/headers/signal/cbindgen.toml | 2 +- .../lib/ygglibc/src/headers/signal/mod.rs | 2 + .../lib/ygglibc/src/headers/stdio/file.rs | 28 ++-- .../lib/ygglibc/src/headers/stdio/mod.rs | 4 +- .../ygglibc/src/headers/stdio/printf/mod.rs | 105 +++---------- .../src/headers/stdio/printf/writer.rs | 145 ++++++++++++++++++ .../src/headers/sys_stat/cbindgen.toml | 2 +- .../lib/ygglibc/src/headers/sys_stat/mod.rs | 2 + .../lib/ygglibc/src/headers/unistd/fs.rs | 36 ++++- userspace/lib/ygglibc/src/io/managed.rs | 11 ++ 24 files changed, 550 insertions(+), 258 deletions(-) create mode 100644 userspace/lib/ygglibc/src/headers/aio/cbindgen.toml create mode 100644 userspace/lib/ygglibc/src/headers/aio/mod.rs create mode 100644 userspace/lib/ygglibc/src/headers/fnmatch/cbindgen.toml create mode 100644 userspace/lib/ygglibc/src/headers/fnmatch/mod.rs create mode 100644 userspace/lib/ygglibc/src/headers/ftw/cbindgen.toml create mode 100644 userspace/lib/ygglibc/src/headers/ftw/mod.rs create mode 100644 userspace/lib/ygglibc/src/headers/glob/cbindgen.toml create mode 100644 userspace/lib/ygglibc/src/headers/glob/mod.rs create mode 100644 userspace/lib/ygglibc/src/headers/stdio/printf/writer.rs diff --git a/userspace/lib/ygglibc/include/bits/stdio.h b/userspace/lib/ygglibc/include/bits/stdio.h index ff139481..d6272948 100644 --- a/userspace/lib/ygglibc/include/bits/stdio.h +++ b/userspace/lib/ygglibc/include/bits/stdio.h @@ -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; diff --git a/userspace/lib/ygglibc/src/headers/aio/cbindgen.toml b/userspace/lib/ygglibc/src/headers/aio/cbindgen.toml new file mode 100644 index 00000000..e45fda09 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/aio/cbindgen.toml @@ -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"] diff --git a/userspace/lib/ygglibc/src/headers/aio/mod.rs b/userspace/lib/ygglibc/src/headers/aio/mod.rs new file mode 100644 index 00000000..44b19cb5 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/aio/mod.rs @@ -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(): is not implemented in Yggdrasil") +} + +#[no_mangle] +unsafe extern "C" fn aio_error(_aiocbp: *const aiocb) -> c_int { + unimplemented!("aio_error(): is not implemented in Yggdrasil") +} + +#[no_mangle] +unsafe extern "C" fn aio_fsync(_fd: c_int, _aiocbp: *mut aiocb) -> c_int { + unimplemented!("aio_fsync(): is not implemented in Yggdrasil") +} + +#[no_mangle] +unsafe extern "C" fn aio_read(_aiocbp: *mut aiocb) -> c_int { + unimplemented!("aio_read(): is not implemented in Yggdrasil") +} + +#[no_mangle] +unsafe extern "C" fn aio_return(_aiocbp: *mut aiocb) -> isize { + unimplemented!("aio_return(): 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(): is not implemented in Yggdrasil") +} + +#[no_mangle] +unsafe extern "C" fn aio_write(_aiocbp: *mut aiocb) -> c_int { + unimplemented!("aio_write(): 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(): is not implemented in Yggdrasil") +} diff --git a/userspace/lib/ygglibc/src/headers/dirent/mod.rs b/userspace/lib/ygglibc/src/headers/dirent/mod.rs index 7246c295..3d0a1dd8 100644 --- a/userspace/lib/ygglibc/src/headers/dirent/mod.rs +++ b/userspace/lib/ygglibc/src/headers/dirent/mod.rs @@ -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 { 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!(": seekdir()") } #[no_mangle] unsafe extern "C" fn rewinddir(_dir: *mut DIR) { - todo!() + todo!(": rewinddir()") } #[no_mangle] unsafe extern "C" fn telldir(_dir: *mut DIR) -> c_long { - todo!() + todo!(": 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!(": scandirat()") } #[no_mangle] unsafe extern "C" fn alphasort(_a: *const *const dirent, _b: *const *const dirent) -> c_int { - todo!() + todo!(": alphasort()") +} + +#[no_mangle] +unsafe extern "C" fn versionsort(_a: *const *const dirent, _b: *const *const dirent) -> c_int { + todo!(": alphasort()") } diff --git a/userspace/lib/ygglibc/src/headers/fcntl/mod.rs b/userspace/lib/ygglibc/src/headers/fcntl/mod.rs index d127c5c1..8ec059a0 100644 --- a/userspace/lib/ygglibc/src/headers/fcntl/mod.rs +++ b/userspace/lib/ygglibc/src/headers/fcntl/mod.rs @@ -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()") } diff --git a/userspace/lib/ygglibc/src/headers/fnmatch/cbindgen.toml b/userspace/lib/ygglibc/src/headers/fnmatch/cbindgen.toml new file mode 100644 index 00000000..46aa00e5 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/fnmatch/cbindgen.toml @@ -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] diff --git a/userspace/lib/ygglibc/src/headers/fnmatch/mod.rs b/userspace/lib/ygglibc/src/headers/fnmatch/mod.rs new file mode 100644 index 00000000..82499dda --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/fnmatch/mod.rs @@ -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()") +} diff --git a/userspace/lib/ygglibc/src/headers/ftw/cbindgen.toml b/userspace/lib/ygglibc/src/headers/ftw/cbindgen.toml new file mode 100644 index 00000000..c3a4c3c5 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/ftw/cbindgen.toml @@ -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"] diff --git a/userspace/lib/ygglibc/src/headers/ftw/mod.rs b/userspace/lib/ygglibc/src/headers/ftw/mod.rs new file mode 100644 index 00000000..baf0a666 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/ftw/mod.rs @@ -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()") +} diff --git a/userspace/lib/ygglibc/src/headers/glob/cbindgen.toml b/userspace/lib/ygglibc/src/headers/glob/cbindgen.toml new file mode 100644 index 00000000..7c92245d --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/glob/cbindgen.toml @@ -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] diff --git a/userspace/lib/ygglibc/src/headers/glob/mod.rs b/userspace/lib/ygglibc/src/headers/glob/mod.rs new file mode 100644 index 00000000..2d7d5ed7 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/glob/mod.rs @@ -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()") +} diff --git a/userspace/lib/ygglibc/src/headers/libgen/mod.rs b/userspace/lib/ygglibc/src/headers/libgen/mod.rs index 95297a20..1ca6acce 100644 --- a/userspace/lib/ygglibc/src/headers/libgen/mod.rs +++ b/userspace/lib/ygglibc/src/headers/libgen/mod.rs @@ -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 + } + } } diff --git a/userspace/lib/ygglibc/src/headers/mod.rs b/userspace/lib/ygglibc/src/headers/mod.rs index 256c1b5a..6681277a 100644 --- a/userspace/lib/ygglibc/src/headers/mod.rs +++ b/userspace/lib/ygglibc/src/headers/mod.rs @@ -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 -// ! -// ! -// ! -// ! -// ! -// ! -// ! -// ! - -// Math -// - -// - -// + - -// I/O utilities -// ! -// ! -// ! -// ! -// ! -// ! -// = -// ! -// ! -// ! - -// I/O -// ! -// + -// + -// = -// + -// = -// ! -// = -// = -// ! -// = -// ! -// ~ - -// Misc utilities -// + -// ! -// + -// = -// = -// = -// = -// ! -// = -// = -// = -// + -// = -// = -// ! - -// Process utilities -// ! -// ! -// + -// = -// = -// ! -// = -// ! -// ! -// = -// = -// = - -// Locale & string utilities -// + -// = -// = -// = -// = -// = -// ! -// + -// = -// = -// = -// ! - +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; diff --git a/userspace/lib/ygglibc/src/headers/poll/mod.rs b/userspace/lib/ygglibc/src/headers/poll/mod.rs index 2e4d9f33..42b1967c 100644 --- a/userspace/lib/ygglibc/src/headers/poll/mod.rs +++ b/userspace/lib/ygglibc/src/headers/poll/mod.rs @@ -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()") } diff --git a/userspace/lib/ygglibc/src/headers/signal/cbindgen.toml b/userspace/lib/ygglibc/src/headers/signal/cbindgen.toml index d92c5606..5b8060af 100644 --- a/userspace/lib/ygglibc/src/headers/signal/cbindgen.toml +++ b/userspace/lib/ygglibc/src/headers/signal/cbindgen.toml @@ -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 = [] diff --git a/userspace/lib/ygglibc/src/headers/signal/mod.rs b/userspace/lib/ygglibc/src/headers/signal/mod.rs index e34fe9a7..2d4e83f7 100644 --- a/userspace/lib/ygglibc/src/headers/signal/mod.rs +++ b/userspace/lib/ygglibc/src/headers/signal/mod.rs @@ -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), diff --git a/userspace/lib/ygglibc/src/headers/stdio/file.rs b/userspace/lib/ygglibc/src/headers/stdio/file.rs index 66dc2cc1..50699b36 100644 --- a/userspace/lib/ygglibc/src/headers/stdio/file.rs +++ b/userspace/lib/ygglibc/src/headers/stdio/file.rs @@ -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 { - 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(); } diff --git a/userspace/lib/ygglibc/src/headers/stdio/mod.rs b/userspace/lib/ygglibc/src/headers/stdio/mod.rs index f9eeb069..7b6c17d6 100644 --- a/userspace/lib/ygglibc/src/headers/stdio/mod.rs +++ b/userspace/lib/ygglibc/src/headers/stdio/mod.rs @@ -14,8 +14,8 @@ mod util; // ssize_t from // // stdin, stdout, stderr as externs from -// 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; diff --git a/userspace/lib/ygglibc/src/headers/stdio/printf/mod.rs b/userspace/lib/ygglibc/src/headers/stdio/printf/mod.rs index 0088887a..81ad9d2a 100644 --- a/userspace/lib/ygglibc/src/headers/stdio/printf/mod.rs +++ b/userspace/lib/ygglibc/src/headers/stdio/printf/mod.rs @@ -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, - position: usize, - capacity: usize, -} - -trait FmtWriter: Write + fmt::Write {} - -impl 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 Write for FileWriter<'_, W> { - fn write(&mut self, data: &[u8]) -> EResult { - self.0.write(data) - } - - fn flush(&mut self) -> EResult<()> { - self.0.flush() - } -} - -impl FmtWriter for FileWriter<'_, W> {} - -impl StringWriter { - pub fn new(buffer: NonNull, 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 { - 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(output: &mut W, format: &[u8], mut ap: VaList) -> EResult { let mut fmt = format.iter(); @@ -196,17 +121,31 @@ fn printf_inner(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] diff --git a/userspace/lib/ygglibc/src/headers/stdio/printf/writer.rs b/userspace/lib/ygglibc/src/headers/stdio/printf/writer.rs new file mode 100644 index 00000000..1dacba56 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/stdio/printf/writer.rs @@ -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, + position: usize, + capacity: usize, +} +pub(super) struct AllocatedStringWriter { + buffer: Vec, + limit: usize, +} + +pub(super) trait FmtWriter: Write + fmt::Write {} + +// File + +impl 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 Write for FileWriter<'_, W> { + fn write(&mut self, data: &[u8]) -> EResult { + self.0.write(data) + } + + fn flush(&mut self) -> EResult<()> { + self.0.flush() + } +} + +impl FmtWriter for FileWriter<'_, W> {} + +// String + +impl StringWriter { + pub fn new(buffer: NonNull, 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 { + 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> { + assert!(self.buffer.len() < self.limit); + let data = c_alloc(self.buffer.len() + 1, 1, false)?.cast::(); + unsafe { + let dst_slice = + NonNull::slice_from_raw_parts(data.cast::(), 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 { + 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 {} diff --git a/userspace/lib/ygglibc/src/headers/sys_stat/cbindgen.toml b/userspace/lib/ygglibc/src/headers/sys_stat/cbindgen.toml index 9a004b15..aab16e27 100644 --- a/userspace/lib/ygglibc/src/headers/sys_stat/cbindgen.toml +++ b/userspace/lib/ygglibc/src/headers/sys_stat/cbindgen.toml @@ -16,4 +16,4 @@ usize_type = "size_t" isize_type = "ssize_t" [export] -include = ["stat"] +include = ["stat", "__ygg_stat_t"] diff --git a/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs b/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs index 7f1a6319..e195f665 100644 --- a/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs +++ b/userspace/lib/ygglibc/src/headers/sys_stat/mod.rs @@ -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; diff --git a/userspace/lib/ygglibc/src/headers/unistd/fs.rs b/userspace/lib/ygglibc/src/headers/unistd/fs.rs index b74aadca..cde3cda0 100644 --- a/userspace/lib/ygglibc/src/headers/unistd/fs.rs +++ b/userspace/lib/ygglibc/src/headers/unistd/fs.rs @@ -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!() diff --git a/userspace/lib/ygglibc/src/io/managed.rs b/userspace/lib/ygglibc/src/io/managed.rs index 6fecd180..75679b60 100644 --- a/userspace/lib/ygglibc/src/io/managed.rs +++ b/userspace/lib/ygglibc/src/io/managed.rs @@ -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();