diff --git a/include/bits/dirent.h b/include/bits/dirent.h new file mode 100644 index 0000000..f5e59a2 --- /dev/null +++ b/include/bits/dirent.h @@ -0,0 +1,8 @@ +#ifndef _YGGDRASIL_DIRENT +#define _YGGDRASIL_DIRENT 1 + +struct __DIR; + +typedef struct __DIR DIR; + +#endif diff --git a/include/bits/fcntl.h b/include/bits/fcntl.h new file mode 100644 index 0000000..617c234 --- /dev/null +++ b/include/bits/fcntl.h @@ -0,0 +1,8 @@ +#ifndef _YGGDRASIL_FCNTL_H +#define _YGGDRASIL_FCNTL_H 1 + +int fcntl(int fd, int cmd, ...); +int open(const char *pathname, int opts, ...); +int openat(int atfd, const char *pathname, int opts, ...); + +#endif diff --git a/include/bits/sys/stat.h b/include/bits/sys/stat.h new file mode 100644 index 0000000..68e647f --- /dev/null +++ b/include/bits/sys/stat.h @@ -0,0 +1,16 @@ +#ifndef _YGGDRASIL_SYS_STAT_H +#define _YGGDRASIL_SYS_STAT_H 1 + +#define st_atime st_atim.tv_sec +#define st_mtime st_mtim.tv_sec +#define st_ctime st_ctim.tv_sec + +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFFIFO) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) + +#endif diff --git a/include/bits/time.h b/include/bits/time.h new file mode 100644 index 0000000..5f7034f --- /dev/null +++ b/include/bits/time.h @@ -0,0 +1,6 @@ +#ifndef _YGGDRASIL_TIME_H +#define _YGGDRASIL_TIME_H 1 + +typedef struct timespec __ygg_timespec_t; + +#endif diff --git a/src/file.rs b/src/file.rs index 3ddfdb9..1681bfb 100644 --- a/src/file.rs +++ b/src/file.rs @@ -53,6 +53,12 @@ impl File { EResult::from(unsafe { syscall::close(self.fd) })?; Ok(()) } + + pub fn into_raw_fd(self) -> RawFd { + let fd = self.fd; + core::mem::forget(self); + fd + } } impl Write for File { diff --git a/src/header/ctype/cbindgen.toml b/src/header/ctype/cbindgen.toml new file mode 100644 index 0000000..8595c33 --- /dev/null +++ b/src/header/ctype/cbindgen.toml @@ -0,0 +1,14 @@ +language = "C" +style = "Type" + +sys_includes = [ + "locale.h" +] +no_includes = true + +include_guard = "_CTYPE_H" + +usize_type = "size_t" +isize_type = "ssize_t" + +[export] diff --git a/src/header/ctype/mod.rs b/src/header/ctype/mod.rs new file mode 100644 index 0000000..4a112d1 --- /dev/null +++ b/src/header/ctype/mod.rs @@ -0,0 +1,142 @@ +use core::ffi::c_int; + +use super::locale::locale_t; + +#[no_mangle] +extern "C" fn isalnum(c: c_int) -> c_int { + (c as u8).is_ascii_alphanumeric() as _ +} + +#[no_mangle] +extern "C" fn isalpha(c: c_int) -> c_int { + (c as u8).is_ascii_alphabetic() as _ +} + +#[no_mangle] +extern "C" fn isascii(c: c_int) -> c_int { + (c as u8).is_ascii() as _ +} + +#[no_mangle] +extern "C" fn isblank(c: c_int) -> c_int { + (c as u8 == b' ' || c as u8 == b'\t') as _ +} + +#[no_mangle] +extern "C" fn iscntrl(c: c_int) -> c_int { + (c as u8).is_ascii_control() as _ +} + +#[no_mangle] +extern "C" fn isdigit(c: c_int) -> c_int { + (c as u8).is_ascii_digit() as _ +} + +#[no_mangle] +extern "C" fn isgraph(c: c_int) -> c_int { + (c as u8).is_ascii_graphic() as _ +} + +#[no_mangle] +extern "C" fn islower(c: c_int) -> c_int { + (c as u8).is_ascii_lowercase() as _ +} + +#[no_mangle] +extern "C" fn isprint(c: c_int) -> c_int { + ((c as u8).is_ascii_graphic() || (c as u8) == b' ') as _ +} + +#[no_mangle] +extern "C" fn ispunct(c: c_int) -> c_int { + (c as u8).is_ascii_punctuation() as _ +} + +#[no_mangle] +extern "C" fn isspace(c: c_int) -> c_int { + (c as u8).is_ascii_whitespace() as _ +} + +#[no_mangle] +extern "C" fn isupper(c: c_int) -> c_int { + (c as u8).is_ascii_uppercase() as _ +} + +#[no_mangle] +extern "C" fn isxdigit(c: c_int) -> c_int { + (c as u8).is_ascii_hexdigit() as _ +} + +#[no_mangle] +extern "C" fn toascii(c: c_int) -> c_int { + ((c as u8) & !0x80) as _ +} + +#[no_mangle] +extern "C" fn tolower(c: c_int) -> c_int { + (c as u8).to_ascii_lowercase() as _ +} + +#[no_mangle] +extern "C" fn toupper(c: c_int) -> c_int { + (c as u8).to_ascii_uppercase() as _ +} + +// Locale + +#[no_mangle] +extern "C" fn isalnum_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} +#[no_mangle] +extern "C" fn isalpha_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} +#[no_mangle] +extern "C" fn isblank_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} +#[no_mangle] +extern "C" fn iscntrl_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} +#[no_mangle] +extern "C" fn isdigit_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} +#[no_mangle] +extern "C" fn isgraph_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} +#[no_mangle] +extern "C" fn islower_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} +#[no_mangle] +extern "C" fn isprint_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} +#[no_mangle] +extern "C" fn ispunct_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} +#[no_mangle] +extern "C" fn isspace_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} +#[no_mangle] +extern "C" fn isupper_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} +#[no_mangle] +extern "C" fn isxdigit_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} +#[no_mangle] +extern "C" fn tolower_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} +#[no_mangle] +extern "C" fn toupper_l(c: c_int, locale: locale_t) -> c_int { + todo!() +} diff --git a/src/header/dirent/cbindgen.toml b/src/header/dirent/cbindgen.toml new file mode 100644 index 0000000..c1adbde --- /dev/null +++ b/src/header/dirent/cbindgen.toml @@ -0,0 +1,20 @@ +language = "C" +style = "Tag" + +sys_includes = [ + "stddef.h", + "stdint.h", + "sys/types.h", + "bits/dirent.h", + "limits.h", +] +no_includes = true + +include_guard = "_DIRENT_H" + +usize_type = "size_t" +isize_type = "ssize_t" + +[export] +include = ["dirent"] +exclude = ["DIR"] diff --git a/src/header/dirent/mod.rs b/src/header/dirent/mod.rs new file mode 100644 index 0000000..f07225f --- /dev/null +++ b/src/header/dirent/mod.rs @@ -0,0 +1,162 @@ +use core::{ + ffi::{c_char, c_int, c_long, CStr}, + mem::MaybeUninit, + ptr::null_mut, +}; + +use alloc::boxed::Box; +use yggdrasil_rt::io::{DirectoryEntry, RawFd}; + +use crate::{error, io::dir::DirReader, util::Nullable}; + +use super::{errno::EBADF, limits::NAME_MAX, sys_types::ino_t}; + +#[derive(Debug)] +#[repr(C)] +pub struct DIR { + reader: DirReader, + buffer: MaybeUninit, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[repr(C)] +pub struct dirent { + pub d_ino: ino_t, + pub d_name: [c_char; NAME_MAX], +} + +pub type __scandir_filter_fn_t = extern "C" fn(*const dirent) -> c_int; +pub type __scandir_compar_fn_t = extern "C" fn(*mut *const dirent, *mut *const dirent) -> c_int; + +impl From for dirent { + fn from(value: DirectoryEntry) -> Self { + let mut d_name = [0; NAME_MAX]; + let len = value.name.len(); + + if len + 1 >= NAME_MAX { + todo!("Name too long"); + } + + // SAFETY this is just to cast u8 to i8, so transmute should be safe + d_name[..len].copy_from_slice(unsafe { core::mem::transmute(value.name.as_bytes()) }); + d_name[len] = 0; + + Self { + // TODO + d_ino: 0, + d_name, + } + } +} + +// Primary stuff + +#[no_mangle] +unsafe extern "C" fn opendir(pathname: *const c_char) -> *mut DIR { + pathname.ensure(); + + let pathname = CStr::from_ptr(pathname).to_str().unwrap(); + + match DirReader::open_at(None, pathname) { + Ok(reader) => Box::into_raw(Box::new(DIR { + reader, + buffer: MaybeUninit::uninit(), + })), + Err(_) => null_mut(), + } +} + +#[no_mangle] +unsafe extern "C" fn fdopendir(fd: c_int) -> *mut DIR { + if fd < 0 { + error::set_errno(EBADF); + return null_mut(); + } + + let reader = DirReader::from_raw_fd(RawFd(fd as _)); + + Box::into_raw(Box::new(DIR { + reader, + buffer: MaybeUninit::uninit(), + })) +} + +#[no_mangle] +unsafe extern "C" fn closedir(dir: *mut DIR) -> c_int { + let result = { + let dir = dir.as_mut().unwrap(); + + match dir.reader.close() { + Ok(_) => 0, + Err(_) => -1, + } + }; + + drop(Box::from_raw(dir)); + + result +} + +#[no_mangle] +unsafe extern "C" fn dirfd(dir: *mut DIR) -> c_int { + let dir = dir.as_mut().unwrap(); + + dir.reader.as_raw_fd().0 as _ +} + +#[no_mangle] +unsafe extern "C" fn readdir(dir: *mut DIR) -> *mut dirent { + let dir = dir.as_mut().unwrap(); + + match dir.reader.read_entry() { + Ok(Some(entry)) => { + dir.buffer.write(dirent::from(entry)); + dir.buffer.as_mut_ptr() + } + Ok(None) | Err(_) => null_mut(), + } +} + +// Deprecated +// #[no_mangle] +// unsafe extern "C" fn readdir_r( +// dir: *mut DIR, +// buffer: *mut dirent, +// outptr: *mut *mut dirent, +// ) -> c_int { +// todo!() +// } + +// Seeking + +#[no_mangle] +unsafe extern "C" fn seekdir(dir: *mut DIR, offset: c_long) { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn rewinddir(dir: *mut DIR) { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn telldir(dir: *mut DIR) -> c_long { + todo!() +} + +// Directory scan and sorting + +#[no_mangle] +unsafe extern "C" fn scandir( + dirp: *const c_char, + namelist: *mut *mut *mut dirent, + filter: __scandir_filter_fn_t, + compar: __scandir_compar_fn_t, +) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn alphasort(a: *const *const dirent, b: *const *const dirent) -> c_int { + todo!() +} diff --git a/src/header/fcntl/cbindgen.toml b/src/header/fcntl/cbindgen.toml new file mode 100644 index 0000000..023de4b --- /dev/null +++ b/src/header/fcntl/cbindgen.toml @@ -0,0 +1,23 @@ +language = "C" +style = "Tag" + +sys_includes = [ + "stddef.h", + "stdint.h", + "sys/types.h", + "sys/stat.h", + "unistd.h", + + "bits/fcntl.h" +] +no_includes = true + +include_guard = "_FCNTL_H" + +usize_type = "size_t" +isize_type = "ssize_t" + +[export] +include = ["flock"] +# Varargs are broken for these +exclude = ["fcntl", "open", "openat"] diff --git a/src/header/fcntl/mod.rs b/src/header/fcntl/mod.rs new file mode 100644 index 0000000..8b45eeb --- /dev/null +++ b/src/header/fcntl/mod.rs @@ -0,0 +1,168 @@ +use core::ffi::{c_char, c_int, c_short, CStr, VaList}; + +use yggdrasil_rt::{ + io::{FileMode, OpenOptions, RawFd}, + path::Path, +}; + +use crate::{error, file::File, header::errno::EINVAL, util::Nullable}; + +use super::{ + errno::Errno, + sys_types::{mode_t, off_t, pid_t}, +}; + +/* +TODO: +int posix_fadvise(int, off_t, off_t, int); +int posix_fallocate(int, off_t, off_t); + */ + +#[repr(C)] +pub struct flock { + pub l_type: c_short, + pub l_whence: c_short, + pub l_start: off_t, + pub l_len: off_t, + pub l_pid: pid_t, +} + +pub const F_DUPFD: c_int = 1; +pub const F_DUPFD_CLOEXEC: c_int = 2; +pub const F_GETFD: c_int = 3; +pub const F_SETFD: c_int = 4; +pub const F_GETFL: c_int = 5; +pub const F_SETFL: c_int = 6; +pub const F_GETLK: c_int = 7; +pub const F_SETLK: c_int = 8; +pub const F_SETLKW: c_int = 9; +pub const F_GETOWN: c_int = 10; +pub const F_SETOWN: c_int = 11; + +pub const FD_CLOEXEC: c_int = 1 << 0; + +pub const F_RDLCK: c_int = 1; +pub const F_UNLCK: c_int = 2; +pub const F_WRLCK: c_int = 3; + +pub const O_CLOEXEC: c_int = 1 << 16; +pub const O_CREAT: c_int = 1 << 17; +pub const O_DIRECTORY: c_int = 1 << 18; +pub const O_EXCL: c_int = 1 << 19; +pub const O_NOCTTY: c_int = 1 << 20; +pub const O_NOFOLLOW: c_int = 1 << 21; +pub const O_TRUNC: c_int = 1 << 22; +pub const O_TTY_INIT: c_int = 1 << 23; + +pub const O_APPEND: c_int = 1 << 24; +pub const O_DSYNC: c_int = 1 << 25; +pub const O_NONBLOCK: c_int = 1 << 26; +pub const O_RSYNC: c_int = 1 << 27; +pub const O_SYNC: c_int = 1 << 28; + +pub const O_ACCMODE: c_int = 0xFF; +pub const O_RDONLY: c_int = 1; +pub const O_WRONLY: c_int = 2; +pub const O_RDWR: c_int = 3; +pub const O_EXEC: c_int = 4; +pub const O_SEARCH: c_int = 5; + +pub const AT_FDCWD: c_int = -65536; + +pub const AT_EACCESS: c_int = 1 << 0; +pub const AT_SYMLINK_NOFOLLOW: c_int = 1 << 1; +pub const AT_REMOVEDIR: c_int = 1 << 2; + +enum OpenMode { + File(OpenOptions, FileMode), + Directory, +} + +fn open_opts(opts: c_int, ap: &mut VaList) -> Result { + if opts & O_DIRECTORY != 0 { + if opts & !O_DIRECTORY != 0 { + todo!(); + } + + return Ok(OpenMode::Directory); + } + + let need_mode = opts & O_CREAT != 0; + let mut res = OpenOptions::empty(); + + match opts & O_ACCMODE { + O_RDONLY => res |= OpenOptions::READ, + O_WRONLY => res |= OpenOptions::WRITE, + O_RDWR => res |= OpenOptions::READ | OpenOptions::WRITE, + O_EXEC => todo!(), + O_SEARCH => todo!(), + _ => { + return error::set_errno_result(EINVAL); + } + } + + if opts & O_CREAT != 0 { + res |= OpenOptions::CREATE; + } + if opts & O_EXCL != 0 { + res |= OpenOptions::CREATE_EXCL; + } + if opts & O_APPEND != 0 { + res |= OpenOptions::APPEND; + } + if opts & O_TRUNC != 0 { + res |= OpenOptions::TRUNCATE; + } + if opts + & (O_DSYNC | O_RSYNC | O_SYNC | O_TTY_INIT | O_NONBLOCK | O_NOFOLLOW | O_CLOEXEC | O_NOCTTY) + != 0 + { + todo!(); + } + + let mode = if need_mode { + let raw = unsafe { ap.arg::() }; + todo!(); + } else { + FileMode::empty() + }; + + Ok(OpenMode::File(res, mode)) +} + +#[no_mangle] +unsafe extern "C" fn creat(pathname: *const c_char, mode: mode_t) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn fcntl(fd: c_int, cmd: c_int, args: ...) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn open(pathname: *const c_char, opts: c_int, mut args: ...) -> c_int { + pathname.ensure(); + let pathname = CStr::from_ptr(pathname).to_str().unwrap(); + let mut args = args.as_va_list(); + + let result = match open_opts(opts, &mut args) { + Ok(OpenMode::File(opts, mode)) => { + File::open_at(None, pathname, opts, mode).map(File::into_raw_fd) + } + Ok(OpenMode::Directory) => { + todo!(); + } + Err(_) => return -1, + }; + + match result { + Ok(fd) => fd.0.try_into().unwrap(), + Err(_) => -1, + } +} + +#[no_mangle] +unsafe extern "C" fn openat(atfd: c_int, pathname: *const c_char, opts: c_int, ...) -> c_int { + todo!() +} diff --git a/src/header/mod.rs b/src/header/mod.rs index 3a36bfa..f629492 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -3,13 +3,18 @@ pub mod sys_types; pub mod sys_wait; +pub mod ctype; +pub mod dirent; pub mod errno; +pub mod fcntl; pub mod limits; pub mod locale; pub mod math; pub mod stdio; pub mod stdlib; pub mod string; +pub mod sys_stat; +pub mod time; pub mod unistd; /* @@ -19,11 +24,11 @@ OSSUP arpa/inet.h - definitions for internet operations +++++ assert.h - verify program assertion ----- complex.h - complex arithmetic ----- cpio.h - cpio archive values ------ ctype.h - character types ------ dirent.h - format of directory entries ++++++ ctype.h - character types TODO locale +MOSTLY dirent.h - format of directory entries OSSUP dlfcn.h - dynamic linking MOSTLY errno.h - system error numbers ------ fcntl.h - file control options +MOSTLY fcntl.h - file control options ----- fenv.h - floating-point environment ----- float.h - floating types ----- fmtmsg.h - message display structures @@ -63,7 +68,7 @@ MAYBE spawn.h - spawn (ADVANCED REALTIME) +++++ stdint.h - integer types +++++ stdio.h - standard buffered input/output MOSTLY stdlib.h - standard library definitions -+++++ +++++ string.h - string operations NOTE: no locale yet ++++++ +++++ string.h - string operations TODO locale ----- strings.h - string operations NEVER stropts.h - STREAMS interface (STREAMS) NEVER sys/ipc.h - XSI interprocess communication access structure @@ -75,7 +80,7 @@ MAYBE sys/select.h - select types NOTE: maybe through PollWait emulati MAYBE sys/sem.h - XSI semaphore facility MAYBE sys/shm.h - XSI shared memory facility OSSUP sys/socket.h - main sockets header ------ sys/stat.h - data returned by the stat() function +PARTIAL sys/stat.h - data returned by the stat() function ----- sys/statvfs.h - VFS File System information structure ----- sys/time.h - time types ----- sys/times.h - file access and modification times structure diff --git a/src/header/sys_stat/cbindgen.toml b/src/header/sys_stat/cbindgen.toml new file mode 100644 index 0000000..9a004b1 --- /dev/null +++ b/src/header/sys_stat/cbindgen.toml @@ -0,0 +1,19 @@ +language = "C" +style = "Tag" + +sys_includes = [ + "stddef.h", + "stdint.h", + "sys/types.h", + "time.h", + "bits/sys/stat.h" +] +no_includes = true + +include_guard = "_SYS_STAT_H" + +usize_type = "size_t" +isize_type = "ssize_t" + +[export] +include = ["stat"] diff --git a/src/header/sys_stat/mod.rs b/src/header/sys_stat/mod.rs new file mode 100644 index 0000000..779a3c8 --- /dev/null +++ b/src/header/sys_stat/mod.rs @@ -0,0 +1,256 @@ +use core::{ + ffi::{c_char, c_int, CStr}, + mem::MaybeUninit, +}; + +use yggdrasil_rt::{ + io::{FileAttr, FileMode, FileType, RawFd}, + path::Path, + sys as syscall, +}; + +use crate::{ + error::{self, CZeroResult, EResult}, + io, + util::Nullable, +}; + +use super::{ + errno::{Errno, EBADF}, + fcntl::{AT_FDCWD, AT_SYMLINK_NOFOLLOW}, + sys_types::{blkcnt_t, blksize_t, dev_t, gid_t, ino_t, mode_t, nlink_t, off_t, uid_t}, + time::{__ygg_timespec_t, timespec}, +}; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[repr(C)] +pub struct stat { + pub st_dev: dev_t, + pub st_ino: ino_t, + pub st_mode: mode_t, + pub st_nlink: nlink_t, + pub st_uid: uid_t, + pub st_gid: gid_t, + pub st_rdev: dev_t, + pub st_size: off_t, + pub st_atim: __ygg_timespec_t, + pub st_mtim: __ygg_timespec_t, + pub st_ctim: __ygg_timespec_t, + pub st_blksize: blksize_t, + pub st_blocks: blkcnt_t, +} + +pub const S_IFMT: c_int = 0xF << 16; +pub const S_IFBLK: c_int = 1 << 16; +pub const S_IFCHR: c_int = 2 << 16; +pub const S_IFIFO: c_int = 3 << 16; +pub const S_IFREG: c_int = 4 << 16; +pub const S_IFDIR: c_int = 5 << 16; +pub const S_IFLNK: c_int = 6 << 16; +pub const S_IFSOCK: c_int = 7 << 16; + +pub const S_IRWXU: c_int = 0x7 << 6; +pub const S_IRUSR: c_int = 0x4 << 6; +pub const S_IWUSR: c_int = 0x2 << 6; +pub const S_IXUSR: c_int = 0x1 << 6; + +pub const S_IRWXG: c_int = 0x7 << 3; +pub const S_IRGRP: c_int = 0x4 << 3; +pub const S_IWGRP: c_int = 0x2 << 3; +pub const S_IXGRP: c_int = 0x1 << 3; + +pub const S_IRWXO: c_int = 0x7 << 0; +pub const S_IROTH: c_int = 0x4 << 0; +pub const S_IWOTH: c_int = 0x2 << 0; +pub const S_IXOTH: c_int = 0x1 << 0; + +pub const S_ISUID: c_int = 1 << 11; +pub const S_ISGID: c_int = 1 << 10; +pub const S_ISVTX: c_int = 1 << 9; + +impl From for stat { + fn from(value: FileAttr) -> Self { + // TODO no translation for st_dev/st_rdev/st_ino/etc + let mut st = Self::default(); + st.st_mode = (value.mode.bits() & 0o777) as _; + match value.ty { + FileType::Block => st.st_mode |= S_IFBLK, + FileType::Char => st.st_mode |= S_IFCHR, + FileType::File => st.st_mode |= S_IFREG, + FileType::Directory => st.st_mode |= S_IFDIR, + FileType::Symlink => st.st_mode |= S_IFLNK, + } + st.st_size = value.size.try_into().unwrap(); + // TODO + st.st_uid = 0; + st.st_gid = 0; + // TODO + st.st_blksize = 512; + st.st_blocks = (st.st_size + 511) / 512; + // TODO + st.st_nlink = 1; + + st + } +} + +#[no_mangle] +unsafe extern "C" fn chmod(pathname: *const c_char, mode: mode_t) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn fchmod(fd: c_int, mode: mode_t) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn fchmodat( + fd: c_int, + pathname: *const c_char, + mode: mode_t, + opt: c_int, +) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn umask(mode: mode_t) -> mode_t { + todo!() +} + +// Create stuff + +#[no_mangle] +unsafe extern "C" fn mkdir(pathname: *const c_char, mode: mode_t) -> c_int { + mkdirat(AT_FDCWD, pathname, mode) +} + +#[no_mangle] +unsafe extern "C" fn mkdirat(atfd: c_int, pathname: *const c_char, mode: mode_t) -> c_int { + pathname.ensure(); + let pathname = CStr::from_ptr(pathname).to_str().unwrap(); + + // TODO move this to a function + let atfd = match atfd { + // Same as stat() + AT_FDCWD => None, + 0.. => Some(RawFd(atfd as _)), + _ => { + error::set_errno(EBADF); + return -1; + } + }; + + let mode = FileMode::new((mode & 0o777) as u32); + + io::create_directory(atfd, pathname, mode).into_zero_status() +} + +#[no_mangle] +unsafe extern "C" fn mkfifo(pathname: *const c_char, mode: mode_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn mkfifoat(atfd: c_int, pathname: *const c_char, mode: mode_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn mknod(pathname: *const c_char, mode: mode_t, dev: dev_t) -> c_int { + unimplemented!() +} + +#[no_mangle] +unsafe extern "C" fn mknodat( + atfd: c_int, + pathname: *const c_char, + mode: mode_t, + dev: dev_t, +) -> c_int { + unimplemented!() +} + +// File status + +#[no_mangle] +unsafe extern "C" fn fstat(fd: c_int, statbuf: *mut stat) -> c_int { + if fd < 0 { + error::set_errno(EBADF); + return -1; + } + + let attr = match io::get_metadata(Some(RawFd(fd as _)), "", false) { + Ok(attr) => attr, + Err(_) => return -1, + }; + + if let Some(statbuf) = statbuf.as_mut() { + *statbuf = attr.into(); + } + + 0 +} + +#[no_mangle] +unsafe extern "C" fn fstatat( + atfd: c_int, + pathname: *const c_char, + statbuf: *mut stat, + opt: c_int, +) -> c_int { + pathname.ensure(); + let pathname = CStr::from_ptr(pathname).to_str().unwrap(); + + // TODO move this to a function + let atfd = match atfd { + // Same as stat() + AT_FDCWD => None, + 0.. => Some(RawFd(atfd as _)), + _ => { + error::set_errno(EBADF); + return -1; + } + }; + + let follow = opt & AT_SYMLINK_NOFOLLOW == 0; + + let attr = match io::get_metadata(atfd, pathname, follow) { + Ok(attr) => attr, + Err(_) => return -1, + }; + + if let Some(statbuf) = statbuf.as_mut() { + *statbuf = attr.into(); + } + + 0 +} + +#[no_mangle] +unsafe extern "C" fn lstat(pathname: *const c_char, statbuf: *mut stat) -> c_int { + fstatat(AT_FDCWD, pathname, statbuf, AT_SYMLINK_NOFOLLOW) +} + +#[no_mangle] +unsafe extern "C" fn stat(pathname: *const c_char, statbuf: *mut stat) -> c_int { + fstatat(AT_FDCWD, pathname, statbuf, 0) +} + +// File time updates + +#[no_mangle] +unsafe extern "C" fn futimens(fd: c_int, times: *const __ygg_timespec_t) -> c_int { + todo!() +} + +#[no_mangle] +unsafe extern "C" fn utimensat( + atfd: c_int, + pathname: *const c_char, + times: *const __ygg_timespec_t, + opt: c_int, +) -> c_int { + todo!() +} diff --git a/src/header/sys_types/mod.rs b/src/header/sys_types/mod.rs index bf1b57c..02888b9 100644 --- a/src/header/sys_types/mod.rs +++ b/src/header/sys_types/mod.rs @@ -1,4 +1,4 @@ -use core::ffi::c_ulong; +use core::ffi::{c_int, c_ulong}; pub type pid_t = i32; pub type uid_t = i32; @@ -10,7 +10,7 @@ pub type dev_t = u32; pub type clockid_t = i32; pub type timer_t = i32; -pub type mode_t = u32; +pub type mode_t = c_int; #[cfg(target_pointer_width = "64")] pub type ssize_t = i64; diff --git a/src/header/time/cbindgen.toml b/src/header/time/cbindgen.toml new file mode 100644 index 0000000..48d1068 --- /dev/null +++ b/src/header/time/cbindgen.toml @@ -0,0 +1,18 @@ +language = "C" +style = "Tag" + +sys_includes = [ + "stddef.h", + "stdint.h", + "sys/types.h", + "bits/time.h" +] +no_includes = true + +include_guard = "_TIME_H" + +usize_type = "size_t" +isize_type = "ssize_t" + +[export] +include = ["timespec"] diff --git a/src/header/time/mod.rs b/src/header/time/mod.rs new file mode 100644 index 0000000..8a9acc9 --- /dev/null +++ b/src/header/time/mod.rs @@ -0,0 +1,12 @@ +use core::ffi::c_long; + +use super::sys_types::time_t; + +#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[repr(C)] +pub struct timespec { + pub tv_sec: time_t, + pub tv_nsec: c_long, +} + +pub type __ygg_timespec_t = timespec; diff --git a/src/io/dir.rs b/src/io/dir.rs new file mode 100644 index 0000000..4bb5ef7 --- /dev/null +++ b/src/io/dir.rs @@ -0,0 +1,96 @@ +use core::mem::MaybeUninit; + +use alloc::{boxed::Box, vec::Vec}; +use yggdrasil_rt::{ + io::{DirectoryEntry, RawFd}, + path::Path, + sys as syscall, +}; + +use crate::{ + error::{self, EResult}, + header::errno::{Errno, EBADF}, +}; + +#[derive(Debug)] +pub struct DirReader { + fd: Option, + + // Read buffer + buffer: Box<[MaybeUninit]>, + position: usize, + len: usize, +} + +impl DirReader { + const BUFFER_SIZE: usize = 16; + + pub fn open_at>(at: Option, path: P) -> Result { + let fd = EResult::from(unsafe { syscall::open_directory(None, path.as_ref().as_str()) })?; + Ok(unsafe { Self::from_raw_fd(fd) }) + } + + pub unsafe fn from_raw_fd(fd: RawFd) -> Self { + Self { + fd: Some(fd), + + buffer: Box::new_uninit_slice(Self::BUFFER_SIZE), + position: 0, + len: 0, + } + } + + pub unsafe fn close(&mut self) -> Result<(), Errno> { + if let Some(fd) = self.fd.take() { + EResult::from(unsafe { syscall::close(fd) })?; + Ok(()) + } else { + error::set_errno_result(EBADF) + } + } + + pub fn as_raw_fd(&self) -> RawFd { + self.fd.unwrap() + } + + fn fill_buf(&mut self) -> Result<&[DirectoryEntry], Errno> { + let Some(fd) = self.fd else { + return error::set_errno_result(EBADF); + }; + + if self.position == self.len { + let count = + EResult::from(unsafe { syscall::read_directory_entries(fd, &mut self.buffer) })?; + + self.position = 0; + self.len = count; + } + + Ok(unsafe { MaybeUninit::slice_assume_init_ref(&self.buffer[self.position..self.len]) }) + } + + fn consume(&mut self, count: usize) { + self.position = (self.position + count).min(self.len); + } + + pub fn read_entry(&mut self) -> Result, Errno> { + let buf = self.fill_buf()?; + + if let Some(&entry) = buf.get(0) { + self.consume(1); + Ok(Some(entry)) + } else { + Ok(None) + } + } +} + +impl Drop for DirReader { + fn drop(&mut self) { + if self.fd.is_some() { + unsafe { + self.close().ok(); + } + } + } +} diff --git a/src/io/mod.rs b/src/io/mod.rs index d489978..bdd215b 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -1,8 +1,18 @@ -use yggdrasil_rt::io::SeekFrom; +use core::mem::MaybeUninit; -use crate::header::errno::{Errno, EINTR}; +use yggdrasil_rt::{ + io::{FileAttr, FileMode, RawFd, SeekFrom}, + path::Path, + sys as syscall, +}; + +use crate::{ + error::EResult, + header::errno::{Errno, EINTR}, +}; pub mod buffered; +pub mod dir; pub trait Write { fn write(&mut self, data: &[u8]) -> Result; @@ -49,3 +59,22 @@ pub trait Seek { self.seek(SeekFrom::Current(0)) } } + +pub fn get_metadata>( + at: Option, + path: P, + follow: bool, +) -> Result { + let mut attr = MaybeUninit::uninit(); + EResult::from(unsafe { syscall::get_metadata(at, path.as_ref().as_str(), &mut attr, follow) })?; + Ok(unsafe { attr.assume_init() }) +} + +pub fn create_directory>( + at: Option, + path: P, + mode: FileMode, +) -> Result<(), Errno> { + EResult::from(unsafe { syscall::create_directory(at, path.as_ref().as_str(), mode) })?; + Ok(()) +}