diff --git a/kernel/src/fs/pipe/anon_pipe.rs b/kernel/src/fs/pipe/anon_pipe.rs index e0a17bba2..e287aa0b0 100644 --- a/kernel/src/fs/pipe/anon_pipe.rs +++ b/kernel/src/fs/pipe/anon_pipe.rs @@ -8,7 +8,7 @@ use crate::{ fs::{ inode_handle::{FileIo, InodeHandle}, pipe::Pipe, - pseudofs::{PipeFs, PseudoInode}, + pseudofs::{PipeFs, PseudoInode, PseudoInodeType}, utils::{ AccessMode, Extension, FileSystem, Inode, InodeIo, InodeMode, InodeType, Metadata, StatusFlags, mkmod, @@ -46,7 +46,7 @@ impl AnonPipeInode { let pipe = Pipe::new(); let pseudo_inode = PipeFs::singleton().alloc_inode( - InodeType::NamedPipe, + PseudoInodeType::Pipe, mkmod!(u+rw), Uid::new_root(), Gid::new_root(), diff --git a/kernel/src/fs/pseudofs.rs b/kernel/src/fs/pseudofs.rs index edbf44941..d34ef12ce 100644 --- a/kernel/src/fs/pseudofs.rs +++ b/kernel/src/fs/pseudofs.rs @@ -71,7 +71,7 @@ impl PseudoFs { sb: SuperBlock::new(magic, aster_block::BLOCK_SIZE, NAME_MAX), root: Arc::new(PseudoInode::new( ROOT_INO, - InodeType::Dir, + PseudoInodeType::Root, mkmod!(u+rw), Uid::new_root(), Gid::new_root(), @@ -85,7 +85,7 @@ impl PseudoFs { pub fn alloc_inode( self: &Arc, - type_: InodeType, + type_: PseudoInodeType, mode: InodeMode, uid: Uid, gid: Gid, @@ -140,7 +140,7 @@ impl SockFs { /// Creates a pseudo `Path` for a socket. pub fn new_path() -> Path { let socket_inode = Arc::new(Self::singleton().alloc_inode( - InodeType::Socket, + PseudoInodeType::Socket, mkmod!(a+rwx), Uid::new_root(), Gid::new_root(), @@ -206,7 +206,7 @@ impl AnonInodeFs { SHARED_INODE.call_once(|| { let shared_inode = Self::singleton().alloc_inode( - InodeType::Unknown, + PseudoInodeType::AnonInode, mkmod!(u+rw), Uid::new_root(), Gid::new_root(), @@ -217,6 +217,51 @@ impl AnonInodeFs { } } +pub struct PidfdFs { + _private: (), +} + +impl PidfdFs { + /// Returns the singleton instance of the pidfd file system. + pub fn singleton() -> &'static Arc { + static PIDFDFS: Once> = Once::new(); + + PseudoFs::singleton(&PIDFDFS, "pidfdfs", PIDFDFS_MAGIC) + } + + /// Creates a pseudo `Path` for a pidfd. + pub fn new_path(name_fn: fn(&dyn Inode) -> String) -> Path { + Path::new_pseudo( + Self::mount_node().clone(), + Self::shared_inode().clone(), + name_fn, + ) + } + + /// Returns the pseudo mount node of the pidfd file system. + pub fn mount_node() -> &'static Arc { + static PIDFDFS_MOUNT: Once> = Once::new(); + + PIDFDFS_MOUNT.call_once(|| Mount::new_pseudo(Self::singleton().clone())) + } + + /// Returns the shared inode of the pidfd file system. + pub fn shared_inode() -> &'static Arc { + static SHARED_INODE: Once> = Once::new(); + + SHARED_INODE.call_once(|| { + let pidfd_inode = Self::singleton().alloc_inode( + PseudoInodeType::Pidfd, + mkmod!(u+rwx), + Uid::new_root(), + Gid::new_root(), + ); + + Arc::new(pidfd_inode) + }) + } +} + pub(super) fn init() { super::registry::register(&PipeFsType).unwrap(); super::registry::register(&SockFsType).unwrap(); @@ -282,24 +327,50 @@ const PIPEFS_MAGIC: u64 = 0x50495045; const SOCKFS_MAGIC: u64 = 0x534F434B; // Reference: const ANON_INODEFS_MAGIC: u64 = 0x09041934; +// Reference: +const PIDFDFS_MAGIC: u64 = 0x50494446; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PseudoInodeType { + Root, + Pipe, + Socket, + AnonInode, + Pidfd, +} + +impl From for InodeType { + fn from(pseudo_type: PseudoInodeType) -> Self { + match pseudo_type { + PseudoInodeType::Root => InodeType::Dir, + PseudoInodeType::Pipe => InodeType::NamedPipe, + PseudoInodeType::Socket => InodeType::Socket, + PseudoInodeType::AnonInode => InodeType::Unknown, + PseudoInodeType::Pidfd => InodeType::Unknown, + } + } +} /// A pseudo inode that does not correspond to any real path in the file system. pub struct PseudoInode { metadata: SpinLock, extension: Extension, fs: Weak, + is_anon: bool, } impl PseudoInode { fn new( ino: u64, - type_: InodeType, + type_: PseudoInodeType, mode: InodeMode, uid: Uid, gid: Gid, fs: Weak, ) -> Self { let now = now(); + let type_ = InodeType::from(type_); + let metadata = Metadata { dev: 0, ino, @@ -316,10 +387,12 @@ impl PseudoInode { gid, rdev: 0, }; + PseudoInode { metadata: SpinLock::new(metadata), extension: Extension::new(), fs, + is_anon: type_ == InodeType::Unknown, } } } @@ -380,6 +453,13 @@ impl Inode for PseudoInode { } fn set_mode(&self, mode: InodeMode) -> Result<()> { + if self.is_anon { + return_errno_with_message!( + Errno::EOPNOTSUPP, + "the mode of anonymous inodes cannot be changed" + ); + } + let mut meta = self.metadata.lock(); meta.mode = mode; meta.ctime = now(); diff --git a/kernel/src/process/pid_file.rs b/kernel/src/process/pid_file.rs index d9611291e..ea12486e7 100644 --- a/kernel/src/process/pid_file.rs +++ b/kernel/src/process/pid_file.rs @@ -11,7 +11,7 @@ use crate::{ file_handle::FileLike, file_table::FdFlags, path::Path, - pseudofs::AnonInodeFs, + pseudofs::PidfdFs, utils::{CreationFlags, StatusFlags}, }, prelude::*, @@ -43,7 +43,7 @@ impl Debug for PidFile { impl PidFile { pub fn new(process: Arc, is_nonblocking: bool) -> Self { - let pseudo_path = AnonInodeFs::new_path(|_| "anon_inode:[pidfd]".to_string()); + let pseudo_path = PidfdFs::new_path(|_| "anon_inode:[pidfd]".to_string()); Self { process: Arc::downgrade(&process), @@ -59,7 +59,7 @@ impl PidFile { // Reference: . let Some(process) = self.process.upgrade() else { // The process has been reaped. - return IoEvents::IN; + return IoEvents::IN | IoEvents::HUP; }; if process.status().is_zombie() { IoEvents::IN @@ -86,6 +86,20 @@ impl FileLike for PidFile { return_errno_with_message!(Errno::EINVAL, "PID file cannot be written"); } + fn read_at(&self, _offset: usize, _writer: &mut VmWriter) -> Result { + return_errno_with_message!( + Errno::EINVAL, + "PID file cannot be read at a specific offset" + ); + } + + fn write_at(&self, _offset: usize, _reader: &mut VmReader) -> Result { + return_errno_with_message!( + Errno::EINVAL, + "PID file cannot be written at a specific offset" + ); + } + fn set_status_flags(&self, new_flags: StatusFlags) -> Result<()> { if new_flags.contains(StatusFlags::O_NONBLOCK) { self.is_nonblocking.store(true, Ordering::Relaxed); @@ -118,8 +132,8 @@ impl FileLike for PidFile { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { writeln!(f, "pos:\t{}", 0)?; writeln!(f, "flags:\t0{:o}", self.flags)?; - writeln!(f, "mnt_id:\t{}", AnonInodeFs::mount_node().id())?; - writeln!(f, "ino:\t{}", AnonInodeFs::shared_inode().ino())?; + writeln!(f, "mnt_id:\t{}", PidfdFs::mount_node().id())?; + writeln!(f, "ino:\t{}", PidfdFs::shared_inode().ino())?; writeln!(f, "Pid:\t{}", self.pid)?; // TODO: Currently we do not support PID namespaces. Just print the PID once. writeln!(f, "NSpid:\t{}", self.pid) @@ -140,7 +154,7 @@ impl Pollable for PidFile { fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents { let Some(process) = self.process.upgrade() else { // The process has been reaped. - return mask & IoEvents::IN; + return mask & (IoEvents::IN | IoEvents::HUP); }; process .pidfile_pollee diff --git a/kernel/src/process/process/mod.rs b/kernel/src/process/process/mod.rs index 69ab1c6d8..8c61b23bf 100644 --- a/kernel/src/process/process/mod.rs +++ b/kernel/src/process/process/mod.rs @@ -20,6 +20,7 @@ use super::{ task_set::TaskSet, }; use crate::{ + events::IoEvents, fs::cgroupfs::CgroupNode, prelude::*, process::{ @@ -151,6 +152,12 @@ pub struct Process { user_ns: Mutex>, } +impl Drop for Process { + fn drop(&mut self) { + self.pidfile_pollee.notify(IoEvents::HUP); + } +} + /// Representing a parent process by holding a weak reference to it and its PID. /// /// This type caches the value of the PID so that it can be retrieved cheaply. diff --git a/test/initramfs/src/apps/process/pidfd.c b/test/initramfs/src/apps/process/pidfd.c index 0d1641c3e..0ac48e89f 100644 --- a/test/initramfs/src/apps/process/pidfd.c +++ b/test/initramfs/src/apps/process/pidfd.c @@ -36,9 +36,9 @@ FN_TEST(read_write) { char buf[1] = {}; TEST_ERRNO(read(pid_fd, buf, 1), EINVAL); - TEST_ERRNO(pread(pid_fd, buf, 1, 0), ESPIPE); + TEST_ERRNO(pread(pid_fd, buf, 1, 0), EINVAL); TEST_ERRNO(write(pid_fd, "a", 1), EINVAL); - TEST_ERRNO(pwrite(pid_fd, "b", 1, 0), ESPIPE); + TEST_ERRNO(pwrite(pid_fd, "b", 1, 0), EINVAL); } END_TEST() @@ -55,7 +55,7 @@ FN_TEST(file_stat) { struct stat file_info; TEST_RES(fstat(pid_fd, &file_info), - file_info.st_mode == 0600 && file_info.st_size == 0 && + file_info.st_mode == 0700 && file_info.st_size == 0 && file_info.st_blksize == 4096); } END_TEST() @@ -78,9 +78,10 @@ END_TEST() FN_TEST(wait) { #define P_PIDFD 3 - TEST_SUCC(waitid(P_PIDFD, pid_fd, NULL, WNOHANG | WEXITED)); - pfd.revents = 0; + TEST_SUCC(waitid(P_PIDFD, pid_fd, NULL, WEXITED | WNOWAIT)); TEST_RES(poll(&pfd, 1, 0), pfd.revents == POLLIN); + TEST_SUCC(waitid(P_PIDFD, pid_fd, NULL, WEXITED)); + TEST_RES(poll(&pfd, 1, 0), pfd.revents == (POLLIN | POLLHUP)); TEST_ERRNO(waitid(P_PIDFD, pid_fd, NULL, WNOHANG | WEXITED), ECHILD); TEST_ERRNO(waitid(P_PIDFD, 100, NULL, WNOHANG | WEXITED), EBADF); diff --git a/test/initramfs/src/apps/pseudofs/pseudo_inode.c b/test/initramfs/src/apps/pseudofs/pseudo_inode.c index d6a99b55a..2a2886bd2 100644 --- a/test/initramfs/src/apps/pseudofs/pseudo_inode.c +++ b/test/initramfs/src/apps/pseudofs/pseudo_inode.c @@ -54,19 +54,17 @@ FN_TEST(anon_inodefs_share_inode) { struct fd_mode { int fd; - mode_t modes[2]; + mode_t mode; }; struct fd_mode fds[] = { - { epoll_fd, { 0600, 0000 } }, { event_fd, { 0000, 0111 } }, - { timer_fd, { 0111, 0222 } }, { signal_fd, { 0222, 0333 } }, - { inotify_fd, { 0333, 0444 } }, { pid_fd, { 0444, 0600 } }, + { epoll_fd, 0600 }, { event_fd, 0600 }, { timer_fd, 0600 }, + { signal_fd, 0600 }, { inotify_fd, 0600 }, { pid_fd, 0700 }, }; for (size_t i = 0; i < sizeof(fds) / sizeof(fds[0]); i++) { - TEST_RES(get_mode(fds[i].fd), _ret == fds[i].modes[0]); - TEST_SUCC(set_mode(fds[i].fd, fds[i].modes[1])); - TEST_RES(get_mode(fds[i].fd), _ret == fds[i].modes[1]); + TEST_RES(get_mode(fds[i].fd), _ret == fds[i].mode); + TEST_ERRNO(set_mode(fds[i].fd, 0600), EOPNOTSUPP); } } END_TEST() diff --git a/test/initramfs/src/apps/pseudofs/pseudo_mount.c b/test/initramfs/src/apps/pseudofs/pseudo_mount.c index 45580c5da..ae139af1c 100644 --- a/test/initramfs/src/apps/pseudofs/pseudo_mount.c +++ b/test/initramfs/src/apps/pseudofs/pseudo_mount.c @@ -24,8 +24,7 @@ static int read_fdinfo_mnt_id(int fd) FN_TEST(pseudo_mount) { - int anon[] = { epoll_fd, event_fd, timer_fd, - signal_fd, inotify_fd, pid_fd }; + int anon[] = { epoll_fd, event_fd, timer_fd, signal_fd, inotify_fd }; struct fd_group { int *fds; @@ -34,13 +33,11 @@ FN_TEST(pseudo_mount) }; struct fd_group groups[] = { - { pipe_1, 2, -1 }, - { sock, 2, -1 }, - { anon, 6, -1 }, - { &mem_fd, 1, -1 }, + { pipe_1, 2, -1 }, { sock, 2, -1 }, { anon, 5, -1 }, + { &mem_fd, 1, -1 }, { &pid_fd, 1, -1 }, }; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 5; i++) { int base = TEST_SUCC(read_fdinfo_mnt_id(groups[i].fds[0])); for (int j = 1; j < groups[i].nr; j++) { TEST_RES(read_fdinfo_mnt_id(groups[i].fds[j]), @@ -49,8 +46,8 @@ FN_TEST(pseudo_mount) groups[i].mnt_id = base; } - for (int i = 0; i < 4; i++) { - for (int j = i + 1; j < 4; j++) { + for (int i = 0; i < 5; i++) { + for (int j = i + 1; j < 5; j++) { TEST_RES(0, groups[i].mnt_id != groups[j].mnt_id); } }