mirror of
https://github.com/hexagonal-sun/moss-kernel.git
synced 2025-12-23 14:37:57 -05:00
implement mkdir and unlink
rm, mkdir and rmdir now work in tmpfs
This commit is contained in:
committed by
Matthew Leach
parent
b8ad29b4b1
commit
781b9b5c3f
@@ -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 |
|
||||
|
||||
@@ -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}"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
23
src/fs/syscalls/at/mkdir.rs
Normal file
23
src/fs/syscalls/at/mkdir.rs
Normal 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)
|
||||
}
|
||||
@@ -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)]
|
||||
|
||||
32
src/fs/syscalls/at/unlink.rs
Normal file
32
src/fs/syscalls/at/unlink.rs
Normal 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)
|
||||
}
|
||||
Reference in New Issue
Block a user