Remove Vnode to let the fs use PageCache for itself

This commit is contained in:
LI Qing
2023-09-13 12:07:58 +08:00
committed by Tate, Hongliang Tian
parent aeea333945
commit 98bf3d4845
30 changed files with 483 additions and 533 deletions

View File

@ -27,7 +27,7 @@ pub fn init() -> Result<()> {
InodeType::SymLink,
InodeMode::from_bits_truncate(0o777),
)?;
ptmx.write_link("pts/ptmx")?;
ptmx.inode().write_link("pts/ptmx")?;
Ok(())
}

View File

@ -46,6 +46,16 @@ impl Inode for PtyMasterInode {
self.0.ptmx().metadata()
}
fn type_(&self) -> InodeType {
self.0.ptmx().metadata().type_
}
fn mode(&self) -> InodeMode {
self.0.ptmx().metadata().mode
}
fn set_mode(&self, mode: InodeMode) {}
fn atime(&self) -> Duration {
self.0.ptmx().metadata().atime
}
@ -58,8 +68,6 @@ impl Inode for PtyMasterInode {
fn set_mtime(&self, time: Duration) {}
fn set_mode(&self, mode: InodeMode) {}
fn read_page(&self, idx: usize, frame: &VmFrame) -> Result<()> {
Ok(())
}
@ -72,10 +80,18 @@ impl Inode for PtyMasterInode {
self.0.read(buf)
}
fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
self.0.read(buf)
}
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
self.0.write(buf)
}
fn write_direct_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
self.0.write(buf)
}
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
self.0.ioctl(cmd, arg)
}

View File

@ -94,7 +94,7 @@ impl FileSystem for DevPts {
}
fn flags(&self) -> FsFlags {
FsFlags::NO_PAGECACHE
FsFlags::empty()
}
}
@ -148,6 +148,16 @@ impl Inode for RootInode {
self.metadata.clone()
}
fn type_(&self) -> InodeType {
self.metadata.type_
}
fn mode(&self) -> InodeMode {
self.metadata.mode
}
fn set_mode(&self, mode: InodeMode) {}
fn atime(&self) -> Duration {
self.metadata.atime
}
@ -160,8 +170,6 @@ impl Inode for RootInode {
fn set_mtime(&self, time: Duration) {}
fn set_mode(&self, mode: InodeMode) {}
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::EPERM))
}

View File

@ -66,6 +66,16 @@ impl Inode for Ptmx {
self.metadata.clone()
}
fn type_(&self) -> InodeType {
self.metadata.type_
}
fn mode(&self) -> InodeMode {
self.metadata.mode
}
fn set_mode(&self, mode: InodeMode) {}
fn atime(&self) -> Duration {
self.metadata.atime
}
@ -78,8 +88,6 @@ impl Inode for Ptmx {
fn set_mtime(&self, time: Duration) {}
fn set_mode(&self, mode: InodeMode) {}
fn read_page(&self, idx: usize, frame: &VmFrame) -> Result<()> {
Ok(())
}
@ -92,10 +100,18 @@ impl Inode for Ptmx {
Ok(0)
}
fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
Ok(0)
}
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
Ok(0)
}
fn write_direct_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
Ok(0)
}
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
Ok(0)
}

View File

@ -50,6 +50,16 @@ impl Inode for PtySlaveInode {
self.metadata.clone()
}
fn type_(&self) -> InodeType {
self.metadata.type_
}
fn mode(&self) -> InodeMode {
self.metadata.mode
}
fn set_mode(&self, mode: InodeMode) {}
fn atime(&self) -> Duration {
self.metadata.atime
}
@ -62,8 +72,6 @@ impl Inode for PtySlaveInode {
fn set_mtime(&self, time: Duration) {}
fn set_mode(&self, mode: InodeMode) {}
fn read_page(&self, idx: usize, frame: &VmFrame) -> Result<()> {
Ok(())
}
@ -76,10 +84,18 @@ impl Inode for PtySlaveInode {
self.device.read(buf)
}
fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
self.device.read(buf)
}
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
self.device.write(buf)
}
fn write_direct_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
self.device.write(buf)
}
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
self.device.ioctl(cmd, arg)
}

View File

