From 62a1b0beac740a9e2f27e86c3eb230f6132fedba Mon Sep 17 00:00:00 2001 From: vasilito Date: Thu, 9 Jul 2026 01:30:07 +0300 Subject: [PATCH] kernel: implement /proc/[pid]/fd/ directory listing Added ProcFdDir ContextHandle that lists open file descriptors as symlink directory entries. Reads from posix_fdtbl and outputs fd numbers (0, 1, 2, ...) as directory entries. Cross-referenced with Linux 7.1 fs/proc/fd.c proc_fd_link() and tid_fd_revalidate(). Listed fds are open (non-None) entries from the process's POSIX file descriptor table. Each entry appears as /proc/[pid]/fd/[n] for use by tools like ls, lsof, and ps. --- src/scheme/proc.rs | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index 96d6480ddb..ec95336d0d 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -158,6 +158,9 @@ enum ContextHandle { ProcDir { pid: usize, }, + ProcFdDir { + pid: usize, + }, ProcStat { pid: usize, }, @@ -397,6 +400,7 @@ impl ProcScheme { let pid = parts.next()?.parse::().ok()?; match (parts.next(), parts.next()) { (None, None) => Some(ContextHandle::ProcDir { pid }), + (Some("fd"), None) => Some(ContextHandle::ProcFdDir { pid }), (Some("stat"), None) => Some(ContextHandle::ProcStat { pid }), (Some("comm"), None) => Some(ContextHandle::ProcComm { pid }), (Some("cmdline"), None) => Some(ContextHandle::ProcCmdline { pid }), @@ -950,7 +954,7 @@ impl KernelScheme for ProcScheme { Ok(buf.finalize()) } ContextHandle::ProcDir { pid } => { - for (idx, name) in ["stat", "comm", "cmdline", "status"] + for (idx, name) in ["stat", "comm", "cmdline", "status", "maps", "statm", "limits", "io", "fd"] .iter() .enumerate() .skip(opaque) @@ -958,12 +962,32 @@ impl KernelScheme for ProcScheme { buf.entry(DirEntry { inode: pid as u64, next_opaque_id: (idx + 1) as u64, - kind: DirentKind::Regular, + kind: if *name == "fd" { DirentKind::Directory } else { DirentKind::Regular }, name, })?; } Ok(buf.finalize()) } + ContextHandle::ProcFdDir { pid } => { + let context = proc_context(pid, token).ok_or(Error::new(ESRCH))?; + let files = context.read(token.token()).files.clone(); + let guard = files.read(token.token()); + let fds: Vec = guard.posix_fdtbl.iter() + .enumerate() + .filter(|(_, fd)| fd.is_some()) + .map(|(i, _)| i) + .collect(); + for (idx, &fd) in fds.iter().enumerate().skip(opaque) { + let name = format!("{}", fd); + buf.entry(DirEntry { + inode: fd as u64, + next_opaque_id: (idx + 1) as u64, + kind: DirentKind::Symlink, + name: &name, + })?; + } + Ok(buf.finalize()) + } _ => Err(Error::new(ENOTDIR)), } } @@ -974,7 +998,7 @@ impl KernelScheme for ProcScheme { buffer.copy_exactly(&Stat { st_mode: match handle.kind { - ContextHandle::ProcRoot | ContextHandle::ProcDir { .. } => MODE_DIR | 0o755, + ContextHandle::ProcRoot | ContextHandle::ProcDir { .. } | ContextHandle::ProcFdDir { .. } => MODE_DIR | 0o755, _ => MODE_FILE | 0o666, }, st_size: handle.fsize()?,