Remove the shim kernel crate

This commit is contained in:
Zhang Junyang
2024-08-19 19:15:22 +08:00
committed by Tate, Hongliang Tian
parent d76c7a5b1e
commit dafd16075f
416 changed files with 231 additions and 273 deletions

View File

@ -0,0 +1,60 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::format;
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
utils::Inode,
},
prelude::*,
};
/// Represents the inode at /proc/filesystems.
pub struct FileSystemsFileOps;
impl FileSystemsFileOps {
pub fn new_inode(parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcFileBuilder::new(Self).parent(parent).build().unwrap()
}
}
impl FileOps for FileSystemsFileOps {
fn data(&self) -> Result<Vec<u8>> {
let mut result = String::new();
for fs in FILESYSTEM_TYPES.iter() {
if fs.is_nodev {
result.push_str(&format!("nodev\t{}\n", fs.name));
} else {
result.push_str(&format!("\t{}\n", fs.name));
}
}
Ok(result.into_bytes())
}
}
lazy_static! {
static ref FILESYSTEM_TYPES: Vec<FileSystemType> = {
vec![
FileSystemType::new("proc", true),
FileSystemType::new("ramfs", true),
FileSystemType::new("devpts", true),
FileSystemType::new("ext2", false),
FileSystemType::new("exfat", false),
]
};
}
struct FileSystemType {
name: String,
is_nodev: bool,
}
impl FileSystemType {
fn new(name: &str, is_nodev: bool) -> Self {
FileSystemType {
name: name.to_string(),
is_nodev,
}
}
}

View File

@ -0,0 +1,48 @@
// SPDX-License-Identifier: MPL-2.0
//! This module offers `/proc/meminfo` file support, which tells the user space
//! about the memory statistics in the entire system. The definition of the
//! fields are similar to that of Linux's but there exist differences.
//!
//! Reference: <https://man7.org/linux/man-pages/man5/proc_meminfo.5.html>
use alloc::format;
use ostd::mm::stat;
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
utils::Inode,
},
prelude::*,
};
/// Represents the inode at `/proc/meminfo`.
pub struct MemInfoFileOps;
impl MemInfoFileOps {
pub fn new_inode(parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcFileBuilder::new(Self).parent(parent).build().unwrap()
}
}
/// Total memory in the entire system in bytes.
fn mem_total() -> usize {
stat::mem_total()
}
/// An estimation of how much memory is available for starting new
/// applications, without disk operations.
fn mem_available() -> usize {
stat::mem_available()
}
impl FileOps for MemInfoFileOps {
fn data(&self) -> Result<Vec<u8>> {
let total = mem_total();
let available = mem_available();
let output = format!("MemTotal:\t{}\nMemAvailable:\t{}\n", total, available);
Ok(output.into_bytes())
}
}

140
kernel/src/fs/procfs/mod.rs Normal file
View File

@ -0,0 +1,140 @@
// SPDX-License-Identifier: MPL-2.0
use core::sync::atomic::{AtomicU64, Ordering};
use sys::SysDirOps;
use self::{
meminfo::MemInfoFileOps,
pid::PidDirOps,
self_::SelfSymOps,
template::{DirOps, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps},
};
use crate::{
events::Observer,
fs::{
procfs::filesystems::FileSystemsFileOps,
utils::{DirEntryVecExt, FileSystem, FsFlags, Inode, SuperBlock, NAME_MAX},
},
prelude::*,
process::{process_table, process_table::PidEvent, Pid},
};
mod filesystems;
mod meminfo;
mod pid;
mod self_;
mod sys;
mod template;
/// Magic number.
const PROC_MAGIC: u64 = 0x9fa0;
/// Root Inode ID.
const PROC_ROOT_INO: u64 = 1;
/// Block size.
const BLOCK_SIZE: usize = 1024;
pub struct ProcFS {
sb: SuperBlock,
root: Arc<dyn Inode>,
inode_allocator: AtomicU64,
}
impl ProcFS {
pub fn new() -> Arc<Self> {
Arc::new_cyclic(|weak_fs| Self {
sb: SuperBlock::new(PROC_MAGIC, BLOCK_SIZE, NAME_MAX),
root: RootDirOps::new_inode(weak_fs.clone()),
inode_allocator: AtomicU64::new(PROC_ROOT_INO + 1),
})
}
pub(in crate::fs::procfs) fn alloc_id(&self) -> u64 {
self.inode_allocator.fetch_add(1, Ordering::SeqCst)
}
}
impl FileSystem for ProcFS {
fn sync(&self) -> Result<()> {
Ok(())
}
fn root_inode(&self) -> Arc<dyn Inode> {
self.root.clone()
}
fn sb(&self) -> SuperBlock {
self.sb.clone()
}
fn flags(&self) -> FsFlags {
FsFlags::empty()
}
}
/// Represents the inode at `/proc`.
struct RootDirOps;
impl RootDirOps {
pub fn new_inode(fs: Weak<ProcFS>) -> Arc<dyn Inode> {
let root_inode = ProcDirBuilder::new(Self)
.fs(fs)
.ino(PROC_ROOT_INO)
.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 name == "sys" {
SysDirOps::new_inode(this_ptr.clone())
} else if name == "filesystems" {
FileSystemsFileOps::new_inode(this_ptr.clone())
} else if name == "meminfo" {
MemInfoFileOps::new_inode(this_ptr.clone())
} else if let Ok(pid) = name.parse::<Pid>() {
let process_ref =
process_table::get_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()));
cached_children.put_entry_if_not_found("sys", || SysDirOps::new_inode(this_ptr.clone()));
cached_children.put_entry_if_not_found("filesystems", || {
FileSystemsFileOps::new_inode(this_ptr.clone())
});
cached_children
.put_entry_if_not_found("meminfo", || MemInfoFileOps::new_inode(this_ptr.clone()));
for process in process_table::process_table().iter() {
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,40 @@
// SPDX-License-Identifier: MPL-2.0
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
utils::Inode,
},
prelude::*,
Process,
};
/// Represents the inode at `/proc/[pid]/cmdline`.
pub struct CmdlineFileOps(Arc<Process>);
impl CmdlineFileOps {
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 CmdlineFileOps {
fn data(&self) -> Result<Vec<u8>> {
let cmdline_output = if self.0.is_zombie() {
// Returns 0 characters for zombie process.
Vec::new()
} else {
let Ok(argv_cstrs) = self.0.vm().init_stack_reader().argv() else {
return Ok(Vec::new());
};
argv_cstrs
.into_iter()
.flat_map(|c_str| c_str.into_bytes_with_nul().into_iter())
.collect()
};
Ok(cmdline_output)
}
}

View File

@ -0,0 +1,39 @@
// SPDX-License-Identifier: MPL-2.0
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
utils::Inode,
},
prelude::*,
Process,
};
/// 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();
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,28 @@
// SPDX-License-Identifier: MPL-2.0
use crate::{
fs::{
procfs::{ProcSymBuilder, SymOps},
utils::Inode,
},
prelude::*,
Process,
};
/// 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())
}
}

View File

@ -0,0 +1,98 @@
// SPDX-License-Identifier: MPL-2.0
use crate::{
fs::{
file_handle::FileLike,
file_table::FileDesc,
inode_handle::InodeHandle,
procfs::{
pid::FdEvents, DirOps, Observer, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps,
},
utils::{DirEntryVecExt, Inode},
},
prelude::*,
Process,
};
/// 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::<FileDesc>()
.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(Arc<dyn FileLike>);
impl FileSymOps {
pub fn new_inode(file: Arc<dyn FileLike>, 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.downcast_ref::<InodeHandle>() {
inode_handle.dentry().abs_path()
} else {
// TODO: get the real path for other FileLike object
String::from("/dev/tty")
};
Ok(path)
}
}

View File

@ -0,0 +1,78 @@
// SPDX-License-Identifier: MPL-2.0
use self::{cmdline::CmdlineFileOps, comm::CommFileOps, exe::ExeSymOps, fd::FdDirOps};
use super::template::{DirOps, ProcDir, ProcDirBuilder};
use crate::{
events::Observer,
fs::{
file_table::FdEvents,
utils::{DirEntryVecExt, Inode},
},
prelude::*,
process::Process,
};
mod cmdline;
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()),
"cmdline" => CmdlineFileOps::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())
});
cached_children.put_entry_if_not_found("cmdline", || {
CmdlineFileOps::new_inode(self.0.clone(), this_ptr.clone())
});
}
}

