Refine RamFs by extracting immutable metadata from the lock

This commit is contained in:
LI Qing
2024-05-22 15:51:48 +08:00
committed by Tate, Hongliang Tian
parent e7298bb276
commit 6ff8497101
2 changed files with 295 additions and 272 deletions

View File

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
use core::{ use core::{
sync::atomic::{AtomicUsize, Ordering}, sync::atomic::{AtomicU64, Ordering},
time::Duration, time::Duration,
}; };
@ -30,39 +30,41 @@ use crate::{
/// A volatile file system whose data and metadata exists only in memory. /// A volatile file system whose data and metadata exists only in memory.
pub struct RamFS { pub struct RamFS {
metadata: RwLock<SuperBlock>, /// The super block
sb: SuperBlock,
/// Root inode
root: Arc<RamInode>, root: Arc<RamInode>,
inode_allocator: AtomicUsize, /// An inode allocator
inode_allocator: AtomicU64,
} }
impl RamFS { impl RamFS {
pub fn new() -> Arc<Self> { pub fn new() -> Arc<Self> {
let sb = SuperBlock::new(RAMFS_MAGIC, BLOCK_SIZE, NAME_MAX); Arc::new_cyclic(|weak_fs| Self {
let root = Arc::new(RamInode(RwMutex::new(Inode_::new_dir( sb: SuperBlock::new(RAMFS_MAGIC, BLOCK_SIZE, NAME_MAX),
ROOT_INO, root: Arc::new_cyclic(|weak_root| RamInode {
node: RwMutex::new(Node::new_dir(
InodeMode::from_bits_truncate(0o755), InodeMode::from_bits_truncate(0o755),
&sb, Uid::new_root(),
)))); Gid::new_root(),
let ramfs = Arc::new(Self { weak_root.clone(),
metadata: RwLock::new(sb), weak_root.clone(),
root, )),
inode_allocator: AtomicUsize::new(ROOT_INO + 1), ino: ROOT_INO,
}); typ: InodeType::Dir,
let mut root = ramfs.root.0.write(); this: weak_root.clone(),
root.inner fs: weak_fs.clone(),
.as_direntry_mut() }),
.unwrap() inode_allocator: AtomicU64::new(ROOT_INO + 1),
.init(Arc::downgrade(&ramfs.root), Arc::downgrade(&ramfs.root)); })
root.this = Arc::downgrade(&ramfs.root);
root.fs = Arc::downgrade(&ramfs);
drop(root);
ramfs
} }
fn alloc_id(&self) -> usize { fn alloc_id(&self) -> u64 {
let next_id = self.inode_allocator.fetch_add(1, Ordering::SeqCst); self.inode_allocator.fetch_add(1, Ordering::SeqCst)
self.metadata.write().files += 1; }
next_id
fn device_id(&self) -> u64 {
0
} }
} }
@ -77,7 +79,7 @@ impl FileSystem for RamFS {
} }
fn sb(&self) -> SuperBlock { fn sb(&self) -> SuperBlock {
self.metadata.read().clone() self.sb.clone()
} }
fn flags(&self) -> FsFlags { fn flags(&self) -> FsFlags {
@ -85,68 +87,63 @@ impl FileSystem for RamFS {
} }
} }
struct RamInode(RwMutex<Inode_>); struct RamInode {
/// The mutable part of the inode
struct Inode_ { node: RwMutex<Node>,
inner: Inner, /// Inode number
metadata: Metadata, ino: u64,
/// Type of the inode
typ: InodeType,
/// Reference to self
this: Weak<RamInode>, this: Weak<RamInode>,
/// Reference to fs
fs: Weak<RamFS>, fs: Weak<RamFS>,
} }
impl Inode_ { struct Node {
pub fn new_dir(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self { inner: Inner,
Self { metadata: InodeMeta,
inner: Inner::Dir(DirEntry::new()),
metadata: Metadata::new_dir(ino, mode, sb),
this: Weak::default(),
fs: Weak::default(),
}
} }
pub fn new_file( impl Node {
ino: usize, pub fn new_dir(
mode: InodeMode, mode: InodeMode,
sb: &SuperBlock, uid: Uid,
weak_inode: Weak<RamInode>, gid: Gid,
this: Weak<RamInode>,
parent: Weak<RamInode>,
) -> Self { ) -> Self {
Self { Self {
inner: Inner::File(PageCache::new(weak_inode).unwrap()), inner: Inner::Dir(DirEntry::new(this, parent)),
metadata: Metadata::new_file(ino, mode, sb), metadata: InodeMeta::new_dir(mode, uid, gid),
this: Weak::default(),
fs: Weak::default(),
} }
} }
pub fn new_symlink(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self { pub fn new_file(mode: InodeMode, uid: Uid, gid: Gid, this: Weak<RamInode>) -> Self {
Self {
inner: Inner::File(PageCache::new(this).unwrap()),
metadata: InodeMeta::new(mode, uid, gid),
}
}
pub fn new_symlink(mode: InodeMode, uid: Uid, gid: Gid) -> Self {
Self { Self {
inner: Inner::SymLink(String::from("")), inner: Inner::SymLink(String::from("")),
metadata: Metadata::new_symlink(ino, mode, sb), metadata: InodeMeta::new(mode, uid, gid),
this: Weak::default(),
fs: Weak::default(),
} }
} }
pub fn new_socket(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self { pub fn new_socket(mode: InodeMode, uid: Uid, gid: Gid) -> Self {
Self { Self {
inner: Inner::Socket, inner: Inner::Socket,
metadata: Metadata::new_socket(ino, mode, sb), metadata: InodeMeta::new(mode, uid, gid),
this: Weak::default(),
fs: Weak::default(),
} }
} }
pub fn new_device( pub fn new_device(mode: InodeMode, uid: Uid, gid: Gid, device: Arc<dyn Device>) -> Self {
ino: usize,
mode: InodeMode,
sb: &SuperBlock,
device: Arc<dyn Device>,
) -> Self {
Self { Self {
metadata: Metadata::new_device(ino, mode, sb, device.as_ref()),
inner: Inner::Device(device), inner: Inner::Device(device),
this: Weak::default(), metadata: InodeMeta::new(mode, uid, gid),
fs: Weak::default(),
} }
} }
@ -176,6 +173,49 @@ impl Inode_ {
} }
} }
#[derive(Debug, Clone, Copy)]
struct InodeMeta {
size: usize,
blocks: usize,
atime: Duration,
mtime: Duration,
ctime: Duration,
mode: InodeMode,
nlinks: usize,
uid: Uid,
gid: Gid,
}
impl InodeMeta {
pub fn new(mode: InodeMode, uid: Uid, gid: Gid) -> Self {
Self {
size: 0,
blocks: 0,
atime: Default::default(),
mtime: Default::default(),
ctime: Default::default(),
mode,
nlinks: 1,
uid,
gid,
}
}
pub fn new_dir(mode: InodeMode, uid: Uid, gid: Gid) -> Self {
Self {
size: 2,
blocks: 1,
atime: Default::default(),
mtime: Default::default(),
ctime: Default::default(),
mode,
nlinks: 2,
uid,
gid,
}
}
}
#[allow(clippy::large_enum_variant)] #[allow(clippy::large_enum_variant)]
enum Inner { enum Inner {
Dir(DirEntry), Dir(DirEntry),
@ -236,19 +276,14 @@ struct DirEntry {
} }
impl DirEntry { impl DirEntry {
fn new() -> Self { fn new(this: Weak<RamInode>, parent: Weak<RamInode>) -> Self {
Self { Self {
children: SlotVec::new(), children: SlotVec::new(),
this: Weak::default(), this,
parent: Weak::default(), parent,
} }
} }
fn init(&mut self, this: Weak<RamInode>, parent: Weak<RamInode>) {
self.this = this;
self.set_parent(parent);
}
fn set_parent(&mut self, parent: Weak<RamInode>) { fn set_parent(&mut self, parent: Weak<RamInode>) {
self.parent = parent; self.parent = parent;
} }
@ -299,22 +334,12 @@ impl DirEntry {
// Read the two special entries("." and ".."). // Read the two special entries("." and "..").
if *idx == 0 { if *idx == 0 {
let this_inode = self.this.upgrade().unwrap(); let this_inode = self.this.upgrade().unwrap();
visitor.visit( visitor.visit(".", this_inode.ino, this_inode.typ, *idx)?;
".",
this_inode.metadata().ino as u64,
this_inode.metadata().type_,
*idx,
)?;
*idx += 1; *idx += 1;
} }
if *idx == 1 { if *idx == 1 {
let parent_inode = self.parent.upgrade().unwrap(); let parent_inode = self.parent.upgrade().unwrap();
visitor.visit( visitor.visit("..", parent_inode.ino, parent_inode.typ, *idx)?;
"..",
parent_inode.metadata().ino as u64,
parent_inode.metadata().type_,
*idx,
)?;
*idx += 1; *idx += 1;
} }
// Read the normal child entries. // Read the normal child entries.
@ -325,12 +350,7 @@ impl DirEntry {
.map(|(offset, (name, child))| (offset + 2, (name, child))) .map(|(offset, (name, child))| (offset + 2, (name, child)))
.skip_while(|(offset, _)| offset < &start_idx) .skip_while(|(offset, _)| offset < &start_idx)
{ {
visitor.visit( visitor.visit(name.as_str().unwrap(), child.ino, child.typ, offset)?;
name.as_str().unwrap(),
child.metadata().ino as u64,
child.metadata().type_,
offset,
)?;
*idx = offset + 1; *idx = offset + 1;
} }
Ok(()) Ok(())
@ -349,82 +369,80 @@ impl DirEntry {
} }
impl RamInode { impl RamInode {
fn new_dir(fs: &Arc<RamFS>, mode: InodeMode, parent: &Weak<Self>) -> Arc<Self> { fn new_dir(
Arc::new_cyclic(|weak_self| { fs: &Arc<RamFS>,
let inode = RamInode(RwMutex::new(Inode_::new_dir(fs.alloc_id(), mode, &fs.sb()))); mode: InodeMode,
inode.0.write().fs = Arc::downgrade(fs); uid: Uid,
inode.0.write().this = weak_self.clone(); gid: Gid,
inode parent: &Weak<RamInode>,
.0 ) -> Arc<Self> {
.write() Arc::new_cyclic(|weak_self| RamInode {
.inner node: RwMutex::new(Node::new_dir(
.as_direntry_mut()
.unwrap()
.init(weak_self.clone(), parent.clone());
inode
})
}
fn new_file(fs: &Arc<RamFS>, mode: InodeMode) -> Arc<Self> {
Arc::new_cyclic(|weak_self| {
let inode = RamInode(RwMutex::new(Inode_::new_file(
fs.alloc_id(),
mode, mode,
&fs.sb(), uid,
gid,
weak_self.clone(), weak_self.clone(),
))); parent.clone(),
inode.0.write().fs = Arc::downgrade(fs); )),
inode.0.write().this = weak_self.clone(); ino: fs.alloc_id(),
inode typ: InodeType::Dir,
this: weak_self.clone(),
fs: Arc::downgrade(fs),
}) })
} }
fn new_socket(fs: &Arc<RamFS>, mode: InodeMode) -> Arc<Self> { fn new_file(fs: &Arc<RamFS>, mode: InodeMode, uid: Uid, gid: Gid) -> Arc<Self> {
Arc::new_cyclic(|weak_self| { Arc::new_cyclic(|weak_self| RamInode {
let inode = RamInode(RwMutex::new(Inode_::new_socket( node: RwMutex::new(Node::new_file(mode, uid, gid, weak_self.clone())),
fs.alloc_id(), ino: fs.alloc_id(),
mode, typ: InodeType::File,
&fs.sb(), this: weak_self.clone(),
))); fs: Arc::downgrade(fs),
inode.0.write().fs = Arc::downgrade(fs);
inode.0.write().this = weak_self.clone();
inode
}) })
} }
fn new_symlink(fs: &Arc<RamFS>, mode: InodeMode) -> Arc<Self> { fn new_symlink(fs: &Arc<RamFS>, mode: InodeMode, uid: Uid, gid: Gid) -> Arc<Self> {
Arc::new_cyclic(|weak_self| { Arc::new_cyclic(|weak_self| RamInode {
let inode = RamInode(RwMutex::new(Inode_::new_symlink( node: RwMutex::new(Node::new_symlink(mode, uid, gid)),
fs.alloc_id(), ino: fs.alloc_id(),
mode, typ: InodeType::SymLink,
&fs.sb(), this: weak_self.clone(),
))); fs: Arc::downgrade(fs),
inode.0.write().fs = Arc::downgrade(fs);
inode.0.write().this = weak_self.clone();
inode
}) })
} }
fn new_device(fs: &Arc<RamFS>, mode: InodeMode, device: Arc<dyn Device>) -> Arc<Self> { fn new_socket(fs: &Arc<RamFS>, mode: InodeMode, uid: Uid, gid: Gid) -> Arc<Self> {
Arc::new_cyclic(|weak_self| { Arc::new_cyclic(|weak_self| RamInode {
let inode = RamInode(RwMutex::new(Inode_::new_device( node: RwMutex::new(Node::new_socket(mode, uid, gid)),
fs.alloc_id(), ino: fs.alloc_id(),
mode, typ: InodeType::Socket,
&fs.sb(), this: weak_self.clone(),
device, fs: Arc::downgrade(fs),
))); })
inode.0.write().fs = Arc::downgrade(fs); }
inode.0.write().this = weak_self.clone();
inode fn new_device(
fs: &Arc<RamFS>,
mode: InodeMode,
uid: Uid,
gid: Gid,
device: Arc<dyn Device>,
) -> Arc<Self> {
Arc::new_cyclic(|weak_self| RamInode {
node: RwMutex::new(Node::new_device(mode, uid, gid, device.clone())),
ino: fs.alloc_id(),
typ: InodeType::from(device.type_()),
this: weak_self.clone(),
fs: Arc::downgrade(fs),
}) })
} }
fn find(&self, name: &str) -> Result<Arc<Self>> { fn find(&self, name: &str) -> Result<Arc<Self>> {
let self_inode = self.0.read(); if self.typ != InodeType::Dir {
if self_inode.metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir"); return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
} }
let self_inode = self.node.read();
let (_, inode) = self_inode let (_, inode) = self_inode
.inner .inner
.as_direntry() .as_direntry()
@ -448,13 +466,13 @@ impl PageCacheBackend for RamInode {
} }
fn npages(&self) -> usize { fn npages(&self) -> usize {
self.0.read().metadata.blocks self.node.read().metadata.blocks
} }
} }
impl Inode for RamInode { impl Inode for RamInode {
fn page_cache(&self) -> Option<Vmo<Full>> { fn page_cache(&self) -> Option<Vmo<Full>> {
self.0 self.node
.read() .read()
.inner .inner
.as_file() .as_file()
@ -462,7 +480,7 @@ impl Inode for RamInode {
} }
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> { fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
let self_inode = self.0.read(); let self_inode = self.node.read();
if let Some(device) = self_inode.inner.as_device() { if let Some(device) = self_inode.inner.as_device() {
return device.read(buf); return device.read(buf);
@ -488,7 +506,7 @@ impl Inode for RamInode {
} }
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> { fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
let self_inode = self.0.upread(); let self_inode = self.node.upread();
if let Some(device) = self_inode.inner.as_device() { if let Some(device) = self_inode.inner.as_device() {
return device.write(buf); return device.write(buf);
@ -517,15 +535,15 @@ impl Inode for RamInode {
} }
fn size(&self) -> usize { fn size(&self) -> usize {
self.0.read().metadata.size self.node.read().metadata.size
} }
fn resize(&self, new_size: usize) -> Result<()> { fn resize(&self, new_size: usize) -> Result<()> {
let self_inode = self.0.upread(); if self.typ != InodeType::File {
if self_inode.inner.as_file().is_none() { return_errno_with_message!(Errno::EISDIR, "not regular file");
return_errno!(Errno::EISDIR);
} }
let self_inode = self.node.upread();
let file_size = self_inode.metadata.size; let file_size = self_inode.metadata.size;
if file_size == new_size { if file_size == new_size {
return Ok(()); return Ok(());
@ -541,53 +559,53 @@ impl Inode for RamInode {
} }
fn atime(&self) -> Duration { fn atime(&self) -> Duration {
self.0.read().metadata.atime self.node.read().metadata.atime
} }
fn set_atime(&self, time: Duration) { fn set_atime(&self, time: Duration) {
self.0.write().metadata.atime = time; self.node.write().metadata.atime = time;
} }
fn mtime(&self) -> Duration { fn mtime(&self) -> Duration {
self.0.read().metadata.mtime self.node.read().metadata.mtime
} }
fn set_mtime(&self, time: Duration) { fn set_mtime(&self, time: Duration) {
self.0.write().metadata.mtime = time; self.node.write().metadata.mtime = time;
} }
fn ino(&self) -> u64 { fn ino(&self) -> u64 {
self.0.read().metadata.ino as _ self.ino
} }
fn type_(&self) -> InodeType { fn type_(&self) -> InodeType {
self.0.read().metadata.type_ self.typ
} }
fn mode(&self) -> Result<InodeMode> { fn mode(&self) -> Result<InodeMode> {
Ok(self.0.read().metadata.mode) Ok(self.node.read().metadata.mode)
} }
fn set_mode(&self, mode: InodeMode) -> Result<()> { fn set_mode(&self, mode: InodeMode) -> Result<()> {
self.0.write().metadata.mode = mode; self.node.write().metadata.mode = mode;
Ok(()) Ok(())
} }
fn owner(&self) -> Result<Uid> { fn owner(&self) -> Result<Uid> {
Ok(self.0.read().metadata.uid) Ok(self.node.read().metadata.uid)
} }
fn set_owner(&self, uid: Uid) -> Result<()> { fn set_owner(&self, uid: Uid) -> Result<()> {
self.0.write().metadata.uid = uid; self.node.write().metadata.uid = uid;
Ok(()) Ok(())
} }
fn group(&self) -> Result<Gid> { fn group(&self) -> Result<Gid> {
Ok(self.0.read().metadata.gid) Ok(self.node.read().metadata.gid)
} }
fn set_group(&self, gid: Gid) -> Result<()> { fn set_group(&self, gid: Gid) -> Result<()> {
self.0.write().metadata.gid = gid; self.node.write().metadata.gid = gid;
Ok(()) Ok(())
} }
@ -600,15 +618,21 @@ impl Inode for RamInode {
if name.len() > NAME_MAX { if name.len() > NAME_MAX {
return_errno!(Errno::ENAMETOOLONG); return_errno!(Errno::ENAMETOOLONG);
} }
if self.typ != InodeType::Dir {
let self_inode = self.0.upread();
if self_inode.metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir"); return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
} }
let self_inode = self.node.upread();
if self_inode.inner.as_direntry().unwrap().contains_entry(name) { if self_inode.inner.as_direntry().unwrap().contains_entry(name) {
return_errno_with_message!(Errno::EEXIST, "entry exists"); return_errno_with_message!(Errno::EEXIST, "entry exists");
} }
let device_inode = RamInode::new_device(&self_inode.fs.upgrade().unwrap(), mode, device); let device_inode = RamInode::new_device(
&self.fs.upgrade().unwrap(),
mode,
Uid::new_root(),
Gid::new_root(),
device,
);
let mut self_inode = self_inode.upgrade(); let mut self_inode = self_inode.upgrade();
self_inode self_inode
@ -621,27 +645,31 @@ impl Inode for RamInode {
} }
fn as_device(&self) -> Option<Arc<dyn Device>> { fn as_device(&self) -> Option<Arc<dyn Device>> {
self.0.read().inner.as_device().cloned() self.node.read().inner.as_device().cloned()
} }
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> { fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {
if name.len() > NAME_MAX { if name.len() > NAME_MAX {
return_errno!(Errno::ENAMETOOLONG); return_errno!(Errno::ENAMETOOLONG);
} }
if self.typ != InodeType::Dir {
let self_inode = self.0.upread();
if self_inode.metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir"); return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
} }
let self_inode = self.node.upread();
if self_inode.inner.as_direntry().unwrap().contains_entry(name) { if self_inode.inner.as_direntry().unwrap().contains_entry(name) {
return_errno_with_message!(Errno::EEXIST, "entry exists"); return_errno_with_message!(Errno::EEXIST, "entry exists");
} }
let fs = self_inode.fs.upgrade().unwrap(); let fs = self.fs.upgrade().unwrap();
let new_inode = match type_ { let new_inode = match type_ {
InodeType::File => RamInode::new_file(&fs, mode), InodeType::File => RamInode::new_file(&fs, mode, Uid::new_root(), Gid::new_root()),
InodeType::SymLink => RamInode::new_symlink(&fs, mode), InodeType::SymLink => {
InodeType::Socket => RamInode::new_socket(&fs, mode), RamInode::new_symlink(&fs, mode, Uid::new_root(), Gid::new_root())
InodeType::Dir => RamInode::new_dir(&fs, mode, &self_inode.this), }
InodeType::Socket => RamInode::new_socket(&fs, mode, Uid::new_root(), Gid::new_root()),
InodeType::Dir => {
RamInode::new_dir(&fs, mode, Uid::new_root(), Gid::new_root(), &self.this)
}
_ => { _ => {
panic!("unsupported inode type"); panic!("unsupported inode type");
} }
@ -661,10 +689,11 @@ impl Inode for RamInode {
} }
fn readdir_at(&self, offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> { fn readdir_at(&self, offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
let self_inode = self.0.read(); if self.typ != InodeType::Dir {
if self_inode.metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir"); return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
} }
let self_inode = self.node.read();
let cnt = self_inode let cnt = self_inode
.inner .inner
.as_direntry() .as_direntry()
@ -677,30 +706,26 @@ impl Inode for RamInode {
if !Arc::ptr_eq(&self.fs(), &old.fs()) { if !Arc::ptr_eq(&self.fs(), &old.fs()) {
return_errno_with_message!(Errno::EXDEV, "not same fs"); return_errno_with_message!(Errno::EXDEV, "not same fs");
} }
if self.typ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
let old = old let old = old
.downcast_ref::<RamInode>() .downcast_ref::<RamInode>()
.ok_or(Error::new(Errno::EXDEV))?; .ok_or(Error::new(Errno::EXDEV))?;
let old_arc = { if old.typ == InodeType::Dir {
let old_inode = old.0.read();
if old_inode.metadata.type_ == InodeType::Dir {
return_errno_with_message!(Errno::EPERM, "old is a dir"); return_errno_with_message!(Errno::EPERM, "old is a dir");
} }
old_inode.this.upgrade().unwrap()
};
let mut self_inode = self.0.write(); let mut self_inode = self.node.write();
if self_inode.metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
let self_dir = self_inode.inner.as_direntry_mut().unwrap(); let self_dir = self_inode.inner.as_direntry_mut().unwrap();
if self_dir.contains_entry(name) { if self_dir.contains_entry(name) {
return_errno_with_message!(Errno::EEXIST, "entry exist"); return_errno_with_message!(Errno::EEXIST, "entry exist");
} }
self_dir.append_entry(name, old.this.upgrade().unwrap());
self_dir.append_entry(name, old_arc);
self_inode.inc_size(); self_inode.inc_size();
drop(self_inode); drop(self_inode);
old.0.write().inc_nlinks(); old.node.write().inc_nlinks();
Ok(()) Ok(())
} }
@ -710,7 +735,7 @@ impl Inode for RamInode {
} }
let target = self.find(name)?; let target = self.find(name)?;
if target.type_() == InodeType::Dir { if target.typ == InodeType::Dir {
return_errno_with_message!(Errno::EISDIR, "unlink on dir"); return_errno_with_message!(Errno::EISDIR, "unlink on dir");
} }
@ -737,10 +762,11 @@ impl Inode for RamInode {
} }
let target = self.find(name)?; let target = self.find(name)?;
let target_inode = target.0.read(); if target.typ != InodeType::Dir {
if target_inode.metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "rmdir on not dir"); return_errno_with_message!(Errno::ENOTDIR, "rmdir on not dir");
} }
let target_inode = target.node.read();
if !target_inode if !target_inode
.inner .inner
.as_direntry() .as_direntry()
@ -758,9 +784,6 @@ impl Inode for RamInode {
if !Arc::ptr_eq(&new_target, &target) { if !Arc::ptr_eq(&new_target, &target) {
return_errno!(Errno::ENOENT); return_errno!(Errno::ENOENT);
} }
if target_inode.metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "rmdir on not dir");
}
if !target_inode if !target_inode
.inner .inner
.as_direntry() .as_direntry()
@ -783,60 +806,38 @@ impl Inode for RamInode {
} }
fn rename(&self, old_name: &str, target: &Arc<dyn Inode>, new_name: &str) -> Result<()> { fn rename(&self, old_name: &str, target: &Arc<dyn Inode>, new_name: &str) -> Result<()> {
let target = target
.downcast_ref::<RamInode>()
.ok_or(Error::new(Errno::EXDEV))?;
let (self_ino, target_ino) = {
if !Arc::ptr_eq(&self.fs(), &target.fs()) {
return_errno_with_message!(Errno::EXDEV, "not same fs");
}
let (self_type, self_ino) = {
let self_inode = self.0.read();
(self_inode.metadata.type_, self_inode.metadata.ino as u64)
};
let (target_type, target_ino) = {
let target_inode = target.0.read();
(
target_inode.metadata.type_,
target_inode.metadata.ino as u64,
)
};
if self_type != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
if target_type != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "target is not dir");
}
if old_name == "." || old_name == ".." { if old_name == "." || old_name == ".." {
return_errno_with_message!(Errno::EISDIR, "old_name is . or .."); return_errno_with_message!(Errno::EISDIR, "old_name is . or ..");
} }
if new_name == "." || new_name == ".." { if new_name == "." || new_name == ".." {
return_errno_with_message!(Errno::EISDIR, "new_name is . or .."); return_errno_with_message!(Errno::EISDIR, "new_name is . or ..");
} }
(self_ino, target_ino)
}; let target = target
.downcast_ref::<RamInode>()
.ok_or(Error::new(Errno::EXDEV))?;
if !Arc::ptr_eq(&self.fs(), &target.fs()) {
return_errno_with_message!(Errno::EXDEV, "not same fs");
}
if self.typ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
if target.typ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "target is not dir");
}
// Perform necessary checks to ensure that `dst_inode` can be replaced by `src_inode`. // Perform necessary checks to ensure that `dst_inode` can be replaced by `src_inode`.
let check_replace_inode = let check_replace_inode =
|src_inode: &Arc<RamInode>, dst_inode: &Arc<RamInode>| -> Result<()> { |src_inode: &Arc<RamInode>, dst_inode: &Arc<RamInode>| -> Result<()> {
let (src_type, src_ino) = { if src_inode.ino == dst_inode.ino {
let src_inode = src_inode.0.read();
(src_inode.metadata.type_, src_inode.metadata.ino as u64)
};
let (dst_type, dst_ino) = {
let dst_inode = dst_inode.0.read();
(dst_inode.metadata.type_, dst_inode.metadata.ino as u64)
};
if src_ino == dst_ino {
return Ok(()); return Ok(());
} }
match (src_type, dst_type) { match (src_inode.typ, dst_inode.typ) {
(InodeType::Dir, InodeType::Dir) => { (InodeType::Dir, InodeType::Dir) => {
if !dst_inode if !dst_inode
.0 .node
.read() .read()
.inner .inner
.as_direntry() .as_direntry()
@ -858,13 +859,13 @@ impl Inode for RamInode {
}; };
// Rename in the same directory // Rename in the same directory
if self_ino == target_ino { if self.ino == target.ino {
let mut self_inode = self.0.write(); let mut self_inode = self.node.write();
let self_dir = self_inode.inner.as_direntry_mut().unwrap(); let self_dir = self_inode.inner.as_direntry_mut().unwrap();
let (src_idx, src_inode) = self_dir let (src_idx, src_inode) = self_dir
.get_entry(old_name) .get_entry(old_name)
.ok_or(Error::new(Errno::ENOENT))?; .ok_or(Error::new(Errno::ENOENT))?;
let is_dir = src_inode.type_() == InodeType::Dir; let is_dir = src_inode.typ == InodeType::Dir;
if let Some((dst_idx, dst_inode)) = self_dir.get_entry(new_name) { if let Some((dst_idx, dst_inode)) = self_dir.get_entry(new_name) {
check_replace_inode(&src_inode, &dst_inode)?; check_replace_inode(&src_inode, &dst_inode)?;
@ -881,8 +882,8 @@ impl Inode for RamInode {
// Or rename across different directories // Or rename across different directories
else { else {
let (mut self_inode, mut target_inode) = write_lock_two_inodes(self, target); let (mut self_inode, mut target_inode) = write_lock_two_inodes(self, target);
let self_inode_arc = self_inode.this.upgrade().unwrap(); let self_inode_arc = self.this.upgrade().unwrap();
let target_inode_arc = target_inode.this.upgrade().unwrap(); let target_inode_arc = target.this.upgrade().unwrap();
let self_dir = self_inode.inner.as_direntry_mut().unwrap(); let self_dir = self_inode.inner.as_direntry_mut().unwrap();
let (src_idx, src_inode) = self_dir let (src_idx, src_inode) = self_dir
.get_entry(old_name) .get_entry(old_name)
@ -891,7 +892,7 @@ impl Inode for RamInode {
if Arc::ptr_eq(&src_inode, &target_inode_arc) { if Arc::ptr_eq(&src_inode, &target_inode_arc) {
return_errno!(Errno::EINVAL); return_errno!(Errno::EINVAL);
} }
let is_dir = src_inode.type_() == InodeType::Dir; let is_dir = src_inode.typ == InodeType::Dir;
let target_dir = target_inode.inner.as_direntry_mut().unwrap(); let target_dir = target_inode.inner.as_direntry_mut().unwrap();
if let Some((dst_idx, dst_inode)) = target_dir.get_entry(new_name) { if let Some((dst_idx, dst_inode)) = target_dir.get_entry(new_name) {
@ -921,31 +922,33 @@ impl Inode for RamInode {
drop(target_inode); drop(target_inode);
if is_dir { if is_dir {
src_inode src_inode
.0 .node
.write() .write()
.inner .inner
.as_direntry_mut() .as_direntry_mut()
.unwrap() .unwrap()
.set_parent(target.0.read().this.clone()); .set_parent(target.this.clone());
} }
} }
Ok(()) Ok(())
} }
fn read_link(&self) -> Result<String> { fn read_link(&self) -> Result<String> {
let self_inode = self.0.read(); if self.typ != InodeType::SymLink {
if self_inode.metadata.type_ != InodeType::SymLink {
return_errno_with_message!(Errno::EINVAL, "self is not symlink"); return_errno_with_message!(Errno::EINVAL, "self is not symlink");
} }
let self_inode = self.node.read();
let link = self_inode.inner.as_symlink().unwrap(); let link = self_inode.inner.as_symlink().unwrap();
Ok(String::from(link)) Ok(String::from(link))
} }
fn write_link(&self, target: &str) -> Result<()> { fn write_link(&self, target: &str) -> Result<()> {
let mut self_inode = self.0.write(); if self.typ != InodeType::SymLink {
if self_inode.metadata.type_ != InodeType::SymLink {
return_errno_with_message!(Errno::EINVAL, "self is not symlink"); return_errno_with_message!(Errno::EINVAL, "self is not symlink");
} }
let mut self_inode = self.node.write();
let link = self_inode.inner.as_symlink_mut().unwrap(); let link = self_inode.inner.as_symlink_mut().unwrap();
*link = String::from(target); *link = String::from(target);
// Symlink's metadata.blocks should be 0, so just set the size. // Symlink's metadata.blocks should be 0, so just set the size.
@ -954,7 +957,29 @@ impl Inode for RamInode {
} }
fn metadata(&self) -> Metadata { fn metadata(&self) -> Metadata {
self.0.read().metadata let self_inode = self.node.read();
Metadata {
dev: self.fs.upgrade().unwrap().device_id(),
ino: self.ino as _,
size: self_inode.metadata.size,
blk_size: BLOCK_SIZE,
blocks: self_inode.metadata.blocks,
atime: self_inode.metadata.atime,
mtime: self_inode.metadata.mtime,
ctime: self_inode.metadata.ctime,
type_: self.typ,
mode: self_inode.metadata.mode,
nlinks: self_inode.metadata.nlinks,
uid: self_inode.metadata.uid,
gid: self_inode.metadata.gid,
rdev: {
if let Some(device) = self_inode.inner.as_device() {
device.id().into()
} else {
0
}
},
}
} }
fn sync(&self) -> Result<()> { fn sync(&self) -> Result<()> {
@ -963,7 +988,7 @@ impl Inode for RamInode {
} }
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents { fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
if let Some(device) = self.0.read().inner.as_device() { if let Some(device) = self.node.read().inner.as_device() {
device.poll(mask, poller) device.poll(mask, poller)
} else { } else {
let events = IoEvents::IN | IoEvents::OUT; let events = IoEvents::IN | IoEvents::OUT;
@ -972,11 +997,11 @@ impl Inode for RamInode {
} }
fn fs(&self) -> Arc<dyn FileSystem> { fn fs(&self) -> Arc<dyn FileSystem> {
Weak::upgrade(&self.0.read().fs).unwrap() Weak::upgrade(&self.fs).unwrap()
} }
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> { fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
if let Some(device) = self.0.read().inner.as_device() { if let Some(device) = self.node.read().inner.as_device() {
return device.ioctl(cmd, arg); return device.ioctl(cmd, arg);
} }
return_errno_with_message!(Errno::EINVAL, "ioctl is not supported"); return_errno_with_message!(Errno::EINVAL, "ioctl is not supported");
@ -986,16 +1011,14 @@ impl Inode for RamInode {
fn write_lock_two_inodes<'a>( fn write_lock_two_inodes<'a>(
this: &'a RamInode, this: &'a RamInode,
other: &'a RamInode, other: &'a RamInode,
) -> (RwMutexWriteGuard<'a, Inode_>, RwMutexWriteGuard<'a, Inode_>) { ) -> (RwMutexWriteGuard<'a, Node>, RwMutexWriteGuard<'a, Node>) {
// TODO: It involves acquiring two read locks before doing the write locks. if this.ino < other.ino {
// Need to move the immutable part out of the metadata. let this = this.node.write();
if this.ino() < other.ino() { let other = other.node.write();
let this = this.0.write();
let other = other.0.write();
(this, other) (this, other)
} else { } else {
let other = other.0.write(); let other = other.node.write();
let this = this.0.write(); let this = this.node.write();
(this, other) (this, other)
} }
} }

View File

@ -8,5 +8,5 @@ mod fs;
const RAMFS_MAGIC: u64 = 0x0102_1994; const RAMFS_MAGIC: u64 = 0x0102_1994;
const BLOCK_SIZE: usize = 4096; const BLOCK_SIZE: usize = 4096;
const ROOT_INO: usize = 1; const ROOT_INO: u64 = 1;
const NAME_MAX: usize = 255; const NAME_MAX: usize = 255;