Add ProcFS

This commit is contained in:
LI Qing 2023-03-27 01:55:13 -04:00 committed by Tate, Hongliang Tian
parent 12b4e0637d
commit b02b29dde7
24 changed files with 1242 additions and 24 deletions

View File

@ -1,3 +1,4 @@
use crate::events::{Events, Observer, Subject};
use crate::prelude::*;
use super::{
@ -7,15 +8,16 @@ use super::{
pub type FileDescripter = i32;
#[derive(Clone)]
pub struct FileTable {
table: BTreeMap<FileDescripter, FileHandle>,
subject: Subject<FdEvents>,
}
impl FileTable {
pub fn new() -> Self {
Self {
table: BTreeMap::new(),
subject: Subject::new(),
}
}
@ -27,7 +29,10 @@ impl FileTable {
table.insert(FD_STDIN, FileHandle::new_file(Arc::new(stdin)));
table.insert(FD_STDOUT, FileHandle::new_file(Arc::new(stdout)));
table.insert(FD_STDERR, FileHandle::new_file(Arc::new(stderr)));
Self { table }
Self {
table,
subject: Subject::new(),
}
}
pub fn dup(&mut self, fd: FileDescripter, new_fd: Option<FileDescripter>) -> Result<()> {
@ -59,11 +64,19 @@ impl FileTable {
}
pub fn insert_at(&mut self, fd: FileDescripter, item: FileHandle) -> Option<FileHandle> {
self.table.insert(fd, item)
let file = self.table.insert(fd, item);
if file.is_some() {
self.notify_close_fd_event(fd);
}
file
}
pub fn close_file(&mut self, fd: FileDescripter) -> Option<FileHandle> {
self.table.remove(&fd)
let file = self.table.remove(&fd);
if file.is_some() {
self.notify_close_fd_event(fd);
}
file
}
pub fn get_file(&self, fd: FileDescripter) -> Result<&FileHandle> {
@ -71,4 +84,45 @@ impl FileTable {
.get(&fd)
.ok_or(Error::with_message(Errno::EBADF, "fd not exits"))
}
pub fn fds_and_files(&self) -> impl Iterator<Item = (&'_ FileDescripter, &'_ FileHandle)> {
self.table.iter()
}
pub fn register_observer(&self, observer: Weak<dyn Observer<FdEvents>>) {
self.subject.register_observer(observer);
}
pub fn unregister_observer(&self, observer: Weak<dyn Observer<FdEvents>>) {
self.subject.unregister_observer(observer);
}
fn notify_close_fd_event(&self, fd: FileDescripter) {
let events = FdEvents::Close(fd);
self.subject.notify_observers(&events);
}
}
impl Clone for FileTable {
fn clone(&self) -> Self {
Self {
table: self.table.clone(),
subject: Subject::new(),
}
}
}
impl Drop for FileTable {
fn drop(&mut self) {
let events = FdEvents::DropFileTable;
self.subject.notify_observers(&events);
}
}
#[derive(Copy, Clone)]
pub enum FdEvents {
Close(FileDescripter),
DropFileTable,
}
impl Events for FdEvents {}

View File

@ -4,6 +4,7 @@ use alloc::string::String;
use super::file_handle::InodeHandle;
use super::file_table::FileDescripter;
use super::procfs::ProcFS;
use super::ramfs::RamFS;
use super::utils::{
AccessMode, CreationFlags, Dentry, FileSystem, InodeMode, InodeType, StatusFlags, Vnode,
@ -19,6 +20,11 @@ lazy_static! {
}
init().unwrap()
};
static ref PROC_FS: Arc<dyn FileSystem> = ProcFS::new();
static ref PROC_DENTRY: Arc<Dentry> = {
let vnode = Vnode::new(PROC_FS.root_inode()).unwrap();
Dentry::new_root(vnode)
};
}
pub struct FsResolver {
@ -127,7 +133,21 @@ impl FsResolver {
fn lookup_inner(&self, path: &FsPath, follow_tail_link: bool) -> Result<Arc<Dentry>> {
let dentry = match path.inner {
FsPathInner::Absolute(path) => {
self.lookup_from_parent(&self.root, path.trim_start_matches('/'), follow_tail_link)?
// TODO: Mount procfs at "/proc" if mount feature is ready
if path.starts_with("/proc") {
let path = path.strip_prefix("/proc").unwrap();
self.lookup_from_parent(
&PROC_DENTRY,
path.trim_start_matches('/'),
follow_tail_link,
)?
} else {
self.lookup_from_parent(
&self.root,
path.trim_start_matches('/'),
follow_tail_link,
)?
}
}
FsPathInner::CwdRelative(path) => {
self.lookup_from_parent(&self.cwd, path, follow_tail_link)?

View File

@ -2,6 +2,7 @@ pub mod file_handle;
pub mod file_table;
pub mod fs_resolver;
pub mod initramfs;
pub mod procfs;
pub mod ramfs;
pub mod stdio;
pub mod utils;

View File

@ -0,0 +1,125 @@
use alloc::string::{String, ToString};
use core::any::Any;
use core::sync::atomic::{AtomicUsize, Ordering};
use crate::events::Observer;
use crate::fs::utils::{DirEntryVecExt, FileSystem, FsFlags, Inode, SuperBlock, NAME_MAX};
use crate::prelude::*;
use crate::process::{process_table, process_table::PidEvent, Pid};
use self::pid::PidDirOps;
use self::self_::SelfSymOps;
use self::template::{DirOps, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps};
mod pid;
mod self_;
mod template;
/// Magic number.
const PROC_MAGIC: usize = 0x9fa0;
/// Root Inode ID.
const PROC_ROOT_INO: usize = 1;
/// Block size.
const BLOCK_SIZE: usize = 1024;
pub struct ProcFS {
sb: RwLock<SuperBlock>,
root: RwLock<Option<Arc<dyn Inode>>>,
inode_allocator: AtomicUsize,
}
impl ProcFS {
pub fn new() -> Arc<Self> {
let procfs = {
let sb = SuperBlock::new(PROC_MAGIC, BLOCK_SIZE, NAME_MAX);
Arc::new(Self {
sb: RwLock::new(sb),
root: RwLock::new(None),
inode_allocator: AtomicUsize::new(PROC_ROOT_INO),
})
};
let root = RootDirOps::new_inode(&procfs);
*procfs.root.write() = Some(root);
procfs
}
pub(in crate::fs::procfs) fn alloc_id(&self) -> usize {
let next_id = self.inode_allocator.fetch_add(1, Ordering::SeqCst);
next_id
}
}
impl FileSystem for ProcFS {
fn sync(&self) -> Result<()> {
Ok(())
}
fn root_inode(&self) -> Arc<dyn Inode> {
self.root.read().as_ref().unwrap().clone()
}
fn sb(&self) -> SuperBlock {
self.sb.read().clone()
}
fn flags(&self) -> FsFlags {
FsFlags::NO_PAGECACHE
}
fn as_any_ref(&self) -> &dyn Any {
self
}
}
/// Represents the inode at `/proc`.
struct RootDirOps;
impl RootDirOps {
pub fn new_inode(fs: &Arc<ProcFS>) -> Arc<dyn Inode> {
let root_inode = ProcDirBuilder::new(Self).fs(fs.clone()).build().unwrap();
let weak_ptr = Arc::downgrade(&root_inode);
process_table::register_observer(weak_ptr);
root_inode
}
}
impl Observer<PidEvent> for ProcDir<RootDirOps> {
fn on_events(&self, events: &PidEvent) {
let PidEvent::Exit(pid) = events;
let mut cached_children = self.cached_children().write();
cached_children.remove_entry_by_name(&pid.to_string());
}
}
impl DirOps for RootDirOps {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
let child = if name == "self" {
SelfSymOps::new_inode(this_ptr.clone())
} else if let Ok(pid) = name.parse::<Pid>() {
let process_ref =
process_table::pid_to_process(pid).ok_or_else(|| Error::new(Errno::ENOENT))?;
PidDirOps::new_inode(process_ref, this_ptr.clone())
} else {
return_errno!(Errno::ENOENT);
};
Ok(child)
}
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {
let this = {
let this = this_ptr.upgrade().unwrap();
this.downcast_ref::<ProcDir<RootDirOps>>().unwrap().this()
};
let mut cached_children = this.cached_children().write();
cached_children.put_entry_if_not_found("self", || SelfSymOps::new_inode(this_ptr.clone()));
let processes = process_table::get_all_processes();
for process in processes {
let pid = process.pid().to_string();
cached_children.put_entry_if_not_found(&pid, || {
PidDirOps::new_inode(process.clone(), this_ptr.clone())
});
}
}
}

View File

@ -0,0 +1,30 @@
use super::*;
/// Represents the inode at `/proc/[pid]/comm`.
pub struct CommFileOps(Arc<Process>);
impl CommFileOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcFileBuilder::new(Self(process_ref))
.parent(parent)
.build()
.unwrap()
}
}
impl FileOps for CommFileOps {
fn data(&self) -> Result<Vec<u8>> {
let mut comm_output = {
let exe_path = self.0.executable_path().read();
let last_component = exe_path.rsplit('/').next().unwrap_or(&exe_path);
let mut comm = last_component.as_bytes().to_vec();
comm.push(b'\0');
comm.truncate(TASK_COMM_LEN);
comm
};
comm_output.push(b'\n');
Ok(comm_output)
}
}
const TASK_COMM_LEN: usize = 16;

View File

@ -0,0 +1,19 @@
use super::*;
/// Represents the inode at `/proc/[pid]/exe`.
pub struct ExeSymOps(Arc<Process>);
impl ExeSymOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcSymBuilder::new(Self(process_ref))
.parent(parent)
.build()
.unwrap()
}
}
impl SymOps for ExeSymOps {
fn read_link(&self) -> Result<String> {
Ok(self.0.executable_path().read().clone())
}
}

View File

@ -0,0 +1,88 @@
use super::*;
use crate::fs::file_handle::FileHandle;
use crate::fs::file_table::FileDescripter;
/// Represents the inode at `/proc/[pid]/fd`.
pub struct FdDirOps(Arc<Process>);
impl FdDirOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let fd_inode = ProcDirBuilder::new(Self(process_ref.clone()))
.parent(parent)
.build()
.unwrap();
let file_table = process_ref.file_table().lock();
let weak_ptr = Arc::downgrade(&fd_inode);
file_table.register_observer(weak_ptr);
fd_inode
}
}
impl Observer<FdEvents> for ProcDir<FdDirOps> {
fn on_events(&self, events: &FdEvents) {
let fd_string = if let FdEvents::Close(fd) = events {
fd.to_string()
} else {
return;
};
let mut cached_children = self.cached_children().write();
cached_children.remove_entry_by_name(&fd_string);
}
}
impl DirOps for FdDirOps {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
let file = {
let fd = name
.parse::<FileDescripter>()
.map_err(|_| Error::new(Errno::ENOENT))?;
let file_table = self.0.file_table().lock();
file_table
.get_file(fd)
.map_err(|_| Error::new(Errno::ENOENT))?
.clone()
};
Ok(FileSymOps::new_inode(file, this_ptr.clone()))
}
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {
let this = {
let this = this_ptr.upgrade().unwrap();
this.downcast_ref::<ProcDir<FdDirOps>>().unwrap().this()
};
let file_table = self.0.file_table().lock();
let mut cached_children = this.cached_children().write();
for (fd, file) in file_table.fds_and_files() {
cached_children.put_entry_if_not_found(&fd.to_string(), || {
FileSymOps::new_inode(file.clone(), this_ptr.clone())
});
}
}
}
/// Represents the inode at `/proc/[pid]/fd/N`.
struct FileSymOps(FileHandle);
impl FileSymOps {
pub fn new_inode(file: FileHandle, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcSymBuilder::new(Self(file))
.parent(parent)
.build()
.unwrap()
}
}
impl SymOps for FileSymOps {
fn read_link(&self) -> Result<String> {
let path = if let Some(inode_handle) = self.0.as_inode_handle() {
inode_handle.dentry().abs_path()
} else if let Some(file) = self.0.as_file() {
// TODO: get the real path for stdio
String::from("/dev/tty")
} else {
unreachable!()
};
Ok(path)
}
}

