diff --git a/src/services/libs/jinux-std/src/fs/ramfs/fs.rs b/src/services/libs/jinux-std/src/fs/ramfs/fs.rs index 9b098f3ed..982a42d19 100644 --- a/src/services/libs/jinux-std/src/fs/ramfs/fs.rs +++ b/src/services/libs/jinux-std/src/fs/ramfs/fs.rs @@ -9,7 +9,8 @@ use spin::{RwLock, RwLockWriteGuard}; use super::*; use crate::fs::utils::{ - DirentVisitor, FileSystem, FsFlags, Inode, InodeMode, InodeType, IoctlCmd, Metadata, SuperBlock, + DirEntryVec, DirentVisitor, FileSystem, FsFlags, Inode, InodeMode, InodeType, IoctlCmd, + Metadata, SuperBlock, }; pub struct RamFS { @@ -159,7 +160,7 @@ impl Inner { } struct DirEntry { - children: LinkedList<(Str256, Arc)>, + children: DirEntryVec<(Str256, Arc)>, this: Weak, parent: Weak, } @@ -167,7 +168,7 @@ struct DirEntry { impl DirEntry { fn new() -> Self { Self { - children: LinkedList::new(), + children: DirEntryVec::new(), this: Weak::default(), parent: Weak::default(), } @@ -200,26 +201,28 @@ impl DirEntry { Some((1, self.parent.upgrade().unwrap())) } else { self.children - .iter() - .enumerate() - .find(|(idx, (child, inode))| child == &Str256::from(name)) + .idxes_and_entries() + .find(|(_, (child, _))| child == &Str256::from(name)) .map(|(idx, (_, inode))| (idx + 2, inode.clone())) } } fn append_entry(&mut self, name: &str, inode: Arc) { - self.children.push_back((Str256::from(name), inode)) + self.children.put((Str256::from(name), inode)) } - fn remove_entry(&mut self, idx: usize) -> (Str256, Arc) { + fn remove_entry(&mut self, idx: usize) -> Option<(Str256, Arc)> { assert!(idx >= 2); self.children.remove(idx - 2) } - fn modify_entry(&mut self, idx: usize, new_name: &str) { + fn substitute_entry( + &mut self, + idx: usize, + new_entry: (Str256, Arc), + ) -> Option<(Str256, Arc)> { assert!(idx >= 2); - let (name, _) = self.children.iter_mut().nth(idx - 2).unwrap(); - *name = Str256::from(new_name); + self.children.put_at(idx - 2, new_entry) } fn visit_entry(&self, mut idx: usize, visitor: &mut dyn DirentVisitor) -> Result { @@ -246,14 +249,21 @@ impl DirEntry { *idx += 1; } // Read the normal child entries. - for (name, child) in self.children.iter().skip(*idx - 2) { + for (offset, (name, child)) in self + .children + .idxes_and_entries() + .map(|(offset, (name, child))| (offset + 2, (name, child))) + { + if offset < *idx { + continue; + } visitor.visit( name.as_ref(), child.metadata().ino as u64, child.metadata().type_, - *idx, + offset, )?; - *idx += 1; + *idx = offset + 1; } Ok(()) }; @@ -555,10 +565,10 @@ impl Inode for RamInode { if self.metadata().ino == target.metadata().ino { let mut self_inode = self.0.write(); let self_dir = self_inode.inner.as_direntry_mut().unwrap(); - let (idx, _) = self_dir + let (idx, inode) = self_dir .get_entry(old_name) .ok_or(Error::new(Errno::ENOENT))?; - self_dir.modify_entry(idx, new_name); + self_dir.substitute_entry(idx, (Str256::from(new_name), inode)); } else { let (mut self_inode, mut target_inode) = write_lock_two_inodes(self, target); let self_dir = self_inode.inner.as_direntry_mut().unwrap(); diff --git a/src/services/libs/jinux-std/src/fs/utils/direntry_vec.rs b/src/services/libs/jinux-std/src/fs/utils/direntry_vec.rs new file mode 100644 index 000000000..07899aa28 --- /dev/null +++ b/src/services/libs/jinux-std/src/fs/utils/direntry_vec.rs @@ -0,0 +1,83 @@ +use crate::prelude::*; + +/// DirEntryVec is used to store the entries of a directory. +/// It can guarantee that the index of one dir entry remains unchanged during +/// adding or deleting other dir entries of it. +pub struct DirEntryVec { + // The slots to store dir entries. + slots: Vec>, + // The number of occupied slots. + // The i-th slot is occupied if `self.slots[i].is_some()`. + num_occupied: usize, +} + +impl DirEntryVec { + /// New an empty vec. + pub fn new() -> Self { + Self { + slots: Vec::new(), + num_occupied: 0, + } + } + + /// Returns `true` if the vec contains no entries. + pub fn is_empty(&self) -> bool { + self.num_occupied == 0 + } + + /// Put a dir entry into the vec. + /// it may be put into an existing empty slot or the back of the vec. + pub fn put(&mut self, entry: T) { + if self.num_occupied == self.slots.len() { + self.slots.push(Some(entry)); + } else { + let idx = self.slots.iter().position(|x| x.is_none()).unwrap(); + self.slots[idx] = Some(entry); + } + self.num_occupied += 1; + } + + /// Removes and returns the entry at position `idx`. + /// Returns `None` if `idx` is out of bounds or the entry has been removed. + pub fn remove(&mut self, idx: usize) -> Option { + if idx >= self.slots.len() { + return None; + } + let mut del_entry = None; + core::mem::swap(&mut del_entry, &mut self.slots[idx]); + if del_entry.is_some() { + debug_assert!(self.num_occupied > 0); + self.num_occupied -= 1; + } + del_entry + } + + /// Put and returns the entry at position `idx`. + /// Returns `None` if `idx` is out of bounds or the entry has been removed. + pub fn put_at(&mut self, idx: usize, entry: T) -> Option { + if idx >= self.slots.len() { + return None; + } + let mut sub_entry = Some(entry); + core::mem::swap(&mut sub_entry, &mut self.slots[idx]); + if sub_entry.is_none() { + self.num_occupied += 1; + } + sub_entry + } + + /// Creates an iterator which gives both of the index and the dir entry. + /// The index may not be continuous. + pub fn idxes_and_entries(&self) -> impl Iterator { + self.slots + .iter() + .enumerate() + .filter(|(_, x)| x.is_some()) + .map(|(idx, x)| (idx, x.as_ref().unwrap())) + } + + /// Creates an iterator which gives the dir entry. + pub fn iter(&self) -> impl Iterator { + self.slots.iter().filter_map(|x| x.as_ref()) + } +} diff --git a/src/services/libs/jinux-std/src/fs/utils/mod.rs b/src/services/libs/jinux-std/src/fs/utils/mod.rs index f642be8b5..0dfcdf228 100644 --- a/src/services/libs/jinux-std/src/fs/utils/mod.rs +++ b/src/services/libs/jinux-std/src/fs/utils/mod.rs @@ -4,6 +4,7 @@ pub use access_mode::AccessMode; pub use creation_flags::CreationFlags; pub use dentry_cache::Dentry; pub use dirent_visitor::DirentVisitor; +pub use direntry_vec::DirEntryVec; pub use fcntl::FcntlCmd; pub use file_creation_mask::FileCreationMask; pub use fs::{FileSystem, FsFlags, SuperBlock}; @@ -19,6 +20,7 @@ mod access_mode; mod creation_flags; mod dentry_cache; mod dirent_visitor; +mod direntry_vec; mod fcntl; mod file_creation_mask; mod fs;