Add support for rename syscall
This commit is contained in:
parent
df82142c9d
commit
4f3e359892
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
Loading…
Reference in New Issue