mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-10 13:56:48 +00:00
Rewrite dentry cache
This commit is contained in:
parent
a0f757d37c
commit
d0bcd2491f
@ -1,94 +1,91 @@
|
||||
use crate::prelude::*;
|
||||
use alloc::string::String;
|
||||
use core::time::Duration;
|
||||
use spin::RwLockWriteGuard;
|
||||
use spin::MutexGuard;
|
||||
|
||||
use super::{InodeMode, InodeType, Metadata, Vnode, NAME_MAX};
|
||||
|
||||
lazy_static! {
|
||||
static ref DCACHE: Mutex<BTreeMap<DentryKey, Arc<Dentry>>> = Mutex::new(BTreeMap::new());
|
||||
}
|
||||
|
||||
/// The dentry cache to accelerate path lookup
|
||||
pub struct Dentry {
|
||||
inner: RwLock<Dentry_>,
|
||||
vnode: Vnode,
|
||||
}
|
||||
|
||||
struct Dentry_ {
|
||||
name: String,
|
||||
name_and_parent: RwLock<(String, Option<Arc<Dentry>>)>,
|
||||
this: Weak<Dentry>,
|
||||
parent: Option<Weak<Dentry>>,
|
||||
children: BTreeMap<String, Arc<Dentry>>,
|
||||
}
|
||||
|
||||
impl Dentry_ {
|
||||
pub fn new(name: &str, parent: Option<Weak<Dentry>>) -> Self {
|
||||
Self {
|
||||
name: String::from(name),
|
||||
this: Weak::default(),
|
||||
parent,
|
||||
children: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
children: Mutex<Children>,
|
||||
}
|
||||
|
||||
impl Dentry {
|
||||
/// Create a new dentry cache with root inode
|
||||
pub fn new_root(root_vnode: Vnode) -> Arc<Self> {
|
||||
let root = Self::new("/", root_vnode, None);
|
||||
let root = Self::new("/", None, root_vnode);
|
||||
DCACHE.lock().insert(root.key(), root.clone());
|
||||
root
|
||||
}
|
||||
|
||||
/// Internal constructor
|
||||
fn new(name: &str, vnode: Vnode, parent: Option<Weak<Dentry>>) -> Arc<Self> {
|
||||
let dentry = {
|
||||
let inner = RwLock::new(Dentry_::new(name, parent));
|
||||
Arc::new(Self { inner, vnode })
|
||||
};
|
||||
dentry.inner.write().this = Arc::downgrade(&dentry);
|
||||
dentry
|
||||
fn new(name: &str, parent: Option<Arc<Dentry>>, vnode: Vnode) -> Arc<Self> {
|
||||
Arc::new_cyclic(|weak_self| Self {
|
||||
vnode,
|
||||
name_and_parent: RwLock::new((String::from(name), parent)),
|
||||
this: weak_self.clone(),
|
||||
children: Mutex::new(Children::new()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the name of Dentry.
|
||||
pub fn name(&self) -> String {
|
||||
self.inner.read().name.clone()
|
||||
self.name_and_parent.read().0.clone()
|
||||
}
|
||||
|
||||
fn set_name(&self, name: &str) {
|
||||
self.inner.write().name = String::from(name);
|
||||
/// Get the parent dentry.
|
||||
///
|
||||
/// Returns None if it is root dentry.
|
||||
pub fn parent(&self) -> Option<Arc<Dentry>> {
|
||||
self.name_and_parent.read().1.clone()
|
||||
}
|
||||
|
||||
fn set_name_and_parent(&self, name: &str, parent: Option<Arc<Dentry>>) {
|
||||
let mut name_and_parent = self.name_and_parent.write();
|
||||
name_and_parent.0 = String::from(name);
|
||||
name_and_parent.1 = parent;
|
||||
}
|
||||
|
||||
fn this(&self) -> Arc<Dentry> {
|
||||
self.inner.read().this.upgrade().unwrap()
|
||||
self.this.upgrade().unwrap()
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<Arc<Dentry>> {
|
||||
self.inner
|
||||
.read()
|
||||
.parent
|
||||
.as_ref()
|
||||
.map(|p| p.upgrade().unwrap())
|
||||
}
|
||||
|
||||
fn set_parent(&self, parent: &Arc<Dentry>) {
|
||||
self.inner.write().parent = Some(Arc::downgrade(parent));
|
||||
fn key(&self) -> DentryKey {
|
||||
let parent = self.parent().unwrap_or(self.this());
|
||||
DentryKey::new(&self.name_and_parent.read().0, &parent)
|
||||
}
|
||||
|
||||
pub(in crate::fs) fn vnode(&self) -> &Vnode {
|
||||
&self.vnode
|
||||
}
|
||||
|
||||
/// Create a dentry by making inode.
|
||||
pub fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<Self>> {
|
||||
if self.vnode.inode_type() != InodeType::Dir {
|
||||
return_errno!(Errno::ENOTDIR);
|
||||
}
|
||||
let mut inner = self.inner.write();
|
||||
if inner.children.get(name).is_some() {
|
||||
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, type_, mode)?;
|
||||
Dentry::new(name, vnode, Some(inner.this.clone()))
|
||||
let dentry = Dentry::new(name, Some(self.this()), vnode);
|
||||
children.insert_dentry(&dentry);
|
||||
dentry
|
||||
};
|
||||
inner.children.insert(String::from(name), child.clone());
|
||||
Ok(child)
|
||||
}
|
||||
|
||||
/// Lookup a dentry.
|
||||
pub fn lookup(&self, name: &str) -> Result<Arc<Self>> {
|
||||
if self.vnode.inode_type() != InodeType::Dir {
|
||||
return_errno!(Errno::ENOTDIR);
|
||||
@ -101,55 +98,60 @@ impl Dentry {
|
||||
"." => self.this(),
|
||||
".." => self.parent().unwrap_or(self.this()),
|
||||
name => {
|
||||
let mut inner = self.inner.write();
|
||||
if let Some(dentry) = inner.children.get(name) {
|
||||
dentry.clone()
|
||||
} else {
|
||||
let vnode = self.vnode.lookup(name)?;
|
||||
let dentry = Dentry::new(name, vnode, Some(inner.this.clone()));
|
||||
inner.children.insert(String::from(name), dentry.clone());
|
||||
dentry
|
||||
let mut children = self.children.lock();
|
||||
match children.find_dentry(name) {
|
||||
Some(dentry) => dentry.clone(),
|
||||
None => {
|
||||
let vnode = self.vnode.lookup(name)?;
|
||||
let dentry = Dentry::new(name, Some(self.this()), vnode);
|
||||
children.insert_dentry(&dentry);
|
||||
dentry
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(dentry)
|
||||
}
|
||||
|
||||
/// Link a new name for the dentry by linking inode.
|
||||
pub fn link(&self, old: &Arc<Self>, name: &str) -> Result<()> {
|
||||
if self.vnode.inode_type() != InodeType::Dir {
|
||||
return_errno!(Errno::ENOTDIR);
|
||||
}
|
||||
let mut inner = self.inner.write();
|
||||
if inner.children.get(name).is_some() {
|
||||
let mut children = self.children.lock();
|
||||
if children.find_dentry(name).is_some() {
|
||||
return_errno!(Errno::EEXIST);
|
||||
}
|
||||
let target_vnode = old.vnode();
|
||||
self.vnode.link(target_vnode, name)?;
|
||||
let new_dentry = Self::new(name, target_vnode.clone(), Some(inner.this.clone()));
|
||||
inner.children.insert(String::from(name), new_dentry);
|
||||
let old_vnode = old.vnode();
|
||||
self.vnode.link(old_vnode, name)?;
|
||||
let dentry = Dentry::new(name, Some(self.this()), old_vnode.clone());
|
||||
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 inner = self.inner.write();
|
||||
let mut children = self.children.lock();
|
||||
self.vnode.unlink(name)?;
|
||||
inner.children.remove(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 inner = self.inner.write();
|
||||
let mut children = self.children.lock();
|
||||
self.vnode.rmdir(name)?;
|
||||
inner.children.remove(name);
|
||||
children.delete_dentry(name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read symbolic link.
|
||||
pub fn read_link(&self) -> Result<String> {
|
||||
if self.vnode.inode_type() != InodeType::SymLink {
|
||||
return_errno!(Errno::EINVAL);
|
||||
@ -157,6 +159,7 @@ impl Dentry {
|
||||
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);
|
||||
@ -164,6 +167,7 @@ impl Dentry {
|
||||
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<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");
|
||||
@ -178,69 +182,78 @@ impl Dentry {
|
||||
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 = self.vnode.lookup(old_name)?;
|
||||
Dentry::new(old_name, vnode, Some(inner.this.clone()))
|
||||
};
|
||||
let mut children = self.children.lock();
|
||||
self.vnode.rename(old_name, &self.vnode, new_name)?;
|
||||
inner.children.remove(old_name);
|
||||
dentry.set_name(new_name);
|
||||
inner.children.insert(String::from(new_name), dentry);
|
||||
match children.find_dentry(old_name) {
|
||||
Some(dentry) => {
|
||||
children.delete_dentry(old_name);
|
||||
dentry.set_name_and_parent(new_name, Some(self.this()));
|
||||
children.insert_dentry(&dentry);
|
||||
}
|
||||
None => {
|
||||
children.delete_dentry(new_name);
|
||||
}
|
||||
}
|
||||
} 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 = self.vnode.lookup(old_name)?;
|
||||
Dentry::new(old_name, vnode, Some(self_inner.this.clone()))
|
||||
};
|
||||
let (mut self_children, mut new_dir_children) =
|
||||
write_lock_children_on_two_dentries(&self, &new_dir);
|
||||
self.vnode.rename(old_name, &new_dir.vnode, 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);
|
||||
match self_children.find_dentry(old_name) {
|
||||
Some(dentry) => {
|
||||
self_children.delete_dentry(old_name);
|
||||
dentry.set_name_and_parent(new_name, Some(new_dir.this()));
|
||||
new_dir_children.insert_dentry(&dentry);
|
||||
}
|
||||
None => {
|
||||
new_dir_children.delete_dentry(new_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub fn abs_path(&self) -> String {
|
||||
let mut path = self.name();
|
||||
let mut dentry = self.this();
|
||||
@ -267,19 +280,69 @@ impl Dentry {
|
||||
}
|
||||
}
|
||||
|
||||
fn write_lock_two_dentries<'a>(
|
||||
struct Children {
|
||||
inner: BTreeMap<String, Weak<Dentry>>,
|
||||
}
|
||||
|
||||
impl Children {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_dentry(&mut self, dentry: &Arc<Dentry>) {
|
||||
DCACHE.lock().insert(dentry.key(), dentry.clone());
|
||||
self.inner.insert(dentry.name(), Arc::downgrade(&dentry));
|
||||
}
|
||||
|
||||
pub fn delete_dentry(&mut self, name: &str) -> Option<Arc<Dentry>> {
|
||||
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<Arc<Dentry>> {
|
||||
if let Some(dentry) = self.inner.get(name) {
|
||||
dentry.upgrade().or_else(|| {
|
||||
self.inner.remove(name);
|
||||
None
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash, PartialOrd, Ord, Eq, PartialEq)]
|
||||
struct DentryKey {
|
||||
name: String,
|
||||
parent_ptr: usize,
|
||||
}
|
||||
|
||||
impl DentryKey {
|
||||
pub fn new(name: &str, parent: &Arc<Dentry>) -> Self {
|
||||
Self {
|
||||
name: String::from(name),
|
||||
parent_ptr: Arc::as_ptr(parent) as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_lock_children_on_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();
|
||||
) -> (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.inner.write();
|
||||
let this = this.inner.write();
|
||||
let other = other.children.lock();
|
||||
let this = this.children.lock();
|
||||
(this, other)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user