From 61a5bb7241c39e411f394ab9e52f9ac3bbaaf520 Mon Sep 17 00:00:00 2001 From: Chen Chengjun Date: Wed, 19 Nov 2025 07:20:11 +0000 Subject: [PATCH] Add fdinfo files to proc filesystem --- kernel/src/fs/file_handle.rs | 20 ++- kernel/src/fs/procfs/pid/task/fd.rs | 167 +++++++++++++++++++++----- kernel/src/fs/procfs/pid/task/mod.rs | 3 +- kernel/src/fs/procfs/template/file.rs | 4 + kernel/src/fs/procfs/template/mod.rs | 2 +- 5 files changed, 163 insertions(+), 33 deletions(-) diff --git a/kernel/src/fs/file_handle.rs b/kernel/src/fs/file_handle.rs index ef33f505a..9182c381a 100644 --- a/kernel/src/fs/file_handle.rs +++ b/kernel/src/fs/file_handle.rs @@ -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; + + /// 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` should be dropped and + /// should not appear in the returned `Box`. + /// - Otherwise, if the file can be dropped asynchronously in another process, the method can + /// return a `Box` containing the `Arc`, 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, fd_flags: FdFlags) -> Box; } impl dyn FileLike { diff --git a/kernel/src/fs/procfs/pid/task/fd.rs b/kernel/src/fs/procfs/pid/task/fd.rs index 29c8c03cb..a69c73743 100644 --- a/kernel/src/fs/procfs/pid/task/fd.rs +++ b/kernel/src/fs/procfs/pid/task/fd.rs @@ -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 { + dir: TidDirOps, + marker: PhantomData, +} -impl FdDirOps { +impl FdDirOps { pub fn new_inode(dir: &TidDirOps, parent: Weak) -> Arc { - // Reference: - ProcDirBuilder::new(Self(dir.clone()), mkmod!(u+rx)) - .parent(parent) - .build() - .unwrap() + ProcDirBuilder::new( + Self { + dir: dir.clone(), + marker: PhantomData, + }, + // Reference: + mkmod!(u+rx), + ) + .parent(parent) + .build() + .unwrap() } } -impl DirOps for FdDirOps { +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 @@ -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)>> { 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::>().unwrap(); + let child = child.downcast_ref::().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::>().unwrap(); + let child = child.downcast_ref::().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, + ) -> Arc; + + fn file_desc(&self) -> FileDesc; + + fn is_valid(&self, correspond_file: &Arc) -> 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; + + 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) -> 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; + + fn new_inode( + tid_dir_ops: TidDirOps, + file_desc: FileDesc, + _access_mode: AccessMode, + parent: Weak, + ) -> Arc { + ProcFileBuilder::new( + Self { + tid_dir_ops, + file_desc, + }, + // Reference: . + mkmod!(a+r), + ) + .parent(parent) + .build() + .unwrap() + } + + fn file_desc(&self) -> FileDesc { + self.file_desc + } + + fn is_valid(&self, _correspond_file: &Arc) -> 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 { + 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()) + } +} diff --git a/kernel/src/fs/procfs/pid/task/mod.rs b/kernel/src/fs/procfs/pid/task/mod.rs index 683e7e405..ccc74dbe4 100644 --- a/kernel/src/fs/procfs/pid/task/mod.rs +++ b/kernel/src/fs/procfs/pid/task/mod.rs @@ -103,7 +103,8 @@ impl TidDirOps { ("comm", CommFileOps::new_inode), ("environ", EnvironFileOps::new_inode), ("exe", ExeSymOps::new_inode), - ("fd", FdDirOps::new_inode), + ("fd", FdDirOps::::new_inode), + ("fdinfo", FdDirOps::::new_inode), ("gid_map", GidMapFileOps::new_inode), ("mem", MemFileOps::new_inode), ("mountinfo", MountInfoFileOps::new_inode), diff --git a/kernel/src/fs/procfs/template/file.rs b/kernel/src/fs/procfs/template/file.rs index f0e3571ef..a750b698c 100644 --- a/kernel/src/fs/procfs/template/file.rs +++ b/kernel/src/fs/procfs/template/file.rs @@ -36,6 +36,10 @@ impl ProcFile { common, }) } + + pub fn inner(&self) -> &F { + &self.inner + } } impl InodeIo for ProcFile { diff --git a/kernel/src/fs/procfs/template/mod.rs b/kernel/src/fs/procfs/template/mod.rs index 6d8b3ec96..752fcb436 100644 --- a/kernel/src/fs/procfs/template/mod.rs +++ b/kernel/src/fs/procfs/template/mod.rs @@ -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};