View File

@ -0,0 +1,24 @@
// SPDX-License-Identifier: MPL-2.0
use crate::{
fs::{
procfs::{ProcSymBuilder, SymOps},
utils::Inode,
},
prelude::*,
};
/// 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,29 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::format;
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
utils::Inode,
},
prelude::*,
process::credentials::capabilities::CapSet,
};
/// Represents the inode at `/proc/sys/kernel/cap_last_cap`.
pub struct CapLastCapFileOps;
impl CapLastCapFileOps {
pub fn new_inode(parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcFileBuilder::new(Self).parent(parent).build().unwrap()
}
}
impl FileOps for CapLastCapFileOps {
fn data(&self) -> Result<Vec<u8>> {
let cap_last_cap_value = CapSet::most_significant_bit();
let output = format!("{}\n", cap_last_cap_value);
Ok(output.into_bytes())
}
}

View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: MPL-2.0
use crate::{
fs::{
procfs::{
sys::kernel::cap_last_cap::CapLastCapFileOps,
template::{DirOps, ProcDirBuilder},
ProcDir,
},
utils::{DirEntryVecExt, Inode},
},
prelude::*,
};
mod cap_last_cap;
/// Represents the inode at `/proc/sys/kernel`.
pub struct KernelDirOps;
impl KernelDirOps {
pub fn new_inode(parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcDirBuilder::new(Self).parent(parent).build().unwrap()
}
}
impl DirOps for KernelDirOps {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
let inode = match name {
"cap_last_cap" => CapLastCapFileOps::new_inode(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<KernelDirOps>>().unwrap().this()
};
let mut cached_children = this.cached_children().write();
cached_children.put_entry_if_not_found("cap_last_cap", || {
CapLastCapFileOps::new_inode(this_ptr.clone())
});
}
}

View File

@ -0,0 +1,41 @@
// SPDX-License-Identifier: MPL-2.0
use self::kernel::KernelDirOps;
use crate::{
fs::{
procfs::template::{DirOps, ProcDir, ProcDirBuilder},
utils::{DirEntryVecExt, Inode},
},
prelude::*,
};
mod kernel;
/// Represents the inode at `/proc/sys`.
pub struct SysDirOps;
impl SysDirOps {
pub fn new_inode(parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
ProcDirBuilder::new(Self).parent(parent).build().unwrap()
}
}
impl DirOps for SysDirOps {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
let inode = match name {
"kernel" => KernelDirOps::new_inode(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<SysDirOps>>().unwrap().this()
};
let mut cached_children = this.cached_children().write();
cached_children
.put_entry_if_not_found("kernel", || KernelDirOps::new_inode(this_ptr.clone()))
}
}

View File

