From bbb6a63ee4e957f4e47d8565a2a8871a20f104bc Mon Sep 17 00:00:00 2001 From: Wang Siyuan Date: Fri, 26 Dec 2025 10:32:50 +0000 Subject: [PATCH] Apply pseudo `Path` to anonymous pipes and remove `AnonPipeFile` --- kernel/src/fs/inode_handle.rs | 15 ++- kernel/src/fs/pipe/anon_pipe.rs | 174 ++++---------------------------- kernel/src/fs/pipe/common.rs | 34 +++++-- kernel/src/fs/pipe/mod.rs | 5 +- kernel/src/fs/pseudofs.rs | 11 +- kernel/src/syscall/open.rs | 13 +-- 6 files changed, 75 insertions(+), 177 deletions(-) diff --git a/kernel/src/fs/inode_handle.rs b/kernel/src/fs/inode_handle.rs index cf8d5d878..199dd9960 100644 --- a/kernel/src/fs/inode_handle.rs +++ b/kernel/src/fs/inode_handle.rs @@ -16,6 +16,7 @@ use crate::{ file_handle::{FileLike, Mappable}, file_table::FdFlags, path::Path, + pipe::PipeHandle, utils::{ AccessMode, CreationFlags, DirentVisitor, FallocMode, FileRange, FlockItem, Inode, InodeType, OFFSET_MAX, RangeLockItem, RangeLockType, SeekFrom, StatusFlags, @@ -364,6 +365,18 @@ impl FileLike for InodeHandle { } fn set_status_flags(&self, new_status_flags: StatusFlags) -> Result<()> { + // TODO: Pipes currently require a special status flag check because + // "packet" mode is not yet supported. Remove this check once "packet" + // mode is implemented. + if self + .file_io + .as_ref() + .and_then(|file_io| (file_io.as_ref() as &dyn Any).downcast_ref::()) + .is_some() + { + crate::fs::pipe::check_status_flags(new_status_flags)?; + } + self.status_flags .store(new_status_flags.bits(), Ordering::Relaxed); @@ -492,7 +505,7 @@ impl Debug for InodeHandle { /// /// This trait is typically implemented for special files like devices or /// named pipes (FIFOs), which have behaviors different from regular on-disk files. -pub trait FileIo: Pollable + InodeIo + Send + Sync + 'static { +pub trait FileIo: Pollable + InodeIo + Any + Send + Sync + 'static { /// Checks whether the `seek()` operation should fail. fn check_seekable(&self) -> Result<()>; diff --git a/kernel/src/fs/pipe/anon_pipe.rs b/kernel/src/fs/pipe/anon_pipe.rs index 0db3a46c9..e0a17bba2 100644 --- a/kernel/src/fs/pipe/anon_pipe.rs +++ b/kernel/src/fs/pipe/anon_pipe.rs @@ -1,179 +1,39 @@ // SPDX-License-Identifier: MPL-2.0 -use core::{ - fmt::Display, - sync::atomic::{AtomicU32, Ordering}, - time::Duration, -}; +use core::time::Duration; use inherit_methods_macro::inherit_methods; use crate::{ - events::IoEvents, fs::{ - file_handle::FileLike, - file_table::FdFlags, - path::RESERVED_MOUNT_ID, - pipe::{Pipe, common::PipeHandle}, + inode_handle::{FileIo, InodeHandle}, + pipe::Pipe, pseudofs::{PipeFs, PseudoInode}, utils::{ - AccessMode, CreationFlags, Extension, FileSystem, Inode, InodeIo, InodeMode, InodeType, - Metadata, StatusFlags, mkmod, + AccessMode, Extension, FileSystem, Inode, InodeIo, InodeMode, InodeType, Metadata, + StatusFlags, mkmod, }, }, prelude::*, - process::{ - Gid, Uid, - signal::{PollHandle, Pollable}, - }, + process::{Gid, Uid}, }; /// Creates a pair of connected pipe file handles with the default capacity. -pub fn new_file_pair() -> Result<(Arc, Arc)> { +pub fn new_file_pair() -> Result<(Arc, Arc)> { let pipe_inode = Arc::new(AnonPipeInode::new()); - let reader = AnonPipeFile::open( - pipe_inode.clone(), + let path = PipeFs::new_path(pipe_inode); + + let reader = InodeHandle::new_unchecked_access( + path.clone(), AccessMode::O_RDONLY, StatusFlags::empty(), )?; - let writer = AnonPipeFile::open(pipe_inode, AccessMode::O_WRONLY, StatusFlags::empty())?; + let writer = + InodeHandle::new_unchecked_access(path, AccessMode::O_WRONLY, StatusFlags::empty())?; Ok((Arc::new(reader), Arc::new(writer))) } -/// An anonymous pipe file. -pub struct AnonPipeFile { - /// The opened pipe handle. `None` if the file is opened as a path. - handle: Option>, - pipe_inode: Arc, - status_flags: AtomicU32, -} - -impl AnonPipeFile { - pub fn open( - pipe_inode: Arc, - access_mode: AccessMode, - status_flags: StatusFlags, - ) -> Result { - check_status_flags(status_flags)?; - - let handle = if !status_flags.contains(StatusFlags::O_PATH) { - let handle = pipe_inode.pipe.open_anon(access_mode, status_flags)?; - Some(handle) - } else { - None - }; - - Ok(Self { - handle, - pipe_inode, - status_flags: AtomicU32::new(status_flags.bits()), - }) - } -} - -impl Pollable for AnonPipeFile { - fn poll(&self, mask: IoEvents, poller: Option<&mut PollHandle>) -> IoEvents { - if let Some(handle) = &self.handle { - handle.poll(mask, poller) - } else { - IoEvents::NVAL - } - } -} - -impl FileLike for AnonPipeFile { - fn read(&self, writer: &mut VmWriter) -> Result { - let Some(handle) = &self.handle else { - return_errno_with_message!(Errno::EBADF, "the file is opened as a path"); - }; - - if !handle.access_mode().is_readable() { - return_errno_with_message!(Errno::EBADF, "the file is not opened readable"); - } - - handle.read_at(0, writer, self.status_flags()) - } - - fn write(&self, reader: &mut VmReader) -> Result { - let Some(handle) = &self.handle else { - return_errno_with_message!(Errno::EBADF, "the file is opened as a path"); - }; - - if !handle.access_mode().is_writable() { - return_errno_with_message!(Errno::EBADF, "the file is not opened writable"); - } - - handle.write_at(0, reader, self.status_flags()) - } - - fn status_flags(&self) -> StatusFlags { - StatusFlags::from_bits_truncate(self.status_flags.load(Ordering::Relaxed)) - } - - fn set_status_flags(&self, new_flags: StatusFlags) -> Result<()> { - check_status_flags(new_flags)?; - - self.status_flags.store(new_flags.bits(), Ordering::Relaxed); - Ok(()) - } - - fn access_mode(&self) -> AccessMode { - if let Some(handle) = &self.handle { - handle.access_mode() - } else { - // The file is opened with `O_PATH`. We follow Linux to report `O_RDONLY` here. - AccessMode::O_RDONLY - } - } - - fn inode(&self) -> &Arc { - &self.pipe_inode - } - - fn dump_proc_fdinfo(self: Arc, fd_flags: FdFlags) -> Box { - let mut flags = self.status_flags().bits() | self.access_mode() as u32; - if fd_flags.contains(FdFlags::CLOEXEC) { - flags |= CreationFlags::O_CLOEXEC.bits(); - } - - Box::new(FdInfo { - flags, - ino: self.inode().ino(), - }) - } -} - -struct FdInfo { - flags: u32, - ino: u64, -} - -impl Display for FdInfo { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - writeln!(f, "pos:\t{}", 0)?; - writeln!(f, "flags:\t0{:o}", self.flags)?; - // TODO: This should be the mount ID of the pseudo filesystem. - writeln!(f, "mnt_id:\t{}", RESERVED_MOUNT_ID)?; - writeln!(f, "ino:\t{}", self.ino) - } -} - -fn check_status_flags(status_flags: StatusFlags) -> Result<()> { - if status_flags.contains(StatusFlags::O_DIRECT) { - // "O_DIRECT .. Older kernels that do not support this flag will indicate this via an - // EINVAL error." - // - // See . - return_errno_with_message!(Errno::EINVAL, "the `O_DIRECT` flag is not supported"); - } - - // TODO: Setting most of the other flags will succeed on Linux, but their effects need to be - // validated. - - Ok(()) -} - /// An anonymous pipe inode. pub struct AnonPipeInode { /// The underlying pipe backend. @@ -233,4 +93,12 @@ impl Inode for AnonPipeInode { fn ctime(&self) -> Duration; fn set_ctime(&self, time: Duration); fn fs(&self) -> Arc; + + fn open( + &self, + access_mode: AccessMode, + status_flags: StatusFlags, + ) -> Option>> { + Some(self.pipe.open_anon(access_mode, status_flags)) + } } diff --git a/kernel/src/fs/pipe/common.rs b/kernel/src/fs/pipe/common.rs index ebf5a6fdb..a2587e2d8 100644 --- a/kernel/src/fs/pipe/common.rs +++ b/kernel/src/fs/pipe/common.rs @@ -29,16 +29,12 @@ use crate::{ /// /// Once a handle for a `Pipe` exists, the corresponding pipe object will /// not be dropped. -pub(super) struct PipeHandle { +pub(in crate::fs) struct PipeHandle { inner: Arc, access_mode: AccessMode, } impl PipeHandle { - pub(super) fn access_mode(&self) -> AccessMode { - self.access_mode - } - fn new(inner: Arc, access_mode: AccessMode) -> Box { Box::new(Self { inner, access_mode }) } @@ -150,7 +146,7 @@ impl FileIo for PipeHandle { /// /// A `Pipe` will maintain exactly one **pipe object** that provides actual pipe /// functionalities when there is at least one handle opened on it. -pub struct Pipe { +pub(in crate::fs) struct Pipe { pipe: Mutex, wait_queue: WaitQueue, } @@ -180,7 +176,7 @@ impl Pipe { access_mode: AccessMode, status_flags: StatusFlags, ) -> Result> { - Ok(self.open_handle(access_mode, status_flags, true)?) + self.open_handle(access_mode, status_flags, true) } /// Opens the anonymous pipe with the specified access mode and status flags. @@ -188,7 +184,7 @@ impl Pipe { &self, access_mode: AccessMode, status_flags: StatusFlags, - ) -> Result> { + ) -> Result> { self.open_handle(access_mode, status_flags, false) } @@ -198,7 +194,9 @@ impl Pipe { access_mode: AccessMode, status_flags: StatusFlags, is_named_pipe: bool, - ) -> Result> { + ) -> Result> { + check_status_flags(status_flags)?; + let mut pipe = self.pipe.lock(); let pipe_obj = pipe.get_or_create_pipe_obj(); @@ -272,6 +270,24 @@ impl Pipe { } } +pub(in crate::fs) fn check_status_flags(status_flags: StatusFlags) -> Result<()> { + if status_flags.contains(StatusFlags::O_DIRECT) { + // TODO: Support "packet" mode for pipes. + // + // The `O_DIRECT` flag indicates that the pipe should operate in "packet" mode. + // "O_DIRECT .. Older kernels that do not support this flag will indicate this via an + // EINVAL error." + // + // See . + return_errno_with_message!(Errno::EINVAL, "the `O_DIRECT` flag is not supported"); + } + + // TODO: Setting most of the other flags will succeed on Linux, but their effects need to be + // validated. + + Ok(()) +} + struct PipeObj { reader: PipeReader, writer: PipeWriter, diff --git a/kernel/src/fs/pipe/mod.rs b/kernel/src/fs/pipe/mod.rs index 6f51d7d46..48cf1be9a 100644 --- a/kernel/src/fs/pipe/mod.rs +++ b/kernel/src/fs/pipe/mod.rs @@ -4,8 +4,9 @@ //! //! This module provides both anonymous and named pipes for inter-process communication. -pub use anon_pipe::{AnonPipeFile, AnonPipeInode, new_file_pair}; -pub use common::Pipe; +pub(super) use anon_pipe::AnonPipeInode; +pub use anon_pipe::new_file_pair; +pub(super) use common::{Pipe, PipeHandle, check_status_flags}; mod anon_pipe; mod common; diff --git a/kernel/src/fs/pseudofs.rs b/kernel/src/fs/pseudofs.rs index af2e4e2cd..1954168d4 100644 --- a/kernel/src/fs/pseudofs.rs +++ b/kernel/src/fs/pseudofs.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: MPL-2.0 +use alloc::format; use core::{ sync::atomic::{AtomicU64, Ordering}, time::Duration, @@ -10,7 +11,8 @@ use spin::Once; use super::utils::{Extension, InodeIo, StatusFlags}; use crate::{ fs::{ - path::Mount, + path::{Mount, Path}, + pipe::AnonPipeInode, registry::{FsProperties, FsType}, utils::{ FileSystem, FsEventSubscriberStats, FsFlags, Inode, InodeMode, InodeType, Metadata, @@ -107,6 +109,13 @@ impl PipeFs { PseudoFs::singleton(&PIPEFS, "pipefs", PIPEFS_MAGIC) } + /// Creates a pseudo `Path` for an anonymous pipe. + pub(super) fn new_path(pipe_inode: Arc) -> Path { + Path::new_pseudo(Self::mount_node().clone(), pipe_inode, |inode| { + format!("pipe:[{}]", inode.ino()) + }) + } + /// Returns the pseudo mount node of the pipe file system. fn mount_node() -> &'static Arc { static PIPEFS_MOUNT: Once> = Once::new(); diff --git a/kernel/src/syscall/open.rs b/kernel/src/syscall/open.rs index fff7e0be7..4d5318970 100644 --- a/kernel/src/syscall/open.rs +++ b/kernel/src/syscall/open.rs @@ -8,7 +8,6 @@ use crate::{ file_table::{FdFlags, FileDesc}, fs_resolver::{AT_FDCWD, FsPath, FsResolver, LookupResult, PathOrInode}, inode_handle::InodeHandle, - pipe::{AnonPipeFile, AnonPipeInode}, utils::{AccessMode, CreationFlags, InodeMode, InodeType, OpenArgs, StatusFlags}, }, prelude::*, @@ -91,16 +90,8 @@ fn do_open( let file_handle: Arc = match lookup_res { LookupResult::Resolved(target) => match target { PathOrInode::Path(path) => Arc::new(path.open(open_args)?), - PathOrInode::Inode(inode) => { - if let Ok(pipe_inode) = Arc::downcast::(inode) { - Arc::new(AnonPipeFile::open( - pipe_inode, - open_args.access_mode, - open_args.status_flags, - )?) - } else { - return_errno_with_message!(Errno::ENXIO, "the inode is not re-openable") - } + PathOrInode::Inode(_) => { + return_errno_with_message!(Errno::ENXIO, "the inode is not re-openable") } }, LookupResult::AtParent(result) => {