Add support for rename syscall

This commit is contained in:
LI Qing 2023-02-21 15:36:51 +08:00 committed by Tate, Hongliang Tian
parent df82142c9d
commit 4f3e359892
3 changed files with 160 additions and 2 deletions

View File

@ -1,5 +1,6 @@
use crate::prelude::*;
use alloc::string::String;
use spin::RwLockWriteGuard;
use super::{InodeMode, InodeType, Vnode, NAME_MAX};
@ -43,15 +44,19 @@ impl Dentry {
dentry
}
fn name(&self) -> String {
pub fn name(&self) -> String {
self.inner.read().name.clone()
}
fn set_name(&self, name: &str) {
self.inner.write().name = String::from(name);
}
fn this(&self) -> Arc<Dentry> {
self.inner.read().this.upgrade().unwrap()
}
fn parent(&self) -> Option<Arc<Dentry>> {
pub fn parent(&self) -> Option<Arc<Dentry>> {
self.inner
.read()
.parent
@ -59,6 +64,10 @@ impl Dentry {
.map(|p| p.upgrade().unwrap())
}
fn set_parent(&self, parent: &Arc<Dentry>) {
self.inner.write().parent = Some(Arc::downgrade(parent));
}
pub fn vnode(&self) -> &Vnode {
&self.vnode
}
@ -140,6 +149,56 @@ impl Dentry {
Ok(())
}
pub fn rename(&self, old_name: &str, new_dir: &Arc<Self>, new_name: &str) -> Result<()> {
if old_name == "." || old_name == ".." || new_name == "." || new_name == ".." {
return_errno_with_message!(Errno::EISDIR, "old_name or new_name is a directory");
}
if self.vnode.inode().metadata().type_ != InodeType::Dir
|| new_dir.vnode.inode().metadata().type_ != InodeType::Dir
{
return_errno!(Errno::ENOTDIR);
}
// Self and new_dir are same Dentry, just modify name
if Arc::ptr_eq(&self.this(), new_dir) {
if old_name == new_name {
return Ok(());
}
let mut inner = self.inner.write();
let dentry = if let Some(dentry) = inner.children.get(old_name) {
dentry.clone()
} else {
let vnode = Vnode::new(self.vnode.inode().lookup(old_name)?)?;
Dentry::new(old_name, vnode, Some(inner.this.clone()))
};
self.vnode
.inode()
.rename(old_name, self.vnode.inode(), new_name)?;
inner.children.remove(old_name);
dentry.set_name(new_name);
inner.children.insert(String::from(new_name), dentry);
} else {
// Self and new_dir are different Dentry
let (mut self_inner, mut new_dir_inner) = write_lock_two_dentries(&self, &new_dir);
let dentry = if let Some(dentry) = self_inner.children.get(old_name) {
dentry.clone()
} else {
let vnode = Vnode::new(self.vnode.inode().lookup(old_name)?)?;
Dentry::new(old_name, vnode, Some(self_inner.this.clone()))
};
self.vnode
.inode()
.rename(old_name, new_dir.vnode.inode(), new_name)?;
self_inner.children.remove(old_name);
dentry.set_name(new_name);
dentry.set_parent(&new_dir.this());
new_dir_inner
.children
.insert(String::from(new_name), dentry);
}
Ok(())
}
pub fn abs_path(&self) -> String {
let mut path = self.name();
let mut dentry = self.this();
@ -165,3 +224,20 @@ impl Dentry {
path
}
}
fn write_lock_two_dentries<'a>(
this: &'a Dentry,
other: &'a Dentry,
) -> (RwLockWriteGuard<'a, Dentry_>, RwLockWriteGuard<'a, Dentry_>) {
let this_ptr = Arc::as_ptr(&this.this());
let other_ptr = Arc::as_ptr(&other.this());
if this_ptr < other_ptr {
let this = this.inner.write();
let other = other.inner.write();
(this, other)
} else {
let other = other.inner.write();
let this = this.inner.write();
(this, other)
}
}

View File

@ -37,6 +37,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, sys_readlinkat};
use crate::syscall::rename::{sys_rename, sys_renameat};
use crate::syscall::rmdir::sys_rmdir;
use crate::syscall::rt_sigaction::sys_rt_sigaction;
use crate::syscall::rt_sigprocmask::sys_rt_sigprocmask;
@ -93,6 +94,7 @@ mod prctl;
mod prlimit64;
mod read;
mod readlink;
mod rename;
mod rmdir;
mod rt_sigaction;
mod rt_sigprocmask;
@ -173,6 +175,7 @@ define_syscall_nums!(
SYS_UNAME = 63,
SYS_FCNTL = 72,
SYS_GETCWD = 79,
SYS_RENAME = 82,
SYS_MKDIR = 83,
SYS_RMDIR = 84,
SYS_LINK = 86,
@ -199,6 +202,7 @@ define_syscall_nums!(
SYS_MKDIRAT = 258,
SYS_FSTATAT = 262,
SYS_UNLINKAT = 263,
SYS_RENAMEAT = 264,
SYS_LINKAT = 265,
SYS_SYMLINKAT = 266,
SYS_READLINKAT = 267,
@ -293,6 +297,7 @@ 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_RENAME => syscall_handler!(2, sys_rename, 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),
@ -319,6 +324,7 @@ pub fn syscall_dispatch(
SYS_MKDIRAT => syscall_handler!(3, sys_mkdirat, args),
SYS_FSTATAT => syscall_handler!(4, sys_fstatat, args),
SYS_UNLINKAT => syscall_handler!(3, sys_unlinkat, args),
SYS_RENAMEAT => syscall_handler!(4, sys_renameat, args),
SYS_LINKAT => syscall_handler!(5, sys_linkat, args),
SYS_SYMLINKAT => syscall_handler!(3, sys_symlinkat, args),
SYS_READLINKAT => syscall_handler!(4, sys_readlinkat, args),

View File

@ -0,0 +1,76 @@
use crate::fs::{
file_table::FileDescripter,
fs_resolver::{FsPath, AT_FDCWD},
utils::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_RENAMEAT;
pub fn sys_renameat(
old_dirfd: FileDescripter,
old_pathname_addr: Vaddr,
new_dirfd: FileDescripter,
new_pathname_addr: Vaddr,
) -> Result<SyscallReturn> {
log_syscall_entry!(SYS_RENAMEAT);
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)?;
debug!(
"old_dirfd = {}, old_pathname = {:?}, new_dirfd = {}, new_pathname = {:?}",
old_dirfd, old_pathname, new_dirfd, new_pathname
);
let current = current!();
let fs = current.fs().read();
let old_dentry = {
let old_pathname = old_pathname.to_string_lossy();
if old_pathname.is_empty() {
return_errno_with_message!(Errno::ENOENT, "oldpath is empty");
}
let old_fs_path = FsPath::new(old_dirfd, old_pathname.as_ref())?;
fs.lookup_no_follow(&old_fs_path)?
};
let (new_dir_dentry, new_name) = {
let new_pathname = new_pathname.to_string_lossy();
if new_pathname.is_empty() {
return_errno_with_message!(Errno::ENOENT, "newpath is empty");
}
if new_pathname.ends_with("/")
&& old_dentry.vnode().inode().metadata().type_ != InodeType::Dir
{
return_errno_with_message!(Errno::ENOTDIR, "oldpath is not dir");
}
let new_fs_path = FsPath::new(new_dirfd, new_pathname.as_ref().trim_end_matches('/'))?;
fs.lookup_dir_and_base_name(&new_fs_path)?
};
// Check abs_path
let old_abs_path = old_dentry.abs_path();
let new_abs_path = new_dir_dentry.abs_path() + "/" + &new_name;
if new_abs_path.starts_with(&old_abs_path) {
if new_abs_path.len() == old_abs_path.len() {
return Ok(SyscallReturn::Return(0));
} else {
return_errno_with_message!(
Errno::EINVAL,
"newpath contains a path prefix of the oldpath"
);
}
}
let old_dir_dentry = old_dentry.parent().unwrap();
old_dir_dentry.rename(&old_dentry.name(), &new_dir_dentry, &new_name)?;
Ok(SyscallReturn::Return(0))
}
pub fn sys_rename(old_pathname_addr: Vaddr, new_pathname_addr: Vaddr) -> Result<SyscallReturn> {
self::sys_renameat(AT_FDCWD, old_pathname_addr, AT_FDCWD, new_pathname_addr)
}