Apply pseudo `Path` to anonymous pipes and remove `AnonPipeFile`
This commit is contained in:
parent
8eade9b631
commit
bbb6a63ee4
|
|
@ -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<()>;
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue