Merge pull request #75 from liqinggd/dev-fs-syscall

Add some basic fs syscalls
This commit is contained in:
Tate, Hongliang Tian 2023-02-20 22:13:40 +08:00 committed by GitHub
commit c7ade129e9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 306 additions and 9 deletions

View File

@ -28,4 +28,8 @@ pub trait File: Send + Sync + Any {
fn poll(&self) -> IoEvents {
IoEvents::empty()
}
fn flush(&self) -> Result<()> {
Ok(())
}
}

View File

@ -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(())
}
}

View File

@ -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<FileHandle> {
self.table.remove(&fd)
}
pub fn get_file(&self, fd: FileDescripter) -> Result<&FileHandle> {

View File

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

View File

@ -65,6 +65,9 @@ impl Dentry {
pub fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<Self>> {
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<Arc<Dentry>> {
pub fn lookup(&self, name: &str) -> Result<Arc<Self>> {
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<Self>, 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();

View File

@ -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<Arc<dyn Inode>>;
fn rename(&self, old_name: &str, target: &Arc<dyn Inode>, new_name: &str) -> Result<()>;

View File

@ -9,6 +9,7 @@ pub fn sys_close(fd: FileDescripter) -> Result<SyscallReturn> {
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))
}

View File

@ -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<SyscallReturn> {
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<SyscallReturn> {
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;
}
}

View File

@ -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<SyscallReturn> {
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<SyscallReturn> {
self::sys_mkdirat(AT_FDCWD, pathname_addr, mode)
}

View File

@ -25,18 +25,21 @@ 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;
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;
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,18 +82,21 @@ mod gettid;
mod getuid;
mod ioctl;
mod kill;
mod link;
mod lseek;
mod lstat;
mod madvise;
mod mkdir;
mod mmap;
mod mprotect;
mod munmap;
mod openat;
mod open;
mod poll;
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;
@ -138,6 +146,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,
@ -166,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,
@ -184,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
);
@ -246,6 +262,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),
@ -274,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),
@ -292,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),
_ => {

View File

@ -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<SyscallReturn> {
self::sys_openat(AT_FDCWD, pathname_addr, flags, mode)
}
/// File for output busybox ash log.
struct BusyBoxTraceFile;

View File

@ -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<SyscallReturn> {
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))
}

View File

@ -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<SyscallReturn> {
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<SyscallReturn> {
self::sys_unlinkat(AT_FDCWD, pathname_addr, 0)
}
bitflags::bitflags! {
struct UnlinkFlags: u32 {
const AT_REMOVEDIR = 0x200;
}
}