View File

@ -0,0 +1,72 @@
use crate::events::Observer;
use crate::fs::file_table::FdEvents;
use crate::fs::utils::{DirEntryVecExt, Inode};
use crate::prelude::*;
use crate::process::Process;
use self::comm::CommFileOps;
use self::exe::ExeSymOps;
use self::fd::FdDirOps;
use super::template::{
DirOps, FileOps, ProcDir, ProcDirBuilder, ProcFileBuilder, ProcSymBuilder, SymOps,
};
mod comm;
mod exe;
mod fd;
/// Represents the inode at `/proc/[pid]`.
pub struct PidDirOps(Arc<Process>);
impl PidDirOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let pid_inode = ProcDirBuilder::new(Self(process_ref.clone()))
.parent(parent)
// The pid directories must be volatile, because it is just associated with one process.
.volatile()
.build()
.unwrap();
let file_table = process_ref.file_table().lock();
let weak_ptr = Arc::downgrade(&pid_inode);
file_table.register_observer(weak_ptr);
pid_inode
}
}
impl Observer<FdEvents> for ProcDir<PidDirOps> {
fn on_events(&self, events: &FdEvents) {
if let FdEvents::DropFileTable = events {
let mut cached_children = self.cached_children().write();
cached_children.remove_entry_by_name("fd");
}
}
}
impl DirOps for PidDirOps {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
let inode = match name {
"exe" => ExeSymOps::new_inode(self.0.clone(), this_ptr.clone()),
"comm" => CommFileOps::new_inode(self.0.clone(), this_ptr.clone()),
"fd" => FdDirOps::new_inode(self.0.clone(), this_ptr.clone()),
_ => return_errno!(Errno::ENOENT),
};
Ok(inode)
}
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {
let this = {
let this = this_ptr.upgrade().unwrap();
this.downcast_ref::<ProcDir<PidDirOps>>().unwrap().this()
};
let mut cached_children = this.cached_children().write();
cached_children.put_entry_if_not_found("exe", || {
ExeSymOps::new_inode(self.0.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("comm", || {
CommFileOps::new_inode(self.0.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("fd", || {
FdDirOps::new_inode(self.0.clone(), this_ptr.clone())
})
}
}

View File

@ -0,0 +1,16 @@
use super::*;
/// Represents the inode at `/proc/self`.
pub struct SelfSymOps;
impl SelfSymOps {
pub fn new_inode(parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcSymBuilder::new(Self).parent(parent).build().unwrap()
}
}
impl SymOps for SelfSymOps {
fn read_link(&self) -> Result<String> {
Ok(current!().pid().to_string())
}
}

View File

@ -0,0 +1,175 @@
use crate::fs::utils::{FileSystem, Inode};
use crate::prelude::*;
use super::{
dir::{DirOps, ProcDir},
file::{FileOps, ProcFile},
sym::{ProcSym, SymOps},
};
pub struct ProcDirBuilder<O: DirOps> {
// Mandatory field
dir: O,
// Optional fields
optional_builder: Option<OptionalBuilder>,
}
impl<O: DirOps> ProcDirBuilder<O> {
pub fn new(dir: O) -> Self {
let optional_builder: OptionalBuilder = Default::default();
Self {
dir,
optional_builder: Some(optional_builder),
}
}
pub fn parent(self, parent: Weak<dyn Inode>) -> Self {
self.optional_builder(|ob| ob.parent(parent))
}
pub fn fs(self, fs: Arc<dyn FileSystem>) -> Self {
self.optional_builder(|ob| ob.fs(fs))
}
pub fn volatile(self) -> Self {
self.optional_builder(|ob| ob.volatile())
}
pub fn build(mut self) -> Result<Arc<ProcDir<O>>> {
let (fs, parent, is_volatile) = self.optional_builder.take().unwrap().build()?;
Ok(ProcDir::new(self.dir, fs, parent, is_volatile))
}
fn optional_builder<F>(mut self, f: F) -> Self
where
F: FnOnce(OptionalBuilder) -> OptionalBuilder,
{
let optional_builder = self.optional_builder.take().unwrap();
self.optional_builder = Some(f(optional_builder));
self
}
}
pub struct ProcFileBuilder<O: FileOps> {
// Mandatory field
file: O,
// Optional fields
optional_builder: Option<OptionalBuilder>,
}
impl<O: FileOps> ProcFileBuilder<O> {
pub fn new(file: O) -> Self {
let optional_builder: OptionalBuilder = Default::default();
Self {
file,
optional_builder: Some(optional_builder),
}
}
pub fn parent(self, parent: Weak<dyn Inode>) -> Self {
self.optional_builder(|ob| ob.parent(parent))
}
pub fn volatile(self) -> Self {
self.optional_builder(|ob| ob.volatile())
}
pub fn build(mut self) -> Result<Arc<ProcFile<O>>> {
let (fs, _, is_volatile) = self.optional_builder.take().unwrap().build()?;
Ok(ProcFile::new(self.file, fs, is_volatile))
}
fn optional_builder<F>(mut self, f: F) -> Self
where
F: FnOnce(OptionalBuilder) -> OptionalBuilder,
{
let optional_builder = self.optional_builder.take().unwrap();
self.optional_builder = Some(f(optional_builder));
self
}
}
pub struct ProcSymBuilder<O: SymOps> {
// Mandatory field
sym: O,
// Optional fields
optional_builder: Option<OptionalBuilder>,
}
impl<O: SymOps> ProcSymBuilder<O> {
pub fn new(sym: O) -> Self {
let optional_builder: OptionalBuilder = Default::default();
Self {
sym,
optional_builder: Some(optional_builder),
}
}
pub fn parent(self, parent: Weak<dyn Inode>) -> Self {
self.optional_builder(|ob| ob.parent(parent))
}
pub fn volatile(self) -> Self {
self.optional_builder(|ob| ob.volatile())
}
pub fn build(mut self) -> Result<Arc<ProcSym<O>>> {
let (fs, _, is_volatile) = self.optional_builder.take().unwrap().build()?;
Ok(ProcSym::new(self.sym, fs, is_volatile))
}
fn optional_builder<F>(mut self, f: F) -> Self
where
F: FnOnce(OptionalBuilder) -> OptionalBuilder,
{
let optional_builder = self.optional_builder.take().unwrap();
self.optional_builder = Some(f(optional_builder));
self
}
}
#[derive(Default)]
struct OptionalBuilder {
parent: Option<Weak<dyn Inode>>,
fs: Option<Arc<dyn FileSystem>>,
is_volatile: bool,
}
impl OptionalBuilder {
pub fn parent(mut self, parent: Weak<dyn Inode>) -> Self {
self.parent = Some(parent);
self
}
pub fn fs(mut self, fs: Arc<dyn FileSystem>) -> Self {
self.fs = Some(fs);
self
}
pub fn volatile(mut self) -> Self {
self.is_volatile = true;
self
}
pub fn build(self) -> Result<(Arc<dyn FileSystem>, Option<Weak<dyn Inode>>, bool)> {
if self.parent.is_none() && self.fs.is_none() {
return_errno_with_message!(Errno::EINVAL, "must have parent or fs");
}
let fs = self
.fs
.unwrap_or_else(|| self.parent.as_ref().unwrap().upgrade().unwrap().fs());
// The volatile property is inherited from parent.
let is_volatile = {
let mut is_volatile = self.is_volatile;
if let Some(parent) = self.parent.as_ref() {
if !parent.upgrade().unwrap().is_dentry_cacheable() {
is_volatile = true;
}
}
is_volatile
};
Ok((fs, self.parent, is_volatile))
}
}

View File

@ -0,0 +1,225 @@
use alloc::string::String;
use core::any::Any;
use core::time::Duration;
use jinux_frame::vm::VmFrame;
use crate::fs::utils::{
DirEntryVec, DirentVisitor, FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata,
};
use crate::prelude::*;
use super::{ProcFS, ProcInodeInfo};
pub struct ProcDir<D: DirOps> {
inner: D,
this: Weak<ProcDir<D>>,
parent: Option<Weak<dyn Inode>>,
cached_children: RwLock<DirEntryVec<(String, Arc<dyn Inode>)>>,
info: ProcInodeInfo,
}
impl<D: DirOps> ProcDir<D> {
pub fn new(
dir: D,
fs: Arc<dyn FileSystem>,
parent: Option<Weak<dyn Inode>>,
is_volatile: bool,
) -> Arc<Self> {
let info = {
let procfs = fs.downcast_ref::<ProcFS>().unwrap();
let metadata = Metadata::new_dir(
procfs.alloc_id(),
InodeMode::from_bits_truncate(0o555),
&fs.sb(),
);
ProcInodeInfo::new(metadata, Arc::downgrade(&fs), is_volatile)
};
Arc::new_cyclic(|weak_self| Self {
inner: dir,
this: weak_self.clone(),
parent,
cached_children: RwLock::new(DirEntryVec::new()),
info,
})
}
pub fn this(&self) -> Arc<ProcDir<D>> {
self.this.upgrade().unwrap()
}
pub fn parent(&self) -> Option<Arc<dyn Inode>> {
self.parent.as_ref().and_then(|p| p.upgrade())
}
pub fn cached_children(&self) -> &RwLock<DirEntryVec<(String, Arc<dyn Inode>)>> {
&self.cached_children
}
}
impl<D: DirOps + 'static> Inode for ProcDir<D> {
fn len(&self) -> usize {
self.info.metadata().size
}
fn resize(&self, _new_size: usize) {}
fn metadata(&self) -> Metadata {
self.info.metadata().clone()
}
fn atime(&self) -> Duration {
self.info.metadata().atime
}
fn set_atime(&self, _time: Duration) {}
fn mtime(&self) -> Duration {
self.info.metadata().mtime
}
fn set_mtime(&self, _time: Duration) {}
fn read_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
Err(Error::new(Errno::EISDIR))
}
fn write_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
Err(Error::new(Errno::EISDIR))
}
fn read_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 mknod(&self, _name: &str, _type_: InodeType, _mode: InodeMode) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::EPERM))
}
fn readdir_at(&self, mut offset: usize, visitor: &mut dyn DirentVisitor) -> Result<usize> {
let try_readdir = |offset: &mut usize, visitor: &mut dyn DirentVisitor| -> Result<()> {
// Read the two special entries.
if *offset == 0 {
let this_inode = self.this();
visitor.visit(
".",
this_inode.info.metadata().ino as u64,
this_inode.info.metadata().type_,
*offset,
)?;
*offset += 1;
}
if *offset == 1 {
let parent_inode = self.parent().unwrap_or(self.this());
visitor.visit(
"..",
parent_inode.metadata().ino as u64,
parent_inode.metadata().type_,
*offset,
)?;
*offset += 1;
}
// Read the normal child entries.
self.inner.populate_children(self.this.clone());
let cached_children = self.cached_children.read();
for (idx, (name, child)) in cached_children
.idxes_and_entries()
.map(|(idx, (name, child))| (idx + 2, (name, child)))
{
if idx < *offset {
continue;
}
visitor.visit(
name.as_ref(),
child.metadata().ino as u64,
child.metadata().type_,
idx,
)?;
*offset = idx + 1;
}
Ok(())
};
let initial_offset = offset;
match try_readdir(&mut offset, visitor) {
Err(e) if initial_offset == offset => Err(e),
_ => Ok(offset - initial_offset),
}
}
fn link(&self, _old: &Arc<dyn Inode>, _name: &str) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn unlink(&self, _name: &str) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn rmdir(&self, _name: &str) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>> {
let inode = match name {
"." => self.this(),
".." => self.parent().unwrap_or(self.this()),
name => {
let mut cached_children = self.cached_children.write();
if let Some((_, inode)) = cached_children
.iter()
.find(|(child_name, inode)| child_name.as_str() == name)
{
return Ok(inode.clone());
}
let inode = self.inner.lookup_child(self.this.clone(), name)?;
cached_children.put((String::from(name), inode.clone()));
inode
}
};
Ok(inode)
}
fn rename(&self, _old_name: &str, _target: &Arc<dyn Inode>, _new_name: &str) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn read_link(&self) -> Result<String> {
Err(Error::new(Errno::EISDIR))
}
fn write_link(&self, _target: &str) -> Result<()> {
Err(Error::new(Errno::EISDIR))
}
fn ioctl(&self, _cmd: &IoctlCmd) -> Result<()> {
Err(Error::new(Errno::EISDIR))
}
fn sync(&self) -> Result<()> {
Ok(())
}
fn fs(&self) -> Arc<dyn FileSystem> {
self.info.fs().upgrade().unwrap()
}
fn is_dentry_cacheable(&self) -> bool {
!self.info.is_volatile()
}
fn as_any_ref(&self) -> &dyn Any {
self
}
}
pub trait DirOps: Sync + Send {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::ENOENT))
}
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {}
}

