From 0a904a21fef178c37672089f0af212220cb5c76c Mon Sep 17 00:00:00 2001 From: Mark Poliakov <mark@alnyan.me> Date: Tue, 12 Nov 2024 11:01:39 +0200 Subject: [PATCH] libc: ctype.h, most of stdio.h, string.h --- userspace/lib/ygglibc/include/bits/ctype.h | 7 + userspace/lib/ygglibc/src/allocator.rs | 76 +++-- userspace/lib/ygglibc/src/error.rs | 15 +- .../ygglibc/src/headers/ctype/cbindgen.toml | 15 + .../lib/ygglibc/src/headers/ctype/mod.rs | 161 ++++++++++ .../lib/ygglibc/src/headers/errno/mod.rs | 5 +- userspace/lib/ygglibc/src/headers/mod.rs | 9 +- .../lib/ygglibc/src/headers/stdio/file.rs | 148 ++++++--- .../lib/ygglibc/src/headers/stdio/get_put.rs | 280 ++++++++++++++++++ userspace/lib/ygglibc/src/headers/stdio/io.rs | 99 +------ .../lib/ygglibc/src/headers/stdio/mod.rs | 17 +- .../lib/ygglibc/src/headers/stdio/util.rs | 47 ++- .../lib/ygglibc/src/headers/string/str.rs | 59 ++-- userspace/lib/ygglibc/src/io/managed.rs | 268 +++++++++++++++-- userspace/lib/ygglibc/src/io/mod.rs | 19 +- 15 files changed, 980 insertions(+), 245 deletions(-) create mode 100644 userspace/lib/ygglibc/include/bits/ctype.h create mode 100644 userspace/lib/ygglibc/src/headers/ctype/cbindgen.toml create mode 100644 userspace/lib/ygglibc/src/headers/ctype/mod.rs create mode 100644 userspace/lib/ygglibc/src/headers/stdio/get_put.rs diff --git a/userspace/lib/ygglibc/include/bits/ctype.h b/userspace/lib/ygglibc/include/bits/ctype.h new file mode 100644 index 00000000..bf667a49 --- /dev/null +++ b/userspace/lib/ygglibc/include/bits/ctype.h @@ -0,0 +1,7 @@ +#ifndef _YGGDRASIL_CTYPE_H +#define _YGGDRASIL_CTYPE_H 1 + +#define _tolower(ch) tolower(ch) +#define _toupper(ch) toupper(ch) + +#endif diff --git a/userspace/lib/ygglibc/src/allocator.rs b/userspace/lib/ygglibc/src/allocator.rs index 32e68751..3c8164f6 100644 --- a/userspace/lib/ygglibc/src/allocator.rs +++ b/userspace/lib/ygglibc/src/allocator.rs @@ -1,18 +1,26 @@ use core::{ - alloc::{GlobalAlloc, Layout}, ffi::c_void, ptr::{self, null, null_mut, NonNull} + alloc::{GlobalAlloc, Layout}, + ffi::c_void, + ptr::{self, null_mut, NonNull}, }; use libyalloc::{allocator::BucketAllocator, sys::PageProvider}; use yggdrasil_rt::{mem::MappingSource, sys as syscall}; -use crate::{error::EResult, headers::errno}; +use crate::{ + error::EResult, + headers::{ + errno, + string::{memcpy, memset}, + }, sync::Mutex, +}; struct Allocator; struct PageProviderImpl; unsafe impl GlobalAlloc for Allocator { unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let pointer = YALLOC.allocate(layout); + let pointer = YALLOC.lock().allocate(layout); match pointer { Some(ptr) => ptr.as_ptr(), None => null_mut(), @@ -21,7 +29,7 @@ unsafe impl GlobalAlloc for Allocator { unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { let pointer = NonNull::new(ptr).expect("NULL pointer passed to dealloc()"); - YALLOC.free(pointer, layout); + YALLOC.lock().free(pointer, layout); } } @@ -43,10 +51,10 @@ impl PageProvider for PageProviderImpl { } } -const MALLOC_HEADER_SIZE: usize = size_of::<usize>(); +const MALLOC_HEADER_SIZE: usize = 16; unsafe fn get_allocation(ptr: NonNull<u8>) -> (NonNull<u8>, Layout) { - assert!(usize::from(ptr.addr()) > 0x10); + assert!(usize::from(ptr.addr()) > MALLOC_HEADER_SIZE); let real_ptr = ptr.sub(MALLOC_HEADER_SIZE); let size = *real_ptr.cast::<usize>().as_ref(); let layout = Layout::from_size_align(size, 16).unwrap(); @@ -54,49 +62,69 @@ unsafe fn get_allocation(ptr: NonNull<u8>) -> (NonNull<u8>, Layout) { } pub fn c_alloc(size: usize, mut align: usize, zero: bool) -> EResult<NonNull<c_void>> { - assert!(align.is_power_of_two()); - if align < align_of::<usize>() { - align = align_of::<usize>(); + if align < 16 { + align = 16; } if align > 16 { todo!() } - let offset = (MALLOC_HEADER_SIZE + align - 1) & !(align - 1); - let size = offset + size; + let size = size + MALLOC_HEADER_SIZE; let layout = Layout::from_size_align(size, align).unwrap(); - let ptr = match unsafe { YALLOC.allocate(layout) } { + let ptr = match YALLOC.lock().allocate(layout) { Some(value) => value, None => return EResult::Err(errno::ENOMEM), }; if zero { - // TODO + unsafe { + memset(ptr.as_ptr().cast(), 0, size); + } } unsafe { // Write the size right below the pointer - ptr.add(offset - size_of::<usize>()).cast::<usize>().write(size); + ptr.cast::<usize>().write(size); } - unsafe { EResult::Ok(ptr.cast::<c_void>().add(offset)) } + unsafe { EResult::Ok(ptr.cast::<c_void>().add(MALLOC_HEADER_SIZE)) } +} + +pub unsafe fn c_realloc(old_ptr: Option<NonNull<c_void>>, size: usize) -> EResult<NonNull<c_void>> { + match old_ptr { + Some(old_ptr) => { + // TODO libyalloc realloc + let (real_old_ptr, old_layout) = get_allocation(old_ptr.cast()); + let new_ptr = c_alloc(size, 16, false)?; + + memcpy( + new_ptr.cast().as_ptr(), + old_ptr.cast().as_ptr(), + old_layout.size() - MALLOC_HEADER_SIZE, + ); + + YALLOC.lock().free(real_old_ptr, old_layout); + + EResult::Ok(new_ptr) + } + None => c_alloc(size, 16, false), + } +} + +pub unsafe fn c_free(ptr: NonNull<c_void>) { + let (real_ptr, layout) = get_allocation(ptr.cast()); + YALLOC.lock().free(real_ptr, layout); } pub unsafe fn malloc(size: usize) -> EResult<NonNull<c_void>> { - todo!() - // // TODO errno setting - // match c_alloc(size, 16, false) { - // Some(ptr) => ptr.as_ptr(), - // None => null_mut() - // } + c_alloc(size, 16, false) } pub unsafe fn free(ptr: NonNull<c_void>) { - todo!() + c_free(ptr) } #[global_allocator] static ALLOCATOR: Allocator = Allocator; -// TODO locking for multithreaded binaries -static mut YALLOC: BucketAllocator<PageProviderImpl> = BucketAllocator::new(); +static YALLOC: Mutex<BucketAllocator<PageProviderImpl>> = Mutex::new(BucketAllocator::new()); diff --git a/userspace/lib/ygglibc/src/error.rs b/userspace/lib/ygglibc/src/error.rs index 58a6bc54..199c32f6 100644 --- a/userspace/lib/ygglibc/src/error.rs +++ b/userspace/lib/ygglibc/src/error.rs @@ -27,6 +27,10 @@ macro impl_from_residual($($ty:ty),+) { #[allow(non_upper_case_globals)] pub static mut errno: Errno = Errno(0); +pub trait ResultExt<T, E> { + fn e_map_err<F: FnOnce(E) -> Errno>(self, map: F) -> EResult<T>; +} + pub trait CResult { const ERROR: Self; } @@ -65,7 +69,7 @@ pub struct CFdResult(c_int); #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(transparent)] -pub struct COffsetResult(off_t); +pub struct COffsetResult(pub(crate) off_t); #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(transparent)] @@ -124,6 +128,15 @@ impl<T> EResult<T> { } } +impl<T, E> ResultExt<T, E> for Result<T, E> { + fn e_map_err<F: FnOnce(E) -> Errno>(self, map: F) -> EResult<T> { + match self { + Ok(value) => EResult::Ok(value), + Err(value) => EResult::Err(map(value)) + } + } +} + impl<T> Try for EResult<T> { type Output = T; type Residual = EResult<Infallible>; diff --git a/userspace/lib/ygglibc/src/headers/ctype/cbindgen.toml b/userspace/lib/ygglibc/src/headers/ctype/cbindgen.toml new file mode 100644 index 00000000..23e42008 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/ctype/cbindgen.toml @@ -0,0 +1,15 @@ +language = "C" +style = "Type" + +sys_includes = [ + "locale.h" +] +no_includes = true + +include_guard = "_CTYPE_H" +trailer = "#include <bits/ctype.h>" + +usize_type = "size_t" +isize_type = "ssize_t" + +[export] diff --git a/userspace/lib/ygglibc/src/headers/ctype/mod.rs b/userspace/lib/ygglibc/src/headers/ctype/mod.rs new file mode 100644 index 00000000..62f913be --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/ctype/mod.rs @@ -0,0 +1,161 @@ +use core::ffi::c_int; + +use super::locale::locale_t; + +#[no_mangle] +unsafe extern "C" fn isalnum(ch: c_int) -> c_int { + (ch < 0xFF && (ch as u8).is_ascii_alphanumeric()) as _ +} + +#[no_mangle] +unsafe extern "C" fn isalpha(ch: c_int) -> c_int { + (ch < 0xFF && (ch as u8).is_ascii_alphabetic()) as _ +} + +#[no_mangle] +unsafe extern "C" fn isascii(ch: c_int) -> c_int { + (ch < 0xFF && (ch as u8).is_ascii()) as _ +} + +#[no_mangle] +unsafe extern "C" fn isblank(ch: c_int) -> c_int { + (ch == b' ' as c_int || ch == b'\t' as c_int) as _ +} + +#[no_mangle] +unsafe extern "C" fn iscntrl(ch: c_int) -> c_int { + (ch < 0xFF && (ch as u8).is_ascii_control()) as _ +} + +#[no_mangle] +unsafe extern "C" fn isdigit(ch: c_int) -> c_int { + (ch < 0xFF && (ch as u8).is_ascii_digit()) as _ +} + +#[no_mangle] +unsafe extern "C" fn isgraph(ch: c_int) -> c_int { + (ch < 0xFF && (ch as u8).is_ascii_graphic()) as _ +} + +#[no_mangle] +unsafe extern "C" fn islower(ch: c_int) -> c_int { + (ch < 0xFF && (ch as u8).is_ascii_lowercase()) as _ +} + +#[no_mangle] +unsafe extern "C" fn isprint(ch: c_int) -> c_int { + (ch < 0xFF && ((ch as u8).is_ascii_graphic() || (ch as u8).is_ascii_whitespace())) as _ +} + +#[no_mangle] +unsafe extern "C" fn ispunct(ch: c_int) -> c_int { + (ch < 0xFF && (ch as u8).is_ascii_punctuation()) as _ +} + +#[no_mangle] +unsafe extern "C" fn isspace(ch: c_int) -> c_int { + (ch < 0xFF && (ch as u8).is_ascii_whitespace()) as _ +} + +#[no_mangle] +unsafe extern "C" fn isupper(ch: c_int) -> c_int { + (ch < 0xFF && (ch as u8).is_ascii_uppercase()) as _ +} + +#[no_mangle] +unsafe extern "C" fn isxdigit(ch: c_int) -> c_int { + (ch < 0xFF && (ch as u8).is_ascii_hexdigit()) as _ +} + +#[no_mangle] +unsafe extern "C" fn toascii(ch: c_int) -> c_int { + ch & 0x7F +} + +#[no_mangle] +unsafe extern "C" fn tolower(ch: c_int) -> c_int { + if ch < 0xFF { + (ch as u8).to_ascii_lowercase() as _ + } else { + ch + } +} + +#[no_mangle] +unsafe extern "C" fn toupper(ch: c_int) -> c_int { + if ch < 0xFF { + (ch as u8).to_ascii_uppercase() as _ + } else { + ch + } +} + +#[no_mangle] +unsafe extern "C" fn isalnum_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn isalpha_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn isblank_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn iscntrl_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn isdigit_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn isgraph_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn islower_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn isprint_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn ispunct_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn isspace_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn isupper_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn isxdigit_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn tolower_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn toupper_l(_ch: c_int, _locale: locale_t) -> c_int { + unimplemented!() +} diff --git a/userspace/lib/ygglibc/src/headers/errno/mod.rs b/userspace/lib/ygglibc/src/headers/errno/mod.rs index b8b68b02..34a238ac 100644 --- a/userspace/lib/ygglibc/src/headers/errno/mod.rs +++ b/userspace/lib/ygglibc/src/headers/errno/mod.rs @@ -1,4 +1,4 @@ -use core::ffi::{c_char, c_int, CStr}; +use core::ffi::{c_int, CStr}; macro_rules! static_cstr { ($string:expr) => { @@ -136,12 +136,11 @@ static ERRNO_STRINGS: &[&CStr] = &[ ]; impl Errno { - pub fn to_c_str(&self) -> *const c_char { + pub fn to_c_str(&self) -> &CStr { ERRNO_STRINGS .get(self.0 as usize) .copied() .unwrap_or(SUCCESS) - .as_ptr() as *const _ } pub fn from_c_int(v: c_int) -> Option<Self> { diff --git a/userspace/lib/ygglibc/src/headers/mod.rs b/userspace/lib/ygglibc/src/headers/mod.rs index 0fa3a635..8b5efa7a 100644 --- a/userspace/lib/ygglibc/src/headers/mod.rs +++ b/userspace/lib/ygglibc/src/headers/mod.rs @@ -2,13 +2,13 @@ // <aio.h> - // <arpa/inet.h> - -// <assert.h> - +// <assert.h> + // <complex.h> - // <cpio.h> - -// <ctype.h> - +// <ctype.h> + // <dirent.h> - // <dlfcn.h> - -// <errno.h> ~ +// <errno.h> + // <fcntl.h> - // <fenv.h> - // <float.h> - @@ -83,12 +83,13 @@ // <wctype.h> - // <wordexp.h> - +pub mod ctype; pub mod errno; pub mod locale; pub mod stdio; pub mod stdlib; pub mod string; -pub mod unistd; pub mod strings; +pub mod unistd; pub mod sys_types; diff --git a/userspace/lib/ygglibc/src/headers/stdio/file.rs b/userspace/lib/ygglibc/src/headers/stdio/file.rs index 5fb29fd8..a231f454 100644 --- a/userspace/lib/ygglibc/src/headers/stdio/file.rs +++ b/userspace/lib/ygglibc/src/headers/stdio/file.rs @@ -1,22 +1,30 @@ use core::{ ffi::{c_char, c_int, c_long, c_void}, - ptr::NonNull, + ptr::{null_mut, NonNull}, }; use alloc::boxed::Box; -use yggdrasil_rt::{debug_trace, io::OpenOptions, path::Path}; +use yggdrasil_rt::{ + debug_trace, + io::{OpenOptions, RawFd, SeekFrom}, + path::Path, +}; use crate::{ - error::{CEofResult, CFdResult, CIntZeroResult, CPtrResult, EResult}, - headers::sys_types::off_t, + error::{ + CEofResult, CFdResult, CIntZeroResult, COffsetResult, CPtrResult, CResult, EResult, + ResultExt, TryFromExt, + }, + headers::{errno, sys_types::off_t}, io::{ self, managed::{FileOpenSource, FILE}, + Seek, Write, }, util::{PointerExt, PointerStrExt}, }; -use super::fpos_t; +use super::{fpos_t, SEEK_SET, _IOFBF, _IOLBF, _IONBF}; fn open_inner<O: FileOpenSource>(source: O, mode_str: &[u8]) -> EResult<NonNull<FILE>> { let opts = match mode_str { @@ -43,7 +51,10 @@ fn open_inner<O: FileOpenSource>(source: O, mode_str: &[u8]) -> EResult<NonNull< } #[no_mangle] -unsafe extern "C" fn clearerr(fp: *mut FILE) {} +unsafe extern "C" fn clearerr(fp: *mut FILE) { + let fp = fp.ensure_mut(); + fp.clear_error(); +} #[no_mangle] unsafe extern "C" fn fclose(fp: *mut FILE) -> CEofResult { @@ -59,55 +70,72 @@ unsafe extern "C" fn fclose(fp: *mut FILE) -> CEofResult { #[no_mangle] unsafe extern "C" fn fdopen(fd: c_int, mode: *const c_char) -> CPtrResult<FILE> { - todo!() + let fd = RawFd::e_try_from(fd)?; + let mode = mode.ensure_cstr(); + let file = open_inner(fd, mode.to_bytes())?; + CPtrResult::success(file) } #[no_mangle] unsafe extern "C" fn feof(fp: *mut FILE) -> c_int { - todo!() + let fp = fp.ensure_mut(); + fp.is_eof() as _ } #[no_mangle] unsafe extern "C" fn ferror(fp: *mut FILE) -> c_int { - todo!() + let fp = fp.ensure_mut(); + fp.is_error() as _ } #[no_mangle] unsafe extern "C" fn fflush(fp: *mut FILE) -> CIntZeroResult { - todo!() + let fp = fp.ensure_mut(); + fp.flush()?; + CIntZeroResult::SUCCESS } #[no_mangle] unsafe extern "C" fn fileno(fp: *mut FILE) -> CFdResult { - todo!() + let fp = fp.ensure_mut(); + let fd = fp.fd(); + match fd { + Some(fd) => CFdResult::success(fd), + None => CFdResult::ERROR, + } } #[no_mangle] unsafe extern "C" fn flockfile(fp: *mut FILE) { - todo!() + let fp = fp.ensure_mut(); + fp.lock(); } #[no_mangle] -unsafe extern "C" fn fgetpos(fp: *mut FILE, pos: *mut fpos_t) -> c_int { - todo!() +unsafe extern "C" fn fgetpos(fp: *mut FILE, pos: *mut fpos_t) -> CIntZeroResult { + let fp = fp.ensure_mut(); + let pos = pos.ensure_mut(); + let p = fp.stream_position()?; + *pos = p; + CIntZeroResult::SUCCESS } #[no_mangle] unsafe extern "C" fn fmemopen( - buffer: *mut c_void, - size: usize, - mode: *const c_char, + _buffer: *mut c_void, + _size: usize, + _mode: *const c_char, ) -> CPtrResult<FILE> { - todo!() + unimplemented!() } #[no_mangle] unsafe extern "C" fn freopen( - path: *const c_char, - mode: *const c_char, - fp: *mut FILE, + _path: *const c_char, + _mode: *const c_char, + _fp: *mut FILE, ) -> CPtrResult<FILE> { - todo!() + unimplemented!() } #[no_mangle] @@ -119,74 +147,100 @@ unsafe extern "C" fn fopen(path: *const c_char, mode: *const c_char) -> CPtrResu } #[no_mangle] -unsafe extern "C" fn fseek(fp: *mut FILE, offset: c_long, whence: c_int) -> c_int { - todo!() +unsafe extern "C" fn fseek(fp: *mut FILE, offset: c_long, whence: c_int) -> CIntZeroResult { + let offset: off_t = offset.try_into().e_map_err(|_| errno::EINVAL)?; + fseeko(fp, offset, whence) } #[no_mangle] -unsafe extern "C" fn fseeko(fp: *mut FILE, offset: off_t, whence: c_int) -> c_int { - todo!() +unsafe extern "C" fn fseeko(fp: *mut FILE, offset: off_t, whence: c_int) -> CIntZeroResult { + let seek = SeekFrom::e_try_from((offset, whence))?; + let fp = fp.ensure_mut(); + fp.seek(seek)?; + CIntZeroResult::SUCCESS } #[no_mangle] -unsafe extern "C" fn fsetpos(fp: *mut FILE, pos: *const fpos_t) -> c_int { - todo!() +unsafe extern "C" fn fsetpos(fp: *mut FILE, pos: *const fpos_t) -> CIntZeroResult { + let fp = fp.ensure_mut(); + let pos = *pos.ensure(); + fp.seek(SeekFrom::Start(pos))?; + CIntZeroResult::SUCCESS } #[no_mangle] unsafe extern "C" fn ftell(fp: *mut FILE) -> c_long { - todo!() + match ftello(fp) { + COffsetResult::ERROR => -1, + COffsetResult(off) => off, + } } #[no_mangle] -unsafe extern "C" fn ftello(fp: *mut FILE) -> off_t { - todo!() +unsafe extern "C" fn ftello(fp: *mut FILE) -> COffsetResult { + let fp = fp.ensure_mut(); + let pos = fp.stream_position()?; + COffsetResult::success(pos) } #[no_mangle] -unsafe extern "C" fn ftrylockfile(fp: *mut FILE) -> c_int { - todo!() +unsafe extern "C" fn ftrylockfile(_fp: *mut FILE) -> c_int { + unimplemented!() } #[no_mangle] unsafe extern "C" fn funlockfile(fp: *mut FILE) { - todo!() + let fp = fp.ensure_mut(); + fp.unlock(); } #[no_mangle] unsafe extern "C" fn open_memstream( - ptr: *mut *mut c_char, - sizeloc: *mut usize, + _ptr: *mut *mut c_char, + _sizeloc: *mut usize, ) -> CPtrResult<FILE> { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn pclose(_fp: *mut FILE) -> c_int { todo!() } #[no_mangle] -unsafe extern "C" fn pclose(fp: *mut FILE) -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn popen(command: *const c_char, ty: *const c_char) -> CPtrResult<FILE> { +unsafe extern "C" fn popen(_command: *const c_char, _ty: *const c_char) -> CPtrResult<FILE> { todo!() } #[no_mangle] unsafe extern "C" fn rewind(fp: *mut FILE) { - todo!() + fseek(fp, 0, SEEK_SET); } #[no_mangle] unsafe extern "C" fn setbuf(fp: *mut FILE, buf: *mut c_char) { - todo!() + setbuffer(fp, buf, 0); +} + +#[no_mangle] +unsafe extern "C" fn setbuffer(fp: *mut FILE, buf: *mut c_char, size: usize) { + let mode = if buf.is_null() { _IONBF } else { _IOFBF }; + setvbuf(fp, buf, mode, size); +} + +#[no_mangle] +unsafe extern "C" fn setlinebuf(fp: *mut FILE) { + setvbuf(fp, null_mut(), _IOLBF, 0); } #[no_mangle] unsafe extern "C" fn setvbuf( fp: *mut FILE, - buffer: *mut c_void, + buffer: *mut c_char, mode: c_int, size: usize, -) -> c_int { - todo!() +) -> CIntZeroResult { + let fp = fp.ensure_mut(); + fp.setvbuf(mode, buffer, size)?; + CIntZeroResult::SUCCESS } diff --git a/userspace/lib/ygglibc/src/headers/stdio/get_put.rs b/userspace/lib/ygglibc/src/headers/stdio/get_put.rs new file mode 100644 index 00000000..38cdba00 --- /dev/null +++ b/userspace/lib/ygglibc/src/headers/stdio/get_put.rs @@ -0,0 +1,280 @@ +// get* + +use core::{ + ffi::{c_char, c_int}, + ptr::{null_mut, NonNull}, +}; + +use crate::{ + allocator, + error::{CEofResult, CIsizeResult, CPtrResult, CResult, EResult}, + headers::errno, + io::{ + managed::{stdin, stdout, FILE}, + Read, Write, + }, + util::{PointerExt, PointerStrExt}, +}; + +#[no_mangle] +unsafe extern "C" fn fgetc(fp: *mut FILE) -> CEofResult { + let fp = fp.ensure_mut(); + let mut buf = [0]; + fp.read_exact(&mut buf)?; + CEofResult::success(buf[0] as _) +} + +#[no_mangle] +unsafe extern "C" fn fgets(s: *mut c_char, size: c_int, fp: *mut FILE) -> CPtrResult<c_char> { + let fp = fp.ensure_mut(); + let mut s = NonNull::new(s).unwrap(); + + if size <= 0 { + return CPtrResult::ERROR; + } + // Nothing to read + if size == 1 { + *s.as_mut() = 0; + return CPtrResult::success(s); + } + + let size = size as usize; + let mut pos = 0; + let mut buf = [0]; + let slice = NonNull::slice_from_raw_parts(s, size).as_mut(); + + while pos < size - 1 { + let ch = match fp.read(&mut buf)? { + 1 => buf[0], + _ => break, + }; + + slice[pos] = ch as _; + pos += 1; + + if ch == b'\n' { + break; + } + } + + if pos == 0 { + CPtrResult::ERROR + } else { + slice[pos] = 0; + CPtrResult::success(s) + } +} + +#[no_mangle] +unsafe extern "C" fn getc(fp: *mut FILE) -> CEofResult { + fgetc(fp) +} + +#[no_mangle] +unsafe extern "C" fn getchar() -> CEofResult { + fgetc(stdin) +} + +#[no_mangle] +unsafe extern "C" fn fgetc_unlocked(fp: *mut FILE) -> CEofResult { + let fp = fp.ensure_mut(); + let mut buf = [0]; + match fp.read_unlocked(&mut buf)? { + 1 => CEofResult::success(buf[0] as _), + _ => CEofResult::ERROR, + } +} + +#[no_mangle] +unsafe extern "C" fn getc_unlocked(fp: *mut FILE) -> CEofResult { + fgetc_unlocked(fp) +} + +#[no_mangle] +unsafe extern "C" fn getchar_unlocked() -> CEofResult { + getc_unlocked(stdin) +} + +// put* + +#[no_mangle] +unsafe extern "C" fn fputc(ch: c_int, fp: *mut FILE) -> CEofResult { + let fp = fp.ensure_mut(); + let ch = ch as u8; + fp.write_all(&[ch])?; + CEofResult::success(ch as c_int) +} + +#[no_mangle] +unsafe extern "C" fn fputs(str: *const c_char, fp: *mut FILE) -> CEofResult { + let fp = fp.ensure_mut(); + let str = str.ensure_cstr(); + fp.write_all(str.to_bytes())?; + CEofResult::success(0) +} + +#[no_mangle] +unsafe extern "C" fn putc(ch: c_int, fp: *mut FILE) -> CEofResult { + fputc(ch, fp) +} + +#[no_mangle] +unsafe extern "C" fn putchar(ch: c_int) -> CEofResult { + fputc(ch, stdout) +} + +#[no_mangle] +unsafe extern "C" fn putc_unlocked(ch: c_int, fp: *mut FILE) -> CEofResult { + let fp = fp.ensure_mut(); + let ch = ch as u8; + match fp.write_unlocked(&[ch])? { + 1 => CEofResult::success(ch as _), + _ => CEofResult::ERROR, + } +} + +#[no_mangle] +unsafe extern "C" fn putchar_unlocked(ch: c_int) -> CEofResult { + putc_unlocked(ch, stdout) +} + +#[no_mangle] +unsafe extern "C" fn puts(str: *const c_char) -> CEofResult { + let str = str.ensure_cstr(); + let out = stdout.ensure_mut(); + out.write_all(str.to_bytes())?; + out.write_all(b"\n")?; + CEofResult::success(0) +} + +// ungetc + +#[no_mangle] +unsafe extern "C" fn ungetc(ch: c_int, fp: *mut FILE) -> CEofResult { + let fp = fp.ensure_mut(); + fp.ungetc(ch as _)?; + CEofResult::success(ch) +} + +// getdelim + +struct MallocBufferWriter { + buffer: Option<NonNull<c_char>>, + capacity: usize, + position: usize, +} + +impl MallocBufferWriter { + unsafe fn new(buffer: Option<NonNull<c_char>>, mut capacity: usize) -> Self { + if buffer.is_none() { + capacity = 0; + } + Self { + buffer, + capacity, + position: 0, + } + } + + fn try_reserve(&mut self) -> EResult<&mut c_char> { + if self.position == self.capacity { + self.capacity = (self.capacity + 64) & !63; + self.buffer = Some( + unsafe { allocator::c_realloc(self.buffer.map(NonNull::cast), self.capacity) }? + .cast(), + ); + } + let buffer = self.buffer.unwrap(); + EResult::Ok(unsafe { buffer.add(self.position).as_mut() }) + } + + fn putc(&mut self, ch: c_int) -> EResult<()> { + let item = self.try_reserve()?; + *item = ch as _; + self.position += 1; + EResult::Ok(()) + } +} + +fn getdelim_inner( + buffer: Option<NonNull<c_char>>, + capacity: usize, + delim: c_int, + stream: &mut FILE, +) -> (MallocBufferWriter, EResult<()>) { + let mut writer = unsafe { MallocBufferWriter::new(buffer, capacity) }; + let mut buf = [0]; + + loop { + let ch = match stream.read(&mut buf) { + EResult::Ok(1) => buf[0] as c_int, + EResult::Ok(_) => break, + EResult::Err(err) => return (writer, EResult::Err(err)), + }; + + match writer.putc(ch) { + EResult::Ok(()) => (), + EResult::Err(err) => { + return (writer, EResult::Err(err)); + } + } + + if ch == delim { + break; + } + } + + if writer.position == 0 { + // EOF reached before anything could be read + return (writer, EResult::Err(errno::ESUCCESS)); + } + + match writer.putc(0) { + EResult::Ok(()) => (), + EResult::Err(err) => { + return (writer, EResult::Err(err)); + } + } + + (writer, EResult::Ok(())) +} +#[no_mangle] +unsafe extern "C" fn getdelim( + lineptr: *mut *mut c_char, + n: *mut usize, + delim: c_int, + fp: *mut FILE, +) -> CIsizeResult { + let lineptr = lineptr.ensure_mut(); + + let buffer = NonNull::new(*lineptr); + let n = n.ensure_mut(); + let fp = fp.ensure_mut(); + + let (writer, result) = getdelim_inner(buffer, *n, delim, fp); + + match writer.buffer { + Some(buffer) => *lineptr = buffer.as_ptr(), + None => *lineptr = null_mut(), + } + *n = writer.capacity; + + result?; + assert_ne!(writer.position, 0); + + CIsizeResult::success(writer.position - 1) +} + +#[no_mangle] +unsafe extern "C" fn getline( + lineptr: *mut *mut c_char, + size: *mut usize, + fp: *mut FILE, +) -> CIsizeResult { + getdelim(lineptr, size, b'\n' as _, fp) +} + +#[no_mangle] +unsafe extern "C" fn gets(_buf: *mut c_char) -> *mut c_char { + unimplemented!("DO NOT USE") +} diff --git a/userspace/lib/ygglibc/src/headers/stdio/io.rs b/userspace/lib/ygglibc/src/headers/stdio/io.rs index 2b7a95d2..130c8e9b 100644 --- a/userspace/lib/ygglibc/src/headers/stdio/io.rs +++ b/userspace/lib/ygglibc/src/headers/stdio/io.rs @@ -1,31 +1,11 @@ -use core::ffi::{c_char, c_int, c_void}; +use core::{ffi::{c_char, c_int, c_void}, ptr::NonNull}; use crate::{ - error::{CEofResult, CUsizeResult}, - io::{managed::{stdout, FILE}, Read, Write}, + error::{CEofResult, CPtrResult, CResult, CUsizeResult}, + io::{managed::{stdin, stdout, FILE}, Read, Write}, util::{PointerExt, PointerStrExt}, }; -#[no_mangle] -unsafe extern "C" fn fgetc(fp: *mut FILE) -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn fgets(buf: *mut c_char, size: c_int, fp: *mut FILE) -> *mut c_char { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn fputc(ch: c_int, fp: *mut FILE) -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn fputs(str: *const c_char, fp: *mut FILE) -> c_int { - todo!() -} - #[no_mangle] unsafe extern "C" fn fread( buf: *mut c_void, @@ -54,76 +34,3 @@ unsafe extern "C" fn fwrite( CUsizeResult::success(len) } -#[no_mangle] -unsafe extern "C" fn getc(fp: *mut FILE) -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn getchar() -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn getc_unlocked(fp: *mut FILE) -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn getchar_unlocked() -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn getdelim( - lineptr: *mut *mut c_char, - size: *mut usize, - delim: c_int, - fp: *mut FILE, -) -> isize { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn getline(lineptr: *mut *mut c_char, size: *mut usize, fp: *mut FILE) -> isize { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn gets(buf: *mut c_char) -> *mut c_char { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn putc(ch: c_int, fp: *mut FILE) -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn putchar(ch: c_int) -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn putc_unlocked(ch: c_int, fp: *mut FILE) -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn putchar_unlocked(ch: c_int) -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn puts(str: *const c_char) -> CEofResult { - let str = str.ensure_cstr(); - let out = stdout.ensure_mut(); - out.write_all(str.to_bytes())?; - out.write_all(b"\n")?; - CEofResult::success(0) -} - -#[no_mangle] -unsafe extern "C" fn ungetc(ch: c_int, fp: *mut FILE) -> c_int { - todo!() -} diff --git a/userspace/lib/ygglibc/src/headers/stdio/mod.rs b/userspace/lib/ygglibc/src/headers/stdio/mod.rs index c8038629..1af41028 100644 --- a/userspace/lib/ygglibc/src/headers/stdio/mod.rs +++ b/userspace/lib/ygglibc/src/headers/stdio/mod.rs @@ -1,11 +1,20 @@ use core::ffi::c_int; +mod file; +mod get_put; +mod io; mod printf; mod scanf; -mod file; -mod io; mod util; +// includes: +// va_list from <stdarg.h> +// FILE from <bits/stdio.h> +// size_t from <stddef.h> +// ssize_t from <sys/types.h> +// +// stdin, stdout, stderr as externs from <bits/stdio.h> + pub const _IOFBF: c_int = 0; pub const _IOLBF: c_int = 1; pub const _IONBF: c_int = 2; @@ -14,6 +23,8 @@ pub const SEEK_SET: c_int = 0; pub const SEEK_CUR: c_int = 1; pub const SEEK_END: c_int = 2; +pub const FILENAME_MAX: usize = 4096; +pub const FOPEN_MAX: usize = 4096; pub const TMP_MAX: usize = 216000; pub const EOF: c_int = -1; @@ -22,4 +33,4 @@ pub type fpos_t = u64; pub const BUFSIZ: usize = 8192; -const UNGETC_MAX: usize = 128; +pub const UNGETC_MAX: usize = 128; diff --git a/userspace/lib/ygglibc/src/headers/stdio/util.rs b/userspace/lib/ygglibc/src/headers/stdio/util.rs index 0edd0e65..5d15c6ec 100644 --- a/userspace/lib/ygglibc/src/headers/stdio/util.rs +++ b/userspace/lib/ygglibc/src/headers/stdio/util.rs @@ -1,35 +1,50 @@ use core::ffi::{c_char, c_int}; -use crate::{error::CPtrResult, io::managed::FILE}; - +use crate::{ + error::{self, CPtrResult}, + io::managed::{stderr, FILE}, + util::{PointerExt, PointerStrExt}, +}; #[no_mangle] unsafe extern "C" fn perror(message: *const c_char) { + let out = stderr.ensure_mut(); + out.lock(); + + if !message.is_null() { + let message = message.ensure_cstr(); + out.write_unlocked(message.to_bytes()).ok(); + out.write_unlocked(b": ").ok(); + } + + out.write_unlocked(error::errno.to_c_str().to_bytes()).ok(); + out.write_unlocked(b"\n").ok(); + + out.unlock(); +} + +#[no_mangle] +unsafe extern "C" fn ctermid(_buf: *mut c_char) -> *mut c_char { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn remove(_path: *const c_char) -> c_int { todo!() } #[no_mangle] -unsafe extern "C" fn ctermid(buf: *mut c_char) -> *mut c_char { +unsafe extern "C" fn rename(_src: *const c_char, _dst: *const c_char) -> c_int { todo!() } #[no_mangle] -unsafe extern "C" fn remove(path: *const c_char) -> c_int { +unsafe extern "C" fn renameat(_atfd: c_int, _src: *const c_char, _dst: *const c_char) -> c_int { todo!() } #[no_mangle] -unsafe extern "C" fn rename(src: *const c_char, dst: *const c_char) -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn renameat(atfd: c_int, src: *const c_char, dst: *const c_char) -> c_int { - todo!() -} - -#[no_mangle] -unsafe extern "C" fn tempnam(dir: *const c_char, prefix: *const c_char) -> *mut c_char { +unsafe extern "C" fn tempnam(_dir: *const c_char, _prefix: *const c_char) -> *mut c_char { todo!() } @@ -39,6 +54,6 @@ unsafe extern "C" fn tmpfile() -> CPtrResult<FILE> { } #[no_mangle] -unsafe extern "C" fn tmpnam(template: *mut c_char) -> *mut c_char { +unsafe extern "C" fn tmpnam(_template: *mut c_char) -> *mut c_char { todo!() } diff --git a/userspace/lib/ygglibc/src/headers/string/str.rs b/userspace/lib/ygglibc/src/headers/string/str.rs index fa98fc2f..ea08135b 100644 --- a/userspace/lib/ygglibc/src/headers/string/str.rs +++ b/userspace/lib/ygglibc/src/headers/string/str.rs @@ -1,11 +1,11 @@ use core::{ cmp::Ordering, ffi::{c_char, c_int}, - ptr::null_mut, + ptr::{null_mut, NonNull}, }; use crate::{ - allocator, + allocator::{self, malloc}, error::CPtrResult, headers::{ errno::{self, Errno}, @@ -20,7 +20,7 @@ unsafe extern "C" fn stpcpy(dst: *mut c_char, src: *const c_char) -> *mut c_char if dst.is_null() || src.is_null() { panic!(); } - let ptr = mempcpy(dst as _, src as _, strlen(src)) as *mut c_char; + let ptr = mempcpy(dst.cast(), src.cast(), strlen(src)).cast::<c_char>(); *ptr = 0; ptr } @@ -30,8 +30,8 @@ unsafe extern "C" fn stpncpy(dst: *mut c_char, src: *const c_char, n: usize) -> if dst.is_null() || src.is_null() { panic!(); } - memset(dst as _, 0, n); - mempcpy(dst as _, src as _, strnlen(src, n)) as _ + memset(dst.cast(), 0, n); + mempcpy(dst.cast(), src.cast(), strnlen(src, n)).cast() } #[no_mangle] @@ -51,7 +51,7 @@ unsafe extern "C" fn strchr(mut s: *const c_char, c: c_int) -> *mut c_char { loop { if *s == c as _ { - return s as _; + return s.cast_mut(); } if *s == 0 { @@ -95,13 +95,13 @@ unsafe extern "C" fn strcspn(mut s: *const c_char, reject: *const c_char) -> usi unsafe extern "C" fn strdup(s: *const c_char) -> CPtrResult<c_char> { let len = strlen(s); let data = allocator::c_alloc(len + 1, 1, false)?; - memcpy(data.cast().as_ptr(), s as _, len + 1); + memcpy(data.cast().as_ptr(), s.cast(), len + 1); CPtrResult::success(data.cast()) } unsafe fn strerror_inner(e: c_int) -> *const c_char { if let Some(errno) = Errno::from_c_int(e) { - errno.to_c_str() + errno.to_c_str().as_ptr() } else { errno::UNKNOWN_ERROR.as_ptr() } @@ -127,7 +127,7 @@ unsafe extern "C" fn strncat(dst: *mut c_char, src: *const c_char, n: usize) -> } let len = strnlen(src, n); let ptr = dst.add(strlen(dst)); - let ptr = mempcpy(ptr as _, src as _, len) as *mut c_char; + let ptr = mempcpy(ptr.cast(), src.cast(), len).cast::<c_char>(); *ptr = 0; dst @@ -171,8 +171,13 @@ unsafe extern "C" fn strncpy(dst: *mut c_char, src: *const c_char, n: usize) -> } #[no_mangle] -unsafe extern "C" fn strndup(s: *const c_char, n: usize) -> *mut c_char { - todo!() +unsafe extern "C" fn strndup(s: *const c_char, n: usize) -> CPtrResult<c_char> { + let src = NonNull::new(s.cast_mut()).unwrap(); + let len = strnlen(s, n); + let dst = malloc(len + 1)?.cast(); + dst.copy_from_nonoverlapping(src, len); + dst.add(len).write(0); + CPtrResult::success(dst) } #[no_mangle] @@ -203,7 +208,7 @@ unsafe extern "C" fn strpbrk(mut a: *const c_char, b: *const c_char) -> *mut c_c } if !strchr(b, c as _).is_null() { - return a as _; + return a.cast_mut(); } a = a.add(1); @@ -221,7 +226,7 @@ unsafe extern "C" fn strrchr(a: *const c_char, c: c_int) -> *mut c_char { let n = strnlen(a, usize::MAX); for i in (0..n).rev() { if *a.add(i) == c as _ { - return a.add(i) as _; + return a.add(i).cast_mut(); } } @@ -229,7 +234,7 @@ unsafe extern "C" fn strrchr(a: *const c_char, c: c_int) -> *mut c_char { } #[no_mangle] -unsafe extern "C" fn strsignal(signum: c_int) -> *mut c_char { +unsafe extern "C" fn strsignal(_signum: c_int) -> *mut c_char { todo!() } @@ -256,12 +261,12 @@ unsafe extern "C" fn strstr(mut a: *const c_char, b: *const c_char) -> *mut c_ch } let n = strnlen(b, usize::MAX); if *a == 0 && *b == 0 { - return a as _; + return a.cast_mut(); } while *a != 0 { if strncmp(a, b, n) == 0 { - return a as _; + return a.cast_mut(); } a = a.add(1); } @@ -306,31 +311,31 @@ unsafe extern "C" fn strtok_r( *saveptr = null_mut(); } *str.add(len) = 0; - str as _ + str.cast() } // TODO locales #[no_mangle] -unsafe extern "C" fn strcoll(a: *const c_char, b: *const c_char) -> c_int { - todo!() +unsafe extern "C" fn strcoll(_a: *const c_char, _b: *const c_char) -> c_int { + unimplemented!() } #[no_mangle] -unsafe extern "C" fn strcoll_l(a: *const c_char, b: *const c_char, l: locale_t) -> c_int { - todo!() +unsafe extern "C" fn strcoll_l(_a: *const c_char, _b: *const c_char, _l: locale_t) -> c_int { + unimplemented!() } #[no_mangle] -unsafe extern "C" fn strerror_l(e: c_int, l: locale_t) -> *mut c_char { - todo!() +unsafe extern "C" fn strerror_l(_e: c_int, _l: locale_t) -> *mut c_char { + unimplemented!() } #[no_mangle] -unsafe extern "C" fn strxfrm(a: *mut c_char, b: *const c_char, n: usize) -> usize { - todo!() +unsafe extern "C" fn strxfrm(_a: *mut c_char, _b: *const c_char, _n: usize) -> usize { + unimplemented!() } #[no_mangle] -unsafe extern "C" fn strxfrm_l(a: *mut c_char, b: *const c_char, n: usize, l: locale_t) -> usize { - todo!() +unsafe extern "C" fn strxfrm_l(_a: *mut c_char, _b: *const c_char, _n: usize, _l: locale_t) -> usize { + unimplemented!() } diff --git a/userspace/lib/ygglibc/src/io/managed.rs b/userspace/lib/ygglibc/src/io/managed.rs index 3a80ec59..478a2ece 100644 --- a/userspace/lib/ygglibc/src/io/managed.rs +++ b/userspace/lib/ygglibc/src/io/managed.rs @@ -1,10 +1,13 @@ #![allow(non_upper_case_globals)] use core::{ - fmt, ops::{Deref, DerefMut}, ptr::{null_mut, NonNull} + ffi::{c_char, c_int}, + fmt, + ops::{Deref, DerefMut}, + ptr::{null_mut, NonNull}, }; -use alloc::{boxed::Box, collections::btree_set::BTreeSet}; +use alloc::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec}; use bitflags::bitflags; use yggdrasil_rt::{ io::{FileMode, OpenOptions, RawFd, SeekFrom}, @@ -12,15 +15,16 @@ use yggdrasil_rt::{ }; use crate::{ - error::EResult, - headers::errno, + error::{EResult, TryFromExt}, + headers::{ + errno, + stdio::{BUFSIZ, UNGETC_MAX, _IOFBF, _IOLBF, _IONBF}, + }, sync::{Mutex, RawMutex}, }; use super::{ - buffer::{FileWriter, FullBufferedWriter, LineBufferedWriter, ReadBuffer, UnbufferedWriter}, - raw::RawFile, - AsRawFd, FromRawFd, Read, Seek, Write, + buffer::{FileWriter, FullBufferedWriter, LineBufferedWriter, ReadBuffer, UnbufferedWriter}, raw::RawFile, AsRawFd, BufRead, FromRawFd, Read, Seek, Write }; macro locked_op($self:expr, $op:expr) {{ @@ -50,6 +54,12 @@ pub enum BufferingMode { None, } +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum Direction { + Read, + Write +} + pub enum FileBacking { File(ManagedFile), } @@ -67,6 +77,13 @@ pub struct FILE { write_buffer: Box<dyn FileWriter>, flags: FileFlags, + + ungetc: Vec<u8>, + // NOTE: + // man setvbuf(3): + // The setvbuf() function may be used only after opening a stream and + // before any other operations have been performed on it. + last_operation: Option<Direction> } // ManagedFile @@ -191,8 +208,8 @@ impl FILE { let write_buffer = backing.make_ref(); let write_buffer: Box<dyn FileWriter> = match buffering { BufferingMode::None => Box::new(UnbufferedWriter::new(write_buffer)), - BufferingMode::Line => Box::new(LineBufferedWriter::with_capacity(write_buffer, 1024)), - BufferingMode::Full => Box::new(FullBufferedWriter::with_capacity(write_buffer, 1024)), + BufferingMode::Line => Box::new(LineBufferedWriter::with_capacity(write_buffer, BUFSIZ)), + BufferingMode::Full => Box::new(FullBufferedWriter::with_capacity(write_buffer, BUFSIZ)), }; Self { @@ -203,6 +220,9 @@ impl FILE { read_buffer: None, flags, + + ungetc: Vec::new(), + last_operation: None, } } @@ -238,22 +258,71 @@ impl FILE { } } - pub fn reset(&mut self) { - if let Some(read_buffer) = self.read_buffer.as_mut() { - read_buffer.reset(); + pub fn fd(&self) -> Option<RawFd> { + match &self.inner { + FileBacking::File(file) => Some(file.as_raw_fd()), } - self.write_buffer.reset(); - self.flags &= !(FileFlags::ERROR | FileFlags::EOF); } - unsafe fn read_unlocked(&mut self, data: &mut [u8]) -> EResult<usize> { + pub fn is_eof(&self) -> bool { + self.flags.contains(FileFlags::EOF) + } + + pub fn is_error(&self) -> bool { + self.flags.contains(FileFlags::ERROR) + } + + pub fn clear_error(&mut self) { + self.flags.remove(FileFlags::ERROR); + } + + pub unsafe fn lock(&mut self) { + self.lock.lock(); + } + + pub unsafe fn unlock(&mut self) { + self.lock.release(); + } + + // pub fn reset(&mut self) { + // if let Some(read_buffer) = self.read_buffer.as_mut() { + // read_buffer.reset(); + // } + // self.write_buffer.reset(); + // self.flags &= !(FileFlags::ERROR | FileFlags::EOF); + // self.ungetc.clear(); + // self.last_operation = None; + // } + + pub fn setvbuf(&mut self, mode: c_int, buffer: *mut c_char, size: usize) -> EResult<()> { + locked_op!(self, self.setvbuf_unlocked(mode, buffer, size)) + } + + pub fn ungetc(&mut self, ch: u8) -> EResult<()> { + locked_op!(self, self.ungetc_unlocked(ch)) + } + + pub unsafe fn read_unlocked(&mut self, data: &mut [u8]) -> EResult<usize> { if !self.flags.contains(FileFlags::READ) { self.flags |= FileFlags::ERROR; return EResult::Err(errno::EBADF); } + self.set_direction(Direction::Read)?; + + if !self.ungetc.is_empty() { + let amount = core::cmp::min(self.ungetc.len(), data.len()); + data[..amount].copy_from_slice(&self.ungetc[..amount]); + self.ungetc.drain(..amount); + return EResult::Ok(amount); + } + if self.read_buffer.is_some() { - todo!() + let buf = self.fill_buf()?; + let len = core::cmp::min(data.len(), buf.len()); + data[..len].copy_from_slice(&buf[..len]); + self.consume(len); + EResult::Ok(len) } else { match self.inner.read(data) { EResult::Ok(0) => { @@ -269,12 +338,14 @@ impl FILE { } } - unsafe fn write_unlocked(&mut self, data: &[u8]) -> EResult<usize> { + pub unsafe fn write_unlocked(&mut self, data: &[u8]) -> EResult<usize> { if !self.flags.contains(FileFlags::WRITE) { self.flags |= FileFlags::ERROR; return EResult::Err(errno::EBADF); } + self.set_direction(Direction::Write)?; + match self.write_buffer.write(data) { EResult::Ok(len) => EResult::Ok(len), EResult::Err(err) => { @@ -285,19 +356,115 @@ impl FILE { } unsafe fn flush_unlocked(&mut self) -> EResult<()> { - if !self.flags.contains(FileFlags::WRITE) { - self.flags |= FileFlags::ERROR; - return EResult::Err(errno::EBADF); - } + self.ungetc.clear(); + self.last_operation = None; match self.write_buffer.flush() { - EResult::Ok(()) => EResult::Ok(()), + EResult::Ok(()) => { + EResult::Ok(()) + }, EResult::Err(err) => { self.flags |= FileFlags::ERROR; EResult::Err(err) } } } + + unsafe fn seek_unlocked(&mut self, off: SeekFrom) -> EResult<u64> { + self.flush_unlocked()?; + + match self.inner.seek(off) { + EResult::Ok(pos) => { + self.flags &= !FileFlags::EOF; + EResult::Ok(pos) + } + EResult::Err(err) => { + self.flags |= FileFlags::ERROR; + EResult::Err(err) + } + } + } + + unsafe fn ungetc_unlocked(&mut self, ch: u8) -> EResult<()> { + // ungetc() for write doesn't make any sense + self.set_direction(Direction::Read)?; + if self.ungetc.len() == UNGETC_MAX { + return EResult::Err(errno::ENOMEM); + } + self.ungetc.push(ch); + EResult::Ok(()) + } + + unsafe fn setvbuf_unlocked( + &mut self, + mode: c_int, + buffer: *mut c_char, + capacity: usize, + ) -> EResult<()> { + let mode = BufferingMode::e_try_from(mode)?; + + if self.last_operation.is_some() { + self.flags |= FileFlags::ERROR; + return EResult::Err(errno::EINVAL); + } + + self.ungetc.clear(); + + let mut read_capacity = capacity; + let mut write_capacity = capacity; + + // Set read buffer + match mode { + BufferingMode::None => self.read_buffer = None, + BufferingMode::Line | BufferingMode::Full => { + if buffer.is_null() { + if read_capacity == 0 { + read_capacity = BUFSIZ; + } + + self.read_buffer = Some(ReadBuffer::owned(read_capacity)); + } else { + assert_ne!(read_capacity, 0); + + let slice = + unsafe { core::slice::from_raw_parts_mut(buffer.cast(), read_capacity) }; + self.read_buffer = Some(ReadBuffer::borrowed(slice)); + } + } + } + + // Set write buffer + let file_ref = unsafe { self.inner.make_ref() }; + match mode { + BufferingMode::None => self.write_buffer = Box::new(UnbufferedWriter::new(file_ref)), + BufferingMode::Line => { + if write_capacity == 0 { + write_capacity = BUFSIZ; + } + self.write_buffer = + Box::new(LineBufferedWriter::with_capacity(file_ref, write_capacity)); + } + BufferingMode::Full => { + if write_capacity == 0 { + write_capacity = BUFSIZ; + } + self.write_buffer = + Box::new(FullBufferedWriter::with_capacity(file_ref, write_capacity)); + } + } + + EResult::Ok(()) + } + + unsafe fn set_direction(&mut self, direction: Direction) -> EResult<()> { + match self.last_operation.replace(direction) { + Some(dir) if dir != direction => { + self.flags |= FileFlags::ERROR; + EResult::Err(errno::EINVAL) + }, + _ => EResult::Ok(()) + } + } } impl Read for FILE { @@ -316,6 +483,35 @@ impl Write for FILE { } } +impl Seek for FILE { + fn seek(&mut self, pos: SeekFrom) -> EResult<u64> { + locked_op!(self, self.seek_unlocked(pos)) + } +} + +impl BufRead for FILE { + fn fill_buf(&mut self) -> EResult<&[u8]> { + let buffer = self.read_buffer.as_mut().unwrap(); + match buffer.fill_from(&mut self.inner) { + EResult::Ok(slice) => { + if slice.is_empty() { + self.flags |= FileFlags::EOF; + } + EResult::Ok(slice) + } + EResult::Err(err) => { + self.flags |= FileFlags::ERROR; + EResult::Err(err) + } + } + } + + fn consume(&mut self, amount: usize) { + let buffer = self.read_buffer.as_mut().unwrap(); + buffer.consume(amount); + } +} + impl FileOpenSource for &Path { fn open_with(self, opts: OpenOptions) -> EResult<FILE> { let f = ManagedFile::open_at(None, self, opts, FileMode::default_file())?; @@ -330,9 +526,35 @@ impl FileOpenSource for &Path { } } +impl FileOpenSource for RawFd { + fn open_with(self, opts: OpenOptions) -> EResult<FILE> { + let f = unsafe { RawFile::from_raw_fd(self) }; + let mut flags = FileFlags::empty(); + if opts.contains(OpenOptions::READ) { + flags |= FileFlags::READ; + } + if opts.contains(OpenOptions::WRITE) { + flags |= FileFlags::WRITE; + } + EResult::Ok(unsafe { FILE::from_raw_file(f, flags) }) + } +} + impl fmt::Write for FILE { fn write_str(&mut self, s: &str) -> fmt::Result { - self.write_all(s.as_bytes()).into_result(|_| fmt::Error, true) + self.write_all(s.as_bytes()) + .into_result(|_| fmt::Error, true) + } +} + +impl TryFromExt<c_int> for BufferingMode { + fn e_try_from(value: c_int) -> EResult<Self> { + match value { + _IOFBF => EResult::Ok(Self::Full), + _IOLBF => EResult::Ok(Self::Line), + _IONBF => EResult::Ok(Self::None), + _ => EResult::Err(errno::EINVAL), + } } } diff --git a/userspace/lib/ygglibc/src/io/mod.rs b/userspace/lib/ygglibc/src/io/mod.rs index 3cf99688..57fc9f18 100644 --- a/userspace/lib/ygglibc/src/io/mod.rs +++ b/userspace/lib/ygglibc/src/io/mod.rs @@ -1,4 +1,4 @@ -use core::{ffi::c_int, mem::MaybeUninit}; +use core::{ffi::{c_int, c_long}, mem::MaybeUninit}; use yggdrasil_rt::{io::{PipeOptions, RawFd, SeekFrom}, sys as syscall}; @@ -46,10 +46,27 @@ pub trait Write { pub trait Read { fn read(&mut self, data: &mut [u8]) -> EResult<usize>; + + fn read_exact(&mut self, buf: &mut [u8]) -> EResult<()> { + match self.read(buf) { + EResult::Ok(n) if n == buf.len() => EResult::Ok(()), + EResult::Ok(_) => todo!(), + EResult::Err(err) => EResult::Err(err) + } + } +} + +pub trait BufRead { + fn fill_buf(&mut self) -> EResult<&[u8]>; + fn consume(&mut self, amount: usize); } pub trait Seek { fn seek(&mut self, pos: SeekFrom) -> EResult<u64>; + + fn stream_position(&mut self) -> EResult<u64> { + self.seek(SeekFrom::Current(0)) + } } pub trait FromRawFd {