Selaa lähdekoodia

feat: impl openat and argument printing in trace_syscall

greatbridf 7 kuukautta sitten
vanhempi
commit
dd1d5927e1

+ 1 - 0
Cargo.lock

@@ -304,6 +304,7 @@ version = "0.1.0"
 name = "posix_types"
 version = "0.1.0"
 dependencies = [
+ "bitflags",
  "cfg-if",
 ]
 

+ 1 - 0
crates/posix_types/Cargo.toml

@@ -5,3 +5,4 @@ edition = "2024"
 
 [dependencies]
 cfg-if = "1.0"
+bitflags = "2.6.0"

+ 1 - 0
crates/posix_types/src/lib.rs

@@ -1,6 +1,7 @@
 #![no_std]
 
 pub mod constants;
+pub mod open;
 pub mod result;
 pub mod signal;
 pub mod stat;

+ 121 - 0
crates/posix_types/src/open.rs

@@ -0,0 +1,121 @@
+use bitflags::bitflags;
+
+bitflags! {
+    #[derive(Debug, Clone, Copy)]
+    pub struct OpenFlags: u32 {
+        /// Open for writing only
+        const O_WRONLY = 0x1;
+        /// Open for reading and writing
+        const O_RDWR = 0x2;
+        /// Create file if it does not exist
+        const O_CREAT = 0x40;
+        /// Exclusive access, fail if file exists
+        const O_EXCL = 0x80;
+        /// Truncate file to zero length if it exists
+        const O_TRUNC = 0x200;
+        /// Open file in append mode
+        const O_APPEND = 0x400;
+        /// Non-blocking mode
+        const O_NONBLOCK = 0x800;
+        /// Open directory
+        const O_DIRECTORY = 0x10000;
+        /// Do not follow symbolic links
+        const O_NOFOLLOW = 0x20000;
+        /// Close on exec
+        const O_CLOEXEC = 0x80000;
+    }
+
+    #[derive(Debug, Clone, Copy)]
+    pub struct FDFlags: u32 {
+        /// Close on exec
+        const FD_CLOEXEC = 0x1;
+    }
+
+    #[derive(Debug, Clone, Copy)]
+    pub struct AtFlags: u32 {
+        /// Do not follow symbolic links
+        const AT_SYMLINK_NOFOLLOW = 0x100;
+        /// Allow removal of directories
+        const AT_REMOVEDIR = 0x200;
+        /// Follow symbolic links when resolving paths
+        const AT_SYMLINK_FOLLOW = 0x400;
+        /// Use the file descriptor with empty path
+        const AT_EMPTY_PATH = 0x1000;
+        /// Force synchronization of file attributes
+        const AT_STATX_FORCE_SYNC = 0x2000;
+        /// Do not synchronize file attributes
+        const AT_STATX_DONT_SYNC = 0x4000;
+    }
+}
+
+impl FDFlags {
+    pub fn close_on_exec(&self) -> bool {
+        self.contains(FDFlags::FD_CLOEXEC)
+    }
+}
+
+impl OpenFlags {
+    pub fn as_fd_flags(&self) -> FDFlags {
+        if self.contains(OpenFlags::O_CLOEXEC) {
+            FDFlags::FD_CLOEXEC
+        } else {
+            FDFlags::empty()
+        }
+    }
+
+    pub fn read(&self) -> bool {
+        !self.contains(Self::O_WRONLY)
+    }
+
+    pub fn write(&self) -> bool {
+        self.intersects(Self::O_WRONLY | Self::O_RDWR)
+    }
+
+    pub fn append(&self) -> bool {
+        self.contains(Self::O_APPEND)
+    }
+
+    pub fn directory(&self) -> bool {
+        self.contains(Self::O_DIRECTORY)
+    }
+
+    pub fn truncate(&self) -> bool {
+        self.contains(Self::O_TRUNC)
+    }
+
+    pub fn follow_symlink(&self) -> bool {
+        !self.contains(Self::O_NOFOLLOW)
+    }
+
+    pub fn as_rwa(&self) -> (bool, bool, bool) {
+        (self.read(), self.write(), self.append())
+    }
+}
+
+impl AtFlags {
+    pub fn at_empty_path(&self) -> bool {
+        self.contains(AtFlags::AT_EMPTY_PATH)
+    }
+
+    /// # Notice
+    /// `no_follow` and `follow` are **DIFFERENT** and are used in different contexts.
+    ///
+    /// `follow` is used to reverse the default behavior of `linkat`, which does not
+    /// follow symlinks by default.
+    pub fn no_follow(&self) -> bool {
+        self.contains(AtFlags::AT_SYMLINK_NOFOLLOW)
+    }
+
+    /// # Notice
+    /// `no_follow` and `follow` are **DIFFERENT** and are used in different contexts.
+    ///
+    /// `follow` is used to reverse the default behavior of `linkat`, which does not
+    /// follow symlinks by default.
+    pub fn follow(&self) -> bool {
+        self.contains(AtFlags::AT_SYMLINK_FOLLOW)
+    }
+
+    pub fn statx_default_sync(&self) -> bool {
+        !self.intersects(AtFlags::AT_STATX_FORCE_SYNC | AtFlags::AT_STATX_DONT_SYNC)
+    }
+}

+ 35 - 14
macros/src/lib.rs

@@ -64,6 +64,29 @@ fn define_syscall_impl(attrs: TokenStream, item: TokenStream) -> TokenStream {
     let syscall_fn_section =
         LitStr::new(&format!(".syscall_fns.{}", syscall_name), Span::call_site());
 
+    let trace_format_string = {
+        let arg_count = item.sig.inputs.len();
+        let brackets = (0..arg_count)
+            .map(|_| String::from("{:x?}"))
+            .collect::<Vec<_>>()
+            .join(", ");
+
+        LitStr::new(&brackets, Span::call_site())
+    };
+
+    let trace_format_args = {
+        let args = item.sig.inputs.iter();
+        let args = args.enumerate().map(|(idx, arg)| match arg {
+            FnArg::Receiver(_) => panic!("&self is not permitted."),
+            FnArg::Typed(_) => {
+                let arg_ident = Ident::new(&format!("arg_{}", idx), Span::call_site());
+                quote! { #arg_ident }
+            }
+        });
+
+        quote! { #(#args,)* }
+    };
+
     quote! {
         #[used]
         #[doc(hidden)]
@@ -85,23 +108,21 @@ fn define_syscall_impl(attrs: TokenStream, item: TokenStream) -> TokenStream {
 
             #(#args_mapped)*
 
-            // eonix_log::println_trace!(
-            //     "trace_syscall",
-            //     "tid{}: {}({}) => {{",
-            //     crate::kernel::task::Thread::current().tid,
-            //     #syscall_name_str,
-            //     crate::kernel::syscall::format_expand!($($arg, $arg),*),
-            // );
+            eonix_log::println_trace!(
+                "trace_syscall",
+                "tid{}: {}({}) => {{",
+                thd.tid,
+                #syscall_name_str,
+                format_args!(#trace_format_string, #trace_format_args),
+            );
 
             let retval = #real_fn(thd, #(#args_call),*).into_retval();
 
-            // eonix_log::println_trace!(
-            //     "trace_syscall",
-            //     "tid{}: {}({}) => {{",
-            //     crate::kernel::task::Thread::current().tid,
-            //     #syscall_name_str,
-            //     crate::kernel::syscall::format_expand!($($arg, $arg),*),
-            // );
+            eonix_log::println_trace!(
+                "trace_syscall",
+                "}} => {:x?}",
+                retval,
+            );
 
             retval
         }

+ 0 - 21
src/kernel/constants.rs

@@ -53,37 +53,16 @@ pub const S_IFMT: u32 = 0o170000;
 
 pub const RLIMIT_STACK: u32 = 0x3;
 
-pub const AT_FDCWD: i32 = -100;
-pub const AT_STATX_SYNC_AS_STAT: u32 = 0;
-pub const AT_SYMLINK_NOFOLLOW: u32 = 0x0100;
-// pub const AT_REMOVEDIR: u32 = 0x0200;
-// pub const AT_SYMLINK_FOLLOW: u32 = 0x0400;
-pub const AT_EMPTY_PATH: u32 = 0x1000;
-// pub const AT_STATX_DONT_SYNC: u32 = 0x2000;
-pub const AT_STATX_SYNC_TYPE: u32 = 0x6000;
-// pub const AT_STATX_SYNC_FORCE: u32 = 0x8000;
-
 pub const SEEK_SET: u32 = 0;
 pub const SEEK_CUR: u32 = 1;
 pub const SEEK_END: u32 = 2;
 
-// pub const O_RDONLY: u32 = 0;
-pub const O_WRONLY: u32 = 1;
-pub const O_RDWR: u32 = 2;
-pub const O_CREAT: u32 = 64;
-pub const O_EXCL: u32 = 128;
-pub const O_TRUNC: u32 = 512;
-pub const O_APPEND: u32 = 1024;
-pub const O_DIRECTORY: u32 = 65536;
-pub const O_CLOEXEC: u32 = 524288;
-
 pub const F_DUPFD: u32 = 0;
 pub const F_GETFD: u32 = 1;
 pub const F_SETFD: u32 = 2;
 // pub const F_GETFL: u32 = 3;
 // pub const F_SETFL: u32 = 4;
 pub const F_DUPFD_CLOEXEC: u32 = 1030;
-pub const FD_CLOEXEC: u32 = 1;
 
 pub const STATX_TYPE: u32 = 1;
 pub const STATX_MODE: u32 = 2;

+ 3 - 2
src/kernel/syscall.rs

@@ -9,6 +9,7 @@ pub mod sysinfo;
 
 const MAX_SYSCALL_NO: usize = 512;
 
+#[derive(Debug, Clone, Copy)]
 pub struct SyscallNoReturn;
 
 #[repr(C)]
@@ -23,11 +24,11 @@ pub struct SyscallHandler {
     pub name: &'static str,
 }
 
-pub trait FromSyscallArg {
+pub trait FromSyscallArg: core::fmt::Debug {
     fn from_arg(value: usize) -> Self;
 }
 
-pub trait SyscallRetVal {
+pub trait SyscallRetVal: core::fmt::Debug {
     fn into_retval(self) -> Option<usize>;
 }
 

+ 76 - 81
src/kernel/syscall/file_rw.rs

@@ -1,12 +1,12 @@
+use super::FromSyscallArg;
 use crate::kernel::constants::{
-    AT_FDCWD, AT_STATX_SYNC_AS_STAT, AT_STATX_SYNC_TYPE, AT_SYMLINK_NOFOLLOW, EBADF, ENOTDIR,
-    SEEK_CUR, SEEK_END, SEEK_SET, S_IFBLK, S_IFCHR,
+    EBADF, EFAULT, EINVAL, ENOENT, ENOTDIR, SEEK_CUR, SEEK_END, SEEK_SET, S_IFBLK, S_IFCHR,
 };
 use crate::kernel::task::Thread;
+use crate::kernel::vfs::filearray::FD;
 use crate::{
     io::{Buffer, BufferFill},
     kernel::{
-        constants::{AT_EMPTY_PATH, EFAULT, EINVAL, ENOENT},
         user::{
             dataflow::{CheckedUserPointer, UserBuffer, UserString},
             UserPointer, UserPointerMut,
@@ -22,20 +22,32 @@ use crate::{
 use alloc::sync::Arc;
 use core::mem::MaybeUninit;
 use eonix_runtime::task::Task;
+use posix_types::open::{AtFlags, OpenFlags};
 use posix_types::stat::{Stat, StatX};
 use posix_types::syscall_no::*;
 
+impl FromSyscallArg for OpenFlags {
+    fn from_arg(value: usize) -> Self {
+        OpenFlags::from_bits_retain(value as u32)
+    }
+}
+
+impl FromSyscallArg for AtFlags {
+    fn from_arg(value: usize) -> Self {
+        AtFlags::from_bits_retain(value as u32)
+    }
+}
+
 fn dentry_from(
     thread: &Thread,
-    dirfd: u32,
+    dirfd: FD,
     pathname: *const u8,
     follow_symlink: bool,
 ) -> KResult<Arc<Dentry>> {
-    const _AT_FDCWD: u32 = AT_FDCWD as u32;
     let path = UserString::new(pathname)?;
 
     match (path.as_cstr().to_bytes_with_nul()[0], dirfd) {
-        (b'/', _) | (_, _AT_FDCWD) => {
+        (b'/', _) | (_, FD::AT_FDCWD) => {
             let path = Path::new(path.as_cstr().to_bytes())?;
             Dentry::open(&thread.fs_context, path, follow_symlink)
         }
@@ -54,49 +66,50 @@ fn dentry_from(
 }
 
 #[eonix_macros::define_syscall(SYS_READ)]
-fn read(fd: u32, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
+fn read(fd: FD, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
     let mut buffer = UserBuffer::new(buffer, bufsize)?;
 
     Task::block_on(thread.files.get(fd).ok_or(EBADF)?.read(&mut buffer))
 }
 
 #[eonix_macros::define_syscall(SYS_WRITE)]
-fn write(fd: u32, buffer: *const u8, count: usize) -> KResult<usize> {
+fn write(fd: FD, buffer: *const u8, count: usize) -> KResult<usize> {
     let data = unsafe { core::slice::from_raw_parts(buffer, count) };
 
     Task::block_on(thread.files.get(fd).ok_or(EBADF)?.write(data))
 }
 
+#[eonix_macros::define_syscall(SYS_OPENAT)]
+fn openat(dirfd: FD, pathname: *const u8, flags: OpenFlags, mode: u32) -> KResult<FD> {
+    let dentry = dentry_from(thread, dirfd, pathname, flags.follow_symlink())?;
+    thread.files.open(&dentry, flags, mode)
+}
+
 #[cfg(target_arch = "x86_64")]
 #[eonix_macros::define_syscall(SYS_OPEN)]
-fn open(path: *const u8, flags: u32, mode: u32) -> KResult<u32> {
-    let path = UserString::new(path)?;
-    let path = Path::new(path.as_cstr().to_bytes())?;
-
-    let mode = mode & !*thread.fs_context.umask.lock();
-
-    thread.files.open(&thread.fs_context, path, flags, mode)
+fn open(path: *const u8, flags: OpenFlags, mode: u32) -> KResult<FD> {
+    sys_openat(thread, FD::AT_FDCWD, path, flags, mode)
 }
 
 #[eonix_macros::define_syscall(SYS_CLOSE)]
-fn close(fd: u32) -> KResult<()> {
+fn close(fd: FD) -> KResult<()> {
     thread.files.close(fd)
 }
 
 #[eonix_macros::define_syscall(SYS_DUP)]
-fn dup(fd: u32) -> KResult<u32> {
+fn dup(fd: FD) -> KResult<FD> {
     thread.files.dup(fd)
 }
 
 #[cfg(target_arch = "x86_64")]
 #[eonix_macros::define_syscall(SYS_DUP2)]
-fn dup2(old_fd: u32, new_fd: u32) -> KResult<u32> {
-    thread.files.dup_to(old_fd, new_fd, 0)
+fn dup2(old_fd: FD, new_fd: FD) -> KResult<FD> {
+    thread.files.dup_to(old_fd, new_fd, OpenFlags::empty())
 }
 
 #[eonix_macros::define_syscall(SYS_PIPE2)]
-fn pipe2(pipe_fd: *mut [u32; 2], flags: u32) -> KResult<()> {
-    let mut buffer = UserBuffer::new(pipe_fd as *mut u8, core::mem::size_of::<[u32; 2]>())?;
+fn pipe2(pipe_fd: *mut [FD; 2], flags: OpenFlags) -> KResult<()> {
+    let mut buffer = UserBuffer::new(pipe_fd as *mut u8, core::mem::size_of::<[FD; 2]>())?;
     let (read_fd, write_fd) = thread.files.pipe(flags)?;
 
     buffer.copy(&[read_fd, write_fd])?.ok_or(EFAULT)
@@ -104,13 +117,13 @@ fn pipe2(pipe_fd: *mut [u32; 2], flags: u32) -> KResult<()> {
 
 #[cfg(target_arch = "x86_64")]
 #[eonix_macros::define_syscall(SYS_PIPE)]
-fn pipe(pipe_fd: *mut [u32; 2]) -> KResult<()> {
-    sys_pipe2(thread, pipe_fd, 0)
+fn pipe(pipe_fd: *mut [FD; 2]) -> KResult<()> {
+    sys_pipe2(thread, pipe_fd, OpenFlags::empty())
 }
 
 #[cfg(target_arch = "x86_64")]
 #[eonix_macros::define_syscall(SYS_GETDENTS)]
-fn getdents(fd: u32, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
+fn getdents(fd: FD, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
     let mut buffer = UserBuffer::new(buffer, bufsize)?;
 
     thread.files.get(fd).ok_or(EBADF)?.getdents(&mut buffer)?;
@@ -118,7 +131,7 @@ fn getdents(fd: u32, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
 }
 
 #[eonix_macros::define_syscall(SYS_GETDENTS64)]
-fn getdents64(fd: u32, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
+fn getdents64(fd: FD, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
     let mut buffer = UserBuffer::new(buffer, bufsize)?;
 
     thread.files.get(fd).ok_or(EBADF)?.getdents64(&mut buffer)?;
@@ -127,13 +140,12 @@ fn getdents64(fd: u32, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
 
 #[cfg(not(target_arch = "x86_64"))]
 #[eonix_macros::define_syscall(SYS_NEWFSTATAT)]
-fn newfstatat(dirfd: u32, pathname: *const u8, statbuf: *mut Stat, flags: u32) -> KResult<()> {
-    let dentry = if (flags & AT_EMPTY_PATH) != 0 {
+fn newfstatat(dirfd: FD, pathname: *const u8, statbuf: *mut Stat, flags: AtFlags) -> KResult<()> {
+    let dentry = if flags.at_empty_path() {
         let file = thread.files.get(dirfd).ok_or(EBADF)?;
         file.as_path().ok_or(EBADF)?.clone()
     } else {
-        let follow_symlink = (flags & AT_SYMLINK_NOFOLLOW) != AT_SYMLINK_NOFOLLOW;
-        dentry_from(thread, dirfd, pathname, follow_symlink)?
+        dentry_from(thread, dirfd, pathname, !flags.no_follow())?
     };
 
     let statbuf = UserPointerMut::new(statbuf)?;
@@ -147,46 +159,35 @@ fn newfstatat(dirfd: u32, pathname: *const u8, statbuf: *mut Stat, flags: u32) -
 }
 
 #[eonix_macros::define_syscall(SYS_STATX)]
-fn statx(dirfd: u32, path: *const u8, flags: u32, mask: u32, buffer: *mut u8) -> KResult<()> {
-    if (flags & AT_STATX_SYNC_TYPE) != AT_STATX_SYNC_AS_STAT {
-        unimplemented!("AT_STATX_SYNC_TYPE={:x}", flags & AT_STATX_SYNC_TYPE);
+fn statx(
+    dirfd: FD,
+    pathname: *const u8,
+    flags: AtFlags,
+    mask: u32,
+    buffer: *mut StatX,
+) -> KResult<()> {
+    if !flags.statx_default_sync() {
+        unimplemented!("statx with no default sync flags: {:?}", flags);
     }
 
-    let mut stat: StatX = unsafe { MaybeUninit::zeroed().assume_init() };
-    let mut buffer = UserBuffer::new(buffer, core::mem::size_of::<StatX>())?;
+    let mut statx = StatX::default();
+    let buffer = UserPointerMut::new(buffer)?;
 
-    if (flags & AT_EMPTY_PATH) != 0 {
+    let dentry = if flags.at_empty_path() {
         let file = thread.files.get(dirfd).ok_or(EBADF)?;
-        file.statx(&mut stat, mask)?;
+        file.as_path().ok_or(EBADF)?.clone()
     } else {
-        let path = UserString::new(path)?;
-        let path = Path::new(path.as_cstr().to_bytes())?;
-
-        let file;
-        if dirfd != AT_FDCWD as u32 && !path.is_absolute() {
-            let at = thread.files.get(dirfd).ok_or(EBADF)?;
-            file = Dentry::open_at(
-                &thread.fs_context,
-                at.as_path().ok_or(EBADF)?,
-                path,
-                (flags & AT_SYMLINK_NOFOLLOW) != AT_SYMLINK_NOFOLLOW,
-            )?;
-        } else {
-            file = Dentry::open(
-                &thread.fs_context,
-                path,
-                (flags & AT_SYMLINK_NOFOLLOW) != AT_SYMLINK_NOFOLLOW,
-            )?;
-        }
+        dentry_from(thread, dirfd, pathname, !flags.no_follow())?
+    };
 
-        file.statx(&mut stat, mask)?;
-    }
+    dentry.statx(&mut statx, mask)?;
+    buffer.write(statx)?;
 
-    buffer.copy(&stat)?.ok_or(EFAULT)
+    Ok(())
 }
 
 #[eonix_macros::define_syscall(SYS_MKDIRAT)]
-fn mkdirat(dirfd: u32, pathname: *const u8, mode: u32) -> KResult<()> {
+fn mkdirat(dirfd: FD, pathname: *const u8, mode: u32) -> KResult<()> {
     let umask = *thread.fs_context.umask.lock();
     let mode = mode & !umask & 0o777;
 
@@ -197,7 +198,7 @@ fn mkdirat(dirfd: u32, pathname: *const u8, mode: u32) -> KResult<()> {
 #[cfg(target_arch = "x86_64")]
 #[eonix_macros::define_syscall(SYS_MKDIR)]
 fn mkdir(pathname: *const u8, mode: u32) -> KResult<()> {
-    sys_mkdirat(thread, AT_FDCWD as u32, pathname, mode)
+    sys_mkdirat(thread, FD::AT_FDCWD, pathname, mode)
 }
 
 #[cfg(target_arch = "x86_64")]
@@ -212,18 +213,18 @@ fn truncate(pathname: *const u8, length: usize) -> KResult<()> {
 }
 
 #[eonix_macros::define_syscall(SYS_UNLINKAT)]
-fn unlinkat(dirfd: u32, pathname: *const u8) -> KResult<()> {
+fn unlinkat(dirfd: FD, pathname: *const u8) -> KResult<()> {
     dentry_from(thread, dirfd, pathname, false)?.unlink()
 }
 
 #[cfg(target_arch = "x86_64")]
 #[eonix_macros::define_syscall(SYS_UNLINK)]
 fn unlink(pathname: *const u8) -> KResult<()> {
-    sys_unlinkat(thread, AT_FDCWD as u32, pathname)
+    sys_unlinkat(thread, FD::AT_FDCWD, pathname)
 }
 
 #[eonix_macros::define_syscall(SYS_SYMLINKAT)]
-fn symlinkat(dirfd: u32, target: *const u8, linkpath: *const u8) -> KResult<()> {
+fn symlinkat(dirfd: FD, target: *const u8, linkpath: *const u8) -> KResult<()> {
     let target = UserString::new(target)?;
     let dentry = dentry_from(thread, dirfd, linkpath, false)?;
 
@@ -233,11 +234,11 @@ fn symlinkat(dirfd: u32, target: *const u8, linkpath: *const u8) -> KResult<()>
 #[cfg(target_arch = "x86_64")]
 #[eonix_macros::define_syscall(SYS_SYMLINK)]
 fn symlink(target: *const u8, linkpath: *const u8) -> KResult<()> {
-    sys_symlinkat(thread, AT_FDCWD as u32, target, linkpath)
+    sys_symlinkat(thread, FD::AT_FDCWD, target, linkpath)
 }
 
 #[eonix_macros::define_syscall(SYS_MKNODAT)]
-fn mknodat(dirfd: u32, pathname: *const u8, mode: u32, dev: u32) -> KResult<()> {
+fn mknodat(dirfd: FD, pathname: *const u8, mode: u32, dev: u32) -> KResult<()> {
     let dentry = dentry_from(thread, dirfd, pathname, true)?;
 
     let umask = *thread.fs_context.umask.lock();
@@ -249,11 +250,11 @@ fn mknodat(dirfd: u32, pathname: *const u8, mode: u32, dev: u32) -> KResult<()>
 #[cfg(target_arch = "x86_64")]
 #[eonix_macros::define_syscall(SYS_MKNOD)]
 fn mknod(pathname: *const u8, mode: u32, dev: u32) -> KResult<()> {
-    sys_mknodat(thread, AT_FDCWD as u32, pathname, mode, dev)
+    sys_mknodat(thread, FD::AT_FDCWD, pathname, mode, dev)
 }
 
 #[eonix_macros::define_syscall(SYS_READLINKAT)]
-fn readlinkat(dirfd: u32, pathname: *const u8, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
+fn readlinkat(dirfd: FD, pathname: *const u8, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
     let dentry = dentry_from(thread, dirfd, pathname, false)?;
     let mut buffer = UserBuffer::new(buffer, bufsize)?;
 
@@ -263,18 +264,12 @@ fn readlinkat(dirfd: u32, pathname: *const u8, buffer: *mut u8, bufsize: usize)
 #[cfg(target_arch = "x86_64")]
 #[eonix_macros::define_syscall(SYS_READLINK)]
 fn readlink(pathname: *const u8, buffer: *mut u8, bufsize: usize) -> KResult<usize> {
-    sys_readlinkat(thread, AT_FDCWD as u32, pathname, buffer, bufsize)
+    sys_readlinkat(thread, FD::AT_FDCWD, pathname, buffer, bufsize)
 }
 
 #[cfg(target_arch = "x86_64")]
 #[eonix_macros::define_syscall(SYS_LLSEEK)]
-fn llseek(
-    fd: u32,
-    offset_high: u32,
-    offset_low: u32,
-    result: *mut u64,
-    whence: u32,
-) -> KResult<()> {
+fn llseek(fd: FD, offset_high: u32, offset_low: u32, result: *mut u64, whence: u32) -> KResult<()> {
     let mut result = UserBuffer::new(result as *mut u8, core::mem::size_of::<u64>())?;
     let file = thread.files.get(fd).ok_or(EBADF)?;
 
@@ -298,7 +293,7 @@ struct IoVec32 {
 }
 
 #[eonix_macros::define_syscall(SYS_READV)]
-fn readv(fd: u32, iov_user: *const IoVec32, iovcnt: u32) -> KResult<usize> {
+fn readv(fd: FD, iov_user: *const IoVec32, iovcnt: u32) -> KResult<usize> {
     let file = thread.files.get(fd).ok_or(EBADF)?;
 
     let mut iov_user = UserPointer::new(iov_user as *mut IoVec32)?;
@@ -330,7 +325,7 @@ fn readv(fd: u32, iov_user: *const IoVec32, iovcnt: u32) -> KResult<usize> {
 }
 
 #[eonix_macros::define_syscall(SYS_WRITEV)]
-fn writev(fd: u32, iov_user: *const u8, iovcnt: u32) -> KResult<usize> {
+fn writev(fd: FD, iov_user: *const u8, iovcnt: u32) -> KResult<usize> {
     let file = thread.files.get(fd).ok_or(EBADF)?;
 
     // TODO: Rewrite this with `UserPointer`.
@@ -390,7 +385,7 @@ fn access(pathname: *const u8, _mode: u32) -> KResult<()> {
 }
 
 #[eonix_macros::define_syscall(SYS_SENDFILE64)]
-fn sendfile64(out_fd: u32, in_fd: u32, offset: *mut u8, count: usize) -> KResult<usize> {
+fn sendfile64(out_fd: FD, in_fd: FD, offset: *mut u8, count: usize) -> KResult<usize> {
     let in_file = thread.files.get(in_fd).ok_or(EBADF)?;
     let out_file = thread.files.get(out_fd).ok_or(EBADF)?;
 
@@ -402,21 +397,21 @@ fn sendfile64(out_fd: u32, in_fd: u32, offset: *mut u8, count: usize) -> KResult
 }
 
 #[eonix_macros::define_syscall(SYS_IOCTL)]
-fn ioctl(fd: u32, request: usize, arg3: usize) -> KResult<usize> {
+fn ioctl(fd: FD, request: usize, arg3: usize) -> KResult<usize> {
     let file = thread.files.get(fd).ok_or(EBADF)?;
 
     file.ioctl(request, arg3)
 }
 
 #[eonix_macros::define_syscall(SYS_FCNTL64)]
-fn fcntl64(fd: u32, cmd: u32, arg: usize) -> KResult<usize> {
+fn fcntl64(fd: FD, cmd: u32, arg: usize) -> KResult<usize> {
     thread.files.fcntl(fd, cmd, arg)
 }
 
 #[repr(C)]
 #[derive(Debug, Clone, Copy)]
 struct UserPollFd {
-    fd: u32,
+    fd: FD,
     events: u16,
     revents: u16,
 }

+ 1 - 8
src/kernel/task/thread.rs

@@ -324,14 +324,7 @@ impl Thread {
                 handler,
                 name: _name,
                 ..
-            })) => {
-                println_trace!(
-                    "trace_syscall",
-                    "Syscall {_name}({no:#x}) on tid {:#x}",
-                    self.tid
-                );
-                handler(self, args)
-            }
+            })) => handler(self, args),
             _ => {
                 println_warn!("Syscall {no}({no:#x}) isn't implemented.");
                 self.raise(Signal::SIGSYS);

+ 8 - 11
src/kernel/vfs/dentry.rs

@@ -4,9 +4,7 @@ use super::{
     inode::{Ino, Inode, Mode, WriteOffset},
     s_isblk, s_ischr, s_isdir, s_isreg, DevId, FsContext,
 };
-use crate::kernel::constants::{
-    EEXIST, EINVAL, EISDIR, ELOOP, ENOENT, ENOTDIR, EPERM, ERANGE, O_CREAT, O_EXCL,
-};
+use crate::kernel::constants::{EEXIST, EINVAL, EISDIR, ELOOP, ENOENT, ENOTDIR, EPERM, ERANGE};
 use crate::{
     hash::KernelHasher,
     io::{Buffer, ByteBuffer},
@@ -23,7 +21,7 @@ use core::{
     sync::atomic::{AtomicPtr, Ordering},
 };
 use eonix_sync::LazyLock;
-use posix_types::stat::StatX;
+use posix_types::{open::OpenFlags, stat::StatX};
 
 struct DentryData {
     inode: Arc<dyn Inode>,
@@ -207,18 +205,17 @@ impl Dentry {
         self.data.load().is_some()
     }
 
-    pub fn open_check(self: &Arc<Self>, flags: u32, mode: Mode) -> KResult<()> {
+    pub fn open_check(self: &Arc<Self>, flags: OpenFlags, mode: Mode) -> KResult<()> {
         let data = self.data.load();
-        let create = flags & O_CREAT != 0;
-        let excl = flags & O_EXCL != 0;
 
         if data.is_some() {
-            if create && excl {
-                return Err(EEXIST);
+            if flags.contains(OpenFlags::O_CREAT | OpenFlags::O_EXCL) {
+                Err(EEXIST)
+            } else {
+                Ok(())
             }
-            return Ok(());
         } else {
-            if !create {
+            if !flags.contains(OpenFlags::O_CREAT) {
                 return Err(ENOENT);
             }
 

+ 62 - 50
src/kernel/vfs/filearray.rs

@@ -1,11 +1,11 @@
 use super::{
     file::{File, InodeFile, TerminalFile},
     inode::Mode,
-    s_ischr, FsContext, Spin,
+    s_ischr, Spin,
 };
-use crate::kernel::constants::{
-    EBADF, EISDIR, ENOTDIR, FD_CLOEXEC, F_DUPFD, F_DUPFD_CLOEXEC, F_GETFD, F_SETFD, O_APPEND,
-    O_CLOEXEC, O_DIRECTORY, O_RDWR, O_TRUNC, O_WRONLY,
+use crate::kernel::{
+    constants::{EBADF, EISDIR, ENOTDIR, F_DUPFD, F_DUPFD_CLOEXEC, F_GETFD, F_SETFD},
+    syscall::{FromSyscallArg, SyscallRetVal},
 };
 use crate::{
     kernel::{
@@ -14,7 +14,6 @@ use crate::{
         vfs::{dentry::Dentry, file::Pipe, s_isdir, s_isreg},
         CharDevice,
     },
-    path::Path,
     prelude::*,
 };
 use alloc::{
@@ -26,13 +25,14 @@ use itertools::{
     FoldWhile::{Continue, Done},
     Itertools,
 };
+use posix_types::open::{FDFlags, OpenFlags};
 
-type FD = u32;
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct FD(u32);
 
 #[derive(Clone)]
 struct OpenFile {
-    /// File descriptor flags, only for `FD_CLOEXEC`.
-    flags: u64,
+    flags: FDFlags,
     file: Arc<File>,
 }
 
@@ -48,7 +48,7 @@ pub struct FileArray {
 
 impl OpenFile {
     pub fn close_on_exec(&self) -> bool {
-        self.flags & O_CLOEXEC as u64 != 0
+        self.flags.contains(FDFlags::FD_CLOEXEC)
     }
 }
 
@@ -57,7 +57,7 @@ impl FileArray {
         Arc::new(FileArray {
             inner: Spin::new(FileArrayInner {
                 files: BTreeMap::new(),
-                fd_min_avail: 0,
+                fd_min_avail: FD(0),
             }),
         })
     }
@@ -80,7 +80,7 @@ impl FileArray {
 
     pub fn close_all(&self) {
         let mut inner = self.inner.lock();
-        inner.fd_min_avail = 0;
+        inner.fd_min_avail = FD(0);
         inner.files.clear();
     }
 
@@ -124,7 +124,9 @@ impl FileArray {
         Ok(new_fd)
     }
 
-    pub fn dup_to(&self, old_fd: FD, new_fd: FD, flags: u64) -> KResult<FD> {
+    pub fn dup_to(&self, old_fd: FD, new_fd: FD, flags: OpenFlags) -> KResult<FD> {
+        let fdflags = flags.as_fd_flags();
+
         let mut inner = self.inner.lock();
         let old_file = inner.files.get(&old_fd).ok_or(EBADF)?;
 
@@ -136,7 +138,7 @@ impl FileArray {
                 let new_file = entry.into_mut();
                 let mut file_swap = new_file_data;
 
-                new_file.flags = flags;
+                new_file.flags = fdflags;
                 core::mem::swap(&mut file_swap, &mut new_file.file);
 
                 drop(inner);
@@ -145,69 +147,61 @@ impl FileArray {
         }
 
         assert_eq!(new_fd, inner.allocate_fd(new_fd));
-        inner.do_insert(new_fd, flags, new_file_data);
+        inner.do_insert(new_fd, fdflags, new_file_data);
 
         Ok(new_fd)
     }
 
     /// # Return
     /// `(read_fd, write_fd)`
-    pub fn pipe(&self, flags: u32) -> KResult<(FD, FD)> {
+    pub fn pipe(&self, flags: OpenFlags) -> KResult<(FD, FD)> {
         let mut inner = self.inner.lock();
 
         let read_fd = inner.next_fd();
         let write_fd = inner.next_fd();
 
-        let fdflag = if flags & O_CLOEXEC != 0 { FD_CLOEXEC } else { 0 };
+        let fdflag = flags.as_fd_flags();
 
         let pipe = Pipe::new();
         let (read_end, write_end) = pipe.split();
-        inner.do_insert(read_fd, fdflag as u64, read_end);
-        inner.do_insert(write_fd, fdflag as u64, write_end);
+        inner.do_insert(read_fd, fdflag, read_end);
+        inner.do_insert(write_fd, fdflag, write_end);
 
         Ok((read_fd, write_fd))
     }
 
-    pub fn open(&self, fs_context: &FsContext, path: Path, flags: u32, mode: Mode) -> KResult<FD> {
-        let dentry = Dentry::open(fs_context, path, true)?;
+    pub fn open(&self, dentry: &Arc<Dentry>, flags: OpenFlags, mode: Mode) -> KResult<FD> {
         dentry.open_check(flags, mode)?;
 
-        let fdflag = if flags & O_CLOEXEC != 0 { FD_CLOEXEC } else { 0 };
-        let can_read = flags & O_WRONLY == 0;
-        let can_write = flags & (O_WRONLY | O_RDWR) != 0;
-        let append = flags & O_APPEND != 0;
+        let fdflag = flags.as_fd_flags();
 
         let inode = dentry.get_inode()?;
         let filemode = inode.mode.load(Ordering::Relaxed);
 
-        if flags & O_DIRECTORY != 0 {
+        if flags.directory() {
             if !s_isdir(filemode) {
                 return Err(ENOTDIR);
             }
         } else {
-            if s_isdir(filemode) && can_write {
+            if s_isdir(filemode) && flags.write() {
                 return Err(EISDIR);
             }
         }
 
-        if flags & O_TRUNC != 0 {
-            if can_write && s_isreg(filemode) {
-                inode.truncate(0)?;
-            }
+        if flags.truncate() && flags.write() && s_isreg(filemode) {
+            inode.truncate(0)?;
         }
 
-        let file;
-
-        if s_ischr(filemode) {
+        let file = if s_ischr(filemode) {
             let device = CharDevice::get(inode.devid()?).ok_or(ENXIO)?;
-            file = device.open()?;
+            device.open()?
         } else {
-            file = InodeFile::new(dentry, (can_read, can_write, append));
-        }
+            InodeFile::new(dentry.clone(), flags.as_rwa())
+        };
 
         let mut inner = self.inner.lock();
         let fd = inner.next_fd();
-        inner.do_insert(fd, fdflag as u64, file);
+        inner.do_insert(fd, fdflag, file);
 
         Ok(fd)
     }
@@ -218,19 +212,21 @@ impl FileArray {
 
         match cmd {
             F_DUPFD | F_DUPFD_CLOEXEC => {
-                let cloexec = cmd == F_DUPFD_CLOEXEC || (ofile.flags & FD_CLOEXEC as u64 != 0);
-                let flags = if cloexec { O_CLOEXEC } else { 0 };
+                let cloexec = cmd == F_DUPFD_CLOEXEC || ofile.flags.close_on_exec();
+                let flags = cloexec
+                    .then_some(FDFlags::FD_CLOEXEC)
+                    .unwrap_or(FDFlags::empty());
 
                 let new_file_data = ofile.file.clone();
-                let new_fd = inner.allocate_fd(arg as FD);
+                let new_fd = inner.allocate_fd(FD(arg as u32));
 
-                inner.do_insert(new_fd, flags as u64, new_file_data);
+                inner.do_insert(new_fd, flags, new_file_data);
 
-                Ok(new_fd as usize)
+                Ok(new_fd.0 as usize)
             }
-            F_GETFD => Ok(ofile.flags as usize),
+            F_GETFD => Ok(ofile.flags.bits() as usize),
             F_SETFD => {
-                ofile.flags = arg as u64;
+                ofile.flags = FDFlags::from_bits_truncate(arg as u32);
                 Ok(0)
             }
             _ => unimplemented!("fcntl: cmd={}", cmd),
@@ -245,17 +241,17 @@ impl FileArray {
 
         inner.do_insert(
             stdin,
-            O_CLOEXEC as u64,
+            FDFlags::FD_CLOEXEC,
             TerminalFile::new(console_terminal.clone()),
         );
         inner.do_insert(
             stdout,
-            O_CLOEXEC as u64,
+            FDFlags::FD_CLOEXEC,
             TerminalFile::new(console_terminal.clone()),
         );
         inner.do_insert(
             stderr,
-            O_CLOEXEC as u64,
+            FDFlags::FD_CLOEXEC,
             TerminalFile::new(console_terminal.clone()),
         );
     }
@@ -271,7 +267,7 @@ impl FileArrayInner {
             .range(&from..)
             .fold_while(from, |current, (&key, _)| {
                 if current == key {
-                    Continue(current + 1)
+                    Continue(FD(current.0 + 1))
                 } else {
                     Done(current)
                 }
@@ -287,7 +283,7 @@ impl FileArrayInner {
         let from = FD::max(from, self.fd_min_avail);
 
         if from == self.fd_min_avail {
-            let next_min_avail = self.find_available(from + 1);
+            let next_min_avail = self.find_available(FD(from.0 + 1));
             let allocated = self.fd_min_avail;
             self.fd_min_avail = next_min_avail;
             allocated
@@ -307,7 +303,23 @@ impl FileArrayInner {
     }
 
     /// Insert a file description to the file array.
-    fn do_insert(&mut self, fd: FD, flags: u64, file: Arc<File>) {
+    fn do_insert(&mut self, fd: FD, flags: FDFlags, file: Arc<File>) {
         assert!(self.files.insert(fd, OpenFile { flags, file }).is_none());
     }
 }
+
+impl FD {
+    pub const AT_FDCWD: FD = FD(-100i32 as u32);
+}
+
+impl FromSyscallArg for FD {
+    fn from_arg(value: usize) -> Self {
+        Self(value as u32)
+    }
+}
+
+impl SyscallRetVal for FD {
+    fn into_retval(self) -> Option<usize> {
+        Some(self.0 as usize)
+    }
+}