View File

@ -0,0 +1,136 @@
use alloc::string::String;
use core::any::Any;
use core::time::Duration;
use jinux_frame::vm::VmFrame;
use crate::fs::utils::{
DirentVisitor, FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata,
};
use crate::prelude::*;
use super::{ProcFS, ProcInodeInfo};
pub struct ProcFile<F: FileOps> {
inner: F,
info: ProcInodeInfo,
}
impl<F: FileOps> ProcFile<F> {
pub fn new(file: F, fs: Arc<dyn FileSystem>, is_volatile: bool) -> Arc<Self> {
let info = {
let procfs = fs.downcast_ref::<ProcFS>().unwrap();
let metadata = Metadata::new_file(
procfs.alloc_id(),
InodeMode::from_bits_truncate(0o444),
&fs.sb(),
);
ProcInodeInfo::new(metadata, Arc::downgrade(&fs), is_volatile)
};
Arc::new(Self { inner: file, info })
}
}
impl<F: FileOps + 'static> Inode for ProcFile<F> {
fn len(&self) -> usize {
self.info.metadata().size
}
fn resize(&self, _new_size: usize) {}
fn metadata(&self) -> Metadata {
self.info.metadata().clone()
}
fn atime(&self) -> Duration {
self.info.metadata().atime
}
fn set_atime(&self, _time: Duration) {}
fn mtime(&self) -> Duration {
self.info.metadata().mtime
}
fn set_mtime(&self, _time: Duration) {}
fn read_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
unreachable!()
}
fn write_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
unreachable!()
}
fn read_at(&self, offset: usize, buf: &mut [u8]) -> Result<usize> {
let data = self.inner.data()?;
let start = data.len().min(offset);
let end = data.len().min(offset + buf.len());
let len = end - start;
buf[0..len].copy_from_slice(&data[start..end]);
Ok(len)
}
fn write_at(&self, _offset: usize, _buf: &[u8]) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn mknod(&self, _name: &str, _type_: InodeType, _mode: InodeMode) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::ENOTDIR))
}
fn readdir_at(&self, _offset: usize, _visitor: &mut dyn DirentVisitor) -> Result<usize> {
Err(Error::new(Errno::ENOTDIR))
}
fn link(&self, _old: &Arc<dyn Inode>, _name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn unlink(&self, _name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn rmdir(&self, _name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn lookup(&self, _name: &str) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::ENOTDIR))
}
fn rename(&self, _old_name: &str, _target: &Arc<dyn Inode>, _new_name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn read_link(&self) -> Result<String> {
Err(Error::new(Errno::EINVAL))
}
fn write_link(&self, _target: &str) -> Result<()> {
Err(Error::new(Errno::EINVAL))
}
fn ioctl(&self, _cmd: &IoctlCmd) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn sync(&self) -> Result<()> {
Ok(())
}
fn fs(&self) -> Arc<dyn FileSystem> {
self.info.fs().upgrade().unwrap()
}
fn is_dentry_cacheable(&self) -> bool {
!self.info.is_volatile()
}
fn as_any_ref(&self) -> &dyn Any {
self
}
}
pub trait FileOps: Sync + Send {
fn data(&self) -> Result<Vec<u8>>;
}

