From 15d2e07a8c473a9657af7e0059a3d71a394577aa Mon Sep 17 00:00:00 2001 From: LI Qing Date: Wed, 15 Feb 2023 16:46:42 +0800 Subject: [PATCH 1/3] Add support for close syscall --- .../libs/jinux-std/src/fs/file_handle/file.rs | 4 ++++ .../libs/jinux-std/src/fs/file_handle/mod.rs | 17 +++++++++++++++++ .../libs/jinux-std/src/fs/file_table.rs | 4 ++-- .../libs/jinux-std/src/syscall/close.rs | 3 ++- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/services/libs/jinux-std/src/fs/file_handle/file.rs b/src/services/libs/jinux-std/src/fs/file_handle/file.rs index 85a6ac82e..5e59233fb 100644 --- a/src/services/libs/jinux-std/src/fs/file_handle/file.rs +++ b/src/services/libs/jinux-std/src/fs/file_handle/file.rs @@ -28,4 +28,8 @@ pub trait File: Send + Sync + Any { fn poll(&self) -> IoEvents { IoEvents::empty() } + + fn flush(&self) -> Result<()> { + Ok(()) + } } diff --git a/src/services/libs/jinux-std/src/fs/file_handle/mod.rs b/src/services/libs/jinux-std/src/fs/file_handle/mod.rs index d6d2f0922..08e386882 100644 --- a/src/services/libs/jinux-std/src/fs/file_handle/mod.rs +++ b/src/services/libs/jinux-std/src/fs/file_handle/mod.rs @@ -6,6 +6,7 @@ mod inode_handle; use crate::prelude::*; use crate::rights::{ReadOp, WriteOp}; use alloc::sync::Arc; +use core::ops::Range; pub use self::file::File; pub use self::inode_handle::InodeHandle; @@ -65,4 +66,20 @@ impl FileHandle { } } } + + pub fn clean_for_close(&self) -> Result<()> { + match &self.inner { + Inner::Inode(inode_handle) => { + let dentry = inode_handle.dentry(); + let ref_count = Arc::strong_count(dentry); + // The dentry is held by dentry cache and self + if ref_count == 2 { + let page_cache_size = dentry.vnode().pages().size(); + dentry.vnode().pages().decommit(0..page_cache_size)?; + } + } + Inner::File(file) => file.flush()?, + } + Ok(()) + } } diff --git a/src/services/libs/jinux-std/src/fs/file_table.rs b/src/services/libs/jinux-std/src/fs/file_table.rs index 6b3dfd7c8..9b2a0c85f 100644 --- a/src/services/libs/jinux-std/src/fs/file_table.rs +++ b/src/services/libs/jinux-std/src/fs/file_table.rs @@ -58,8 +58,8 @@ impl FileTable { fd } - pub fn close_file(&mut self, fd: FileDescripter) { - self.table.remove(&fd); + pub fn close_file(&mut self, fd: FileDescripter) -> Option { + self.table.remove(&fd) } pub fn get_file(&self, fd: FileDescripter) -> Result<&FileHandle> { diff --git a/src/services/libs/jinux-std/src/syscall/close.rs b/src/services/libs/jinux-std/src/syscall/close.rs index 3e3ca7562..3061664ad 100644 --- a/src/services/libs/jinux-std/src/syscall/close.rs +++ b/src/services/libs/jinux-std/src/syscall/close.rs @@ -9,6 +9,7 @@ pub fn sys_close(fd: FileDescripter) -> Result { let current = current!(); let mut file_table = current.file_table().lock(); let _ = file_table.get_file(fd)?; - file_table.close_file(fd); + let file = file_table.close_file(fd).unwrap(); + file.clean_for_close()?; Ok(SyscallReturn::Return(0)) } From f3fa998e7bc53abc180f0e77ac286dab2fd24e30 Mon Sep 17 00:00:00 2001 From: LI Qing Date: Thu, 16 Feb 2023 10:41:29 +0800 Subject: [PATCH 2/3] Add support for open syscall --- src/services/libs/jinux-std/src/syscall/mod.rs | 6 ++++-- .../libs/jinux-std/src/syscall/{openat.rs => open.rs} | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) rename src/services/libs/jinux-std/src/syscall/{openat.rs => open.rs} (94%) diff --git a/src/services/libs/jinux-std/src/syscall/mod.rs b/src/services/libs/jinux-std/src/syscall/mod.rs index 8007e4a04..b7c3b9ed8 100644 --- a/src/services/libs/jinux-std/src/syscall/mod.rs +++ b/src/services/libs/jinux-std/src/syscall/mod.rs @@ -31,7 +31,7 @@ use crate::syscall::madvise::sys_madvise; use crate::syscall::mmap::sys_mmap; use crate::syscall::mprotect::sys_mprotect; use crate::syscall::munmap::sys_munmap; -use crate::syscall::openat::sys_openat; +use crate::syscall::open::{sys_open, sys_openat}; use crate::syscall::poll::sys_poll; use crate::syscall::prctl::sys_prctl; use crate::syscall::prlimit64::sys_prlimit64; @@ -84,7 +84,7 @@ mod madvise; mod mmap; mod mprotect; mod munmap; -mod openat; +mod open; mod poll; mod prctl; mod prlimit64; @@ -138,6 +138,7 @@ macro_rules! syscall_handler { define_syscall_nums!( SYS_READ = 0, SYS_WRITE = 1, + SYS_OPEN = 2, SYS_CLOSE = 3, SYS_STAT = 4, SYS_FSTAT = 5, @@ -246,6 +247,7 @@ pub fn syscall_dispatch( match syscall_number { SYS_READ => syscall_handler!(3, sys_read, args), SYS_WRITE => syscall_handler!(3, sys_write, args), + SYS_OPEN => syscall_handler!(3, sys_open, args), SYS_CLOSE => syscall_handler!(1, sys_close, args), SYS_STAT => syscall_handler!(2, sys_stat, args), SYS_FSTAT => syscall_handler!(2, sys_fstat, args), diff --git a/src/services/libs/jinux-std/src/syscall/openat.rs b/src/services/libs/jinux-std/src/syscall/open.rs similarity index 94% rename from src/services/libs/jinux-std/src/syscall/openat.rs rename to src/services/libs/jinux-std/src/syscall/open.rs index 0f63238f0..a67f0c08c 100644 --- a/src/services/libs/jinux-std/src/syscall/openat.rs +++ b/src/services/libs/jinux-std/src/syscall/open.rs @@ -67,6 +67,10 @@ pub fn sys_openat( Ok(SyscallReturn::Return(fd as _)) } +pub fn sys_open(pathname_addr: Vaddr, flags: u32, mode: u16) -> Result { + self::sys_openat(AT_FDCWD, pathname_addr, flags, mode) +} + /// File for output busybox ash log. struct BusyBoxTraceFile; From 5965d72998db0e4b601ac19b737ff5884a21b9fd Mon Sep 17 00:00:00 2001 From: LI Qing Date: Wed, 15 Feb 2023 19:31:13 +0800 Subject: [PATCH 3/3] Add link, unlink, mkdir, rmdir syscalls --- .../libs/jinux-std/src/fs/ramfs/fs.rs | 25 ++++++- .../jinux-std/src/fs/utils/dentry_cache.rs | 40 ++++++++++- .../libs/jinux-std/src/fs/utils/inode.rs | 2 + .../libs/jinux-std/src/syscall/link.rs | 71 +++++++++++++++++++ .../libs/jinux-std/src/syscall/mkdir.rs | 42 +++++++++++ .../libs/jinux-std/src/syscall/mod.rs | 22 ++++++ .../libs/jinux-std/src/syscall/rmdir.rs | 23 ++++++ .../libs/jinux-std/src/syscall/unlink.rs | 52 ++++++++++++++ 8 files changed, 273 insertions(+), 4 deletions(-) create mode 100644 src/services/libs/jinux-std/src/syscall/link.rs create mode 100644 src/services/libs/jinux-std/src/syscall/mkdir.rs create mode 100644 src/services/libs/jinux-std/src/syscall/rmdir.rs create mode 100644 src/services/libs/jinux-std/src/syscall/unlink.rs diff --git a/src/services/libs/jinux-std/src/fs/ramfs/fs.rs b/src/services/libs/jinux-std/src/fs/ramfs/fs.rs index 52918948e..3471840d3 100644 --- a/src/services/libs/jinux-std/src/fs/ramfs/fs.rs +++ b/src/services/libs/jinux-std/src/fs/ramfs/fs.rs @@ -404,9 +404,28 @@ impl Inode for RamInode { let self_dir = self_inode.inner.as_direntry_mut().unwrap(); let (idx, other) = self_dir.get_entry(name).ok_or(Error::new(Errno::ENOENT))?; let other_inode = other.0.read(); - if other_inode.metadata.type_ == InodeType::Dir - && !other_inode.inner.as_direntry().unwrap().is_empty_children() - { + if other_inode.metadata.type_ == InodeType::Dir { + return_errno_with_message!(Errno::EISDIR, "unlink on dir"); + } + self_dir.remove_entry(idx); + Ok(()) + } + + fn rmdir(&self, name: &str) -> Result<()> { + if self.0.read().metadata.type_ != InodeType::Dir { + return_errno_with_message!(Errno::ENOTDIR, "self is not dir"); + } + if name == "." || name == ".." { + return_errno_with_message!(Errno::EISDIR, "rmdir on . or .."); + } + let mut self_inode = self.0.write(); + let self_dir = self_inode.inner.as_direntry_mut().unwrap(); + let (idx, other) = self_dir.get_entry(name).ok_or(Error::new(Errno::ENOENT))?; + let other_inode = other.0.read(); + if other_inode.metadata.type_ != InodeType::Dir { + return_errno_with_message!(Errno::ENOTDIR, "rmdir on not dir"); + } + if other_inode.inner.as_direntry().unwrap().is_empty_children() { return_errno_with_message!(Errno::ENOTEMPTY, "dir not empty"); } self_dir.remove_entry(idx); diff --git a/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs b/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs index b0839b2bb..a341b3543 100644 --- a/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs +++ b/src/services/libs/jinux-std/src/fs/utils/dentry_cache.rs @@ -65,6 +65,9 @@ impl Dentry { pub fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result> { let mut inner = self.inner.write(); + if inner.children.get(name).is_some() { + return_errno!(Errno::EEXIST); + } let child = { let vnode = Vnode::new(self.vnode.inode().mknod(name, type_, mode)?)?; Dentry::new(name, vnode, Some(inner.this.clone())) @@ -73,7 +76,7 @@ impl Dentry { Ok(child) } - pub fn lookup(&self, name: &str) -> Result> { + pub fn lookup(&self, name: &str) -> Result> { if self.vnode.inode().metadata().type_ != InodeType::Dir { return_errno!(Errno::ENOTDIR); } @@ -99,6 +102,41 @@ impl Dentry { Ok(dentry) } + pub fn link(&self, old: &Arc, name: &str) -> Result<()> { + if self.vnode.inode().metadata().type_ != InodeType::Dir { + return_errno!(Errno::ENOTDIR); + } + let mut inner = self.inner.write(); + if inner.children.get(name).is_some() { + return_errno!(Errno::EEXIST); + } + let target_vnode = old.vnode(); + self.vnode.inode().link(target_vnode.inode(), name)?; + let new_dentry = Self::new(name, target_vnode.clone(), Some(inner.this.clone())); + inner.children.insert(String::from(name), new_dentry); + Ok(()) + } + + pub fn unlink(&self, name: &str) -> Result<()> { + if self.vnode.inode().metadata().type_ != InodeType::Dir { + return_errno!(Errno::ENOTDIR); + } + let mut inner = self.inner.write(); + self.vnode.inode().unlink(name)?; + inner.children.remove(name); + Ok(()) + } + + pub fn rmdir(&self, name: &str) -> Result<()> { + if self.vnode.inode().metadata().type_ != InodeType::Dir { + return_errno!(Errno::ENOTDIR); + } + let mut inner = self.inner.write(); + self.vnode.inode().rmdir(name)?; + inner.children.remove(name); + Ok(()) + } + pub fn abs_path(&self) -> String { let mut path = self.name(); let mut dentry = self.this(); diff --git a/src/services/libs/jinux-std/src/fs/utils/inode.rs b/src/services/libs/jinux-std/src/fs/utils/inode.rs index faf5ee807..d1f355c78 100644 --- a/src/services/libs/jinux-std/src/fs/utils/inode.rs +++ b/src/services/libs/jinux-std/src/fs/utils/inode.rs @@ -176,6 +176,8 @@ pub trait Inode: Any + Sync + Send { fn unlink(&self, name: &str) -> Result<()>; + fn rmdir(&self, name: &str) -> Result<()>; + fn lookup(&self, name: &str) -> Result>; fn rename(&self, old_name: &str, target: &Arc, new_name: &str) -> Result<()>; diff --git a/src/services/libs/jinux-std/src/syscall/link.rs b/src/services/libs/jinux-std/src/syscall/link.rs new file mode 100644 index 000000000..9fa82b153 --- /dev/null +++ b/src/services/libs/jinux-std/src/syscall/link.rs @@ -0,0 +1,71 @@ +use crate::fs::{ + file_table::FileDescripter, + fs_resolver::{FsPath, AT_FDCWD}, +}; +use crate::log_syscall_entry; +use crate::prelude::*; +use crate::syscall::constants::MAX_FILENAME_LEN; +use crate::util::read_cstring_from_user; + +use super::SyscallReturn; +use super::SYS_LINKAT; + +pub fn sys_linkat( + old_dirfd: FileDescripter, + old_pathname_addr: Vaddr, + new_dirfd: FileDescripter, + new_pathname_addr: Vaddr, + flags: u32, +) -> Result { + log_syscall_entry!(SYS_LINKAT); + let old_pathname = read_cstring_from_user(old_pathname_addr, MAX_FILENAME_LEN)?; + let new_pathname = read_cstring_from_user(new_pathname_addr, MAX_FILENAME_LEN)?; + let flags = + LinkFlags::from_bits(flags).ok_or(Error::with_message(Errno::EINVAL, "invalid flags"))?; + debug!( + "old_dirfd = {}, old_pathname = {:?}, new_dirfd = {}, new_pathname = {:?}, flags = {:?}", + old_dirfd, old_pathname, new_dirfd, new_pathname, flags + ); + + let current = current!(); + let (old_dentry, new_dir_dentry, new_name) = { + let old_pathname = old_pathname.to_string_lossy(); + if old_pathname.ends_with("/") { + return_errno_with_message!(Errno::EPERM, "oldpath is dir"); + } + if old_pathname.is_empty() && !flags.contains(LinkFlags::AT_EMPTY_PATH) { + return_errno_with_message!(Errno::ENOENT, "oldpath is empty"); + } + let new_pathname = new_pathname.to_string_lossy(); + if new_pathname.ends_with("/") { + return_errno_with_message!(Errno::EPERM, "newpath is dir"); + } + if new_pathname.is_empty() { + return_errno_with_message!(Errno::ENOENT, "newpath is empty"); + } + + let old_fs_path = FsPath::new(old_dirfd, old_pathname.as_ref())?; + let new_fs_path = FsPath::new(new_dirfd, new_pathname.as_ref())?; + let fs = current.fs().read(); + let old_dentry = if flags.contains(LinkFlags::AT_SYMLINK_FOLLOW) { + fs.lookup(&old_fs_path)? + } else { + fs.lookup_no_follow(&old_fs_path)? + }; + let (new_dir_dentry, new_name) = fs.lookup_dir_and_base_name(&new_fs_path)?; + (old_dentry, new_dir_dentry, new_name) + }; + new_dir_dentry.link(&old_dentry, &new_name)?; + Ok(SyscallReturn::Return(0)) +} + +pub fn sys_link(old_pathname_addr: Vaddr, new_pathname_addr: Vaddr) -> Result { + self::sys_linkat(AT_FDCWD, old_pathname_addr, AT_FDCWD, new_pathname_addr, 0) +} + +bitflags::bitflags! { + pub struct LinkFlags: u32 { + const AT_EMPTY_PATH = 0x1000; + const AT_SYMLINK_FOLLOW = 0x400; + } +} diff --git a/src/services/libs/jinux-std/src/syscall/mkdir.rs b/src/services/libs/jinux-std/src/syscall/mkdir.rs new file mode 100644 index 000000000..ddf46a347 --- /dev/null +++ b/src/services/libs/jinux-std/src/syscall/mkdir.rs @@ -0,0 +1,42 @@ +use crate::fs::{ + file_table::FileDescripter, + fs_resolver::{FsPath, AT_FDCWD}, + utils::{InodeMode, InodeType}, +}; +use crate::log_syscall_entry; +use crate::prelude::*; +use crate::syscall::constants::MAX_FILENAME_LEN; +use crate::util::read_cstring_from_user; + +use super::SyscallReturn; +use super::SYS_MKDIRAT; + +pub fn sys_mkdirat( + dirfd: FileDescripter, + pathname_addr: Vaddr, + mode: u16, +) -> Result { + log_syscall_entry!(SYS_MKDIRAT); + let pathname = read_cstring_from_user(pathname_addr, MAX_FILENAME_LEN)?; + debug!( + "dirfd = {}, pathname = {:?}, mode = {}", + dirfd, pathname, mode + ); + + let current = current!(); + let (dir_dentry, name) = { + let pathname = pathname.to_string_lossy(); + if pathname.is_empty() { + return_errno_with_message!(Errno::ENOENT, "path is empty"); + } + let fs_path = FsPath::new(dirfd, pathname.as_ref())?; + current.fs().read().lookup_dir_and_base_name(&fs_path)? + }; + let inode_mode = InodeMode::from_bits_truncate(mode); + let _ = dir_dentry.create(&name.trim_end_matches('/'), InodeType::Dir, inode_mode)?; + Ok(SyscallReturn::Return(0)) +} + +pub fn sys_mkdir(pathname_addr: Vaddr, mode: u16) -> Result { + self::sys_mkdirat(AT_FDCWD, pathname_addr, mode) +} diff --git a/src/services/libs/jinux-std/src/syscall/mod.rs b/src/services/libs/jinux-std/src/syscall/mod.rs index b7c3b9ed8..e5f1429c2 100644 --- a/src/services/libs/jinux-std/src/syscall/mod.rs +++ b/src/services/libs/jinux-std/src/syscall/mod.rs @@ -25,9 +25,11 @@ use crate::syscall::gettid::sys_gettid; use crate::syscall::getuid::sys_getuid; use crate::syscall::ioctl::sys_ioctl; use crate::syscall::kill::sys_kill; +use crate::syscall::link::{sys_link, sys_linkat}; use crate::syscall::lseek::sys_lseek; use crate::syscall::lstat::sys_lstat; use crate::syscall::madvise::sys_madvise; +use crate::syscall::mkdir::{sys_mkdir, sys_mkdirat}; use crate::syscall::mmap::sys_mmap; use crate::syscall::mprotect::sys_mprotect; use crate::syscall::munmap::sys_munmap; @@ -37,6 +39,7 @@ use crate::syscall::prctl::sys_prctl; use crate::syscall::prlimit64::sys_prlimit64; use crate::syscall::read::sys_read; use crate::syscall::readlink::sys_readlink; +use crate::syscall::rmdir::sys_rmdir; use crate::syscall::rt_sigaction::sys_rt_sigaction; use crate::syscall::rt_sigprocmask::sys_rt_sigprocmask; use crate::syscall::rt_sigreturn::sys_rt_sigreturn; @@ -47,6 +50,7 @@ use crate::syscall::setpgid::sys_setpgid; use crate::syscall::stat::sys_stat; use crate::syscall::tgkill::sys_tgkill; use crate::syscall::uname::sys_uname; +use crate::syscall::unlink::{sys_unlink, sys_unlinkat}; use crate::syscall::wait4::sys_wait4; use crate::syscall::waitid::sys_waitid; use crate::syscall::write::sys_write; @@ -78,9 +82,11 @@ mod gettid; mod getuid; mod ioctl; mod kill; +mod link; mod lseek; mod lstat; mod madvise; +mod mkdir; mod mmap; mod mprotect; mod munmap; @@ -90,6 +96,7 @@ mod prctl; mod prlimit64; mod read; mod readlink; +mod rmdir; mod rt_sigaction; mod rt_sigprocmask; mod rt_sigreturn; @@ -100,6 +107,7 @@ mod setpgid; mod stat; mod tgkill; mod uname; +mod unlink; mod wait4; mod waitid; mod write; @@ -167,6 +175,10 @@ define_syscall_nums!( SYS_UNAME = 63, SYS_FCNTL = 72, SYS_GETCWD = 79, + SYS_MKDIR = 83, + SYS_RMDIR = 84, + SYS_LINK = 86, + SYS_UNLINK = 87, SYS_READLINK = 89, SYS_GETUID = 102, SYS_GETGID = 104, @@ -185,6 +197,9 @@ define_syscall_nums!( SYS_TGKILL = 234, SYS_WAITID = 247, SYS_OPENAT = 257, + SYS_MKDIRAT = 258, + SYS_UNLINKAT = 263, + SYS_LINKAT = 265, SYS_SET_ROBUST_LIST = 273, SYS_PRLIMIT64 = 302 ); @@ -276,6 +291,10 @@ pub fn syscall_dispatch( SYS_UNAME => syscall_handler!(1, sys_uname, args), SYS_FCNTL => syscall_handler!(3, sys_fcntl, args), SYS_GETCWD => syscall_handler!(2, sys_getcwd, args), + SYS_MKDIR => syscall_handler!(2, sys_mkdir, args), + SYS_RMDIR => syscall_handler!(1, sys_rmdir, args), + SYS_LINK => syscall_handler!(2, sys_link, args), + SYS_UNLINK => syscall_handler!(1, sys_unlink, args), SYS_READLINK => syscall_handler!(3, sys_readlink, args), SYS_GETUID => syscall_handler!(0, sys_getuid), SYS_GETGID => syscall_handler!(0, sys_getgid), @@ -294,6 +313,9 @@ pub fn syscall_dispatch( SYS_TGKILL => syscall_handler!(3, sys_tgkill, args), SYS_WAITID => syscall_handler!(5, sys_waitid, args), SYS_OPENAT => syscall_handler!(4, sys_openat, args), + SYS_MKDIRAT => syscall_handler!(3, sys_mkdirat, args), + SYS_UNLINKAT => syscall_handler!(3, sys_unlinkat, args), + SYS_LINKAT => syscall_handler!(5, sys_linkat, args), SYS_SET_ROBUST_LIST => syscall_handler!(2, sys_set_robust_list, args), SYS_PRLIMIT64 => syscall_handler!(4, sys_prlimit64, args), _ => { diff --git a/src/services/libs/jinux-std/src/syscall/rmdir.rs b/src/services/libs/jinux-std/src/syscall/rmdir.rs new file mode 100644 index 000000000..c642f59c2 --- /dev/null +++ b/src/services/libs/jinux-std/src/syscall/rmdir.rs @@ -0,0 +1,23 @@ +use crate::fs::fs_resolver::FsPath; +use crate::log_syscall_entry; +use crate::prelude::*; +use crate::syscall::constants::MAX_FILENAME_LEN; +use crate::util::read_cstring_from_user; + +use super::SyscallReturn; +use super::SYS_RMDIR; + +pub fn sys_rmdir(pathname_addr: Vaddr) -> Result { + log_syscall_entry!(SYS_RMDIR); + let pathname = read_cstring_from_user(pathname_addr, MAX_FILENAME_LEN)?; + debug!("pathname = {:?}", pathname); + + let current = current!(); + let (dir_dentry, name) = { + let pathname = pathname.to_string_lossy(); + let fs_path = FsPath::try_from(pathname.as_ref())?; + current.fs().read().lookup_dir_and_base_name(&fs_path)? + }; + dir_dentry.rmdir(&name.trim_end_matches('/'))?; + Ok(SyscallReturn::Return(0)) +} diff --git a/src/services/libs/jinux-std/src/syscall/unlink.rs b/src/services/libs/jinux-std/src/syscall/unlink.rs new file mode 100644 index 000000000..5f5cbfa8e --- /dev/null +++ b/src/services/libs/jinux-std/src/syscall/unlink.rs @@ -0,0 +1,52 @@ +use crate::fs::{ + file_table::FileDescripter, + fs_resolver::{FsPath, AT_FDCWD}, +}; +use crate::log_syscall_entry; +use crate::prelude::*; +use crate::syscall::constants::MAX_FILENAME_LEN; +use crate::util::read_cstring_from_user; + +use super::SyscallReturn; +use super::SYS_UNLINKAT; + +pub fn sys_unlinkat( + dirfd: FileDescripter, + pathname_addr: Vaddr, + flags: u32, +) -> Result { + let flags = + UnlinkFlags::from_bits(flags).ok_or(Error::with_message(Errno::EINVAL, "invalid flags"))?; + if flags.contains(UnlinkFlags::AT_REMOVEDIR) { + return super::rmdir::sys_rmdir(pathname_addr); + } + + log_syscall_entry!(SYS_UNLINKAT); + let pathname = read_cstring_from_user(pathname_addr, MAX_FILENAME_LEN)?; + debug!("dirfd = {}, pathname = {:?}", dirfd, pathname); + + let current = current!(); + let (dir_dentry, name) = { + let pathname = pathname.to_string_lossy(); + if pathname.is_empty() { + return_errno_with_message!(Errno::ENOENT, "path is empty"); + } + if pathname.ends_with("/") { + return_errno_with_message!(Errno::EISDIR, "unlink on directory"); + } + let fs_path = FsPath::new(dirfd, pathname.as_ref())?; + current.fs().read().lookup_dir_and_base_name(&fs_path)? + }; + dir_dentry.unlink(&name)?; + Ok(SyscallReturn::Return(0)) +} + +pub fn sys_unlink(pathname_addr: Vaddr) -> Result { + self::sys_unlinkat(AT_FDCWD, pathname_addr, 0) +} + +bitflags::bitflags! { + struct UnlinkFlags: u32 { + const AT_REMOVEDIR = 0x200; + } +}