Correct lock usages in procfs

This commit is contained in:
Ruihan Li 2025-10-31 23:58:08 +08:00 committed by Jianfeng Jiang
parent 4e0142b176
commit 2d01804d19
23 changed files with 599 additions and 465 deletions

View File

@ -6,7 +6,7 @@ use aster_util::slot_vec::SlotVec;
use super::file_handle::FileLike;
use crate::{
events::{Events, IoEvents, Observer, Subject},
events::{IoEvents, Observer},
fs::utils::StatusFlags,
prelude::*,
process::{
@ -18,16 +18,15 @@ use crate::{
pub type FileDesc = i32;
#[derive(Clone)]
pub struct FileTable {
table: SlotVec<FileTableEntry>,
subject: Subject<FdEvents>,
}
impl FileTable {
pub const fn new() -> Self {
Self {
table: SlotVec::new(),
subject: Subject::new(),
}
}
@ -70,10 +69,6 @@ impl FileTable {
pub fn close_file(&mut self, fd: FileDesc) -> Option<Arc<dyn FileLike>> {
let removed_entry = self.table.remove(fd as usize)?;
let events = FdEvents::Close(fd);
self.notify_fd_events(&events);
Some(removed_entry.file)
}
@ -100,8 +95,6 @@ impl FileTable {
for fd in closed_fds {
let removed_entry = self.table.remove(fd as usize).unwrap();
let events = FdEvents::Close(fd);
self.notify_fd_events(&events);
closed_files.push(removed_entry.file);
}
@ -132,19 +125,6 @@ impl FileTable {
.idxes_and_items()
.map(|(idx, entry)| (idx as FileDesc, entry.file()))
}
pub fn register_observer(&self, observer: Weak<dyn Observer<FdEvents>>) {
self.subject.register_observer(observer, ());
}
#[expect(dead_code)]
pub fn unregister_observer(&self, observer: &Weak<dyn Observer<FdEvents>>) {
self.subject.unregister_observer(observer);
}
fn notify_fd_events(&self, events: &FdEvents) {
self.subject.notify_observers(events);
}
}
impl Default for FileTable {
@ -153,25 +133,6 @@ impl Default for FileTable {
}
}
impl Clone for FileTable {
fn clone(&self) -> Self {
Self {
table: self.table.clone(),
subject: Subject::new(),
}
}
}
impl Drop for FileTable {
fn drop(&mut self) {
// Closes all files first.
self.close_files(|_| true);
let events = FdEvents::DropFileTable;
self.subject.notify_observers(&events);
}
}
/// A helper trait that provides methods to operate the file table.
pub trait WithFileTable {
/// Calls `f` with the file table.
@ -238,14 +199,6 @@ macro_rules! get_file_fast {
pub(crate) use get_file_fast;
#[derive(Copy, Clone, Debug)]
pub enum FdEvents {
Close(FileDesc),
DropFileTable,
}
impl Events for FdEvents {}
pub struct FileTableEntry {
file: Arc<dyn FileLike>,
flags: AtomicU8,

View File

@ -2,6 +2,10 @@
use core::sync::atomic::{AtomicU64, Ordering};
use aster_util::slot_vec::SlotVec;
use ostd::sync::RwMutexUpgradeableGuard;
use template::{lookup_child_from_table, populate_children_from_table};
use self::{
cmdline::CmdLineFileOps,
cpuinfo::CpuInfoFileOps,
@ -136,78 +140,83 @@ impl RootDirOps {
root_inode
}
#[expect(clippy::type_complexity)]
const STATIC_ENTRIES: &'static [(&'static str, fn(Weak<dyn Inode>) -> Arc<dyn Inode>)] = &[
("cmdline", CmdLineFileOps::new_inode),
("cpuinfo", CpuInfoFileOps::new_inode),
("filesystems", FileSystemsFileOps::new_inode),
("loadavg", LoadAvgFileOps::new_inode),
("meminfo", MemInfoFileOps::new_inode),
("self", SelfSymOps::new_inode),
("stat", StatFileOps::new_inode),
("sys", SysDirOps::new_inode),
("thread-self", ThreadSelfSymOps::new_inode),
("uptime", UptimeFileOps::new_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 == "thread-self" {
ThreadSelfSymOps::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 name == "loadavg" {
LoadAvgFileOps::new_inode(this_ptr.clone())
} else if name == "cpuinfo" {
CpuInfoFileOps::new_inode(this_ptr.clone())
} else if name == "uptime" {
UptimeFileOps::new_inode(this_ptr.clone())
} else if name == "stat" {
StatFileOps::new_inode(this_ptr.clone())
} else if name == "cmdline" {
CmdLineFileOps::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)
// Lock order: process table -> cached entries
//
// Note that inverting the lock order is non-trivial because `Observer::on_events` will be
// called with the process table locked.
fn lookup_child(&self, dir: &ProcDir<Self>, name: &str) -> Result<Arc<dyn Inode>> {
if let Ok(pid) = name.parse::<Pid>()
&& let process_table_mut = process_table::process_table_mut()
&& let Some(process_ref) = process_table_mut.get(pid)
{
let mut cached_children = dir.cached_children().write();
return Ok(cached_children
.put_entry_if_not_found(name, || {
PidDirOps::new_inode(process_ref.clone(), dir.this_weak().clone())
})
.clone());
}
let mut cached_children = dir.cached_children().write();
if let Some(child) =
lookup_child_from_table(name, &mut cached_children, Self::STATIC_ENTRIES, |f| {
(f)(dir.this_weak().clone())
})
{
return Ok(child);
}
return_errno_with_message!(Errno::ENOENT, "the file does not exist");
}
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("thread-self", || {
ThreadSelfSymOps::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()));
cached_children
.put_entry_if_not_found("loadavg", || LoadAvgFileOps::new_inode(this_ptr.clone()));
cached_children
.put_entry_if_not_found("cpuinfo", || CpuInfoFileOps::new_inode(this_ptr.clone()));
cached_children
.put_entry_if_not_found("uptime", || UptimeFileOps::new_inode(this_ptr.clone()));
cached_children.put_entry_if_not_found("stat", || StatFileOps::new_inode(this_ptr.clone()));
cached_children
.put_entry_if_not_found("cmdline", || CmdLineFileOps::new_inode(this_ptr.clone()));
for process in process_table::process_table_mut().iter() {
let pid = process.pid().to_string();
fn populate_children<'a>(
&self,
dir: &'a ProcDir<Self>,
) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc<dyn Inode>)>> {
let process_table_mut = process_table::process_table_mut();
let mut cached_children = dir.cached_children().write();
for process_ref in process_table_mut.iter() {
let pid = process_ref.pid().to_string();
cached_children.put_entry_if_not_found(&pid, || {
PidDirOps::new_inode(process.clone(), this_ptr.clone())
PidDirOps::new_inode(process_ref.clone(), dir.this_weak().clone())
});
}
drop(process_table_mut);
populate_children_from_table(&mut cached_children, Self::STATIC_ENTRIES, |f| {
(f)(dir.this_weak().clone())
});
cached_children.downgrade()
}
}

View File

@ -1,18 +1,21 @@
// SPDX-License-Identifier: MPL-2.0
use super::template::{DirOps, ProcDir, ProcDirBuilder};
use aster_util::slot_vec::SlotVec;
use ostd::sync::RwMutexUpgradeableGuard;
use super::template::{
lookup_child_from_table, populate_children_from_table, DirOps, ProcDir, ProcDirBuilder,
};
use crate::{
events::Observer,
fs::{
file_table::FdEvents,
procfs::pid::{
stat::StatFileOps,
task::{TaskDirOps, TidDirOps},
},
utils::{mkmod, DirEntryVecExt, Inode},
utils::{mkmod, Inode},
},
prelude::*,
process::{posix_thread::AsPosixThread, Process},
process::Process,
};
mod stat;
@ -35,88 +38,63 @@ impl PidDirOps {
thread_ref,
}
};
let file_table = tid_dir_ops
.thread_ref
.as_posix_thread()
.unwrap()
.file_table();
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3493>
let pid_inode = ProcDirBuilder::new(Self(tid_dir_ops.clone()), mkmod!(a+rx))
ProcDirBuilder::new(Self(tid_dir_ops.clone()), mkmod!(a+rx))
.parent(parent)
// The pid directories must be volatile, because it is just associated with one process.
// The PID directories must be volatile, because it is just associated with one process.
.volatile()
.build()
.unwrap();
// This is for an exiting process that has not yet been reaped by its parent,
// whose file table may have already been released.
if let Some(file_table_ref) = file_table.lock().as_ref() {
file_table_ref
.read()
.register_observer(Arc::downgrade(&pid_inode) as _);
}
pid_inode
.unwrap()
}
}
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");
}
}
#[expect(clippy::type_complexity)]
const STATIC_ENTRIES: &'static [(
&'static str,
fn(&PidDirOps, Weak<dyn Inode>) -> Arc<dyn Inode>,
)] = &[
("stat", StatFileOps::new_inode_pid),
("task", TaskDirOps::new_inode),
];
}
impl DirOps for PidDirOps {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
fn lookup_child(&self, dir: &ProcDir<Self>, name: &str) -> Result<Arc<dyn Inode>> {
let mut cached_children = dir.cached_children().write();
// Look up entries that either exist under `/proc/<pid>`
// but not under `/proc/<pid>/task/<tid>`,
// or entries whose contents differ between `/proc/<pid>` and `/proc/<pid>/task/<tid>`.
match name {
"stat" => {
return Ok(StatFileOps::new_inode(
self.0.process_ref.clone(),
self.0.thread_ref.clone(),
true,
this_ptr,
))
}
"task" => return Ok(TaskDirOps::new_inode(self.0.process_ref.clone(), this_ptr)),
_ => {}
if let Some(child) =
lookup_child_from_table(name, &mut cached_children, Self::STATIC_ENTRIES, |f| {
(f)(self, dir.this_weak().clone())
})
{
return Ok(child);
}
// For all other children, the content is the same under both `/proc/<pid>` and `/proc/<pid>/task/<tid>`.
self.0.lookup_child(this_ptr, name)
self.0
.lookup_child_locked(&mut cached_children, dir.this_weak().clone(), name)
}
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();
fn populate_children<'a>(
&self,
dir: &'a ProcDir<Self>,
) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc<dyn Inode>)>> {
let mut cached_children = dir.cached_children().write();
// Populate entries that either exist under `/proc/<pid>`
// but not under `/proc/<pid>/task/<tid>`,
// or whose contents differ between the two paths.
cached_children.put_entry_if_not_found("stat", || {
StatFileOps::new_inode(
self.0.process_ref.clone(),
self.0.thread_ref.clone(),
true,
this_ptr.clone(),
)
});
cached_children.put_entry_if_not_found("task", || {
TaskDirOps::new_inode(self.0.process_ref.clone(), this_ptr.clone())
populate_children_from_table(&mut cached_children, Self::STATIC_ENTRIES, |f| {
(f)(self, dir.this_weak().clone())
});
// Populate the remaining children that are identical
// under both `/proc/<pid>` and `/proc/<pid>/task/<tid>`.
self.0
.populate_children_inner(&mut cached_children, this_ptr);
.populate_children_locked(&mut cached_children, dir.this_weak().clone());
cached_children.downgrade()
}
}

View File

@ -2,6 +2,7 @@
use core::{fmt::Write, sync::atomic::Ordering};
use super::{PidDirOps, TidDirOps};
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
@ -81,7 +82,19 @@ pub struct StatFileOps {
}
impl StatFileOps {
pub fn new_inode(
pub fn new_inode_pid(dir: &PidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let process_ref = dir.0.process_ref.clone();
let thread_ref = dir.0.thread_ref.clone();
Self::new_inode_impl(process_ref, thread_ref, true, parent)
}
pub fn new_inode_tid(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let process_ref = dir.process_ref.clone();
let thread_ref = dir.thread_ref.clone();
Self::new_inode_impl(process_ref, thread_ref, false, parent)
}
fn new_inode_impl(
process_ref: Arc<Process>,
thread_ref: Arc<Thread>,
is_pid_stat: bool,

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use super::TidDirOps;
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
@ -13,7 +14,8 @@ use crate::{
pub struct CmdlineFileOps(Arc<Process>);
impl CmdlineFileOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let process_ref = dir.process_ref.clone();
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3340>
ProcFileBuilder::new(Self(process_ref), mkmod!(a+r))
.parent(parent)

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use super::TidDirOps;
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
@ -13,7 +14,8 @@ use crate::{
pub struct CommFileOps(Arc<Process>);
impl CommFileOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let process_ref = dir.process_ref.clone();
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3336>
ProcFileBuilder::new(Self(process_ref), mkmod!(a+r, u+w))
.parent(parent)

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use super::TidDirOps;
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
@ -13,7 +14,8 @@ use crate::{
pub struct EnvironFileOps(Arc<Process>);
impl EnvironFileOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let process_ref = dir.process_ref.clone();
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3324>
ProcFileBuilder::new(Self(process_ref), mkmod!(u+r))
.parent(parent)

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use super::TidDirOps;
use crate::{
fs::{
procfs::{ProcSymBuilder, SymOps},
@ -13,7 +14,8 @@ use crate::{
pub struct ExeSymOps(Arc<Process>);
impl ExeSymOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let process_ref = dir.process_ref.clone();
// Reference:
// <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3350>
// <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L174-L175>

View File

@ -1,14 +1,15 @@
// SPDX-License-Identifier: MPL-2.0
use aster_util::slot_vec::SlotVec;
use ostd::sync::RwMutexUpgradeableGuard;
use super::TidDirOps;
use crate::{
fs::{
file_handle::FileLike,
file_table::FileDesc,
inode_handle::InodeHandle,
procfs::{
pid::FdEvents, DirOps, Observer, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps,
},
utils::{chmod, mkmod, DirEntryVecExt, Inode},
procfs::{template::ProcSym, DirOps, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps},
utils::{chmod, mkmod, AccessMode, DirEntryVecExt, Inode},
},
prelude::*,
process::posix_thread::AsPosixThread,
@ -19,115 +20,178 @@ use crate::{
pub struct FdDirOps(Arc<Thread>);
impl FdDirOps {
pub fn new_inode(thread_ref: Arc<Thread>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let posix_thread = thread_ref.as_posix_thread().unwrap();
let file_table = posix_thread.file_table();
let fd_inode = ProcDirBuilder::new(
Self(thread_ref.clone()),
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3317>
mkmod!(u+rx),
)
.parent(parent)
.build()
.unwrap();
// This is for an exiting process that has not yet been reaped by its parent,
// whose file table may have already been released.
if let Some(file_table_ref) = file_table.lock().as_ref() {
file_table_ref
.read()
.register_observer(Arc::downgrade(&fd_inode) as _);
}
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 posix_thread = self.0.as_posix_thread().unwrap();
let file_table = posix_thread.file_table().lock();
let file_table = file_table
.as_ref()
.ok_or_else(|| Error::new(Errno::ENOENT))?;
let file = {
let fd = name
.parse::<FileDesc>()
.map_err(|_| Error::new(Errno::ENOENT))?;
file_table
.read()
.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 posix_thread = self.0.as_posix_thread().unwrap();
let file_table = posix_thread.file_table().lock();
let Some(file_table) = file_table.as_ref() else {
return;
};
let this = {
let this = this_ptr.upgrade().unwrap();
this.downcast_ref::<ProcDir<FdDirOps>>().unwrap().this()
};
let mut cached_children = this.cached_children().write();
for (fd, file) in file_table.read().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]/task/[tid]/fd/[n]` (and also `/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> {
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/fd.c#L127-L141>
let mut mode = mkmod!(a=);
if file.access_mode().is_readable() {
mode = chmod!(mode, u+rx);
}
if file.access_mode().is_writable() {
mode = chmod!(mode, u+wx);
}
ProcSymBuilder::new(Self(file), mode)
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let thread_ref = dir.thread_ref.clone();
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3317>
ProcDirBuilder::new(Self(thread_ref), mkmod!(u+rx))
.parent(parent)
.build()
.unwrap()
}
}
impl DirOps for FdDirOps {
// Lock order: cached entries -> file table
//
// Note that inverting the lock order is non-trivial because the file table is protected by a
// spin lock but the cached entries are protected by a mutex.
fn lookup_child(&self, dir: &ProcDir<Self>, name: &str) -> Result<Arc<dyn Inode>> {
let Ok(file_desc) = name.parse::<FileDesc>() else {
return_errno_with_message!(Errno::ENOENT, "the name is not a valid FD");
};
let mut cached_children = dir.cached_children().write();
let posix_thread = self.0.as_posix_thread().unwrap();
let access_mode = if let Some(file_table) = posix_thread.file_table().lock().as_ref()
&& let Ok(file) = file_table.read().get_file(file_desc)
{
file.access_mode()
} else {
return_errno_with_message!(Errno::ENOENT, "the file does not exist");
};
let child = FileSymOps::new_inode(
self.0.clone(),
file_desc,
access_mode,
dir.this_weak().clone(),
);
// The old entry is likely outdated given that `lookup_child` is called. Race conditions
// may occur, but caching the file descriptor (which aligns with the Linux implementation)
// is inherently racy, so preventing race conditions is not very meaningful.
cached_children.remove_entry_by_name(name);
cached_children.put((String::from(name), child.clone()));
Ok(child)
}
fn populate_children<'a>(
&self,
dir: &'a ProcDir<Self>,
) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc<dyn Inode>)>> {
let mut cached_children = dir.cached_children().write();
let posix_thread = self.0.as_posix_thread().unwrap();
let file_table = posix_thread.file_table().lock();
let Some(file_table) = file_table.as_ref() else {
*cached_children = SlotVec::new();
return cached_children.downgrade();
};
let file_table = file_table.read();
// Remove outdated entries.
for i in 0..cached_children.slots_len() {
let Some((_, child)) = cached_children.get(i) else {
continue;
};
let child = child.downcast_ref::<ProcSym<FileSymOps>>().unwrap();
let Ok(file) = file_table.get_file(child.inner().file_desc) else {
cached_children.remove(i);
continue;
};
if file.access_mode() != child.inner().access_mode {
cached_children.remove(i);
}
// We'll reuse the old entry if the access mode is the same, even if the file is
// different.
}
// Add new entries.
for (file_desc, file) in file_table.fds_and_files() {
cached_children.put_entry_if_not_found(&file_desc.to_string(), || {
FileSymOps::new_inode(
self.0.clone(),
file_desc,
file.access_mode(),
dir.this_weak().clone(),
)
});
}
cached_children.downgrade()
}
fn validate_child(&self, child: &dyn Inode) -> bool {
let ops = child.downcast_ref::<ProcSym<FileSymOps>>().unwrap();
let posix_thread = self.0.as_posix_thread().unwrap();
let is_valid = if let Some(file_table) = posix_thread.file_table().lock().as_ref()
&& let Ok(file) = file_table.read().get_file(ops.inner().file_desc)
{
// We'll reuse the old entry if the access mode is the same, even if the file is
// different.
file.access_mode() == ops.inner().access_mode
} else {
false
};
is_valid
}
}
/// Represents the inode at `/proc/[pid]/task/[tid]/fd/[n]` (and also `/proc/[pid]/fd/[n]`).
struct FileSymOps {
thread_ref: Arc<Thread>,
file_desc: FileDesc,
access_mode: AccessMode,
}
impl FileSymOps {
pub fn new_inode(
thread_ref: Arc<Thread>,
file_desc: FileDesc,
access_mode: AccessMode,
parent: Weak<dyn Inode>,
) -> Arc<dyn Inode> {
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/fd.c#L127-L141>
let mut mode = mkmod!(a=);
if access_mode.is_readable() {
mode = chmod!(mode, u+rx);
}
if access_mode.is_writable() {
mode = chmod!(mode, u+wx);
}
ProcSymBuilder::new(
Self {
thread_ref,
file_desc,
access_mode,
},
mode,
)
.parent(parent)
.build()
.unwrap()
}
}
impl SymOps for FileSymOps {
fn read_link(&self) -> Result<String> {
let path_name = if let Some(inode_handle) = self.0.downcast_ref::<InodeHandle>() {
let posix_thread = self.thread_ref.as_posix_thread().unwrap();
let file = if let Some(file_table) = posix_thread.file_table().lock().as_ref()
&& let Ok(file) = file_table.read().get_file(self.file_desc)
{
file.clone()
} else {
return_errno_with_message!(Errno::ENOENT, "the file does not exist");
};
let path_name = if let Some(inode_handle) = file.downcast_ref::<InodeHandle>() {
inode_handle.path().abs_path()
} else {
// TODO: get the real path for other FileLike object
// TODO: Get the real path of other `FileLike` objects.
String::from("/dev/tty")
};
// FIXME: This may not always be a suitable context to drop a `FileLike` object.
drop(file);
Ok(path_name)
}
}

View File

@ -2,6 +2,7 @@
use alloc::format;
use super::TidDirOps;
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
@ -16,8 +17,9 @@ use crate::{
pub struct GidMapFileOps(Arc<Process>);
impl GidMapFileOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3386>
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let process_ref = dir.process_ref.clone();
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3403>
ProcFileBuilder::new(Self(process_ref), mkmod!(a+r, u+w))
.parent(parent)
.build()

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use super::TidDirOps;
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
@ -13,7 +14,8 @@ use crate::{
pub struct MemFileOps(Arc<Process>);
impl MemFileOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let process_ref = dir.process_ref.clone();
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3347>
ProcFileBuilder::new(Self(process_ref), mkmod!(u+rw))
.parent(parent)

View File

@ -1,9 +1,9 @@
// SPDX-License-Identifier: MPL-2.0
use alloc::format;
use aster_util::slot_vec::SlotVec;
use ostd::sync::RwMutexUpgradeableGuard;
use super::PidDirOps;
use crate::{
fs::{
procfs::{
@ -16,13 +16,16 @@ use crate::{
status::StatusFileOps, uid_map::UidMapFileOps,
},
},
template::{DirOps, ProcDir, ProcDirBuilder},
template::{
lookup_child_from_table, populate_children_from_table, DirOps, ProcDir,
ProcDirBuilder,
},
},
utils::{mkmod, DirEntryVecExt, Inode},
},
prelude::*,
process::posix_thread::AsPosixThread,
thread::{AsThread, Thread},
thread::{AsThread, Thread, Tid},
Process,
};
@ -42,7 +45,8 @@ mod uid_map;
pub struct TaskDirOps(Arc<Process>);
impl TaskDirOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
pub fn new_inode(dir: &PidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let process_ref = dir.0.process_ref.clone();
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3316>
ProcDirBuilder::new(Self(process_ref), mkmod!(a+rx))
.parent(parent)
@ -76,133 +80,120 @@ impl TidDirOps {
.build()
.unwrap()
}
#[expect(clippy::type_complexity)]
const STATIC_ENTRIES: &'static [(
&'static str,
fn(&TidDirOps, Weak<dyn Inode>) -> Arc<dyn Inode>,
)] = &[
("cmdline", CmdlineFileOps::new_inode),
("comm", CommFileOps::new_inode),
("environ", EnvironFileOps::new_inode),
("exe", ExeSymOps::new_inode),
("fd", FdDirOps::new_inode),
("gid_map", GidMapFileOps::new_inode),
("mem", MemFileOps::new_inode),
("mountinfo", MountInfoFileOps::new_inode),
("oom_score_adj", OomScoreAdjFileOps::new_inode),
("stat", StatFileOps::new_inode_tid),
("status", StatusFileOps::new_inode),
("uid_map", UidMapFileOps::new_inode),
];
}
impl DirOps for TidDirOps {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
let inode = match name {
"cmdline" => CmdlineFileOps::new_inode(self.process_ref.clone(), this_ptr),
"comm" => CommFileOps::new_inode(self.process_ref.clone(), this_ptr),
"environ" => EnvironFileOps::new_inode(self.process_ref.clone(), this_ptr),
"exe" => ExeSymOps::new_inode(self.process_ref.clone(), this_ptr),
"fd" => FdDirOps::new_inode(self.thread_ref.clone(), this_ptr),
"gid_map" => GidMapFileOps::new_inode(self.process_ref.clone(), this_ptr),
"mem" => MemFileOps::new_inode(self.process_ref.clone(), this_ptr),
"mountinfo" => MountInfoFileOps::new_inode(self.thread_ref.clone(), this_ptr),
"oom_score_adj" => OomScoreAdjFileOps::new_inode(self.process_ref.clone(), this_ptr),
"stat" => StatFileOps::new_inode(
self.process_ref.clone(),
self.thread_ref.clone(),
false,
this_ptr,
),
"status" => StatusFileOps::new_inode(
self.process_ref.clone(),
self.thread_ref.clone(),
this_ptr,
),
"uid_map" => UidMapFileOps::new_inode(self.process_ref.clone(), this_ptr),
_ => return_errno!(Errno::ENOENT),
};
Ok(inode)
fn lookup_child(&self, dir: &ProcDir<Self>, name: &str) -> Result<Arc<dyn Inode>> {
let mut cached_children = dir.cached_children().write();
self.lookup_child_locked(&mut cached_children, dir.this_weak().clone(), name)
}
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {
let this = {
let this = this_ptr.upgrade().unwrap();
this.downcast_ref::<ProcDir<TidDirOps>>().unwrap().this()
};
let mut cached_children = this.cached_children().write();
self.populate_children_inner(&mut cached_children, this_ptr);
fn populate_children<'a>(
&self,
dir: &'a ProcDir<Self>,
) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc<dyn Inode>)>> {
let mut cached_children = dir.cached_children().write();
self.populate_children_locked(&mut cached_children, dir.this_weak().clone());
cached_children.downgrade()
}
}
impl TidDirOps {
pub(super) fn populate_children_inner(
pub(super) fn lookup_child_locked(
&self,
cached_children: &mut SlotVec<(String, Arc<dyn Inode>)>,
this_ptr: Weak<dyn Inode>,
name: &str,
) -> Result<Arc<dyn Inode>> {
if let Some(child) =
lookup_child_from_table(name, cached_children, Self::STATIC_ENTRIES, |f| {
(f)(self, this_ptr)
})
{
return Ok(child);
}
return_errno_with_message!(Errno::ENOENT, "the file does not exist");
}
pub(super) fn populate_children_locked(
&self,
cached_children: &mut SlotVec<(String, Arc<dyn Inode>)>,
this_ptr: Weak<dyn Inode>,
) {
cached_children.put_entry_if_not_found("cmdline", || {
CmdlineFileOps::new_inode(self.process_ref.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("comm", || {
CommFileOps::new_inode(self.process_ref.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("environ", || {
EnvironFileOps::new_inode(self.process_ref.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("exe", || {
ExeSymOps::new_inode(self.process_ref.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("fd", || {
FdDirOps::new_inode(self.thread_ref.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("gid_map", || {
GidMapFileOps::new_inode(self.process_ref.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("mem", || {
MemFileOps::new_inode(self.process_ref.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("mountinfo", || {
MountInfoFileOps::new_inode(self.thread_ref.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("oom_score_adj", || {
OomScoreAdjFileOps::new_inode(self.process_ref.clone(), this_ptr.clone())
});
cached_children.put_entry_if_not_found("stat", || {
StatFileOps::new_inode(
self.process_ref.clone(),
self.thread_ref.clone(),
false,
this_ptr.clone(),
)
});
cached_children.put_entry_if_not_found("status", || {
StatusFileOps::new_inode(
self.process_ref.clone(),
self.thread_ref.clone(),
this_ptr.clone(),
)
});
cached_children.put_entry_if_not_found("uid_map", || {
UidMapFileOps::new_inode(self.process_ref.clone(), this_ptr.clone())
populate_children_from_table(cached_children, Self::STATIC_ENTRIES, |f| {
(f)(self, this_ptr.clone())
});
}
}
impl DirOps for TaskDirOps {
fn lookup_child(&self, this_ptr: Weak<dyn Inode>, name: &str) -> Result<Arc<dyn Inode>> {
let Ok(tid) = name.parse::<u32>() else {
return_errno_with_message!(Errno::ENOENT, "Can not parse name to u32 type");
fn lookup_child(&self, dir: &ProcDir<Self>, name: &str) -> Result<Arc<dyn Inode>> {
let Ok(tid) = name.parse::<Tid>() else {
return_errno_with_message!(Errno::ENOENT, "the name is not a valid TID");
};
for task in self.0.tasks().lock().as_slice() {
let thread = task.as_thread().unwrap();
if thread.as_posix_thread().unwrap().tid() != tid {
let thread_ref = task.as_thread().unwrap();
if thread_ref.as_posix_thread().unwrap().tid() != tid {
continue;
}
return Ok(TidDirOps::new_inode(
self.0.clone(),
thread.clone(),
this_ptr,
));
let mut cached_children = dir.cached_children().write();
return Ok(cached_children
.put_entry_if_not_found(name, || {
TidDirOps::new_inode(
self.0.clone(),
thread_ref.clone(),
dir.this_weak().clone(),
)
})
.clone());
}
return_errno_with_message!(Errno::ENOENT, "No such thread")
return_errno_with_message!(Errno::ENOENT, "the thread does not exist")
}
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {
let this = {
let this = this_ptr.upgrade().unwrap();
this.downcast_ref::<ProcDir<TaskDirOps>>().unwrap().this()
};
let mut cached_children = this.cached_children().write();
for task in self.0.tasks().lock().as_slice() {
let thread = task.as_thread().unwrap();
cached_children.put_entry_if_not_found(
&format!("{}", task.as_posix_thread().unwrap().tid()),
|| TidDirOps::new_inode(self.0.clone(), thread.clone(), this_ptr.clone()),
fn populate_children<'a>(
&self,
dir: &'a ProcDir<Self>,
) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc<dyn Inode>)>> {
let tasks = self.0.tasks().lock();
let mut cached_dentries = dir.cached_children().write();
for task in tasks.as_slice() {
let thread_ref = task.as_thread().unwrap();
cached_dentries.put_entry_if_not_found(
&task.as_posix_thread().unwrap().tid().to_string(),
|| {
TidDirOps::new_inode(
self.0.clone(),
thread_ref.clone(),
dir.this_weak().clone(),
)
},
);
}
cached_dentries.downgrade()
}
}

View File

@ -1,5 +1,6 @@
// SPDX-License-Identifier: MPL-2.0
use super::TidDirOps;
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
@ -16,7 +17,8 @@ pub struct MountInfoFileOps {
}
impl MountInfoFileOps {
pub fn new_inode(thread_ref: Arc<Thread>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let thread_ref = dir.thread_ref.clone();
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3352>
ProcFileBuilder::new(Self { thread_ref }, mkmod!(a+r))
.parent(parent)

View File

@ -2,6 +2,7 @@
use core::{fmt::Write, sync::atomic::Ordering};
use super::TidDirOps;
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
@ -15,7 +16,8 @@ use crate::{
pub struct OomScoreAdjFileOps(Arc<Process>);
impl OomScoreAdjFileOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let process_ref = dir.process_ref.clone();
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3386>
ProcFileBuilder::new(Self(process_ref), mkmod!(a+r, u+w))
.parent(parent)

View File

@ -2,6 +2,7 @@
use core::fmt::Write;
use super::TidDirOps;
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
@ -65,11 +66,9 @@ pub struct StatusFileOps {
}
impl StatusFileOps {
pub fn new_inode(
process_ref: Arc<Process>,
thread_ref: Arc<Thread>,
parent: Weak<dyn Inode>,
) -> Arc<dyn Inode> {
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let process_ref = dir.process_ref.clone();
let thread_ref = dir.thread_ref.clone();
ProcFileBuilder::new(
Self {
process_ref,

View File

@ -2,6 +2,7 @@
use alloc::format;
use super::TidDirOps;
use crate::{
fs::{
procfs::template::{FileOps, ProcFileBuilder},
@ -16,8 +17,9 @@ use crate::{
pub struct UidMapFileOps(Arc<Process>);
impl UidMapFileOps {
pub fn new_inode(process_ref: Arc<Process>, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3386>
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
let process_ref = dir.process_ref.clone();
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3402>
ProcFileBuilder::new(Self(process_ref), mkmod!(a+r, u+w))
.parent(parent)
.build()

View File

@ -1,13 +1,18 @@
// SPDX-License-Identifier: MPL-2.0
use aster_util::slot_vec::SlotVec;
use ostd::sync::RwMutexUpgradeableGuard;
use crate::{
fs::{
procfs::{
sys::kernel::{cap_last_cap::CapLastCapFileOps, pid_max::PidMaxFileOps},
template::{DirOps, ProcDirBuilder},
template::{
lookup_child_from_table, populate_children_from_table, DirOps, ProcDirBuilder,
},
ProcDir,
},
utils::{mkmod, DirEntryVecExt, Inode},
utils::{mkmod, Inode},
},
prelude::*,
};
@ -28,28 +33,39 @@ impl KernelDirOps {
.build()
.unwrap()
}
#[expect(clippy::type_complexity)]
const STATIC_ENTRIES: &'static [(&'static str, fn(Weak<dyn Inode>) -> Arc<dyn Inode>)] = &[
("cap_last_cap", CapLastCapFileOps::new_inode),
("pid_max", PidMaxFileOps::new_inode),
];
}
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()),
"pid_max" => PidMaxFileOps::new_inode(this_ptr.clone()),
_ => return_errno!(Errno::ENOENT),
};
Ok(inode)
fn lookup_child(&self, dir: &ProcDir<Self>, name: &str) -> Result<Arc<dyn Inode>> {
let mut cached_children = dir.cached_children().write();
if let Some(child) =
lookup_child_from_table(name, &mut cached_children, Self::STATIC_ENTRIES, |f| {
(f)(dir.this_weak().clone())
})
{
return Ok(child);
}
return_errno_with_message!(Errno::ENOENT, "the file does not exist");
}
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())
fn populate_children<'a>(
&self,
dir: &'a ProcDir<Self>,
) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc<dyn Inode>)>> {
let mut cached_children = dir.cached_children().write();
populate_children_from_table(&mut cached_children, Self::STATIC_ENTRIES, |f| {
(f)(dir.this_weak().clone())
});
cached_children
.put_entry_if_not_found("pid_max", || PidMaxFileOps::new_inode(this_ptr.clone()));
cached_children.downgrade()
}
}

View File

@ -1,10 +1,14 @@
// SPDX-License-Identifier: MPL-2.0
use aster_util::slot_vec::SlotVec;
use ostd::sync::RwMutexUpgradeableGuard;
use self::kernel::KernelDirOps;
use super::template::populate_children_from_table;
use crate::{
fs::{
procfs::template::{DirOps, ProcDir, ProcDirBuilder},
utils::{mkmod, DirEntryVecExt, Inode},
procfs::template::{lookup_child_from_table, DirOps, ProcDir, ProcDirBuilder},
utils::{mkmod, Inode},
},
prelude::*,
};
@ -24,24 +28,37 @@ impl SysDirOps {
.build()
.unwrap()
}
#[expect(clippy::type_complexity)]
const STATIC_ENTRIES: &'static [(&'static str, fn(Weak<dyn Inode>) -> Arc<dyn Inode>)] =
&[("kernel", KernelDirOps::new_inode)];
}
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 lookup_child(&self, dir: &ProcDir<Self>, name: &str) -> Result<Arc<dyn Inode>> {
let mut cached_children = dir.cached_children().write();
if let Some(child) =
lookup_child_from_table(name, &mut cached_children, Self::STATIC_ENTRIES, |f| {
(f)(dir.this_weak().clone())
})
{
return Ok(child);
}
return_errno_with_message!(Errno::ENOENT, "the file does not exist");
}
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()))
fn populate_children<'a>(
&self,
dir: &'a ProcDir<Self>,
) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc<dyn Inode>)>> {
let mut cached_children = dir.cached_children().write();
populate_children_from_table(&mut cached_children, Self::STATIC_ENTRIES, |f| {
(f)(dir.this_weak().clone())
});
cached_children.downgrade()
}
}

View File

@ -1,17 +1,19 @@
// SPDX-License-Identifier: MPL-2.0
#![expect(unused_variables)]
use core::time::Duration;
use aster_util::slot_vec::SlotVec;
use inherit_methods_macro::inherit_methods;
use ostd::sync::RwMutexUpgradeableGuard;
use super::{Common, ProcFs};
use crate::{
fs::{
path::{is_dot, is_dotdot},
utils::{DirentVisitor, FileSystem, Inode, InodeMode, InodeType, Metadata, MknodType},
utils::{
DirEntryVecExt, DirentVisitor, FileSystem, Inode, InodeMode, InodeType, Metadata,
MknodType,
},
},
prelude::*,
process::{Gid, Uid},
@ -56,6 +58,10 @@ impl<D: DirOps> ProcDir<D> {
self.this.upgrade().unwrap()
}
pub fn this_weak(&self) -> &Weak<ProcDir<D>> {
&self.this
}
pub fn parent(&self) -> Option<Arc<dyn Inode>> {
self.parent.as_ref().and_then(|p| p.upgrade())
}
@ -96,7 +102,7 @@ impl<D: DirOps + 'static> Inode for ProcDir<D> {
Err(Error::new(Errno::EPERM))
}
fn mknod(&self, _name: &str, _mode: InodeMode, type_: MknodType) -> Result<Arc<dyn Inode>> {
fn mknod(&self, _name: &str, _mode: InodeMode, _type_: MknodType) -> Result<Arc<dyn Inode>> {
Err(Error::new(Errno::EPERM))
}
@ -120,8 +126,7 @@ impl<D: DirOps + 'static> Inode for ProcDir<D> {
}
// Read the normal child entries.
self.inner.populate_children(self.this.clone());
let cached_children = self.cached_children.read();
let cached_children = self.inner.populate_children(self);
let start_offset = *offset;
for (idx, (name, child)) in cached_children
.idxes_and_items()
@ -154,23 +159,22 @@ impl<D: DirOps + 'static> Inode for ProcDir<D> {
}
fn lookup(&self, name: &str) -> Result<Arc<dyn Inode>> {
let inode = if is_dot(name) {
self.this()
} else if is_dotdot(name) {
self.parent().unwrap_or(self.this())
} else {
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)
if is_dot(name) {
return Ok(self.this());
}
if is_dotdot(name) {
return Ok(self.parent().unwrap_or(self.this()));
}
let cached_children = self.cached_children.read();
if let Some(inode) = cached_children.find_entry_by_name(name)
&& self.inner.validate_child(inode.as_ref())
{
return Ok(inode.clone());
}
drop(cached_children);
self.inner.lookup_child(self, name)
}
fn rename(&self, _old_name: &str, _target: &Arc<dyn Inode>, _new_name: &str) -> Result<()> {
@ -182,10 +186,53 @@ impl<D: DirOps + 'static> Inode for ProcDir<D> {
}
}
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))
pub trait DirOps: Sync + Send + Sized {
fn lookup_child(&self, dir: &ProcDir<Self>, name: &str) -> Result<Arc<dyn Inode>>;
fn populate_children<'a>(
&self,
dir: &'a ProcDir<Self>,
) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc<dyn Inode>)>>;
#[must_use]
fn validate_child(&self, _child: &dyn Inode) -> bool {
true
}
}
pub fn lookup_child_from_table<Fp, F>(
name: &str,
cached_children: &mut SlotVec<(String, Arc<dyn Inode>)>,
table: &[(&str, Fp)],
constructor_adaptor: F,
) -> Option<Arc<dyn Inode>>
where
Fp: Copy,
F: FnOnce(Fp) -> Arc<dyn Inode>,
{
for (child_name, child_constructor) in table.iter() {
if *child_name == name {
return Some(
cached_children
.put_entry_if_not_found(name, || (constructor_adaptor)(*child_constructor))
.clone(),
);
}
}
fn populate_children(&self, this_ptr: Weak<dyn Inode>) {}
None
}
pub fn populate_children_from_table<Fp, F>(
cached_children: &mut SlotVec<(String, Arc<dyn Inode>)>,
table: &[(&str, Fp)],
constructor_adaptor: F,
) where
Fp: Copy,
F: Fn(Fp) -> Arc<dyn Inode>,
{
for (child_name, child_constructor) in table.iter() {
cached_children
.put_entry_if_not_found(child_name, || (constructor_adaptor)(*child_constructor));
}
}

View File

@ -4,9 +4,9 @@ use core::time::Duration;
pub use self::{
builder::{ProcDirBuilder, ProcFileBuilder, ProcSymBuilder},
dir::{DirOps, ProcDir},
dir::{lookup_child_from_table, populate_children_from_table, DirOps, ProcDir},
file::FileOps,
sym::SymOps,
sym::{ProcSym, SymOps},
};
use super::{ProcFs, BLOCK_SIZE};
use crate::{

View File

@ -31,6 +31,10 @@ impl<S: SymOps> ProcSym<S> {
};
Arc::new(Self { inner: sym, common })
}
pub fn inner(&self) -> &S {
&self.inner
}
}
#[inherit_methods(from = "self.common")]

View File

@ -5,7 +5,7 @@ use aster_rights::Rights;
use crate::prelude::*;
#[expect(non_camel_case_types)]
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum AccessMode {
/// read only

View File

@ -6,22 +6,47 @@ use super::Inode;
use crate::prelude::*;
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>);
/// Finds the entry by the `name`.
fn find_entry_by_name(&self, name: &str) -> Option<&Arc<dyn Inode>>;
/// Remove and returns the entry by name.
/// Returns `None` if the entry has been removed.
/// Puts the entry given by `f` into the vector if it is not found by the `name`.
fn put_entry_if_not_found(
&mut self,
name: &str,
f: impl FnOnce() -> Arc<dyn Inode>,
) -> &Arc<dyn Inode>;
/// Removes the entry by the `name`.
fn remove_entry_by_name(&mut self, name: &str) -> Option<(String, Arc<dyn Inode>)>;
}
impl DirEntryVecExt for SlotVec<(String, Arc<dyn Inode>)> {
fn put_entry_if_not_found(&mut self, name: &str, f: impl Fn() -> Arc<dyn Inode>) {
if !self.iter().any(|(child_name, _)| child_name == name) {
let inode = f();
self.put((String::from(name), inode));
fn find_entry_by_name(&self, name: &str) -> Option<&Arc<dyn Inode>> {
if let Some((_, inode)) = self.iter().find(|(child_name, _)| child_name == name) {
Some(inode)
} else {
None
}
}
fn put_entry_if_not_found(
&mut self,
name: &str,
f: impl FnOnce() -> Arc<dyn Inode>,
) -> &Arc<dyn Inode> {
let idx = self
.idxes_and_items()
.find(|(_, (child_name, _))| child_name == name)
.map(|(idx, _)| idx);
let idx = if let Some(idx) = idx {
idx
} else {
let inode = f();
self.put((String::from(name), inode))
};
&self.get(idx).unwrap().1
}
fn remove_entry_by_name(&mut self, name: &str) -> Option<(String, Arc<dyn Inode>)> {
let idx = self
.idxes_and_items()