@ -0,0 +1,198 @@
// SPDX-License-Identifier: MPL-2.0
#![allow(dead_code)]
use super::{
dir::{DirOps, ProcDir},
file::{FileOps, ProcFile},
sym::{ProcSym, SymOps},
};
use crate::{
fs::utils::{FileSystem, Inode},
prelude::*,
};
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: Weak<dyn FileSystem>) -> Self {
self.optional_builder(|ob| ob.fs(fs))
}
pub fn volatile(self) -> Self {
self.optional_builder(|ob| ob.volatile())
}
pub fn ino(self, ino: u64) -> Self {
self.optional_builder(|ob| ob.ino(ino))
}
pub fn build(mut self) -> Result<Arc<ProcDir<O>>> {
let (fs, parent, ino, is_volatile) = self.optional_builder.take().unwrap().build()?;
Ok(ProcDir::new(self.dir, fs, parent, ino, 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<Weak<dyn FileSystem>>,
ino: Option<u64>,
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: Weak<dyn FileSystem>) -> Self {
self.fs = Some(fs);
self
}
pub fn ino(mut self, ino: u64) -> Self {
self.ino = Some(ino);
self
}
pub fn volatile(mut self) -> Self {
self.is_volatile = true;
self
}
#[allow(clippy::type_complexity)]
pub fn build(
self,
) -> Result<(
Weak<dyn FileSystem>,
Option<Weak<dyn Inode>>,
Option<u64>,
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(|| {
Arc::downgrade(&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, self.ino, is_volatile))
}
}

View File

@ -0,0 +1,189 @@
// SPDX-License-Identifier: MPL-2.0
#![allow(unused_variables)]
use core::time::Duration;
use aster_util::slot_vec::SlotVec;
use inherit_methods_macro::inherit_methods;
use super::{Common, ProcFS};
use crate::{
fs::utils::{DirentVisitor, FileSystem, Inode, InodeMode, InodeType, Metadata, MknodType},
prelude::*,
process::{Gid, Uid},
};
pub struct ProcDir<D: DirOps> {
inner: D,
this: Weak<ProcDir<D>>,
parent: Option<Weak<dyn Inode>>,
cached_children: RwMutex<SlotVec<(String, Arc<dyn Inode>)>>,
common: Common,
}
impl<D: DirOps> ProcDir<D> {
pub fn new(
dir: D,
fs: Weak<dyn FileSystem>,
parent: Option<Weak<dyn Inode>>,
ino: Option<u64>,
is_volatile: bool,
) -> Arc<Self> {
let common = {
let ino = ino.unwrap_or_else(|| {
let arc_fs = fs.upgrade().unwrap();
let procfs = arc_fs.downcast_ref::<ProcFS>().unwrap();
procfs.alloc_id()
});
let metadata =
Metadata::new_dir(ino, InodeMode::from_bits_truncate(0o555), super::BLOCK_SIZE);
Common::new(metadata, fs, is_volatile)
};
Arc::new_cyclic(|weak_self| Self {
inner: dir,
this: weak_self.clone(),
parent,
cached_children: RwMutex::new(SlotVec::new()),
common,
})
}
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) -> &RwMutex<SlotVec<(String, Arc<dyn Inode>)>> {
&self.cached_children
}
}
#[inherit_methods(from = "self.common")]
impl<D: DirOps + 'static> Inode for ProcDir<D> {
fn size(&self) -> usize;
fn metadata(&self) -> Metadata;
fn ino(&self) -> u64;
fn mode(&self) -> Result<InodeMode>;
fn set_mode(&self, mode: InodeMode) -> Result<()>;
fn owner(&self) -> Result<Uid>;
fn set_owner(&self, uid: Uid) -> Result<()>;
fn group(&self) -> Result<Gid>;
fn set_group(&self, gid: Gid) -> Result<()>;
fn atime(&self) -> Duration;
fn set_atime(&self, time: Duration);
fn mtime(&self) -> Duration;
fn set_mtime(&self, time: Duration);
fn ctime(&self) -> Duration;
fn set_ctime(&self, time: Duration);
fn fs(&self) -> Arc<dyn FileSystem>;
fn resize(&self, _new_size: usize) -> Result<()> {
Err(Error::new(Errno::EISDIR))
}
fn type_(&self) -> InodeType {
InodeType::Dir
}
fn create(&self, _name: &str, _type_: InodeType, _mode: InodeMode) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::EPERM))
}
fn mknod(&self, _name: &str, _mode: InodeMode, type_: MknodType) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::EPERM))
}
fn readdir_at(&self, 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.common.ino(),
this_inode.common.type_(),
*offset,
)?;
*offset += 1;
}
if *offset == 1 {
let parent_inode = self.parent().unwrap_or(self.this());
visitor.visit("..", parent_inode.ino(), parent_inode.type_(), *offset)?;
*offset += 1;
}
// Read the normal child entries.
self.inner.populate_children(self.this.clone());
let cached_children = self.cached_children.read();
let start_offset = *offset;
for (idx, (name, child)) in cached_children
.idxes_and_items()
.map(|(idx, (name, child))| (idx + 2, (name, child)))
.skip_while(|(idx, _)| idx < &start_offset)
{
visitor.visit(name.as_ref(), child.ino(), child.type_(), idx)?;
*offset = idx + 1;
}
Ok(())
};
let mut iterate_offset = offset;
match try_readdir(&mut iterate_offset, visitor) {
Err(e) if iterate_offset == offset => Err(e),
_ => Ok(iterate_offset - 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 is_dentry_cacheable(&self) -> bool {
!self.common.is_volatile()
}
}
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,105 @@
// SPDX-License-Identifier: MPL-2.0
use core::time::Duration;
use inherit_methods_macro::inherit_methods;
use super::{Common, ProcFS};
use crate::{
fs::utils::{FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata},
prelude::*,
process::{Gid, Uid},
};
pub struct ProcFile<F: FileOps> {
inner: F,
common: Common,
}
impl<F: FileOps> ProcFile<F> {
pub fn new(file: F, fs: Weak<dyn FileSystem>, is_volatile: bool) -> Arc<Self> {
let common = {
let arc_fs = fs.upgrade().unwrap();
let procfs = arc_fs.downcast_ref::<ProcFS>().unwrap();
let metadata = Metadata::new_file(
procfs.alloc_id(),
InodeMode::from_bits_truncate(0o444),
super::BLOCK_SIZE,
);
Common::new(metadata, fs, is_volatile)
};
Arc::new(Self {
inner: file,
common,
})
}
}
#[inherit_methods(from = "self.common")]
impl<F: FileOps + 'static> Inode for ProcFile<F> {
fn size(&self) -> usize;
fn metadata(&self) -> Metadata;
fn ino(&self) -> u64;
fn mode(&self) -> Result<InodeMode>;
fn set_mode(&self, mode: InodeMode) -> Result<()>;
fn owner(&self) -> Result<Uid>;
fn set_owner(&self, uid: Uid) -> Result<()>;
fn group(&self) -> Result<Gid>;
fn set_group(&self, gid: Gid) -> Result<()>;
fn atime(&self) -> Duration;
fn set_atime(&self, time: Duration);
fn mtime(&self) -> Duration;
fn set_mtime(&self, time: Duration);
fn ctime(&self) -> Duration;
fn set_ctime(&self, time: Duration);
fn fs(&self) -> Arc<dyn FileSystem>;
fn resize(&self, _new_size: usize) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn type_(&self) -> InodeType {
InodeType::File
}
fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
let data = self.inner.data()?;
let start = data.len().min(offset);
let end = data.len().min(offset + writer.avail());
let len = end - start;
writer.write_fallible(&mut (&data[start..end]).into())?;
Ok(len)
}
fn read_direct_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
self.read_at(offset, writer)
}
fn write_at(&self, _offset: usize, _reader: &mut VmReader) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn write_direct_at(&self, _offset: usize, _reader: &mut VmReader) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
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, _arg: usize) -> Result<i32> {
Err(Error::new(Errno::EPERM))
}
fn is_dentry_cacheable(&self) -> bool {
!self.common.is_volatile()
}
}
pub trait FileOps: Sync + Send {
fn data(&self) -> Result<Vec<u8>>;
}

