From 077a9ac3ab1cd3d21981a2e1ad91e3c62391131a Mon Sep 17 00:00:00 2001 From: Qingsong Chen Date: Fri, 26 Jul 2024 03:03:04 +0000 Subject: [PATCH] Add fcntl subcommand F_GETOWN/F_SETOWN --- kernel/aster-nix/src/fs/file_table.rs | 85 ++++++++++++++++++- .../src/net/socket/unix/stream/connected.rs | 17 +++- .../src/net/socket/unix/stream/endpoint.rs | 34 +++++++- .../src/net/socket/unix/stream/init.rs | 18 +++- .../src/net/socket/unix/stream/listener.rs | 37 +++++++- .../src/net/socket/unix/stream/socket.rs | 27 +++++- kernel/aster-nix/src/syscall/fcntl.rs | 24 ++++++ kernel/aster-nix/src/syscall/socket.rs | 3 +- test/syscall_test/Makefile | 1 + test/syscall_test/blocklists/fcntl_test | 22 +++++ 10 files changed, 261 insertions(+), 7 deletions(-) create mode 100644 test/syscall_test/blocklists/fcntl_test diff --git a/kernel/aster-nix/src/fs/file_table.rs b/kernel/aster-nix/src/fs/file_table.rs index 09000332a..cdec4b74e 100644 --- a/kernel/aster-nix/src/fs/file_table.rs +++ b/kernel/aster-nix/src/fs/file_table.rs @@ -12,9 +12,15 @@ use super::{ utils::{AccessMode, InodeMode}, }; use crate::{ - events::{Events, Observer, Subject}, + events::{Events, IoEvents, Observer, Subject}, + fs::utils::StatusFlags, net::socket::Socket, prelude::*, + process::{ + process_table, + signal::{constants::SIGIO, signals::kernel::KernelSignal}, + Pid, Process, + }, }; pub type FileDesc = i32; @@ -232,6 +238,7 @@ pub struct FileTableEntry { file: Arc, flags: AtomicU8, subject: Subject, + owner: RwLock>, } impl FileTableEntry { @@ -240,6 +247,7 @@ impl FileTableEntry { file, flags: AtomicU8::new(flags.bits()), subject: Subject::new(), + owner: RwLock::new(None), } } @@ -247,6 +255,48 @@ impl FileTableEntry { &self.file } + pub fn owner(&self) -> Option { + self.owner.read().as_ref().map(|(pid, _)| *pid) + } + + /// Set a process (group) as owner of the file descriptor. + /// + /// Such that this process (group) will receive `SIGIO` and `SIGURG` signals + /// for I/O events on the file descriptor, if `O_ASYNC` status flag is set + /// on this file. + pub fn set_owner(&self, owner: Pid) -> Result<()> { + if self.owner().is_some_and(|pid| pid == owner) { + return Ok(()); + } + + // Unset the owner if the given pid is zero. + let new_owner = if owner == 0 { + None + } else { + let process = process_table::get_process(owner as _).ok_or(Error::with_message( + Errno::ESRCH, + "cannot set_owner with an invalid pid", + ))?; + let observer = OwnerObserver::new(self.file.clone(), Arc::downgrade(&process)); + Some((owner, observer)) + }; + + let mut self_owner = self.owner.write(); + if let Some((_, observer)) = self_owner.as_ref() { + let _ = self.file.unregister_observer(&Arc::downgrade(observer)); + } + + *self_owner = match new_owner { + None => None, + Some((pid, observer)) => { + self.file + .register_observer(observer.weak_self(), IoEvents::empty())?; + Some((pid, observer)) + } + }; + Ok(()) + } + pub fn flags(&self) -> FdFlags { FdFlags::from_bits(self.flags.load(Ordering::Relaxed)).unwrap() } @@ -274,6 +324,7 @@ impl Clone for FileTableEntry { file: self.file.clone(), flags: AtomicU8::new(self.flags.load(Ordering::Relaxed)), subject: Subject::new(), + owner: RwLock::new(self.owner.read().clone()), } } } @@ -284,3 +335,35 @@ bitflags! { const CLOEXEC = 1; } } + +type Owner = (Pid, Arc>); + +struct OwnerObserver { + file: Arc, + owner: Weak, + weak_self: Weak, +} + +impl OwnerObserver { + pub fn new(file: Arc, owner: Weak) -> Arc { + Arc::new_cyclic(|weak_ref| Self { + file, + owner, + weak_self: weak_ref.clone(), + }) + } + + pub fn weak_self(&self) -> Weak { + self.weak_self.clone() + } +} + +impl Observer for OwnerObserver { + fn on_events(&self, events: &IoEvents) { + if self.file.status_flags().contains(StatusFlags::O_ASYNC) + && let Some(process) = self.owner.upgrade() + { + process.enqueue_signal(KernelSignal::new(SIGIO)); + } + } +} diff --git a/kernel/aster-nix/src/net/socket/unix/stream/connected.rs b/kernel/aster-nix/src/net/socket/unix/stream/connected.rs index e8deb0855..f496d0b1f 100644 --- a/kernel/aster-nix/src/net/socket/unix/stream/connected.rs +++ b/kernel/aster-nix/src/net/socket/unix/stream/connected.rs @@ -2,7 +2,7 @@ use super::endpoint::Endpoint; use crate::{ - events::IoEvents, + events::{IoEvents, Observer}, net::socket::{unix::addr::UnixSocketAddrBound, SockShutdownCmd}, prelude::*, process::signal::Poller, @@ -40,4 +40,19 @@ impl Connected { pub(super) fn poll(&self, mask: IoEvents, poller: Option<&mut Poller>) -> IoEvents { self.local_endpoint.poll(mask, poller) } + + pub(super) fn register_observer( + &self, + observer: Weak>, + mask: IoEvents, + ) -> Result<()> { + self.local_endpoint.register_observer(observer, mask) + } + + pub(super) fn unregister_observer( + &self, + observer: &Weak>, + ) -> Option>> { + self.local_endpoint.unregister_observer(observer) + } } diff --git a/kernel/aster-nix/src/net/socket/unix/stream/endpoint.rs b/kernel/aster-nix/src/net/socket/unix/stream/endpoint.rs index c780c0610..8a67e8e59 100644 --- a/kernel/aster-nix/src/net/socket/unix/stream/endpoint.rs +++ b/kernel/aster-nix/src/net/socket/unix/stream/endpoint.rs @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MPL-2.0 use crate::{ - events::IoEvents, + events::{IoEvents, Observer}, fs::utils::{Channel, Consumer, Producer}, net::socket::{unix::addr::UnixSocketAddrBound, SockShutdownCmd}, prelude::*, @@ -88,6 +88,38 @@ impl Endpoint { events } + + pub(super) fn register_observer( + &self, + observer: Weak>, + mask: IoEvents, + ) -> Result<()> { + if mask.contains(IoEvents::IN) { + self.reader.register_observer(observer.clone(), mask)? + } + + if mask.contains(IoEvents::OUT) { + self.writer.register_observer(observer, mask)? + } + + Ok(()) + } + + pub(super) fn unregister_observer( + &self, + observer: &Weak>, + ) -> Option>> { + let observer0 = self.reader.unregister_observer(observer); + let observer1 = self.writer.unregister_observer(observer); + + if observer0.is_some() { + observer0 + } else if observer1.is_some() { + observer1 + } else { + None + } + } } const DAFAULT_BUF_SIZE: usize = 4096; diff --git a/kernel/aster-nix/src/net/socket/unix/stream/init.rs b/kernel/aster-nix/src/net/socket/unix/stream/init.rs index 2abaeacb9..197b44a3b 100644 --- a/kernel/aster-nix/src/net/socket/unix/stream/init.rs +++ b/kernel/aster-nix/src/net/socket/unix/stream/init.rs @@ -2,7 +2,7 @@ use super::{connected::Connected, endpoint::Endpoint, listener::push_incoming}; use crate::{ - events::IoEvents, + events::{IoEvents, Observer}, fs::{ fs_resolver::{split_path, FsPath}, path::Dentry, @@ -66,6 +66,22 @@ impl Init { pub(super) fn poll(&self, mask: IoEvents, poller: Option<&mut Poller>) -> IoEvents { self.pollee.poll(mask, poller) } + + pub(super) fn register_observer( + &self, + observer: Weak>, + mask: IoEvents, + ) -> Result<()> { + self.pollee.register_observer(observer, mask); + Ok(()) + } + + pub(super) fn unregister_observer( + &self, + observer: &Weak>, + ) -> Option>> { + self.pollee.unregister_observer(observer) + } } fn create_socket_file(path: &str) -> Result> { diff --git a/kernel/aster-nix/src/net/socket/unix/stream/listener.rs b/kernel/aster-nix/src/net/socket/unix/stream/listener.rs index 90b8e3515..5d9e2ce80 100644 --- a/kernel/aster-nix/src/net/socket/unix/stream/listener.rs +++ b/kernel/aster-nix/src/net/socket/unix/stream/listener.rs @@ -4,7 +4,7 @@ use keyable_arc::KeyableWeak; use super::{connected::Connected, endpoint::Endpoint, UnixStreamSocket}; use crate::{ - events::IoEvents, + events::{IoEvents, Observer}, fs::{file_handle::FileLike, path::Dentry, utils::Inode}, net::socket::{ unix::addr::{UnixSocketAddr, UnixSocketAddrBound}, @@ -51,6 +51,25 @@ impl Listener { let backlog = BACKLOG_TABLE.get_backlog(addr).unwrap(); backlog.poll(mask, poller) } + + pub(super) fn register_observer( + &self, + observer: Weak>, + mask: IoEvents, + ) -> Result<()> { + let addr = self.addr(); + let backlog = BACKLOG_TABLE.get_backlog(addr)?; + backlog.register_observer(observer, mask) + } + + pub(super) fn unregister_observer( + &self, + observer: &Weak>, + ) -> Option>> { + let addr = self.addr(); + let backlog = BACKLOG_TABLE.get_backlog(addr).ok()?; + backlog.unregister_observer(observer) + } } static BACKLOG_TABLE: BacklogTable = BacklogTable::new(); @@ -169,6 +188,22 @@ impl Backlog { let _lock = self.incoming_endpoints.lock(); self.pollee.poll(mask, poller) } + + fn register_observer( + &self, + observer: Weak>, + mask: IoEvents, + ) -> Result<()> { + self.pollee.register_observer(observer, mask); + Ok(()) + } + + fn unregister_observer( + &self, + observer: &Weak>, + ) -> Option>> { + self.pollee.unregister_observer(observer) + } } fn create_keyable_inode(dentry: &Arc) -> KeyableWeak { diff --git a/kernel/aster-nix/src/net/socket/unix/stream/socket.rs b/kernel/aster-nix/src/net/socket/unix/stream/socket.rs index 9bdea7595..df93af0fc 100644 --- a/kernel/aster-nix/src/net/socket/unix/stream/socket.rs +++ b/kernel/aster-nix/src/net/socket/unix/stream/socket.rs @@ -11,7 +11,7 @@ use super::{ listener::{unregister_backlog, Listener}, }; use crate::{ - events::IoEvents, + events::{IoEvents, Observer}, fs::{ file_handle::FileLike, fs_resolver::FsPath, @@ -166,6 +166,31 @@ impl FileLike for UnixStreamSocket { self.set_nonblocking(new_flags.contains(StatusFlags::O_NONBLOCK)); Ok(()) } + + fn register_observer( + &self, + observer: Weak>, + mask: IoEvents, + ) -> Result<()> { + let inner = self.state.write(); + match &*inner { + State::Init(init) => init.register_observer(observer, mask), + State::Listen(listen) => listen.register_observer(observer, mask), + State::Connected(connected) => connected.register_observer(observer, mask), + } + } + + fn unregister_observer( + &self, + observer: &Weak>, + ) -> Option>> { + let inner = self.state.write(); + match &*inner { + State::Init(init) => init.unregister_observer(observer), + State::Listen(listen) => listen.unregister_observer(observer), + State::Connected(connected) => connected.unregister_observer(observer), + } + } } impl Socket for UnixStreamSocket { diff --git a/kernel/aster-nix/src/syscall/fcntl.rs b/kernel/aster-nix/src/syscall/fcntl.rs index 89274bd58..7c05321f7 100644 --- a/kernel/aster-nix/src/syscall/fcntl.rs +++ b/kernel/aster-nix/src/syscall/fcntl.rs @@ -7,6 +7,7 @@ use crate::{ utils::StatusFlags, }, prelude::*, + process::Pid, }; pub fn sys_fcntl(fd: FileDesc, cmd: i32, arg: u64) -> Result { @@ -80,6 +81,27 @@ pub fn sys_fcntl(fd: FileDesc, cmd: i32, arg: u64) -> Result { file.set_status_flags(new_status_flags)?; Ok(SyscallReturn::Return(0)) } + FcntlCmd::F_SETOWN => { + let current = current!(); + let file_table = current.file_table().lock(); + let file_entry = file_table.get_entry(fd)?; + // A process ID is specified as a positive value; a process group ID is specified as a negative value. + let abs_arg = (arg as i32).unsigned_abs(); + if abs_arg > i32::MAX as u32 { + return_errno_with_message!(Errno::EINVAL, "process (group) id overflowed"); + } + let pid = Pid::try_from(abs_arg) + .map_err(|_| Error::with_message(Errno::EINVAL, "invalid process (group) id"))?; + file_entry.set_owner(pid)?; + Ok(SyscallReturn::Return(0)) + } + FcntlCmd::F_GETOWN => { + let current = current!(); + let file_table = current.file_table().lock(); + let file_entry = file_table.get_entry(fd)?; + let pid = file_entry.owner().unwrap_or(0); + Ok(SyscallReturn::Return(pid as _)) + } } } @@ -92,5 +114,7 @@ enum FcntlCmd { F_SETFD = 2, F_GETFL = 3, F_SETFL = 4, + F_SETOWN = 8, + F_GETOWN = 9, F_DUPFD_CLOEXEC = 1030, } diff --git a/kernel/aster-nix/src/syscall/socket.rs b/kernel/aster-nix/src/syscall/socket.rs index 83262650b..9b49485d6 100644 --- a/kernel/aster-nix/src/syscall/socket.rs +++ b/kernel/aster-nix/src/syscall/socket.rs @@ -23,7 +23,8 @@ pub fn sys_socket(domain: i32, type_: i32, protocol: i32) -> Result { + // FIXME: SOCK_SEQPACKET is added to run fcntl_test, not supported yet. + (CSocketAddrFamily::AF_UNIX, SockType::SOCK_STREAM | SockType::SOCK_SEQPACKET, _) => { UnixStreamSocket::new(nonblocking) as Arc } ( diff --git a/test/syscall_test/Makefile b/test/syscall_test/Makefile index 6aa6d13b6..6cd4c5f51 100644 --- a/test/syscall_test/Makefile +++ b/test/syscall_test/Makefile @@ -15,6 +15,7 @@ TESTS ?= \ dup_test \ epoll_test \ eventfd_test \ + fcntl_test \ fsync_test \ getdents_test \ link_test \ diff --git a/test/syscall_test/blocklists/fcntl_test b/test/syscall_test/blocklists/fcntl_test new file mode 100644 index 000000000..3c116a082 --- /dev/null +++ b/test/syscall_test/blocklists/fcntl_test @@ -0,0 +1,22 @@ +FcntlLockTest.* +FcntlTest.GetAllFlags +FcntlTest.SetFlags +FcntlTest.GetO_ASYNC +FcntlTest.SetFlO_ASYNC +FcntlTest.SetFdO_ASYNC +FcntlTest.DupAfterO_ASYNC +# SetOwnPgrp is verified with F_GETOWN_EX, which is not supported now. +FcntlTest.SetOwnPgrp +FcntlTest.GetOwnExNone +FcntlTest.GetOwnExTid +FcntlTest.GetOwnExPid +FcntlTest.GetOwnExPgrp +FcntlTest.SetOwnExInvalidType +FcntlTest.SetOwnExInvalidTid +FcntlTest.SetOwnExInvalidPid +FcntlTest.SetOwnExInvalidPgrp +FcntlTest.SetOwnExTid +FcntlTest.SetOwnExPid +FcntlTest.SetOwnExPgrp +FcntlTest.SetOwnExUnset +FcntlTest.SetFlSetOwnDoNotRace \ No newline at end of file