mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3435 lines
84 KiB
3435 lines
84 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
vrnmpipe.c
|
|
|
|
Abstract:
|
|
|
|
Contains Named Pipe function handlers for Vdm Redir support. This module
|
|
contains the following Vr (VdmRedir) routines:
|
|
|
|
Contents:
|
|
VrGetNamedPipeInfo
|
|
VrGetNamedPipeHandleState
|
|
VrSetNamedPipeHandleState
|
|
VrPeekNamedPipe
|
|
VrTransactNamedPipe
|
|
VrCallNamedPipe
|
|
VrWaitNamedPipe
|
|
VrNetHandleGetInfo
|
|
VrNetHandleSetInfo
|
|
VrReadWriteAsyncNmPipe
|
|
VrNmPipeInterrupt
|
|
VrTerminateNamedPipes
|
|
VrCancelPipeIo
|
|
|
|
There are a couple of extra routines which must be called on open and close.
|
|
Because these routines (in Dos Emulator) are general purpose, our open
|
|
and close routines will be called for every file open/handle close. We
|
|
must check that the operation is being performed on a named pipe entity.
|
|
The routines are:
|
|
|
|
VrAddOpenNamedPipeInfo
|
|
VrRemoveOpenNamedPipeInfo
|
|
|
|
Because named pipes are now opened in overlapped I/O mode, in case an app
|
|
wishes to perform an asynchronous read or write operation, we must provide
|
|
our own read/write routines for synchronously reading a pipe. If we just
|
|
left this to the standard read/write routines in DEM, they would return an
|
|
error because the handles were opened with FLAG_FILE_OVERLAPPED and the
|
|
operations are performed with the LPOVERLAPPED parameter set to NULL
|
|
|
|
VrReadNamedPipe
|
|
VrWriteNamedPipe
|
|
|
|
A couple of helper routines which are callable from outside of this module:
|
|
|
|
VrIsNamedPipeName
|
|
VrIsNamedPipeHandle
|
|
VrConvertLocalNtPipeName
|
|
|
|
Private (Vrp) routines:
|
|
|
|
VrpAsyncNmPipeThread
|
|
VrpSnapshotEventList
|
|
VrpSearchForRequestByEventHandle
|
|
VrpCompleteAsyncRequest
|
|
VrpQueueAsyncRequest
|
|
VrpDequeueAsyncRequest
|
|
VrpFindCompletedRequest
|
|
VrpAddOpenNamedPipeInfo
|
|
VrpGetOpenNamedPipeInfo
|
|
VrpRemoveOpenNamedPipeInfo
|
|
RememberPipeIo
|
|
ForgetPipeIo
|
|
|
|
Author:
|
|
|
|
Richard L Firth (rfirth) 10-Sep-1991
|
|
|
|
Environment:
|
|
|
|
Any 32-bit flat address space
|
|
|
|
Notes:
|
|
|
|
This module implements client-side named pipe support for the VDM process.
|
|
Client-side named pipes are opened using the standard DOS open call (INT 21/
|
|
ah=3dh) from a DOS app. The actual open is performed in the 32-bit context
|
|
where a 32-bit handle is returned. This is put in the DOS context SFT and
|
|
DOS returns an 8-bit J(ob) F(ile) N(umber) which the app then uses in other
|
|
named pipe calls. The redir, which handles named pipe requests apart from
|
|
open and close, must map the 8-bit JFN to the original 32-bit handle using
|
|
a routine exported from DOS. The handle is then stored in BP:BX and control
|
|
passed here.
|
|
|
|
When an open succeeds, we add an OPEN_NAMED_PIPE_INFO structure to a list
|
|
of structures. This maps the handle and name (for DosQNmPipeInfo). We don't
|
|
expect to have very many of these structures at any one time, so they are
|
|
singly linked and sequentially traversed using the handle as a key
|
|
|
|
This code assumes that only one process at a time will be updating the list
|
|
of structures and that any non-stack data items in this module will be
|
|
replicated to all processes which use these functions (Ie the data is NOT
|
|
shared)
|
|
|
|
Revision History:
|
|
|
|
10-Sep-1991 RFirth
|
|
Created
|
|
|
|
--*/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h> // ASSERT, DbgPrint
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#include <softpc.h> // x86 virtual machine definitions
|
|
#include <vrdlctab.h>
|
|
#include <vdmredir.h> // common Vdm Redir stuff
|
|
#include <vrinit.h> // VrQueueCompletionHandler
|
|
#include "vrdebug.h" // IF_DEBUG
|
|
#include "vrputil.h" // private utility prototypes
|
|
//#include <os2def.h>
|
|
//#include <bsedos.h> // PIPEINFO structure
|
|
#include <align.h>
|
|
#include <lmcons.h> // LM20_PATHLEN
|
|
#include <lmerr.h> // NERR_
|
|
#include <string.h> // Dos still dealing with ASCII
|
|
#include <dossvc.h> // PDEMEXTERR
|
|
#include <exterr.h> // extended error info
|
|
|
|
//
|
|
// the following 2 #undef's required because without them, insignia.h gives
|
|
// errors (BOOL previously typedef'd) when compiled for MIPS
|
|
//
|
|
|
|
#undef BOOL
|
|
#undef NT_INCLUDED
|
|
#include <insignia.h> // Insignia defines
|
|
#include <xt.h> // half_word
|
|
#include <ica.h> // ica_hw_interrupt
|
|
#include <idetect.h> // WaitIfIdle
|
|
#include <vrica.h> // call_ica_hw_interrupt
|
|
#include <vrnmpipe.h> // routine prototypes
|
|
|
|
#include <stdio.h>
|
|
|
|
//
|
|
// manifests
|
|
//
|
|
|
|
//#define NAMED_PIPE_TIMEOUT 300000 // 5 minutes
|
|
#define NAMED_PIPE_TIMEOUT INFINITE
|
|
|
|
//
|
|
// private data types
|
|
//
|
|
|
|
//
|
|
// OVERLAPPED_PIPE_IO - contains handle of thread issuing named pipe I/O request.
|
|
// If the app is later killed, we need to cancel any pending named pipe I/O
|
|
//
|
|
|
|
typedef struct _OVERLAPPED_PIPE_IO {
|
|
struct _OVERLAPPED_PIPE_IO* Next;
|
|
DWORD Thread;
|
|
BOOL Cancelled;
|
|
OVERLAPPED Overlapped;
|
|
} OVERLAPPED_PIPE_IO, *POVERLAPPED_PIPE_IO;
|
|
|
|
|
|
//
|
|
// private routine prototypes
|
|
//
|
|
|
|
#undef PRIVATE
|
|
#define PRIVATE /* static */ // actually, want to see routines in FREE build
|
|
|
|
PRIVATE
|
|
DWORD
|
|
VrpAsyncNmPipeThread(
|
|
IN LPVOID Parameters
|
|
);
|
|
|
|
PRIVATE
|
|
DWORD
|
|
VrpSnapshotEventList(
|
|
OUT LPHANDLE pList
|
|
);
|
|
|
|
PRIVATE
|
|
PDOS_ASYNC_NAMED_PIPE_INFO
|
|
VrpSearchForRequestByEventHandle(
|
|
IN HANDLE EventHandle
|
|
);
|
|
|
|
PRIVATE
|
|
VOID
|
|
VrpCompleteAsyncRequest(
|
|
IN PDOS_ASYNC_NAMED_PIPE_INFO pAsyncInfo
|
|
);
|
|
|
|
PRIVATE
|
|
VOID
|
|
VrpQueueAsyncRequest(
|
|
IN PDOS_ASYNC_NAMED_PIPE_INFO pAsyncInfo
|
|
);
|
|
|
|
PRIVATE
|
|
PDOS_ASYNC_NAMED_PIPE_INFO
|
|
VrpDequeueAsyncRequest(
|
|
IN PDOS_ASYNC_NAMED_PIPE_INFO pAsyncInfo
|
|
);
|
|
|
|
PRIVATE
|
|
PDOS_ASYNC_NAMED_PIPE_INFO
|
|
VrpFindCompletedRequest(
|
|
VOID
|
|
);
|
|
|
|
PRIVATE
|
|
BOOL
|
|
VrpAddOpenNamedPipeInfo(
|
|
IN HANDLE Handle,
|
|
IN LPSTR PipeName
|
|
);
|
|
|
|
PRIVATE
|
|
POPEN_NAMED_PIPE_INFO
|
|
VrpGetOpenNamedPipeInfo(
|
|
IN HANDLE Handle
|
|
);
|
|
|
|
PRIVATE
|
|
BOOL
|
|
VrpRemoveOpenNamedPipeInfo(
|
|
IN HANDLE Handle
|
|
);
|
|
|
|
PRIVATE
|
|
VOID
|
|
RememberPipeIo(
|
|
IN POVERLAPPED_PIPE_IO PipeIo
|
|
);
|
|
|
|
PRIVATE
|
|
VOID
|
|
ForgetPipeIo(
|
|
IN POVERLAPPED_PIPE_IO PipeIo
|
|
);
|
|
|
|
#if DBG
|
|
VOID DumpOpenPipeList(VOID);
|
|
VOID DumpRequestQueue(VOID);
|
|
#endif
|
|
|
|
//
|
|
// global data
|
|
//
|
|
|
|
DWORD VrPeekNamedPipeTickCount;
|
|
|
|
//
|
|
// private data
|
|
//
|
|
|
|
CRITICAL_SECTION VrNamedPipeCancelCritSec;
|
|
POVERLAPPED_PIPE_IO PipeIoQueue = NULL;
|
|
|
|
|
|
//
|
|
// Vdm Redir Named Pipe support routines
|
|
//
|
|
|
|
VOID
|
|
VrGetNamedPipeInfo(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs GetNamedPipeInfo (DosQNmPipeInfo) request on behalf of VDM redir
|
|
|
|
Arguments:
|
|
|
|
Function = 5F32h
|
|
|
|
ENTRY BP:BX = 32-bit Named Pipe handle
|
|
CX = Buffer size
|
|
DX = Info level
|
|
DS:SI = Buffer
|
|
|
|
EXIT CF = 1
|
|
AX = Error code
|
|
|
|
CF = 0
|
|
no error
|
|
AX = undefined
|
|
|
|
Return Value:
|
|
|
|
None. Returns values in VDM Ax and Flags registers
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE Handle;
|
|
DWORD Flags, OutBufferSize, InBufferSize, MaxInstances, CurInstances, bufLen;
|
|
PIPEINFO* PipeInfo;
|
|
BOOL Ok;
|
|
POPEN_NAMED_PIPE_INFO OpenNamedPipeInfo;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrGetNamedPipeInfo(0x%08x, %d, %04x:%04x, %d)\n",
|
|
HANDLE_FROM_WORDS(getBP(), getBX()),
|
|
getDX(),
|
|
getDS(),
|
|
getSI(),
|
|
getCX()
|
|
);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// bp:bx is 32-bit named pipe handle. Mapped from 8-bit handle in redir
|
|
//
|
|
|
|
Handle = HANDLE_FROM_WORDS(getBP(), getBX());
|
|
|
|
//
|
|
// we have to collect the info to put in the PIPEINFO structure from
|
|
// various sources - we stored the name (& name length) in a
|
|
// OPEN_NAMED_PIPE_INFO structure. The other stuff we get from
|
|
// GetNamedPipeInfo and GetNamedPipeHandleState
|
|
//
|
|
|
|
OpenNamedPipeInfo = VrpGetOpenNamedPipeInfo(Handle);
|
|
if (OpenNamedPipeInfo) {
|
|
bufLen = getCX();
|
|
if (bufLen >= sizeof(PIPEINFO)) {
|
|
Ok = GetNamedPipeInfo(Handle,
|
|
&Flags,
|
|
&OutBufferSize,
|
|
&InBufferSize,
|
|
&MaxInstances
|
|
);
|
|
if (Ok) {
|
|
|
|
//
|
|
// we are only interested in the current # instances of the
|
|
// named pipe from this next call
|
|
//
|
|
|
|
Ok = GetNamedPipeHandleState(Handle,
|
|
NULL,
|
|
&CurInstances,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0
|
|
);
|
|
if (Ok) {
|
|
PipeInfo = (PIPEINFO*)POINTER_FROM_WORDS(getDS(), getSI());
|
|
WRITE_WORD(&PipeInfo->cbOut, (OutBufferSize > 65535 ? 65535 : OutBufferSize));
|
|
WRITE_WORD(&PipeInfo->cbIn, (InBufferSize > 65535 ? 65535 : InBufferSize));
|
|
WRITE_BYTE(&PipeInfo->cbMaxInst, (MaxInstances > 255 ? 255 : MaxInstances));
|
|
WRITE_BYTE(&PipeInfo->cbCurInst, (CurInstances > 255 ? 255 : CurInstances));
|
|
WRITE_BYTE(&PipeInfo->cbName, OpenNamedPipeInfo->NameLength);
|
|
|
|
//
|
|
// copy name if enough space
|
|
//
|
|
|
|
if (bufLen - sizeof(PIPEINFO) >= OpenNamedPipeInfo->NameLength) {
|
|
strcpy(PipeInfo->szName, OpenNamedPipeInfo->Name);
|
|
}
|
|
setCF(0);
|
|
} else {
|
|
SET_ERROR(VrpMapLastError());
|
|
}
|
|
} else {
|
|
SET_ERROR(VrpMapLastError());
|
|
}
|
|
} else {
|
|
SET_ERROR(ERROR_BUFFER_OVERFLOW);
|
|
}
|
|
} else {
|
|
|
|
#if DBG
|
|
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrGetNamedPipeInfo: Error: can't map handle 0x%08x\n", Handle);
|
|
}
|
|
|
|
#endif
|
|
|
|
SET_ERROR(ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
if (getCF()) {
|
|
DbgPrint("VrGetNamedPipeInfo: returning ERROR: %d\n", getAX());
|
|
} else {
|
|
DbgPrint("VrGetNamedPipeInfo: returning OK. PIPEINFO:\n"
|
|
"cbOut %04x\n"
|
|
"cbIn %04x\n"
|
|
"cbMaxInst %02x\n"
|
|
"cbCurInst %02x\n"
|
|
"cbName %02x\n"
|
|
"szName %s\n",
|
|
READ_WORD(&PipeInfo->cbOut),
|
|
READ_WORD(&PipeInfo->cbIn),
|
|
READ_BYTE(&PipeInfo->cbMaxInst),
|
|
READ_BYTE(&PipeInfo->cbCurInst),
|
|
READ_BYTE(&PipeInfo->cbName),
|
|
READ_BYTE(&PipeInfo->szName)
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
VrGetNamedPipeHandleState(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs GetNamedPipeHandleState request on behalf of VDM redir
|
|
|
|
Arguments:
|
|
|
|
Function = 5F33h
|
|
|
|
ENTRY BP:BX = 32-bit Named Pipe handle
|
|
|
|
EXIT CF = 1
|
|
AX = Error code
|
|
|
|
CF = 0
|
|
AX = Pipe mode:
|
|
BSxxxWxRIIIIIIII
|
|
|
|
where:
|
|
B = Blocking mode. If B=1 the pipe is non blocking
|
|
S = Server end of pipe if 1
|
|
W = Pipe is written in message mode if 1 (else byte mode)
|
|
R = Pipe is read in message mode if 1 (else byte mode)
|
|
I = Pipe instances. Unlimited if 0xFF
|
|
|
|
Return Value:
|
|
|
|
None. Returns values in VDM Ax and Flags registers
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE Handle;
|
|
DWORD State, CurInstances, Flags, MaxInstances;
|
|
BOOL Ok;
|
|
WORD PipeHandleState;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrGetNamedPipeHandleState\n");
|
|
}
|
|
#endif
|
|
|
|
Handle = HANDLE_FROM_WORDS(getBP(), getBX());
|
|
Ok = GetNamedPipeHandleState(Handle,
|
|
&State,
|
|
&CurInstances,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0
|
|
);
|
|
if (Ok) {
|
|
Ok = GetNamedPipeInfo(Handle, &Flags, NULL, NULL, &MaxInstances);
|
|
if (Ok) {
|
|
|
|
//
|
|
// Create the Dos pipe handle state from the information gathered
|
|
//
|
|
|
|
PipeHandleState = (WORD)((MaxInstances > 255) ? 255 : (MaxInstances & 0xff))
|
|
| (WORD)((State & PIPE_NOWAIT) ? NP_NBLK : 0)
|
|
| (WORD)((State & PIPE_READMODE_MESSAGE) ? NP_RMESG : 0)
|
|
|
|
//
|
|
// BUGBUG - can't possibly be server end????
|
|
//
|
|
|
|
| (WORD)((Flags & PIPE_SERVER_END) ? NP_SERVER : 0)
|
|
| (WORD)((Flags & PIPE_TYPE_MESSAGE) ? NP_WMESG : 0)
|
|
;
|
|
|
|
setAX((WORD)PipeHandleState);
|
|
setCF(0);
|
|
} else {
|
|
SET_ERROR(VrpMapLastError());
|
|
}
|
|
} else {
|
|
SET_ERROR(VrpMapLastError());
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
VrSetNamedPipeHandleState(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs SetNamedPipeHandleState request on behalf of VDM redir
|
|
|
|
Arguments:
|
|
|
|
Function = 5F34h
|
|
|
|
ENTRY BP:BX = 32-bit Named Pipe handle
|
|
CX = Pipe mode to set
|
|
|
|
EXIT CF = 1
|
|
AX = Error code
|
|
|
|
CF = 0
|
|
AX = Pipe mode set
|
|
|
|
Return Value:
|
|
|
|
None. Returns values in VDM Ax and Flags registers
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE Handle = HANDLE_FROM_WORDS(getBP(), getBX());
|
|
BOOL Ok;
|
|
WORD DosPipeMode;
|
|
DWORD WinPipeMode;
|
|
|
|
#define ILLEGAL_NMP_SETMODE_BITS ~(NP_NBLK | NP_RMESG | NP_WMESG)
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrSetNamedPipeHandleState(0x%08x, %04x)\n", Handle, getCX());
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Convert the Dos pipe mode bits to Win32 pipe mode bits. We can only
|
|
// change the wait/no-wait status and the read mode of the pipe (byte
|
|
// or message)
|
|
//
|
|
|
|
DosPipeMode = getCX();
|
|
|
|
//
|
|
// catch disallowed flags
|
|
//
|
|
|
|
if (DosPipeMode & ILLEGAL_NMP_SETMODE_BITS) {
|
|
SET_ERROR(ERROR_INVALID_PARAMETER);
|
|
return;
|
|
}
|
|
|
|
WinPipeMode = ((DosPipeMode & NP_NBLK)
|
|
? PIPE_NOWAIT
|
|
: PIPE_WAIT)
|
|
| ((DosPipeMode & NP_RMESG)
|
|
? PIPE_READMODE_MESSAGE
|
|
: PIPE_READMODE_BYTE);
|
|
if (!(Ok = SetNamedPipeHandleState(Handle, &WinPipeMode, NULL, NULL))) {
|
|
|
|
#if DBG
|
|
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("Error: VrSetNamedPipeHandleState: returning %d\n", GetLastError());
|
|
}
|
|
|
|
#endif
|
|
|
|
SET_ERROR(VrpMapLastError());
|
|
} else {
|
|
setCF(0);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
VrPeekNamedPipe(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs PeekNamedPipe request on behalf of VDM redir
|
|
|
|
Arguments:
|
|
|
|
Function = 5F35h
|
|
|
|
ENTRY BP:BX = 32-bit Named Pipe handle
|
|
CX = Size of buffer for peek
|
|
DS:SI = Buffer address
|
|
|
|
EXIT CF = 1
|
|
AX = Error code
|
|
|
|
CF = 0
|
|
AX = Pipe status
|
|
BX = Number of bytes peeked into buffer
|
|
CX = Number of bytes in pipe
|
|
DX = Number of bytes in message
|
|
DI = Pipe status
|
|
DS:SI = Data peeked
|
|
|
|
Return Value:
|
|
|
|
None. Returns values in VDM Ax and Flags registers
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE Handle;
|
|
LPBYTE lpBuffer;
|
|
DWORD nBufferSize, BytesRead, BytesAvailable, BytesLeftInMessage;
|
|
BOOL Ok;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrPeekNamedPipe(0x%08x, %04x:%04x, %d)\n",
|
|
HANDLE_FROM_WORDS(getBP(), getBX()),
|
|
getDS(),
|
|
getSI(),
|
|
getCX()
|
|
);
|
|
}
|
|
#endif
|
|
|
|
Handle = HANDLE_FROM_WORDS(getBP(), getBX());
|
|
lpBuffer = (LPBYTE)POINTER_FROM_WORDS(getDS(), getSI());
|
|
nBufferSize = (DWORD)getCX();
|
|
Ok = PeekNamedPipe(Handle,
|
|
lpBuffer,
|
|
nBufferSize,
|
|
&BytesRead,
|
|
&BytesAvailable,
|
|
&BytesLeftInMessage
|
|
);
|
|
if (Ok) {
|
|
|
|
//
|
|
// Since we gave a 16-bit quantity for the buffer size, BytesRead
|
|
// cannot be >64K
|
|
//
|
|
|
|
setBX((WORD)BytesRead);
|
|
setCX((WORD)BytesAvailable);
|
|
|
|
//
|
|
// if message mode pipe, return total bytes in message (as opposed to
|
|
// NT's bytes LEFT in message)
|
|
//
|
|
|
|
setDX((WORD)(BytesLeftInMessage ? ((WORD)BytesLeftInMessage + (WORD)BytesRead) : 0));
|
|
|
|
//
|
|
// Not sure what this means. According to NETPIAPI.ASM, a 3 is returned
|
|
// on success, meaning status = connected. The named pipe statuses are
|
|
// (according to BSEDOS.H):
|
|
//
|
|
// NP_DISCONNECTED 1
|
|
// NP_LISTENING 2
|
|
// NP_CONNECTED 3
|
|
// NP_CLOSING 4
|
|
//
|
|
// Presumably, a client-side pipe can only be connected or the pipe is
|
|
// closed
|
|
//
|
|
|
|
setDI(NP_CONNECTED);
|
|
setCF(0);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrPeekNamedPipe: Ok: %d bytes peeked, %d avail, %d left in message\n",
|
|
BytesRead,
|
|
BytesAvailable,
|
|
BytesLeftInMessage
|
|
);
|
|
}
|
|
#endif
|
|
|
|
} else {
|
|
SET_ERROR(VrpMapLastError());
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrPeekNamedPipe: Error %d\n", getAX());
|
|
}
|
|
#endif
|
|
|
|
BytesRead = 0;
|
|
}
|
|
|
|
//
|
|
// idle processing - only idle if there is nothing to return (including
|
|
// an error occurred)
|
|
//
|
|
// For now, allow 10(!) peeks per second - on ALL pipe handles
|
|
//
|
|
|
|
if (!BytesRead) {
|
|
if (GetTickCount() - VrPeekNamedPipeTickCount < 100) {
|
|
WaitIfIdle();
|
|
}
|
|
}
|
|
VrPeekNamedPipeTickCount = GetTickCount();
|
|
}
|
|
|
|
|
|
VOID
|
|
VrTransactNamedPipe(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs TransactNamedPipe request on behalf of VDM redir
|
|
|
|
Arguments:
|
|
|
|
Function = 5F36h
|
|
|
|
ENTRY BP:BX = 32-bit Named Pipe handle
|
|
CX = Transmit buffer length
|
|
DX = Receive buffer length
|
|
DS:SI = Transmit buffer
|
|
ES:DI = Receive buffer
|
|
|
|
EXIT CF = 1
|
|
AX = Error code
|
|
|
|
CF = 0
|
|
CX = Number of bytes in Receive buffer
|
|
|
|
|
|
Return Value:
|
|
|
|
None. Returns values in VDM Ax and Flags registers
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD BytesRead;
|
|
BOOL Ok;
|
|
OVERLAPPED_PIPE_IO pipeio;
|
|
DWORD Error;
|
|
HANDLE Handle;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrTransactNamedPipe(0x%08x, TxLen=%d, TxBuf=%04x:%04x, RxLen=%d, RxBuf=%04x:%04x)\n",
|
|
HANDLE_FROM_WORDS(getBP(), getBX()),
|
|
getCX(),
|
|
getDS(),
|
|
getSI(),
|
|
getDX(),
|
|
getES(),
|
|
getDI()
|
|
);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// now that we are opening named pipes with FLAG_FILE_OVERLAPPED, we have
|
|
// to perform every I/O operation with an OVERLAPPED structure. We are only
|
|
// interested in the event handle. We create a new event for synchronous
|
|
// operation which requires an OVERLAPPED structure. Create the event to
|
|
// be manually reset - this way, if we wait on it & the read has already
|
|
// completed, the wait completes immediately. If we create an auto-reset
|
|
// event, then it may go back into the not-signalled state, causing us to
|
|
// wait forever for an event that has already occurred
|
|
//
|
|
|
|
RtlZeroMemory(&pipeio, sizeof(pipeio));
|
|
pipeio.Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (pipeio.Overlapped.hEvent != NULL) {
|
|
|
|
//
|
|
// collect arguments from registers and perform transact named pipe call
|
|
//
|
|
|
|
Handle = HANDLE_FROM_WORDS(getBP(), getBX());
|
|
RememberPipeIo(&pipeio);
|
|
Ok = TransactNamedPipe(Handle,
|
|
(LPVOID)POINTER_FROM_WORDS(getDS(), getSI()),
|
|
(DWORD)getCX(),
|
|
(LPVOID)POINTER_FROM_WORDS(getES(), getDI()),
|
|
(DWORD)getDX(),
|
|
&BytesRead,
|
|
&pipeio.Overlapped
|
|
);
|
|
Error = Ok ? NO_ERROR : GetLastError();
|
|
if (Error == ERROR_IO_PENDING) {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrTransactNamedPipe: Ok, Waiting on hEvent...\n");
|
|
}
|
|
#endif
|
|
|
|
Error = WaitForSingleObject(pipeio.Overlapped.hEvent, NAMED_PIPE_TIMEOUT);
|
|
}
|
|
ForgetPipeIo(&pipeio);
|
|
if (pipeio.Cancelled) {
|
|
Error = WAIT_TIMEOUT;
|
|
}
|
|
if (Error == NO_ERROR || Error == ERROR_MORE_DATA) {
|
|
GetOverlappedResult(Handle, &pipeio.Overlapped, &BytesRead, TRUE);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("WaitForSingleObject completed. BytesRead=%d\n", BytesRead);
|
|
}
|
|
#endif
|
|
|
|
setCX((WORD)BytesRead);
|
|
setAX((WORD)Error);
|
|
|
|
//
|
|
// if we are returning NO_ERROR then carry flag is clear, else we
|
|
// are returning ERROR_MORE_DATA: set carry flag
|
|
//
|
|
|
|
setCF(Error == ERROR_MORE_DATA);
|
|
} else {
|
|
|
|
//
|
|
// if we timed-out then close the pipe handle
|
|
//
|
|
|
|
if (Error == WAIT_TIMEOUT) {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrTransactNamedPipe: Wait timed out: closing handle %08x\n", Handle);
|
|
}
|
|
#endif
|
|
CloseHandle(Handle);
|
|
VrpRemoveOpenNamedPipeInfo(Handle);
|
|
} else {
|
|
Error = VrpMapDosError(Error);
|
|
}
|
|
SET_ERROR((WORD)Error);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrTransactNamedPipe: Error: %d\n", getAX());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// kill the event handle
|
|
//
|
|
|
|
CloseHandle(pipeio.Overlapped.hEvent);
|
|
} else {
|
|
|
|
//
|
|
// failed to create event handle
|
|
//
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("vdmredir: DosTransactNamedPipe couldn't create event error %d\n", GetLastError());
|
|
}
|
|
#endif
|
|
SET_ERROR(VrpMapLastError());
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
VrCallNamedPipe(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs CallNamedPipe request on behalf of VDM redir
|
|
|
|
Arguments:
|
|
|
|
Function = 5F37h
|
|
|
|
ENTRY DS:SI = Pointer to CallNmPipe structure:
|
|
|
|
DWORD Timeout; +0
|
|
LPWORD lpBytesRead; +4
|
|
WORD OutputBufferLen; +8
|
|
LPBYTE OutputBuffer; +10
|
|
WORD InputBufferLength; +14
|
|
LPBYTE InputBuffer; +16
|
|
LPSTR PipeName; +20
|
|
|
|
EXIT CF = 1
|
|
AX = Error code
|
|
|
|
CF = 0
|
|
CX = Bytes received
|
|
|
|
Return Value:
|
|
|
|
None. Returns values in VDM Ax and Flags registers
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL Ok;
|
|
DWORD BytesRead;
|
|
PDOS_CALL_NAMED_PIPE_STRUCT StructurePointer;
|
|
|
|
|
|
StructurePointer = (PDOS_CALL_NAMED_PIPE_STRUCT)POINTER_FROM_WORDS(getDS(), getSI());
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrCallNamedPipe(%s)\n", (LPSTR)READ_FAR_POINTER(&StructurePointer->lpPipeName));
|
|
}
|
|
#endif
|
|
|
|
Ok = CallNamedPipe((LPSTR)READ_FAR_POINTER(&StructurePointer->lpPipeName),
|
|
READ_FAR_POINTER(&StructurePointer->lpInBuffer),
|
|
READ_WORD(&StructurePointer->nInBufferLen),
|
|
READ_FAR_POINTER(&StructurePointer->lpOutBuffer),
|
|
READ_WORD(&StructurePointer->nOutBufferLen),
|
|
&BytesRead,
|
|
READ_DWORD(&StructurePointer->Timeout)
|
|
);
|
|
if (!Ok) {
|
|
SET_ERROR(VrpMapLastError());
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrCallNamedPipe: Error: CallNamedPipe returns %u\n", getAX());
|
|
}
|
|
#endif
|
|
} else {
|
|
WRITE_WORD(READ_FAR_POINTER(&StructurePointer->lpBytesRead), (WORD)BytesRead);
|
|
setCX((WORD)BytesRead);
|
|
setCF(0);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrCallNamedPipe: Ok\n");
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
VrWaitNamedPipe(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs WaitNamedPipe request on behalf of VDM redir. We assume that the
|
|
name we are getting is \\computer\pipe\name, anything else is invalid
|
|
|
|
Arguments:
|
|
|
|
Function = 5F38h
|
|
|
|
ENTRY BX:CX = Timeout
|
|
DS:DX = Pipe name
|
|
|
|
EXIT CF = 1
|
|
AX = Error code
|
|
|
|
CF = 0
|
|
No error
|
|
|
|
Return Value:
|
|
|
|
None. Returns values in VDM Ax and Flags registers
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL Ok;
|
|
|
|
//
|
|
// BUGBUG - should really perform DosPathCanonicalization on input string -
|
|
// DOS redir would convert eg //server/pipe\foo.bar into \\SERVER\PIPE\FOO.BAR
|
|
// if it makes any difference
|
|
//
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrWaitNamedPipe(%s, %d)\n",
|
|
LPSTR_FROM_WORDS(getDS(), getDX()),
|
|
DWORD_FROM_WORDS(getBX(), getCX())
|
|
);
|
|
}
|
|
#endif
|
|
|
|
Ok = WaitNamedPipe(LPSTR_FROM_WORDS(getDS(), getDX()),
|
|
DWORD_FROM_WORDS(getBX(), getCX())
|
|
);
|
|
if (!Ok) {
|
|
SET_ERROR(VrpMapLastError());
|
|
} else {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("WaitNamedPipe returns TRUE\n");
|
|
}
|
|
#endif
|
|
setAX(0);
|
|
setCF(0);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
VrNetHandleGetInfo(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs local NetHandleGetInfo on behalf of the Vdm client
|
|
|
|
Arguments:
|
|
|
|
Function = 5F3Ch
|
|
|
|
ENTRY BP:BX = 32-bit Named Pipe handle
|
|
CX = Buffer length
|
|
SI = Level (1)
|
|
DS:DX = Buffer
|
|
|
|
EXIT CX = size of required buffer (whether we got it or not)
|
|
CF = 1
|
|
AX = Error code
|
|
|
|
CF = 0
|
|
indicated stuff put in buffer
|
|
|
|
Return Value:
|
|
|
|
None. Results returned via VDM registers or in VDM memory, according to
|
|
request
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE Handle;
|
|
DWORD Level;
|
|
DWORD BufLen;
|
|
BOOL Ok;
|
|
DWORD CollectCount;
|
|
DWORD CollectTime;
|
|
LPVDM_HANDLE_INFO_1 StructurePointer;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrNetHandleGetInfo\n");
|
|
}
|
|
#endif
|
|
|
|
Handle = HANDLE_FROM_WORDS(getBP(), getBX());
|
|
Level = (DWORD)getSI();
|
|
if (Level == 1) {
|
|
BufLen = (DWORD)getCX();
|
|
if (BufLen >= sizeof(VDM_HANDLE_INFO_1)) {
|
|
|
|
//
|
|
// BUGBUG - the information we are interested in cannot be returned
|
|
// if the client and server are on the same machine, or if this is
|
|
// the server end of the pipe???
|
|
//
|
|
|
|
Ok = GetNamedPipeHandleState(Handle,
|
|
NULL, // not interested in state
|
|
NULL, // ditto curInstances
|
|
&CollectCount,
|
|
&CollectTime,
|
|
NULL, // not interested in client app name
|
|
0
|
|
);
|
|
if (!Ok) {
|
|
SET_ERROR(VrpMapLastError());
|
|
} else {
|
|
StructurePointer = (LPVDM_HANDLE_INFO_1)POINTER_FROM_WORDS(getDS(), getDX());
|
|
StructurePointer->CharTime = CollectTime;
|
|
StructurePointer->CharCount = (WORD)CollectCount;
|
|
setCF(0);
|
|
}
|
|
} else {
|
|
SET_ERROR(NERR_BufTooSmall);
|
|
}
|
|
} else {
|
|
SET_ERROR(ERROR_INVALID_LEVEL);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
VrNetHandleSetInfo(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs local NetHandleSetInfo on behalf of the Vdm client
|
|
|
|
Arguments:
|
|
|
|
Function = 5F3Bh
|
|
|
|
ENTRY BP:BX = 32-bit Named Pipe handle
|
|
CX = Buffer length
|
|
SI = Level (1)
|
|
DI = Parmnum
|
|
DS:DX = Buffer
|
|
|
|
EXIT CF = 1
|
|
AX = Error code
|
|
|
|
CF = 0
|
|
Stuff from buffer set
|
|
|
|
Return Value:
|
|
|
|
None. Results returned via VDM registers or in VDM memory, according to
|
|
request
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE Handle;
|
|
DWORD Level;
|
|
DWORD BufLen;
|
|
BOOL Ok;
|
|
DWORD Data;
|
|
DWORD ParmNum;
|
|
LPBYTE Buffer;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrNetHandleGetInfo\n");
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
|
|
Handle = HANDLE_FROM_WORDS(getBP(), getBX());
|
|
Level = (DWORD)getSI();
|
|
Buffer = LPBYTE_FROM_WORDS(getDS(), getDX());
|
|
if (Level == 1) {
|
|
BufLen = (DWORD)getCX();
|
|
|
|
//
|
|
// ParmNum can be 1 (CharTime) or 2 (CharCount), Can't be 0 (set
|
|
// everything)
|
|
//
|
|
|
|
ParmNum = (DWORD)getDI();
|
|
if (!--ParmNum) {
|
|
if (BufLen < sizeof(((LPVDM_HANDLE_INFO_1)0)->CharTime)) {
|
|
SET_ERROR(NERR_BufTooSmall);
|
|
return ;
|
|
}
|
|
Data = (DWORD)*(LPDWORD)Buffer;
|
|
} else if (!--ParmNum) {
|
|
if (BufLen < sizeof(((LPVDM_HANDLE_INFO_1)0)->CharCount)) {
|
|
SET_ERROR(NERR_BufTooSmall);
|
|
return ;
|
|
}
|
|
Data = (DWORD)*(LPWORD)Buffer;
|
|
} else {
|
|
SET_ERROR(ERROR_INVALID_PARAMETER);
|
|
return ;
|
|
}
|
|
|
|
//
|
|
// BUGBUG - the information we are interested in cannot be set
|
|
// if the client and server are on the same machine, or if this is
|
|
// the server end of the pipe???
|
|
//
|
|
|
|
Ok = SetNamedPipeHandleState(Handle,
|
|
NULL, // not interested in mode
|
|
(LPDWORD)((ParmNum == 1) ? &Data : NULL),
|
|
(LPDWORD)((ParmNum == 2) ? &Data : NULL)
|
|
);
|
|
if (!Ok) {
|
|
SET_ERROR(VrpMapLastError());
|
|
} else {
|
|
setCF(0);
|
|
}
|
|
} else {
|
|
SET_ERROR(ERROR_INVALID_LEVEL);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Request Queue. This queue holds a singly linked list of async named pipe
|
|
// read/write requests. The async thread will search this list when an async
|
|
// read or write completes (the event is signalled). It then sets up the
|
|
// information for the call back to the VDM and dequeues the request info.
|
|
// Because we can have the async thread and the request thread simultaneously
|
|
// accessing the queue, it is protected by a critical section
|
|
//
|
|
|
|
CRITICAL_SECTION VrNmpRequestQueueCritSec;
|
|
PDOS_ASYNC_NAMED_PIPE_INFO RequestQueueHead = NULL;
|
|
PDOS_ASYNC_NAMED_PIPE_INFO RequestQueueTail = NULL;
|
|
HANDLE VrpNmpSomethingToDo;
|
|
|
|
|
|
VOID
|
|
VrReadWriteAsyncNmPipe(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs asynchronous read or write of a message mode named pipe on behalf
|
|
of the VDM DOS application
|
|
|
|
Arguments:
|
|
|
|
None. All arguments are extracted from DOS registers/memory.
|
|
|
|
These calls are made through int 2fh/ax=function code, not int 21h/ah=5fh
|
|
|
|
AX = 1186h DosReadAsyncNmPipe
|
|
118Fh DosWriteAsyncNmPipe
|
|
1190h DosReadAsyncNmPipe2
|
|
1191h DosWriteAsyncNmPipe2
|
|
|
|
BP:BX = 32-bit Named Pipe Handle
|
|
|
|
DS:SI = DOS_ASYNC_NAMED_PIPE_STRUCT
|
|
DD address of returned bytes read
|
|
DW size of caller's buffer
|
|
DD address of caller's buffer
|
|
DD address of returned error code
|
|
DD address of Asynchronous Notification Routine
|
|
DW named pipe handle
|
|
DD address of caller's 'semaphore'
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE Handle;
|
|
|
|
//
|
|
// Type is type of request - read or write, standard or 2 (meaning the
|
|
// request has an associated 'semaphore' which must be cleared)
|
|
//
|
|
|
|
DWORD Type;
|
|
|
|
//
|
|
// StructurePointer is 32-bit flat pointer to structure in DOS memory
|
|
// containing request parameters
|
|
//
|
|
|
|
PDOS_ASYNC_NAMED_PIPE_STRUCT StructurePointer;
|
|
|
|
//
|
|
// pAsyncInfo is a pointer to the request packet we stick on the request
|
|
// queue
|
|
//
|
|
|
|
PDOS_ASYNC_NAMED_PIPE_INFO pAsyncInfo;
|
|
|
|
//
|
|
// pipeInfo is a pointer to the information we created/stored when the
|
|
// named pipe was opened. We just need this to check the handle's valid
|
|
//
|
|
|
|
POPEN_NAMED_PIPE_INFO pipeInfo;
|
|
|
|
WORD length;
|
|
LPBYTE buffer;
|
|
DWORD error;
|
|
BOOL ok;
|
|
HANDLE hEvent;
|
|
DWORD bytesTransferred;
|
|
|
|
//
|
|
// hThread and tid: these must be kept alive so long as the async named
|
|
// pipe (completion) thread exists. tid can be used with ResumeThread and
|
|
// SuspendThread as we may see fit
|
|
//
|
|
|
|
static HANDLE hThread = NULL;
|
|
static DWORD tid;
|
|
|
|
//
|
|
// get info from registers and the async named pipe structure
|
|
//
|
|
|
|
Handle = HANDLE_FROM_WORDS(getBP(), getBX());
|
|
pipeInfo = VrpGetOpenNamedPipeInfo(Handle);
|
|
Type = (DWORD)getAX() & 0xff; // 0x86, 0x8f, 0x90 or 0x91
|
|
StructurePointer = (PDOS_ASYNC_NAMED_PIPE_STRUCT)POINTER_FROM_WORDS(getDS(), getSI());
|
|
length = READ_WORD(&StructurePointer->BufferLength);
|
|
buffer = READ_FAR_POINTER(&StructurePointer->lpBuffer);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint( "\n"
|
|
"VrReadWriteAsyncNmPipe (%04x) [%s]:\n"
|
|
"DOS_READ_ASYNC_NAMED_PIPE_STRUCT @ %08x:\n"
|
|
"32-bit named pipe handle . . . . %08x\n"
|
|
"Address of returned bytes read . %04x:%04x\n"
|
|
"Size of caller's buffer. . . . . %04x\n"
|
|
"Address of caller's buffer . . . %04x:%04x\n"
|
|
"Address of returned error code . %04x:%04x\n"
|
|
"Address of ANR . . . . . . . . . %04x:%04x\n"
|
|
"Named pipe handle. . . . . . . . %04x\n"
|
|
"Address of caller's semaphore. . %04x:%04x\n"
|
|
"\n",
|
|
(DWORD)getAX(), // type of read/write request
|
|
Type == ANP_READ
|
|
? "READ"
|
|
: Type == ANP_WRITE
|
|
? "WRITE"
|
|
: Type == ANP_READ2
|
|
? "READ2"
|
|
: Type == ANP_WRITE2
|
|
? "WRITE2"
|
|
: "?????",
|
|
StructurePointer,
|
|
Handle,
|
|
(DWORD)GET_SELECTOR(&StructurePointer->lpBytesRead),
|
|
(DWORD)GET_OFFSET(&StructurePointer->lpBytesRead),
|
|
(DWORD)StructurePointer->BufferLength,
|
|
(DWORD)GET_SELECTOR(&StructurePointer->lpBuffer),
|
|
(DWORD)GET_OFFSET(&StructurePointer->lpBuffer),
|
|
(DWORD)GET_SELECTOR(&StructurePointer->lpErrorCode),
|
|
(DWORD)GET_OFFSET(&StructurePointer->lpErrorCode),
|
|
(DWORD)GET_SELECTOR(&StructurePointer->lpANR),
|
|
(DWORD)GET_OFFSET(&StructurePointer->lpANR),
|
|
(DWORD)StructurePointer->PipeHandle,
|
|
(DWORD)GET_SELECTOR(&StructurePointer->lpSemaphore),
|
|
(DWORD)GET_OFFSET(&StructurePointer->lpSemaphore)
|
|
);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// if we can't find this handle in our list of opened named pipes, return
|
|
// an error
|
|
//
|
|
|
|
if (!pipeInfo) {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadWriteAsyncNmPipe: Handle 0x%08x is invalid\n", Handle);
|
|
}
|
|
#endif
|
|
|
|
SET_ERROR(ERROR_INVALID_HANDLE);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// looks like we're going to make an async read/write request. Create the
|
|
// async thread if it doesn't already exist. Create also the "something to
|
|
// do" event. Create this as an auto reset event which is initially in the
|
|
// not-signalled state
|
|
//
|
|
|
|
if (hThread == NULL) {
|
|
VrpNmpSomethingToDo = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (VrpNmpSomethingToDo == NULL) {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadWriteAsyncNmPipe: Error: Couldn't create something-to-do event: %d\n",
|
|
GetLastError()
|
|
);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// return an out-of-resources error
|
|
//
|
|
|
|
SET_ERROR(ERROR_NOT_ENOUGH_MEMORY);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// we have the "something to do" event. Now create the thread
|
|
//
|
|
|
|
hThread = CreateThread(NULL,
|
|
0,
|
|
VrpAsyncNmPipeThread,
|
|
NULL,
|
|
0,
|
|
&tid
|
|
);
|
|
if (hThread == NULL) {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadWriteAsyncNmPipe: Error: Couldn't create thread: %d\n",
|
|
GetLastError()
|
|
);
|
|
}
|
|
#endif
|
|
|
|
CloseHandle(VrpNmpSomethingToDo);
|
|
SET_ERROR(ERROR_NOT_ENOUGH_MEMORY);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// allocate a structure in which to store the information required to
|
|
// complete the request (in the VDM)
|
|
//
|
|
|
|
pAsyncInfo = (PDOS_ASYNC_NAMED_PIPE_INFO)LocalAlloc(LMEM_FIXED, sizeof(DOS_ASYNC_NAMED_PIPE_INFO));
|
|
if (pAsyncInfo == NULL) {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadWriteAsyncNmPipe: Error: Couldn't allocate structure\n");
|
|
}
|
|
#endif
|
|
|
|
SET_ERROR(ERROR_NOT_ENOUGH_MEMORY);
|
|
return;
|
|
}
|
|
|
|
RtlZeroMemory(&pAsyncInfo->Overlapped, sizeof(pAsyncInfo->Overlapped));
|
|
|
|
//
|
|
// create a new event for this request - there can be multiple simultaneous
|
|
// requests per named pipe. The event is manual reset so that if the request
|
|
// completes before the WaitForMultipleObjects snaps the list, the event
|
|
// will stay reset and hence the wait will complete. If we created the event
|
|
// as auto-reset, it may get signalled, and go not-signalled before we wait
|
|
// on it, potentially causing an infinite wait
|
|
//
|
|
|
|
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (hEvent == NULL) {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadWriteAsyncNmPipe: Error: Couldn't create event: %d\n", GetLastError());
|
|
}
|
|
#endif
|
|
|
|
LocalFree((HLOCAL)pAsyncInfo);
|
|
|
|
//
|
|
// return approximation out-of-resources error
|
|
//
|
|
|
|
SET_ERROR(ERROR_NOT_ENOUGH_MEMORY);
|
|
return;
|
|
} else {
|
|
pAsyncInfo->Overlapped.hEvent = hEvent;
|
|
}
|
|
|
|
//
|
|
// set up rest of async operation info structure
|
|
//
|
|
|
|
pAsyncInfo->Completed = FALSE;
|
|
pAsyncInfo->Handle = Handle;
|
|
pAsyncInfo->Buffer = (DWORD)StructurePointer->lpBuffer;
|
|
pAsyncInfo->pBytesTransferred = READ_FAR_POINTER(&StructurePointer->lpBytesRead);
|
|
pAsyncInfo->pErrorCode = READ_FAR_POINTER(&StructurePointer->lpErrorCode);
|
|
pAsyncInfo->ANR = READ_DWORD(&StructurePointer->lpANR);
|
|
|
|
//
|
|
// if this is an AsyncNmPipe2 call then it has an associated semaphore
|
|
// handle. Earlier versions don't have a semaphore
|
|
//
|
|
|
|
if (Type == ANP_READ2 || Type == ANP_WRITE2) {
|
|
pAsyncInfo->Type2 = TRUE;
|
|
pAsyncInfo->Semaphore = READ_DWORD(&StructurePointer->lpSemaphore);
|
|
} else {
|
|
pAsyncInfo->Type2 = FALSE;
|
|
pAsyncInfo->Semaphore = (DWORD)NULL;
|
|
}
|
|
|
|
#if DBG
|
|
pAsyncInfo->RequestType = Type;
|
|
#endif
|
|
|
|
//
|
|
// add the completion info structure to the async thread's work queue
|
|
//
|
|
|
|
VrpQueueAsyncRequest(pAsyncInfo);
|
|
|
|
//
|
|
// Q: what happens if the request completes asynchronously before we finish
|
|
// this routine?
|
|
//
|
|
|
|
if (Type == ANP_READ || Type == ANP_READ2) {
|
|
ok = ReadFile(Handle,
|
|
buffer,
|
|
length,
|
|
&bytesTransferred,
|
|
&pAsyncInfo->Overlapped
|
|
);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadWriteAsyncNmPipe: ReadFile(%x, %x, %d, ...): %d\n",
|
|
Handle,
|
|
buffer,
|
|
length,
|
|
ok
|
|
);
|
|
}
|
|
#endif
|
|
|
|
} else {
|
|
ok = WriteFile(Handle,
|
|
buffer,
|
|
length,
|
|
&bytesTransferred,
|
|
&pAsyncInfo->Overlapped
|
|
);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadWriteAsyncNmPipe: WriteFile(%x, %x, %d, ...): %d\n",
|
|
Handle,
|
|
buffer,
|
|
length,
|
|
ok
|
|
);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
error = ok ? NO_ERROR : GetLastError();
|
|
|
|
//
|
|
// if we get ERROR_MORE_DATA then treat it as an error. GetOverlappedResult
|
|
// will give us the same error which we will return asynchronously
|
|
//
|
|
|
|
if (error != NO_ERROR && error != ERROR_IO_PENDING && error != ERROR_MORE_DATA) {
|
|
|
|
//
|
|
// we didn't get to start the I/O operation successfully, therefore
|
|
// we won't get called back, so we dequeue and free the completion
|
|
// structure and return the error
|
|
//
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadWriteAsyncNmPipe: Error: IO operation returns %d\n", error);
|
|
}
|
|
#endif
|
|
|
|
VrpDequeueAsyncRequest(pAsyncInfo);
|
|
CloseHandle(hEvent);
|
|
LocalFree(pAsyncInfo);
|
|
SET_ERROR((WORD)error);
|
|
} else {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadWriteAsyncNmPipe: IO operation started: returns %s\n",
|
|
error == ERROR_IO_PENDING ? "ERROR_IO_PENDING" : "NO_ERROR"
|
|
);
|
|
}
|
|
#endif
|
|
|
|
setCF(0);
|
|
}
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadWriteAsyncNmPipe: returning CF=%d, AX=%d\n", getCF(), getAX());
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
VrNmPipeInterrupt(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called from hardware interrupt BOP processing to check if there are any
|
|
async named pipe ANRs to call
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN
|
|
TRUE - there was an async named pipe operation to complete. The
|
|
VDM registers & data areas have been modified to indicate
|
|
that the named pipe ANR must be called
|
|
|
|
FALSE - no async named pipe processing to do. Interrupt must have
|
|
been generated by NetBios or DLC
|
|
|
|
--*/
|
|
|
|
{
|
|
PDOS_ASYNC_NAMED_PIPE_INFO pAsyncInfo;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrNmPipeInterrupt\n");
|
|
}
|
|
#endif
|
|
|
|
|
|
//
|
|
// locate the first async named pipe request packet that has completed and
|
|
// is waiting for interrupt processing (ie its ANR to be called)
|
|
//
|
|
|
|
pAsyncInfo = VrpFindCompletedRequest();
|
|
if (!pAsyncInfo) {
|
|
|
|
#if DBG
|
|
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrNmPipeInterrupt - nothing to do\n");
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// returning FALSE indicates that the hardware interrupt callback was
|
|
// not generated by async named pipe request completing
|
|
//
|
|
|
|
return FALSE;
|
|
} else {
|
|
|
|
//
|
|
// set the VDM registers to indicate a named pipe callback
|
|
//
|
|
|
|
setDS(HIWORD(pAsyncInfo->Buffer));
|
|
setSI(LOWORD(pAsyncInfo->Buffer));
|
|
setES(HIWORD(pAsyncInfo->Semaphore));
|
|
setDI(LOWORD(pAsyncInfo->Semaphore));
|
|
setCX(HIWORD(pAsyncInfo->ANR));
|
|
setBX(LOWORD(pAsyncInfo->ANR));
|
|
setAL((BYTE)pAsyncInfo->Type2);
|
|
SET_CALLBACK_NAMEPIPE();
|
|
|
|
//
|
|
// finished with this request packet, so dequeue and deallocate it
|
|
//
|
|
|
|
VrpDequeueAsyncRequest(pAsyncInfo);
|
|
CloseHandle(pAsyncInfo->Overlapped.hEvent);
|
|
LocalFree(pAsyncInfo);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrNmPipeInterrupt: Setting DOS Registers:\n"
|
|
"DS:SI=%04x:%04x, ES:DI=%04x:%04x, CX:BX=%04x:%04x, AL=%02x\n",
|
|
getDS(), getSI(),
|
|
getES(), getDI(),
|
|
getCX(), getBX(),
|
|
getAL()
|
|
);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// returning TRUE indicates that we have accepted a named pipe
|
|
// completion request
|
|
//
|
|
|
|
//VrDismissInterrupt();
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
VrTerminateNamedPipes(
|
|
IN WORD DosPdb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleans out all open named pipe info and pending async named pipe requests
|
|
when the owning DOS app terminates
|
|
|
|
Arguments:
|
|
|
|
DosPdb - PDB (DOS 'process' identifier) of terminating DOS process
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrTerminateNamedPipes\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
VOID
|
|
VrCancelPipeIo(
|
|
IN DWORD Thread
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
For all pending named pipe I/Os owned by Thread, mark them as cancelled
|
|
and signal the event in the OVERLAPPED structure, causing the wait to
|
|
terminate.
|
|
|
|
This thread may not have any outstanding named pipe I/O
|
|
|
|
Arguments:
|
|
|
|
Thread - pseudo-handle of thread owning named pipe I/O
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
POVERLAPPED_PIPE_IO ptr;
|
|
|
|
EnterCriticalSection(&VrNamedPipeCancelCritSec);
|
|
for (ptr = PipeIoQueue; ptr; ptr = ptr->Next) {
|
|
if (ptr->Thread == Thread) {
|
|
ptr->Cancelled = TRUE;
|
|
SetEvent(ptr->Overlapped.hEvent);
|
|
}
|
|
}
|
|
LeaveCriticalSection(&VrNamedPipeCancelCritSec);
|
|
}
|
|
|
|
|
|
#if _MSC_FULL_VER >= 13008827
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4715) // Not all control paths return (due to infinite loop)
|
|
#endif
|
|
|
|
PRIVATE
|
|
DWORD
|
|
VrpAsyncNmPipeThread(
|
|
IN LPVOID Parameters
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Waits for an asynchronous named pipe read or write operation to complete.
|
|
Loops forever, waiting on list of pending async (overlapped) named pipe
|
|
operations. If there are no more outstanding named pipe read/writes then
|
|
waits on VrpNmpSomethingToDo which is reset (put in not-signalled state)
|
|
when there are no packets left on the request queue
|
|
|
|
Arguments:
|
|
|
|
Parameters - unused parameter block
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
0
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD numberOfHandles;
|
|
DWORD index;
|
|
HANDLE eventList[MAXIMUM_ASYNC_PIPES + 1];
|
|
PDOS_ASYNC_NAMED_PIPE_INFO pRequest;
|
|
|
|
UNREFERENCED_PARAMETER(Parameters);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrAsyncNamedPipeThread: *** Started ***\n");
|
|
}
|
|
#endif
|
|
|
|
while (TRUE) {
|
|
|
|
//
|
|
// create an array of event handles. The first handle in the array is
|
|
// the "something to do" event. This will only be reset when the queue
|
|
// of requests changes from the empty set
|
|
//
|
|
|
|
numberOfHandles = VrpSnapshotEventList(eventList);
|
|
index = WaitForMultipleObjects(numberOfHandles, eventList, FALSE, INFINITE);
|
|
|
|
//
|
|
// if the index is 0, then the "something to do" event has been signalled,
|
|
// meaning that we have to snapshot a new event list and re-wait
|
|
//
|
|
|
|
if (index > 0 && index < numberOfHandles) {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpAsyncNmPipeThread: event #%d fired\n", index);
|
|
}
|
|
#endif
|
|
|
|
pRequest = VrpSearchForRequestByEventHandle(eventList[index]);
|
|
if (pRequest != NULL) {
|
|
VrpCompleteAsyncRequest(pRequest);
|
|
}
|
|
|
|
#if DBG
|
|
else {
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpAsyncNmPipeThread: Couldn't find request for handle 0x%08x\n",
|
|
eventList[index]
|
|
);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
} else if (index) {
|
|
|
|
//
|
|
// an error occurred
|
|
//
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpAsyncNmPipeThread: Error: WaitForMultipleObjects returns %d (%d)\n",
|
|
index,
|
|
GetLastError()
|
|
);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
#if DBG
|
|
else {
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpAsyncNmPipeThread: Something-to-do event fired\n");
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpAsyncNmPipeThread: *** Terminated ***\n");
|
|
}
|
|
#endif
|
|
|
|
return 0; // appease the compiler-god
|
|
}
|
|
|
|
#if _MSC_FULL_VER >= 13008827
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
|
|
PRIVATE
|
|
DWORD
|
|
VrpSnapshotEventList(
|
|
OUT LPHANDLE pList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds an array of event handles for those asynchronous named pipe I/O
|
|
requests which are still not completed (the Completed flag is FALSE).
|
|
The first event handle is always the "something to do" event
|
|
|
|
Arguments:
|
|
|
|
pList - pointer to callers list to build
|
|
|
|
Return Value:
|
|
|
|
DWORD
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD count = 1;
|
|
PDOS_ASYNC_NAMED_PIPE_INFO ptr;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpSnapshotEventList\n");
|
|
}
|
|
#endif
|
|
|
|
pList[0] = VrpNmpSomethingToDo;
|
|
EnterCriticalSection(&VrNmpRequestQueueCritSec);
|
|
for (ptr = RequestQueueHead; ptr; ptr = ptr->Next) {
|
|
if (ptr->Completed == FALSE) {
|
|
pList[count++] = ptr->Overlapped.hEvent;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&VrNmpRequestQueueCritSec);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpSnapshotEventList: returning %d events\n", count);
|
|
}
|
|
#endif
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
PDOS_ASYNC_NAMED_PIPE_INFO
|
|
VrpSearchForRequestByEventHandle(
|
|
IN HANDLE EventHandle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Searches the async request queue for the structure with this event handle.
|
|
If the structure is found, it is marked as Completed. The required structure
|
|
may NOT be located: this might occur when an item was removed from the list
|
|
due to an error in VrReadWriteAsyncNmPipe
|
|
|
|
Arguments:
|
|
|
|
EventHandle - to search for
|
|
|
|
Return Value:
|
|
|
|
PDOS_ASYNC_NAMED_PIPE_INFO
|
|
Success - the located structure
|
|
Failure - NULL
|
|
|
|
--*/
|
|
|
|
{
|
|
PDOS_ASYNC_NAMED_PIPE_INFO ptr;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpSearchForRequestByEventHandle(0x%08x)\n", EventHandle);
|
|
}
|
|
#endif
|
|
|
|
EnterCriticalSection(&VrNmpRequestQueueCritSec);
|
|
for (ptr = RequestQueueHead; ptr; ptr = ptr->Next) {
|
|
if (ptr->Overlapped.hEvent == EventHandle) {
|
|
ptr->Completed = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&VrNmpRequestQueueCritSec);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpLocateAsyncRequestByEventHandle returning 0x%08x: Request is %s\n",
|
|
ptr,
|
|
!ptr
|
|
? "NO REQUEST!!"
|
|
: ptr->RequestType == ANP_READ
|
|
? "READ"
|
|
: ptr->RequestType == ANP_WRITE
|
|
? "WRITE"
|
|
: ptr->RequestType == ANP_READ2
|
|
? "READ2"
|
|
: ptr->RequestType == ANP_WRITE2
|
|
? "WRITE2"
|
|
: "UNKNOWN REQUEST!!"
|
|
);
|
|
}
|
|
#endif
|
|
|
|
return ptr;
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
VOID
|
|
VrpCompleteAsyncRequest(
|
|
IN PDOS_ASYNC_NAMED_PIPE_INFO pAsyncInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Completes the asynchronous named pipe I/O request by getting the results
|
|
of the transfer and filling-in the error & bytes transferred fields of
|
|
the async named pipe info structure. If there is an ANR to be called, then
|
|
a simulated hardware interrupt request is generated to the VDM. If there
|
|
is no ANR to call then the async named pipe info structure is cleared out.
|
|
|
|
If there is an ANR, the request will be completed finally when it is
|
|
dequeued & freed by VrNmPipeInterrupt
|
|
|
|
Arguments:
|
|
|
|
pAsyncInfo - pointer to DOS_ASYNC_NAMED_PIPE_INFO structure to complete
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL ok;
|
|
DWORD bytesTransferred=0;
|
|
DWORD error;
|
|
|
|
#if DBG
|
|
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpCompleteAsyncRequest(0x%08x)\n", pAsyncInfo);
|
|
}
|
|
|
|
#endif
|
|
|
|
ok = GetOverlappedResult(pAsyncInfo->Handle,
|
|
&pAsyncInfo->Overlapped,
|
|
&bytesTransferred,
|
|
FALSE
|
|
);
|
|
error = ok ? NO_ERROR : GetLastError();
|
|
|
|
//
|
|
// update the VDM variables
|
|
//
|
|
|
|
WRITE_WORD(pAsyncInfo->pErrorCode, error);
|
|
WRITE_WORD(pAsyncInfo->pBytesTransferred, bytesTransferred);
|
|
|
|
#if DBG
|
|
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpCompleteAsyncRequest: error=%d, bytesTransferred=%d\n",
|
|
error,
|
|
bytesTransferred
|
|
);
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// if there is no ANR then we cannot make a call-back to DOS (error? DOS
|
|
// app polls 'semaphore'?) so close the event handle, dequeue the request
|
|
// packet and free it
|
|
//
|
|
|
|
if (!pAsyncInfo->ANR) {
|
|
|
|
#if DBG
|
|
|
|
PDOS_ASYNC_NAMED_PIPE_INFO ptr;
|
|
|
|
ptr = VrpDequeueAsyncRequest(pAsyncInfo);
|
|
if (ptr != pAsyncInfo) {
|
|
DbgPrint("*** Error: incorrect request packet dequeued ***\n");
|
|
DbgBreakPoint();
|
|
}
|
|
#else
|
|
|
|
VrpDequeueAsyncRequest(pAsyncInfo);
|
|
|
|
#endif
|
|
|
|
CloseHandle(pAsyncInfo->Overlapped.hEvent);
|
|
LocalFree(pAsyncInfo);
|
|
} else {
|
|
|
|
//
|
|
// interrupt the VDM. It must call back to find out what asynchronous
|
|
// processing there is to do
|
|
//
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpCompleteAsyncRequest: *** INTERRUPTING VDM ***\n");
|
|
}
|
|
#endif
|
|
|
|
VrQueueCompletionHandler(VrNmPipeInterrupt);
|
|
VrRaiseInterrupt();
|
|
}
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
VOID
|
|
VrpQueueAsyncRequest(
|
|
IN PDOS_ASYNC_NAMED_PIPE_INFO pAsyncInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a DOS_ASYNC_NAMED_PIPE_INFO structure to the end of the request queue.
|
|
The queue is protected by a critical section
|
|
|
|
Arguments:
|
|
|
|
pAsyncInfo - pointer to structure to add
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpQueueAsyncRequest\n");
|
|
}
|
|
#endif
|
|
|
|
EnterCriticalSection(&VrNmpRequestQueueCritSec);
|
|
if (!RequestQueueHead) {
|
|
RequestQueueHead = pAsyncInfo;
|
|
|
|
//
|
|
// the set is changing state from empty set to not empty set. Set the
|
|
// "something to do" event. Note: it is OK to do this here (before we
|
|
// have finished updating the queue info): because the async thread
|
|
// must gain this critical section before it can access the request
|
|
// queue
|
|
//
|
|
|
|
// PulseEvent(VrpNmpSomethingToDo);
|
|
} else {
|
|
RequestQueueTail->Next = pAsyncInfo;
|
|
}
|
|
pAsyncInfo->Next = NULL;
|
|
RequestQueueTail = pAsyncInfo;
|
|
SetEvent(VrpNmpSomethingToDo);
|
|
LeaveCriticalSection(&VrNmpRequestQueueCritSec);
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
PDOS_ASYNC_NAMED_PIPE_INFO
|
|
VrpDequeueAsyncRequest(
|
|
IN PDOS_ASYNC_NAMED_PIPE_INFO pAsyncInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes the DOS_ASYNC_NAMED_PIPE_INFO structure pointed at by pAsyncInfo
|
|
from the request queue. Protected by critical section
|
|
|
|
Arguments:
|
|
|
|
pAsyncInfo - pointer to DOS_ASYNC_NAMED_PIPE_INFO to remove
|
|
|
|
Return Value:
|
|
|
|
PDOS_ASYNC_NAMED_PIPE_INFO
|
|
Success - pAsyncInfo is returned
|
|
Failure - NULL - AsyncInfo wasn't found on the queue
|
|
|
|
--*/
|
|
|
|
{
|
|
PDOS_ASYNC_NAMED_PIPE_INFO ptr;
|
|
PDOS_ASYNC_NAMED_PIPE_INFO prev = (PDOS_ASYNC_NAMED_PIPE_INFO)&RequestQueueHead;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpDequeueAsyncRequest(0x%08x)\n", pAsyncInfo);
|
|
}
|
|
#endif
|
|
|
|
EnterCriticalSection(&VrNmpRequestQueueCritSec);
|
|
for (ptr = RequestQueueHead; ptr; ptr = ptr->Next) {
|
|
if (ptr == pAsyncInfo) {
|
|
break;
|
|
} else {
|
|
prev = ptr;
|
|
}
|
|
}
|
|
if (ptr) {
|
|
prev->Next = ptr->Next;
|
|
if (RequestQueueTail == ptr) {
|
|
RequestQueueTail = prev;
|
|
}
|
|
}
|
|
|
|
//
|
|
// if this was the last item on the queue (in the set) then the set has
|
|
// changed state from not empty to empty set. Reset the "something to do"
|
|
// event to stop the async thread until another request arrives. Note: it
|
|
// is safe to reset the event here
|
|
//
|
|
|
|
// ResetEvent(VrpNmpSomethingToDo);
|
|
LeaveCriticalSection(&VrNmpRequestQueueCritSec);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpDequeueAsyncRequest returning %08x\n", ptr);
|
|
}
|
|
#endif
|
|
|
|
return ptr;
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
PDOS_ASYNC_NAMED_PIPE_INFO
|
|
VrpFindCompletedRequest(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Tries to locate the first request packet in the queue with the Completed
|
|
field set, meaning the I/O request has completed and is waiting to generate
|
|
a callback
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
PDOS_ASYNC_NAMED_PIPE_INFO
|
|
Success - pointer to request packet to complete
|
|
Failure - NULL
|
|
|
|
--*/
|
|
|
|
{
|
|
PDOS_ASYNC_NAMED_PIPE_INFO ptr;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpFindCompletedRequest\n");
|
|
}
|
|
#endif
|
|
|
|
EnterCriticalSection(&VrNmpRequestQueueCritSec);
|
|
for (ptr = RequestQueueHead; ptr; ptr = ptr->Next) {
|
|
if (ptr->Completed) {
|
|
break;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&VrNmpRequestQueueCritSec);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpFindCompletedRequest returning 0x%08x: Request is %s\n",
|
|
ptr,
|
|
!ptr
|
|
? "NO REQUEST!!"
|
|
: ptr->RequestType == ANP_READ
|
|
? "READ"
|
|
: ptr->RequestType == ANP_WRITE
|
|
? "WRITE"
|
|
: ptr->RequestType == ANP_READ2
|
|
? "READ2"
|
|
: ptr->RequestType == ANP_WRITE2
|
|
? "WRITE2"
|
|
: "UNKNOWN REQUEST!!"
|
|
);
|
|
}
|
|
#endif
|
|
|
|
return ptr;
|
|
}
|
|
|
|
|
|
//
|
|
// externally callable interceptors
|
|
//
|
|
|
|
BOOL
|
|
VrAddOpenNamedPipeInfo(
|
|
IN HANDLE Handle,
|
|
IN LPSTR lpFileName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called whenever DEM (Dos Emulator) successfully opens a
|
|
handle to a file. We check if the file just opened was a named pipe (based
|
|
on the name) and if so create an association between name and handle
|
|
|
|
Arguments:
|
|
|
|
Handle - of just opened file/pipe/device
|
|
lpFileName - symbolic name of what was just opened
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
TRUE - created/added open named pipe structure
|
|
FALSE - couldn't allocate structure memory or create event
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL ok;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrAddOpenNamedPipeInfo\n");
|
|
}
|
|
#endif
|
|
|
|
if (VrIsNamedPipeName(lpFileName)) {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("Adding %s as named pipe\n", lpFileName);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// if we can't create the named pipe info structure, or the async
|
|
// read/write event, return FALSE which results in an out-of-resources
|
|
// error (not enough memory) since DOS doesn't understand about events
|
|
//
|
|
|
|
ok = VrpAddOpenNamedPipeInfo(Handle, lpFileName);
|
|
} else {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrAddOpenNamedPipeInfo: Error: not named pipe: %s\n", lpFileName);
|
|
}
|
|
#endif
|
|
|
|
ok = FALSE;
|
|
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
|
|
BOOL
|
|
VrRemoveOpenNamedPipeInfo(
|
|
IN HANDLE Handle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the companion routine to VrAddOpenNamedPipeInfo. When a handle is
|
|
successfully closed for a DOS app, we must check if it referenced a named
|
|
pipe, and if so remove the info structure we created when the pipe was
|
|
opened
|
|
|
|
Arguments:
|
|
|
|
Handle - to file/pipe/device just closed for Dos app
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
TRUE
|
|
FALSE
|
|
|
|
--*/
|
|
|
|
{
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrRemoveOpenNamedPipeInfo\n");
|
|
}
|
|
|
|
if (!VrpRemoveOpenNamedPipeInfo(Handle)) {
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("Handle 0x%08x is not a named pipe\n", Handle);
|
|
}
|
|
return FALSE;
|
|
} else {
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrRemoveOpenNamedPipeInfo - Handle 0x%08x has been removed\n", Handle);
|
|
}
|
|
return TRUE;
|
|
}
|
|
#else
|
|
VrpRemoveOpenNamedPipeInfo(Handle);
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
VrReadNamedPipe(
|
|
IN HANDLE Handle,
|
|
IN LPBYTE Buffer,
|
|
IN DWORD Buflen,
|
|
OUT LPDWORD BytesRead,
|
|
OUT LPDWORD Error
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs ReadFile on a named pipe handle. All named pipes are opened in
|
|
overlapped-IO mode because async read/writes cannot be performed otherwise
|
|
|
|
Arguments:
|
|
|
|
Handle - of opened NamedPipe
|
|
Buffer - client (VDM) data buffer
|
|
Buflen - size of read buffer
|
|
BytesRead - where actual bytes read is returned
|
|
Error - pointer to returned error in case of failure or more data
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
TRUE - handle was successfully written
|
|
FALSE - an error occurred, use GetLastError
|
|
|
|
--*/
|
|
|
|
{
|
|
OVERLAPPED_PIPE_IO pipeio;
|
|
BOOL success;
|
|
DWORD error;
|
|
DWORD dwBytesRead = 0;
|
|
BOOL bWaited = FALSE;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadNamePipe(0x%08x, %x, %d)\n", Handle, Buffer, Buflen);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// create an event to wait on. This goes in the overlapped structure - it
|
|
// is the only thing in the overlapped structure we are interested in.
|
|
// Create the event with manual reset. This is so that if the I/O operation
|
|
// completes immediately, we don't wait on the event. If we create the
|
|
// event as auto-reset, it can go into the signalled state, and back to the
|
|
// not-signalled state before we prime the wait, causing us to wait forever
|
|
// for an event that has already occurred
|
|
//
|
|
|
|
RtlZeroMemory(&pipeio, sizeof(pipeio));
|
|
if ((pipeio.Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) {
|
|
*Error = ERROR_NOT_ENOUGH_MEMORY; // really want out-of-resources (71?)
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// event handle created ok
|
|
//
|
|
|
|
RememberPipeIo(&pipeio);
|
|
success = ReadFile(Handle, Buffer, Buflen, BytesRead, &pipeio.Overlapped);
|
|
if (!success) {
|
|
error = GetLastError();
|
|
if (error == ERROR_IO_PENDING) {
|
|
error = WaitForSingleObject(pipeio.Overlapped.hEvent, NAMED_PIPE_TIMEOUT);
|
|
bWaited = TRUE;
|
|
if (error == 0xffffffff) {
|
|
error = GetLastError();
|
|
} else {
|
|
success = (error == WAIT_OBJECT_0);
|
|
}
|
|
|
|
} else {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadNamedPipe: ReadFile failed: %d\n", GetLastError());
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// if we got ERROR_MORE_DATA, then this is actually success(!). In this case
|
|
// we don't want to SetLastError, but we do want to set the extended error
|
|
// info in DOS data segment. This is done by demRead
|
|
//
|
|
|
|
if (error == ERROR_MORE_DATA) {
|
|
success = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
error = NO_ERROR;
|
|
dwBytesRead = *BytesRead;
|
|
}
|
|
|
|
ForgetPipeIo(&pipeio);
|
|
if (pipeio.Cancelled) {
|
|
error = WAIT_TIMEOUT;
|
|
success = FALSE;
|
|
}
|
|
|
|
if (success && bWaited) {
|
|
|
|
//
|
|
// get the real bytes read. If GetOverlappedResult returns FALSE,
|
|
// check for ERROR_MORE_DATA
|
|
//
|
|
|
|
success = GetOverlappedResult(Handle, &pipeio.Overlapped, &dwBytesRead, FALSE);
|
|
error = success ? NO_ERROR : GetLastError();
|
|
|
|
//
|
|
// if we got ERROR_MORE_DATA, then this is actually success(!). In this case
|
|
// we don't want to SetLastError, but we do want to set the extended error
|
|
// info in DOS data segment. This is done by demRead
|
|
//
|
|
|
|
if (error == ERROR_MORE_DATA) {
|
|
success = TRUE;
|
|
}
|
|
} else if (error == WAIT_TIMEOUT) {
|
|
CloseHandle(Handle);
|
|
VrpRemoveOpenNamedPipeInfo(Handle);
|
|
}
|
|
|
|
CloseHandle(pipeio.Overlapped.hEvent);
|
|
|
|
//
|
|
// if no bytes were read and success was returned then treat this as an
|
|
// error - this is what the DOS Redir does
|
|
//
|
|
|
|
if (error == NO_ERROR && dwBytesRead == 0) {
|
|
error = ERROR_NO_DATA;
|
|
success = FALSE;
|
|
}
|
|
|
|
if (!success) {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadNamePipe: Error: Returning %d\n", error);
|
|
}
|
|
#endif
|
|
|
|
SetLastError(error);
|
|
} else {
|
|
*BytesRead = dwBytesRead;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrReadNamePipe: Ok: %d bytes read from pipe\n", *BytesRead);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// set the error code so that we can set the extended error code info
|
|
// from demRead and return the success/failure indication
|
|
//
|
|
|
|
*Error = error;
|
|
return success;
|
|
}
|
|
|
|
|
|
BOOL
|
|
VrWriteNamedPipe(
|
|
IN HANDLE Handle,
|
|
IN LPBYTE Buffer,
|
|
IN DWORD Buflen,
|
|
OUT LPDWORD BytesWritten
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Performs WriteFile on a named pipe handle. All named pipes are opened in
|
|
overlapped-IO mode because async read/writes cannot be performed otherwise
|
|
|
|
Arguments:
|
|
|
|
Handle - of opened NamedPipe
|
|
Buffer - client (VDM) data buffer
|
|
Buflen - size of write
|
|
BytesWritten - where actual bytes written is returned
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
TRUE - handle was successfully written
|
|
FALSE - an error occurred, use GetLastError
|
|
|
|
--*/
|
|
|
|
{
|
|
OVERLAPPED_PIPE_IO pipeio;
|
|
BOOL success;
|
|
DWORD error;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrWriteNamePipe(0x%08x, %x, %d)\n", Handle, Buffer, Buflen);
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// create an event to wait on. This goes in the overlapped structure - it
|
|
// is the only thing in the overlapped structure we are interested in.
|
|
// Create the event with manual reset. This is so that if the I/O operation
|
|
// completes immediately, we don't wait on the event. If we create the
|
|
// event as auto-reset, it can go into the signalled state, and back to the
|
|
// not-signalled state before we prime the wait, causing us to wait forever
|
|
// for an event that has already occurred
|
|
//
|
|
|
|
RtlZeroMemory(&pipeio, sizeof(pipeio));
|
|
if ((pipeio.Overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) {
|
|
error = ERROR_NOT_ENOUGH_MEMORY;
|
|
success = FALSE;
|
|
} else {
|
|
RememberPipeIo(&pipeio);
|
|
success = WriteFile(Handle, Buffer, Buflen, BytesWritten, &pipeio.Overlapped);
|
|
error = success ? NO_ERROR : GetLastError();
|
|
if (error == ERROR_IO_PENDING) {
|
|
error = WaitForSingleObject(pipeio.Overlapped.hEvent, NAMED_PIPE_TIMEOUT);
|
|
if (error == 0xffffffff) {
|
|
error = GetLastError();
|
|
} else {
|
|
success = (error == WAIT_OBJECT_0);
|
|
}
|
|
}
|
|
ForgetPipeIo(&pipeio);
|
|
if (pipeio.Cancelled) {
|
|
error = WAIT_TIMEOUT;
|
|
success = FALSE;
|
|
}
|
|
}
|
|
if (success) {
|
|
|
|
//
|
|
// get the real bytes written
|
|
//
|
|
|
|
GetOverlappedResult(Handle, &pipeio.Overlapped, BytesWritten, FALSE);
|
|
}
|
|
CloseHandle(pipeio.Overlapped.hEvent);
|
|
if (!success) {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrWriteNamePipe: Error: Returning %d\n", error);
|
|
}
|
|
#endif
|
|
|
|
SetLastError(error);
|
|
if (error == WAIT_TIMEOUT) {
|
|
CloseHandle(Handle);
|
|
VrpRemoveOpenNamedPipeInfo(Handle);
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
|
|
else {
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrWriteNamePipe: Ok: %d bytes written to pipe\n", *BytesWritten);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return success;
|
|
}
|
|
|
|
|
|
//
|
|
// externally callable helpers
|
|
//
|
|
|
|
BOOL
|
|
VrIsNamedPipeName(
|
|
IN LPSTR Name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks if a string designates a named pipe. As criteria for the decision
|
|
we use:
|
|
|
|
\\computername\PIPE\...
|
|
|
|
DOS (client-side) can only open a named pipe which is created at a server
|
|
and must therefore be prefixed by a computername
|
|
|
|
We *know* that Name has just been used to successfully open a handle to
|
|
a named <something>, so it should at least be semi-sensible. We can
|
|
assume the following:
|
|
|
|
* ASCIZ string
|
|
* an LPSTR points at a single byte (& therefore ++ will add 1)
|
|
|
|
But we can't assume the following:
|
|
|
|
* Canonicalized name
|
|
|
|
Arguments:
|
|
|
|
Name - to check for (Dos) named pipe syntax
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
TRUE - Name refers to (local or remote) named pipe
|
|
FALSE - Name doesn't look like name of pipe
|
|
|
|
--*/
|
|
|
|
{
|
|
int CharCount;
|
|
|
|
#if DBG
|
|
LPSTR OriginalName = Name;
|
|
#endif
|
|
|
|
if (IS_ASCII_PATH_SEPARATOR(*Name)) {
|
|
++Name;
|
|
if (IS_ASCII_PATH_SEPARATOR(*Name)) {
|
|
++Name;
|
|
CharCount = 0;
|
|
while (*Name && !IS_ASCII_PATH_SEPARATOR(*Name)) {
|
|
++Name;
|
|
++CharCount;
|
|
}
|
|
if (!CharCount || !*Name) {
|
|
|
|
//
|
|
// Name is \\ or \\\ or just \\name which I don't understand,
|
|
// so its not a named pipe - fail it
|
|
//
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrIsNamedPipeName - returning FALSE for %s\n", OriginalName);
|
|
}
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// bump name past next path separator. Note that we don't have to
|
|
// check CharCount for max. length of a computername, because this
|
|
// function is called only after the (presumed) named pipe has been
|
|
// successfully opened, therefore we know that the name has been
|
|
// validated
|
|
//
|
|
|
|
++Name;
|
|
} else {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrIsNamedPipeName - returning FALSE for %s\n", OriginalName);
|
|
}
|
|
#endif
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// We are at <something> (after \ or \\<name>\). Check if <something>
|
|
// is [Pp][Ii][Pp][Ee][\\/]
|
|
//
|
|
|
|
if (!_strnicmp(Name, "PIPE", 4)) {
|
|
Name += 4;
|
|
if (IS_ASCII_PATH_SEPARATOR(*Name)) {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrIsNamedPipeName - returning TRUE for %s\n", OriginalName);
|
|
}
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrIsNamedPipeName - returning FALSE for %s\n", OriginalName);
|
|
}
|
|
#endif
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
VrIsNamedPipeHandle(
|
|
IN HANDLE Handle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks if Handle appears in the list of known named pipe handles. Callable
|
|
from outside this module
|
|
|
|
Arguments:
|
|
|
|
Handle - of suspected name pipe
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
TRUE Handle refers to an open named pipe
|
|
FALSE Don't know what Handle refers to
|
|
|
|
--*/
|
|
|
|
{
|
|
return VrpGetOpenNamedPipeInfo(Handle) != NULL;
|
|
}
|
|
|
|
|
|
LPSTR
|
|
VrConvertLocalNtPipeName(
|
|
OUT LPSTR Buffer OPTIONAL,
|
|
IN LPSTR Name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Converts a pipe name of the form \\<local-machine-name>\pipe\name to
|
|
\\.\pipe\name
|
|
|
|
If non-NULL pointer is returned, the buffer contains a canonicalized
|
|
name - any forward-slash characters (/) are converted to backward-slash
|
|
characters (\). In the interest of future-proofing, the name is not
|
|
upper-cased
|
|
|
|
Assumes: Name points to a named pipe specification (\\Server\PIPE\name)
|
|
|
|
Note: it is possible to supply the same input and output buffers and have
|
|
the conversion take place in situ. However, this is a side-effect
|
|
of the fact the input computername is replaced by effectively a
|
|
computername of length 1. Nevertheless, it is safe
|
|
|
|
Arguments:
|
|
|
|
Buffer - pointer to CHAR array where name is placed. If this parameter
|
|
is not present then this routine will allocate a buffer (using
|
|
LocalAlloc and return that
|
|
Name - pointer to ASCIZ pipe name
|
|
|
|
Return Value:
|
|
|
|
LPSTR - pointer to buffer containing name or NULL if failed
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD prefixLength; // length of \\computername
|
|
DWORD pipeLength; // length of pipe name without computername/device prefix
|
|
LPSTR pipeName; // \PIPE\name...
|
|
static char ThisComputerName[MAX_COMPUTERNAME_LENGTH+1] = {0};
|
|
static DWORD ThisComputerNameLength = 0xffffffff;
|
|
BOOLEAN mapped = FALSE;
|
|
|
|
ASSERT(Name);
|
|
ASSERT(IS_ASCII_PATH_SEPARATOR(Name[0]) && IS_ASCII_PATH_SEPARATOR(Name[1]));
|
|
|
|
//
|
|
// first time round, get the computername. If this fails assume there is no
|
|
// computername (i.e. no network)
|
|
//
|
|
|
|
if (ThisComputerNameLength == 0xffffffff) {
|
|
ThisComputerNameLength = sizeof(ThisComputerName);
|
|
if (!GetComputerName((LPTSTR)&ThisComputerName, &ThisComputerNameLength)) {
|
|
ThisComputerNameLength = 0;
|
|
}
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT(Buffer)) {
|
|
Buffer = (LPSTR)LocalAlloc(LMEM_FIXED, strlen(Name)+1);
|
|
}
|
|
|
|
if (Buffer) {
|
|
pipeName = strchr(Name+2, '\\'); // starts \pipe\...
|
|
if (!pipeName) {
|
|
pipeName = strchr(Name+2, '/');
|
|
}
|
|
ASSERT(pipeName);
|
|
|
|
if ( NULL == pipeName ) {
|
|
LocalFree ( (HLOCAL)Buffer );
|
|
return NULL;
|
|
}
|
|
|
|
pipeLength = strlen(pipeName);
|
|
prefixLength = (DWORD)pipeName - (DWORD)Name;
|
|
if (ThisComputerNameLength && (prefixLength - 2 == ThisComputerNameLength)) {
|
|
if (!_strnicmp(ThisComputerName, &Name[2], ThisComputerNameLength)) {
|
|
strcpy(Buffer, LOCAL_DEVICE_PREFIX);
|
|
mapped = TRUE;
|
|
}
|
|
}
|
|
if (!mapped) {
|
|
strncpy(Buffer, Name, prefixLength);
|
|
Buffer[prefixLength] = 0;
|
|
|
|
}
|
|
strcat(Buffer, pipeName);
|
|
|
|
//
|
|
// convert any forward-slashes to backward-slashes
|
|
//
|
|
|
|
|
|
do {
|
|
if (pipeName = strchr(Buffer, '/')) {
|
|
*pipeName++ = '\\';
|
|
}
|
|
} while (pipeName);
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrConvertLocalNtPipeName - returning %s\n", Buffer);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
|
|
//
|
|
// Private utilities
|
|
//
|
|
|
|
//
|
|
// Private list of open named pipe info structures for this VDM process, and
|
|
// associated manipulation routines
|
|
//
|
|
|
|
PRIVATE
|
|
POPEN_NAMED_PIPE_INFO OpenNamedPipeInfoList = NULL;
|
|
|
|
PRIVATE
|
|
POPEN_NAMED_PIPE_INFO LastOpenNamedPipeInfo = NULL;
|
|
|
|
PRIVATE
|
|
BOOL
|
|
VrpAddOpenNamedPipeInfo(
|
|
IN HANDLE Handle,
|
|
IN LPSTR PipeName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
When a named pipe is successfully opened, we call this routine to
|
|
associate an open handle and a pipe name. This is required by
|
|
DosQNmPipeInfo (VrGetNamedPipeInfo)
|
|
|
|
Arguments:
|
|
|
|
Handle - The handle returned from CreateFile (in demOpen)
|
|
PipeName - Name of pipe being opened
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
TRUE - created a OPEN_NAMED_PIPE_INFO structure and added to list
|
|
FALSE - couldn't get memory, or couldn't create event. Use GetLastError
|
|
if you really want to know why this failed
|
|
|
|
--*/
|
|
|
|
{
|
|
POPEN_NAMED_PIPE_INFO PipeInfo;
|
|
DWORD NameLength;
|
|
|
|
//
|
|
// grab a OPEN_NAMED_PIPE_INFO structure
|
|
//
|
|
|
|
NameLength = strlen(PipeName) + 1;
|
|
PipeInfo = (POPEN_NAMED_PIPE_INFO)
|
|
LocalAlloc(LMEM_FIXED,
|
|
ROUND_UP_COUNT((sizeof(OPEN_NAMED_PIPE_INFO) + NameLength),
|
|
sizeof(DWORD)
|
|
)
|
|
);
|
|
|
|
//
|
|
// if we cannot claim memory here, we should *really* close the pipe and
|
|
// return an insufficient memory error to the VDM. However, I don't expect
|
|
// us to run out of memory
|
|
//
|
|
|
|
if (PipeInfo == NULL) {
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpAddOpenNamedPipeInfo: couldn't allocate structure - returning FALSE\n");
|
|
}
|
|
#endif
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// fill it in
|
|
//
|
|
|
|
PipeInfo->Next = NULL;
|
|
PipeInfo->Handle = Handle;
|
|
PipeInfo->NameLength = NameLength;
|
|
strcpy(PipeInfo->Name, PipeName); // from DOS, so its old-fashioned ASCII
|
|
|
|
//
|
|
// put it at the end of the list
|
|
//
|
|
|
|
if (LastOpenNamedPipeInfo == NULL) {
|
|
OpenNamedPipeInfoList = PipeInfo;
|
|
} else {
|
|
LastOpenNamedPipeInfo->Next = PipeInfo;
|
|
}
|
|
LastOpenNamedPipeInfo = PipeInfo;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpAddOpenNamedPipeInfo - adding structure @ %08x, Handle=0x%08x, Name=%s\n",
|
|
PipeInfo,
|
|
PipeInfo->Handle,
|
|
PipeInfo->Name
|
|
);
|
|
}
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
POPEN_NAMED_PIPE_INFO
|
|
VrpGetOpenNamedPipeInfo(
|
|
IN HANDLE Handle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Linear search for an OPEN_NAMED_PIPE_INFO structure in OpenNamedPipeInfoList
|
|
using the handle as search criteria
|
|
|
|
Arguments:
|
|
|
|
Handle - to search for
|
|
|
|
Return Value:
|
|
|
|
POPEN_NAMED_PIPE_INFO
|
|
Success - Pointer to located structure
|
|
Failure - NULL
|
|
|
|
--*/
|
|
|
|
{
|
|
POPEN_NAMED_PIPE_INFO ptr;
|
|
|
|
for (ptr = OpenNamedPipeInfoList; ptr; ptr = ptr->Next) {
|
|
if (ptr->Handle == Handle) {
|
|
break;
|
|
}
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
BOOL
|
|
VrpRemoveOpenNamedPipeInfo(
|
|
IN HANDLE Handle
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unlinks and frees an OPEN_NAMED_PIPE_INFO structure from
|
|
OpenNamedPipeInfoList
|
|
|
|
Note: Assumes that the Handle is in the list (no action taken if not
|
|
found)
|
|
|
|
Arguments:
|
|
|
|
Handle - defining OPEN_NAMED_PIPE_INFO structure to remove from list
|
|
|
|
Return Value:
|
|
|
|
BOOL
|
|
TRUE - OPEN_NAMED_PIPE_INFO structure corresponding to Handle was
|
|
removed from list and freed
|
|
FALSE - OPEN_NAMED_PIPE_INFO structure corresponding to Handle was
|
|
not found
|
|
|
|
--*/
|
|
|
|
{
|
|
POPEN_NAMED_PIPE_INFO ptr, prev = NULL;
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpRemoveOpenNamedPipeInfo(0x%08x)\n", Handle);
|
|
DumpOpenPipeList();
|
|
DumpRequestQueue();
|
|
}
|
|
#endif
|
|
|
|
for (ptr = OpenNamedPipeInfoList; ptr; ) {
|
|
if (ptr->Handle == Handle) {
|
|
if (!prev) {
|
|
OpenNamedPipeInfoList = ptr->Next;
|
|
} else {
|
|
prev->Next = ptr->Next;
|
|
}
|
|
if (LastOpenNamedPipeInfo == ptr) {
|
|
LastOpenNamedPipeInfo = prev;
|
|
}
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpRemoveOpenNamedPipeInfo - freeing structure @ %08x, Handle=0x%08x, Name=%s\n",
|
|
ptr,
|
|
ptr->Handle,
|
|
ptr->Name
|
|
);
|
|
}
|
|
#endif
|
|
|
|
LocalFree(ptr);
|
|
return TRUE;
|
|
} else {
|
|
prev = ptr;
|
|
ptr = ptr->Next;
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
IF_DEBUG(NAMEPIPE) {
|
|
DbgPrint("VrpRemoveOpenNamedPipeInfo: Can't find 0x%08x in list\n", Handle);
|
|
}
|
|
#endif
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
VOID
|
|
RememberPipeIo(
|
|
IN POVERLAPPED_PIPE_IO PipeIo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds an OVERLAPPED_PIPE_IO structure to the list of in-progress named pipe
|
|
I/Os
|
|
|
|
Arguments:
|
|
|
|
PipeIo - pointer to OVERLAPPED_PIPE_IO structure to add to list
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// just stick this at front of list; order is not important - this is just
|
|
// a stack of in-progress requests
|
|
//
|
|
|
|
PipeIo->Thread = GetCurrentThreadId();
|
|
EnterCriticalSection(&VrNamedPipeCancelCritSec);
|
|
PipeIo->Next = PipeIoQueue;
|
|
PipeIoQueue = PipeIo;
|
|
LeaveCriticalSection(&VrNamedPipeCancelCritSec);
|
|
}
|
|
|
|
|
|
PRIVATE
|
|
VOID
|
|
ForgetPipeIo(
|
|
IN POVERLAPPED_PIPE_IO PipeIo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Removes an OVERLAPPED_PIPE_IO structure from the list of in-progress named
|
|
pipe I/Os
|
|
|
|
Arguments:
|
|
|
|
PipeIo - pointer to OVERLAPPED_PIPE_IO structure to remove
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
POVERLAPPED_PIPE_IO prev, ptr;
|
|
|
|
EnterCriticalSection(&VrNamedPipeCancelCritSec);
|
|
for (ptr = PipeIoQueue, prev = (POVERLAPPED_PIPE_IO)&PipeIoQueue; ptr && ptr != PipeIo; ) {
|
|
prev = ptr;
|
|
ptr = ptr->Next;
|
|
}
|
|
if (ptr == PipeIo) {
|
|
prev->Next = ptr->Next;
|
|
}
|
|
LeaveCriticalSection(&VrNamedPipeCancelCritSec);
|
|
}
|
|
|
|
|
|
#if DBG
|
|
VOID DumpOpenPipeList()
|
|
{
|
|
POPEN_NAMED_PIPE_INFO ptr = OpenNamedPipeInfoList;
|
|
|
|
DbgPrint("DumpOpenPipeList\n");
|
|
|
|
if (!ptr) {
|
|
DbgPrint("DumpOpenPipeList: no open named pipe structures\n");
|
|
} else {
|
|
while (ptr) {
|
|
DbgPrint("\n"
|
|
"OPEN_NAMED_PIPE_INFO structure @%08x:\n"
|
|
"Next. . . . . . . . . . %08x\n"
|
|
"Handle. . . . . . . . . %08x\n"
|
|
"NameLength. . . . . . . %d\n"
|
|
"DosPdb. . . . . . . . . %04x\n"
|
|
"Name. . . . . . . . . . %s\n",
|
|
ptr,
|
|
ptr->Next,
|
|
ptr->Handle,
|
|
ptr->NameLength,
|
|
ptr->DosPdb,
|
|
ptr->Name
|
|
);
|
|
ptr = ptr->Next;
|
|
}
|
|
DbgPrint("\n");
|
|
}
|
|
}
|
|
|
|
VOID DumpRequestQueue()
|
|
{
|
|
PDOS_ASYNC_NAMED_PIPE_INFO ptr;
|
|
|
|
DbgPrint("DumpRequestQueue\n");
|
|
|
|
EnterCriticalSection(&VrNmpRequestQueueCritSec);
|
|
ptr = RequestQueueHead;
|
|
if (!ptr) {
|
|
DbgPrint("DumpRequestQueue: no request packets queued\n");
|
|
} else {
|
|
for (; ptr; ptr = ptr->Next) {
|
|
|
|
//
|
|
// NT (308c) can't handle all this being put on the stack - gets
|
|
// fault in KdpCopyDataToStack
|
|
//
|
|
|
|
DbgPrint("\n"
|
|
"DOS_ASYNC_NAMED_PIPE_INFO structure @%08x:\n"
|
|
"Next. . . . . . . . . . %08x\n"
|
|
"Overlapped.Internal . . %08x\n"
|
|
"Overlapped.InternalHigh %08x\n"
|
|
"Overlapped.Offset . . . %08x\n"
|
|
"Overlapped.OffsetHigh . %08x\n"
|
|
"Overlapped.hEvent . . . %08x\n",
|
|
ptr,
|
|
ptr->Next,
|
|
ptr->Overlapped.Internal,
|
|
ptr->Overlapped.InternalHigh,
|
|
ptr->Overlapped.Offset,
|
|
ptr->Overlapped.OffsetHigh,
|
|
ptr->Overlapped.hEvent
|
|
);
|
|
DbgPrint("Type2 . . . . . . . . . %d\n"
|
|
"Completed . . . . . . . %d\n"
|
|
"Handle. . . . . . . . . %08x\n"
|
|
"Buffer. . . . . . . . . %04x:%04x\n"
|
|
"BytesTransferred. . . . %d\n"
|
|
"pBytesTransferred . . . %08x\n"
|
|
"pErrorCode. . . . . . . %08x\n"
|
|
"ANR . . . . . . . . . . %04x:%04x\n"
|
|
"Semaphore . . . . . . . %04x:%04x\n"
|
|
"RequestType . . . . . . %04x [%s]\n",
|
|
ptr->Type2,
|
|
ptr->Completed,
|
|
ptr->Handle,
|
|
HIWORD(ptr->Buffer),
|
|
LOWORD(ptr->Buffer),
|
|
ptr->BytesTransferred,
|
|
ptr->pBytesTransferred,
|
|
ptr->pErrorCode,
|
|
HIWORD(ptr->ANR),
|
|
LOWORD(ptr->ANR),
|
|
HIWORD(ptr->Semaphore),
|
|
LOWORD(ptr->Semaphore),
|
|
ptr->RequestType,
|
|
ptr->RequestType == ANP_READ
|
|
? "READ"
|
|
: ptr->RequestType == ANP_READ2
|
|
? "READ2"
|
|
: ptr->RequestType == ANP_WRITE
|
|
? "WRITE"
|
|
: ptr->RequestType == ANP_WRITE2
|
|
? "WRITE2"
|
|
: "*** UNKNOWN REQUEST ***"
|
|
);
|
|
}
|
|
DbgPrint("\n");
|
|
}
|
|
LeaveCriticalSection(&VrNmpRequestQueueCritSec);
|
|
}
|
|
#endif
|