Apply pseudo `Path` to anonymous pipes and remove `AnonPipeFile`

This commit is contained in:
Wang Siyuan 2025-12-26 10:32:50 +00:00 committed by Tate, Hongliang Tian
parent 8eade9b631
commit bbb6a63ee4
6 changed files with 75 additions and 177 deletions

View File

@ -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::<PipeHandle>())
.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<()>;

View File

@ -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<AnonPipeFile>, Arc<AnonPipeFile>)> {
pub fn new_file_pair() -> Result<(Arc<InodeHandle>, Arc<InodeHandle>)> {
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<Box<PipeHandle>>,
pipe_inode: Arc<dyn Inode>,
status_flags: AtomicU32,
}
impl AnonPipeFile {
pub fn open(
pipe_inode: Arc<AnonPipeInode>,
access_mode: AccessMode,
status_flags: StatusFlags,
) -> Result<Self> {
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<usize> {
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<usize> {
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<dyn Inode> {
&self.pipe_inode
}
fn dump_proc_fdinfo(self: Arc<Self>, fd_flags: FdFlags) -> Box<dyn Display> {
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 <https://man7.org/linux/man-pages/man2/pipe.2.html>.
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<dyn FileSystem>;
fn open(
&self,
access_mode: AccessMode,
status_flags: StatusFlags,
) -> Option<Result<Box<dyn FileIo>>> {
Some(self.pipe.open_anon(access_mode, status_flags))
}
}

View File

@ -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<PipeObj>,
access_mode: AccessMode,
}
impl PipeHandle {
pub(super) fn access_mode(&self) -> AccessMode {
self.access_mode
}
fn new(inner: Arc<PipeObj>, access_mode: AccessMode) -> Box<Self> {
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<PipeInner>,
wait_queue: WaitQueue,
}
@ -180,7 +176,7 @@ impl Pipe {
access_mode: AccessMode,
status_flags: StatusFlags,
) -> Result<Box<dyn FileIo>> {
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<Box<PipeHandle>> {
) -> Result<Box<dyn FileIo>> {
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<Box<PipeHandle>> {
) -> Result<Box<dyn FileIo>> {
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 <https://man7.org/linux/man-pages/man2/pipe.2.html>.
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,

View File

@ -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;

View File

@ -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<AnonPipeInode>) -> 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<Mount> {
static PIPEFS_MOUNT: Once<Arc<Mount>> = Once::new();

View File

@ -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<dyn FileLike> = 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::<AnonPipeInode>(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) => {