View File

@ -0,0 +1,42 @@
use crate::fs::utils::{FileSystem, Metadata};
use crate::prelude::*;
use super::ProcFS;
pub use self::builder::{ProcDirBuilder, ProcFileBuilder, ProcSymBuilder};
pub use self::dir::{DirOps, ProcDir};
pub use self::file::FileOps;
pub use self::sym::SymOps;
mod builder;
mod dir;
mod file;
mod sym;
struct ProcInodeInfo {
metadata: Metadata,
fs: Weak<dyn FileSystem>,
is_volatile: bool,
}
impl ProcInodeInfo {
pub fn new(metadata: Metadata, fs: Weak<dyn FileSystem>, is_volatile: bool) -> Self {
Self {
metadata,
fs,
is_volatile,
}
}
pub fn fs(&self) -> &Weak<dyn FileSystem> {
&self.fs
}
pub fn metadata(&self) -> &Metadata {
&self.metadata
}
pub fn is_volatile(&self) -> bool {
self.is_volatile
}
}

View File

@ -0,0 +1,131 @@
use alloc::string::String;
use core::any::Any;
use core::time::Duration;
use jinux_frame::vm::VmFrame;
use crate::fs::utils::{
DirentVisitor, FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata,
};
use crate::prelude::*;
use super::{ProcFS, ProcInodeInfo};
pub struct ProcSym<S: SymOps> {
inner: S,
info: ProcInodeInfo,
}
impl<S: SymOps> ProcSym<S> {
pub fn new(sym: S, fs: Arc<dyn FileSystem>, is_volatile: bool) -> Arc<Self> {
let info = {
let procfs = fs.downcast_ref::<ProcFS>().unwrap();
let metadata = Metadata::new_symlink(
procfs.alloc_id(),
InodeMode::from_bits_truncate(0o777),
&fs.sb(),
);
ProcInodeInfo::new(metadata, Arc::downgrade(&fs), is_volatile)
};
Arc::new(Self { inner: sym, info })
}
}
impl<S: SymOps + 'static> Inode for ProcSym<S> {
fn len(&self) -> usize {
self.info.metadata().size
}
fn resize(&self, _new_size: usize) {}
fn metadata(&self) -> Metadata {
self.info.metadata().clone()
}
fn atime(&self) -> Duration {
self.info.metadata().atime
}
fn set_atime(&self, _time: Duration) {}
fn mtime(&self) -> Duration {
self.info.metadata().mtime
}
fn set_mtime(&self, _time: Duration) {}
fn read_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn write_page(&self, _idx: usize, _frame: &VmFrame) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn read_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 mknod(&self, _name: &str, _type_: InodeType, _mode: InodeMode) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::ENOTDIR))
}
fn readdir_at(&self, _offset: usize, _visitor: &mut dyn DirentVisitor) -> Result<usize> {
Err(Error::new(Errno::ENOTDIR))
}
fn link(&self, _old: &Arc<dyn Inode>, _name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn unlink(&self, _name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn rmdir(&self, _name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn lookup(&self, _name: &str) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::ENOTDIR))
}
fn rename(&self, _old_name: &str, _target: &Arc<dyn Inode>, _new_name: &str) -> Result<()> {
Err(Error::new(Errno::ENOTDIR))
}
fn read_link(&self) -> Result<String> {
self.inner.read_link()
}
fn write_link(&self, _target: &str) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn ioctl(&self, _cmd: &IoctlCmd) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn sync(&self) -> Result<()> {
Ok(())
}
fn fs(&self) -> Arc<dyn FileSystem> {
self.info.fs().upgrade().unwrap()
}
fn is_dentry_cacheable(&self) -> bool {
!self.info.is_volatile()
}
fn as_any_ref(&self) -> &dyn Any {
self
}
}
pub trait SymOps: Sync + Send {
fn read_link(&self) -> Result<String>;
}