View File

@ -0,0 +1,112 @@
// SPDX-License-Identifier: MPL-2.0
use core::time::Duration;
pub use self::{
builder::{ProcDirBuilder, ProcFileBuilder, ProcSymBuilder},
dir::{DirOps, ProcDir},
file::FileOps,
sym::SymOps,
};
use super::{ProcFS, BLOCK_SIZE};
use crate::{
fs::utils::{FileSystem, InodeMode, InodeType, Metadata},
prelude::*,
process::{Gid, Uid},
};
mod builder;
mod dir;
mod file;
mod sym;
struct Common {
metadata: RwLock<Metadata>,
fs: Weak<dyn FileSystem>,
is_volatile: bool,
}
impl Common {
pub fn new(metadata: Metadata, fs: Weak<dyn FileSystem>, is_volatile: bool) -> Self {
Self {
metadata: RwLock::new(metadata),
fs,
is_volatile,
}
}
pub fn fs(&self) -> Arc<dyn FileSystem> {
self.fs.upgrade().unwrap()
}
pub fn metadata(&self) -> Metadata {
*self.metadata.read()
}
pub fn ino(&self) -> u64 {
self.metadata.read().ino
}
pub fn type_(&self) -> InodeType {
self.metadata.read().type_
}
pub fn size(&self) -> usize {
self.metadata.read().size
}
pub fn atime(&self) -> Duration {
self.metadata.read().atime
}
pub fn set_atime(&self, time: Duration) {
self.metadata.write().atime = time;
}
pub fn mtime(&self) -> Duration {
self.metadata.read().mtime
}
pub fn set_mtime(&self, time: Duration) {
self.metadata.write().mtime = time;
}
pub fn ctime(&self) -> Duration {
self.metadata.read().ctime
}
pub fn set_ctime(&self, time: Duration) {
self.metadata.write().ctime = time;
}
pub fn mode(&self) -> Result<InodeMode> {
Ok(self.metadata.read().mode)
}
pub fn set_mode(&self, mode: InodeMode) -> Result<()> {
self.metadata.write().mode = mode;
Ok(())
}
pub fn owner(&self) -> Result<Uid> {
Ok(self.metadata.read().uid)
}
pub fn set_owner(&self, uid: Uid) -> Result<()> {
self.metadata.write().uid = uid;
Ok(())
}
pub fn group(&self) -> Result<Gid> {
Ok(self.metadata.read().gid)
}
pub fn set_group(&self, gid: Gid) -> Result<()> {
self.metadata.write().gid = gid;
Ok(())
}
pub fn is_volatile(&self) -> bool {
self.is_volatile
}
}

