Cleanup the lookup APIs of FsResolver
This commit is contained in:
parent
384e5bc70d
commit
c2ab393d57
|
|
@ -6,9 +6,8 @@ use ostd::task::Task;
|
|||
|
||||
use super::{
|
||||
file_table::{get_file_fast, FileDesc},
|
||||
inode_handle::InodeHandle,
|
||||
path::Path,
|
||||
utils::{AccessMode, CreationFlags, InodeMode, InodeType, StatusFlags, PATH_MAX, SYMLINKS_MAX},
|
||||
utils::{InodeType, PATH_MAX, SYMLINKS_MAX},
|
||||
};
|
||||
use crate::{fs::path::MountNamespace, prelude::*, process::posix_thread::AsThreadLocal};
|
||||
|
||||
|
|
@ -83,136 +82,64 @@ impl FsResolver {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Opens or creates a file inode handler.
|
||||
pub fn open(&self, path: &FsPath, flags: u32, mode: u16) -> Result<InodeHandle> {
|
||||
let open_args = OpenArgs::from_flags_and_mode(flags, mode)?;
|
||||
|
||||
let follow_tail_link = open_args.follow_tail_link();
|
||||
let stop_on_parent = false;
|
||||
let mut lookup_ctx = LookupCtx::new(follow_tail_link, stop_on_parent);
|
||||
|
||||
let lookup_res = self.lookup_inner(path, &mut lookup_ctx);
|
||||
|
||||
let inode_handle = match lookup_res {
|
||||
Ok(target_path) => self.open_existing_file(target_path, &open_args)?,
|
||||
Err(e)
|
||||
if e.error() == Errno::ENOENT
|
||||
&& open_args.creation_flags.contains(CreationFlags::O_CREAT) =>
|
||||
{
|
||||
self.create_new_file(&open_args, &mut lookup_ctx)?
|
||||
}
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
Ok(inode_handle)
|
||||
}
|
||||
|
||||
fn open_existing_file(&self, target_path: Path, open_args: &OpenArgs) -> Result<InodeHandle> {
|
||||
let inode = target_path.inode();
|
||||
let inode_type = inode.type_();
|
||||
let creation_flags = &open_args.creation_flags;
|
||||
|
||||
match inode_type {
|
||||
InodeType::NamedPipe => {
|
||||
warn!("NamedPipe doesn't support additional operation when opening.");
|
||||
debug!("Open NamedPipe with args: {open_args:?}.");
|
||||
}
|
||||
InodeType::SymLink => {
|
||||
if creation_flags.contains(CreationFlags::O_NOFOLLOW)
|
||||
&& !open_args.status_flags.contains(StatusFlags::O_PATH)
|
||||
{
|
||||
return_errno_with_message!(Errno::ELOOP, "file is a symlink");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if creation_flags.contains(CreationFlags::O_CREAT)
|
||||
&& creation_flags.contains(CreationFlags::O_EXCL)
|
||||
{
|
||||
return_errno_with_message!(Errno::EEXIST, "file exists");
|
||||
}
|
||||
if creation_flags.contains(CreationFlags::O_DIRECTORY) && inode_type != InodeType::Dir {
|
||||
return_errno_with_message!(
|
||||
Errno::ENOTDIR,
|
||||
"O_DIRECTORY is specified but file is not a directory"
|
||||
);
|
||||
}
|
||||
|
||||
if inode_type.is_regular_file() && creation_flags.contains(CreationFlags::O_TRUNC) {
|
||||
target_path.resize(0)?;
|
||||
}
|
||||
InodeHandle::new(target_path, open_args.access_mode, open_args.status_flags)
|
||||
}
|
||||
|
||||
fn create_new_file(
|
||||
&self,
|
||||
open_args: &OpenArgs,
|
||||
lookup_ctx: &mut LookupCtx,
|
||||
) -> Result<InodeHandle> {
|
||||
if open_args
|
||||
.creation_flags
|
||||
.contains(CreationFlags::O_DIRECTORY)
|
||||
{
|
||||
return_errno_with_message!(Errno::ENOTDIR, "cannot create directory");
|
||||
}
|
||||
if lookup_ctx.tail_is_dir() {
|
||||
return_errno_with_message!(Errno::EISDIR, "path refers to a directory");
|
||||
}
|
||||
|
||||
let parent = lookup_ctx
|
||||
.parent()
|
||||
.ok_or_else(|| Error::with_message(Errno::ENOENT, "parent not found"))?;
|
||||
|
||||
let tail_file_name = lookup_ctx.tail_file_name().unwrap();
|
||||
let new_path =
|
||||
parent.new_fs_child(&tail_file_name, InodeType::File, open_args.inode_mode)?;
|
||||
// Don't check access mode for newly created file
|
||||
InodeHandle::new_unchecked_access(new_path, open_args.access_mode, open_args.status_flags)
|
||||
}
|
||||
|
||||
/// Lookups the target path according to the `fs_path`.
|
||||
///
|
||||
/// Symlinks are always followed.
|
||||
pub fn lookup(&self, fs_path: &FsPath) -> Result<Path> {
|
||||
let (follow_tail_link, stop_on_parent) = (true, false);
|
||||
self.lookup_inner(
|
||||
fs_path,
|
||||
&mut LookupCtx::new(follow_tail_link, stop_on_parent),
|
||||
)
|
||||
self.lookup_inner(fs_path, true)?.into_path()
|
||||
}
|
||||
|
||||
/// Lookups the target path according to the `fs_path`.
|
||||
///
|
||||
/// If the last component is a symlink, it will not be followed.
|
||||
pub fn lookup_no_follow(&self, fs_path: &FsPath) -> Result<Path> {
|
||||
let (follow_tail_link, stop_on_parent) = (false, false);
|
||||
self.lookup_inner(
|
||||
fs_path,
|
||||
&mut LookupCtx::new(follow_tail_link, stop_on_parent),
|
||||
)
|
||||
self.lookup_inner(fs_path, false)?.into_path()
|
||||
}
|
||||
|
||||
fn lookup_inner(&self, path: &FsPath, lookup_ctx: &mut LookupCtx) -> Result<Path> {
|
||||
let path = match path.inner {
|
||||
/// Lookups the target path according to the `fs_path` and leaves
|
||||
/// the result unresolved.
|
||||
///
|
||||
/// An unresolved result may indicate either successful full-path resolution,
|
||||
/// or resolution stopping at a parent path when the target doesn't exist
|
||||
/// but its parent does.
|
||||
///
|
||||
/// Symlinks are always followed.
|
||||
pub fn lookup_unresolved(&self, fs_path: &FsPath) -> Result<LookupResult> {
|
||||
self.lookup_inner(fs_path, true)
|
||||
}
|
||||
|
||||
/// Lookups the target path according to the `fs_path` and leaves
|
||||
/// the result unresolved.
|
||||
///
|
||||
/// An unresolved result may indicate either successful full-path resolution,
|
||||
/// or resolution stopping at a parent path when the target doesn't exist
|
||||
/// but its parent does.
|
||||
///
|
||||
/// If the last component is a symlink, it will not be followed.
|
||||
pub fn lookup_unresolved_no_follow(&self, fs_path: &FsPath) -> Result<LookupResult> {
|
||||
self.lookup_inner(fs_path, false)
|
||||
}
|
||||
|
||||
fn lookup_inner(&self, fs_path: &FsPath, follow_tail_link: bool) -> Result<LookupResult> {
|
||||
let path = match fs_path.inner {
|
||||
FsPathInner::Absolute(path) => {
|
||||
self.lookup_from_parent(&self.root, path.trim_start_matches('/'), lookup_ctx)?
|
||||
self.lookup_from_parent(&self.root, path.trim_start_matches('/'), follow_tail_link)?
|
||||
}
|
||||
FsPathInner::CwdRelative(path) => {
|
||||
self.lookup_from_parent(&self.cwd, path, lookup_ctx)?
|
||||
self.lookup_from_parent(&self.cwd, path, follow_tail_link)?
|
||||
}
|
||||
FsPathInner::Cwd => self.cwd.clone(),
|
||||
FsPathInner::Cwd => LookupResult::Resolved(self.cwd.clone()),
|
||||
FsPathInner::FdRelative(fd, path) => {
|
||||
let task = Task::current().unwrap();
|
||||
let mut file_table = task.as_thread_local().unwrap().borrow_file_table_mut();
|
||||
let file = get_file_fast!(&mut file_table, fd);
|
||||
self.lookup_from_parent(file.as_inode_or_err()?.path(), path, lookup_ctx)?
|
||||
self.lookup_from_parent(file.as_inode_or_err()?.path(), path, follow_tail_link)?
|
||||
}
|
||||
FsPathInner::Fd(fd) => {
|
||||
let task = Task::current().unwrap();
|
||||
let mut file_table = task.as_thread_local().unwrap().borrow_file_table_mut();
|
||||
let file = get_file_fast!(&mut file_table, fd);
|
||||
file.as_inode_or_err()?.path().clone()
|
||||
LookupResult::Resolved(file.as_inode_or_err()?.path().clone())
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -235,19 +162,18 @@ impl FsResolver {
|
|||
&self,
|
||||
parent: &Path,
|
||||
relative_path: &str,
|
||||
lookup_ctx: &mut LookupCtx,
|
||||
) -> Result<Path> {
|
||||
follow_tail_link: bool,
|
||||
) -> Result<LookupResult> {
|
||||
debug_assert!(!relative_path.starts_with('/'));
|
||||
|
||||
if relative_path.len() > PATH_MAX {
|
||||
return_errno_with_message!(Errno::ENAMETOOLONG, "path is too long");
|
||||
}
|
||||
if relative_path.is_empty() {
|
||||
return Ok(parent.clone());
|
||||
return Ok(LookupResult::Resolved(parent.clone()));
|
||||
}
|
||||
|
||||
// To handle symlinks
|
||||
let follow_tail_link = lookup_ctx.follow_tail_link;
|
||||
let mut link_path_opt = None;
|
||||
let mut follows = 0;
|
||||
|
||||
|
|
@ -255,7 +181,7 @@ impl FsResolver {
|
|||
let (mut current_path, mut relative_path) = (parent.clone(), relative_path);
|
||||
|
||||
while !relative_path.is_empty() {
|
||||
let (next_name, path_remain, must_be_dir) =
|
||||
let (next_name, path_remain, target_is_dir) =
|
||||
if let Some((prefix, suffix)) = relative_path.split_once('/') {
|
||||
let suffix = suffix.trim_start_matches('/');
|
||||
(prefix, suffix, true)
|
||||
|
|
@ -265,24 +191,21 @@ impl FsResolver {
|
|||
|
||||
// Iterate next path
|
||||
let next_is_tail = path_remain.is_empty();
|
||||
if next_is_tail && lookup_ctx.stop_on_parent {
|
||||
lookup_ctx.set_tail_file(next_name, must_be_dir);
|
||||
return Ok(current_path);
|
||||
}
|
||||
|
||||
let next_path = match current_path.lookup(next_name) {
|
||||
Ok(current_dir) => current_dir,
|
||||
Err(e) => {
|
||||
if next_is_tail && e.error() == Errno::ENOENT && lookup_ctx.tail_file.is_none()
|
||||
{
|
||||
lookup_ctx.set_tail_file(next_name, must_be_dir);
|
||||
lookup_ctx.set_parent(¤t_path);
|
||||
if next_is_tail && e.error() == Errno::ENOENT {
|
||||
return Ok(LookupResult::AtParent(LookupParentResult::new(
|
||||
current_path,
|
||||
next_name.to_string(),
|
||||
target_is_dir,
|
||||
)));
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
let next_type = next_path.type_();
|
||||
|
||||
let next_type = next_path.type_();
|
||||
// If next inode is a symlink, follow symlinks at most `SYMLINKS_MAX` times.
|
||||
if next_type == InodeType::SymLink && (follow_tail_link || !next_is_tail) {
|
||||
if follows >= SYMLINKS_MAX {
|
||||
|
|
@ -296,7 +219,7 @@ impl FsResolver {
|
|||
if !path_remain.is_empty() {
|
||||
tmp_link_path += "/";
|
||||
tmp_link_path += path_remain;
|
||||
} else if must_be_dir {
|
||||
} else if target_is_dir {
|
||||
tmp_link_path += "/";
|
||||
}
|
||||
tmp_link_path
|
||||
|
|
@ -313,7 +236,7 @@ impl FsResolver {
|
|||
follows += 1;
|
||||
} else {
|
||||
// If path ends with `/`, the inode must be a directory
|
||||
if must_be_dir && next_type != InodeType::Dir {
|
||||
if target_is_dir && next_type != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "inode is not dir");
|
||||
}
|
||||
current_path = next_path;
|
||||
|
|
@ -321,146 +244,7 @@ impl FsResolver {
|
|||
}
|
||||
}
|
||||
|
||||
Ok(current_path)
|
||||
}
|
||||
|
||||
/// Lookups the target parent directory path and
|
||||
/// the base file name according to the given `path`.
|
||||
///
|
||||
/// If the last component is a symlink, do not deference it.
|
||||
pub fn lookup_dir_and_base_name(&self, path: &FsPath) -> Result<(Path, String)> {
|
||||
if matches!(path.inner, FsPathInner::Fd(_)) {
|
||||
return_errno!(Errno::ENOENT);
|
||||
}
|
||||
|
||||
let (follow_tail_link, stop_on_parent) = (false, true);
|
||||
let mut lookup_ctx = LookupCtx::new(follow_tail_link, stop_on_parent);
|
||||
let parent_dir = self.lookup_inner(path, &mut lookup_ctx)?;
|
||||
|
||||
let tail_file_name = lookup_ctx.tail_file_name().unwrap();
|
||||
Ok((parent_dir, tail_file_name))
|
||||
}
|
||||
|
||||
/// Lookups the target parent directory path and checks whether
|
||||
/// the base file does not exist yet according to the given `path`.
|
||||
///
|
||||
/// `is_dir` is used to determine whether a directory needs to be created.
|
||||
///
|
||||
/// # Usage case
|
||||
///
|
||||
/// `mkdir`, `mknod`, `link`, and `symlink` all need to create
|
||||
/// new file and all need to perform unique processing on the last
|
||||
/// component of the path name. It is used to provide a unified
|
||||
/// method for pathname lookup and error handling.
|
||||
pub fn lookup_dir_and_new_basename(
|
||||
&self,
|
||||
path: &FsPath,
|
||||
is_dir: bool,
|
||||
) -> Result<(Path, String)> {
|
||||
if matches!(path.inner, FsPathInner::Fd(_)) {
|
||||
return_errno!(Errno::ENOENT);
|
||||
}
|
||||
|
||||
let (follow_tail_link, stop_on_parent) = (false, true);
|
||||
let mut lookup_ctx = LookupCtx::new(follow_tail_link, stop_on_parent);
|
||||
let parent_dir = self.lookup_inner(path, &mut lookup_ctx)?;
|
||||
let tail_file_name = lookup_ctx.tail_file_name().ok_or_else(|| {
|
||||
// If the path is the root directory ("/"), there is no basename,
|
||||
// so this operation is not allowed.
|
||||
Error::with_message(Errno::EEXIST, "operation not allowed on root directory")
|
||||
})?;
|
||||
|
||||
if parent_dir
|
||||
.lookup(tail_file_name.trim_end_matches('/'))
|
||||
.is_ok()
|
||||
{
|
||||
return_errno_with_message!(Errno::EEXIST, "file exists");
|
||||
}
|
||||
if !is_dir && lookup_ctx.tail_is_dir() {
|
||||
return_errno_with_message!(Errno::ENOENT, "No such file or directory");
|
||||
}
|
||||
|
||||
Ok((parent_dir.clone(), tail_file_name))
|
||||
}
|
||||
}
|
||||
|
||||
/// Context information describing one lookup operation.
|
||||
#[derive(Debug)]
|
||||
struct LookupCtx {
|
||||
follow_tail_link: bool,
|
||||
stop_on_parent: bool,
|
||||
// (file_name, file_is_dir)
|
||||
tail_file: Option<(String, bool)>,
|
||||
parent: Option<Path>,
|
||||
}
|
||||
|
||||
impl LookupCtx {
|
||||
pub fn new(follow_tail_link: bool, stop_on_parent: bool) -> Self {
|
||||
Self {
|
||||
follow_tail_link,
|
||||
stop_on_parent,
|
||||
tail_file: None,
|
||||
parent: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tail_file_name(&self) -> Option<String> {
|
||||
self.tail_file.as_ref().map(|(file_name, file_is_dir)| {
|
||||
let mut tail_file_name = file_name.clone();
|
||||
if *file_is_dir {
|
||||
tail_file_name += "/";
|
||||
}
|
||||
tail_file_name
|
||||
})
|
||||
}
|
||||
|
||||
pub fn tail_is_dir(&self) -> bool {
|
||||
self.tail_file
|
||||
.as_ref()
|
||||
.map(|(_, is_dir)| *is_dir)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<&Path> {
|
||||
self.parent.as_ref()
|
||||
}
|
||||
|
||||
pub fn set_tail_file(&mut self, file_name: &str, file_is_dir: bool) {
|
||||
let _ = self.tail_file.insert((file_name.to_string(), file_is_dir));
|
||||
}
|
||||
|
||||
pub fn set_parent(&mut self, parent: &Path) {
|
||||
let _ = self.parent.insert(parent.clone());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Arguments for an open request.
|
||||
struct OpenArgs {
|
||||
creation_flags: CreationFlags,
|
||||
status_flags: StatusFlags,
|
||||
access_mode: AccessMode,
|
||||
inode_mode: InodeMode,
|
||||
}
|
||||
|
||||
impl OpenArgs {
|
||||
pub fn from_flags_and_mode(flags: u32, mode: u16) -> Result<Self> {
|
||||
let creation_flags = CreationFlags::from_bits_truncate(flags);
|
||||
let status_flags = StatusFlags::from_bits_truncate(flags);
|
||||
let access_mode = AccessMode::from_u32(flags)?;
|
||||
let inode_mode = InodeMode::from_bits_truncate(mode);
|
||||
Ok(Self {
|
||||
creation_flags,
|
||||
status_flags,
|
||||
access_mode,
|
||||
inode_mode,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn follow_tail_link(&self) -> bool {
|
||||
!(self.creation_flags.contains(CreationFlags::O_NOFOLLOW)
|
||||
|| self.creation_flags.contains(CreationFlags::O_CREAT)
|
||||
&& self.creation_flags.contains(CreationFlags::O_EXCL))
|
||||
Ok(LookupResult::Resolved(current_path))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -526,6 +310,88 @@ impl<'a> TryFrom<&'a str> for FsPath<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// A result type for lookup operations.
|
||||
pub enum LookupResult {
|
||||
/// The entire path was resolved to a final `Path`.
|
||||
Resolved(Path),
|
||||
/// The path resolution stopped at a parent directory.
|
||||
AtParent(LookupParentResult),
|
||||
}
|
||||
|
||||
impl LookupResult {
|
||||
fn into_path(self) -> Result<Path> {
|
||||
match self {
|
||||
LookupResult::Resolved(path) => Ok(path),
|
||||
LookupResult::AtParent(_) => Err(Error::with_message(
|
||||
Errno::ENOENT,
|
||||
"path resolution did not reach the final target",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes the `LookupResult` and returns the parent path and the tail file name.
|
||||
///
|
||||
/// If the path was resolved or the target was expected to be a directory,
|
||||
/// an error will be returned.
|
||||
pub fn into_parent_and_tail_filename(self) -> Result<(Path, String)> {
|
||||
let LookupResult::AtParent(res) = self else {
|
||||
return_errno_with_message!(Errno::EEXIST, "the path already exists");
|
||||
};
|
||||
res.into_parent_and_tail_filename()
|
||||
}
|
||||
|
||||
/// Consumes the `LookupResult` and returns the parent path and the tail name.
|
||||
///
|
||||
/// If the path was resolved, an error will be returned.
|
||||
pub fn into_parent_and_tail_name(self) -> Result<(Path, String)> {
|
||||
let LookupResult::AtParent(res) = self else {
|
||||
return_errno_with_message!(Errno::EEXIST, "the path already exists");
|
||||
};
|
||||
Ok(res.into_parent_and_tail_name())
|
||||
}
|
||||
}
|
||||
|
||||
/// A result that contains information about a path lookup that stopped
|
||||
/// at a parent directory.
|
||||
pub struct LookupParentResult {
|
||||
/// The path of the parent directory where resolution stopped.
|
||||
parent: Path,
|
||||
/// The remaining unresolved component name.
|
||||
tail_name: String,
|
||||
/// Indicates whether the target was expected to be a directory.
|
||||
target_is_dir: bool,
|
||||
}
|
||||
|
||||
impl LookupParentResult {
|
||||
fn new(parent: Path, tail_name: String, target_is_dir: bool) -> Self {
|
||||
Self {
|
||||
parent,
|
||||
tail_name,
|
||||
target_is_dir,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the target was expected to be a directory.
|
||||
pub fn target_is_dir(&self) -> bool {
|
||||
self.target_is_dir
|
||||
}
|
||||
|
||||
/// Consumes the `LookupParentResult` and returns the parent path and the tail file name.
|
||||
///
|
||||
/// If the target was expected to be a directory, an error will be returned.
|
||||
pub fn into_parent_and_tail_filename(self) -> Result<(Path, String)> {
|
||||
if self.target_is_dir {
|
||||
return_errno_with_message!(Errno::ENOENT, "the path is a directory");
|
||||
}
|
||||
Ok((self.parent, self.tail_name))
|
||||
}
|
||||
|
||||
/// Consumes the `LookupParentResult` and returns the parent path and the tail name.
|
||||
pub fn into_parent_and_tail_name(self) -> (Path, String) {
|
||||
(self.parent, self.tail_name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Splits a `path` to (`dir_path`, `file_name`).
|
||||
///
|
||||
/// The `dir_path` must be a directory.
|
||||
|
|
|
|||
|
|
@ -51,7 +51,10 @@ pub fn sys_linkat(
|
|||
} else {
|
||||
fs.lookup_no_follow(&old_fs_path)?
|
||||
};
|
||||
let (new_path, new_name) = fs.lookup_dir_and_new_basename(&new_fs_path, false)?;
|
||||
let (new_path, new_name) = fs
|
||||
.lookup_unresolved_no_follow(&new_fs_path)?
|
||||
.into_parent_and_tail_filename()?;
|
||||
|
||||
(old_path, new_path, new_name)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ pub fn sys_mkdirat(
|
|||
fs_ref
|
||||
.resolver()
|
||||
.read()
|
||||
.lookup_dir_and_new_basename(&fs_path, true)?
|
||||
.lookup_unresolved_no_follow(&fs_path)?
|
||||
.into_parent_and_tail_name()?
|
||||
};
|
||||
|
||||
let inode_mode = {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ pub fn sys_mknodat(
|
|||
fs_ref
|
||||
.resolver()
|
||||
.read()
|
||||
.lookup_dir_and_new_basename(&fs_path, false)?
|
||||
.lookup_unresolved_no_follow(&fs_path)?
|
||||
.into_parent_and_tail_filename()?
|
||||
};
|
||||
|
||||
match inode_type {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use super::SyscallReturn;
|
|||
use crate::{
|
||||
fs::{
|
||||
file_table::FileDesc,
|
||||
fs_resolver::{FsPath, AT_FDCWD},
|
||||
fs_resolver::{split_path, FsPath, AT_FDCWD},
|
||||
utils::InodeType,
|
||||
},
|
||||
prelude::*,
|
||||
|
|
@ -38,31 +38,33 @@ pub fn sys_renameat2(
|
|||
let fs_ref = ctx.thread_local.borrow_fs();
|
||||
let fs = fs_ref.resolver().read();
|
||||
|
||||
let old_path_name = old_path_name.to_string_lossy();
|
||||
if old_path_name.is_empty() {
|
||||
return_errno_with_message!(Errno::ENOENT, "oldpath is empty");
|
||||
}
|
||||
let (old_dir_path, old_name) = {
|
||||
let old_path_name = old_path_name.to_string_lossy();
|
||||
if old_path_name.is_empty() {
|
||||
return_errno_with_message!(Errno::ENOENT, "oldpath is empty");
|
||||
}
|
||||
let old_fs_path = FsPath::new(old_dirfd, old_path_name.as_ref())?;
|
||||
fs.lookup_dir_and_base_name(&old_fs_path)?
|
||||
let (old_parent_path_name, old_name) = split_path(&old_path_name);
|
||||
let old_fs_path = FsPath::new(old_dirfd, old_parent_path_name)?;
|
||||
(fs.lookup(&old_fs_path)?, old_name)
|
||||
};
|
||||
let old_path = old_dir_path.lookup(&old_name)?;
|
||||
let old_path = old_dir_path.lookup(old_name)?;
|
||||
|
||||
let new_path_name = new_path_name.to_string_lossy();
|
||||
if new_path_name.is_empty() {
|
||||
return_errno_with_message!(Errno::ENOENT, "newpath is empty");
|
||||
}
|
||||
if new_path_name.ends_with('/') && old_path.type_() != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "oldpath is not dir");
|
||||
}
|
||||
let (new_dir_path, new_name) = {
|
||||
let new_path_name = new_path_name.to_string_lossy();
|
||||
if new_path_name.is_empty() {
|
||||
return_errno_with_message!(Errno::ENOENT, "newpath is empty");
|
||||
}
|
||||
if new_path_name.ends_with('/') && old_path.type_() != InodeType::Dir {
|
||||
return_errno_with_message!(Errno::ENOTDIR, "oldpath is not dir");
|
||||
}
|
||||
let new_fs_path = FsPath::new(new_dirfd, new_path_name.as_ref().trim_end_matches('/'))?;
|
||||
fs.lookup_dir_and_base_name(&new_fs_path)?
|
||||
let (new_parent_path_name, new_name) = split_path(&new_path_name);
|
||||
let new_fs_path = FsPath::new(new_dirfd, new_parent_path_name.trim_end_matches('/'))?;
|
||||
(fs.lookup(&new_fs_path)?, new_name)
|
||||
};
|
||||
|
||||
// Check abs_path
|
||||
let old_abs_path = old_path.abs_path();
|
||||
let new_abs_path = new_dir_path.abs_path() + "/" + &new_name;
|
||||
let new_abs_path = new_dir_path.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));
|
||||
|
|
@ -74,7 +76,7 @@ pub fn sys_renameat2(
|
|||
}
|
||||
}
|
||||
|
||||
old_dir_path.rename(&old_name, &new_dir_path, &new_name)?;
|
||||
old_dir_path.rename(old_name, &new_dir_path, new_name)?;
|
||||
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use super::SyscallReturn;
|
|||
use crate::{
|
||||
fs::{
|
||||
file_table::FileDesc,
|
||||
fs_resolver::{FsPath, AT_FDCWD},
|
||||
fs_resolver::{split_path, FsPath, AT_FDCWD},
|
||||
},
|
||||
prelude::*,
|
||||
syscall::constants::MAX_FILENAME_LEN,
|
||||
|
|
@ -22,21 +22,27 @@ pub(super) fn sys_rmdirat(
|
|||
let path_name = ctx.user_space().read_cstring(path_addr, MAX_FILENAME_LEN)?;
|
||||
debug!("dirfd = {}, path_addr = {:?}", dirfd, path_addr);
|
||||
|
||||
let path_name = path_name.to_string_lossy();
|
||||
if path_name.is_empty() {
|
||||
return_errno_with_message!(Errno::ENOENT, "path is empty");
|
||||
}
|
||||
if path_name.trim_end_matches('/').is_empty() {
|
||||
return_errno_with_message!(Errno::EBUSY, "is root directory");
|
||||
}
|
||||
|
||||
let (dir_path, name) = {
|
||||
let path_name = path_name.to_string_lossy();
|
||||
if path_name.is_empty() {
|
||||
return_errno_with_message!(Errno::ENOENT, "path is empty");
|
||||
}
|
||||
if path_name.trim_end_matches('/').is_empty() {
|
||||
return_errno_with_message!(Errno::EBUSY, "is root directory");
|
||||
}
|
||||
let fs_path = FsPath::new(dirfd, path_name.as_ref())?;
|
||||
ctx.thread_local
|
||||
.borrow_fs()
|
||||
.resolver()
|
||||
.read()
|
||||
.lookup_dir_and_base_name(&fs_path)?
|
||||
let (parent_path_name, target_name) = split_path(&path_name);
|
||||
let fs_path = FsPath::new(dirfd, parent_path_name)?;
|
||||
(
|
||||
ctx.thread_local
|
||||
.borrow_fs()
|
||||
.resolver()
|
||||
.read()
|
||||
.lookup(&fs_path)?,
|
||||
target_name,
|
||||
)
|
||||
};
|
||||
|
||||
dir_path.rmdir(name.trim_end_matches('/'))?;
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@ pub fn sys_symlinkat(
|
|||
.borrow_fs()
|
||||
.resolver()
|
||||
.read()
|
||||
.lookup_dir_and_new_basename(&fs_path, false)?
|
||||
.lookup_unresolved_no_follow(&fs_path)?
|
||||
.into_parent_and_tail_filename()?
|
||||
};
|
||||
|
||||
let new_path = dir_path.new_fs_child(&link_name, InodeType::SymLink, mkmod!(a+rwx))?;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use super::SyscallReturn;
|
|||
use crate::{
|
||||
fs::{
|
||||
file_table::FileDesc,
|
||||
fs_resolver::{FsPath, AT_FDCWD},
|
||||
fs_resolver::{split_path, FsPath, AT_FDCWD},
|
||||
},
|
||||
prelude::*,
|
||||
syscall::constants::MAX_FILENAME_LEN,
|
||||
|
|
@ -25,22 +25,28 @@ pub fn sys_unlinkat(
|
|||
let path_name = ctx.user_space().read_cstring(path_addr, MAX_FILENAME_LEN)?;
|
||||
debug!("dirfd = {}, path = {:?}", dirfd, path_name);
|
||||
|
||||
let path_name = path_name.to_string_lossy();
|
||||
if path_name.is_empty() {
|
||||
return_errno_with_message!(Errno::ENOENT, "path is empty");
|
||||
}
|
||||
if path_name.ends_with('/') {
|
||||
return_errno_with_message!(Errno::EISDIR, "unlink on directory");
|
||||
}
|
||||
|
||||
let (dir_path, name) = {
|
||||
let path_name = path_name.to_string_lossy();
|
||||
if path_name.is_empty() {
|
||||
return_errno_with_message!(Errno::ENOENT, "path is empty");
|
||||
}
|
||||
if path_name.ends_with('/') {
|
||||
return_errno_with_message!(Errno::EISDIR, "unlink on directory");
|
||||
}
|
||||
let fs_path = FsPath::new(dirfd, path_name.as_ref())?;
|
||||
ctx.thread_local
|
||||
.borrow_fs()
|
||||
.resolver()
|
||||
.read()
|
||||
.lookup_dir_and_base_name(&fs_path)?
|
||||
let (parent_path_name, target_name) = split_path(&path_name);
|
||||
let fs_path = FsPath::new(dirfd, parent_path_name)?;
|
||||
(
|
||||
ctx.thread_local
|
||||
.borrow_fs()
|
||||
.resolver()
|
||||
.read()
|
||||
.lookup(&fs_path)?,
|
||||
target_name,
|
||||
)
|
||||
};
|
||||
dir_path.unlink(&name)?;
|
||||
|
||||
dir_path.unlink(name)?;
|
||||
Ok(SyscallReturn::Return(0))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue