Refactor Dentry to optimize the vfs layer

This commit is contained in:
Shaowei Song
2024-10-10 12:51:26 +00:00
committed by Tate, Hongliang Tian
parent 271e893889
commit ea489252f4
14 changed files with 177 additions and 175 deletions

View File

@ -16,7 +16,7 @@ mod pty;
pub use pty::{PtyMaster, PtySlave}; pub use pty::{PtyMaster, PtySlave};
use spin::Once; use spin::Once;
static DEV_PTS: Once<Arc<Dentry>> = Once::new(); static DEV_PTS: Once<Dentry> = Once::new();
pub fn init() -> Result<()> { pub fn init() -> Result<()> {
let fs = FsResolver::new(); let fs = FsResolver::new();

View File

@ -91,7 +91,7 @@ impl From<u64> for DeviceId {
/// ///
/// If the parent path is not existing, `mkdir -p` the parent path. /// If the parent path is not existing, `mkdir -p` the parent path.
/// This function is used in registering device. /// This function is used in registering device.
pub fn add_node(device: Arc<dyn Device>, path: &str) -> Result<Arc<Dentry>> { pub fn add_node(device: Arc<dyn Device>, path: &str) -> Result<Dentry> {
let mut dentry = { let mut dentry = {
let fs_resolver = FsResolver::new(); let fs_resolver = FsResolver::new();
fs_resolver.lookup(&FsPath::try_from("/dev").unwrap())? fs_resolver.lookup(&FsPath::try_from("/dev").unwrap())?

View File

@ -17,8 +17,8 @@ pub const AT_FDCWD: FileDesc = -100;
/// File system resolver. /// File system resolver.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct FsResolver { pub struct FsResolver {
root: Arc<Dentry>, root: Dentry,
cwd: Arc<Dentry>, cwd: Dentry,
} }
impl FsResolver { impl FsResolver {
@ -31,22 +31,22 @@ impl FsResolver {
} }
/// Gets the root directory. /// Gets the root directory.
pub fn root(&self) -> &Arc<Dentry> { pub fn root(&self) -> &Dentry {
&self.root &self.root
} }
/// Gets the current working directory. /// Gets the current working directory.
pub fn cwd(&self) -> &Arc<Dentry> { pub fn cwd(&self) -> &Dentry {
&self.cwd &self.cwd
} }
/// Sets the current working directory to the given `dentry`. /// Sets the current working directory to the given `dentry`.
pub fn set_cwd(&mut self, dentry: Arc<Dentry>) { pub fn set_cwd(&mut self, dentry: Dentry) {
self.cwd = dentry; self.cwd = dentry;
} }
/// Sets the root directory to the given `dentry`. /// Sets the root directory to the given `dentry`.
pub fn set_root(&mut self, dentry: Arc<Dentry>) { pub fn set_root(&mut self, dentry: Dentry) {
self.root = dentry; self.root = dentry;
} }
@ -76,7 +76,7 @@ impl FsResolver {
fn open_existing_file( fn open_existing_file(
&self, &self,
target_dentry: Arc<Dentry>, target_dentry: Dentry,
open_args: &OpenArgs, open_args: &OpenArgs,
) -> Result<InodeHandle> { ) -> Result<InodeHandle> {
let inode = target_dentry.inode(); let inode = target_dentry.inode();
@ -147,19 +147,19 @@ impl FsResolver {
/// Lookups the target dentry according to the `path`. /// Lookups the target dentry according to the `path`.
/// Symlinks are always followed. /// Symlinks are always followed.
pub fn lookup(&self, path: &FsPath) -> Result<Arc<Dentry>> { pub fn lookup(&self, path: &FsPath) -> Result<Dentry> {
let (follow_tail_link, stop_on_parent) = (true, false); let (follow_tail_link, stop_on_parent) = (true, false);
self.lookup_inner(path, &mut LookupCtx::new(follow_tail_link, stop_on_parent)) self.lookup_inner(path, &mut LookupCtx::new(follow_tail_link, stop_on_parent))
} }
/// Lookups the target dentry according to the `path`. /// Lookups the target dentry according to the `path`.
/// If the last component is a symlink, it will not be followed. /// If the last component is a symlink, it will not be followed.
pub fn lookup_no_follow(&self, path: &FsPath) -> Result<Arc<Dentry>> { pub fn lookup_no_follow(&self, path: &FsPath) -> Result<Dentry> {
let (follow_tail_link, stop_on_parent) = (false, false); let (follow_tail_link, stop_on_parent) = (false, false);
self.lookup_inner(path, &mut LookupCtx::new(follow_tail_link, stop_on_parent)) self.lookup_inner(path, &mut LookupCtx::new(follow_tail_link, stop_on_parent))
} }
fn lookup_inner(&self, path: &FsPath, lookup_ctx: &mut LookupCtx) -> Result<Arc<Dentry>> { fn lookup_inner(&self, path: &FsPath, lookup_ctx: &mut LookupCtx) -> Result<Dentry> {
let dentry = match path.inner { let dentry = match path.inner {
FsPathInner::Absolute(path) => { 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('/'), lookup_ctx)?
@ -192,10 +192,10 @@ impl FsResolver {
#[allow(clippy::redundant_closure)] #[allow(clippy::redundant_closure)]
fn lookup_from_parent( fn lookup_from_parent(
&self, &self,
parent: &Arc<Dentry>, parent: &Dentry,
relative_path: &str, relative_path: &str,
lookup_ctx: &mut LookupCtx, lookup_ctx: &mut LookupCtx,
) -> Result<Arc<Dentry>> { ) -> Result<Dentry> {
debug_assert!(!relative_path.starts_with('/')); debug_assert!(!relative_path.starts_with('/'));
if relative_path.len() > PATH_MAX { if relative_path.len() > PATH_MAX {
@ -285,7 +285,7 @@ impl FsResolver {
} }
/// Lookups the target dentry according to the given `fd`. /// Lookups the target dentry according to the given `fd`.
pub fn lookup_from_fd(&self, fd: FileDesc) -> Result<Arc<Dentry>> { pub fn lookup_from_fd(&self, fd: FileDesc) -> Result<Dentry> {
let current = current!(); let current = current!();
let file_table = current.file_table().lock(); let file_table = current.file_table().lock();
let inode_handle = file_table let inode_handle = file_table
@ -299,7 +299,7 @@ impl FsResolver {
/// the base file name according to the given `path`. /// the base file name according to the given `path`.
/// ///
/// If the last component is a symlink, do not deference it. /// If the last component is a symlink, do not deference it.
pub fn lookup_dir_and_base_name(&self, path: &FsPath) -> Result<(Arc<Dentry>, String)> { pub fn lookup_dir_and_base_name(&self, path: &FsPath) -> Result<(Dentry, String)> {
if matches!(path.inner, FsPathInner::Fd(_)) { if matches!(path.inner, FsPathInner::Fd(_)) {
return_errno!(Errno::ENOENT); return_errno!(Errno::ENOENT);
} }
@ -327,7 +327,7 @@ impl FsResolver {
&self, &self,
path: &FsPath, path: &FsPath,
is_dir: bool, is_dir: bool,
) -> Result<(Arc<Dentry>, String)> { ) -> Result<(Dentry, String)> {
if matches!(path.inner, FsPathInner::Fd(_)) { if matches!(path.inner, FsPathInner::Fd(_)) {
return_errno!(Errno::ENOENT); return_errno!(Errno::ENOENT);
} }
@ -364,7 +364,7 @@ struct LookupCtx {
stop_on_parent: bool, stop_on_parent: bool,
// (file_name, file_is_dir) // (file_name, file_is_dir)
tail_file: Option<(String, bool)>, tail_file: Option<(String, bool)>,
parent: Option<Arc<Dentry>>, parent: Option<Dentry>,
} }
impl LookupCtx { impl LookupCtx {
@ -394,7 +394,7 @@ impl LookupCtx {
.unwrap_or(false) .unwrap_or(false)
} }
pub fn parent(&self) -> Option<&Arc<Dentry>> { pub fn parent(&self) -> Option<&Dentry> {
self.parent.as_ref() self.parent.as_ref()
} }
@ -402,7 +402,7 @@ impl LookupCtx {
let _ = self.tail_file.insert((file_name.to_string(), file_is_dir)); let _ = self.tail_file.insert((file_name.to_string(), file_is_dir));
} }
pub fn set_parent(&mut self, parent: &Arc<Dentry>) { pub fn set_parent(&mut self, parent: &Dentry) {
let _ = self.parent.insert(parent.clone()); let _ = self.parent.insert(parent.clone());
} }
} }

View File

@ -7,11 +7,7 @@ use super::*;
use crate::{prelude::*, process::signal::Pollable}; use crate::{prelude::*, process::signal::Pollable};
impl InodeHandle<Rights> { impl InodeHandle<Rights> {
pub fn new( pub fn new(dentry: Dentry, access_mode: AccessMode, status_flags: StatusFlags) -> Result<Self> {
dentry: Arc<Dentry>,
access_mode: AccessMode,
status_flags: StatusFlags,
) -> Result<Self> {
let inode_mode = dentry.inode().mode()?; let inode_mode = dentry.inode().mode()?;
if access_mode.is_readable() && !inode_mode.is_readable() { if access_mode.is_readable() && !inode_mode.is_readable() {
return_errno_with_message!(Errno::EACCES, "file is not readable"); return_errno_with_message!(Errno::EACCES, "file is not readable");
@ -24,7 +20,7 @@ impl InodeHandle<Rights> {
} }
pub fn new_unchecked_access( pub fn new_unchecked_access(
dentry: Arc<Dentry>, dentry: Dentry,
access_mode: AccessMode, access_mode: AccessMode,
status_flags: StatusFlags, status_flags: StatusFlags,
) -> Result<Self> { ) -> Result<Self> {

View File

@ -32,7 +32,7 @@ use crate::{
pub struct InodeHandle<R = Rights>(Arc<InodeHandle_>, R); pub struct InodeHandle<R = Rights>(Arc<InodeHandle_>, R);
struct InodeHandle_ { struct InodeHandle_ {
dentry: Arc<Dentry>, dentry: Dentry,
/// `file_io` is Similar to `file_private` field in `file` structure in linux. If /// `file_io` is Similar to `file_private` field in `file` structure in linux. If
/// `file_io` is Some, typical file operations including `read`, `write`, `poll`, /// `file_io` is Some, typical file operations including `read`, `write`, `poll`,
/// `ioctl` will be provided by `file_io`, instead of `dentry`. /// `ioctl` will be provided by `file_io`, instead of `dentry`.
@ -354,7 +354,7 @@ impl Debug for InodeHandle_ {
/// Methods for both dyn and static /// Methods for both dyn and static
impl<R> InodeHandle<R> { impl<R> InodeHandle<R> {
pub fn dentry(&self) -> &Arc<Dentry> { pub fn dentry(&self) -> &Dentry {
&self.0.dentry &self.0.dentry
} }

View File

@ -22,21 +22,20 @@ use crate::{
}; };
/// A `Dentry` is used to represent a location in the mount tree. /// A `Dentry` is used to represent a location in the mount tree.
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Dentry { pub struct Dentry {
mount_node: Arc<MountNode>, mount_node: Arc<MountNode>,
inner: Arc<Dentry_>, inner: Arc<Dentry_>,
this: Weak<Dentry>,
} }
/// The inner structure of `Dentry` for caching helpful nodes /// The inner structure of `Dentry` for caching helpful nodes
/// to accelerate the path lookup. /// to accelerate the path lookup.
pub struct Dentry_ { pub struct Dentry_ {
inode: Arc<dyn Inode>, inode: Arc<dyn Inode>,
name_and_parent: RwMutex<Option<(String, Arc<Dentry_>)>>, name_and_parent: RwLock<Option<(String, Arc<Dentry_>)>>,
this: Weak<Dentry_>,
children: RwMutex<Children>, children: RwMutex<Children>,
flags: AtomicU32, flags: AtomicU32,
this: Weak<Dentry_>,
} }
impl Dentry_ { impl Dentry_ {
@ -53,8 +52,8 @@ impl Dentry_ {
inode, inode,
flags: AtomicU32::new(DentryFlags::empty().bits()), flags: AtomicU32::new(DentryFlags::empty().bits()),
name_and_parent: match options { name_and_parent: match options {
DentryOptions::Leaf(name_and_parent) => RwMutex::new(Some(name_and_parent)), DentryOptions::Leaf(name_and_parent) => RwLock::new(Some(name_and_parent)),
_ => RwMutex::new(None), _ => RwLock::new(None),
}, },
this: weak_self.clone(), this: weak_self.clone(),
children: RwMutex::new(Children::new()), children: RwMutex::new(Children::new()),
@ -139,129 +138,116 @@ impl Dentry_ {
/// Creates a `Dentry_` by creating a new inode of the `type_` with the `mode`. /// Creates a `Dentry_` by creating a new inode of the `type_` with the `mode`.
pub fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<Self>> { pub fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<Self>> {
if self.inode.type_() != InodeType::Dir { if self.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR); return_errno!(Errno::ENOTDIR);
} }
let children = self.children.upread(); let children = self.children.upread();
if children.find_dentry(name).is_some() { if children.contains(name) {
return_errno!(Errno::EEXIST); return_errno!(Errno::EEXIST);
} }
let child = { let new_inode = self.inode.create(name, type_, mode)?;
let inode = self.inode.create(name, type_, mode)?; let name = String::from(name);
let dentry = Self::new( let new_child = Dentry_::new(new_inode, DentryOptions::Leaf((name.clone(), self.this())));
inode,
DentryOptions::Leaf((String::from(name), self.this())),
);
let mut children = children.upgrade(); let mut children = children.upgrade();
children.insert_dentry(&dentry); children.insert(name, new_child.clone());
dentry Ok(new_child)
};
Ok(child)
} }
/// Lookups a target `Dentry_` from the cache in children. /// Lookups a target `Dentry_` from the cache in children.
pub fn lookup_via_cache(&self, name: &str) -> Option<Arc<Dentry_>> { pub fn lookup_via_cache(&self, name: &str) -> Option<Arc<Dentry_>> {
let children = self.children.read(); let children = self.children.read();
children.find_dentry(name) children.find(name)
} }
/// Lookups a target `Dentry_` from the file system. /// Lookups a target `Dentry_` from the file system.
pub fn lookup_via_fs(&self, name: &str) -> Result<Arc<Dentry_>> { pub fn lookup_via_fs(&self, name: &str) -> Result<Arc<Dentry_>> {
let children = self.children.upread(); let children = self.children.upread();
let inode = self.inode.lookup(name)?; let inode = self.inode.lookup(name)?;
let inner = Self::new( let name = String::from(name);
inode, let target = Self::new(inode, DentryOptions::Leaf((name.clone(), self.this())));
DentryOptions::Leaf((String::from(name), self.this())),
);
let mut children = children.upgrade(); let mut children = children.upgrade();
children.insert_dentry(&inner); children.insert(name, target.clone());
Ok(inner) Ok(target)
}
fn insert_dentry(&self, child_dentry: &Arc<Dentry_>) {
let mut children = self.children.write();
children.insert_dentry(child_dentry);
} }
/// Creates a `Dentry_` by making an inode of the `type_` with the `mode`. /// Creates a `Dentry_` by making an inode of the `type_` with the `mode`.
pub fn mknod(&self, name: &str, mode: InodeMode, type_: MknodType) -> Result<Arc<Self>> { pub fn mknod(&self, name: &str, mode: InodeMode, type_: MknodType) -> Result<Arc<Self>> {
if self.inode.type_() != InodeType::Dir { if self.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR); return_errno!(Errno::ENOTDIR);
} }
let children = self.children.upread(); let children = self.children.upread();
if children.find_dentry(name).is_some() { if children.contains(name) {
return_errno!(Errno::EEXIST); return_errno!(Errno::EEXIST);
} }
let child = {
let inode = self.inode.mknod(name, mode, type_)?; let inode = self.inode.mknod(name, mode, type_)?;
let dentry = Self::new( let name = String::from(name);
inode, let new_child = Dentry_::new(inode, DentryOptions::Leaf((name.clone(), self.this())));
DentryOptions::Leaf((String::from(name), self.this())),
);
let mut children = children.upgrade(); let mut children = children.upgrade();
children.insert_dentry(&dentry); children.insert(name, new_child.clone());
dentry Ok(new_child)
};
Ok(child)
} }
/// Links a new name for the `Dentry_` by `link()` the inner inode. /// Links a new name for the `Dentry_` by `link()` the inner inode.
pub fn link(&self, old: &Arc<Self>, name: &str) -> Result<()> { pub fn link(&self, old: &Arc<Self>, name: &str) -> Result<()> {
if self.inode.type_() != InodeType::Dir { if self.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR); return_errno!(Errno::ENOTDIR);
} }
let children = self.children.upread(); let children = self.children.upread();
if children.find_dentry(name).is_some() { if children.contains(name) {
return_errno!(Errno::EEXIST); return_errno!(Errno::EEXIST);
} }
let old_inode = old.inode(); let old_inode = old.inode();
self.inode.link(old_inode, name)?; self.inode.link(old_inode, name)?;
let dentry = Self::new( let name = String::from(name);
let dentry = Dentry_::new(
old_inode.clone(), old_inode.clone(),
DentryOptions::Leaf((String::from(name), self.this())), DentryOptions::Leaf((name.clone(), self.this())),
); );
let mut children = children.upgrade(); let mut children = children.upgrade();
children.insert_dentry(&dentry); children.insert(name, dentry);
Ok(()) Ok(())
} }
/// Deletes a `Dentry_` by `unlink()` the inner inode. /// Deletes a `Dentry_` by `unlink()` the inner inode.
pub fn unlink(&self, name: &str) -> Result<()> { pub fn unlink(&self, name: &str) -> Result<()> {
if self.inode.type_() != InodeType::Dir { if self.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR); return_errno!(Errno::ENOTDIR);
} }
let children = self.children.upread(); let children = self.children.upread();
let _ = children.find_dentry_with_checking_mountpoint(name)?; children.check_mountpoint(name)?;
self.inode.unlink(name)?; self.inode.unlink(name)?;
let mut children = children.upgrade(); let mut children = children.upgrade();
children.delete_dentry(name); children.delete(name);
Ok(()) Ok(())
} }
/// Deletes a directory `Dentry_` by `rmdir()` the inner inode. /// Deletes a directory `Dentry_` by `rmdir()` the inner inode.
pub fn rmdir(&self, name: &str) -> Result<()> { pub fn rmdir(&self, name: &str) -> Result<()> {
if self.inode.type_() != InodeType::Dir { if self.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR); return_errno!(Errno::ENOTDIR);
} }
let children = self.children.upread(); let children = self.children.upread();
let _ = children.find_dentry_with_checking_mountpoint(name)?; children.check_mountpoint(name)?;
self.inode.rmdir(name)?; self.inode.rmdir(name)?;
let mut children = children.upgrade(); let mut children = children.upgrade();
children.delete_dentry(name); children.delete(name);
Ok(()) Ok(())
} }
@ -270,7 +256,7 @@ impl Dentry_ {
if old_name == "." || old_name == ".." || new_name == "." || new_name == ".." { if old_name == "." || old_name == ".." || new_name == "." || new_name == ".." {
return_errno_with_message!(Errno::EISDIR, "old_name or new_name is a directory"); return_errno_with_message!(Errno::EISDIR, "old_name or new_name is a directory");
} }
if self.inode.type_() != InodeType::Dir || new_dir.inode.type_() != InodeType::Dir { if self.type_() != InodeType::Dir || new_dir.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR); return_errno!(Errno::ENOTDIR);
} }
@ -281,37 +267,38 @@ impl Dentry_ {
} }
let children = self.children.upread(); let children = self.children.upread();
let old_dentry = children.find_dentry_with_checking_mountpoint(old_name)?; let old_dentry = children.check_mountpoint_then_find(old_name)?;
let _ = children.find_dentry_with_checking_mountpoint(new_name)?; children.check_mountpoint(new_name)?;
self.inode.rename(old_name, &self.inode, new_name)?; self.inode.rename(old_name, &self.inode, new_name)?;
let mut children = children.upgrade(); let mut children = children.upgrade();
match old_dentry.as_ref() { match old_dentry.as_ref() {
Some(dentry) => { Some(dentry) => {
children.delete_dentry(old_name); children.delete(old_name);
dentry.set_name_and_parent(new_name, self.this()); dentry.set_name_and_parent(new_name, self.this());
children.insert_dentry(dentry); children.insert(new_name.to_string(), dentry.clone());
} }
None => { None => {
children.delete_dentry(new_name); children.delete(new_name);
} }
} }
} else { } else {
// The two are different dentries // The two are different dentries
let (mut self_children, mut new_dir_children) = let (mut self_children, mut new_dir_children) =
write_lock_children_on_two_dentries(self, new_dir); write_lock_children_on_two_dentries(self, new_dir);
let old_dentry = self_children.find_dentry_with_checking_mountpoint(old_name)?; let old_dentry = self_children.check_mountpoint_then_find(old_name)?;
let _ = new_dir_children.find_dentry_with_checking_mountpoint(new_name)?; new_dir_children.check_mountpoint(new_name)?;
self.inode.rename(old_name, &new_dir.inode, new_name)?; self.inode.rename(old_name, &new_dir.inode, new_name)?;
match old_dentry.as_ref() { match old_dentry.as_ref() {
Some(dentry) => { Some(dentry) => {
self_children.delete_dentry(old_name); self_children.delete(old_name);
dentry.set_name_and_parent(new_name, new_dir.this()); dentry.set_name_and_parent(new_name, new_dir.this());
new_dir_children.insert_dentry(dentry); new_dir_children.insert(new_name.to_string(), dentry.clone());
} }
None => { None => {
new_dir_children.delete_dentry(new_name); new_dir_children.delete(new_name);
} }
} }
} }
@ -387,41 +374,60 @@ enum DentryOptions {
} }
struct Children { struct Children {
inner: HashMap<String, Arc<Dentry_>>, dentries: HashMap<String, Arc<Dentry_>>,
} }
impl Children { impl Children {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
inner: HashMap::new(), dentries: HashMap::new(),
} }
} }
pub fn insert_dentry(&mut self, dentry: &Arc<Dentry_>) { pub fn len(&self) -> usize {
self.dentries.len()
}
pub fn contains(&self, name: &str) -> bool {
self.dentries.contains_key(name)
}
pub fn find(&self, name: &str) -> Option<Arc<Dentry_>> {
self.dentries.get(name).cloned()
}
pub fn insert(&mut self, name: String, dentry: Arc<Dentry_>) {
// Do not cache it in the children if is not cacheable. // Do not cache it in the children if is not cacheable.
// When we lookup it from the parent, it will always be newly created. // When we lookup it from the parent, it will always be newly created.
if !dentry.inode().is_dentry_cacheable() { if !dentry.inode.is_dentry_cacheable() {
return; return;
} }
let _ = self.inner.insert(dentry.name(), dentry.clone()); let _ = self.dentries.insert(name, dentry);
} }
pub fn delete_dentry(&mut self, name: &str) -> Option<Arc<Dentry_>> { pub fn delete(&mut self, name: &str) -> Option<Arc<Dentry_>> {
self.inner.remove(name) self.dentries.remove(name)
} }
pub fn find_dentry(&self, name: &str) -> Option<Arc<Dentry_>> { pub fn check_mountpoint(&self, name: &str) -> Result<()> {
self.inner.get(name).cloned() if let Some(dentry) = self.dentries.get(name) {
}
pub fn find_dentry_with_checking_mountpoint(&self, name: &str) -> Result<Option<Arc<Dentry_>>> {
let dentry = self.find_dentry(name);
if let Some(dentry) = dentry.as_ref() {
if dentry.is_mountpoint() { if dentry.is_mountpoint() {
return_errno_with_message!(Errno::EBUSY, "dentry is mountpint"); return_errno_with_message!(Errno::EBUSY, "dentry is mountpint");
} }
} }
Ok(())
}
pub fn check_mountpoint_then_find(&self, name: &str) -> Result<Option<Arc<Dentry_>>> {
let dentry = if let Some(dentry) = self.dentries.get(name) {
if dentry.is_mountpoint() {
return_errno_with_message!(Errno::EBUSY, "dentry is mountpint");
}
Some(dentry.clone())
} else {
None
};
Ok(dentry) Ok(dentry)
} }
} }
@ -448,52 +454,50 @@ fn write_lock_children_on_two_dentries<'a>(
impl Dentry { impl Dentry {
/// Creates a new `Dentry` to represent the root directory of a file system. /// Creates a new `Dentry` to represent the root directory of a file system.
pub fn new_fs_root(mount_node: Arc<MountNode>) -> Arc<Self> { pub fn new_fs_root(mount_node: Arc<MountNode>) -> Self {
Self::new(mount_node.clone(), mount_node.root_dentry().clone()) let inner = mount_node.root_dentry().clone();
Self::new(mount_node, inner)
} }
/// Creates a new `Dentry` to represent the child directory of a file system. /// Creates a new `Dentry` to represent the child directory of a file system.
pub fn new_fs_child(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<Self>> { pub fn new_fs_child(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Self> {
let new_child_dentry = self.inner.create(name, type_, mode)?; let new_child_dentry = self.inner.create(name, type_, mode)?;
Ok(Self::new(self.mount_node.clone(), new_child_dentry.clone())) Ok(Self::new(self.mount_node.clone(), new_child_dentry))
} }
fn new(mount_node: Arc<MountNode>, inner: Arc<Dentry_>) -> Arc<Self> { fn new(mount_node: Arc<MountNode>, inner: Arc<Dentry_>) -> Self {
Arc::new_cyclic(|weak_self| Self { Self { mount_node, inner }
mount_node,
inner,
this: weak_self.clone(),
})
} }
/// Lookups the target `Dentry` given the `name`. /// Lookups the target `Dentry` given the `name`.
pub fn lookup(&self, name: &str) -> Result<Arc<Self>> { pub fn lookup(&self, name: &str) -> Result<Self> {
if self.inner.inode().type_() != InodeType::Dir { if self.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR); return_errno!(Errno::ENOTDIR);
} }
if !self.inner.inode().mode()?.is_executable() { if !self.mode()?.is_executable() {
return_errno!(Errno::EACCES); return_errno!(Errno::EACCES);
} }
if name.len() > NAME_MAX { if name.len() > NAME_MAX {
return_errno!(Errno::ENAMETOOLONG); return_errno!(Errno::ENAMETOOLONG);
} }
let dentry = match name { let target_dentry = match name {
"." => self.this(), "." => self.this(),
".." => self.effective_parent().unwrap_or_else(|| self.this()), ".." => self.effective_parent().unwrap_or_else(|| self.this()),
name => { name => {
let children_inner = self.inner.lookup_via_cache(name); let target_inner_opt = self.inner.lookup_via_cache(name);
match children_inner { match target_inner_opt {
Some(inner) => Self::new(self.mount_node().clone(), inner.clone()), Some(target_inner) => Self::new(self.mount_node.clone(), target_inner),
None => { None => {
let fs_inner = self.inner.lookup_via_fs(name)?; let target_inner = self.inner.lookup_via_fs(name)?;
Self::new(self.mount_node().clone(), fs_inner.clone()) Self::new(self.mount_node.clone(), target_inner)
} }
} }
} }
}; };
let dentry = dentry.get_top_dentry();
Ok(dentry) let target_dentry = target_dentry.get_top_dentry();
Ok(target_dentry)
} }
/// Gets the absolute path. /// Gets the absolute path.
@ -535,8 +539,8 @@ impl Dentry {
}; };
let parent_inner = Self::new( let parent_inner = Self::new(
self.mount_node.parent().unwrap().upgrade().unwrap().clone(), self.mount_node.parent().unwrap().upgrade().unwrap(),
self.mount_node.mountpoint_dentry().unwrap().clone(), self.mount_node.mountpoint_dentry().unwrap(),
); );
parent_inner.effective_name() parent_inner.effective_name()
} }
@ -545,18 +549,18 @@ impl Dentry {
/// ///
/// If it is the root of a mount, it will go up to the mountpoint /// If it is the root of a mount, it will go up to the mountpoint
/// to get the parent of the mountpoint recursively. /// to get the parent of the mountpoint recursively.
fn effective_parent(&self) -> Option<Arc<Self>> { fn effective_parent(&self) -> Option<Self> {
if !self.inner.is_root_of_mount() { if !self.inner.is_root_of_mount() {
return Some(Self::new( return Some(Self::new(
self.mount_node.clone(), self.mount_node.clone(),
self.inner.parent().unwrap().clone(), self.inner.parent().unwrap(),
)); ));
} }
let parent = self.mount_node.parent()?; let parent = self.mount_node.parent()?;
let mountpoint = self.mount_node.mountpoint_dentry()?; let mountpoint = self.mount_node.mountpoint_dentry()?;
let parent_dentry = Self::new(parent.upgrade().unwrap(), mountpoint.clone()); let parent_dentry = Self::new(parent.upgrade().unwrap(), mountpoint);
parent_dentry.effective_parent() parent_dentry.effective_parent()
} }
@ -567,15 +571,17 @@ impl Dentry {
/// For example, first `mount /dev/sda1 /mnt` and then `mount /dev/sda2 /mnt`. /// For example, first `mount /dev/sda1 /mnt` and then `mount /dev/sda2 /mnt`.
/// After the second mount is completed, the content of the first mount will be overridden. /// After the second mount is completed, the content of the first mount will be overridden.
/// We need to recursively obtain the top `Dentry`. /// We need to recursively obtain the top `Dentry`.
fn get_top_dentry(&self) -> Arc<Self> { fn get_top_dentry(self) -> Self {
if !self.inner.is_mountpoint() { if !self.inner.is_mountpoint() {
return self.this(); return self;
} }
match self.mount_node.get(self) {
match self.mount_node.get(&self) {
Some(child_mount) => { Some(child_mount) => {
Self::new(child_mount.clone(), child_mount.root_dentry().clone()).get_top_dentry() let inner = child_mount.root_dentry().clone();
Self::new(child_mount, inner).get_top_dentry()
} }
None => self.this(), None => self,
} }
} }
@ -594,14 +600,14 @@ impl Dentry {
/// ///
/// Returns the mounted child mount. /// Returns the mounted child mount.
pub fn mount(&self, fs: Arc<dyn FileSystem>) -> Result<Arc<MountNode>> { pub fn mount(&self, fs: Arc<dyn FileSystem>) -> Result<Arc<MountNode>> {
if self.inner.inode().type_() != InodeType::Dir { if self.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR); return_errno!(Errno::ENOTDIR);
} }
if self.effective_parent().is_none() { if self.effective_parent().is_none() {
return_errno_with_message!(Errno::EINVAL, "can not mount on root"); return_errno_with_message!(Errno::EINVAL, "can not mount on root");
} }
let child_mount = self.mount_node().mount(fs, &self.this())?; let child_mount = self.mount_node.mount(fs, &self.this())?;
self.set_mountpoint(child_mount.clone()); self.set_mountpoint(child_mount.clone());
Ok(child_mount) Ok(child_mount)
} }
@ -614,12 +620,11 @@ impl Dentry {
return_errno_with_message!(Errno::EINVAL, "not mounted"); return_errno_with_message!(Errno::EINVAL, "not mounted");
} }
let mount_node = self.mount_node.clone(); let Some(mountpoint_dentry) = self.mount_node.mountpoint_dentry() else {
let Some(mountpoint_dentry) = mount_node.mountpoint_dentry() else {
return_errno_with_message!(Errno::EINVAL, "cannot umount root mount"); return_errno_with_message!(Errno::EINVAL, "cannot umount root mount");
}; };
let mountpoint_mount_node = mount_node.parent().unwrap().upgrade().unwrap(); let mountpoint_mount_node = self.mount_node.parent().unwrap().upgrade().unwrap();
let mountpoint = Self::new(mountpoint_mount_node.clone(), mountpoint_dentry.clone()); let mountpoint = Self::new(mountpoint_mount_node.clone(), mountpoint_dentry.clone());
let child_mount = mountpoint_mount_node.unmount(&mountpoint)?; let child_mount = mountpoint_mount_node.unmount(&mountpoint)?;
@ -628,13 +633,13 @@ impl Dentry {
} }
/// Creates a `Dentry` by making an inode of the `type_` with the `mode`. /// Creates a `Dentry` by making an inode of the `type_` with the `mode`.
pub fn mknod(&self, name: &str, mode: InodeMode, type_: MknodType) -> Result<Arc<Self>> { pub fn mknod(&self, name: &str, mode: InodeMode, type_: MknodType) -> Result<Self> {
let inner = self.inner.mknod(name, mode, type_)?; let inner = self.inner.mknod(name, mode, type_)?;
Ok(Self::new(self.mount_node.clone(), inner.clone())) Ok(Self::new(self.mount_node.clone(), inner))
} }
/// Links a new name for the `Dentry`. /// Links a new name for the `Dentry`.
pub fn link(&self, old: &Arc<Self>, name: &str) -> Result<()> { pub fn link(&self, old: &Self, name: &str) -> Result<()> {
if !Arc::ptr_eq(&old.mount_node, &self.mount_node) { if !Arc::ptr_eq(&old.mount_node, &self.mount_node) {
return_errno_with_message!(Errno::EXDEV, "cannot cross mount"); return_errno_with_message!(Errno::EXDEV, "cannot cross mount");
} }
@ -652,7 +657,7 @@ impl Dentry {
} }
/// Renames a `Dentry` to the new `Dentry` by `rename()` the inner inode. /// Renames a `Dentry` to the new `Dentry` by `rename()` the inner inode.
pub fn rename(&self, old_name: &str, new_dir: &Arc<Self>, new_name: &str) -> Result<()> { pub fn rename(&self, old_name: &str, new_dir: &Self, new_name: &str) -> Result<()> {
if !Arc::ptr_eq(&self.mount_node, &new_dir.mount_node) { if !Arc::ptr_eq(&self.mount_node, &new_dir.mount_node) {
return_errno_with_message!(Errno::EXDEV, "cannot cross mount"); return_errno_with_message!(Errno::EXDEV, "cannot cross mount");
} }
@ -664,7 +669,7 @@ impl Dentry {
/// If `recursive` is true, it will bind mount the whole mount tree /// If `recursive` is true, it will bind mount the whole mount tree
/// to the destination `Dentry`. Otherwise, it will only bind mount /// to the destination `Dentry`. Otherwise, it will only bind mount
/// the root mount node. /// the root mount node.
pub fn bind_mount_to(&self, dst_dentry: &Arc<Self>, recursive: bool) -> Result<()> { pub fn bind_mount_to(&self, dst_dentry: &Self, recursive: bool) -> Result<()> {
let src_mount = self let src_mount = self
.mount_node .mount_node
.clone_mount_node_tree(&self.inner, recursive); .clone_mount_node_tree(&self.inner, recursive);
@ -672,8 +677,8 @@ impl Dentry {
Ok(()) Ok(())
} }
fn this(&self) -> Arc<Self> { fn this(&self) -> Self {
self.this.upgrade().unwrap() self.clone()
} }
/// Gets the mount node of current `Dentry`. /// Gets the mount node of current `Dentry`.

View File

@ -69,7 +69,7 @@ impl MountNode {
/// mountpoint. It is the fs's responsibility to ensure the data consistency. /// mountpoint. It is the fs's responsibility to ensure the data consistency.
/// ///
/// Return the mounted child mount. /// Return the mounted child mount.
pub fn mount(&self, fs: Arc<dyn FileSystem>, mountpoint: &Arc<Dentry>) -> Result<Arc<Self>> { pub fn mount(&self, fs: Arc<dyn FileSystem>, mountpoint: &Dentry) -> Result<Arc<Self>> {
if !Arc::ptr_eq(mountpoint.mount_node(), &self.this()) { if !Arc::ptr_eq(mountpoint.mount_node(), &self.this()) {
return_errno_with_message!(Errno::EINVAL, "mountpoint not belongs to this"); return_errno_with_message!(Errno::EINVAL, "mountpoint not belongs to this");
} }
@ -129,13 +129,13 @@ impl MountNode {
) -> Arc<Self> { ) -> Arc<Self> {
let new_root_mount = self.clone_mount_node(root_dentry); let new_root_mount = self.clone_mount_node(root_dentry);
if !recursive { if !recursive {
return new_root_mount.clone(); return new_root_mount;
} }
let mut stack = vec![self.this()]; let mut stack = vec![self.this()];
let mut new_stack = vec![new_root_mount.clone()]; let mut new_stack = vec![new_root_mount.clone()];
while let Some(old_mount) = stack.pop() { while let Some(old_mount) = stack.pop() {
let new_parent_mount = new_stack.pop().unwrap().clone(); let new_parent_mount = new_stack.pop().unwrap();
let old_children = old_mount.children.read(); let old_children = old_mount.children.read();
for old_child_mount in old_children.values() { for old_child_mount in old_children.values() {
let mountpoint_dentry = old_child_mount.mountpoint_dentry().unwrap(); let mountpoint_dentry = old_child_mount.mountpoint_dentry().unwrap();
@ -153,10 +153,11 @@ impl MountNode {
new_child_mount new_child_mount
.set_mountpoint_dentry(&old_child_mount.mountpoint_dentry().unwrap()); .set_mountpoint_dentry(&old_child_mount.mountpoint_dentry().unwrap());
stack.push(old_child_mount.clone()); stack.push(old_child_mount.clone());
new_stack.push(new_child_mount.clone()); new_stack.push(new_child_mount);
} }
} }
new_root_mount.clone()
new_root_mount
} }
/// Detaches the mount node from the parent mount node. /// Detaches the mount node from the parent mount node.
@ -171,7 +172,7 @@ impl MountNode {
} }
/// Attaches the mount node to the mountpoint. /// Attaches the mount node to the mountpoint.
fn attach_mount_node(&self, mountpoint: &Arc<Dentry>) { fn attach_mount_node(&self, mountpoint: &Dentry) {
let key = mountpoint.key(); let key = mountpoint.key();
mountpoint mountpoint
.mount_node() .mount_node()
@ -183,7 +184,7 @@ impl MountNode {
} }
/// Grafts the mount node tree to the mountpoint. /// Grafts the mount node tree to the mountpoint.
pub fn graft_mount_node_tree(&self, mountpoint: &Arc<Dentry>) -> Result<()> { pub fn graft_mount_node_tree(&self, mountpoint: &Dentry) -> Result<()> {
if mountpoint.type_() != InodeType::Dir { if mountpoint.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR); return_errno!(Errno::ENOTDIR);
} }

View File

@ -72,7 +72,7 @@ impl TryFrom<SocketAddr> for UnixSocketAddr {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(super) enum UnixSocketAddrBound { pub(super) enum UnixSocketAddrBound {
Path(Arc<str>, Arc<Dentry>), Path(Arc<str>, Dentry),
Abstract(Arc<AbstractHandle>), Abstract(Arc<AbstractHandle>),
} }

View File

@ -9,7 +9,7 @@ use crate::{
prelude::*, prelude::*,
}; };
pub fn lookup_socket_file(path: &str) -> Result<Arc<Dentry>> { pub fn lookup_socket_file(path: &str) -> Result<Dentry> {
let dentry = { let dentry = {
let current = current!(); let current = current!();
let fs = current.fs().read(); let fs = current.fs().read();
@ -31,7 +31,7 @@ pub fn lookup_socket_file(path: &str) -> Result<Arc<Dentry>> {
Ok(dentry) Ok(dentry)
} }
pub fn create_socket_file(path: &str) -> Result<Arc<Dentry>> { pub fn create_socket_file(path: &str) -> Result<Dentry> {
let (parent_pathname, file_name) = split_path(path); let (parent_pathname, file_name) = split_path(path);
let parent = { let parent = {

View File

@ -34,7 +34,7 @@ use crate::{
pub fn load_elf_to_vm( pub fn load_elf_to_vm(
process_vm: &ProcessVm, process_vm: &ProcessVm,
file_header: &[u8], file_header: &[u8],
elf_file: Arc<Dentry>, elf_file: Dentry,
fs_resolver: &FsResolver, fs_resolver: &FsResolver,
argv: Vec<CString>, argv: Vec<CString>,
envp: Vec<CString>, envp: Vec<CString>,
@ -85,7 +85,7 @@ fn lookup_and_parse_ldso(
elf: &Elf, elf: &Elf,
file_header: &[u8], file_header: &[u8],
fs_resolver: &FsResolver, fs_resolver: &FsResolver,
) -> Result<Option<(Arc<Dentry>, Elf)>> { ) -> Result<Option<(Dentry, Elf)>> {
let ldso_file = { let ldso_file = {
let Some(ldso_path) = elf.ldso_path(file_header)? else { let Some(ldso_path) = elf.ldso_path(file_header)? else {
return Ok(None); return Ok(None);
@ -112,7 +112,7 @@ fn load_ldso(root_vmar: &Vmar<Full>, ldso_file: &Dentry, ldso_elf: &Elf) -> Resu
fn init_and_map_vmos( fn init_and_map_vmos(
process_vm: &ProcessVm, process_vm: &ProcessVm,
ldso: Option<(Arc<Dentry>, Elf)>, ldso: Option<(Dentry, Elf)>,
parsed_elf: &Elf, parsed_elf: &Elf,
elf_file: &Dentry, elf_file: &Dentry,
) -> Result<(Vaddr, AuxVec)> { ) -> Result<(Vaddr, AuxVec)> {

View File

@ -25,7 +25,7 @@ use crate::{
/// because the interpreter is usually an elf binary(e.g., /bin/bash) /// because the interpreter is usually an elf binary(e.g., /bin/bash)
pub fn load_program_to_vm( pub fn load_program_to_vm(
process_vm: &ProcessVm, process_vm: &ProcessVm,
elf_file: Arc<Dentry>, elf_file: Dentry,
argv: Vec<CString>, argv: Vec<CString>,
envp: Vec<CString>, envp: Vec<CString>,
fs_resolver: &FsResolver, fs_resolver: &FsResolver,
@ -68,7 +68,7 @@ pub fn load_program_to_vm(
Ok((abs_path, elf_load_info)) Ok((abs_path, elf_load_info))
} }
pub fn check_executable_file(dentry: &Arc<Dentry>) -> Result<()> { pub fn check_executable_file(dentry: &Dentry) -> Result<()> {
if dentry.type_().is_directory() { if dentry.type_().is_directory() {
return_errno_with_message!(Errno::EISDIR, "the file is a directory"); return_errno_with_message!(Errno::EISDIR, "the file is a directory");
} }

View File

@ -58,7 +58,7 @@ fn lookup_executable_file(
filename: String, filename: String,
flags: OpenFlags, flags: OpenFlags,
ctx: &Context, ctx: &Context,
) -> Result<Arc<Dentry>> { ) -> Result<Dentry> {
let fs_resolver = ctx.process.fs().read(); let fs_resolver = ctx.process.fs().read();
let dentry = if flags.contains(OpenFlags::AT_EMPTY_PATH) && filename.is_empty() { let dentry = if flags.contains(OpenFlags::AT_EMPTY_PATH) && filename.is_empty() {
fs_resolver.lookup_from_fd(dfd) fs_resolver.lookup_from_fd(dfd)
@ -79,7 +79,7 @@ fn lookup_executable_file(
} }
fn do_execve( fn do_execve(
elf_file: Arc<Dentry>, elf_file: Dentry,
argv_ptr_ptr: Vaddr, argv_ptr_ptr: Vaddr,
envp_ptr_ptr: Vaddr, envp_ptr_ptr: Vaddr,
ctx: &Context, ctx: &Context,
@ -193,7 +193,7 @@ fn read_cstring_vec(
fn set_uid_from_elf( fn set_uid_from_elf(
current: &Process, current: &Process,
credentials: &Credentials<WriteOp>, credentials: &Credentials<WriteOp>,
elf_file: &Arc<Dentry>, elf_file: &Dentry,
) -> Result<()> { ) -> Result<()> {
if elf_file.mode()?.has_set_uid() { if elf_file.mode()?.has_set_uid() {
let uid = elf_file.owner()?; let uid = elf_file.owner()?;
@ -211,7 +211,7 @@ fn set_uid_from_elf(
fn set_gid_from_elf( fn set_gid_from_elf(
current: &Process, current: &Process,
credentials: &Credentials<WriteOp>, credentials: &Credentials<WriteOp>,
elf_file: &Arc<Dentry>, elf_file: &Dentry,
) -> Result<()> { ) -> Result<()> {
if elf_file.mode()?.has_set_gid() { if elf_file.mode()?.has_set_gid() {
let gid = elf_file.group()?; let gid = elf_file.group()?;

View File

@ -83,7 +83,7 @@ fn do_remount() -> Result<()> {
/// Such as use user command `mount --rbind src dst`. /// Such as use user command `mount --rbind src dst`.
fn do_bind_mount( fn do_bind_mount(
src_name: CString, src_name: CString,
dst_dentry: Arc<Dentry>, dst_dentry: Dentry,
recursive: bool, recursive: bool,
ctx: &Context, ctx: &Context,
) -> Result<()> { ) -> Result<()> {
@ -109,7 +109,7 @@ fn do_change_type() -> Result<()> {
} }
/// Move a mount from src location to dst location. /// Move a mount from src location to dst location.
fn do_move_mount_old(src_name: CString, dst_dentry: Arc<Dentry>, ctx: &Context) -> Result<()> { fn do_move_mount_old(src_name: CString, dst_dentry: Dentry, ctx: &Context) -> Result<()> {
let src_dentry = { let src_dentry = {
let src_name = src_name.to_string_lossy(); let src_name = src_name.to_string_lossy();
if src_name.is_empty() { if src_name.is_empty() {
@ -135,7 +135,7 @@ fn do_move_mount_old(src_name: CString, dst_dentry: Arc<Dentry>, ctx: &Context)
fn do_new_mount( fn do_new_mount(
devname: CString, devname: CString,
fs_type: Vaddr, fs_type: Vaddr,
target_dentry: Arc<Dentry>, target_dentry: Dentry,
ctx: &Context, ctx: &Context,
) -> Result<()> { ) -> Result<()> {
if target_dentry.type_() != InodeType::Dir { if target_dentry.type_() != InodeType::Dir {

View File

@ -109,7 +109,7 @@ struct Utimbuf {
modtime: i64, modtime: i64,
} }
fn vfs_utimes(dentry: &Arc<Dentry>, times: Option<TimeSpecPair>) -> Result<SyscallReturn> { fn vfs_utimes(dentry: &Dentry, times: Option<TimeSpecPair>) -> Result<SyscallReturn> {
let (atime, mtime, ctime) = match times { let (atime, mtime, ctime) = match times {
Some(times) => { Some(times) => {
if !times.atime.is_valid() || !times.mtime.is_valid() { if !times.atime.is_valid() || !times.mtime.is_valid() {