1325 lines
33 KiB
C
1325 lines
33 KiB
C
/*++
|
||
|
||
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
|
||
};
|