use crate::fs::device::Device; use crate::prelude::*; use alloc::string::String; use core::sync::atomic::{AtomicU32, Ordering}; use core::time::Duration; use super::{FileSystem, InodeMode, InodeType, Metadata, MountNode, Vnode, NAME_MAX}; lazy_static! { static ref DCACHE: Mutex>> = Mutex::new(BTreeMap::new()); } /// The dentry cache to accelerate path lookup pub struct Dentry { vnode: Vnode, name_and_parent: RwLock)>>, this: Weak, children: Mutex, mount_node: Weak, flags: AtomicU32, } impl Dentry { /// Create a new root dentry with the giving vnode and mount node. /// /// It is been created during the construction of MountNode struct. The MountNode /// struct holds an arc reference to this root dentry, while this dentry holds a /// weak reference to the MountNode struct. pub(super) fn new_root(vnode: Vnode, mount: Weak) -> Arc { let root = Self::new(vnode, DentryOptions::Root(mount)); DCACHE.lock().insert(root.key(), root.clone()); root } /// Internal constructor. fn new(vnode: Vnode, options: DentryOptions) -> Arc { Arc::new_cyclic(|weak_self| Self { vnode, mount_node: match &options { DentryOptions::Root(mount) => mount.clone(), DentryOptions::Leaf(name_and_parent) => name_and_parent.1.mount_node.clone(), }, flags: AtomicU32::new(DentryFlags::empty().bits()), name_and_parent: match options { DentryOptions::Leaf(name_and_parent) => RwLock::new(Some(name_and_parent)), _ => RwLock::new(None), }, this: weak_self.clone(), children: Mutex::new(Children::new()), }) } /// Get the overlaid dentry of self. /// /// It will jump into the child mount if it is a mountpoint. fn overlaid_dentry(&self) -> Arc { if !self.is_mountpoint() { return self.this(); } match self.mount_node().get(&self) { Some(child_mount) => child_mount.root_dentry().overlaid_dentry(), None => self.this(), } } /// Get the name of dentry. /// /// Returns "/" if it is a root dentry. fn name(&self) -> String { match self.name_and_parent.read().as_ref() { Some(name_and_parent) => name_and_parent.0.clone(), None => String::from("/"), } } /// Get the effective name of dentry. /// /// If it is the root of mount, it will go up to the mountpoint to get the name /// of the mountpoint recursively. fn effective_name(&self) -> String { if !self.is_root_of_mount() { return self.name(); } match self.mount_node().mountpoint_dentry() { Some(self_mountpoint) => self_mountpoint.effective_name(), None => self.name(), } } /// Get the parent. /// /// Returns None if it is root dentry. fn parent(&self) -> Option> { self.name_and_parent .read() .as_ref() .map(|name_and_parent| name_and_parent.1.clone()) } /// Get the effective parent of dentry. /// /// If it is the root of mount, it will go up to the mountpoint to get the parent /// of the mountpoint recursively. fn effective_parent(&self) -> Option> { if !self.is_root_of_mount() { return self.parent(); } match self.mount_node().mountpoint_dentry() { Some(self_mountpoint) => self_mountpoint.effective_parent(), None => self.parent(), } } fn set_name_and_parent(&self, name: &str, parent: Arc) { let mut name_and_parent = self.name_and_parent.write(); *name_and_parent = Some((String::from(name), parent)); } /// Get the arc reference to self. fn this(&self) -> Arc { self.this.upgrade().unwrap() } /// Get the DentryKey. pub fn key(&self) -> DentryKey { DentryKey::new(&self) } /// Get the vnode. pub fn vnode(&self) -> &Vnode { &self.vnode } /// Get the DentryFlags. fn flags(&self) -> DentryFlags { let flags = self.flags.load(Ordering::Relaxed); DentryFlags::from_bits(flags).unwrap() } fn is_mountpoint(&self) -> bool { self.flags().contains(DentryFlags::MOUNTED) } fn set_mountpoint(&self) { self.flags .fetch_or(DentryFlags::MOUNTED.bits(), Ordering::Release); } fn clear_mountpoint(&self) { self.flags .fetch_and(!(DentryFlags::MOUNTED.bits()), Ordering::Release); } /// Currently, the root dentry of a fs is the root of a mount. fn is_root_of_mount(&self) -> bool { self.name_and_parent.read().as_ref().is_none() } /// Get the mount node which the dentry belongs to. pub fn mount_node(&self) -> Arc { self.mount_node.upgrade().unwrap() } /// Create a dentry by making inode. pub fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result> { if self.vnode.inode_type() != InodeType::Dir { return_errno!(Errno::ENOTDIR); } let mut children = self.children.lock(); if children.find_dentry(name).is_some() { return_errno!(Errno::EEXIST); } let child = { let vnode = self.vnode.create(name, type_, mode)?; let dentry = Self::new( vnode, DentryOptions::Leaf((String::from(name), self.this())), ); children.insert_dentry(&dentry); dentry }; Ok(child) } /// Create a dentry by making a device inode. pub fn mknod(&self, name: &str, mode: InodeMode, device: Arc) -> Result> { if self.vnode.inode_type() != InodeType::Dir { return_errno!(Errno::ENOTDIR); } let mut children = self.children.lock(); if children.find_dentry(name).is_some() { return_errno!(Errno::EEXIST); } let child = { let vnode = self.vnode.mknod(name, mode, device)?; let dentry = Self::new( vnode, DentryOptions::Leaf((String::from(name), self.this())), ); children.insert_dentry(&dentry); dentry }; Ok(child) } /// Lookup a dentry. pub fn lookup(&self, name: &str) -> Result> { if self.vnode.inode_type() != InodeType::Dir { return_errno!(Errno::ENOTDIR); } if !self.vnode.inode_mode().is_executable() { return_errno!(Errno::EACCES); } if name.len() > NAME_MAX { return_errno!(Errno::ENAMETOOLONG); } let dentry = match name { "." => self.this(), ".." => self.effective_parent().unwrap_or(self.this()), name => { let mut children = self.children.lock(); match children.find_dentry(name) { Some(dentry) => dentry.overlaid_dentry(), None => { let vnode = self.vnode.lookup(name)?; let dentry = Self::new( vnode, DentryOptions::Leaf((String::from(name), self.this())), ); children.insert_dentry(&dentry); dentry } } } }; Ok(dentry) } /// Link a new name for the dentry by linking inode. pub fn link(&self, old: &Arc, name: &str) -> Result<()> { if self.vnode.inode_type() != InodeType::Dir { return_errno!(Errno::ENOTDIR); } let mut children = self.children.lock(); if children.find_dentry(name).is_some() { return_errno!(Errno::EEXIST); } if !Arc::ptr_eq(&old.mount_node(), &self.mount_node()) { return_errno_with_message!(Errno::EXDEV, "cannot cross mount"); } let old_vnode = old.vnode(); self.vnode.link(old_vnode, name)?; let dentry = Self::new( old_vnode.clone(), DentryOptions::Leaf((String::from(name), self.this())), ); children.insert_dentry(&dentry); Ok(()) } /// Delete a dentry by unlinking inode. pub fn unlink(&self, name: &str) -> Result<()> { if self.vnode.inode_type() != InodeType::Dir { return_errno!(Errno::ENOTDIR); } let mut children = self.children.lock(); let _ = children.find_dentry_with_checking_mountpoint(name)?; self.vnode.unlink(name)?; children.delete_dentry(name); Ok(()) } /// Delete a directory dentry by rmdiring inode. pub fn rmdir(&self, name: &str) -> Result<()> { if self.vnode.inode_type() != InodeType::Dir { return_errno!(Errno::ENOTDIR); } let mut children = self.children.lock(); let _ = children.find_dentry_with_checking_mountpoint(name)?; self.vnode.rmdir(name)?; children.delete_dentry(name); Ok(()) } /// Read symbolic link. pub fn read_link(&self) -> Result { if self.vnode.inode_type() != InodeType::SymLink { return_errno!(Errno::EINVAL); } self.vnode.read_link() } /// Write symbolic link. pub fn write_link(&self, target: &str) -> Result<()> { if self.vnode.inode_type() != InodeType::SymLink { return_errno!(Errno::EINVAL); } self.vnode.write_link(target) } /// Rename a dentry to the new dentry by renaming inode. pub fn rename(&self, old_name: &str, new_dir: &Arc, 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_type() != InodeType::Dir || new_dir.vnode.inode_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 children = self.children.lock(); let old_dentry = children.find_dentry_with_checking_mountpoint(old_name)?; let _ = children.find_dentry_with_checking_mountpoint(new_name)?; self.vnode.rename(old_name, &self.vnode, new_name)?; match old_dentry.as_ref() { Some(dentry) => { children.delete_dentry(old_name); dentry.set_name_and_parent(new_name, self.this()); children.insert_dentry(&dentry); } None => { children.delete_dentry(new_name); } } } else { // Self and new_dir are different Dentry if !Arc::ptr_eq(&self.mount_node(), &new_dir.mount_node()) { return_errno_with_message!(Errno::EXDEV, "cannot cross mount"); } let (mut self_children, mut new_dir_children) = write_lock_children_on_two_dentries(&self, &new_dir); let old_dentry = self_children.find_dentry_with_checking_mountpoint(old_name)?; let _ = new_dir_children.find_dentry_with_checking_mountpoint(new_name)?; self.vnode.rename(old_name, &new_dir.vnode, new_name)?; match old_dentry.as_ref() { Some(dentry) => { self_children.delete_dentry(old_name); dentry.set_name_and_parent(new_name, new_dir.this()); new_dir_children.insert_dentry(&dentry); } None => { new_dir_children.delete_dentry(new_name); } } } Ok(()) } /// Mount the fs on this dentry. It will make this dentry to be a mountpoint. /// /// If the given mountpoint has already been mounted, then its mounted child mount /// will be updated. /// The root dentry cannot be mounted. /// /// Return the mounted child mount. pub fn mount(&self, fs: Arc) -> Result> { if self.vnode.inode_type() != InodeType::Dir { return_errno!(Errno::ENOTDIR); } if self.effective_parent().is_none() { return_errno_with_message!(Errno::EINVAL, "can not mount on root"); } let child_mount = self.mount_node().mount(fs, &self.this())?; self.set_mountpoint(); Ok(child_mount) } /// Unmount and return the mounted child mount. /// /// Note that the root mount cannot be unmounted. pub fn umount(&self) -> Result> { if !self.is_root_of_mount() { return_errno_with_message!(Errno::EINVAL, "not mounted"); } let mount_node = self.mount_node(); let Some(mountpoint) = mount_node.mountpoint_dentry() else { return_errno_with_message!(Errno::EINVAL, "cannot umount root mount"); }; let child_mount = mountpoint.mount_node().umount(mountpoint)?; mountpoint.clear_mountpoint(); Ok(child_mount) } /// Get the filesystem the inode belongs to pub fn fs(&self) -> Arc { self.vnode.fs() } /// Get the inode metadata pub fn inode_metadata(&self) -> Metadata { self.vnode.metadata() } /// Get the inode type pub fn inode_type(&self) -> InodeType { self.vnode.inode_type() } /// Get the inode permission mode pub fn inode_mode(&self) -> InodeMode { self.vnode.inode_mode() } /// Set the inode permission mode pub fn set_inode_mode(&self, mode: InodeMode) { self.vnode.set_inode_mode(mode) } /// Get the inode length pub fn inode_len(&self) -> usize { self.vnode.len() } /// Get the access timestamp pub fn atime(&self) -> Duration { self.vnode.atime() } /// Set the access timestamp pub fn set_atime(&self, time: Duration) { self.vnode.set_atime(time) } /// Get the modified timestamp pub fn mtime(&self) -> Duration { self.vnode.mtime() } /// Set the modified timestamp pub fn set_mtime(&self, time: Duration) { self.vnode.set_mtime(time) } /// Get the absolute path. /// /// It will resolve the mountpoint automatically. pub fn abs_path(&self) -> String { let mut path = self.effective_name(); let mut dentry = self.this(); loop { match dentry.effective_parent() { None => break, Some(parent_dentry) => { path = { let parent_name = parent_dentry.effective_name(); if parent_name != "/" { parent_name + "/" + &path } else { parent_name + &path } }; dentry = parent_dentry; } } } debug_assert!(path.starts_with("/")); path } } /// DentryKey is the unique identifier for Dentry in DCACHE. /// /// For none-root dentries, it uses self's name and parent's pointer to form the key, /// meanwhile, the root dentry uses "/" and self's pointer to form the key. #[derive(Clone, Hash, PartialOrd, Ord, Eq, PartialEq)] pub struct DentryKey { name: String, parent_ptr: usize, } impl DentryKey { /// Form the DentryKey for the dentry. pub fn new(dentry: &Dentry) -> Self { let (name, parent) = match dentry.name_and_parent.read().as_ref() { Some(name_and_parent) => name_and_parent.clone(), None => (String::from("/"), dentry.this()), }; Self { name, parent_ptr: Arc::as_ptr(&parent) as usize, } } } bitflags! { struct DentryFlags: u32 { const MOUNTED = 1 << 0; } } enum DentryOptions { Root(Weak), Leaf((String, Arc)), } struct Children { inner: BTreeMap>, } impl Children { pub fn new() -> Self { Self { inner: BTreeMap::new(), } } pub fn insert_dentry(&mut self, dentry: &Arc) { if dentry.vnode().is_dentry_cacheable() { DCACHE.lock().insert(dentry.key(), dentry.clone()); } self.inner.insert(dentry.name(), Arc::downgrade(dentry)); } pub fn delete_dentry(&mut self, name: &str) -> Option> { self.inner .remove(name) .and_then(|d| d.upgrade()) .and_then(|d| DCACHE.lock().remove(&d.key())) } pub fn find_dentry(&mut self, name: &str) -> Option> { if let Some(dentry) = self.inner.get(name) { dentry.upgrade().or_else(|| { self.inner.remove(name); None }) } else { None } } pub fn find_dentry_with_checking_mountpoint( &mut self, name: &str, ) -> Result>> { let dentry = self.find_dentry(name); if let Some(dentry) = dentry.as_ref() { if dentry.is_mountpoint() { return_errno_with_message!(Errno::EBUSY, "dentry is mountpint"); } } Ok(dentry) } } fn write_lock_children_on_two_dentries<'a>( this: &'a Dentry, other: &'a Dentry, ) -> (MutexGuard<'a, Children>, MutexGuard<'a, Children>) { let this_key = this.key(); let other_key = other.key(); if this_key < other_key { let this = this.children.lock(); let other = other.children.lock(); (this, other) } else { let other = other.children.lock(); let this = this.children.lock(); (this, other) } }