View File

@ -0,0 +1,97 @@
// SPDX-License-Identifier: MPL-2.0
use core::time::Duration;
use inherit_methods_macro::inherit_methods;
use super::{Common, ProcFS};
use crate::{
fs::utils::{FileSystem, Inode, InodeMode, InodeType, IoctlCmd, Metadata},
prelude::*,
process::{Gid, Uid},
};
pub struct ProcSym<S: SymOps> {
inner: S,
common: Common,
}
impl<S: SymOps> ProcSym<S> {
pub fn new(sym: S, fs: Weak<dyn FileSystem>, is_volatile: bool) -> Arc<Self> {
let common = {
let arc_fs = fs.upgrade().unwrap();
let procfs = arc_fs.downcast_ref::<ProcFS>().unwrap();
let metadata = Metadata::new_symlink(
procfs.alloc_id(),
InodeMode::from_bits_truncate(0o777),
super::BLOCK_SIZE,
);
Common::new(metadata, fs, is_volatile)
};
Arc::new(Self { inner: sym, common })
}
}
#[inherit_methods(from = "self.common")]
impl<S: SymOps + 'static> Inode for ProcSym<S> {
fn size(&self) -> usize;
fn metadata(&self) -> Metadata;
fn ino(&self) -> u64;
fn mode(&self) -> Result<InodeMode>;
fn set_mode(&self, mode: InodeMode) -> Result<()>;
fn owner(&self) -> Result<Uid>;
fn set_owner(&self, uid: Uid) -> Result<()>;
fn group(&self) -> Result<Gid>;
fn set_group(&self, gid: Gid) -> Result<()>;
fn atime(&self) -> Duration;
fn set_atime(&self, time: Duration);
fn mtime(&self) -> Duration;
fn set_mtime(&self, time: Duration);
fn ctime(&self) -> Duration;
fn set_ctime(&self, time: Duration);
fn fs(&self) -> Arc<dyn FileSystem>;
fn resize(&self, _new_size: usize) -> Result<()> {
Err(Error::new(Errno::EPERM))
}
fn type_(&self) -> InodeType {
InodeType::SymLink
}
fn read_at(&self, _offset: usize, _writer: &mut VmWriter) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn read_direct_at(&self, _offset: usize, _writer: &mut VmWriter) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn write_at(&self, _offset: usize, _reader: &mut VmReader) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
fn write_direct_at(&self, _offset: usize, _reader: &mut VmReader) -> Result<usize> {
Err(Error::new(Errno::EPERM))
}
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, _arg: usize) -> Result<i32> {
Err(Error::new(Errno::EPERM))
}
fn is_dentry_cacheable(&self) -> bool {
!self.common.is_volatile()
}
}
pub trait SymOps: Sync + Send {
fn read_link(&self) -> Result<String>;
}