|
|
/*++
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 };
|