Add fdinfo files to proc filesystem
This commit is contained in:
parent
668876aeee
commit
61a5bb7241
|
|
@ -4,11 +4,16 @@
|
|||
|
||||
//! Opened File Handle
|
||||
|
||||
use core::fmt::Display;
|
||||
|
||||
use ostd::io::IoMem;
|
||||
|
||||
use super::inode_handle::InodeHandle;
|
||||
use crate::{
|
||||
fs::utils::{AccessMode, FallocMode, Inode, IoctlCmd, SeekFrom, StatusFlags},
|
||||
fs::{
|
||||
file_table::FdFlags,
|
||||
utils::{AccessMode, FallocMode, Inode, IoctlCmd, SeekFrom, StatusFlags},
|
||||
},
|
||||
net::socket::Socket,
|
||||
prelude::*,
|
||||
process::signal::Pollable,
|
||||
|
|
@ -92,6 +97,19 @@ pub trait FileLike: Pollable + Send + Sync + Any {
|
|||
}
|
||||
|
||||
fn inode(&self) -> &Arc<dyn Inode>;
|
||||
|
||||
/// Dumps information to appear in the `fdinfo` file under procfs.
|
||||
///
|
||||
/// This method must not break atomic mode because it will be called with the file table's spin
|
||||
/// lock held. There are two strategies for implementing this method:
|
||||
/// - If the necessary information can be obtained without breaking atomic mode, the method
|
||||
/// can collect and return the information directly. `Arc<Self>` should be dropped and
|
||||
/// should not appear in the returned `Box<dyn Display>`.
|
||||
/// - Otherwise, if the file can be dropped asynchronously in another process, the method can
|
||||
/// return a `Box<dyn Display>` containing the `Arc<Self>`, so that the information can be
|
||||
/// collected later in its `Display::display()` method, after dropping the file table's spin
|
||||
/// lock.
|
||||
fn dump_proc_fdinfo(self: Arc<Self>, fd_flags: FdFlags) -> Box<dyn Display>;
|
||||
}
|
||||
|
||||
impl dyn FileLike {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use aster_util::slot_vec::SlotVec;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use aster_util::{printer::VmPrinter, slot_vec::SlotVec};
|
||||
use ostd::sync::RwMutexUpgradeableGuard;
|
||||
|
||||
use super::TidDirOps;
|
||||
use crate::{
|
||||
fs::{
|
||||
file_handle::FileLike,
|
||||
file_table::FileDesc,
|
||||
inode_handle::InodeHandle,
|
||||
procfs::{template::ProcSym, DirOps, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps},
|
||||
procfs::{
|
||||
template::{FileOps, ProcFile, ProcFileBuilder, ProcSym},
|
||||
DirOps, ProcDir, ProcDirBuilder, ProcSymBuilder, SymOps,
|
||||
},
|
||||
utils::{chmod, mkmod, AccessMode, DirEntryVecExt, Inode, SymbolicLink},
|
||||
},
|
||||
prelude::*,
|
||||
|
|
@ -16,19 +22,28 @@ use crate::{
|
|||
};
|
||||
|
||||
/// Represents the inode at `/proc/[pid]/task/[tid]/fd` (and also `/proc/[pid]/fd`).
|
||||
pub struct FdDirOps(TidDirOps);
|
||||
pub(super) struct FdDirOps<T> {
|
||||
dir: TidDirOps,
|
||||
marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl FdDirOps {
|
||||
impl<T: FdOps> FdDirOps<T> {
|
||||
pub fn new_inode(dir: &TidDirOps, parent: Weak<dyn Inode>) -> Arc<dyn Inode> {
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3317>
|
||||
ProcDirBuilder::new(Self(dir.clone()), mkmod!(u+rx))
|
||||
.parent(parent)
|
||||
.build()
|
||||
.unwrap()
|
||||
ProcDirBuilder::new(
|
||||
Self {
|
||||
dir: dir.clone(),
|
||||
marker: PhantomData,
|
||||
},
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/base.c#L3317>
|
||||
mkmod!(u+rx),
|
||||
)
|
||||
.parent(parent)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl DirOps for FdDirOps {
|
||||
impl<T: FdOps> DirOps for FdDirOps<T> {
|
||||
// Lock order: cached entries -> file table
|
||||
//
|
||||
// Note that inverting the lock order is non-trivial because the file table is protected by a
|
||||
|
|
@ -41,7 +56,7 @@ impl DirOps for FdDirOps {
|
|||
|
||||
let mut cached_children = dir.cached_children().write();
|
||||
|
||||
let thread = self.0.thread();
|
||||
let thread = self.dir.thread();
|
||||
let posix_thread = thread.as_posix_thread().unwrap();
|
||||
|
||||
let access_mode = if let Some(file_table) = posix_thread.file_table().lock().as_ref()
|
||||
|
|
@ -52,8 +67,8 @@ impl DirOps for FdDirOps {
|
|||
return_errno_with_message!(Errno::ENOENT, "the file does not exist");
|
||||
};
|
||||
|
||||
let child = FileSymOps::new_inode(
|
||||
self.0.clone(),
|
||||
let child = T::new_inode(
|
||||
self.dir.clone(),
|
||||
file_desc,
|
||||
access_mode,
|
||||
dir.this_weak().clone(),
|
||||
|
|
@ -73,7 +88,7 @@ impl DirOps for FdDirOps {
|
|||
) -> RwMutexUpgradeableGuard<'a, SlotVec<(String, Arc<dyn Inode>)>> {
|
||||
let mut cached_children = dir.cached_children().write();
|
||||
|
||||
let thread = self.0.thread();
|
||||
let thread = self.dir.thread();
|
||||
let posix_thread = thread.as_posix_thread().unwrap();
|
||||
|
||||
let file_table = posix_thread.file_table().lock();
|
||||
|
|
@ -89,24 +104,23 @@ impl DirOps for FdDirOps {
|
|||
let Some((_, child)) = cached_children.get(i) else {
|
||||
continue;
|
||||
};
|
||||
let child = child.downcast_ref::<ProcSym<FileSymOps>>().unwrap();
|
||||
let child = child.downcast_ref::<T::NodeType>().unwrap();
|
||||
let child_ops = T::ref_from_inode(child);
|
||||
|
||||
let Ok(file) = file_table.get_file(child.inner().file_desc) else {
|
||||
let Ok(file) = file_table.get_file(child_ops.file_desc()) else {
|
||||
cached_children.remove(i);
|
||||
continue;
|
||||
};
|
||||
if file.access_mode() != child.inner().access_mode {
|
||||
if !child_ops.is_valid(file) {
|
||||
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(),
|
||||
T::new_inode(
|
||||
self.dir.clone(),
|
||||
file_desc,
|
||||
file.access_mode(),
|
||||
dir.this_weak().clone(),
|
||||
|
|
@ -118,17 +132,16 @@ impl DirOps for FdDirOps {
|
|||
}
|
||||
|
||||
fn validate_child(&self, child: &dyn Inode) -> bool {
|
||||
let ops = child.downcast_ref::<ProcSym<FileSymOps>>().unwrap();
|
||||
let child = child.downcast_ref::<T::NodeType>().unwrap();
|
||||
let child_ops = T::ref_from_inode(child);
|
||||
|
||||
let thread = self.0.thread();
|
||||
let thread = self.dir.thread();
|
||||
let posix_thread = thread.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)
|
||||
&& let Ok(file) = file_table.read().get_file(child_ops.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
|
||||
child_ops.is_valid(file)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
|
@ -137,15 +150,34 @@ impl DirOps for FdDirOps {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) trait FdOps: Send + Sync + 'static {
|
||||
type NodeType: Inode;
|
||||
|
||||
fn new_inode(
|
||||
tid_dir_ops: TidDirOps,
|
||||
file_desc: FileDesc,
|
||||
access_mode: AccessMode,
|
||||
parent: Weak<dyn Inode>,
|
||||
) -> Arc<dyn Inode>;
|
||||
|
||||
fn file_desc(&self) -> FileDesc;
|
||||
|
||||
fn is_valid(&self, correspond_file: &Arc<dyn FileLike>) -> bool;
|
||||
|
||||
fn ref_from_inode(inode: &Self::NodeType) -> &Self;
|
||||
}
|
||||
|
||||
/// Represents the inode at `/proc/[pid]/task/[tid]/fd/[n]` (and also `/proc/[pid]/fd/[n]`).
|
||||
struct FileSymOps {
|
||||
pub(super) struct FileSymOps {
|
||||
tid_dir_ops: TidDirOps,
|
||||
file_desc: FileDesc,
|
||||
access_mode: AccessMode,
|
||||
}
|
||||
|
||||
impl FileSymOps {
|
||||
pub fn new_inode(
|
||||
impl FdOps for FileSymOps {
|
||||
type NodeType = ProcSym<FileSymOps>;
|
||||
|
||||
fn new_inode(
|
||||
tid_dir_ops: TidDirOps,
|
||||
file_desc: FileDesc,
|
||||
access_mode: AccessMode,
|
||||
|
|
@ -172,6 +204,20 @@ impl FileSymOps {
|
|||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn file_desc(&self) -> FileDesc {
|
||||
self.file_desc
|
||||
}
|
||||
|
||||
fn is_valid(&self, correspond_file: &Arc<dyn FileLike>) -> bool {
|
||||
// We'll treat the old entry as valid and reuse it if the access mode is the same,
|
||||
// even if the file is different.
|
||||
self.access_mode == correspond_file.access_mode()
|
||||
}
|
||||
|
||||
fn ref_from_inode(inode: &Self::NodeType) -> &Self {
|
||||
inode.inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl SymOps for FileSymOps {
|
||||
|
|
@ -197,3 +243,64 @@ impl SymOps for FileSymOps {
|
|||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents the inode at `/proc/[pid]/task/[tid]/fdinfo/[n]` (and also `/proc/[pid]/fdinfo/[n]`).
|
||||
pub(super) struct FileInfoOps {
|
||||
tid_dir_ops: TidDirOps,
|
||||
file_desc: FileDesc,
|
||||
}
|
||||
|
||||
impl FdOps for FileInfoOps {
|
||||
type NodeType = ProcFile<FileInfoOps>;
|
||||
|
||||
fn new_inode(
|
||||
tid_dir_ops: TidDirOps,
|
||||
file_desc: FileDesc,
|
||||
_access_mode: AccessMode,
|
||||
parent: Weak<dyn Inode>,
|
||||
) -> Arc<dyn Inode> {
|
||||
ProcFileBuilder::new(
|
||||
Self {
|
||||
tid_dir_ops,
|
||||
file_desc,
|
||||
},
|
||||
// Reference: <https://elixir.bootlin.com/linux/v6.16.5/source/fs/proc/fd.c#L383>.
|
||||
mkmod!(a+r),
|
||||
)
|
||||
.parent(parent)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn file_desc(&self) -> FileDesc {
|
||||
self.file_desc
|
||||
}
|
||||
|
||||
fn is_valid(&self, _correspond_file: &Arc<dyn FileLike>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ref_from_inode(inode: &Self::NodeType) -> &Self {
|
||||
inode.inner()
|
||||
}
|
||||
}
|
||||
|
||||
impl FileOps for FileInfoOps {
|
||||
fn read_at(&self, offset: usize, writer: &mut VmWriter) -> Result<usize> {
|
||||
let thread = self.tid_dir_ops.thread();
|
||||
let posix_thread = thread.as_posix_thread().unwrap();
|
||||
|
||||
let info = if let Some(file_table) = posix_thread.file_table().lock().as_ref()
|
||||
&& let Ok(entry) = file_table.read().get_entry(self.file_desc)
|
||||
{
|
||||
entry.file().clone().dump_proc_fdinfo(entry.flags())
|
||||
} else {
|
||||
return_errno_with_message!(Errno::ENOENT, "the file does not exist");
|
||||
};
|
||||
|
||||
let mut printer = VmPrinter::new_skip(writer, offset);
|
||||
write!(printer, "{}", info)?;
|
||||
|
||||
Ok(printer.bytes_written())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,7 +103,8 @@ impl TidDirOps {
|
|||
("comm", CommFileOps::new_inode),
|
||||
("environ", EnvironFileOps::new_inode),
|
||||
("exe", ExeSymOps::new_inode),
|
||||
("fd", FdDirOps::new_inode),
|
||||
("fd", FdDirOps::<fd::FileSymOps>::new_inode),
|
||||
("fdinfo", FdDirOps::<fd::FileInfoOps>::new_inode),
|
||||
("gid_map", GidMapFileOps::new_inode),
|
||||
("mem", MemFileOps::new_inode),
|
||||
("mountinfo", MountInfoFileOps::new_inode),
|
||||
|
|
|
|||
|
|
@ -36,6 +36,10 @@ impl<F: FileOps> ProcFile<F> {
|
|||
common,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn inner(&self) -> &F {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FileOps + 'static> InodeIo for ProcFile<F> {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use core::time::Duration;
|
|||
pub(super) use self::{
|
||||
builder::{ProcDirBuilder, ProcFileBuilder, ProcSymBuilder},
|
||||
dir::{lookup_child_from_table, populate_children_from_table, DirOps, ProcDir},
|
||||
file::FileOps,
|
||||
file::{FileOps, ProcFile},
|
||||
sym::{ProcSym, SymOps},
|
||||
};
|
||||
use super::{ProcFs, BLOCK_SIZE};
|
||||
|
|
|
|||
Loading…
Reference in New Issue