mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 21:06:48 +00:00
Add ProcFS
This commit is contained in:
parent
12b4e0637d
commit
b02b29dde7
@ -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 {}
|
||||
|
@ -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)?
|
||||
|
@ -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;
|
||||
|
125
src/services/libs/jinux-std/src/fs/procfs/mod.rs
Normal file
125
src/services/libs/jinux-std/src/fs/procfs/mod.rs
Normal 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())
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
30
src/services/libs/jinux-std/src/fs/procfs/pid/comm.rs
Normal file
30
src/services/libs/jinux-std/src/fs/procfs/pid/comm.rs
Normal 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;
|
19
src/services/libs/jinux-std/src/fs/procfs/pid/exe.rs
Normal file
19
src/services/libs/jinux-std/src/fs/procfs/pid/exe.rs
Normal 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())
|
||||
}
|
||||
}
|
88
src/services/libs/jinux-std/src/fs/procfs/pid/fd.rs
Normal file
88
src/services/libs/jinux-std/src/fs/procfs/pid/fd.rs
Normal 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)
|
||||
}
|
||||
}
|
72
src/services/libs/jinux-std/src/fs/procfs/pid/mod.rs
Normal file
72
src/services/libs/jinux-std/src/fs/procfs/pid/mod.rs
Normal 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())
|
||||
})
|
||||
}
|
||||
}
|
16
src/services/libs/jinux-std/src/fs/procfs/self_.rs
Normal file
16
src/services/libs/jinux-std/src/fs/procfs/self_.rs
Normal 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())
|
||||
}
|
||||
}
|
175
src/services/libs/jinux-std/src/fs/procfs/template/builder.rs
Normal file
175
src/services/libs/jinux-std/src/fs/procfs/template/builder.rs
Normal 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))
|
||||
}
|
||||
}
|
225
src/services/libs/jinux-std/src/fs/procfs/template/dir.rs
Normal file
225
src/services/libs/jinux-std/src/fs/procfs/template/dir.rs
Normal 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>) {}
|
||||
}
|
136
src/services/libs/jinux-std/src/fs/procfs/template/file.rs
Normal file
136
src/services/libs/jinux-std/src/fs/procfs/template/file.rs
Normal 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>>;
|
||||
}
|
42
src/services/libs/jinux-std/src/fs/procfs/template/mod.rs
Normal file
42
src/services/libs/jinux-std/src/fs/procfs/template/mod.rs
Normal 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
|
||||
}
|
||||
}
|
131
src/services/libs/jinux-std/src/fs/procfs/template/sym.rs
Normal file
131
src/services/libs/jinux-std/src/fs/procfs/template/sym.rs
Normal 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>;
|
||||
}
|
@ -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_>);
|
||||
|
@ -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>> {
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>()
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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};
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
@ -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"))?;
|
||||
|
@ -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() {
|
||||
|
Loading…
x
Reference in New Issue
Block a user