Correct lock usages in procfs
This commit is contained in:
parent
4e0142b176
commit
2d01804d19
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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::{
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in New Issue