2445 lines
65 KiB
C
2445 lines
65 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
npipe.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the routines that support remote named pipes.
|
||
|
||
Author:
|
||
|
||
Colin Watson (ColinW) 24-Dec-1990
|
||
|
||
Revision History:
|
||
|
||
24-Dec-1990 ColinW
|
||
|
||
Created
|
||
|
||
Notes:
|
||
|
||
|
||
--*/
|
||
|
||
#define INCLUDE_SMB_FILE_CONTROL
|
||
#define INCLUDE_SMB_READ_WRITE
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
//
|
||
// The NT IO system does not include priority information in the API's.
|
||
// Using priority 0 will cause the server default to be used.
|
||
//
|
||
|
||
#define NPPRIORITY 0
|
||
|
||
|
||
DBGSTATIC
|
||
NTSTATUS
|
||
CoreNpRead (
|
||
IN PIRP Irp,
|
||
IN PICB Icb,
|
||
IN ULONG Length,
|
||
IN PUCHAR Buffer,
|
||
OUT PUSHORT AmountActuallyRead
|
||
);
|
||
|
||
DBGSTATIC
|
||
VOID
|
||
NpStartTimer (
|
||
IN PICB Icb
|
||
);
|
||
|
||
DBGSTATIC
|
||
BOOLEAN
|
||
RdrNpAcquireExclusive (
|
||
IN BOOLEAN Wait,
|
||
IN PKSEMAPHORE SynchronizationEvent
|
||
);
|
||
|
||
#if RDRDBG
|
||
VOID
|
||
ndump_core(
|
||
PCHAR far_p,
|
||
ULONG len
|
||
);
|
||
#endif // DBG
|
||
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, RdrNpPeek)
|
||
#pragma alloc_text(PAGE, RdrNpTransceive)
|
||
#pragma alloc_text(PAGE, RdrQueryNpInfo)
|
||
#pragma alloc_text(PAGE, RdrQueryNpLocalInfo)
|
||
#pragma alloc_text(PAGE, RdrQueryNpRemoteInfo)
|
||
#pragma alloc_text(PAGE, RdrSetNpInfo)
|
||
#pragma alloc_text(PAGE, RdrSetNpRemoteInfo)
|
||
#pragma alloc_text(PAGE, RdrNpWait)
|
||
#pragma alloc_text(PAGE, RdrNpFlushBuffers)
|
||
#pragma alloc_text(PAGE, RdrNpCachedRead)
|
||
#pragma alloc_text(PAGE, CoreNpRead)
|
||
#pragma alloc_text(PAGE, RdrNpCachedWrite)
|
||
#pragma alloc_text(PAGE, RdrNpWriteFlush)
|
||
#pragma alloc_text(PAGE, RdrNpAcquireExclusive)
|
||
#pragma alloc_text(PAGE3FILE, NpStartTimer)
|
||
#pragma alloc_text(PAGE3FILE, RdrNpCancelTimer)
|
||
#pragma alloc_text(PAGE3FILE, RdrNpTimerDispatch)
|
||
#pragma alloc_text(PAGE3FILE, RdrNpTimedOut)
|
||
#endif
|
||
|
||
NTSTATUS
|
||
RdrNpPeek (
|
||
IN BOOLEAN Wait,
|
||
IN BOOLEAN InFsd,
|
||
IN PIRP Irp,
|
||
IN PICB Icb,
|
||
IN PVOID OutputBuffer,
|
||
IN OUT PULONG OutputBufferLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the peek control function
|
||
|
||
Arguments:
|
||
|
||
Irp,IrpSp - Supplies the request being processed
|
||
|
||
Wait - Indicates if we are allowed to block for a resource
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - An appropriate return status (STATUS_PENDING means
|
||
queue request to fsp).
|
||
|
||
Note:
|
||
Expect this call to become neither I/O in the future.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS Status;
|
||
|
||
PFILE_PIPE_PEEK_BUFFER PeekBuffer = OutputBuffer;
|
||
|
||
PFCB Fcb = Icb->Fcb;
|
||
|
||
USHORT Setup[2];
|
||
|
||
RESP_PEEK_NMPIPE Parameters;
|
||
|
||
CLONG OutParameterCount = sizeof(RESP_PEEK_NMPIPE);
|
||
|
||
CLONG OutDataCount;
|
||
|
||
CLONG OutSetupCount = 0;
|
||
|
||
UNICODE_STRING Name;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER(InFsd);
|
||
|
||
dprintf(DPRT_NP, ("RdrNpPeek...\n"));
|
||
|
||
//
|
||
// Extract the important fields from the IrpSp
|
||
//
|
||
|
||
dprintf( DPRT_NP, ("OutputBufferLength = %lx\n", *OutputBufferLength));
|
||
|
||
if (Icb->Type != NamedPipe) {
|
||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
dprintf(DPRT_NP, ("RdrNpPeek returning status: %X\n", Status));
|
||
return Status;
|
||
}
|
||
|
||
if ( Icb->Flags & ( ICB_ERROR ) ) {
|
||
Status = STATUS_PIPE_DISCONNECTED;
|
||
dprintf(DPRT_NP, ("RdrNpPeek returning status: %X\n", Status));
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Reference the system buffer as a peek and make sure it's large enough.
|
||
//
|
||
|
||
if (*OutputBufferLength < (ULONG)FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0])) {
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
dprintf(DPRT_NP, ("RdrNpPeek returning status: %X\n", Status));
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// If this is a read ahead buffer then try to read from that.
|
||
//
|
||
|
||
if (( Icb->NonPagedFcb->FileType == FileTypeByteModePipe ) &&
|
||
( Icb->u.p.PipeState & SMB_PIPE_NOWAIT )){
|
||
|
||
// Prevent 2 threads corrupting Icb->
|
||
if ( !RdrNpAcquireExclusive ( Wait, &Icb->u.p.ReadData.Semaphore ) ) {
|
||
// Another thread is accessing the pipe handle and !Wait
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
if ( Icb->u.p.ReadData.Length != 0 ) {
|
||
USHORT TransferLength = MIN ( ((USHORT)*OutputBufferLength) -
|
||
FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]),
|
||
Icb->u.p.ReadData.Length );
|
||
|
||
PeekBuffer->NamedPipeState = FILE_PIPE_CONNECTED_STATE;
|
||
PeekBuffer->ReadDataAvailable = Icb->u.p.ReadData.Length;
|
||
PeekBuffer->NumberOfMessages = MAXULONG;
|
||
PeekBuffer->MessageLength = Icb->u.p.ReadData.Length;
|
||
|
||
#if RDRDBG
|
||
IFDEBUG(NP) {
|
||
dprintf( DPRT_NP, ("Peek: read buffer contents\n"));
|
||
ndump_core(Icb->u.p.ReadData.Buffer, Icb->u.p.ReadData.MaximumLength );
|
||
}
|
||
#endif
|
||
|
||
try {
|
||
RtlCopyMemory((PCHAR)PeekBuffer + FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]),
|
||
&Icb->u.p.ReadData.Buffer[Icb->u.p.ReadData.Offset],
|
||
TransferLength);
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
RdrNpRelease ( &Icb->u.p.ReadData.Semaphore );
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// The copy worked, return success to the caller.
|
||
//
|
||
|
||
*OutputBufferLength = TransferLength +
|
||
FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]);
|
||
RdrNpRelease ( &Icb->u.p.ReadData.Semaphore );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
RdrNpRelease ( &Icb->u.p.ReadData.Semaphore );
|
||
} // else nothing in read ahead buffer.
|
||
|
||
//
|
||
// Should we send the request to the network or back off because the
|
||
// caller is attempting to flood the network with peek requests which
|
||
// are returning 0 bytes?
|
||
//
|
||
|
||
if ( RdrBackOff ( &Icb->u.p.BackOff ) ) {
|
||
*OutputBufferLength = FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]);
|
||
Status = STATUS_SUCCESS;
|
||
|
||
ASSERT ( FILE_PIPE_CONNECTED_STATE == 3);
|
||
PeekBuffer->NamedPipeState = FILE_PIPE_CONNECTED_STATE;
|
||
PeekBuffer->ReadDataAvailable = 0;
|
||
PeekBuffer->NumberOfMessages = MAXULONG;
|
||
PeekBuffer->MessageLength = 0;
|
||
|
||
dprintf(DPRT_NP, ("RdrNpPeek returning back off status: %X\n", Status));
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// From this point on we will wait for a response from the server - check
|
||
// to see if the user has allowed us to wait.
|
||
//
|
||
|
||
if (!Wait) {
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
//
|
||
// Now we are ready to copy over the data from the server
|
||
// into the peek buffer. First establish how much room we
|
||
// have in the peek buffer and how much is remaining.
|
||
//
|
||
|
||
OutDataCount = *OutputBufferLength - FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]);
|
||
|
||
//
|
||
// Build and initialize the Setup bytes
|
||
//
|
||
|
||
Setup[0] = TRANS_PEEK_NMPIPE;
|
||
Setup[1] = Icb->FileId;
|
||
|
||
//
|
||
// Build and initialize the Parameters
|
||
//
|
||
// - No parameters to sent on Peek
|
||
|
||
RtlInitUnicodeString(&Name, L"\\PIPE\\");
|
||
|
||
Status = RdrTransact(Irp,
|
||
Fcb->Connection,
|
||
Icb->Se,
|
||
Setup,
|
||
(CLONG) sizeof(Setup), // InSetupCount,
|
||
&OutSetupCount,
|
||
&Name,
|
||
&Parameters,
|
||
0,// InParameterCount,
|
||
&OutParameterCount,
|
||
NULL, // InData,
|
||
0, // InDataCount,
|
||
&PeekBuffer->Data[0], // OutData,
|
||
&OutDataCount,
|
||
&Icb->FileId, // Fid
|
||
0, // Timeout
|
||
0, // Flags
|
||
0,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if ( !NT_ERROR(Status) ) {
|
||
|
||
//
|
||
// Stash away all returned parameters.
|
||
// Note all parameters are word aligned already so no
|
||
// need to use SmbGetUshort
|
||
//
|
||
|
||
ASSERT(OutParameterCount >= sizeof(RESP_PEEK_NMPIPE));
|
||
|
||
//
|
||
// Os/2 servers will allow PeekNamedPipes on closed pipes to succeed
|
||
// even if the server side of the pipe is closed.
|
||
//
|
||
// If we get the status PIPE_STATE_CLOSING from the server, then
|
||
// we need to return an error of STATUS_PIPE_DISCONNECTED, as this
|
||
// is what NPFS will do.
|
||
//
|
||
|
||
if ((SmbGetAlignedUshort(&Parameters.NamedPipeState) & PIPE_STATE_CLOSING) &&
|
||
(Parameters.ReadDataAvailable == 0)) {
|
||
Status = STATUS_PIPE_DISCONNECTED;
|
||
} else {
|
||
PeekBuffer->NamedPipeState = (ULONG)SmbGetAlignedUshort(&Parameters.NamedPipeState);
|
||
PeekBuffer->ReadDataAvailable = (ULONG)Parameters.ReadDataAvailable;
|
||
PeekBuffer->NumberOfMessages = MAXULONG;
|
||
PeekBuffer->MessageLength = (ULONG)Parameters.MessageLength;
|
||
|
||
if (PeekBuffer->MessageLength > OutDataCount) {
|
||
Status = STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Complete the request. The amount of information copied
|
||
// is stored in length written. Tell the back off package whether
|
||
// the request received no data or some data.
|
||
//
|
||
|
||
*OutputBufferLength = FIELD_OFFSET(FILE_PIPE_PEEK_BUFFER, Data[0]) + OutDataCount;
|
||
|
||
if ( OutDataCount == 0 ) {
|
||
|
||
RdrBackPackFailure( &Icb->u.p.BackOff );
|
||
dprintf(DPRT_NP, ("RdrNpPeek returnF status: %X\n", Status));
|
||
|
||
} else {
|
||
|
||
RdrBackPackSuccess( &Icb->u.p.BackOff );
|
||
dprintf(DPRT_NP, ("RdrNpPeek returnS status: %X\n", Status));
|
||
|
||
}
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
RdrNpTransceive (
|
||
IN BOOLEAN Wait,
|
||
IN BOOLEAN InFsd,
|
||
IN PIRP Irp,
|
||
IN PICB Icb,
|
||
IN PVOID InputBuffer,
|
||
IN ULONG InputBufferLength,
|
||
OUT PVOID OutputBuffer,
|
||
IN OUT PULONG OutputBufferLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the transceive control function.
|
||
|
||
Arguments:
|
||
|
||
Irp,IrpSp - Supplies the request being processed
|
||
|
||
Wait - Indicates if we are allowed to block for a resource
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - An appropriate return status (STATUS_PENDING means
|
||
queue request to fsp).
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS Status;
|
||
|
||
PFCB Fcb = Icb->Fcb;
|
||
|
||
USHORT Setup[2];
|
||
|
||
UCHAR Parameters[1];
|
||
|
||
CLONG OutParameterCount = 0;
|
||
|
||
CLONG OutSetupCount = 0;
|
||
|
||
UNICODE_STRING Name;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER(InFsd);
|
||
|
||
dprintf(DPRT_NP, ("RdrNpTransceive...\n"));
|
||
|
||
//
|
||
// Extract the important fields from the IrpSp
|
||
//
|
||
|
||
dprintf( DPRT_NP, ("OutputBuffer =%lx, %lx\n", OutputBuffer, OutputBufferLength));
|
||
|
||
dprintf( DPRT_NP, ("InputBuffer = %lx, %lx\n", InputBuffer, InputBufferLength));
|
||
|
||
//
|
||
// Decode the file object to figure out who we are.
|
||
//
|
||
|
||
if ( Icb->Type != NamedPipe ) {
|
||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
dprintf(DPRT_NP, ("RdrNpTransceive returning status: %X\n", Status));
|
||
return Status;
|
||
}
|
||
|
||
if ( Icb->Flags & ( ICB_ERROR ) ) {
|
||
Status = STATUS_PIPE_DISCONNECTED;
|
||
dprintf(DPRT_NP, ("RdrNpTransceive returning status: %X\n", Status));
|
||
return Status;
|
||
}
|
||
|
||
if ( !(Icb->u.p.PipeState & SMB_PIPE_TYPE_MESSAGE) ) {
|
||
Status = STATUS_INVALID_READ_MODE;
|
||
dprintf(DPRT_NP, ("RdrNpTransceive returning status: %X\n", Status));
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// From this point on we will wait for a response from the server - check
|
||
// to see if the user has allowed us to wait.
|
||
//
|
||
|
||
if (!Wait) {
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
//
|
||
// Build and initialize the Setup bytes
|
||
//
|
||
|
||
Setup[0] = TRANS_TRANSACT_NMPIPE;
|
||
Setup[1] = Icb->FileId;
|
||
|
||
//
|
||
// Build and initialize the Parameters
|
||
//
|
||
// - No parameters to send on Transceive
|
||
|
||
RtlInitUnicodeString(&Name, L"\\PIPE\\");
|
||
|
||
Status = RdrTransact(Irp,
|
||
Fcb->Connection,
|
||
Icb->Se,
|
||
Setup,
|
||
(CLONG) sizeof(Setup), // InSetupCount,
|
||
&OutSetupCount,
|
||
&Name,
|
||
Parameters,
|
||
0,// InParameterCount,
|
||
&OutParameterCount,
|
||
InputBuffer, // InData,
|
||
InputBufferLength, // InDataCount,
|
||
OutputBuffer, // OutData,
|
||
OutputBufferLength,
|
||
&Icb->FileId, // Fid
|
||
0, // Timeout
|
||
0, // Flags
|
||
0,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
if (Status == STATUS_INVALID_HANDLE) {
|
||
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
||
}
|
||
|
||
//
|
||
// Complete the request. The amount of information copied
|
||
// is stored in length written. The IO system will unlock the Mdl
|
||
// chain when it completes the Mdl.
|
||
//
|
||
|
||
dprintf(DPRT_NP, ("RdrNpTransceive return status: %X\n", Status));
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
RdrQueryNpInfo(
|
||
PICB Icb,
|
||
PVOID UsersBuffer,
|
||
PULONG BufferSize,
|
||
PNTSTATUS FinalStatus
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the FilePipeInformation value of the
|
||
NtQueryInformationFile api.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PICB Icb - Supplies the ICB associated with this request.
|
||
|
||
OUT PVOID UsersBuffer - Supplies the user's buffer that is filled with
|
||
the values to set.
|
||
|
||
IN OUT PULONG BufferSize - Supplies the size of the buffer. On return,
|
||
the amount of the buffer consumed is
|
||
subtracted from the initial size.
|
||
|
||
OUT PNTSTATUS FinalStatus - Status to be returned for this operation.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - True if request is to be processed in the FSP.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_NP, ("RdrQueryNpInfo...\n"));
|
||
|
||
if (*BufferSize < sizeof(FILE_PIPE_INFORMATION)) {
|
||
*FinalStatus = STATUS_BUFFER_TOO_SMALL;
|
||
} else {
|
||
|
||
((PFILE_PIPE_INFORMATION)UsersBuffer)->ReadMode =
|
||
( Icb->u.p.PipeState & SMB_PIPE_READMODE_MESSAGE ) ?
|
||
FILE_PIPE_MESSAGE_MODE : FILE_PIPE_BYTE_STREAM_MODE;
|
||
|
||
((PFILE_PIPE_INFORMATION)UsersBuffer)->CompletionMode =
|
||
( Icb->u.p.PipeState & SMB_PIPE_NOWAIT ) ?
|
||
FILE_PIPE_COMPLETE_OPERATION : FILE_PIPE_QUEUE_OPERATION;
|
||
|
||
*BufferSize -= sizeof(FILE_PIPE_INFORMATION);
|
||
*FinalStatus = STATUS_SUCCESS;
|
||
}
|
||
|
||
dprintf(DPRT_NP, ("RdrQueryNpInfo return status: %X\n", *FinalStatus));
|
||
dprintf(DPRT_NP, ("ReadMode: %lx, CompletionMode: %lx\n",
|
||
((PFILE_PIPE_INFORMATION)UsersBuffer)->ReadMode,
|
||
((PFILE_PIPE_INFORMATION)UsersBuffer)->CompletionMode));
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
RdrQueryNpLocalInfo(
|
||
PIRP Irp,
|
||
PICB Icb,
|
||
PVOID UsersBuffer,
|
||
PULONG BufferSize,
|
||
PNTSTATUS FinalStatus,
|
||
BOOLEAN Wait
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the FilePipeLocalInformation value of the
|
||
NtQueryInformationFile api.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PICB Icb - Supplies the ICB associated with this request.
|
||
|
||
OUT PVOID UsersBuffer - Supplies the user's buffer that is filled with
|
||
the values to set.
|
||
|
||
IN OUT PULONG BufferSize - Supplies the size of the buffer. On return,
|
||
the amount of the buffer consumed is
|
||
subtracted from the initial size.
|
||
|
||
OUT PNTSTATUS FinalStatus - Status to be returned for this operation.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - True if request is to be processed in the FSP.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_NP, ("RdrQueryNpLocalInfo...\n"));
|
||
|
||
if (*BufferSize < sizeof(FILE_PIPE_LOCAL_INFORMATION)) {
|
||
*FinalStatus = STATUS_BUFFER_TOO_SMALL;
|
||
} else {
|
||
PFILE_PIPE_LOCAL_INFORMATION Buffer =
|
||
(PFILE_PIPE_LOCAL_INFORMATION)UsersBuffer;
|
||
|
||
// Make PipeInfo big enough to include the pipename
|
||
CHAR PipeBuffer[sizeof(NAMED_PIPE_INFORMATION_1) + MAXIMUM_FILENAME_LENGTH];
|
||
PNAMED_PIPE_INFORMATION_1 PipeInfo = (PNAMED_PIPE_INFORMATION_1)PipeBuffer;
|
||
USHORT Setup[2];
|
||
USHORT Level = 1;
|
||
CLONG OutParameterCount = 0;
|
||
CLONG OutDataCount = sizeof(NAMED_PIPE_INFORMATION_1) + MAXIMUM_FILENAME_LENGTH;
|
||
CLONG OutSetupCount = 0;
|
||
|
||
UNICODE_STRING Name;
|
||
|
||
//
|
||
// From this point on we will wait for a response from the server - check
|
||
// to see if the user has allowed us to wait.
|
||
//
|
||
|
||
if (!Wait) {
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Build and initialize the Setup bytes
|
||
//
|
||
|
||
Setup[0] = TRANS_QUERY_NMPIPE_INFO;
|
||
Setup[1] = Icb->FileId;
|
||
|
||
RtlInitUnicodeString(&Name, L"\\PIPE\\");
|
||
|
||
*FinalStatus = RdrTransact(Irp,
|
||
Icb->Fcb->Connection,
|
||
Icb->Se,
|
||
Setup,
|
||
(CLONG) sizeof(Setup), // InSetupCount,
|
||
&OutSetupCount,
|
||
&Name,
|
||
&Level,
|
||
sizeof(Level),// InParameterCount,
|
||
&OutParameterCount,
|
||
NULL, // InData,
|
||
0, // InDataCount,
|
||
PipeInfo, // OutData,
|
||
&OutDataCount,
|
||
&Icb->FileId, // Fid
|
||
0, // Timeout
|
||
0, // Flags
|
||
0,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
// If the update worked then record new mode
|
||
|
||
if (NT_SUCCESS(*FinalStatus) ) {
|
||
|
||
Buffer->NamedPipeType =
|
||
( Icb->NonPagedFcb->FileType == FileTypeByteModePipe) ?
|
||
FILE_PIPE_BYTE_STREAM_TYPE : FILE_PIPE_MESSAGE_TYPE;
|
||
|
||
Buffer->NamedPipeConfiguration;
|
||
|
||
Buffer->MaximumInstances = (ULONG)PipeInfo->MaximumInstances;
|
||
Buffer->CurrentInstances = (ULONG)PipeInfo->CurrentInstances;
|
||
Buffer->InboundQuota = SmbGetUshort(&PipeInfo->InputBufferSize);
|
||
Buffer->ReadDataAvailable = 0xffffffff;
|
||
Buffer->OutboundQuota = SmbGetUshort(&PipeInfo->OutputBufferSize);
|
||
Buffer->WriteQuotaAvailable = 0xffffffff;
|
||
Buffer->NamedPipeState = FILE_PIPE_CONNECTED_STATE;// Since no error
|
||
Buffer->NamedPipeEnd = FILE_PIPE_CLIENT_END;
|
||
*BufferSize -= sizeof(FILE_PIPE_LOCAL_INFORMATION);
|
||
} else {
|
||
if (*FinalStatus == STATUS_INVALID_HANDLE) {
|
||
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
||
}
|
||
|
||
}
|
||
}
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
RdrQueryNpRemoteInfo(
|
||
PICB Icb,
|
||
PVOID UsersBuffer,
|
||
PULONG BufferSize,
|
||
PNTSTATUS FinalStatus
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the FilePipeRemoteInformation value of the
|
||
NtQueryInformationFile api.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PICB Icb - Supplies the ICB associated with this request.
|
||
|
||
OUT PVOID UsersBuffer - Supplies the user's buffer that is filled with
|
||
the values to set.
|
||
|
||
IN OUT PULONG BufferSize - Supplies the size of the buffer. On return,
|
||
the amount of the buffer consumed is
|
||
subtracted from the initial size.
|
||
|
||
OUT PNTSTATUS FinalStatus - Status to be returned for this operation.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - True if request is to be processed in the FSP.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_NP, ("RdrQueryNpRemoteInfo...\n"));
|
||
|
||
if (*BufferSize < sizeof(FILE_PIPE_REMOTE_INFORMATION)) {
|
||
*FinalStatus = STATUS_BUFFER_TOO_SMALL;
|
||
} else {
|
||
|
||
((PFILE_PIPE_REMOTE_INFORMATION)UsersBuffer)->CollectDataTime =
|
||
Icb->u.p.CollectDataTime;
|
||
|
||
((PFILE_PIPE_REMOTE_INFORMATION)UsersBuffer)->MaximumCollectionCount =
|
||
Icb->u.p.MaximumCollectionCount;
|
||
|
||
*BufferSize -= sizeof(FILE_PIPE_REMOTE_INFORMATION);
|
||
*FinalStatus = STATUS_SUCCESS;
|
||
|
||
}
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
RdrSetNpInfo(
|
||
PIRP Irp,
|
||
PICB Icb,
|
||
PVOID UsersBuffer,
|
||
ULONG BufferSize,
|
||
PNTSTATUS FinalStatus,
|
||
BOOLEAN Wait
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the FilePipeInformation value of the
|
||
NtSetInformationFile api. It returns the following information:
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PICB Icb - Supplies the ICB associated with this request.
|
||
|
||
OUT PVOID UsersBuffer - Supplies the user's buffer that is filled with
|
||
the values to set.
|
||
|
||
IN ULONG BufferSize - Supplies the size of the buffer. On return,
|
||
the amount of the buffer consumed is
|
||
subtracted from the initial size.
|
||
|
||
OUT PNTSTATUS FinalStatus - Status to be returned for this operation.
|
||
|
||
IN BOOLEAN Wait - True if FSD can wait for this request.
|
||
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - True if request is to be processed in the FSP.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_NP, ("RdrSetNpInfo...\n"));
|
||
dprintf(DPRT_NP, ("RdrSetNpInfo: ReadMode %lx, CompletionMode %lx\n "
|
||
"Icb->u.p.PipeState: %lx\n",
|
||
((PFILE_PIPE_INFORMATION)UsersBuffer)->ReadMode,
|
||
((PFILE_PIPE_INFORMATION)UsersBuffer)->CompletionMode,
|
||
Icb->u.p.PipeState));
|
||
|
||
if (BufferSize < sizeof(FILE_PIPE_INFORMATION)) {
|
||
*FinalStatus = STATUS_BUFFER_TOO_SMALL;
|
||
return FALSE;
|
||
} else {
|
||
USHORT NewState = Icb->u.p.PipeState;
|
||
|
||
// If the user requested message mode and the file is stream mode
|
||
// only or an invalid mode was selected then return an error.
|
||
// The other check is that the parameters have valid values (0 or 1).
|
||
|
||
|
||
if (!(NewState & SMB_PIPE_TYPE_MESSAGE) &&
|
||
((PFILE_PIPE_INFORMATION)UsersBuffer)->ReadMode != FILE_PIPE_BYTE_STREAM_MODE ) {
|
||
*FinalStatus = STATUS_INVALID_PARAMETER;
|
||
|
||
} else if ( (((PFILE_PIPE_INFORMATION)UsersBuffer)->ReadMode |
|
||
((PFILE_PIPE_INFORMATION)UsersBuffer)->CompletionMode
|
||
) &~1) {
|
||
// Only the bottom bit can be set in these paramters
|
||
*FinalStatus = STATUS_INVALID_PARAMETER;
|
||
|
||
} else {
|
||
|
||
// Parameters are ok.
|
||
|
||
if ( ((PFILE_PIPE_INFORMATION)UsersBuffer)->ReadMode == FILE_PIPE_MESSAGE_MODE) {
|
||
NewState |= SMB_PIPE_READMODE_MESSAGE; // Message mode
|
||
} else {
|
||
NewState &= ~SMB_PIPE_READMODE_MESSAGE; // Byte mode
|
||
}
|
||
if ( ((PFILE_PIPE_INFORMATION)UsersBuffer)->CompletionMode == FILE_PIPE_COMPLETE_OPERATION ) {
|
||
NewState |= SMB_PIPE_NOWAIT; // if no data then return immediately
|
||
} else {
|
||
NewState &= ~SMB_PIPE_NOWAIT; // ie wait for data
|
||
}
|
||
|
||
// If request changed the mode of the pipe, tell the server.
|
||
|
||
if ( NewState != Icb->u.p.PipeState ) {
|
||
|
||
USHORT Setup[2];
|
||
CLONG OutParameterCount = 0;
|
||
CLONG OutDataCount = 0;
|
||
CLONG OutSetupCount = 0;
|
||
|
||
UNICODE_STRING Name;
|
||
|
||
//
|
||
// From this point on we will wait for a response from the server - check
|
||
// to see if the user has allowed us to wait.
|
||
//
|
||
|
||
if (!Wait) {
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Build and initialize the Setup bytes
|
||
//
|
||
|
||
Setup[0] = TRANS_SET_NMPIPE_STATE;
|
||
Setup[1] = Icb->FileId;
|
||
|
||
//
|
||
// Build the Parameter
|
||
//
|
||
|
||
// Remove ICOUNT and Type fields from PipeState
|
||
|
||
NewState &= ~(SMB_PIPE_UNLIMITED_INSTANCES| SMB_PIPE_TYPE_MESSAGE);
|
||
|
||
RtlInitUnicodeString(&Name, L"\\PIPE\\");
|
||
|
||
*FinalStatus = RdrTransact(Irp,
|
||
Icb->Fcb->Connection,
|
||
Icb->Se,
|
||
Setup,
|
||
(CLONG) sizeof(Setup), // InSetupCount,
|
||
&OutSetupCount,
|
||
&Name,
|
||
&NewState,
|
||
sizeof(NewState),// InParameterCount,
|
||
&OutParameterCount,
|
||
NULL, // InData,
|
||
0, // InDataCount,
|
||
NULL, // OutData,
|
||
&OutDataCount,
|
||
&Icb->FileId, // Fid
|
||
0, // Timeout
|
||
0, // Flags
|
||
0,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
// If the update worked then record new mode
|
||
|
||
if (NT_SUCCESS(*FinalStatus) ) {
|
||
// Preserve the Icount & pipe type fields from the Open and X reply.
|
||
Icb->u.p.PipeState = NewState | (USHORT)( Icb->u.p.PipeState &
|
||
(SMB_PIPE_UNLIMITED_INSTANCES | SMB_PIPE_TYPE_MESSAGE));
|
||
} else {
|
||
if (*FinalStatus == STATUS_INVALID_HANDLE) {
|
||
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
||
}
|
||
|
||
}
|
||
} else {
|
||
*FinalStatus = STATUS_SUCCESS;
|
||
}
|
||
}
|
||
}
|
||
dprintf(DPRT_NP, ("RdrSetNpInfo return status: %X\n", *FinalStatus));
|
||
dprintf(DPRT_NP, ("Icb->u.p.PipeState: %lx\n", Icb->u.p.PipeState));
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
BOOLEAN
|
||
RdrSetNpRemoteInfo(
|
||
PICB Icb,
|
||
PVOID UsersBuffer,
|
||
ULONG BufferSize,
|
||
PNTSTATUS FinalStatus
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the FilePipeRemoteInformation value of the
|
||
NtSetInformationFile api. It returns the following information:
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PICB Icb - Supplies the ICB associated with this request.
|
||
|
||
OUT PVOID UsersBuffer - Supplies the user's buffer that is filled with
|
||
the values to set.
|
||
|
||
IN ULONG BufferSize - Supplies the size of the buffer. On return,
|
||
the amount of the buffer consumed is
|
||
subtracted from the initial size.
|
||
|
||
OUT PNTSTATUS FinalStatus - Status to be returned for this operation.
|
||
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - True if request is to be processed in the FSP.
|
||
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
dprintf(DPRT_NP, ("RdrSetNpRemoteInfo...\n"));
|
||
dprintf(DPRT_NP, ("RdrSetNpRemoteInfo:CollectDataTime %lx %lx, MaximumCollectionCount %lx\n",
|
||
((PFILE_PIPE_REMOTE_INFORMATION)UsersBuffer)->CollectDataTime,
|
||
((PFILE_PIPE_REMOTE_INFORMATION)UsersBuffer)->MaximumCollectionCount));
|
||
|
||
if (BufferSize < sizeof(FILE_PIPE_REMOTE_INFORMATION)) {
|
||
*FinalStatus = STATUS_BUFFER_TOO_SMALL;
|
||
} else {
|
||
|
||
Icb->u.p.CollectDataTime =
|
||
((PFILE_PIPE_REMOTE_INFORMATION)UsersBuffer)->CollectDataTime;
|
||
|
||
Icb->u.p.MaximumCollectionCount = (USHORT)
|
||
((PFILE_PIPE_REMOTE_INFORMATION)UsersBuffer)->MaximumCollectionCount;
|
||
*FinalStatus = STATUS_SUCCESS;
|
||
}
|
||
dprintf(DPRT_NP, ("RdrSetNpRemoteInfo return status: %X\n", *FinalStatus));
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
RdrNpWait (
|
||
IN BOOLEAN Wait,
|
||
IN BOOLEAN InFsd,
|
||
IN PIRP Irp,
|
||
IN PICB Icb,
|
||
IN PVOID InputBuffer,
|
||
IN ULONG InputBufferLength
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine waits for a server to have a listen outstanding
|
||
on a remote named pipe. Note, there is a window between the successful
|
||
return of this routine and the caller trying to open. The listen
|
||
may go to another caller.
|
||
|
||
Arguments:
|
||
|
||
Irp,IrpSp - Supplies the request being processed
|
||
|
||
Wait - Indicates if we are allowed to block for a resource
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - An appropriate return status (STATUS_PENDING means
|
||
queue request to fsp).
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS Status;
|
||
|
||
PFILE_PIPE_WAIT_FOR_BUFFER NpWaitBuffer = InputBuffer;
|
||
|
||
PFCB Fcb = Icb->Fcb;
|
||
|
||
// Variables for the connection to the server
|
||
WCHAR NameBuffer[MAXIMUM_FILENAME_LENGTH];
|
||
|
||
// Variables for the Transaction
|
||
USHORT Setup[2];
|
||
UCHAR Parameters[1];
|
||
CLONG OutParameterCount = 0;
|
||
CLONG OutDataCount = 0;
|
||
CLONG OutSetupCount = 0;
|
||
UNICODE_STRING Name; // Gets modified by RdrCopyNetworkPath
|
||
ULONG Timeout;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER(InFsd);
|
||
|
||
dprintf(DPRT_NP, ("RdrNpWait...\n"));
|
||
|
||
//
|
||
// Extract the important fields from the IrpSp
|
||
//
|
||
|
||
dprintf( DPRT_NP, ("InputBufferLength = %lx\n", InputBufferLength));
|
||
|
||
//
|
||
// Decode the file object to figure out who we are. To wait for a
|
||
// Listen, the caller must open "\Device\LanmanRedirector\"
|
||
//
|
||
|
||
if ((Icb->Type != NamedPipe) &&
|
||
(Icb->Type != TreeConnect )) {
|
||
Status = STATUS_INVALID_DEVICE_REQUEST;
|
||
dprintf(DPRT_NP, ("RdrNpWait returning status1: %X\n", Status));
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// Reference the system buffer as a NpWait and make sure it's large enough
|
||
// and not so large that we would trash the stack.
|
||
// Initially check its large enough to include the NameLength.
|
||
//
|
||
|
||
if ((InputBufferLength <
|
||
(ULONG)FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name[0])) ||
|
||
(InputBufferLength <
|
||
(ULONG)FIELD_OFFSET(FILE_PIPE_WAIT_FOR_BUFFER, Name[0])+NpWaitBuffer->NameLength) ||
|
||
InputBufferLength > MAXIMUM_FILENAME_LENGTH + 1
|
||
) {
|
||
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
dprintf(DPRT_NP, ("RdrNpWait returning status2: %X\n", Status));
|
||
return Status;
|
||
|
||
}
|
||
|
||
//
|
||
// Connect to the server if necessary
|
||
//
|
||
|
||
// The timeout must be converted from an NT delay to milliseconds
|
||
|
||
if ( NpWaitBuffer->TimeoutSpecified ) {
|
||
LARGE_INTEGER TimeWorkspace;
|
||
LARGE_INTEGER WaitForever;
|
||
|
||
WaitForever.LowPart = 0;
|
||
WaitForever.HighPart =0x80000000;
|
||
|
||
//
|
||
// Avoid negate of "WaitForever" since this generates an integer
|
||
// overflow exception on some machines.
|
||
//
|
||
//
|
||
|
||
|
||
if (NpWaitBuffer->Timeout.QuadPart == WaitForever.QuadPart) {
|
||
|
||
Timeout = 0xfffffffe; // Maximum we can request
|
||
|
||
} else {
|
||
|
||
TimeWorkspace.QuadPart = -NpWaitBuffer->Timeout.QuadPart / 10000;
|
||
|
||
if ( TimeWorkspace.HighPart ) {
|
||
|
||
// Tried to specify a larger timeout than we can select.
|
||
|
||
Timeout = 0xfffffffe; // Maximum we can request
|
||
|
||
} else {
|
||
|
||
Timeout = TimeWorkspace.LowPart;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
dprintf(DPRT_NP, ("RdrNpWait Timeout requested: %lx, %lx selected: %lx\n",
|
||
NpWaitBuffer->Timeout, Timeout));
|
||
|
||
} else {
|
||
|
||
Timeout = 0; // Let server choose default for this pipe
|
||
|
||
}
|
||
|
||
//
|
||
// From this point on we will wait for a response from the server - check
|
||
// to see if the user has allowed us to wait.
|
||
//
|
||
|
||
if (!Wait) {
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
//
|
||
// Build and initialize the Setup bytes
|
||
//
|
||
|
||
Setup[0] = TRANS_WAIT_NMPIPE;
|
||
Setup[1] = NPPRIORITY;
|
||
|
||
//
|
||
// Build and initialize the Parameters
|
||
//
|
||
// - No parameters to send on NpWait
|
||
|
||
//
|
||
// Build the Name from the filename.
|
||
//
|
||
|
||
wcscpy(NameBuffer, L"\\PIPE\\");
|
||
|
||
RtlCopyMemory(NameBuffer+6, NpWaitBuffer->Name, NpWaitBuffer->NameLength);
|
||
|
||
Name.Length = (USHORT)(NpWaitBuffer->NameLength + 12);
|
||
Name.MaximumLength = (USHORT)sizeof( NameBuffer );
|
||
Name.Buffer = NameBuffer;
|
||
|
||
dprintf(DPRT_NP, ("RdrNpWait pipename %wZ\n", &Name));
|
||
|
||
Status = RdrTransact(Irp,
|
||
Icb->Fcb->Connection,
|
||
Icb->Se,
|
||
Setup,
|
||
(CLONG) sizeof(Setup), // InSetupCount,
|
||
&OutSetupCount,
|
||
&Name,
|
||
Parameters,
|
||
0, // InParameterCount,
|
||
&OutParameterCount,
|
||
NULL, // InData,
|
||
0, // InDataCount,
|
||
NULL, // OutData,
|
||
&OutDataCount,
|
||
NULL, // Fid
|
||
Timeout, // Timeout
|
||
0, // Flags
|
||
0,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
dprintf(DPRT_NP, ("RdrNpWait returning status6: %X\n", Status));
|
||
return Status;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
RdrNpFlushBuffers (
|
||
IN BOOLEAN Wait,
|
||
PIRP Irp,
|
||
PICB Icb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine waits for all data on a pipe to be written to the
|
||
application at the server.
|
||
|
||
Arguments:
|
||
|
||
IN BOOLEAN Wait - Indicates if we are allowed to block for a resource.
|
||
|
||
IN PIRP Irp - Supplies the request being processed.
|
||
|
||
IN PICB Icb - Supplies the ICB associated with this request.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - An appropriate return status (STATUS_PENDING means
|
||
queue request to fsp).
|
||
|
||
Notes:
|
||
|
||
This is a synchronous operation that could take a very long time
|
||
so we will be using the callers thread to block and not Fsp threads.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
NTSTATUS Status;
|
||
PSMB_BUFFER SMBBuffer;
|
||
PSMB_HEADER Smb;
|
||
PREQ_FLUSH FlushFile;
|
||
PMDL SendMDL;
|
||
ULONG SendLength;
|
||
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_NP, ("RdrNpFlushBuffers...\n"));
|
||
|
||
// If we have write behind then flush the buffer.
|
||
|
||
if (( Icb->NonPagedFcb->FileType == FileTypeByteModePipe ) &&
|
||
( Icb->u.p.PipeState & SMB_PIPE_NOWAIT )){
|
||
// Prevent 2 threads corrupting Icb->u.p.WriteData
|
||
|
||
if ( !RdrNpAcquireExclusive ( Wait, &Icb->u.p.WriteData.Semaphore ) ) {
|
||
// Another thread is accessing the pipe handle and !Wait
|
||
InternalError(("Failed Exclusive access with Wait==TRUE"));
|
||
}
|
||
|
||
Status = RdrNpWriteFlush ( Irp, Icb, TRUE );
|
||
|
||
RdrNpRelease ( &Icb->u.p.WriteData.Semaphore );
|
||
|
||
if ( !NT_SUCCESS( Status ) ){
|
||
return Status;
|
||
}
|
||
}
|
||
|
||
|
||
ASSERT( Wait );
|
||
|
||
if ((SMBBuffer = RdrAllocateSMBBuffer())==NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
Smb = (PSMB_HEADER ) SMBBuffer->Buffer;
|
||
|
||
//
|
||
// Build the SMB
|
||
//
|
||
|
||
Smb->Command = SMB_COM_FLUSH;
|
||
|
||
FlushFile = (PREQ_FLUSH ) (Smb+1);
|
||
|
||
FlushFile->WordCount = 1;
|
||
|
||
//
|
||
// We put a hard coded search attributes of 0x16 in the
|
||
// SMB.
|
||
//
|
||
|
||
SmbPutUshort(&FlushFile->Fid, Icb->FileId);
|
||
|
||
SmbPutUshort( &FlushFile->ByteCount, 0);
|
||
|
||
SendLength = FlushFile->Buffer - (PUCHAR )(Smb);
|
||
|
||
SendMDL = SMBBuffer->Mdl;
|
||
|
||
SendMDL->ByteCount = SendLength;
|
||
|
||
Status = RdrNetTranceive(NT_NORMAL | NT_NORECONNECT, // Flags
|
||
Irp,
|
||
Icb->Fcb->Connection,
|
||
SendMDL,
|
||
NULL, // Only interested in the error code.
|
||
Icb->Se);
|
||
|
||
RdrFreeSMBBuffer(SMBBuffer);
|
||
|
||
if (Status == STATUS_INVALID_HANDLE) {
|
||
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
||
}
|
||
|
||
dprintf(DPRT_NP, ("RdrNpFlushBuffers returning status: %X\n", Status));
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
RdrNpCachedRead (
|
||
IN BOOLEAN Wait,
|
||
IN BOOLEAN InFsd,
|
||
IN PFS_DEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
OUT PBOOLEAN Processed,
|
||
OUT PULONG TotalDataRead
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes the NtRead request for a NamedPipe where
|
||
potentially the request will be buffered.
|
||
|
||
Arguments:
|
||
|
||
Wait - True iff FSD can wait for IRP to complete.
|
||
InFsd - True iff the request is coming from the FSD.
|
||
DriverObject - Supplies a pointer to the redirector driver object.
|
||
Irp - Supplies a pointer to the IRP to be processed.
|
||
Processed - False iff standard read code to be used for this request.
|
||
TotalDataRead - Returns value for IoStatus.Information.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The FSD status for this Irp.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
PICB Icb = ICB_OF(IrpSp);
|
||
USHORT Length = (USHORT)IrpSp->Parameters.Read.Length;
|
||
PVOID BufferAddress; // Mapped buffer address for reads.
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
BOOLEAN Acquired;
|
||
|
||
PAGED_CODE();
|
||
|
||
*Processed = TRUE; // Assume we will process this request.
|
||
*TotalDataRead = 0;
|
||
dprintf(DPRT_NP, ("RdrNpCachedRead...\n"));
|
||
|
||
// Prevent 2 threads corrupting Icb->
|
||
if ( !RdrNpAcquireExclusive ( Wait, &Icb->u.p.ReadData.Semaphore ) ) {
|
||
// Another thread is accessing the pipe handle and !Wait
|
||
ASSERT(InFsd);
|
||
|
||
//
|
||
// Allocate an MDL to describe the users buffer before we pass the
|
||
// request to the FSP.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status = RdrLockUsersBuffer(Irp, IoWriteAccess, Length))) {
|
||
dprintf(DPRT_NP, ("RdrNpCachedRead failed to lock buffer\n"));
|
||
return Status;
|
||
}
|
||
|
||
dprintf(DPRT_NP, ("RdrNpCachedRead queue to Fsp\n"));
|
||
RdrFsdPostToFsp(DeviceObject, Irp);
|
||
return STATUS_PENDING;
|
||
}
|
||
Acquired = TRUE; // must call RdrNpRelease before exit.
|
||
|
||
try {
|
||
|
||
// If read ahead buffer is empty then fill it.
|
||
|
||
if ( !Icb->u.p.ReadData.Length ) {
|
||
|
||
//
|
||
// If there is a danger of flooding the network with this
|
||
// request because the remote application has no data in the
|
||
// pipe then respond directly to the caller that there is no data
|
||
//
|
||
|
||
if ( RdrBackOff ( &Icb->u.p.BackOff ) ) {
|
||
ASSERT( Status == STATUS_SUCCESS );
|
||
try_return( Status );
|
||
}
|
||
|
||
// Do read ahead into the Icb.u.p.ReadData.Buffer
|
||
|
||
if (!Wait ) {
|
||
|
||
//
|
||
// If we must read and cannot wait then the Fsp must fill the buffer
|
||
// must free the semaphore before the Fsp Acquires the semaphore.
|
||
//
|
||
|
||
RdrNpRelease ( &Icb->u.p.ReadData.Semaphore );
|
||
Acquired = FALSE;
|
||
|
||
//
|
||
// Allocate an MDL to describe the users buffer before we pass the
|
||
// request to the FSP.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status = RdrLockUsersBuffer(Irp, IoWriteAccess, Length))) {
|
||
dprintf(DPRT_NP, ("RdrNpCachedRead failed to lock buffer1\n"));
|
||
try_return( Status );
|
||
}
|
||
|
||
dprintf(DPRT_NP, ("RdrNpCachedRead queue to Fsp1\n"));
|
||
RdrFsdPostToFsp(DeviceObject, Irp);
|
||
try_return( Status = STATUS_PENDING );
|
||
}
|
||
|
||
if ( Length > Icb->u.p.ReadData.MaximumLength ) {
|
||
|
||
//
|
||
// Too big for readahead buffer. Assume that the caller
|
||
// knows there is a good chance theres lots of data and
|
||
// pass the request to the standard read logic.
|
||
//
|
||
|
||
*Processed = FALSE;
|
||
ASSERT( Status == STATUS_SUCCESS );
|
||
try_return( Status );
|
||
}
|
||
|
||
ASSERT( Icb->u.p.ReadData.MaximumLength );
|
||
|
||
Status = CoreNpRead ( Irp,
|
||
Icb,
|
||
Icb->u.p.ReadData.MaximumLength,
|
||
Icb->u.p.ReadData.Buffer,
|
||
&Icb->u.p.ReadData.Length);
|
||
|
||
// Next byte to be read from ReadData.Buffer is at the start of the buffer
|
||
Icb->u.p.ReadData.Offset = 0;
|
||
|
||
if ( ( Icb->u.p.ReadData.Length == 0 ) ||
|
||
!NT_SUCCESS(Status) ) {
|
||
|
||
if ( Status == STATUS_INSUFFICIENT_RESOURCES ) {
|
||
// Let the normal non-cached read try as a fall back
|
||
*Processed = FALSE;
|
||
} else {
|
||
RdrBackPackFailure( &Icb->u.p.BackOff );
|
||
ASSERT( Icb->u.p.ReadData.Length == 0 );
|
||
}
|
||
try_return( Status );
|
||
|
||
}
|
||
|
||
//
|
||
// If we have been backing off the user then receiving data
|
||
// swiches the backoff delta back to zero
|
||
//
|
||
|
||
RdrBackPackSuccess( &Icb->u.p.BackOff );
|
||
}
|
||
|
||
if ( Icb->u.p.ReadData.Length ) {
|
||
|
||
//
|
||
// There is read ahead data available so satisfy the user with
|
||
// this data.
|
||
|
||
USHORT TransferLength = MIN ( Length, Icb->u.p.ReadData.Length );
|
||
BOOLEAN BufferMapped;
|
||
|
||
|
||
|
||
try {
|
||
BufferMapped = RdrMapUsersBuffer(Irp, &BufferAddress, TransferLength);
|
||
#if RDRDBG
|
||
IFDEBUG(NP) {
|
||
dprintf( DPRT_NP, ("Read: read buffer contents\n"));
|
||
dprintf(DPRT_NP, ("Offset: %lx, Length %lx\n",
|
||
Icb->u.p.ReadData.Offset,
|
||
Icb->u.p.ReadData.Length));
|
||
|
||
ndump_core(Icb->u.p.ReadData.Buffer, Icb->u.p.ReadData.MaximumLength );
|
||
}
|
||
#endif
|
||
|
||
|
||
RtlCopyMemory(BufferAddress,
|
||
&Icb->u.p.ReadData.Buffer[Icb->u.p.ReadData.Offset],
|
||
TransferLength);
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
if (BufferMapped) {
|
||
RdrUnMapUsersBuffer(Irp, BufferAddress);
|
||
}
|
||
try_return( Status );
|
||
}
|
||
|
||
if (BufferMapped) {
|
||
RdrUnMapUsersBuffer(Irp, BufferAddress);
|
||
}
|
||
|
||
//
|
||
// The copy worked, return success to the caller.
|
||
//
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
Icb->u.p.ReadData.Offset += (USHORT)TransferLength;
|
||
Icb->u.p.ReadData.Length -= (USHORT)TransferLength;
|
||
*TotalDataRead = TransferLength;
|
||
|
||
try_return( Status );
|
||
|
||
} // else return no data
|
||
|
||
try_return( Status);
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
if ( Acquired ) {
|
||
RdrNpRelease ( &Icb->u.p.ReadData.Semaphore );
|
||
}
|
||
dprintf(DPRT_NP, ("RdrNpCachedRead returning status: %X processed: %lx\n",
|
||
Status, *Processed));
|
||
|
||
}
|
||
return Status;
|
||
}
|
||
|
||
DBGSTATIC
|
||
NTSTATUS
|
||
CoreNpRead (
|
||
IN PIRP Irp,
|
||
IN PICB Icb,
|
||
IN ULONG Length,
|
||
IN PUCHAR Buffer,
|
||
OUT PUSHORT AmountActuallyRead
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine uses the core SMB read protocol to read from the specified
|
||
file.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PIRP Irp - Supplies an IRP to use for the raw read request.
|
||
IN PICB Icb - Supplies an ICB for the file to read.
|
||
IN ULONG Length - Supplies the total number of bytes to read.
|
||
IN PUCHAR Buffer - Supplies where to put the data
|
||
OUT PULONG AmountActuallyRead - Returns the number of bytes read.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Status of read request.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PSMB_BUFFER SendSmbBuffer = NULL;
|
||
PSMB_BUFFER ReceiveSmbBuffer = NULL;
|
||
PSMB_HEADER Smb;
|
||
PRESP_READ ReadResponse; // Pointer to read information in SMB
|
||
PREQ_READ Read;
|
||
PMDL DataMdl; // MDL mapped into user's buffer.
|
||
NTSTATUS Status;
|
||
ULONG SrvReadSize = Icb->Fcb->Connection->Server->BufferSize -
|
||
(sizeof(SMB_HEADER)+sizeof(RESP_READ));
|
||
USHORT AmountRequestedToRead = (USHORT )MIN(Length, SrvReadSize);
|
||
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_NP, ("CoreNpRead...\n"));
|
||
|
||
//
|
||
// Allocate an SMB buffer for the read operation.
|
||
//
|
||
|
||
if ((SendSmbBuffer = RdrAllocateSMBBuffer())==NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto ReturnError;
|
||
}
|
||
|
||
//
|
||
// Also allocate one to hold the response SMB buffer header.
|
||
//
|
||
|
||
if ((ReceiveSmbBuffer = RdrAllocateSMBBuffer())==NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto ReturnError;
|
||
}
|
||
|
||
ASSERT (AmountRequestedToRead <= 0xffff);
|
||
|
||
Smb = (PSMB_HEADER )(SendSmbBuffer->Buffer);
|
||
|
||
Smb->Command = SMB_COM_READ;
|
||
|
||
Read = (PREQ_READ )(Smb+1);
|
||
|
||
Read->WordCount = 5;
|
||
SmbPutUshort(&Read->Fid, Icb->FileId);
|
||
SmbPutUshort(&Read->Count, (USHORT )MIN(0xffff, Length));
|
||
SmbPutUshort(&Read->Remaining, (USHORT )MIN(0xffff, Length));
|
||
SmbPutUlong(&Read->Offset, 0);
|
||
SmbPutUshort(&Read->ByteCount, 0);
|
||
|
||
dprintf(DPRT_READWRITE, ("Read %x bytes, %x remaining (%lx), offset %lx\n", Read->Count, Read->Remaining,
|
||
Length, 0));
|
||
|
||
//
|
||
// Set the number of bytes to send in this request.
|
||
//
|
||
|
||
SendSmbBuffer->Mdl->ByteCount = sizeof(SMB_HEADER)+sizeof(REQ_READ);
|
||
|
||
//
|
||
// Set the size of the data to be received into the SMB buffer.
|
||
//
|
||
|
||
ReceiveSmbBuffer->Mdl->ByteCount=
|
||
sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ, Buffer[0]);
|
||
|
||
//
|
||
// Allocate an MDL large enough to hold the request, lock down the pages.
|
||
//
|
||
|
||
DataMdl = IoAllocateMdl(Buffer,
|
||
Length,
|
||
FALSE, // Secondary Buffer
|
||
FALSE, // Charge Quota
|
||
NULL);
|
||
|
||
if (DataMdl == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto ReturnError;
|
||
}
|
||
|
||
try {
|
||
MmProbeAndLockPages( DataMdl,
|
||
KernelMode,
|
||
IoWriteAccess );
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
IoFreeMdl(DataMdl);
|
||
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto ReturnError;
|
||
}
|
||
|
||
//
|
||
// Now link this new MDL into the SMB buffer we allocated for
|
||
// the receive.
|
||
//
|
||
|
||
ReceiveSmbBuffer->Mdl->Next = DataMdl;
|
||
|
||
Status = RdrNetTranceive(NT_NORMAL | NT_NORECONNECT, // Flags
|
||
Irp,
|
||
Icb->Fcb->Connection,
|
||
SendSmbBuffer->Mdl,
|
||
ReceiveSmbBuffer->Mdl,
|
||
Icb->Se);
|
||
|
||
MmUnlockPages( DataMdl );
|
||
|
||
IoFreeMdl(DataMdl);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
ReadResponse = (PRESP_READ )(((PSMB_HEADER )ReceiveSmbBuffer->Buffer)+1);
|
||
|
||
*AmountActuallyRead = SmbGetUshort(&ReadResponse->Count);
|
||
|
||
} else {
|
||
if (Status == STATUS_INVALID_HANDLE) {
|
||
RdrInvalidateFileId(Icb->NonPagedFcb, Icb->FileId);
|
||
}
|
||
}
|
||
|
||
ReturnError:
|
||
|
||
if (SendSmbBuffer!=NULL) {
|
||
RdrFreeSMBBuffer(SendSmbBuffer);
|
||
}
|
||
|
||
if (ReceiveSmbBuffer!=NULL) {
|
||
RdrFreeSMBBuffer(ReceiveSmbBuffer);
|
||
}
|
||
|
||
dprintf(DPRT_NP, ("CoreNpRead returning status: %X\n", Status));
|
||
return Status;
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
RdrNpCachedWrite (
|
||
IN BOOLEAN Wait,
|
||
IN BOOLEAN InFsd,
|
||
IN PFS_DEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
OUT PBOOLEAN Processed
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes the NtWrite request for a NamedPipe where
|
||
potentially the request will be buffered.
|
||
|
||
Arguments:
|
||
|
||
Wait - True iff FSD can wait for IRP to complete.
|
||
InFsd - True iff the request is coming from the FSD.
|
||
DriverObject - Supplies a pointer to the redirector driver object.
|
||
Irp - Supplies a pointer to the IRP to be processed.
|
||
Processed - False iff standard write code to be used for this request.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - The FSD status for this Irp.
|
||
|
||
Notes:
|
||
|
||
This routine attempts to stop the caller from flooding the network with
|
||
small write requests and with write requests that the server has no room
|
||
in its buffers for. It uses a write behind buffer to pack small requests
|
||
into a larger transfer over the network. It uses the backoff package to
|
||
notice when the replies from the server indicate no data is being written
|
||
into the pipe.
|
||
|
||
There are several 3 cases that are handled:
|
||
|
||
1) The data will not fit in the buffer and the buffer is not empty.
|
||
Flush the buffer with 1 SMB and then use the standard write code to
|
||
output the callers data directly from the callers buffer.
|
||
|
||
2) The request will go in the buffer. Add the data to the buffer,
|
||
If the buffer has reached the threshold then send it over the network.
|
||
If the threshold is not reached and the buffer was empty then start
|
||
the timer so that the data will be written even if no further writes
|
||
occur.
|
||
|
||
3) The data will not fit in the buffer and the buffer is empty.
|
||
Use the standard write code to output the callers data directly
|
||
from the callers buffer.
|
||
|
||
Using the normal write code to do transfers over MaximumLength simplifies
|
||
this routine since it does not need to cope with situations such as
|
||
the write being bigger than the servers negotiated buffer size.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
PICB Icb = ICB_OF(IrpSp);
|
||
PSECURITY_ENTRY Se = Icb->Se;
|
||
USHORT Length = (USHORT)IrpSp->Parameters.Write.Length;
|
||
LARGE_INTEGER ByteOffset = IrpSp->Parameters.Write.ByteOffset;
|
||
ULONG TotalDataWritten = 0;
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
PVOID BufferAddress; // Mapped buffer address for writes.
|
||
BOOLEAN Acquired;
|
||
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_NP, ("RdrNpCachedWrite...\n"));
|
||
*Processed = TRUE; // Assume we will process this request.
|
||
|
||
// Prevent 2 threads corrupting Icb->
|
||
if ( !RdrNpAcquireExclusive ( Wait, &Icb->u.p.WriteData.Semaphore ) ) {
|
||
// Another thread is accessing the pipe handle and !Wait
|
||
ASSERT(InFsd);
|
||
|
||
//
|
||
// Allocate an MDL to describe the users buffer before we pass the
|
||
// request to the FSP.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status = RdrLockUsersBuffer(Irp, IoReadAccess, Length))) {
|
||
return Status;
|
||
}
|
||
|
||
RdrFsdPostToFsp(DeviceObject, Irp);
|
||
return STATUS_PENDING;
|
||
}
|
||
|
||
Acquired = TRUE; // must call RdrNpRelease before exit.
|
||
|
||
try {
|
||
|
||
//
|
||
// If Write behind buffer will overflow, then empty it.
|
||
// When it is written then let the normal write code write
|
||
// the users request. This simplifies this routine because it never
|
||
// has to write more than the size of the write behind buffer.
|
||
//
|
||
|
||
if ( (Icb->u.p.WriteData.Length + Length) >
|
||
Icb->u.p.WriteData.MaximumLength) {
|
||
|
||
// Write behind the Icb.u.p.WriteData.Buffer
|
||
|
||
//
|
||
// If there is a danger of flooding the network with this
|
||
// request because the remote application keeps rejecting more data
|
||
// then respond directly to the caller that nothing was written.
|
||
//
|
||
|
||
if ( RdrBackOff ( &Icb->u.p.BackOff ) ) {
|
||
Irp->IoStatus.Information = 0;
|
||
ASSERT( Status == STATUS_SUCCESS );
|
||
try_return( Status );
|
||
}
|
||
|
||
if (!Wait ) {
|
||
|
||
//
|
||
// If we must write and cannot wait then the Fsp must empty the buffer
|
||
// must free the semaphore before the Fsp Acquires the semaphore.
|
||
//
|
||
|
||
RdrNpRelease ( &Icb->u.p.WriteData.Semaphore );
|
||
Acquired = FALSE;
|
||
|
||
//
|
||
// Allocate an MDL to describe the users buffer before we pass the
|
||
// request to the FSP.
|
||
//
|
||
|
||
if (!NT_SUCCESS(Status = RdrLockUsersBuffer(Irp, IoReadAccess, Length))) {
|
||
try_return( Status );
|
||
}
|
||
|
||
RdrFsdPostToFsp(DeviceObject, Irp);
|
||
try_return( Status = STATUS_PENDING );
|
||
}
|
||
|
||
Status = RdrNpWriteFlush ( Irp, Icb, FALSE );
|
||
|
||
//
|
||
// If theres anything still in the write behind buffer then
|
||
// return nothing written to the caller - the pipe is full.
|
||
//
|
||
|
||
if ( Icb->u.p.WriteData.Length ) {
|
||
Irp->IoStatus.Information = 0;
|
||
try_return( Status );
|
||
}
|
||
|
||
//
|
||
// Tell normal write code to process the request straight from
|
||
// the callers buffer.
|
||
//
|
||
|
||
*Processed = FALSE;
|
||
try_return( Status = STATUS_SUCCESS );
|
||
|
||
}
|
||
|
||
//
|
||
// If the request will fit in the buffer then move it into the write
|
||
// behind buffer.
|
||
//
|
||
|
||
if ( (Icb->u.p.WriteData.Length + Length) <=
|
||
Icb->u.p.WriteData.MaximumLength) {
|
||
BOOLEAN BufferMapped;
|
||
|
||
// Do Write behind into the Icb.u.p.WriteData.Buffer
|
||
|
||
if ((Length + Icb->u.p.WriteData.Length) >
|
||
Icb->u.p.MaximumCollectionCount && !Wait) {
|
||
|
||
// We will need to flush the buffer so pass to the Fsp.
|
||
|
||
if (NT_SUCCESS(Status = RdrLockUsersBuffer(Irp, IoReadAccess, Length))) {
|
||
Status = STATUS_PENDING;
|
||
RdrFsdPostToFsp(DeviceObject, Irp);
|
||
}
|
||
try_return( Status );
|
||
}
|
||
|
||
|
||
try {
|
||
BufferMapped = RdrMapUsersBuffer(Irp, &BufferAddress, Length);
|
||
|
||
RtlCopyMemory(
|
||
&Icb->u.p.WriteData.Buffer[Icb->u.p.WriteData.Length],
|
||
BufferAddress,
|
||
Length);
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
if (BufferMapped) {
|
||
RdrUnMapUsersBuffer(Irp, BufferAddress);
|
||
}
|
||
try_return( Status );
|
||
}
|
||
|
||
if (BufferMapped) {
|
||
RdrUnMapUsersBuffer(Irp, BufferAddress);
|
||
}
|
||
|
||
//
|
||
// Update record to indicate that there is data in the write behind
|
||
// buffer.
|
||
//
|
||
|
||
Icb->u.p.WriteData.Length += (USHORT)Length;
|
||
|
||
if ( Icb->u.p.WriteData.Length >
|
||
(USHORT)Icb->u.p.MaximumCollectionCount ) {
|
||
// We have reached the threshold, attempt to write the buffer.
|
||
|
||
Status = RdrNpWriteFlush ( Irp, Icb, FALSE );
|
||
try_return( Status );
|
||
}
|
||
|
||
//
|
||
// If the write behind buffer was empty then we need to start the
|
||
// timeout so that if no more writes are performed then the data
|
||
// will be written.
|
||
//
|
||
|
||
if ( Icb->u.p.TimeoutRunning == FALSE ) {
|
||
NpStartTimer(Icb);
|
||
}
|
||
|
||
try_return( Status = STATUS_SUCCESS );
|
||
|
||
}
|
||
|
||
//
|
||
// Else Buffer empty and request will not fit into the buffer.
|
||
// Tell normal write code to process the request straight from
|
||
// the callers buffer.
|
||
//
|
||
|
||
*Processed = FALSE;
|
||
try_return( Status = STATUS_SUCCESS );
|
||
|
||
try_exit: NOTHING;
|
||
} finally {
|
||
|
||
if ( Acquired ) {
|
||
RdrNpRelease ( &Icb->u.p.WriteData.Semaphore );
|
||
}
|
||
|
||
}
|
||
dprintf(DPRT_NP, ("RdrNpCachedWrite returning status: %X processed: %lx\n",
|
||
Status, *Processed));
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
RdrNpWriteFlush (
|
||
IN PIRP Irp OPTIONAL,
|
||
IN PICB Icb,
|
||
IN BOOLEAN Forever
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine uses the core SMB write protocol to empty the write behind
|
||
buffer.
|
||
|
||
|
||
Arguments:
|
||
|
||
IN PIRP Irp - Supplies an IRP to use for the raw read request.
|
||
IN PICB Icb - Supplies an ICB for the file to read.
|
||
IN BOOLEAN Forever - if TRUE wait for output to drain until it goes or
|
||
there is a network error.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Status of read request.
|
||
|
||
Note:
|
||
The Write buffer must be RdrNpAcquireExclusive before calling this
|
||
===============================================================
|
||
routine.
|
||
========
|
||
|
||
--*/
|
||
|
||
{
|
||
PMDL DataMdl;
|
||
BOOLEAN AllWriteDataWritten;
|
||
ULONG AmountActuallyWritten = 0; // Amount actually written to the pipe.
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_NP, ("RdrNpWriteFlush....\n"));
|
||
|
||
// If the buffer is already empty then finished.
|
||
if ( !Icb->u.p.WriteData.Length) {
|
||
return Status;
|
||
}
|
||
|
||
//
|
||
// It is imperative that there is no timeout running when this flush
|
||
// completes if we have been called due to a cleanup Irp. This includes
|
||
// when the timeout has fired and is in the Dpc queue. In this case
|
||
// RdrNpCancelTimer will return FALSE. This is not a problem since by
|
||
// the time that the Smb that we are sending to the remote server
|
||
// completes the timeout must have come off the front of the Dpc queue.
|
||
//
|
||
|
||
if ( RdrNpCancelTimer ( Icb ) == FALSE ) {
|
||
return STATUS_DRIVER_INTERNAL_ERROR;
|
||
}
|
||
|
||
DataMdl = IoAllocateMdl(Icb->u.p.WriteData.Buffer,
|
||
Icb->u.p.WriteData.Length,
|
||
FALSE, // Secondary Buffer
|
||
FALSE, // Charge Quota
|
||
NULL);
|
||
|
||
if (DataMdl == NULL) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
//
|
||
// Lock the pages associated with the MDL that we just allocated.
|
||
//
|
||
|
||
try {
|
||
MmProbeAndLockPages( DataMdl,
|
||
KernelMode,
|
||
IoReadAccess );
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
do {
|
||
|
||
dprintf(DPRT_NP, ("RdrNpWriteFlush length: %lx\n", Icb->u.p.WriteData.Length));
|
||
|
||
Status = RdrCoreWrite( Irp,
|
||
Icb->u.p.FileObject,
|
||
DataMdl,
|
||
Icb->u.p.WriteData.Buffer,
|
||
Icb->u.p.WriteData.Length,
|
||
RdrZero, // IOOffset
|
||
TRUE,
|
||
NULL,
|
||
NULL,
|
||
&AllWriteDataWritten,
|
||
&AmountActuallyWritten);
|
||
|
||
// Shuffle the remaining contents of the buffer to the start
|
||
if ( Icb->u.p.WriteData.Length - AmountActuallyWritten ) {
|
||
RtlCopyMemory( Icb->u.p.WriteData.Buffer,
|
||
Icb->u.p.WriteData.Buffer+AmountActuallyWritten,
|
||
Icb->u.p.WriteData.Length - AmountActuallyWritten);
|
||
}
|
||
|
||
Icb->u.p.WriteData.Length -= (USHORT)AmountActuallyWritten;
|
||
|
||
} while ( Icb->u.p.WriteData.Length && NT_SUCCESS(Status) && Forever);
|
||
|
||
//
|
||
// Undo the effect of MmProbeAndLockBuffers and free the DataMdl
|
||
//
|
||
|
||
MmUnlockPages( DataMdl );
|
||
|
||
IoFreeMdl(DataMdl);
|
||
|
||
if ( Icb->u.p.WriteData.Length && NT_SUCCESS(Status) && !Forever ) {
|
||
// It did'nt all go for some reason so kick off the timer again
|
||
NpStartTimer ( Icb );
|
||
}
|
||
|
||
dprintf(DPRT_NP, ("RdrNpWriteFlush returning status: %X\n", Status));
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
|
||
DBGSTATIC
|
||
VOID
|
||
NpStartTimer (
|
||
IN PICB Icb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets an event so that when the timer expires it calls
|
||
flush on an Icb write behind buffer.
|
||
|
||
Arguments:
|
||
|
||
IN PICB Icb - Supplies an ICB for the file to read.
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL OldIrql;
|
||
|
||
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
||
dprintf(DPRT_NP, ("NpStartTimer....\n"));
|
||
|
||
ACQUIRE_SPIN_LOCK(&Icb->u.p.TimerLock, &OldIrql );
|
||
|
||
if ( Icb->u.p.TimeoutRunning == TRUE ) {
|
||
|
||
RELEASE_SPIN_LOCK(&Icb->u.p.TimerLock, OldIrql );
|
||
|
||
dprintf(DPRT_NP, ("NpStartTimer:already running\n"));
|
||
|
||
return;
|
||
}
|
||
|
||
Icb->u.p.TimeoutCancelled = FALSE;
|
||
|
||
RELEASE_SPIN_LOCK(&Icb->u.p.TimerLock, OldIrql );
|
||
|
||
RdrNpRelease ( &Icb->u.p.WriteData.Semaphore );
|
||
|
||
KeWaitForSingleObject( &Icb->u.p.TimerDone,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE, // Don't receive Alerts
|
||
NULL);
|
||
if ( !RdrNpAcquireExclusive ( TRUE, &Icb->u.p.WriteData.Semaphore ) ) {
|
||
|
||
// Another thread is accessing the pipe handle and !Wait
|
||
|
||
InternalError(("Failed Exclusive access with Wait==TRUE"));
|
||
}
|
||
|
||
ACQUIRE_SPIN_LOCK(&Icb->u.p.TimerLock, &OldIrql );
|
||
|
||
KeClearEvent(&Icb->u.p.TimerDone);
|
||
|
||
//
|
||
// Reference the file object during the duration of the timer.
|
||
//
|
||
|
||
ObReferenceObject(Icb->u.p.FileObject);
|
||
|
||
(VOID)KeSetTimer( &Icb->u.p.Timer,
|
||
Icb->u.p.CollectDataTime,
|
||
&Icb->u.p.Dpc );
|
||
|
||
Icb->u.p.TimeoutRunning = TRUE;
|
||
|
||
RELEASE_SPIN_LOCK(&Icb->u.p.TimerLock, OldIrql );
|
||
|
||
dprintf(DPRT_NP, ("NpStartTimer Timer running\n"));
|
||
|
||
return;
|
||
}
|
||
|
||
BOOLEAN
|
||
RdrNpCancelTimer (
|
||
IN PICB Icb
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine cancels any outstanding timeout for this write behind buffer.
|
||
|
||
It is very unlikely that this routine will return FALSE. This thread
|
||
would have to be scheduled five times in succession between the timer
|
||
firing and RdrTimedOut executing.
|
||
|
||
Arguments:
|
||
|
||
IN PICB Icb - Supplies an ICB for the file to read.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN - FALSE if could not cancel the timeout.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL OldIrql;
|
||
LONG i;
|
||
// PAGED_CODE();
|
||
|
||
dprintf(DPRT_NP, ("RdrNpCancelTimer..\n"));
|
||
|
||
for (i=0; i < 5 ; i++ ) {
|
||
|
||
ACQUIRE_SPIN_LOCK(&Icb->u.p.TimerLock, &OldIrql );
|
||
|
||
if ( Icb->u.p.TimeoutRunning == FALSE ) {
|
||
|
||
RELEASE_SPIN_LOCK(&Icb->u.p.TimerLock, OldIrql );
|
||
|
||
return TRUE; // No timeout to cancel
|
||
}
|
||
|
||
Icb->u.p.TimeoutCancelled = TRUE;
|
||
|
||
if ( KeCancelTimer( &Icb->u.p.Timer ) ) {
|
||
|
||
Icb->u.p.TimeoutRunning = FALSE;
|
||
|
||
//
|
||
// The timer isn't running any more, we can dereference the
|
||
// file object now.
|
||
//
|
||
|
||
ObDereferenceObject(Icb->u.p.FileObject);
|
||
|
||
RELEASE_SPIN_LOCK(&Icb->u.p.TimerLock, OldIrql );
|
||
|
||
KeSetEvent( &Icb->u.p.TimerDone, 0, FALSE );
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK(&Icb->u.p.TimerLock, OldIrql );
|
||
|
||
RdrNpRelease ( &Icb->u.p.WriteData.Semaphore );
|
||
|
||
dprintf(DPRT_NP, ("RdrNpCancelTimer KeCancelTimer returned FALSE\n"));
|
||
|
||
KeWaitForSingleObject( &Icb->u.p.TimerDone,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE, // Don't receive Alerts
|
||
NULL);
|
||
|
||
if ( !RdrNpAcquireExclusive ( TRUE, &Icb->u.p.WriteData.Semaphore ) ) {
|
||
|
||
// Another thread is accessing the pipe handle and !Wait
|
||
|
||
InternalError(("Failed Exclusive access with Wait==TRUE"));
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
VOID
|
||
RdrNpTimerDispatch(
|
||
IN PKDPC Dpc,
|
||
IN PVOID Contxt, //Icb
|
||
IN PVOID SystemArgument1,
|
||
IN PVOID SystemArgument2
|
||
)
|
||
|
||
{
|
||
UNREFERENCED_PARAMETER(Dpc);
|
||
UNREFERENCED_PARAMETER(SystemArgument1);
|
||
UNREFERENCED_PARAMETER(SystemArgument2);
|
||
|
||
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
||
|
||
dprintf(DPRT_NP, ("RdrNpTimerDispatch....\n"));
|
||
// Queue to the Fsp
|
||
|
||
RdrQueueWorkItem ( &((PICB)Contxt)->u.p.WorkEntry, CriticalWorkQueue );
|
||
|
||
//
|
||
// And now return to our caller
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
VOID
|
||
RdrNpTimedOut(
|
||
PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will attempt to flush the write behind data if there
|
||
are no more timeouts for this Write Buffer.
|
||
|
||
Arguments:
|
||
|
||
IN PVOID Context - Supplies the Icb.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PICB Icb = Context;
|
||
KIRQL OldIrql;
|
||
|
||
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
||
|
||
// PAGED_CODE();
|
||
|
||
dprintf(DPRT_NP, ("RdrNpTimedOut....\n"));
|
||
ACQUIRE_SPIN_LOCK(&Icb->u.p.TimerLock, &OldIrql );
|
||
Icb->u.p.TimeoutRunning = FALSE;
|
||
|
||
if ( Icb->u.p.TimeoutCancelled == FALSE ) {
|
||
|
||
RELEASE_SPIN_LOCK(&Icb->u.p.TimerLock, OldIrql );
|
||
|
||
dprintf(DPRT_NP, ("RdrNpTimerDispatch calling RdrNpAcquireExclusive....\n"));
|
||
|
||
RdrNpAcquireExclusive ( TRUE, &Icb->u.p.WriteData.Semaphore );
|
||
|
||
//
|
||
// The last thing done is to set the event. This allows through another
|
||
// start timer or allows a canceltimer to proceed.
|
||
//
|
||
|
||
KeSetEvent( &Icb->u.p.TimerDone, 0, FALSE );
|
||
|
||
dprintf(DPRT_NP, ("RdrNpTimerDispatch calling RdrNpWriteFlush....\n"));
|
||
|
||
RdrNpWriteFlush ( NULL,
|
||
Icb,
|
||
FALSE);
|
||
|
||
RdrNpRelease ( &Icb->u.p.WriteData.Semaphore );
|
||
|
||
} else {
|
||
|
||
//
|
||
// The last thing done is to set the event. This allows through another
|
||
// start timer or allows a canceltimer to proceed.
|
||
//
|
||
|
||
KeSetEvent( &Icb->u.p.TimerDone, 0, FALSE );
|
||
|
||
RELEASE_SPIN_LOCK(&Icb->u.p.TimerLock, OldIrql );
|
||
|
||
}
|
||
|
||
//
|
||
// The timer has run to completion. Dereference the file object,
|
||
// since we're done with it.
|
||
//
|
||
|
||
ObDereferenceObject(Icb->u.p.FileObject);
|
||
|
||
}
|
||
|
||
DBGSTATIC
|
||
BOOLEAN
|
||
RdrNpAcquireExclusive (
|
||
IN BOOLEAN Wait,
|
||
IN PKSEMAPHORE Semaphore
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes the NtWrite request for a NamedPipe where
|
||
potentially the request will be buffered.
|
||
|
||
Arguments:
|
||
|
||
Wait - True iff FSD can wait for IRP to complete.
|
||
Semaphore - Semaphore to claim
|
||
|
||
|
||
Return Value:
|
||
|
||
Returns FALSE if cannot wait && cannot obtain exclusive access
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
dprintf(DPRT_NP, ("RdrNpAcquireExclusive .... Semaphore: %lx\n", Semaphore));
|
||
if (!Wait) {
|
||
|
||
LARGE_INTEGER RdrZero = {0,0};
|
||
|
||
// Attempt to get lock without blocking.
|
||
|
||
if (KeWaitForSingleObject(
|
||
Semaphore,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE, // Don't receive Alerts
|
||
&RdrZero //Don't wait if Object owned elsewhere
|
||
) != STATUS_SUCCESS ) {
|
||
|
||
|
||
//
|
||
// A thread is already accessing this event and the request
|
||
// has asked not to be blocked.
|
||
//
|
||
dprintf(DPRT_NP, ("RdrNpAcquireExclusive return false Semaphore: %lx\n", Semaphore));
|
||
return FALSE;
|
||
}
|
||
|
||
// else success, access was obtained without blocking
|
||
|
||
} else {
|
||
|
||
// This thread can block if necessary
|
||
|
||
if (KeWaitForSingleObject(
|
||
Semaphore,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE, // Don't receive Alerts
|
||
NULL // Wait as long as it takes
|
||
) != STATUS_SUCCESS ) {
|
||
|
||
InternalError(("Failed Exclusive access with Wait==TRUE"));
|
||
}
|
||
}
|
||
dprintf(DPRT_NP, ("RdrNpAcquireExclusive return true Semaphore: %lx\n", Semaphore));
|
||
return TRUE;
|
||
}
|
||
|