mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-27 03:13:23 +00:00
Add fcntl subcommand F_GETOWN/F_SETOWN
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
6ab3373f9b
commit
077a9ac3ab
@ -12,9 +12,15 @@ use super::{
|
|||||||
utils::{AccessMode, InodeMode},
|
utils::{AccessMode, InodeMode},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
events::{Events, Observer, Subject},
|
events::{Events, IoEvents, Observer, Subject},
|
||||||
|
fs::utils::StatusFlags,
|
||||||
net::socket::Socket,
|
net::socket::Socket,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
process::{
|
||||||
|
process_table,
|
||||||
|
signal::{constants::SIGIO, signals::kernel::KernelSignal},
|
||||||
|
Pid, Process,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type FileDesc = i32;
|
pub type FileDesc = i32;
|
||||||
@ -232,6 +238,7 @@ pub struct FileTableEntry {
|
|||||||
file: Arc<dyn FileLike>,
|
file: Arc<dyn FileLike>,
|
||||||
flags: AtomicU8,
|
flags: AtomicU8,
|
||||||
subject: Subject<FdEvents>,
|
subject: Subject<FdEvents>,
|
||||||
|
owner: RwLock<Option<Owner>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileTableEntry {
|
impl FileTableEntry {
|
||||||
@ -240,6 +247,7 @@ impl FileTableEntry {
|
|||||||
file,
|
file,
|
||||||
flags: AtomicU8::new(flags.bits()),
|
flags: AtomicU8::new(flags.bits()),
|
||||||
subject: Subject::new(),
|
subject: Subject::new(),
|
||||||
|
owner: RwLock::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,6 +255,48 @@ impl FileTableEntry {
|
|||||||
&self.file
|
&self.file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn owner(&self) -> Option<Pid> {
|
||||||
|
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 {
|
pub fn flags(&self) -> FdFlags {
|
||||||
FdFlags::from_bits(self.flags.load(Ordering::Relaxed)).unwrap()
|
FdFlags::from_bits(self.flags.load(Ordering::Relaxed)).unwrap()
|
||||||
}
|
}
|
||||||
@ -274,6 +324,7 @@ impl Clone for FileTableEntry {
|
|||||||
file: self.file.clone(),
|
file: self.file.clone(),
|
||||||
flags: AtomicU8::new(self.flags.load(Ordering::Relaxed)),
|
flags: AtomicU8::new(self.flags.load(Ordering::Relaxed)),
|
||||||
subject: Subject::new(),
|
subject: Subject::new(),
|
||||||
|
owner: RwLock::new(self.owner.read().clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,3 +335,35 @@ bitflags! {
|
|||||||
const CLOEXEC = 1;
|
const CLOEXEC = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Owner = (Pid, Arc<dyn Observer<IoEvents>>);
|
||||||
|
|
||||||
|
struct OwnerObserver {
|
||||||
|
file: Arc<dyn FileLike>,
|
||||||
|
owner: Weak<Process>,
|
||||||
|
weak_self: Weak<Self>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OwnerObserver {
|
||||||
|
pub fn new(file: Arc<dyn FileLike>, owner: Weak<Process>) -> Arc<Self> {
|
||||||
|
Arc::new_cyclic(|weak_ref| Self {
|
||||||
|
file,
|
||||||
|
owner,
|
||||||
|
weak_self: weak_ref.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn weak_self(&self) -> Weak<Self> {
|
||||||
|
self.weak_self.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Observer<IoEvents> 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use super::endpoint::Endpoint;
|
use super::endpoint::Endpoint;
|
||||||
use crate::{
|
use crate::{
|
||||||
events::IoEvents,
|
events::{IoEvents, Observer},
|
||||||
net::socket::{unix::addr::UnixSocketAddrBound, SockShutdownCmd},
|
net::socket::{unix::addr::UnixSocketAddrBound, SockShutdownCmd},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
process::signal::Poller,
|
process::signal::Poller,
|
||||||
@ -40,4 +40,19 @@ impl Connected {
|
|||||||
pub(super) fn poll(&self, mask: IoEvents, poller: Option<&mut Poller>) -> IoEvents {
|
pub(super) fn poll(&self, mask: IoEvents, poller: Option<&mut Poller>) -> IoEvents {
|
||||||
self.local_endpoint.poll(mask, poller)
|
self.local_endpoint.poll(mask, poller)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn register_observer(
|
||||||
|
&self,
|
||||||
|
observer: Weak<dyn Observer<IoEvents>>,
|
||||||
|
mask: IoEvents,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.local_endpoint.register_observer(observer, mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn unregister_observer(
|
||||||
|
&self,
|
||||||
|
observer: &Weak<dyn Observer<IoEvents>>,
|
||||||
|
) -> Option<Weak<dyn Observer<IoEvents>>> {
|
||||||
|
self.local_endpoint.unregister_observer(observer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
events::IoEvents,
|
events::{IoEvents, Observer},
|
||||||
fs::utils::{Channel, Consumer, Producer},
|
fs::utils::{Channel, Consumer, Producer},
|
||||||
net::socket::{unix::addr::UnixSocketAddrBound, SockShutdownCmd},
|
net::socket::{unix::addr::UnixSocketAddrBound, SockShutdownCmd},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
@ -88,6 +88,38 @@ impl Endpoint {
|
|||||||
|
|
||||||
events
|
events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn register_observer(
|
||||||
|
&self,
|
||||||
|
observer: Weak<dyn Observer<IoEvents>>,
|
||||||
|
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<dyn Observer<IoEvents>>,
|
||||||
|
) -> Option<Weak<dyn Observer<IoEvents>>> {
|
||||||
|
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;
|
const DAFAULT_BUF_SIZE: usize = 4096;
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use super::{connected::Connected, endpoint::Endpoint, listener::push_incoming};
|
use super::{connected::Connected, endpoint::Endpoint, listener::push_incoming};
|
||||||
use crate::{
|
use crate::{
|
||||||
events::IoEvents,
|
events::{IoEvents, Observer},
|
||||||
fs::{
|
fs::{
|
||||||
fs_resolver::{split_path, FsPath},
|
fs_resolver::{split_path, FsPath},
|
||||||
path::Dentry,
|
path::Dentry,
|
||||||
@ -66,6 +66,22 @@ impl Init {
|
|||||||
pub(super) fn poll(&self, mask: IoEvents, poller: Option<&mut Poller>) -> IoEvents {
|
pub(super) fn poll(&self, mask: IoEvents, poller: Option<&mut Poller>) -> IoEvents {
|
||||||
self.pollee.poll(mask, poller)
|
self.pollee.poll(mask, poller)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn register_observer(
|
||||||
|
&self,
|
||||||
|
observer: Weak<dyn Observer<IoEvents>>,
|
||||||
|
mask: IoEvents,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.pollee.register_observer(observer, mask);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn unregister_observer(
|
||||||
|
&self,
|
||||||
|
observer: &Weak<dyn Observer<IoEvents>>,
|
||||||
|
) -> Option<Weak<dyn Observer<IoEvents>>> {
|
||||||
|
self.pollee.unregister_observer(observer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_socket_file(path: &str) -> Result<Arc<Dentry>> {
|
fn create_socket_file(path: &str) -> Result<Arc<Dentry>> {
|
||||||
|
@ -4,7 +4,7 @@ use keyable_arc::KeyableWeak;
|
|||||||
|
|
||||||
use super::{connected::Connected, endpoint::Endpoint, UnixStreamSocket};
|
use super::{connected::Connected, endpoint::Endpoint, UnixStreamSocket};
|
||||||
use crate::{
|
use crate::{
|
||||||
events::IoEvents,
|
events::{IoEvents, Observer},
|
||||||
fs::{file_handle::FileLike, path::Dentry, utils::Inode},
|
fs::{file_handle::FileLike, path::Dentry, utils::Inode},
|
||||||
net::socket::{
|
net::socket::{
|
||||||
unix::addr::{UnixSocketAddr, UnixSocketAddrBound},
|
unix::addr::{UnixSocketAddr, UnixSocketAddrBound},
|
||||||
@ -51,6 +51,25 @@ impl Listener {
|
|||||||
let backlog = BACKLOG_TABLE.get_backlog(addr).unwrap();
|
let backlog = BACKLOG_TABLE.get_backlog(addr).unwrap();
|
||||||
backlog.poll(mask, poller)
|
backlog.poll(mask, poller)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn register_observer(
|
||||||
|
&self,
|
||||||
|
observer: Weak<dyn Observer<IoEvents>>,
|
||||||
|
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<dyn Observer<IoEvents>>,
|
||||||
|
) -> Option<Weak<dyn Observer<IoEvents>>> {
|
||||||
|
let addr = self.addr();
|
||||||
|
let backlog = BACKLOG_TABLE.get_backlog(addr).ok()?;
|
||||||
|
backlog.unregister_observer(observer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static BACKLOG_TABLE: BacklogTable = BacklogTable::new();
|
static BACKLOG_TABLE: BacklogTable = BacklogTable::new();
|
||||||
@ -169,6 +188,22 @@ impl Backlog {
|
|||||||
let _lock = self.incoming_endpoints.lock();
|
let _lock = self.incoming_endpoints.lock();
|
||||||
self.pollee.poll(mask, poller)
|
self.pollee.poll(mask, poller)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn register_observer(
|
||||||
|
&self,
|
||||||
|
observer: Weak<dyn Observer<IoEvents>>,
|
||||||
|
mask: IoEvents,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.pollee.register_observer(observer, mask);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unregister_observer(
|
||||||
|
&self,
|
||||||
|
observer: &Weak<dyn Observer<IoEvents>>,
|
||||||
|
) -> Option<Weak<dyn Observer<IoEvents>>> {
|
||||||
|
self.pollee.unregister_observer(observer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_keyable_inode(dentry: &Arc<Dentry>) -> KeyableWeak<dyn Inode> {
|
fn create_keyable_inode(dentry: &Arc<Dentry>) -> KeyableWeak<dyn Inode> {
|
||||||
|
@ -11,7 +11,7 @@ use super::{
|
|||||||
listener::{unregister_backlog, Listener},
|
listener::{unregister_backlog, Listener},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
events::IoEvents,
|
events::{IoEvents, Observer},
|
||||||
fs::{
|
fs::{
|
||||||
file_handle::FileLike,
|
file_handle::FileLike,
|
||||||
fs_resolver::FsPath,
|
fs_resolver::FsPath,
|
||||||
@ -166,6 +166,31 @@ impl FileLike for UnixStreamSocket {
|
|||||||
self.set_nonblocking(new_flags.contains(StatusFlags::O_NONBLOCK));
|
self.set_nonblocking(new_flags.contains(StatusFlags::O_NONBLOCK));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn register_observer(
|
||||||
|
&self,
|
||||||
|
observer: Weak<dyn Observer<IoEvents>>,
|
||||||
|
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<dyn Observer<IoEvents>>,
|
||||||
|
) -> Option<Weak<dyn Observer<IoEvents>>> {
|
||||||
|
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 {
|
impl Socket for UnixStreamSocket {
|
||||||
|
@ -7,6 +7,7 @@ use crate::{
|
|||||||
utils::StatusFlags,
|
utils::StatusFlags,
|
||||||
},
|
},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
|
process::Pid,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn sys_fcntl(fd: FileDesc, cmd: i32, arg: u64) -> Result<SyscallReturn> {
|
pub fn sys_fcntl(fd: FileDesc, cmd: i32, arg: u64) -> Result<SyscallReturn> {
|
||||||
@ -80,6 +81,27 @@ pub fn sys_fcntl(fd: FileDesc, cmd: i32, arg: u64) -> Result<SyscallReturn> {
|
|||||||
file.set_status_flags(new_status_flags)?;
|
file.set_status_flags(new_status_flags)?;
|
||||||
Ok(SyscallReturn::Return(0))
|
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_SETFD = 2,
|
||||||
F_GETFL = 3,
|
F_GETFL = 3,
|
||||||
F_SETFL = 4,
|
F_SETFL = 4,
|
||||||
|
F_SETOWN = 8,
|
||||||
|
F_GETOWN = 9,
|
||||||
F_DUPFD_CLOEXEC = 1030,
|
F_DUPFD_CLOEXEC = 1030,
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@ pub fn sys_socket(domain: i32, type_: i32, protocol: i32) -> Result<SyscallRetur
|
|||||||
);
|
);
|
||||||
let nonblocking = sock_flags.contains(SockFlags::SOCK_NONBLOCK);
|
let nonblocking = sock_flags.contains(SockFlags::SOCK_NONBLOCK);
|
||||||
let file_like = match (domain, sock_type, protocol) {
|
let file_like = match (domain, sock_type, protocol) {
|
||||||
(CSocketAddrFamily::AF_UNIX, SockType::SOCK_STREAM, _) => {
|
// 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<dyn FileLike>
|
UnixStreamSocket::new(nonblocking) as Arc<dyn FileLike>
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
|
@ -15,6 +15,7 @@ TESTS ?= \
|
|||||||
dup_test \
|
dup_test \
|
||||||
epoll_test \
|
epoll_test \
|
||||||
eventfd_test \
|
eventfd_test \
|
||||||
|
fcntl_test \
|
||||||
fsync_test \
|
fsync_test \
|
||||||
getdents_test \
|
getdents_test \
|
||||||
link_test \
|
link_test \
|
||||||
|
22
test/syscall_test/blocklists/fcntl_test
Normal file
22
test/syscall_test/blocklists/fcntl_test
Normal file
@ -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
|
Reference in New Issue
Block a user