WindowsXP-SP1/base/subsys/posix/psxss/lpipeio.c
2020-09-30 16:53:49 +02:00

1325 lines
33 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
lpipeio.c
Abstract:
This module implements all file descriptor oriented APIs.
Author:
Mark Lucovsky (markl) 30-Mar-1989
Revision History:
--*/
#include <sys/stat.h>
#include "psxsrv.h"
BOOLEAN
LocalPipeRead (
IN PPSX_PROCESS p,
IN OUT PPSX_API_MSG m,
IN PFILEDESCRIPTOR Fd
);
BOOLEAN
LocalPipeWrite (
IN PPSX_PROCESS p,
IN OUT PPSX_API_MSG m,
IN PFILEDESCRIPTOR Fd
);
BOOLEAN
LocalPipeDup(
IN PPSX_PROCESS p,
IN OUT PPSX_API_MSG m,
IN PFILEDESCRIPTOR Fd,
IN PFILEDESCRIPTOR FdDup
);
BOOLEAN
LocalPipeLseek (
IN PPSX_PROCESS p,
IN OUT PPSX_API_MSG m,
IN PFILEDESCRIPTOR Fd
)
/*++
Routine Description:
This procedure implements lseek when the device being seeked on
is a local or named pipe.
Arguments:
p - Supplies the address of the process making the call.
m - Supplies the address of the message associated with the request.
Fd - supplies the address of the file descriptor being seekd
Return Value:
???
--*/
{
m->Error = ESPIPE;
return TRUE;
}
BOOLEAN
LocalPipeStat (
IN PIONODE IoNode,
IN HANDLE FileHandle,
OUT struct stat *StatBuf,
OUT NTSTATUS *pStatus
)
/*++
Routine Description:
This procedure implements stat when the device being read
is a local pipe.
Arguments:
IoNode - supplies a pointer to the ionode of the pipe for which stat is
requested.
FileHandle - supplies the Nt file handle of the pipe. NULL for local pipes.
StatBuf - Supplies the address of the statbuf portion of the message
associated with the request.
Return Value:
???
--*/
{
// Pipe() sets the IoNode fields.
StatBuf->st_mode = IoNode->Mode;
StatBuf->st_ino = (ino_t)IoNode->FileSerialNumber;
StatBuf->st_dev = IoNode->DeviceSerialNumber;
StatBuf->st_uid = IoNode->OwnerId;
StatBuf->st_gid = IoNode->GroupId;
StatBuf->st_atime = IoNode->AccessDataTime;
StatBuf->st_mtime = IoNode->ModifyDataTime;
StatBuf->st_ctime = IoNode->ModifyIoNodeTime;
StatBuf->st_size = PIPE_BUF;
// This implementation dependent.
StatBuf->st_nlink = 0;
return TRUE;
}
VOID
LocalPipeNewHandle (
IN PPSX_PROCESS p,
IN PFILEDESCRIPTOR Fd
)
/*++
Routine Description:
This function is called any time a handle is created for a pipe.
Arguments:
p - Supplies a pointer to the process creating the handle to the pipe.
Fd - Supplies the file descriptor that refers to the pipe.
Return Value:
None.
--*/
{
PLOCAL_PIPE Pipe;
Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
RtlEnterCriticalSection(&Pipe->CriticalSection);
if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
Pipe->ReadHandleCount++;
}
if (Fd->SystemOpenFileDesc->Flags & PSX_FD_WRITE) {
Pipe->WriteHandleCount++;
}
RtlLeaveCriticalSection(&Pipe->CriticalSection);
}
VOID
LocalPipeClose (
IN PPSX_PROCESS p,
IN PFILEDESCRIPTOR Fd
)
/*++
Routine Description:
This function is called any time a handle is deleted for a pipe.
Arguments:
p - Supplies a pointer to the closing the handle to the pipe.
Fd - Supplies the file descriptor that refers to the pipe.
Return Value:
None.
--*/
{
PLOCAL_PIPE Pipe;
PINTCB IntCb;
PPSX_PROCESS WaitingProc;
PLIST_ENTRY Next;
Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
RtlEnterCriticalSection(&Pipe->CriticalSection);
if ((Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) &&
(0 == --Pipe->ReadHandleCount)) {
//
// Last reader close; any writers hanging around
// get EPIPE and a SIGPIPE
//
RtlEnterCriticalSection(&BlockLock);
Next = Pipe->WaitingWriters.Flink;
while (Next != &Pipe->WaitingWriters) {
IntCb = CONTAINING_RECORD(Next, INTCB, Links);
WaitingProc = (PPSX_PROCESS)IntCb->IntContext;
UnblockProcess(WaitingProc, IoCompletionInterrupt,
TRUE, 0);
RtlEnterCriticalSection(&BlockLock);
Next = Pipe->WaitingWriters.Flink;
}
RtlLeaveCriticalSection(&BlockLock);
}
if ((Fd->SystemOpenFileDesc->Flags & PSX_FD_WRITE) &&
(0 == --Pipe->WriteHandleCount)) {
//
// Last writer close; any readers hanging around
// get 0.
//
RtlEnterCriticalSection(&BlockLock);
Next = Pipe->WaitingReaders.Flink;
while (Next != &Pipe->WaitingReaders) {
IntCb = CONTAINING_RECORD(Next, INTCB, Links);
WaitingProc = (PPSX_PROCESS)IntCb->IntContext;
UnblockProcess(WaitingProc, IoCompletionInterrupt,
TRUE, 0);
RtlEnterCriticalSection(&BlockLock);
Next = Pipe->WaitingReaders.Flink;
}
RtlLeaveCriticalSection(&BlockLock);
}
RtlLeaveCriticalSection(&Pipe->CriticalSection);
}
VOID
LocalPipeIoNodeClose (
IN PIONODE IoNode
)
/*++
Routine Description:
This function is called when the IONODE representing a pipe is
closed. Its function is to tear down the pipe.
Arguments:
IoNode - Supplies the IoNode being deleted
Return Value:
None.
--*/
{
PLOCAL_PIPE Pipe;
Pipe = (PLOCAL_PIPE) IoNode->Context;
RtlDeleteCriticalSection(&Pipe->CriticalSection);
RtlFreeHeap(PsxHeap, 0,Pipe);
}
PSXIO_VECTORS LocalPipeVectors = {
NULL,
LocalPipeNewHandle,
LocalPipeClose,
NULL,
LocalPipeIoNodeClose,
LocalPipeRead,
LocalPipeWrite,
LocalPipeDup,
LocalPipeLseek,
LocalPipeStat
};
VOID
InitializeLocalPipe(
IN PLOCAL_PIPE Pipe
)
/*++
Routine Description:
This function initializes a local pipe
Arguments:
Pipe - Supplies the address of a local pipe
Return Value:
None.
--*/
{
NTSTATUS st;
st = RtlInitializeCriticalSection(&Pipe->CriticalSection);
ASSERT(NT_SUCCESS(st));
InitializeListHead(&Pipe->WaitingWriters);
InitializeListHead(&Pipe->WaitingReaders);
Pipe->ReadHandleCount = 0;
Pipe->WriteHandleCount = 0;
Pipe->BufferSize = PIPE_BUF;
Pipe->DataInPipe = 0;
Pipe->WritePointer = &Pipe->Buffer[0];
Pipe->ReadPointer = &Pipe->Buffer[0];
}
VOID
LocalPipeWriteHandler(
IN PPSX_PROCESS p,
IN PINTCB IntControlBlock,
IN PSX_INTERRUPTREASON InterruptReason,
IN int Signal // signal causing wakeup, if any
)
/*++
Routine Description:
This procedure is called when a there is room in a pipe, and a blocked
writer exists whose current write request length is less than the
amount of room in the pipe.
Arguments:
p - Supplies the address of the process being interrupted.
IntControlBlock - Supplies the address of the interrupt control block.
InterruptReason - Supplies the reason that this process is being
interrupted.
Return Value:
None.
--*/
{
PFILEDESCRIPTOR Fd;
BOOLEAN reply;
PPSX_API_MSG m;
PPSX_WRITE_MSG args;
RtlLeaveCriticalSection(&BlockLock);
m = IntControlBlock->IntMessage;
args = &m->u.Write;
if (InterruptReason == SignalInterrupt) {
//
// The write was interrupted by a signal. Bail out of
// service and let the interrupt be handled
//
RtlFreeHeap(PsxHeap, 0,IntControlBlock);
m->Error = EINTR;
m->Signal = Signal;
ApiReply(p,m,NULL);
RtlFreeHeap(PsxHeap, 0,m);
return;
}
Fd = FdIndexToFd(p,args->FileDes);
if (!Fd) {
Panic("LocalPipeWriteHandler: FdIndex");
}
RtlFreeHeap(PsxHeap, 0, IntControlBlock);
reply = LocalPipeWrite(p, m, Fd);
if (reply) {
ApiReply(p, m, NULL);
}
RtlFreeHeap(PsxHeap, 0,m);
}
BOOLEAN
LocalPipeWrite (
IN PPSX_PROCESS p,
IN OUT PPSX_API_MSG m,
IN PFILEDESCRIPTOR Fd
)
/*++
Routine Description:
This procedure implements write when the device being written
is a local pipe.
Arguments:
p - Supplies the address of the process making the call.
m - Supplies the address of the message associated with the request.
Fd - supplies the address of the file descriptor being written.
Return Value:
TRUE - the routine completed, and a reply should be sent
FALSE - the routine was blocked, no reply should be sent
--*/
{
PPSX_WRITE_MSG args;
PLOCAL_PIPE Pipe;
LONG Chunk, RoomInPipe;
SIZE_T cb;
PUCHAR WriteDataPoint, ProcessBuffer;
NTSTATUS st;
PINTCB IntCb;
PPSX_PROCESS WaitingReader;
LARGE_INTEGER Time;
ULONG PosixTime;
NTSTATUS Status;
args = &m->u.Write;
Pipe = (PLOCAL_PIPE) Fd->SystemOpenFileDesc->IoNode->Context;
RtlEnterCriticalSection(&Pipe->CriticalSection);
//
// If we're writing to a pipe with no readers connected, we return
// EPIPE and send a SIGPIPE to the process. Broken pipe, call a
// plumber.
//
if (0 == Pipe->ReadHandleCount) {
RtlLeaveCriticalSection(&Pipe->CriticalSection);
m->Error = EPIPE;
AcquireProcessStructureLock();
PsxSignalProcess(p, SIGPIPE);
ReleaseProcessStructureLock();
return TRUE;
}
//
// if requested write size is greater than buffer size,
// write must be broken up into Pipe->BufferSize atomic
// chunks. If this is the case, Scratch1 is used to record
// amount of data transfered so far, and Scratch2 is used to
// record the number of bytes left in the total transfer
//
if (args->Nbytes > Pipe->BufferSize) {
args->Scratch2 = args->Nbytes - Pipe->BufferSize;
args->Nbytes = Pipe->BufferSize;
}
RoomInPipe = Pipe->BufferSize - Pipe->DataInPipe;
if (args->Nbytes > RoomInPipe) {
//
// There is not enough space in the pipe for the write to
// succeed. If the O_NONBLOCK flag is set, write whatever
// will fit. Otherwise, block the write and wait for a read
// to empty some of the data.
//
if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
args->Nbytes = 1;
if (args->Nbytes > RoomInPipe) {
m->Error = EAGAIN;
return TRUE;
}
// continue below to write
} else {
Status = BlockProcess(p, (PVOID)p, LocalPipeWriteHandler, m,
&Pipe->WaitingWriters, &Pipe->CriticalSection);
if (!NT_SUCCESS(Status)) {
m->Error = PsxStatusToErrno(Status);
return TRUE;
}
//
// Successfully blocked -- don't reply to message.
//
return FALSE;
}
}
//
// there is room in the pipe for the write to occur
//
WriteDataPoint = Pipe->WritePointer;
ProcessBuffer = (PUCHAR) args->Buf;
if ((ULONG_PTR)WriteDataPoint + args->Nbytes >
(ULONG_PTR)&Pipe->Buffer[Pipe->BufferSize-1]) {
Chunk = (LONG)((ULONG_PTR)&Pipe->Buffer[Pipe->BufferSize-1] -
(ULONG_PTR)WriteDataPoint + 1);
} else {
Chunk = args->Nbytes;
}
st = NtReadVirtualMemory(p->Process, ProcessBuffer, WriteDataPoint,
(SIZE_T)Chunk, &cb);
if (!NT_SUCCESS(st) || (LONG)cb != Chunk) {
//
// If the read did not work, then report as IO error
//
RtlLeaveCriticalSection(&Pipe->CriticalSection);
m->Error = EIO;
return TRUE;
}
ProcessBuffer += Chunk;
if (Chunk < args->Nbytes) {
Chunk = args->Nbytes - Chunk;
WriteDataPoint = &Pipe->Buffer[0];
st = NtReadVirtualMemory(p->Process, ProcessBuffer, WriteDataPoint,
(ULONG)Chunk, &cb);
if (!NT_SUCCESS(st) || (LONG)cb != Chunk) {
//
// If the read did not work, then report as IO error
//
RtlLeaveCriticalSection(&Pipe->CriticalSection);
m->Error = EIO;
return TRUE;
}
Pipe->WritePointer = (PUCHAR)((ULONG_PTR)WriteDataPoint + Chunk);
} else {
Pipe->WritePointer = (PUCHAR)((ULONG_PTR)WriteDataPoint + Chunk);
}
if (Pipe->WritePointer > &Pipe->Buffer[Pipe->BufferSize - 1]) {
Pipe->WritePointer = &Pipe->Buffer[0];
}
Pipe->DataInPipe += args->Nbytes;
if (Pipe->DataInPipe > Pipe->BufferSize) {
Panic("LocalPipeWrite: Oops\n");
}
// Update ctime and mtime in IoNode - done in subsystem for local pipes
NtQuerySystemTime(&Time);
if (!RtlTimeToSecondsSince1970(&Time, &PosixTime)) {
PosixTime = 0L; // Time not within range of 1970 - 2105
}
RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
Fd->SystemOpenFileDesc->IoNode->ModifyDataTime =
Fd->SystemOpenFileDesc->IoNode->ModifyIoNodeTime = PosixTime;
RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
m->ReturnValue += args->Nbytes;
args->Buf += args->Nbytes;
//
// Check for WaitingReaders. If any are found, then kick em
//
RtlEnterCriticalSection(&BlockLock);
if (!IsListEmpty(&Pipe->WaitingReaders)) {
IntCb = (PINTCB)Pipe->WaitingReaders.Flink;
IntCb = CONTAINING_RECORD(IntCb,INTCB,Links);
WaitingReader = (PPSX_PROCESS) IntCb->IntContext;
RtlLeaveCriticalSection(&Pipe->CriticalSection);
UnblockProcess(WaitingReader, IoCompletionInterrupt, TRUE, 0);
//
// Determine if this is a broken up long write. If Scratch2 is
// non-zero then more transfers need to occur. If Scratch1 is
// non-zero, then update to account for data transfered in this
// iteration.
//
} else {
RtlLeaveCriticalSection(&BlockLock);
RtlLeaveCriticalSection(&Pipe->CriticalSection);
}
//
// If we're doing non-blocking io, we've written what will fit into
// the pipe and we should return to the user now.
//
if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
return TRUE;
}
//
// Determine if this is a broken up long write. If Scratch2 is
// non-zero then more transfers need to occur. If Scratch1 is
// non-zero, then update to account for data transfered in this
// iteration.
//
if (args->Scratch2) {
args->Nbytes = args->Scratch2;
args->Scratch2 = 0;
return LocalPipeWrite(p, m, Fd);
}
return TRUE;
}
VOID
LocalPipeReadHandler(
IN PPSX_PROCESS p,
IN PINTCB IntControlBlock,
IN PSX_INTERRUPTREASON InterruptReason,
IN int Signal // signal causing wakeup, if any
)
/*++
Routine Description:
This procedure is called when data appears in a pipe and the process
specified by p has placed itself on the WaitingReaders queue for the pipe.
Arguments:
p - Supplies the address of the process being interrupted.
IntControlBlock - Supplies the address of the interrupt control block.
InterruptReason - Supplies the reason that this process is being
interrupted. Not used in this handler.
Return Value:
None.
--*/
{
PFILEDESCRIPTOR Fd;
BOOLEAN reply;
PPSX_API_MSG m;
PPSX_READ_MSG args;
RtlLeaveCriticalSection(&BlockLock);
m = IntControlBlock->IntMessage;
args = &m->u.Read;
if (InterruptReason == SignalInterrupt) {
//
// The read was interrupted by a signal. Bail out of
// service and let the interrupt be handled
//
RtlFreeHeap(PsxHeap, 0, IntControlBlock);
m->Error = EINTR;
m->Signal = Signal;
ApiReply(p, m, NULL);
RtlFreeHeap(PsxHeap, 0, m);
return;
}
//
// IoCompletionInterrupt
//
Fd = FdIndexToFd(p, args->FileDes);
if (!Fd) {
Panic("LocalPipeReadHandler: FdIndex");
}
reply = LocalPipeRead(p, m, Fd);
RtlFreeHeap(PsxHeap, 0, IntControlBlock);
if (reply) {
ApiReply(p, m, NULL);
}
RtlFreeHeap(PsxHeap, 0, m);
}
BOOLEAN
LocalPipeRead (
IN PPSX_PROCESS p,
IN OUT PPSX_API_MSG m,
IN PFILEDESCRIPTOR Fd
)
/*++
Routine Description:
This procedure implements read when the device being read
is a local pipe.
Arguments:
p - Supplies the address of the process making the call.
m - Supplies the address of the message associated with the request.
Fd - supplies the address of the file descriptor being read.
Return Value:
TRUE if the read completed, FALSE if the process should block.
--*/
{
PPSX_READ_MSG args, WaitingArgs;
PPSX_PROCESS WaitingWriter;
PLOCAL_PIPE Pipe;
SIZE_T Chunk;
LONG RoomInPipe;
ULONG LargestRead;
SIZE_T cb;
PUCHAR ReadDataPoint, ProcessBuffer;
NTSTATUS st;
PPSX_API_MSG WaitingM;
PLIST_ENTRY Next;
PINTCB IntCb;
LARGE_INTEGER Time;
ULONG PosixTime;
args = &m->u.Read;
//
// check to see if any process has the pipe open for write
//
Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
ASSERT(NULL != Pipe);
RtlEnterCriticalSection(&Pipe->CriticalSection);
if (0 == Pipe->WriteHandleCount && !Pipe->DataInPipe) {
//
// Reading from an empty pipe with no writers attached gets you
// 0 (EOF).
//
RtlLeaveCriticalSection(&Pipe->CriticalSection);
m->ReturnValue = 0;
return TRUE;
}
if (!Pipe->DataInPipe) {
//
// if we have the pipe open O_NOBLOCK, then simply
// return EAGAIN
//
if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
RtlLeaveCriticalSection(&Pipe->CriticalSection);
m->Error = EAGAIN;
return TRUE;
}
//
// There is no data in the pipe. Set up an interrupt control
// block to wait for data and then block.
//
st = BlockProcess(p, (PVOID)p, LocalPipeReadHandler, m,
&Pipe->WaitingReaders, &Pipe->CriticalSection);
if (!NT_SUCCESS(st)) {
m->Error = PsxStatusToErrno(st);
return TRUE;
}
//
// Successfully blocked -- don't reply to api request.
//
return FALSE;
}
//
// If there is any data in the pipe, then compute the largest
// read size. Then figure out if it has to be broken into two
// transfers in order to turn the circular buffer boundary.
//
if (args->Nbytes > Pipe->DataInPipe) {
LargestRead = Pipe->DataInPipe;
} else {
LargestRead = args->Nbytes;
}
ReadDataPoint = Pipe->ReadPointer;
ProcessBuffer = (PUCHAR)args->Buf;
//
// determine if read can be done in one piece, or if
// the read has to be done in two pieces.
//
if ((ULONG_PTR)ReadDataPoint + LargestRead >
(ULONG_PTR)&Pipe->Buffer[Pipe->BufferSize - 1]) {
Chunk = (SIZE_T)((ULONG_PTR)&Pipe->Buffer[Pipe->BufferSize - 1] -
(ULONG_PTR)ReadDataPoint + 1);
} else {
Chunk = LargestRead;
}
//
// transfer from the pipe to the reading process
//
st = NtWriteVirtualMemory(p->Process, ProcessBuffer, ReadDataPoint,
Chunk, &cb);
if (!NT_SUCCESS(st) || cb != Chunk ) {
//
// If the write did not work, then report as IO error
//
RtlLeaveCriticalSection(&Pipe->CriticalSection);
m->Error = EIO;
return TRUE;
}
ProcessBuffer += Chunk;
if (Chunk < LargestRead) {
//
// the read wraps the pipe boundry. Transfer the second part of
// the read.
//
Chunk = LargestRead - Chunk;
ReadDataPoint = &Pipe->Buffer[0];
st = NtWriteVirtualMemory(p->Process, ProcessBuffer,
ReadDataPoint, Chunk, &cb);
if (!NT_SUCCESS(st) || cb != Chunk) {
//
// If the read did not work, then report as IO error
//
RtlLeaveCriticalSection(&Pipe->CriticalSection);
m->Error = EIO;
return TRUE;
}
Pipe->ReadPointer = (PUCHAR)((ULONG_PTR)ReadDataPoint + Chunk);
} else {
Pipe->ReadPointer = (PUCHAR)((ULONG_PTR)ReadDataPoint + Chunk);
}
if (Pipe->ReadPointer > &Pipe->Buffer[Pipe->BufferSize - 1]) {
Pipe->ReadPointer = &Pipe->Buffer[0];
}
//
// Adjust DataInPipe to account for read. Then check if there
// are any writers present. Pick a writer and kick him
//
Pipe->DataInPipe -= LargestRead;
m->ReturnValue = LargestRead;
RoomInPipe = Pipe->BufferSize - Pipe->DataInPipe;
// Update atime in IoNode - done in subsystem for local pipes
NtQuerySystemTime(&Time);
if ( !RtlTimeToSecondsSince1970(&Time, &PosixTime) ) {
PosixTime = 0L; // Time not within range of 1970 - 2105
}
RtlEnterCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
Fd->SystemOpenFileDesc->IoNode->AccessDataTime = PosixTime;
RtlLeaveCriticalSection(&Fd->SystemOpenFileDesc->IoNode->IoNodeLock);
//
// Check for WaitingWriters. If any are found, then kick em
//
RtlEnterCriticalSection(&BlockLock);
if (!IsListEmpty(&Pipe->WaitingWriters)) {
//
// If there are waiting writers, then pick a writer
// and unblock him. The first writer whose current
// write count that is less than or equal to the room
// in pipe is chosen.
//
Next = Pipe->WaitingWriters.Flink;
while (Next != &Pipe->WaitingWriters) {
IntCb = CONTAINING_RECORD(Next, INTCB, Links);
WaitingM = IntCb->IntMessage;
WaitingArgs = &WaitingM->u.Read;
WaitingWriter = (PPSX_PROCESS) IntCb->IntContext;
if (WaitingArgs->Nbytes <= RoomInPipe) {
RtlLeaveCriticalSection(&Pipe->CriticalSection);
UnblockProcess(WaitingWriter,
IoCompletionInterrupt, TRUE, 0);
return TRUE;
}
Next = Next->Flink;
}
}
RtlLeaveCriticalSection(&BlockLock);
RtlLeaveCriticalSection(&Pipe->CriticalSection);
return TRUE;
}
BOOLEAN
LocalPipeDup(
IN PPSX_PROCESS p,
IN OUT PPSX_API_MSG m,
IN PFILEDESCRIPTOR Fd,
IN PFILEDESCRIPTOR FdDup
)
{
PPSX_DUP_MSG args;
PLOCAL_PIPE Pipe;
args = &m->u.Dup;
//
// Copy contents of source file descriptor
// Note that FD_CLOEXEC must be CLEAR in FdDup.
//
*FdDup = *Fd;
FdDup->Flags &= ~PSX_FD_CLOSE_ON_EXEC;
//
// Increment reference count assocated with the SystemOpenFile
// descriptor for this file.
//
RtlEnterCriticalSection(&SystemOpenFileLock);
Fd->SystemOpenFileDesc->HandleCount++;
RtlLeaveCriticalSection(&SystemOpenFileLock);
Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
RtlEnterCriticalSection(&Pipe->CriticalSection);
if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
++Pipe->ReadHandleCount;
}
if (Fd->SystemOpenFileDesc->Flags & PSX_FD_WRITE) {
++Pipe->WriteHandleCount;
}
RtlLeaveCriticalSection(&Pipe->CriticalSection);
return TRUE;
}
//
// Named Pipes are very similar to local pipes
// once the opens have completed. For that reason,
// they share read/write io routines.
//
VOID
NamedPipeOpenHandler(
IN PPSX_PROCESS p,
IN PINTCB IntControlBlock,
IN PSX_INTERRUPTREASON InterruptReason,
IN int Signal // Signal causing interruption, if any
)
/*++
Routine Description:
This procedure is called when a process waiting for a named pipe
open to complete is either interrupted, or the pipe is opened.
Arguments:
p - Supplies the address of the process being interrupted.
IntControlBlock - Supplies the address of the interrupt control block.
InterruptReason - Supplies the reason that this process is being
interrupted. Not used in this handler.
Return Value:
None.
--*/
{
PFILEDESCRIPTOR Fd;
PPSX_API_MSG m;
PPSX_OPEN_MSG args;
PLOCAL_PIPE Pipe;
PLIST_ENTRY ListToScan;
PPSX_PROCESS Waiter;
PPSX_API_MSG WaitingM;
PLIST_ENTRY Next;
PINTCB IntCb;
RtlLeaveCriticalSection(&BlockLock);
m = IntControlBlock->IntMessage;
args = &m->u.Open;
RtlFreeHeap(PsxHeap, 0, IntControlBlock);
if (InterruptReason == SignalInterrupt) {
//
// The open was interrupted by a signal. Bail out of
// service by closing the half opened pipe and let
// the interrupt be handled
//
m->Error = EINTR;
m->Signal = Signal;
DeallocateFd(p, m->ReturnValue);
ApiReply(p, m, NULL);
RtlFreeHeap(PsxHeap, 0,m);
return;
}
//
// This Open Should be completed.
// Determine the list to scan to
// see if more opens should be completed
// at this time.
//
Fd = FdIndexToFd(p, m->ReturnValue);
ASSERT(Fd);
Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
//
// The list to scan is the list this process just came off
//
if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
ListToScan = &Pipe->WaitingReaders;
} else {
ListToScan = &Pipe->WaitingWriters;
}
RtlEnterCriticalSection(&Pipe->CriticalSection);
RtlEnterCriticalSection(&BlockLock);
if (!IsListEmpty(ListToScan)) {
//
// Scan list to see if there are processes waiting in an
// open whose wait can be satisfied.
//
Next = ListToScan->Flink;
while ( Next != ListToScan ) {
IntCb = CONTAINING_RECORD(Next,INTCB,Links);
WaitingM = IntCb->IntMessage;
if ( WaitingM->ApiNumber == PsxOpenApi ) {
Waiter = (PPSX_PROCESS) IntCb->IntContext;
RtlLeaveCriticalSection(&Pipe->CriticalSection);
UnblockProcess(Waiter, IoCompletionInterrupt, TRUE, 0);
ApiReply(p, m, NULL);
RtlFreeHeap(PsxHeap, 0, m);
return;
}
Next = Next->Flink;
}
}
RtlLeaveCriticalSection(&BlockLock);
RtlLeaveCriticalSection(&Pipe->CriticalSection);
ApiReply(p, m, NULL);
RtlFreeHeap(PsxHeap, 0, m);
}
BOOLEAN
NamedPipeOpenNewHandle (
IN PPSX_PROCESS p,
IN PFILEDESCRIPTOR Fd,
IN OUT PPSX_API_MSG m
)
{
NTSTATUS Status;
PLOCAL_PIPE Pipe;
PULONG CountToTest;
PLIST_ENTRY ListToBlockOn;
PLIST_ENTRY ListToScan;
PPSX_PROCESS Waiter;
PPSX_API_MSG WaitingM;
PLIST_ENTRY Next;
PINTCB IntCb;
Pipe = (PLOCAL_PIPE)Fd->SystemOpenFileDesc->IoNode->Context;
LocalPipeNewHandle(p, Fd);
if ((Fd->SystemOpenFileDesc->Flags & (PSX_FD_READ | PSX_FD_WRITE)) ==
(PSX_FD_READ | PSX_FD_WRITE)) {
return TRUE;
}
if (Fd->SystemOpenFileDesc->Flags & PSX_FD_NOBLOCK) {
if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
return TRUE;
} else {
RtlEnterCriticalSection(&Pipe->CriticalSection);
if (!Pipe->ReadHandleCount) {
m->Error = ENXIO;
RtlLeaveCriticalSection(&Pipe->CriticalSection);
DeallocateFd(p,m->ReturnValue);
} else {
RtlLeaveCriticalSection(&Pipe->CriticalSection);
}
return TRUE;
}
} else {
//
// Pipe is not being opened O_NONBLOCK. If pipe is being opened
// for read, then wait for a writer; otherwise, wait for
// a reader
//
if (Fd->SystemOpenFileDesc->Flags & PSX_FD_READ) {
CountToTest = &Pipe->WriteHandleCount;
ListToBlockOn = &Pipe->WaitingReaders;
ListToScan = &Pipe->WaitingWriters;
} else {
CountToTest = &Pipe->ReadHandleCount;
ListToBlockOn = &Pipe->WaitingWriters;
ListToScan = &Pipe->WaitingReaders;
}
RtlEnterCriticalSection(&Pipe->CriticalSection);
if (!*CountToTest) {
Status = BlockProcess(p, (PVOID)p, NamedPipeOpenHandler, m,
ListToBlockOn, &Pipe->CriticalSection);
if (!NT_SUCCESS(Status)) {
m->Error = PsxStatusToErrno(Status);
return TRUE;
}
//
// The process is successfully blocked -- do not reply to the api
// request.
//
return FALSE;
} else {
RtlEnterCriticalSection(&BlockLock);
if (!IsListEmpty(ListToScan)) {
//
// Scan list to see if there are processes waiting in an
// open whose wait can be satisfied.
//
Next = ListToScan->Flink;
while (Next != ListToScan) {
IntCb = CONTAINING_RECORD(Next,INTCB,Links);
WaitingM = IntCb->IntMessage;
if (WaitingM->ApiNumber == PsxOpenApi) {
Waiter = (PPSX_PROCESS) IntCb->IntContext;
RtlLeaveCriticalSection(&Pipe->CriticalSection);
UnblockProcess(Waiter, IoCompletionInterrupt, TRUE, 0);
return TRUE;
}
Next = Next->Flink;
}
}
RtlLeaveCriticalSection(&BlockLock);
}
RtlLeaveCriticalSection(&Pipe->CriticalSection);
return TRUE;
}
}
VOID
NamedPipeLastClose (
IN PPSX_PROCESS p,
IN PSYSTEMOPENFILE SystemOpenFile
)
/*++
Routine Description:
This function is called when the last handle to a local
pipe is closed. Its function is to tear down the pipe.
Arguments:
SystemOpenFile - Supplies the system open file describing the
pipe being closed.
Return Value:
None.
--*/
{
NTSTATUS st;
st = NtClose(SystemOpenFile->NtIoHandle);
ASSERT(NT_SUCCESS(st));
}
PSXIO_VECTORS NamedPipeVectors = {
NamedPipeOpenNewHandle,
LocalPipeNewHandle,
LocalPipeClose,
NamedPipeLastClose,
LocalPipeIoNodeClose,
LocalPipeRead,
LocalPipeWrite,
LocalPipeDup,
LocalPipeLseek,
LocalPipeStat
};