diff --git a/etc/syscalls_linux_aarch64.md b/etc/syscalls_linux_aarch64.md index e6d76f0..ceae71e 100644 --- a/etc/syscalls_linux_aarch64.md +++ b/etc/syscalls_linux_aarch64.md @@ -35,8 +35,8 @@ | 0x1f (31) | ioprio_get | (int which, int who) | __arm64_sys_ioprio_get | false | | 0x20 (32) | flock | (unsigned int fd, unsigned int cmd) | __arm64_sys_flock | false | | 0x21 (33) | mknodat | (int dfd, const char *filename, umode_t mode, unsigned int dev) | __arm64_sys_mknodat | false | -| 0x22 (34) | mkdirat | (int dfd, const char *pathname, umode_t mode) | __arm64_sys_mkdirat | false | -| 0x23 (35) | unlinkat | (int dfd, const char *pathname, int flag) | __arm64_sys_unlinkat | false | +| 0x22 (34) | mkdirat | (int dfd, const char *pathname, umode_t mode) | __arm64_sys_mkdirat | true | +| 0x23 (35) | unlinkat | (int dfd, const char *pathname, int flag) | __arm64_sys_unlinkat | true | | 0x24 (36) | symlinkat | (const char *oldname, int newdfd, const char *newname) | __arm64_sys_symlinkat | false | | 0x25 (37) | linkat | (int olddfd, const char *oldname, int newdfd, const char *newname, int flags) | __arm64_sys_linkat | false | | 0x26 (38) | renameat | (int olddfd, const char *oldname, int newdfd, const char *newname) | __arm64_sys_renameat | false | diff --git a/libkernel/src/error/syscall_error.rs b/libkernel/src/error/syscall_error.rs index f21d8f6..4822e45 100644 --- a/libkernel/src/error/syscall_error.rs +++ b/libkernel/src/error/syscall_error.rs @@ -50,6 +50,6 @@ pub fn kern_err_to_syscall(err: KernelError) -> isize { KernelError::SeekPipe => ESPIPE, KernelError::NotSupported => ENOSYS, KernelError::NoMemory => ENOMEM, - _ => todo!(), + e => todo!("{e}"), } } diff --git a/libkernel/src/fs/filesystems/tmpfs.rs b/libkernel/src/fs/filesystems/tmpfs.rs index 667e795..91ef402 100644 --- a/libkernel/src/fs/filesystems/tmpfs.rs +++ b/libkernel/src/fs/filesystems/tmpfs.rs @@ -429,6 +429,18 @@ where Ok(inode) } + + async fn unlink(&self, name: &str) -> Result<()> { + let mut entries = self.entries.lock_save_irq(); + let index = entries.iter().position(|e| e.name == name); + + if let Some(idx) = index { + entries.remove(idx); + Ok(()) + } else { + Err(FsError::NotFound.into()) + } + } } impl TmpFsDirInode diff --git a/src/arch/arm64/exceptions/syscall.rs b/src/arch/arm64/exceptions/syscall.rs index f2f2272..5c8a428 100644 --- a/src/arch/arm64/exceptions/syscall.rs +++ b/src/arch/arm64/exceptions/syscall.rs @@ -11,9 +11,11 @@ use crate::{ syscalls::{ at::{ access::{sys_faccessat, sys_faccessat2}, + mkdir::sys_mkdirat, open::sys_openat, readlink::sys_readlinkat, stat::sys_newfstatat, + unlink::sys_unlinkat, }, chdir::{sys_chdir, sys_getcwd}, close::sys_close, @@ -92,6 +94,8 @@ pub async fn handle_syscall() { 0x18 => sys_dup3(arg1.into(), arg2.into(), arg3 as _), 0x19 => sys_fcntl(arg1.into(), arg2 as _, arg3 as _).await, 0x1d => sys_ioctl(arg1.into(), arg2 as _, arg3 as _).await, + 0x22 => sys_mkdirat(arg1.into(), TUA::from_value(arg2 as _), arg3 as _).await, + 0x23 => sys_unlinkat(arg1.into(), TUA::from_value(arg2 as _), arg3 as _).await, 0x2e => sys_ftruncate(arg1.into(), arg2 as _).await, 0x30 => sys_faccessat(arg1.into(), TUA::from_value(arg2 as _), arg3 as _).await, 0x31 => sys_chdir(TUA::from_value(arg1 as _)).await, diff --git a/src/fs/mod.rs b/src/fs/mod.rs index d949fb0..2af62b0 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -238,11 +238,15 @@ impl VFS { Err(KernelError::Fs(FsError::NotFound)) => { // If O_CREAT is specified, we should create it. if flags.contains(OpenFlags::O_CREAT) { - // Resolve its parent directory. - let parent_path = path.parent().ok_or(FsError::InvalidInput)?; + // Determine the target name and parent directory. If the path has no + // explicit parent component (e.g., "foo"), use the provided `root` + // (cwd or dirfd) as the parent directory. let file_name = path.file_name().ok_or(FsError::InvalidInput)?; - - let parent_inode = self.resolve_path(parent_path, root).await?; + let parent_inode = if let Some(parent_path) = path.parent() { + self.resolve_path(parent_path, root.clone()).await? + } else { + root.clone() + }; // Ensure the parent is actually a directory before creating a // file in it. @@ -314,6 +318,87 @@ impl VFS { FileType::Socket => todo!(), } } + + pub async fn mkdir( + &self, + path: &Path, + root: Arc, + mode: FilePermissions, + ) -> Result<()> { + // Try to resolve the target directory first. + match self.resolve_path(path, root.clone()).await { + // The path already exists, this is an error. + Ok(_) => Err(FsError::AlreadyExists.into()), + + // The path does not exist, we need to create it. + Err(KernelError::Fs(FsError::NotFound)) => { + // Determine the new directory name. + let dir_name = path.file_name().ok_or(FsError::InvalidInput)?; + + // Resolve the parent directory. If the path has no parent + // component (e.g., \"foo\"), treat the provided `root` + // directory (AT_FDCWD / cwd / dirfd) as the parent. + let parent_inode = if let Some(parent_path) = path.parent() { + self.resolve_path(parent_path, root.clone()).await? + } else { + root.clone() + }; + + // Verify that the parent is actually a directory. + if parent_inode.getattr().await?.file_type != FileType::Directory { + return Err(FsError::NotADirectory.into()); + } + + // Delegate the creation to the filesystem-specific inode. + parent_inode + .create(dir_name, FileType::Directory, mode) + .await?; + + Ok(()) + } + + // Propagate any other errors up the stack. + Err(e) => Err(e), + } + } + + pub async fn unlink(&self, path: &Path, root: Arc, remove_dir: bool) -> Result<()> { + // First, resolve the target inode so we can inspect its type. + let target_inode = self.resolve_path(path, root.clone()).await?; + + let attr = target_inode.getattr().await?; + + // Validate flag and file-type combinations. + match attr.file_type { + FileType::Directory if !remove_dir => { + return Err(FsError::IsADirectory.into()); + } + FileType::Directory => { /* OK: rmdir semantics */ } + _ if remove_dir => { + return Err(FsError::NotADirectory.into()); + } + _ => { /* Regular unlink */ } + } + + // Determine the parent directory inode in which to perform the unlink. + let parent_inode = if let Some(parent_path) = path.parent() { + self.resolve_path(parent_path, root.clone()).await? + } else { + root.clone() + }; + + // Ensure the parent really is a directory. + if parent_inode.getattr().await?.file_type != FileType::Directory { + return Err(FsError::NotADirectory.into()); + } + + // Extract the final component (name) and perform the unlink on the parent. + let name = path.file_name().ok_or(FsError::InvalidInput)?; + + parent_inode.unlink(name).await?; + + Ok(()) + } } pub static VFS: VFS = VFS::new(); diff --git a/src/fs/syscalls/at/mkdir.rs b/src/fs/syscalls/at/mkdir.rs new file mode 100644 index 0000000..eeaebde --- /dev/null +++ b/src/fs/syscalls/at/mkdir.rs @@ -0,0 +1,23 @@ +use crate::fs::VFS; +use crate::fs::syscalls::at::resolve_at_start_node; +use crate::memory::uaccess::cstr::UserCStr; +use crate::process::fd_table::Fd; +use core::ffi::c_char; +use libkernel::fs::attr::FilePermissions; +use libkernel::fs::path::Path; +use libkernel::memory::address::TUA; + +pub async fn sys_mkdirat( + dirfd: Fd, + path: TUA, + mode: u16, +) -> libkernel::error::Result { + let mut buf = [0; 1024]; + + let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?); + let start_node = resolve_at_start_node(dirfd, path).await?; + let mode = FilePermissions::from_bits_retain(mode); + + VFS.mkdir(path, start_node, mode).await?; + Ok(0) +} diff --git a/src/fs/syscalls/at/mod.rs b/src/fs/syscalls/at/mod.rs index 99814dc..0d20a6e 100644 --- a/src/fs/syscalls/at/mod.rs +++ b/src/fs/syscalls/at/mod.rs @@ -6,9 +6,11 @@ use libkernel::{ }; pub mod access; +pub mod mkdir; pub mod open; pub mod readlink; pub mod stat; +pub mod unlink; bitflags::bitflags! { #[derive(Clone, Copy, Debug, PartialEq, Eq)] diff --git a/src/fs/syscalls/at/unlink.rs b/src/fs/syscalls/at/unlink.rs new file mode 100644 index 0000000..1475767 --- /dev/null +++ b/src/fs/syscalls/at/unlink.rs @@ -0,0 +1,32 @@ +use core::ffi::c_char; + +use libkernel::{error::Result, fs::path::Path, memory::address::TUA}; + +use crate::{ + fs::{VFS, syscalls::at::resolve_at_start_node}, + memory::uaccess::cstr::UserCStr, + process::fd_table::Fd, +}; + +// As defined in linux/fcntl.h ─ enables directory removal via unlinkat. +const AT_REMOVEDIR: u32 = 0x200; + +/// unlinkat(2) implementation. +/// +/// The semantics are: +/// - If `flags & AT_REMOVEDIR` is set, behave like `rmdir`. +/// - Otherwise behave like `unlink`. +pub async fn sys_unlinkat(dirfd: Fd, path: TUA, flags: u32) -> Result { + // Copy the user-provided path into kernel memory. + let mut buf = [0u8; 1024]; + let path = Path::new(UserCStr::from_ptr(path).copy_from_user(&mut buf).await?); + + // Determine the starting inode for path resolution. + let start_node = resolve_at_start_node(dirfd, path).await?; + + let remove_dir = flags & AT_REMOVEDIR != 0; + + VFS.unlink(path, start_node, remove_dir).await?; + + Ok(0) +}