@ -62,8 +62,8 @@ impl FsResolver {
let follow_tail_link = !creation_flags.contains(CreationFlags::O_NOFOLLOW);
let dentry = match self.lookup_inner(path, follow_tail_link) {
Ok(dentry) => {
let vnode = dentry.vnode();
if vnode.inode_type() == InodeType::SymLink
let inode = dentry.inode();
if inode.type_() == InodeType::SymLink
&& !status_flags.contains(StatusFlags::O_PATH)
{
return_errno_with_message!(Errno::ELOOP, "file is a symlink");
@ -74,7 +74,7 @@ impl FsResolver {
return_errno_with_message!(Errno::EEXIST, "file exists");
}
if creation_flags.contains(CreationFlags::O_DIRECTORY)
&& vnode.inode_type() != InodeType::Dir
&& inode.type_() != InodeType::Dir
{
return_errno_with_message!(
Errno::ENOTDIR,
@ -95,7 +95,7 @@ impl FsResolver {
if file_name.ends_with('/') {
return_errno_with_message!(Errno::EISDIR, "path refers to a directory");
}
if !dir_dentry.vnode().inode_mode().is_writable() {
if !dir_dentry.inode_mode().is_writable() {
return_errno_with_message!(Errno::EACCES, "file cannot be created");
}
dir_dentry.create(&file_name, InodeType::File, inode_mode)?
@ -177,7 +177,7 @@ impl FsResolver {
// Iterate next dentry
let next_dentry = dentry.lookup(next_name)?;
let next_type = next_dentry.vnode().inode_type();
let next_type = next_dentry.inode_type();
let next_is_tail = path_remain.is_empty();
// If next inode is a symlink, follow symlinks at most `SYMLINKS_MAX` times.
@ -186,7 +186,7 @@ impl FsResolver {
return_errno_with_message!(Errno::ELOOP, "too many symlinks");
}
let link_path_remain = {
let mut tmp_link_path = next_dentry.vnode().read_link()?;
let mut tmp_link_path = next_dentry.inode().read_link()?;
if tmp_link_path.is_empty() {
return_errno_with_message!(Errno::ENOENT, "empty symlink");
}
@ -275,9 +275,9 @@ impl FsResolver {
// Dereference the tail symlinks if needed
loop {
match dir_dentry.lookup(base_name.trim_end_matches('/')) {
Ok(dentry) if dentry.vnode().inode_type() == InodeType::SymLink => {
Ok(dentry) if dentry.inode_type() == InodeType::SymLink => {
let link = {
let mut link = dentry.vnode().read_link()?;
let mut link = dentry.inode().read_link()?;
if link.is_empty() {
return_errno_with_message!(Errno::ENOENT, "invalid symlink");
}

View File

@ -11,14 +11,14 @@ impl InodeHandle<Rights> {
access_mode: AccessMode,
status_flags: StatusFlags,
) -> Result<Self> {
let vnode = dentry.vnode();
if access_mode.is_readable() && !vnode.inode_mode().is_readable() {
let inode = dentry.inode();
if access_mode.is_readable() && !inode.mode().is_readable() {
return_errno_with_message!(Errno::EACCES, "File is not readable");
}
if access_mode.is_writable() && !vnode.inode_mode().is_writable() {
if access_mode.is_writable() && !inode.mode().is_writable() {
return_errno_with_message!(Errno::EACCES, "File is not writable");
}
if access_mode.is_writable() && vnode.inode_type() == InodeType::Dir {
if access_mode.is_writable() && inode.type_() == InodeType::Dir {
return_errno_with_message!(Errno::EISDIR, "Directory cannot open to write");
}
let inner = Arc::new(InodeHandle_ {
@ -75,15 +75,15 @@ impl FileLike for InodeHandle<Rights> {
}
fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
self.dentry().vnode().poll(mask, poller)
self.dentry().inode().poll(mask, poller)
}
fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
self.dentry().vnode().ioctl(cmd, arg)
self.dentry().inode().ioctl(cmd, arg)
}
fn metadata(&self) -> Metadata {
self.dentry().vnode().metadata()
self.dentry().inode_metadata()
}
fn status_flags(&self) -> StatusFlags {

View File

@ -26,9 +26,9 @@ impl InodeHandle_ {
pub fn read(&self, buf: &mut [u8]) -> Result<usize> {
let mut offset = self.offset.lock();
let len = if self.status_flags().contains(StatusFlags::O_DIRECT) {
self.dentry.vnode().read_direct_at(*offset, buf)?
self.dentry.inode().read_direct_at(*offset, buf)?
} else {
self.dentry.vnode().read_at(*offset, buf)?
self.dentry.inode().read_at(*offset, buf)?
};
*offset += len;
@ -38,12 +38,12 @@ impl InodeHandle_ {
pub fn write(&self, buf: &[u8]) -> Result<usize> {
let mut offset = self.offset.lock();
if self.status_flags().contains(StatusFlags::O_APPEND) {
*offset = self.dentry.vnode().len();
*offset = self.dentry.inode_len();
}
let len = if self.status_flags().contains(StatusFlags::O_DIRECT) {
self.dentry.vnode().write_direct_at(*offset, buf)?
self.dentry.inode().write_direct_at(*offset, buf)?
} else {
self.dentry.vnode().write_at(*offset, buf)?
self.dentry.inode().write_at(*offset, buf)?
};
*offset += len;
@ -52,9 +52,9 @@ impl InodeHandle_ {
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
let len = if self.status_flags().contains(StatusFlags::O_DIRECT) {
self.dentry.vnode().read_direct_to_end(buf)?
self.dentry.inode().read_direct_to_end(buf)?
} else {
self.dentry.vnode().read_to_end(buf)?
self.dentry.inode().read_to_end(buf)?
};
Ok(len)
}
@ -69,7 +69,7 @@ impl InodeHandle_ {
off as isize
}
SeekFrom::End(off /* as isize */) => {
let file_size = self.dentry.vnode().len() as isize;
let file_size = self.dentry.inode_len() as isize;
assert!(file_size >= 0);
file_size
.checked_add(off)
@ -94,7 +94,7 @@ impl InodeHandle_ {
}
pub fn len(&self) -> usize {
self.dentry.vnode().len()
self.dentry.inode_len()
}
pub fn access_mode(&self) -> AccessMode {
@ -113,7 +113,7 @@ impl InodeHandle_ {
pub fn readdir(&self, visitor: &mut dyn DirentVisitor) -> Result<usize> {
let mut offset = self.offset.lock();
let read_cnt = self.dentry.vnode().readdir_at(*offset, visitor)?;
let read_cnt = self.dentry.inode().readdir_at(*offset, visitor)?;
*offset += read_cnt;
Ok(read_cnt)
}

View File

@ -61,7 +61,7 @@ impl FileSystem for ProcFS {
}
fn flags(&self) -> FsFlags {
FsFlags::NO_PAGECACHE
FsFlags::empty()
}
}

View File

@ -64,6 +64,18 @@ impl<D: DirOps + 'static> Inode for ProcDir<D> {
self.info.metadata()
}
fn type_(&self) -> InodeType {
InodeType::Dir
}
fn mode(&self) -> InodeMode {
self.info.mode()
}
fn set_mode(&self, mode: InodeMode) {
self.info.set_mode(mode)
}
fn atime(&self) -> Duration {
self.info.atime()
}
@ -80,10 +92,6 @@ impl<D: DirOps + 'static> Inode for ProcDir<D> {
self.info.set_mtime(time)
}
fn set_mode(&self, mode: InodeMode) {
self.info.set_mode(mode)
}
fn create(&self, _name: &str, _type_: InodeType, _mode: InodeMode) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::EPERM))
}

View File

@ -1,7 +1,7 @@
use core::time::Duration;
use jinux_frame::vm::VmFrame;
use crate::fs::utils::{FileSystem, Inode, InodeMode, IoctlCmd, Metadata};
use crate::fs::utils::{FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata};
use crate::prelude::*;
use super::{ProcFS, ProcInodeInfo};
@ -37,6 +37,18 @@ impl<F: FileOps + 'static> Inode for ProcFile<F> {
self.info.metadata()
}
fn type_(&self) -> InodeType {
InodeType::File
}
fn mode(&self) -> InodeMode {
self.info.mode()
}
fn set_mode(&self, mode: InodeMode) {
self.info.set_mode(mode)
}
fn atime(&self) -> Duration {
self.info.atime()
}
@ -53,10 +65,6 @@ impl<F: FileOps + 'static> Inode for ProcFile<F> {
self.info.set_mtime(time)
}
fn set_mode(&self, mode: InodeMode) {
self.info.set_mode(mode)
}
fn read_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
unreachable!()
}
@ -74,10 +82,18 @@ impl<F: FileOps + 'static> Inode for ProcFile<F> {
Ok(len)
}
fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
self.read_at(offset, buf)
}
fn write_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn write_direct_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn read_link(&self) -> Result<String> {
Err(Error::new(Errno::EINVAL))
}

View File

@ -58,6 +58,10 @@ impl ProcInodeInfo {
self.metadata.write().mtime = time;
}
pub fn mode(&self) -> InodeMode {
self.metadata.read().mode
}
pub fn set_mode(&self, mode: InodeMode) {
self.metadata.write().mode = mode;
}

View File

@ -1,7 +1,7 @@
use core::time::Duration;
use jinux_frame::vm::VmFrame;
use crate::fs::utils::{FileSystem, Inode, InodeMode, IoctlCmd, Metadata};
use crate::fs::utils::{FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata};
use crate::prelude::*;
use super::{ProcFS, ProcInodeInfo};
@ -37,6 +37,18 @@ impl<S: SymOps + 'static> Inode for ProcSym<S> {
self.info.metadata()
}
fn type_(&self) -> InodeType {
InodeType::SymLink
}
fn mode(&self) -> InodeMode {
self.info.mode()
}
fn set_mode(&self, mode: InodeMode) {
self.info.set_mode(mode)
}
fn atime(&self) -> Duration {
self.info.atime()
}
@ -53,10 +65,6 @@ impl<S: SymOps + 'static> Inode for ProcSym<S> {
self.info.set_mtime(time)
}
fn set_mode(&self, mode: InodeMode) {
self.info.set_mode(mode)
}
fn read_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
@ -69,10 +77,18 @@ impl<S: SymOps + 'static> Inode for ProcSym<S> {
Err(Error::new(Errno::EPERM))
}
fn read_direct_at(&self, _offset: usize, _buf: &mut [u8]) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn write_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn write_direct_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn read_link(&self) -> Result<String> {
self.inner.read_link()
}

View File

@ -1,49 +1,41 @@
use crate::events::IoEvents;
use crate::prelude::*;
use crate::process::signal::Poller;
use alloc::str;
use alloc::string::String;
use core::sync::atomic::{AtomicUsize, Ordering};
use core::time::Duration;
use jinux_frame::sync::{RwLock, RwLockWriteGuard};
use jinux_frame::vm::VmFrame;
use jinux_frame::vm::{VmFrame, VmIo};
use jinux_rights::Full;
use jinux_util::slot_vec::SlotVec;
use super::*;
use crate::events::IoEvents;
use crate::fs::device::Device;
use crate::fs::utils::{
DirentVisitor, FileSystem, FsFlags, Inode, InodeMode, InodeType, IoctlCmd, Metadata,
SuperBlock, NAME_MAX,
DirentVisitor, FileSystem, FsFlags, Inode, InodeMode, InodeType, IoctlCmd, Metadata, PageCache,
SuperBlock,
};
use crate::prelude::*;
use crate::process::signal::Poller;
use crate::vm::vmo::Vmo;
/// A volatile file system whose data and metadata exists only in memory.
pub struct RamFS {
metadata: RwLock<SuperBlock>,
root: Arc<RamInode>,
inode_allocator: AtomicUsize,
flags: FsFlags,
}
impl RamFS {
pub fn new(use_pagecache: bool) -> Arc<Self> {
pub fn new() -> Arc<Self> {
let sb = SuperBlock::new(RAMFS_MAGIC, BLOCK_SIZE, NAME_MAX);
let root = Arc::new(RamInode(RwLock::new(Inode_::new_dir(
ROOT_INO,
InodeMode::from_bits_truncate(0o755),
&sb,
))));
let flags = {
let mut flags = FsFlags::DENTRY_UNEVICTABLE;
if !use_pagecache {
flags |= FsFlags::NO_PAGECACHE;
}
flags
};
let ramfs = Arc::new(Self {
metadata: RwLock::new(sb),
root,
inode_allocator: AtomicUsize::new(ROOT_INO + 1),
flags,
});
let mut root = ramfs.root.0.write();
root.inner
@ -78,7 +70,7 @@ impl FileSystem for RamFS {
}
fn flags(&self) -> FsFlags {
self.flags
FsFlags::DENTRY_UNEVICTABLE
}
}
@ -101,9 +93,14 @@ impl Inode_ {
}
}
pub fn new_file(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
pub fn new_file(
ino: usize,
mode: InodeMode,
sb: &SuperBlock,
weak_inode: Weak<RamInode>,
) -> Self {
Self {
inner: Inner::File,
inner: Inner::File(PageCache::new(weak_inode).unwrap()),
metadata: Metadata::new_file(ino, mode, sb),
this: Weak::default(),
fs: Weak::default(),
@ -112,7 +109,7 @@ impl Inode_ {
pub fn new_symlink(ino: usize, mode: InodeMode, sb: &SuperBlock) -> Self {
Self {
inner: Inner::SymLink(Str256::from("")),
inner: Inner::SymLink(String::from("")),
metadata: Metadata::new_symlink(ino, mode, sb),
this: Weak::default(),
fs: Weak::default(),
@ -157,18 +154,34 @@ impl Inode_ {
self.metadata.size = new_size;
self.metadata.blocks = (new_size + BLOCK_SIZE - 1) / BLOCK_SIZE;
}
pub fn inc_nlinks(&mut self) {
self.metadata.nlinks += 1;
}
pub fn dec_nlinks(&mut self) {
debug_assert!(self.metadata.nlinks > 0);
self.metadata.nlinks -= 1;
}
}
#[allow(clippy::large_enum_variant)]
enum Inner {
Dir(DirEntry),
File,
SymLink(Str256),
File(PageCache),
SymLink(String),
Device(Arc<dyn Device>),
Socket,
}
impl Inner {
fn as_file(&self) -> Option<&PageCache> {
match self {
Inner::File(page_cache) => Some(page_cache),
_ => None,
}
}
fn as_direntry(&self) -> Option<&DirEntry> {
match self {
Inner::Dir(dir_entry) => Some(dir_entry),
@ -190,7 +203,7 @@ impl Inner {
}
}
fn as_symlink_mut(&mut self) -> Option<&mut Str256> {
fn as_symlink_mut(&mut self) -> Option<&mut String> {
match self {
Inner::SymLink(link) => Some(link),
_ => None,
@ -235,7 +248,7 @@ impl DirEntry {
} else {
self.children
.iter()
.any(|(child, _)| child == &Str256::from(name))
.any(|(child, _)| child.as_ref() == name)
}
}
@ -247,7 +260,7 @@ impl DirEntry {
} else {
self.children
.idxes_and_items()
.find(|(_, (child, _))| child == &Str256::from(name))
.find(|(_, (child, _))| child.as_ref() == name)
.map(|(idx, (_, inode))| (idx + 2, inode.clone()))
}
}
@ -344,7 +357,7 @@ impl<'a> From<&'a str> for Str256 {
s.len()
};
inner[0..len].copy_from_slice(&s.as_bytes()[0..len]);
Str256(inner)
Self(inner)
}
}
@ -373,7 +386,12 @@ impl RamInode {
fn new_file(fs: &Arc<RamFS>, mode: InodeMode) -> Arc<Self> {
Arc::new_cyclic(|weak_self| {
let inode = RamInode(RwLock::new(Inode_::new_file(fs.alloc_id(), mode, &fs.sb())));
let inode = RamInode(RwLock::new(Inode_::new_file(
fs.alloc_id(),
mode,
&fs.sb(),
weak_self.clone(),
)));
inode.0.write().fs = Arc::downgrade(fs);
inode.0.write().this = weak_self.clone();
inode
@ -432,18 +450,65 @@ impl Inode for RamInode {
Ok(())
}
fn read_at(&self, _offset: usize, buf: &mut [u8]) -> Result<usize> {
fn page_cache(&self) -> Option<Vmo<Full>> {
self.0
.read()
.inner
.as_file()
.map(|page_cache| page_cache.pages())
}
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
if let Some(device) = self.0.read().inner.as_device() {
return device.read(buf);
}
return_errno_with_message!(Errno::EOPNOTSUPP, "direct read is not supported");
let self_inode = self.0.read();
let Some(page_cache) = self_inode.inner.as_file() else {
return_errno_with_message!(Errno::EISDIR, "read is not supported");
};
let (offset, read_len) = {
let file_len = self_inode.metadata.size;
let start = file_len.min(offset);
let end = file_len.min(offset + buf.len());
(start, end - start)
};
page_cache
.pages()
.read_bytes(offset, &mut buf[..read_len])?;
Ok(read_len)
}
fn write_at(&self, _offset: usize, buf: &[u8]) -> Result<usize> {
fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
self.read_at(offset, buf)
}
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
if let Some(device) = self.0.read().inner.as_device() {
return device.write(buf);
}
return_errno_with_message!(Errno::EOPNOTSUPP, "direct write is not supported");
let self_inode = self.0.read();
let Some(page_cache) = self_inode.inner.as_file() else {
return_errno_with_message!(Errno::EISDIR, "write is not supported");
};
let file_len = self_inode.metadata.size;
let new_len = offset + buf.len();
let should_expand_len = new_len > file_len;
if should_expand_len {
page_cache.pages().resize(new_len)?;
}
page_cache.pages().write_bytes(offset, buf)?;
if should_expand_len {
// Turn the read guard into a write guard without releasing the lock.
let mut self_inode = self_inode.upgrade();
self_inode.resize(new_len);
}
Ok(buf.len())
}
fn write_direct_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
self.write_at(offset, buf)
}
fn len(&self) -> usize {
@ -470,6 +535,14 @@ impl Inode for RamInode {
self.0.write().metadata.mtime = time;
}
fn type_(&self) -> InodeType {
self.0.read().metadata.type_
}
fn mode(&self) -> InodeMode {
self.0.read().metadata.mode
}
fn set_mode(&self, mode: InodeMode) {
self.0.write().metadata.mode = mode;
}
@ -483,6 +556,10 @@ impl Inode for RamInode {
if self.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
if name.len() > NAME_MAX {
return_errno!(Errno::ENAMETOOLONG);
}
let mut self_inode = self.0.write();
if self_inode.inner.as_direntry().unwrap().contains_entry(name) {
return_errno_with_message!(Errno::EEXIST, "entry exists");
@ -501,6 +578,10 @@ impl Inode for RamInode {
if self.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
if name.len() > NAME_MAX {
return_errno!(Errno::ENAMETOOLONG);
}
let mut self_inode = self.0.write();
if self_inode.inner.as_direntry().unwrap().contains_entry(name) {
return_errno_with_message!(Errno::EEXIST, "entry exists");
@ -512,7 +593,7 @@ impl Inode for RamInode {
InodeType::Socket => RamInode::new_socket(&fs, mode),
InodeType::Dir => {
let dir_inode = RamInode::new_dir(&fs, mode, &self_inode.this);
self_inode.metadata.nlinks += 1;
self_inode.inc_nlinks();
dir_inode
}
_ => {
@ -545,6 +626,9 @@ impl Inode for RamInode {
if self.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
if !Arc::ptr_eq(&self.fs(), &old.fs()) {
return_errno_with_message!(Errno::EXDEV, "not same fs");
}
let old = old
.downcast_ref::<RamInode>()
.ok_or(Error::new(Errno::EXDEV))?;
@ -563,7 +647,7 @@ impl Inode for RamInode {
.append_entry(name, old.0.read().this.upgrade().unwrap());
self_inode.inc_size();
drop(self_inode);
old.0.write().metadata.nlinks += 1;
old.0.write().inc_nlinks();
Ok(())
}
@ -583,7 +667,7 @@ impl Inode for RamInode {
self_dir.remove_entry(idx);
self_inode.dec_size();
drop(self_inode);
target.0.write().metadata.nlinks -= 1;
target.0.write().dec_nlinks();
Ok(())
}
@ -612,9 +696,11 @@ impl Inode for RamInode {
}
self_dir.remove_entry(idx);
self_inode.dec_size();
self_inode.metadata.nlinks -= 1;
self_inode.dec_nlinks();
drop(self_inode);
target.0.write().metadata.nlinks -= 2;
let mut target_inode = self.0.write();
target_inode.dec_nlinks();
target_inode.dec_nlinks();
Ok(())
}
@ -638,6 +724,9 @@ impl Inode for RamInode {
if self.0.read().metadata.type_ != InodeType::Dir {
return_errno_with_message!(Errno::ENOTDIR, "self is not dir");
}
if !Arc::ptr_eq(&self.fs(), &target.fs()) {
return_errno_with_message!(Errno::EXDEV, "not same fs");
}
let target = target
.downcast_ref::<RamInode>()
.ok_or(Error::new(Errno::EXDEV))?;
@ -703,8 +792,8 @@ impl Inode for RamInode {
self_inode.dec_size();
target_inode.inc_size();
if src_inode.0.read().metadata.type_ == InodeType::Dir {
self_inode.metadata.nlinks -= 1;
target_inode.metadata.nlinks += 1;
self_inode.dec_nlinks();
target_inode.inc_nlinks();
}
drop(self_inode);
drop(target_inode);
@ -736,7 +825,7 @@ impl Inode for RamInode {
}
let mut self_inode = self.0.write();
let link = self_inode.inner.as_symlink_mut().unwrap();
*link = Str256::from(target);
*link = String::from(target);
// Symlink's metadata.blocks should be 0, so just set the size.
self_inode.metadata.size = target.len();
Ok(())

View File

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

View File

@ -12,7 +12,7 @@ use spin::Once;
/// Unpack and prepare the rootfs from the initramfs CPIO buffer.
pub fn init(initramfs_buf: &[u8]) -> Result<()> {
init_root_mount()?;
init_root_mount();
println!("[kernel] unpacking the initramfs.cpio.gz to rootfs ...");
let fs = FsResolver::new();
@ -52,7 +52,7 @@ pub fn init(initramfs_buf: &[u8]) -> Result<()> {
match metadata.file_type() {
FileType::File => {
let dentry = parent.create(name, InodeType::File, mode)?;
entry.read_all(dentry.vnode().writer(0))?;
entry.read_all(dentry.inode().writer(0))?;
}
FileType::Dir => {
let _ = parent.create(name, InodeType::Dir, mode)?;
@ -64,7 +64,7 @@ pub fn init(initramfs_buf: &[u8]) -> Result<()> {
entry.read_all(&mut link_data)?;
core::str::from_utf8(&link_data)?.to_string()
};
dentry.vnode().write_link(&link_content)?;
dentry.inode().write_link(&link_content)?;
}
type_ => {
panic!("unsupported file type = {:?} in initramfs", type_);
@ -76,7 +76,7 @@ pub fn init(initramfs_buf: &[u8]) -> Result<()> {
proc_dentry.mount(ProcFS::new())?;
// Mount DevFS
let dev_dentry = fs.lookup(&FsPath::try_from("/dev")?)?;
dev_dentry.mount(RamFS::new(false))?;
dev_dentry.mount(RamFS::new())?;
println!("[kernel] rootfs is ready");
Ok(())
@ -84,14 +84,11 @@ pub fn init(initramfs_buf: &[u8]) -> Result<()> {
static ROOT_MOUNT: Once<Arc<MountNode>> = Once::new();
fn init_root_mount() -> Result<()> {
ROOT_MOUNT.try_call_once(|| -> Result<Arc<MountNode>> {
let rootfs = RamFS::new(true);
let root_mount = MountNode::new_root(rootfs)?;
Ok(root_mount)
})?;
Ok(())
fn init_root_mount() {
ROOT_MOUNT.call_once(|| -> Arc<MountNode> {
let rootfs = RamFS::new();
MountNode::new_root(rootfs)
});
}
pub fn root_mount() -> &'static Arc<MountNode> {

View File

@ -5,7 +5,7 @@ use alloc::string::String;
use core::sync::atomic::{AtomicU32, Ordering};
use core::time::Duration;
use super::{FileSystem, Inode, InodeMode, InodeType, Metadata, MountNode, Vnode, NAME_MAX};
use super::{FileSystem, Inode, InodeMode, InodeType, Metadata, MountNode, NAME_MAX};
lazy_static! {
static ref DCACHE: Mutex<BTreeMap<DentryKey, Arc<Dentry>>> = Mutex::new(BTreeMap::new());
@ -13,7 +13,7 @@ lazy_static! {
/// The dentry cache to accelerate path lookup
pub struct Dentry {
vnode: Vnode,
inode: Arc<dyn Inode>,
name_and_parent: RwLock<Option<(String, Arc<Dentry>)>>,
this: Weak<Dentry>,
children: Mutex<Children>,
@ -22,21 +22,21 @@ pub struct Dentry {
}
impl Dentry {
/// Create a new root dentry with the giving vnode and mount node.
/// Create a new root dentry with the giving inode 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<MountNode>) -> Arc<Self> {
let root = Self::new(vnode, DentryOptions::Root(mount));
pub(super) fn new_root(inode: Arc<dyn Inode>, mount: Weak<MountNode>) -> Arc<Self> {
let root = Self::new(inode, DentryOptions::Root(mount));
DCACHE.lock().insert(root.key(), root.clone());
root
}
/// Internal constructor.
fn new(vnode: Vnode, options: DentryOptions) -> Arc<Self> {
fn new(inode: Arc<dyn Inode>, options: DentryOptions) -> Arc<Self> {
Arc::new_cyclic(|weak_self| Self {
vnode,
inode,
mount_node: match &options {
DentryOptions::Root(mount) => mount.clone(),
DentryOptions::Leaf(name_and_parent) => name_and_parent.1.mount_node.clone(),
@ -129,9 +129,9 @@ impl Dentry {
DentryKey::new(self)
}
/// Get the vnode.
pub fn vnode(&self) -> &Vnode {
&self.vnode
/// Get the inode.
pub fn inode(&self) -> &Arc<dyn Inode> {
&self.inode
}
/// Get the DentryFlags.
@ -164,13 +164,9 @@ impl Dentry {
self.mount_node.upgrade().unwrap()
}
pub fn inode(&self) -> Weak<dyn Inode> {
self.vnode.inode()
}
/// 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 {
if self.inode.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR);
}
let mut children = self.children.lock();
@ -179,9 +175,9 @@ impl Dentry {
}
let child = {
let vnode = self.vnode.create(name, type_, mode)?;
let inode = self.inode.create(name, type_, mode)?;
let dentry = Self::new(
vnode,
inode,
DentryOptions::Leaf((String::from(name), self.this())),
);
children.insert_dentry(&dentry);
@ -192,7 +188,7 @@ impl Dentry {
/// Create a dentry by making a device inode.
pub fn mknod(&self, name: &str, mode: InodeMode, device: Arc<dyn Device>) -> Result<Arc<Self>> {
if self.vnode.inode_type() != InodeType::Dir {
if self.inode.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR);
}
let mut children = self.children.lock();
@ -201,9 +197,9 @@ impl Dentry {
}
let child = {
let vnode = self.vnode.mknod(name, mode, device)?;
let inode = self.inode.mknod(name, mode, device)?;
let dentry = Self::new(
vnode,
inode,
DentryOptions::Leaf((String::from(name), self.this())),
);
children.insert_dentry(&dentry);
@ -214,10 +210,10 @@ impl Dentry {
/// Lookup a dentry.
pub fn lookup(&self, name: &str) -> Result<Arc<Self>> {
if self.vnode.inode_type() != InodeType::Dir {
if self.inode.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR);
}
if !self.vnode.inode_mode().is_executable() {
if !self.inode.mode().is_executable() {
return_errno!(Errno::EACCES);
}
if name.len() > NAME_MAX {
@ -232,9 +228,9 @@ impl Dentry {
match children.find_dentry(name) {
Some(dentry) => dentry.overlaid_dentry(),
None => {
let vnode = self.vnode.lookup(name)?;
let inode = self.inode.lookup(name)?;
let dentry = Self::new(
vnode,
inode,
DentryOptions::Leaf((String::from(name), self.this())),
);
children.insert_dentry(&dentry);
@ -248,7 +244,7 @@ impl 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 {
if self.inode.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR);
}
let mut children = self.children.lock();
@ -258,10 +254,10 @@ impl Dentry {
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 old_inode = old.inode();
self.inode.link(old_inode, name)?;
let dentry = Self::new(
old_vnode.clone(),
old_inode.clone(),
DentryOptions::Leaf((String::from(name), self.this())),
);
children.insert_dentry(&dentry);
@ -270,51 +266,34 @@ impl Dentry {
/// Delete a dentry by unlinking inode.
pub fn unlink(&self, name: &str) -> Result<()> {
if self.vnode.inode_type() != InodeType::Dir {
if self.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)?;
self.inode.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 {
if self.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)?;
self.inode.rmdir(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);
}
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<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");
}
if self.vnode.inode_type() != InodeType::Dir || new_dir.vnode.inode_type() != InodeType::Dir
{
if self.inode.type_() != InodeType::Dir || new_dir.inode.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR);
}
@ -326,7 +305,7 @@ impl Dentry {
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)?;
self.inode.rename(old_name, &self.inode, new_name)?;
match old_dentry.as_ref() {
Some(dentry) => {
children.delete_dentry(old_name);
@ -346,7 +325,7 @@ impl Dentry {
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)?;
self.inode.rename(old_name, &new_dir.inode, new_name)?;
match old_dentry.as_ref() {
Some(dentry) => {
self_children.delete_dentry(old_name);
@ -369,7 +348,7 @@ impl Dentry {
///
/// Return the mounted child mount.
pub fn mount(&self, fs: Arc<dyn FileSystem>) -> Result<Arc<MountNode>> {
if self.vnode.inode_type() != InodeType::Dir {
if self.inode.type_() != InodeType::Dir {
return_errno!(Errno::ENOTDIR);
}
if self.effective_parent().is_none() {
@ -401,52 +380,52 @@ impl Dentry {
/// Get the filesystem the inode belongs to
pub fn fs(&self) -> Arc<dyn FileSystem> {
self.vnode.fs()
self.inode.fs()
}
/// Get the inode metadata
pub fn inode_metadata(&self) -> Metadata {
self.vnode.metadata()
self.inode.metadata()
}
/// Get the inode type
pub fn inode_type(&self) -> InodeType {
self.vnode.inode_type()
self.inode.type_()
}
/// Get the inode permission mode
pub fn inode_mode(&self) -> InodeMode {
self.vnode.inode_mode()
self.inode.mode()
}
/// Set the inode permission mode
pub fn set_inode_mode(&self, mode: InodeMode) {
self.vnode.set_inode_mode(mode)
self.inode.set_mode(mode)
}
/// Get the inode length
pub fn inode_len(&self) -> usize {
self.vnode.len()
self.inode.len()
}
/// Get the access timestamp
pub fn atime(&self) -> Duration {
self.vnode.atime()
self.inode.atime()
}
/// Set the access timestamp
pub fn set_atime(&self, time: Duration) {
self.vnode.set_atime(time)
self.inode.set_atime(time)
}
/// Get the modified timestamp
pub fn mtime(&self) -> Duration {
self.vnode.mtime()
self.inode.mtime()
}
/// Set the modified timestamp
pub fn set_mtime(&self, time: Duration) {
self.vnode.set_mtime(time)
self.inode.set_mtime(time)
}
/// Get the absolute path.
@ -482,7 +461,7 @@ impl Debug for Dentry {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("Dentry")
.field("abs_path", &self.abs_path())
.field("vnode", &self.vnode)
.field("inode", &self.inode)
.field("flags", &self.flags())
.finish()
}
@ -537,7 +516,7 @@ impl Children {
pub fn insert_dentry(&mut self, dentry: &Arc<Dentry>) {
// Do not cache it in DCACHE and children if is not cacheable.
// When we look up it from the parent, it will always be newly created.
if !dentry.vnode().is_dentry_cacheable() {
if !dentry.inode().is_dentry_cacheable() {
return;
}

View File

@ -1,7 +1,3 @@
use alloc::sync::Arc;
use bitflags::bitflags;
use core::any::Any;
use super::Inode;
use crate::prelude::*;
@ -40,8 +36,6 @@ impl SuperBlock {
bitflags! {
pub struct FsFlags: u32 {
/// Disable page cache.
const NO_PAGECACHE = 1 << 0;
/// Dentry cannot be evicted.
const DENTRY_UNEVICTABLE = 1 << 1;
}

View File

@ -1,18 +1,17 @@
use alloc::string::String;
use alloc::sync::Arc;
use bitflags::bitflags;
use core::any::Any;
use core::time::Duration;
use core2::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, Write};
use jinux_frame::vm::VmFrame;
use jinux_rights::Full;
use super::{DirentVisitor, FileSystem, IoctlCmd, SuperBlock};
use crate::events::IoEvents;
use crate::fs::device::{Device, DeviceType};
use crate::prelude::*;
use crate::process::signal::Poller;
use crate::vm::vmo::Vmo;
#[repr(u32)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, TryFromInt)]
pub enum InodeType {
NamedPipe = 0o010000,
CharDevice = 0o020000,
@ -236,6 +235,12 @@ pub trait Inode: Any + Sync + Send {
fn metadata(&self) -> Metadata;
fn type_(&self) -> InodeType;
fn mode(&self) -> InodeMode;
fn set_mode(&self, mode: InodeMode);
fn atime(&self) -> Duration;
fn set_atime(&self, time: Duration);
@ -244,8 +249,6 @@ pub trait Inode: Any + Sync + Send {
fn set_mtime(&self, time: Duration);
fn set_mode(&self, mode: InodeMode);
fn read_page(&self, idx: usize, frame: &VmFrame) -> Result<()> {
Err(Error::new(Errno::EISDIR))
}
@ -254,14 +257,26 @@ pub trait Inode: Any + Sync + Send {
Err(Error::new(Errno::EISDIR))
}
fn page_cache(&self) -> Option<Vmo<Full>> {
None
}
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
Err(Error::new(Errno::EISDIR))
}
fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
Err(Error::new(Errno::EISDIR))
}
fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
Err(Error::new(Errno::EISDIR))
}
fn write_direct_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
Err(Error::new(Errno::EISDIR))
}
fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::ENOTDIR))
}
@ -343,6 +358,59 @@ impl dyn Inode {
pub fn downcast_ref<T: Inode>(&self) -> Option<&T> {
(self as &dyn Any).downcast_ref::<T>()
}
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
if !self.type_().support_read() {
return_errno!(Errno::EISDIR);
}
let file_len = self.len();
if buf.len() < file_len {
buf.resize(file_len, 0);
}
self.read_at(0, &mut buf[..file_len])
}
pub fn read_direct_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
if !self.type_().support_read() {
return_errno!(Errno::EISDIR);
}
let file_len = self.len();
if buf.len() < file_len {
buf.resize(file_len, 0);
}
self.read_direct_at(0, &mut buf[..file_len])
}
pub fn writer(&self, from_offset: usize) -> InodeWriter {
InodeWriter {
inner: self,
offset: from_offset,
}
}
}
pub struct InodeWriter<'a> {
inner: &'a dyn Inode,
offset: usize,
}
impl<'a> Write for InodeWriter<'a> {
#[inline]
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
let write_len = self
.inner
.write_at(self.offset, buf)
.map_err(|_| IoError::new(IoErrorKind::WriteZero, "failed to write buffer"))?;
self.offset += write_len;
Ok(write_len)
}
#[inline]
fn flush(&mut self) -> IoResult<()> {
Ok(())
}
}
impl Debug for dyn Inode {

View File

@ -13,7 +13,6 @@ pub use ioctl::IoctlCmd;
pub use mount::MountNode;
pub use page_cache::PageCache;
pub use status_flags::StatusFlags;
pub use vnode::{Vnode, VnodeWriter};
mod access_mode;
mod channel;
@ -28,7 +27,6 @@ mod ioctl;
mod mount;
mod page_cache;
mod status_flags;
mod vnode;
#[derive(Copy, PartialEq, Eq, Clone, Debug)]
pub enum SeekFrom {

View File

@ -1,6 +1,6 @@
use crate::prelude::*;
use super::{Dentry, DentryKey, FileSystem, InodeType, Vnode};
use super::{Dentry, DentryKey, FileSystem, InodeType};
/// The MountNode can form a mount tree to maintain the mount information.
pub struct MountNode {
@ -25,22 +25,21 @@ impl MountNode {
///
/// It is allowed to create a mount node even if the fs has been provided to another
/// mount node. It is the fs's responsibility to ensure the data consistency.
pub fn new_root(fs: Arc<dyn FileSystem>) -> Result<Arc<Self>> {
pub fn new_root(fs: Arc<dyn FileSystem>) -> Arc<Self> {
Self::new(fs, None)
}
/// The internal constructor.
///
/// Root mount node has no mountpoint which other mount nodes must have mountpoint.
fn new(fs: Arc<dyn FileSystem>, mountpoint: Option<Arc<Dentry>>) -> Result<Arc<Self>> {
let vnode = Vnode::new(fs.root_inode())?;
Ok(Arc::new_cyclic(|weak_self| Self {
root_dentry: Dentry::new_root(vnode, weak_self.clone()),
fn new(fs: Arc<dyn FileSystem>, mountpoint: Option<Arc<Dentry>>) -> Arc<Self> {
Arc::new_cyclic(|weak_self| Self {
root_dentry: Dentry::new_root(fs.root_inode(), weak_self.clone()),
mountpoint_dentry: mountpoint,
children: Mutex::new(BTreeMap::new()),
fs,
this: weak_self.clone(),
}))
})
}
/// Mount an fs on the mountpoint, it will create a new child mount node.
@ -63,7 +62,7 @@ impl MountNode {
}
let key = mountpoint.key();
let child_mount = Self::new(fs, Some(mountpoint.clone()))?;
let child_mount = Self::new(fs, Some(mountpoint.clone()));
self.children.lock().insert(key, child_mount.clone());
Ok(child_mount)
}

View File

@ -1,6 +1,6 @@
use super::Inode;
use crate::prelude::*;
use crate::vm::vmo::{Pager, Vmo, VmoFlags, VmoOptions};
use crate::vm::vmo::{get_page_idx_range, Pager, Vmo, VmoFlags, VmoOptions};
use jinux_rights::Full;
use core::ops::Range;
@ -13,24 +13,40 @@ pub struct PageCache {
}
impl PageCache {
pub fn new(inode: &Arc<dyn Inode>) -> Result<Self> {
let manager = Arc::new(PageCacheManager::new(Arc::downgrade(inode)));
let pages = VmoOptions::<Full>::new(inode.len())
/// Creates an empty size page cache associated with a new inode.
pub fn new(backed_inode: Weak<dyn Inode>) -> Result<Self> {
let manager = Arc::new(PageCacheManager::new(backed_inode));
let pages = VmoOptions::<Full>::new(0)
.flags(VmoFlags::RESIZABLE)
.pager(manager.clone())
.alloc()?;
Ok(Self { pages, manager })
}
pub fn pages(&self) -> &Vmo<Full> {
&self.pages
/// Creates a page cache associated with an existing inode.
///
/// The `capacity` is the initial cache size required by the inode.
/// It is usually used the same size as the inode.
pub fn with_capacity(capacity: usize, backed_inode: Weak<dyn Inode>) -> Result<Self> {
let manager = Arc::new(PageCacheManager::new(backed_inode));
let pages = VmoOptions::<Full>::new(capacity)
.flags(VmoFlags::RESIZABLE)
.pager(manager.clone())
.alloc()?;
Ok(Self { pages, manager })
}
/// Returns the Vmo object backed by inode.
// TODO: The capability is too highrestrict it to eliminate the possibility of misuse.
// For example, the `resize` api should be forbidded.
pub fn pages(&self) -> Vmo<Full> {
self.pages.dup().unwrap()
}
/// Evict the data within a specified range from the page cache and persist
/// them to the disk.
pub fn evict_range(&self, range: Range<usize>) {
// TODO: Implement this method.
warn!("pagecache: evict_range is not implemented");
pub fn evict_range(&self, range: Range<usize>) -> Result<()> {
self.manager.evict_range(range)
}
}
@ -49,12 +65,31 @@ struct PageCacheManager {
}
impl PageCacheManager {
pub fn new(inode: Weak<dyn Inode>) -> Self {
pub fn new(backed_inode: Weak<dyn Inode>) -> Self {
Self {
pages: Mutex::new(LruCache::unbounded()),
backed_inode: inode,
backed_inode,
}
}
pub fn evict_range(&self, range: Range<usize>) -> Result<()> {
let page_idx_range = get_page_idx_range(&range);
let mut pages = self.pages.lock();
for page_idx in page_idx_range {
if let Some(page) = pages.get_mut(&page_idx) {
if let PageState::Dirty = page.state() {
self.backed_inode
.upgrade()
.unwrap()
.write_page(page_idx, page.frame())?;
page.set_state(PageState::UpToDate);
}
} else {
warn!("page {} is not in page cache, do nothing", page_idx);
}
}
Ok(())
}
}
impl Debug for PageCacheManager {
@ -70,20 +105,18 @@ impl Pager for PageCacheManager {
let page_idx = offset / PAGE_SIZE;
let mut pages = self.pages.lock();
let frame = if let Some(page) = pages.get(&page_idx) {
page.frame()
page.frame().clone()
} else {
let page = if offset < self.backed_inode.upgrade().unwrap().metadata().size {
let mut page = Page::alloc_zero()?;
self.backed_inode
.upgrade()
.unwrap()
.read_page(page_idx, &page.frame())?;
let backed_inode = self.backed_inode.upgrade().unwrap();
let page = if offset < backed_inode.len() {
let mut page = Page::alloc()?;
backed_inode.read_page(page_idx, page.frame())?;
page.set_state(PageState::UpToDate);
page
} else {
Page::alloc_zero()?
};
let frame = page.frame();
let frame = page.frame().clone();
pages.put(page_idx, page);
frame
};
@ -110,7 +143,7 @@ impl Pager for PageCacheManager {
self.backed_inode
.upgrade()
.unwrap()
.write_page(page_idx, &page.frame())?
.write_page(page_idx, page.frame())?
}
} else {
warn!("page {} is not in page cache, do nothing", page_idx);
@ -151,8 +184,8 @@ impl Page {
})
}
pub fn frame(&self) -> VmFrame {
self.frame.clone()
pub fn frame(&self) -> &VmFrame {
&self.frame
}
pub fn state(&self) -> &PageState {

View File

@ -1,300 +0,0 @@
use super::{
DirentVisitor, FileSystem, FsFlags, Inode, InodeMode, InodeType, IoctlCmd, Metadata, PageCache,
};
use crate::events::IoEvents;
use crate::fs::device::Device;
use crate::prelude::*;
use crate::process::signal::Poller;
use crate::vm::vmo::Vmo;
use alloc::string::String;
use core::time::Duration;
use core2::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, Write};
use jinux_frame::vm::VmIo;
use jinux_rights::Full;
/// VFS-level representation of an inode
#[derive(Clone)]
pub struct Vnode {
// The RwLock is to maintain the correct file length for concurrent read or write.
inner: Arc<RwLock<Inner>>,
}
struct Inner {
inode: Arc<dyn Inode>,
page_cache: Option<PageCache>,
}
impl Vnode {
pub fn page_cache(&self) -> Option<Vmo<Full>> {
self.inner
.read()
.page_cache
.as_ref()
.map(|page_chche| page_chche.pages().dup().unwrap())
}
pub fn new(inode: Arc<dyn Inode>) -> Result<Self> {
let page_cache = if inode.fs().flags().contains(FsFlags::NO_PAGECACHE) {
None
} else {
Some(PageCache::new(&inode)?)
};
Ok(Self {
inner: Arc::new(RwLock::new(Inner { inode, page_cache })),
})
}
pub fn write_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
let type_ = self.inode_type();
if !type_.support_write() {
return_errno!(Errno::EINVAL);
}
let inner = self.inner.write();
match &inner.page_cache {
None => inner.inode.write_at(offset, buf),
Some(page_cache) => {
let file_len = inner.inode.len();
let should_expand_len = offset + buf.len() > file_len;
if should_expand_len {
page_cache.pages().resize(offset + buf.len())?;
}
page_cache.pages().write_bytes(offset, buf)?;
if should_expand_len {
inner.inode.resize(offset + buf.len());
}
Ok(buf.len())
}
}
}
pub fn write_direct_at(&self, offset: usize, buf: &[u8]) -> Result<usize> {
let type_ = self.inode_type();
if !type_.support_write() {
return_errno!(Errno::EINVAL);
}
let inner = self.inner.write();
if let Some(page_cache) = &inner.page_cache {
page_cache.evict_range(offset..offset + buf.len());
}
inner.inode.write_at(offset, buf)
}
pub fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
let type_ = self.inode_type();
if !type_.support_read() {
return_errno!(Errno::EISDIR);
}
let inner = self.inner.read();
match &inner.page_cache {
None => inner.inode.read_at(offset, buf),
Some(page_cache) => {
let (offset, read_len) = {
let file_len = inner.inode.len();
let start = file_len.min(offset);
let end = file_len.min(offset + buf.len());
(start, end - start)
};
page_cache
.pages()
.read_bytes(offset, &mut buf[..read_len])?;
Ok(read_len)
}
}
}
pub fn read_direct_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
let type_ = self.inode_type();
if !type_.support_read() {
return_errno!(Errno::EISDIR);
}
let inner = self.inner.read();
if let Some(page_cache) = &inner.page_cache {
page_cache.evict_range(offset..offset + buf.len());
}
inner.inode.read_at(offset, buf)
}
pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
let type_ = self.inode_type();
if !type_.support_read() {
return_errno!(Errno::EISDIR);
}
let inner = self.inner.read();
let file_len = inner.inode.len();
if buf.len() < file_len {
buf.resize(file_len, 0);
}
match &inner.page_cache {
None => inner.inode.read_at(0, &mut buf[..file_len]),
Some(page_cache) => {
page_cache.pages().read_bytes(0, &mut buf[..file_len])?;
Ok(file_len)
}
}
}
pub fn read_direct_to_end(&self, buf: &mut Vec<u8>) -> Result<usize> {
let type_ = self.inode_type();
if !type_.support_read() {
return_errno!(Errno::EISDIR);
}
let inner = self.inner.read();
let file_len = inner.inode.len();
if buf.len() < file_len {
buf.resize(file_len, 0);
}
if let Some(page_cache) = &inner.page_cache {
page_cache.evict_range(0..file_len);
}
inner.inode.read_at(0, &mut buf[..file_len])
}
pub fn create(&self, name: &str, type_: InodeType, mode: InodeMode) -> Result<Self> {
let inode = self.inner.read().inode.create(name, type_, mode)?;
Self::new(inode)
}
pub fn mknod(&self, name: &str, mode: InodeMode, device: Arc<dyn Device>) -> Result<Self> {
let inode = self.inner.read().inode.mknod(name, mode, device)?;
Self::new(inode)
}
pub fn lookup(&self, name: &str) -> Result<Self> {
let inode = self.inner.read().inode.lookup(name)?;
Self::new(inode)
}
pub fn link(&self, old: &Vnode, name: &str) -> Result<()> {
self.inner.read().inode.link(&old.inner.read().inode, name)
}
pub fn unlink(&self, name: &str) -> Result<()> {
self.inner.read().inode.unlink(name)
}
pub fn rmdir(&self, name: &str) -> Result<()> {
self.inner.read().inode.rmdir(name)
}
pub fn rename(&self, old_name: &str, target: &Vnode, new_name: &str) -> Result<()> {
self.inner
.read()
.inode
.rename(old_name, &target.inner.read().inode, new_name)
}
pub fn read_link(&self) -> Result<String> {
self.inner.read().inode.read_link()
}
pub fn write_link(&self, target: &str) -> Result<()> {
self.inner.write().inode.write_link(target)
}
pub fn readdir_at(&self, offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
self.inner.read().inode.readdir_at(offset, visitor)
}
pub fn poll(&self, mask: IoEvents, poller: Option<&Poller>) -> IoEvents {
let inode = self.inner.read().inode.clone();
inode.poll(mask, poller)
}
pub fn ioctl(&self, cmd: IoctlCmd, arg: usize) -> Result<i32> {
let inode = self.inner.read().inode.clone();
inode.ioctl(cmd, arg)
}
pub fn fs(&self) -> Arc<dyn FileSystem> {
self.inner.read().inode.fs()
}
pub fn metadata(&self) -> Metadata {
self.inner.read().inode.metadata()
}
pub fn inode(&self) -> Weak<dyn Inode> {
let inner = self.inner.read();
Arc::downgrade(&inner.inode)
}
pub fn inode_type(&self) -> InodeType {
self.inner.read().inode.metadata().type_
}
pub fn inode_mode(&self) -> InodeMode {
self.inner.read().inode.metadata().mode
}
pub fn set_inode_mode(&self, mode: InodeMode) {
self.inner.read().inode.set_mode(mode)
}
pub fn len(&self) -> usize {
self.inner.read().inode.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn atime(&self) -> Duration {
self.inner.read().inode.atime()
}
pub fn set_atime(&self, time: Duration) {
self.inner.read().inode.set_atime(time)
}
pub fn mtime(&self) -> Duration {
self.inner.read().inode.mtime()
}
pub fn set_mtime(&self, time: Duration) {
self.inner.read().inode.set_mtime(time)
}
pub fn is_dentry_cacheable(&self) -> bool {
self.inner.read().inode.is_dentry_cacheable()
}
pub fn writer(&self, from_offset: usize) -> VnodeWriter {
VnodeWriter {
inner: self,
offset: from_offset,
}
}
}
impl Debug for Vnode {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("Vnode")
.field("inode", &self.inner.read().inode)
.field("page_cache", &self.inner.read().page_cache)
.finish()
}
}
pub struct VnodeWriter<'a> {
inner: &'a Vnode,
offset: usize,
}
impl<'a> Write for VnodeWriter<'a> {
#[inline]
fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
let write_len = self
.inner
.write_at(self.offset, buf)
.map_err(|_| IoError::new(IoErrorKind::WriteZero, "failed to write buffer"))?;
self.offset += write_len;
Ok(write_len)
}
#[inline]
fn flush(&mut self) -> IoResult<()> {
Ok(())
}
}

View File

@ -18,15 +18,7 @@ impl PartialEq for UnixSocketAddrBound {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Abstract(l0), Self::Abstract(r0)) => l0 == r0,
(Self::Path(l0), Self::Path(r0)) => {
let Some(linode) = l0.inode().upgrade() else {
return false;
};
let Some(rinode) = r0.inode().upgrade() else {
return false;
};
Arc::ptr_eq(&linode, &rinode)
}
(Self::Path(l0), Self::Path(r0)) => Arc::ptr_eq(l0.inode(), r0.inode()),
_ => false,
}
}

View File

@ -203,8 +203,8 @@ impl Backlog {
}
fn create_keyable_inode(dentry: &Arc<Dentry>) -> KeyableWeak<dyn Inode> {
let inode = dentry.inode();
KeyableWeak::from(inode)
let weak_inode = Arc::downgrade(dentry.inode());
KeyableWeak::from(weak_inode)
}
pub(super) fn unregister_backlog(addr: &UnixSocketAddrBound) {

View File

@ -69,8 +69,8 @@ fn lookup_and_parse_ldso(
};
let ldso_elf = {
let mut buf = Box::new([0u8; PAGE_SIZE]);
let vnode = ldso_file.vnode();
vnode.read_at(0, &mut *buf)?;
let inode = ldso_file.inode();
inode.read_at(0, &mut *buf)?;
Elf::parse_elf(&*buf)?
};
Ok((ldso_file, ldso_elf))
@ -250,8 +250,8 @@ fn init_segment_vmo(program_header: &ProgramHeader64, elf_file: &Dentry) -> Resu
let virtual_addr = program_header.virtual_addr as usize;
debug_assert!(file_offset % PAGE_SIZE == virtual_addr % PAGE_SIZE);
let page_cache_vmo = {
let vnode = elf_file.vnode();
vnode.page_cache().ok_or(Error::with_message(
let inode = elf_file.inode();
inode.page_cache().ok_or(Error::with_message(
Errno::ENOENT,
"executable has no page cache",
))?

View File

@ -26,11 +26,11 @@ pub fn load_program_to_vm(
recursion_limit: usize,
) -> Result<(String, ElfLoadInfo)> {
let abs_path = elf_file.abs_path();
let vnode = elf_file.vnode();
let inode = elf_file.inode();
let file_header = {
// read the first page of file header
let mut file_header_buffer = Box::new([0u8; PAGE_SIZE]);
vnode.read_at(0, &mut *file_header_buffer)?;
inode.read_at(0, &mut *file_header_buffer)?;
file_header_buffer
};
if let Some(mut new_argv) = parse_shebang_line(&*file_header)? {

View File

@ -102,8 +102,8 @@ fn mmap_filebacked_vmo(
let page_cache_vmo = {
let fs_resolver = current.fs().read();
let dentry = fs_resolver.lookup_from_fd(fd)?;
let vnode = dentry.vnode();
vnode.page_cache().ok_or(Error::with_message(
let inode = dentry.inode();
inode.page_cache().ok_or(Error::with_message(
Errno::EBADF,
"File does not have page cache",
))?

View File

@ -32,7 +32,7 @@ pub fn sys_readlinkat(
let fs_path = FsPath::new(dirfd, pathname.as_ref())?;
current.fs().read().lookup_no_follow(&fs_path)?
};
let linkpath = dentry.read_link()?;
let linkpath = dentry.inode().read_link()?;
let bytes = linkpath.as_bytes();
let write_len = bytes.len().min(usr_buf_len);
write_bytes_to_user(usr_buf_addr, &bytes[..write_len])?;

View File

@ -46,7 +46,7 @@ pub fn sys_symlinkat(
InodeType::SymLink,
InodeMode::from_bits_truncate(0o777),
)?;
new_dentry.write_link(&target)?;
new_dentry.inode().write_link(&target)?;
Ok(SyscallReturn::Return(0))
}