View File

@ -67,6 +67,10 @@ impl FileSystem for RamFS {
fn flags(&self) -> FsFlags {
FsFlags::DENTRY_UNEVICTABLE
}
fn as_any_ref(&self) -> &dyn Any {
self
}
}
struct RamInode(RwLock<Inode_>);

View File

@ -292,8 +292,10 @@ impl Children {
}
pub fn insert_dentry(&mut self, dentry: &Arc<Dentry>) {
DCACHE.lock().insert(dentry.key(), dentry.clone());
self.inner.insert(dentry.name(), Arc::downgrade(&dentry));
if dentry.vnode().is_dentry_cacheable() {
DCACHE.lock().insert(dentry.key(), dentry.clone());
}
self.inner.insert(dentry.name(), Arc::downgrade(dentry));
}
pub fn delete_dentry(&mut self, name: &str) -> Option<Arc<Dentry>> {

View File

@ -1,3 +1,4 @@
use super::Inode;
use crate::prelude::*;
/// DirEntryVec is used to store the entries of a directory.
@ -81,3 +82,37 @@ impl<T> DirEntryVec<T> {
self.slots.iter().filter_map(|x| x.as_ref())
}
}
pub trait DirEntryVecExt {
/// If the entry is not found by `name`, use `f` to get the inode, then put the entry into vec.
fn put_entry_if_not_found(&mut self, name: &str, f: impl Fn() -> Arc<dyn Inode>);
/// Remove and returns the entry by name.
/// Returns `None` if the entry has been removed.
fn remove_entry_by_name(&mut self, name: &str) -> Option<(String, Arc<dyn Inode>)>;
}
impl DirEntryVecExt for DirEntryVec<(String, Arc<dyn Inode>)> {
fn put_entry_if_not_found(&mut self, name: &str, f: impl Fn() -> Arc<dyn Inode>) {
if self
.iter()
.find(|(child_name, _)| child_name == name)
.is_none()
{
let inode = f();
self.put((String::from(name), inode));
}
}
fn remove_entry_by_name(&mut self, name: &str) -> Option<(String, Arc<dyn Inode>)> {
let idx = self
.idxes_and_entries()
.find(|(_, (child_name, _))| child_name == name)
.map(|(idx, _)| idx);
if let Some(idx) = idx {
self.remove(idx)
} else {
None
}
}
}

