implement mkdir and unlink

rm, mkdir and rmdir now work in tmpfs
This commit is contained in:
Ashwin Naren
2025-12-17 22:44:19 -08:00
committed by Matthew Leach
parent b8ad29b4b1
commit 781b9b5c3f
8 changed files with 165 additions and 7 deletions

View File

@@ -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 |

View File

@@ -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}"),
}
}

View File

@@ -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<C, G, T> TmpFsDirInode<C, G, T>

View File

@@ -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,

View File

@@ -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<dyn Inode>,
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<dyn Inode>, 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();

View File

@@ -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<c_char>,
mode: u16,
) -> libkernel::error::Result<usize> {
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)
}

View File

@@ -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)]

View File

@@ -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<c_char>, flags: u32) -> Result<usize> {
// 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)
}