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.
This commit is contained in:
2026-07-09 01:30:07 +03:00
parent 2cc512a3b9
commit 62a1b0beac
+27 -3
View File
@@ -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::<usize>().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<usize> = 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()?,