View File

@ -1,5 +1,6 @@
use alloc::sync::Arc;
use bitflags::bitflags;
use core::any::Any;
use super::Inode;
use crate::prelude::*;
@ -46,7 +47,7 @@ bitflags! {
}
}
pub trait FileSystem: Sync + Send {
pub trait FileSystem: Any + Sync + Send {
fn sync(&self) -> Result<()>;
fn root_inode(&self) -> Arc<dyn Inode>;
@ -54,4 +55,12 @@ pub trait FileSystem: Sync + Send {
fn sb(&self) -> SuperBlock;
fn flags(&self) -> FsFlags;
fn as_any_ref(&self) -> &dyn Any;
}
impl dyn FileSystem {
pub fn downcast_ref<T: FileSystem>(&self) -> Option<&T> {
self.as_any_ref().downcast_ref::<T>()
}
}

View File

@ -200,6 +200,26 @@ pub trait Inode: Any + Sync + Send {
fn fs(&self) -> Arc<dyn FileSystem>;
fn as_any_ref(&self) -> &dyn Any;
/// Returns whether a VFS dentry for this inode should be put into the dentry cache.
///
/// The dentry cache in the VFS layer can accelerate the lookup of inodes. So usually,
/// it is preferable to use the dentry cache. And thus, the default return value of this method
/// is `true`.
///
/// But this caching can raise consistency issues in certain use cases. Specifically, the dentry
/// cache works on the assumption that all FS operations go through the dentry layer first.
/// This is why the dentry cache can reflect the up-to-date FS state. Yet, this assumption
/// may be broken. For example, an inode in procfs (say, `/proc/1/fd/2`) can "disappear" without
/// notice from the perspective of the dentry cache. So for such inodes, they are incompatible
/// with the dentry cache. And this method returns `false`.
///
/// Note that if any ancestor directory of an inode has this method returns `false`, then
/// this inode would not be cached by the dentry cache, even when the method of this
/// inode returns `true`.
fn is_dentry_cacheable(&self) -> bool {
true
}
}
impl dyn Inode {

View File

@ -4,7 +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 direntry_vec::{DirEntryVec, DirEntryVecExt};
pub use fcntl::FcntlCmd;
pub use file_creation_mask::FileCreationMask;
pub use fs::{FileSystem, FsFlags, SuperBlock};

View File

@ -206,4 +206,8 @@ impl Vnode {
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()
}
}

View File

@ -2,6 +2,7 @@
//! This table can be used to get process with pid.
//! TODO: progress group, thread all need similar mapping
use crate::events::{Events, Observer, Subject};
use crate::prelude::*;
use super::{process_group::ProcessGroup, Pgid, Pid, Process};
@ -10,6 +11,7 @@ lazy_static! {
static ref PROCESS_TABLE: Mutex<BTreeMap<Pid, Arc<Process>>> = Mutex::new(BTreeMap::new());
static ref PROCESS_GROUP_TABLE: Mutex<BTreeMap<Pgid, Arc<ProcessGroup>>> =
Mutex::new(BTreeMap::new());
static ref PROCESS_TABLE_SUBJECT: Subject<PidEvent> = Subject::new();
}
/// add a process to global table
@ -21,6 +23,9 @@ pub fn add_process(process: Arc<Process>) {
/// remove a process from global table
pub fn remove_process(pid: Pid) {
PROCESS_TABLE.lock().remove(&pid);
let events = PidEvent::Exit(pid);
PROCESS_TABLE_SUBJECT.notify_observers(&events);
}
/// get a process with pid
@ -58,3 +63,18 @@ pub fn pgid_to_process_group(pgid: Pgid) -> Option<Arc<ProcessGroup>> {
.get(&pgid)
.map(|process_group| process_group.clone())
}
pub fn register_observer(observer: Weak<dyn Observer<PidEvent>>) {
PROCESS_TABLE_SUBJECT.register_observer(observer);
}
pub fn unregister_observer(observer: Weak<dyn Observer<PidEvent>>) {
PROCESS_TABLE_SUBJECT.unregister_observer(observer);
}
#[derive(Copy, Clone)]
pub enum PidEvent {
Exit(Pid),
}
impl Events for PidEvent {}

View File

@ -21,9 +21,11 @@ pub fn sys_getdents64(
fd, buf_addr, buf_len
);
let current = current!();
let file_table = current.file_table().lock();
let file = file_table.get_file(fd)?;
let file = {
let current = current!();
let file_table = current.file_table().lock();
file_table.get_file(fd)?.clone()
};
let inode_handle = file
.as_inode_handle()
.ok_or(Error::with_message(Errno::EBADE, "not inode"))?;

View File

@ -24,18 +24,6 @@ pub fn sys_readlinkat(
);
let current = current!();
if pathname == CString::new("/proc/self/exe")? {
// "proc/self/exe" is used to read the filename of current executable
let process_file_name = current.executable_path().read();
debug!("process exec filename= {:?}", process_file_name);
// readlink does not append a terminating null byte to buf
let bytes = process_file_name.as_bytes();
let write_len = bytes.len().min(usr_buf_len);
write_bytes_to_user(usr_buf_addr, &bytes[..write_len])?;
return Ok(SyscallReturn::Return(write_len as _));
}
// The common path
let dentry = {
let pathname = pathname.to_string_lossy();
if pathname.is_empty() {