1271 lines
34 KiB
C
1271 lines
34 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
fsdraw.c
|
||
|
||
Abstract:
|
||
|
||
This module contains routines for processing the following SMBs in
|
||
the server FSD:
|
||
|
||
Read Block Raw
|
||
Write Block Raw
|
||
|
||
The routines in this module generally work closely with the routines
|
||
in smbraw.c.
|
||
|
||
*** There is no support here for raw writes from MS-NET 1.03 clients.
|
||
There are very few of these machines in existence, and raw mode
|
||
is only a performance issue, so it is not worth the trouble to
|
||
add the necessary hacks for MS-NET 1.03, which sends raw write
|
||
requests in a different format.
|
||
|
||
Author:
|
||
|
||
Chuck Lenzmeier (chuckl) 8-Sep-1990
|
||
Manny Weiser (mannyw)
|
||
David Treadwell (davidtr)
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#include "fsdraw.tmh"
|
||
#pragma hdrstop
|
||
|
||
//
|
||
// Forward declarations.
|
||
//
|
||
|
||
STATIC
|
||
VOID SRVFASTCALL
|
||
RestartWriteCompleteResponse (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE8FIL, SrvFsdBuildWriteCompleteResponse )
|
||
#pragma alloc_text( PAGE8FIL, RestartWriteCompleteResponse )
|
||
#endif
|
||
#if 0
|
||
NOT PAGEABLE -- RestartCopyReadRawResponse
|
||
NOT PAGEABLE -- SrvFsdRestartPrepareRawMdlWrite
|
||
NOT PAGEABLE -- SrvFsdRestartReadRaw
|
||
NOT PAGEABLE -- SrvFsdRestartWriteRaw
|
||
#endif
|
||
|
||
#if DBG
|
||
VOID
|
||
DumpMdlChain(
|
||
IN PMDL mdl
|
||
);
|
||
#endif
|
||
|
||
|
||
VOID
|
||
SrvFsdBuildWriteCompleteResponse (
|
||
IN OUT PWORK_CONTEXT WorkContext,
|
||
IN NTSTATUS Status,
|
||
IN ULONG BytesWritten
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets up a final response to a Write Block Raw/Mpx request.
|
||
|
||
This routine is called in both the FSP and the FSD. It can be called
|
||
in the FSD only if Status == STATUS_SUCCESS.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
describing server-specific context for the request.
|
||
|
||
Status - Supplies a status value to be returned to the client.
|
||
|
||
BytesWritten - Supplies the number of bytes actually written.
|
||
|
||
Return Value:
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE - Returns SmbStatusSendResponse.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSMB_HEADER header;
|
||
PRESP_WRITE_COMPLETE response;
|
||
|
||
if ( WorkContext->Rfcb != NULL ) {
|
||
UNLOCKABLE_CODE( 8FIL );
|
||
} else {
|
||
ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
|
||
}
|
||
|
||
//
|
||
// Get pointers to the header and the response parameters area.
|
||
// Note that Write Block Raw/Mpx can't be chained to an AndX SMB.
|
||
//
|
||
|
||
header = WorkContext->ResponseHeader;
|
||
response = (PRESP_WRITE_COMPLETE)WorkContext->ResponseParameters;
|
||
|
||
//
|
||
// Change the SMB command code to Write Complete.
|
||
//
|
||
|
||
header->Command = SMB_COM_WRITE_COMPLETE;
|
||
|
||
//
|
||
// Put the error code in the header. Note that SrvSetSmbError
|
||
// writes a null parameter area to the end of the SMB; we overwrite
|
||
// that with our own parameter area.
|
||
//
|
||
|
||
if ( Status != STATUS_SUCCESS ) {
|
||
ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
|
||
SrvSetSmbError2( WorkContext, Status, TRUE );
|
||
}
|
||
|
||
//
|
||
// Build the response SMB.
|
||
//
|
||
|
||
response->WordCount = 1;
|
||
SmbPutUshort( &response->Count, (USHORT)BytesWritten );
|
||
SmbPutUshort( &response->ByteCount, 0 );
|
||
|
||
WorkContext->ResponseParameters = NEXT_LOCATION(
|
||
response,
|
||
RESP_WRITE_COMPLETE,
|
||
0
|
||
);
|
||
|
||
return;
|
||
|
||
} // SrvFsdBuildWriteCompleteResponse
|
||
|
||
|
||
NTSTATUS
|
||
RestartCopyReadRawResponse (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the restart routine that is invoked when the send of a
|
||
Read Block Raw response completes.
|
||
|
||
This routine is called in the FSD.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to target device object for the request.
|
||
|
||
Irp - Pointer to I/O request packet
|
||
|
||
WorkContext - Caller-specified context parameter associated with IRP.
|
||
This is actually a pointer to a Work Context block.
|
||
|
||
Return Value:
|
||
|
||
STATUS_MORE_PROCESSING_REQUIRED.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldIrql;
|
||
PCONNECTION connection;
|
||
|
||
IF_DEBUG(FSD1) SrvPrint0( " - RestartCopyReadRawResponse\n" );
|
||
|
||
//
|
||
// Check the status of the send completion.
|
||
//
|
||
|
||
CHECK_SEND_COMPLETION_STATUS( Irp->IoStatus.Status );
|
||
|
||
//
|
||
// Reset the IRP cancelled bit.
|
||
//
|
||
|
||
Irp->Cancel = FALSE;
|
||
|
||
//
|
||
// Deallocate the raw buffer, if the original SMB buffer was not
|
||
// used. Note that we do not need to unlock the raw buffer, because
|
||
// it was allocated out of nonpaged pool and locked using
|
||
// MmBuildMdlForNonPagedPool, which doesn't increment reference
|
||
// counts and therefore has no inverse.
|
||
//
|
||
|
||
if ( WorkContext->Parameters.ReadRaw.SavedResponseBuffer != NULL ) {
|
||
|
||
DEALLOCATE_NONPAGED_POOL( WorkContext->ResponseBuffer->Buffer );
|
||
|
||
IoFreeMdl( WorkContext->ResponseBuffer->Mdl );
|
||
|
||
DEALLOCATE_NONPAGED_POOL( WorkContext->ResponseBuffer );
|
||
WorkContext->ResponseBuffer =
|
||
WorkContext->Parameters.ReadRaw.SavedResponseBuffer;
|
||
|
||
}
|
||
|
||
//
|
||
// If there is an oplock break request pending, then we must go to the
|
||
// FSP to initiate the break, otherwise complete processing in the FSD.
|
||
//
|
||
|
||
connection = WorkContext->Connection;
|
||
|
||
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
||
ACQUIRE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
|
||
if ( IsListEmpty( &connection->OplockWorkList ) ) {
|
||
|
||
//
|
||
// Dereference control blocks and put the work item back on the
|
||
// receive queue.
|
||
//
|
||
|
||
WorkContext->Connection->RawReadsInProgress--;
|
||
RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
SrvFsdRestartSmbComplete( WorkContext );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Send this work context to the FSP for completion.
|
||
//
|
||
|
||
RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
|
||
WorkContext->FspRestartRoutine = SrvRestartReadRawComplete;
|
||
QUEUE_WORK_TO_FSP( WorkContext );
|
||
|
||
}
|
||
|
||
IF_DEBUG(TRACE2) SrvPrint0( "RestartCopyReadRawResponse complete\n" );
|
||
|
||
KeLowerIrql( oldIrql );
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} // RestartCopyReadRawResponse
|
||
|
||
|
||
VOID SRVFASTCALL
|
||
SrvFsdRestartPrepareRawMdlWrite(
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Restart routine for completion of a "prepare MDL write" I/O request.
|
||
Prepares a work context block and an IRP describing the raw receive,
|
||
posts the receive, and sends a "go-ahead" response.
|
||
|
||
This routine is called in both the FSP and the FSD.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
describing server-specific context for the request.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PREQ_WRITE_RAW request;
|
||
PRESP_WRITE_RAW_INTERIM response;
|
||
|
||
PVOID finalResponseBuffer;
|
||
PWORK_CONTEXT rawWorkContext;
|
||
ULONG writeLength;
|
||
ULONG immediateLength;
|
||
BOOLEAN immediateWriteDone;
|
||
PMDL mdl;
|
||
|
||
PCHAR src;
|
||
PCHAR dest;
|
||
ULONG lengthToCopy;
|
||
|
||
PIO_STACK_LOCATION irpSp;
|
||
BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
|
||
|
||
if (bNeedTrace) {
|
||
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
||
WorkContext->PreviousSMB = EVENT_TYPE_SMB_WRITE_RAW;
|
||
SrvWmiStartContext(WorkContext);
|
||
}
|
||
else
|
||
WorkContext->bAlreadyTrace = FALSE;
|
||
|
||
IF_DEBUG(FSD1) SrvPrint0( " - SrvFsdRestartPrepareRawMdlWrite\n" );
|
||
|
||
//
|
||
// Get request parameters and saved context.
|
||
//
|
||
|
||
request = (PREQ_WRITE_RAW)WorkContext->RequestParameters;
|
||
|
||
rawWorkContext = WorkContext->Parameters.WriteRawPhase1.RawWorkContext;
|
||
|
||
writeLength = SmbGetUshort( &request->Count );
|
||
immediateLength = SmbGetUshort( &request->DataLength );
|
||
immediateWriteDone = rawWorkContext->Parameters.WriteRaw.ImmediateWriteDone;
|
||
if ( immediateWriteDone ) {
|
||
writeLength -= immediateLength;
|
||
}
|
||
|
||
finalResponseBuffer =
|
||
rawWorkContext->Parameters.WriteRaw.FinalResponseBuffer;
|
||
|
||
//
|
||
// If the prepare MDL write I/O failed, send an error response.
|
||
//
|
||
|
||
if ( !NT_SUCCESS(WorkContext->Irp->IoStatus.Status) ) {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint1( "SrvFsdRestartPrepareRawMdlWrite: Write failed: %X\n",
|
||
WorkContext->Irp->IoStatus.Status );
|
||
}
|
||
|
||
//
|
||
// We won't be needing the final response buffer or the raw mode
|
||
// work item.
|
||
//
|
||
|
||
if ( finalResponseBuffer != NULL ) {
|
||
DEALLOCATE_NONPAGED_POOL( finalResponseBuffer );
|
||
}
|
||
|
||
rawWorkContext->ResponseBuffer->Buffer = NULL;
|
||
RestartWriteCompleteResponse( rawWorkContext );
|
||
|
||
//
|
||
// Build and send the response.
|
||
//
|
||
|
||
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
||
WorkContext->Irp->IoStatus.Information =
|
||
immediateWriteDone ? immediateLength : 0;
|
||
WorkContext->FspRestartRoutine = SrvBuildAndSendWriteCompleteResponse;
|
||
WorkContext->FsdRestartRoutine = SrvFsdRestartSmbComplete; // after response
|
||
QUEUE_WORK_TO_FSP( WorkContext );
|
||
} else {
|
||
SrvFsdBuildWriteCompleteResponse(
|
||
WorkContext,
|
||
WorkContext->Irp->IoStatus.Status,
|
||
immediateWriteDone ? immediateLength : 0
|
||
);
|
||
SrvFsdSendResponse( WorkContext );
|
||
}
|
||
|
||
IF_DEBUG(TRACE2) {
|
||
SrvPrint0( "SrvFsdRestartPrepareRawMdlWrite complete\n" );
|
||
}
|
||
goto Cleanup;
|
||
|
||
}
|
||
|
||
//
|
||
// If a final response is going to be sent, save information from
|
||
// the request in the final response buffer.
|
||
//
|
||
|
||
if ( finalResponseBuffer != NULL ) {
|
||
RtlCopyMemory(
|
||
(PSMB_HEADER)finalResponseBuffer,
|
||
WorkContext->RequestHeader,
|
||
sizeof(SMB_HEADER)
|
||
);
|
||
}
|
||
|
||
//
|
||
// If the immediate data has not yet been written, copy it now.
|
||
//
|
||
|
||
mdl = WorkContext->Irp->MdlAddress;
|
||
#if DBG
|
||
IF_SMB_DEBUG(RAW2) {
|
||
KdPrint(( "SrvFsdRestartPrepareRawMdlWrite: input chain:\n" ));
|
||
DumpMdlChain( mdl );
|
||
}
|
||
#endif
|
||
rawWorkContext->Parameters.WriteRaw.FirstMdl = mdl;
|
||
|
||
if ( !immediateWriteDone ) {
|
||
|
||
src = (PCHAR)WorkContext->RequestHeader +
|
||
SmbGetUshort( &request->DataOffset );
|
||
|
||
while ( immediateLength ) {
|
||
|
||
lengthToCopy = MIN( immediateLength, mdl->ByteCount );
|
||
dest = MmGetSystemAddressForMdlSafe( mdl,NormalPoolPriority );
|
||
|
||
if (dest == NULL) {
|
||
WorkContext->Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
WorkContext->Irp->IoStatus.Information = 0;
|
||
WorkContext->FspRestartRoutine = SrvBuildAndSendWriteCompleteResponse;
|
||
WorkContext->FsdRestartRoutine = SrvFsdRestartSmbComplete; // after response
|
||
QUEUE_WORK_TO_FSP( WorkContext );
|
||
goto Cleanup;
|
||
}
|
||
|
||
RtlCopyMemory( dest, src, lengthToCopy );
|
||
|
||
src += lengthToCopy;
|
||
immediateLength -= lengthToCopy;
|
||
writeLength -= lengthToCopy;
|
||
|
||
if ( lengthToCopy == mdl->ByteCount ) {
|
||
|
||
mdl = mdl->Next;
|
||
|
||
} else {
|
||
|
||
PCHAR baseVa;
|
||
ULONG lengthOfMdl;
|
||
PMDL rawMdl;
|
||
|
||
ASSERT( immediateLength == 0 );
|
||
baseVa = (PCHAR)MmGetMdlVirtualAddress(mdl) + lengthToCopy;
|
||
lengthOfMdl = mdl->ByteCount - lengthToCopy;
|
||
ASSERT( lengthOfMdl <= 65535 );
|
||
|
||
rawMdl = rawWorkContext->RequestBuffer->Mdl;
|
||
rawMdl->Size = (CSHORT)(sizeof(MDL) + (sizeof(ULONG_PTR) *
|
||
ADDRESS_AND_SIZE_TO_SPAN_PAGES( baseVa, lengthOfMdl )));
|
||
|
||
IoBuildPartialMdl( mdl, rawMdl, baseVa, lengthOfMdl );
|
||
|
||
rawMdl->Next = mdl->Next;
|
||
#if DBG
|
||
IF_SMB_DEBUG(RAW2) {
|
||
KdPrint(( "SrvFsdRestartPrepareRawMdlWrite: built partial MDL at 0x%p\n", rawMdl ));
|
||
DumpMdlChain( rawMdl );
|
||
}
|
||
#endif
|
||
|
||
mdl = rawMdl;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Save the length of the raw write.
|
||
//
|
||
|
||
rawWorkContext->RequestBuffer->BufferLength = writeLength;
|
||
|
||
//
|
||
// Set up the restart routines in the work context.
|
||
//
|
||
|
||
rawWorkContext->FsdRestartRoutine = SrvQueueWorkToFspAtDpcLevel;
|
||
rawWorkContext->FspRestartRoutine = SrvRestartRawReceive;
|
||
|
||
//
|
||
// Build the TdiReceive request packet.
|
||
//
|
||
|
||
|
||
{
|
||
PIRP irp = rawWorkContext->Irp;
|
||
PIO_STACK_LOCATION irpSp;
|
||
PTDI_REQUEST_KERNEL_RECEIVE parameters;
|
||
|
||
irp->Tail.Overlay.OriginalFileObject = NULL;
|
||
irp->Tail.Overlay.Thread = WorkContext->CurrentWorkQueue->IrpThread;
|
||
DEBUG irp->RequestorMode = KernelMode;
|
||
|
||
//
|
||
// Get a pointer to the next stack location. This one is used to
|
||
// hold the parameters for the device I/O control request.
|
||
//
|
||
|
||
irpSp = IoGetNextIrpStackLocation( irp );
|
||
|
||
//
|
||
// Set up the completion routine.
|
||
//
|
||
|
||
IoSetCompletionRoutine(
|
||
irp,
|
||
SrvFsdIoCompletionRoutine,
|
||
rawWorkContext,
|
||
TRUE,
|
||
TRUE,
|
||
TRUE
|
||
);
|
||
|
||
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
||
irpSp->MinorFunction = (UCHAR)TDI_RECEIVE;
|
||
|
||
irpSp->FileObject = NULL;
|
||
irpSp->DeviceObject = NULL;
|
||
|
||
//
|
||
// Copy the caller's parameters to the service-specific portion of the
|
||
// IRP for those parameters that are the same for all three methods.
|
||
//
|
||
|
||
parameters = (PTDI_REQUEST_KERNEL_RECEIVE)&irpSp->Parameters;
|
||
parameters->ReceiveLength = writeLength;
|
||
parameters->ReceiveFlags = 0;
|
||
|
||
irp->MdlAddress = mdl;
|
||
irp->AssociatedIrp.SystemBuffer = NULL;
|
||
irp->Flags = (ULONG)IRP_BUFFERED_IO;
|
||
}
|
||
|
||
IF_SMB_DEBUG(RAW2) {
|
||
KdPrint(( "Issuing receive with MDL %p\n", rawWorkContext->Irp->MdlAddress ));
|
||
}
|
||
|
||
irpSp = IoGetNextIrpStackLocation( rawWorkContext->Irp );
|
||
|
||
//
|
||
// If this is a writebehind write, tell the transport that we don't
|
||
// plan to reply to the received message.
|
||
//
|
||
|
||
if ( finalResponseBuffer == NULL ) {
|
||
((PTDI_REQUEST_KERNEL_RECEIVE)&irpSp->Parameters)->ReceiveFlags |=
|
||
TDI_RECEIVE_NO_RESPONSE_EXP;
|
||
}
|
||
|
||
//
|
||
// Post the receive.
|
||
//
|
||
|
||
irpSp->Flags = 0;
|
||
irpSp->DeviceObject = rawWorkContext->Connection->DeviceObject;
|
||
irpSp->FileObject = rawWorkContext->Connection->FileObject;
|
||
|
||
ASSERT( rawWorkContext->Irp->StackCount >=
|
||
irpSp->DeviceObject->StackSize );
|
||
|
||
(VOID)IoCallDriver( irpSp->DeviceObject, rawWorkContext->Irp );
|
||
|
||
//
|
||
// Send the interim (go-ahead) response.
|
||
//
|
||
|
||
response = (PRESP_WRITE_RAW_INTERIM)WorkContext->ResponseParameters;
|
||
|
||
response->WordCount = 1;
|
||
SmbPutUshort( &response->Remaining, (USHORT)-1 );
|
||
SmbPutUshort( &response->ByteCount, 0 );
|
||
WorkContext->ResponseParameters = NEXT_LOCATION(
|
||
response,
|
||
RESP_WRITE_RAW_INTERIM,
|
||
0
|
||
);
|
||
|
||
SrvFsdSendResponse( WorkContext );
|
||
|
||
IF_DEBUG(TRACE2) SrvPrint0( "SrvFsdRestartPrepareRawMdlWrite complete\n" );
|
||
|
||
Cleanup:
|
||
if (bNeedTrace) {
|
||
SrvWmiEndContext(WorkContext);
|
||
}
|
||
return;
|
||
|
||
} // SrvFsdRestartPrepareRawMdlWrite
|
||
|
||
|
||
VOID SRVFASTCALL
|
||
SrvFsdRestartReadRaw (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes file read completion for the Read Block Raw SMB.
|
||
|
||
This routine is called in both the FSP and the FSD.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
describing server-specific context for the request.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRFCB rfcb;
|
||
KIRQL oldIrql;
|
||
USHORT readLength;
|
||
BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
|
||
|
||
if (bNeedTrace) {
|
||
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
||
WorkContext->PreviousSMB = EVENT_TYPE_SMB_READ_RAW;
|
||
SrvWmiStartContext(WorkContext);
|
||
}
|
||
else
|
||
WorkContext->bAlreadyTrace = FALSE;
|
||
|
||
IF_DEBUG(FSD1) SrvPrint0( " - SrvFsdRestartReadRaw\n" );
|
||
|
||
//
|
||
// Get the file pointer.
|
||
//
|
||
|
||
rfcb = WorkContext->Rfcb;
|
||
IF_DEBUG(TRACE2) {
|
||
SrvPrint2( " connection 0x%p, RFCB 0x%p\n",
|
||
WorkContext->Connection, rfcb );
|
||
}
|
||
|
||
//
|
||
// Calculate the amount of data read.
|
||
//
|
||
|
||
if ( WorkContext->Irp->IoStatus.Status == STATUS_END_OF_FILE ) {
|
||
|
||
readLength = 0;
|
||
IF_SMB_DEBUG(RAW2) {
|
||
SrvPrint0( "SrvFsdRestartReadRaw: 0 bytes read, at end-of-file\n" );
|
||
}
|
||
|
||
} else if ( !NT_SUCCESS(WorkContext->Irp->IoStatus.Status) ) {
|
||
|
||
readLength = 0;
|
||
IF_SMB_DEBUG(ERRORS) {
|
||
SrvPrint1( "SrvFsdRestartReadRaw: read request failed: %X\n",
|
||
WorkContext->Irp->IoStatus.Status );
|
||
}
|
||
|
||
} else if ( WorkContext->Parameters.ReadRaw.MdlRead ) {
|
||
|
||
//
|
||
// For an MDL read, we have to walk the MDL chain in order to
|
||
// determine how much data was read. This is because the
|
||
// operation may have happened in multiple step, with the MDLs
|
||
// being chained together. For example, part of the read may
|
||
// have been satisfied by the fast path, while the rest was
|
||
// satisfied using an IRP.
|
||
//
|
||
|
||
#if DBG
|
||
ULONG mdlCount = 0;
|
||
#endif
|
||
PMDL mdl = WorkContext->Irp->MdlAddress;
|
||
|
||
readLength = 0;
|
||
|
||
while ( mdl != NULL ) {
|
||
IF_SMB_DEBUG(RAW2) {
|
||
#if DBG
|
||
SrvPrint3( " mdl %ld at 0x%p, %ld bytes\n",
|
||
mdlCount,
|
||
mdl, MmGetMdlByteCount(mdl) );
|
||
#else
|
||
SrvPrint3( " mdl 0x%p, %ld bytes\n",
|
||
mdl, MmGetMdlByteCount(mdl) );
|
||
#endif
|
||
}
|
||
readLength += (USHORT)MmGetMdlByteCount(mdl);
|
||
#if DBG
|
||
mdlCount++;
|
||
#endif
|
||
mdl = mdl->Next;
|
||
}
|
||
IF_SMB_DEBUG(RAW2) {
|
||
#if DBG
|
||
SrvPrint2( "SrvFsdRestartReadRaw: %ld bytes in %ld MDLs\n",
|
||
readLength, mdlCount );
|
||
#else
|
||
SrvPrint2( "SrvFsdRestartReadRaw: %ld bytes\n",
|
||
readLength );
|
||
#endif
|
||
SrvPrint1( " info = 0x%p\n",
|
||
(PVOID)WorkContext->Irp->IoStatus.Information );
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Copy read. The I/O status block has the length.
|
||
//
|
||
|
||
readLength = (USHORT)WorkContext->Irp->IoStatus.Information;
|
||
IF_SMB_DEBUG(RAW2) {
|
||
SrvPrint1( "SrvFsdRestartReadRaw: %ld bytes read\n", readLength );
|
||
}
|
||
|
||
}
|
||
|
||
#ifdef SLMDBG
|
||
{
|
||
PRFCB_TRACE entry;
|
||
ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
|
||
rfcb->OperationCount++;
|
||
entry = &rfcb->Trace[rfcb->NextTrace];
|
||
if ( ++rfcb->NextTrace == SLMDBG_TRACE_COUNT ) {
|
||
rfcb->NextTrace = 0;
|
||
rfcb->TraceWrapped = TRUE;
|
||
}
|
||
RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
|
||
entry->Command = WorkContext->NextCommand;
|
||
entry->Flags =
|
||
(UCHAR)(WorkContext->Parameters.ReadRaw.MdlRead ? 1 : 0);
|
||
KeQuerySystemTime( &entry->Time );
|
||
entry->Data.ReadWrite.Offset =
|
||
WorkContext->Parameters.ReadRaw.ReadRawOtherInfo.Offset.LowPart;
|
||
entry->Data.ReadWrite.Length = readLength;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Update the file position.
|
||
//
|
||
// *** Note that we ignore the status of the operation, except to
|
||
// check for end-of-file, since we can't tell the client what
|
||
// the status was. We simply return as many bytes as the file
|
||
// system says were read.
|
||
//
|
||
// !!! Should we save the error and return it when the client
|
||
// retries?
|
||
//
|
||
// !!! Need to worry about wraparound?
|
||
//
|
||
|
||
if ( rfcb->ShareType == ShareTypeDisk ) {
|
||
|
||
rfcb->CurrentPosition =
|
||
WorkContext->Parameters.ReadRaw.ReadRawOtherInfo.Offset.LowPart +
|
||
readLength;
|
||
|
||
}
|
||
|
||
//
|
||
// Save the count of bytes read, to be used to update the server
|
||
// statistics database.
|
||
//
|
||
|
||
UPDATE_READ_STATS( WorkContext, readLength );
|
||
|
||
//
|
||
// Send the raw read data as the response.
|
||
//
|
||
|
||
WorkContext->ResponseBuffer->DataLength = readLength;
|
||
|
||
//
|
||
// There is no header on this SMB, do not generate a security signature
|
||
//
|
||
WorkContext->NoResponseSmbSecuritySignature = TRUE;
|
||
|
||
if ( WorkContext->Parameters.ReadRaw.MdlRead ) {
|
||
|
||
//
|
||
// MDL read. The data is described by the MDL returned by the
|
||
// file system (in irp->MdlAddress).
|
||
//
|
||
// *** Note that if the read failed completely (which happens if
|
||
// the read starts beyond EOF), there is no MDL.
|
||
// SrvStartSend handles this appropriately. So must
|
||
// RestartMdlReadRawResponse.
|
||
//
|
||
|
||
//
|
||
// Send the response.
|
||
//
|
||
|
||
SRV_START_SEND(
|
||
WorkContext,
|
||
WorkContext->Irp->MdlAddress,
|
||
0,
|
||
SrvQueueWorkToFspAtSendCompletion,
|
||
NULL,
|
||
RestartMdlReadRawResponse
|
||
);
|
||
|
||
} else {
|
||
|
||
//
|
||
// Copy read. The data is described by the MDL allocated in
|
||
// SrvFsdSmbReadRaw.
|
||
//
|
||
// *** Changing Mdl->ByteCount like this would be a problem if
|
||
// we had to unlock the pages in RestartCopyReadRawResponse,
|
||
// because we might end up unlocking fewer pages than we
|
||
// locked. But we don't actually lock the pages to build
|
||
// the MDL -- the buffer is allocated from nonpaged pool, so
|
||
// we use MmBuildMdlForNonPagedPool rather than
|
||
// MmProbeAndLockPages. So the pages haven't been
|
||
// referenced to account for the MDL, so there's no need to
|
||
// unlock them, so changing ByteCount isn't a problem.
|
||
//
|
||
|
||
//
|
||
// Send the response.
|
||
//
|
||
|
||
SRV_START_SEND_2(
|
||
WorkContext,
|
||
RestartCopyReadRawResponse,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// The response send has been started.
|
||
//
|
||
|
||
IF_DEBUG(TRACE2) SrvPrint0( "SrvFsdRestartReadRaw complete\n" );
|
||
|
||
if (bNeedTrace) {
|
||
SrvWmiEndContext(WorkContext);
|
||
}
|
||
return;
|
||
|
||
} // SrvFsdRestartReadRaw
|
||
|
||
|
||
VOID SRVFASTCALL
|
||
RestartWriteCompleteResponse (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine attempts, at DPC level, to clean up after a Write Raw
|
||
completes. It tries to dereference control blocks referenced by the
|
||
raw mode work item. If this cannot be done at DPC level (e.g., a
|
||
reference count goes to zero), this routine queues the work item to
|
||
the FSP for processing.
|
||
|
||
This routine is called in the FSD. Its FSP counterpart is
|
||
SrvRestartWriteCompleteResponse.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
describing server-specific context for the request.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PCONNECTION connection;
|
||
KIRQL oldIrql;
|
||
PRFCB rfcb;
|
||
PWORK_QUEUE queue;
|
||
|
||
UNLOCKABLE_CODE( 8FIL );
|
||
|
||
IF_DEBUG(FSD1) SrvPrint0( " - RestartWriteCompleteResponse\n" );
|
||
|
||
connection = WorkContext->Connection;
|
||
queue = connection->CurrentWorkQueue;
|
||
|
||
//
|
||
// If a final response was sent, check the status and deallocate the
|
||
// buffer.
|
||
//
|
||
|
||
if ( WorkContext->ResponseBuffer->Buffer != NULL ) {
|
||
|
||
//
|
||
// If the I/O request failed or was canceled, print an error
|
||
// message.
|
||
//
|
||
// !!! If I/O failure, should we drop the connection?
|
||
//
|
||
|
||
if ( WorkContext->Irp->Cancel ||
|
||
!NT_SUCCESS(WorkContext->Irp->IoStatus.Status) ) {
|
||
|
||
IF_DEBUG(FSD1) {
|
||
if ( WorkContext->Irp->Cancel ) {
|
||
SrvPrint0( " I/O canceled\n" );
|
||
} else {
|
||
SrvPrint1( " I/O failed: %X\n",
|
||
WorkContext->Irp->IoStatus.Status );
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Deallocate the final response buffer.
|
||
//
|
||
// *** Note that we don't need to unlock it, because it was
|
||
// allocated from nonpaged pool.
|
||
//
|
||
|
||
DEALLOCATE_NONPAGED_POOL( WorkContext->ResponseBuffer->Buffer );
|
||
|
||
}
|
||
|
||
//
|
||
// If the work context block has references to a share, a session,
|
||
// or a tree connect, queue it to the FSP immediately. These blocks
|
||
// are not in nonpaged pool, so they can't be touched at DPC level.
|
||
//
|
||
|
||
if ( (WorkContext->Share != NULL) ||
|
||
(WorkContext->Session != NULL) ||
|
||
(WorkContext->TreeConnect != NULL) ) {
|
||
|
||
goto queueToFsp;
|
||
|
||
}
|
||
|
||
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
|
||
|
||
//
|
||
// See if we can dereference the RawWriteCount here. If the raw
|
||
// write count goes to 0, and the RFCB is closing, or if there are
|
||
// work items queued waiting for the raw write to complete, we need
|
||
// to do this in the FSP.
|
||
//
|
||
// NOTE: The FSP decrements the count if WorkContext->Rfcb != NULL.
|
||
//
|
||
|
||
rfcb = WorkContext->Rfcb;
|
||
--rfcb->RawWriteCount;
|
||
|
||
if ( (rfcb->RawWriteCount == 0) &&
|
||
( (GET_BLOCK_STATE(rfcb) == BlockStateClosing) ||
|
||
!IsListEmpty(&rfcb->RawWriteSerializationList) ) ) {
|
||
|
||
rfcb->RawWriteCount++;
|
||
|
||
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
|
||
goto queueToFsp;
|
||
|
||
}
|
||
|
||
//
|
||
// Dereference the file block. It is safe to decrement the count here
|
||
// because either the rfcb is not closed or RawWriteCount is not zero
|
||
// which means that the active reference is still there.
|
||
//
|
||
|
||
UPDATE_REFERENCE_HISTORY( rfcb, TRUE );
|
||
--rfcb->BlockHeader.ReferenceCount;
|
||
ASSERT( rfcb->BlockHeader.ReferenceCount > 0 );
|
||
|
||
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
|
||
WorkContext->Rfcb = NULL;
|
||
|
||
//
|
||
// Attempt to dereference the connection.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK( connection->EndpointSpinLock, &oldIrql );
|
||
|
||
if ( connection->BlockHeader.ReferenceCount == 1 ) {
|
||
RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
|
||
goto queueToFsp;
|
||
}
|
||
|
||
--connection->BlockHeader.ReferenceCount;
|
||
RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
|
||
|
||
UPDATE_REFERENCE_HISTORY( connection, TRUE );
|
||
|
||
WorkContext->Connection = NULL;
|
||
WorkContext->Endpoint = NULL; // not a referenced pointer
|
||
|
||
//
|
||
// Put the work item back on the raw mode work item list.
|
||
//
|
||
|
||
InterlockedIncrement( &queue->FreeRawModeWorkItems );
|
||
|
||
ExInterlockedPushEntrySList( &queue->RawModeWorkItemList,
|
||
&WorkContext->SingleListEntry,
|
||
&queue->SpinLock );
|
||
|
||
IF_DEBUG(FSD2) SrvPrint0( "RestartWriteCompleteResponse complete\n" );
|
||
return;
|
||
|
||
queueToFsp:
|
||
|
||
//
|
||
// We were unable to do all the necessary cleanup at DPC level.
|
||
// Queue the work item to the FSP.
|
||
//
|
||
|
||
WorkContext->FspRestartRoutine = SrvRestartWriteCompleteResponse;
|
||
|
||
SrvQueueWorkToFsp( WorkContext );
|
||
|
||
IF_DEBUG(FSD2) SrvPrint0( "RestartWriteCompleteResponse complete\n" );
|
||
return;
|
||
|
||
} // RestartWriteCompleteResponse
|
||
|
||
|
||
VOID SRVFASTCALL
|
||
SrvFsdRestartWriteRaw (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes file write completion for the Write Block Raw SMB.
|
||
|
||
This routine is called in both the FSP and the FSD.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
describing server-specific context for the request.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldIrql;
|
||
ULONG writeLength;
|
||
ULONG immediateLength;
|
||
BOOLEAN immediateWriteDone;
|
||
SHARE_TYPE shareType;
|
||
PMDL mdl;
|
||
ULONG sendLength;
|
||
PVOID finalResponseBuffer;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PRFCB rfcb = WorkContext->Rfcb;
|
||
|
||
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
||
WorkContext->PreviousSMB = EVENT_TYPE_SMB_WRITE_RAW;
|
||
SrvWmiStartContext(WorkContext);
|
||
IF_DEBUG(FSD1) SrvPrint0( " - SrvFsdRestartWriteRaw\n" );
|
||
|
||
//
|
||
// Find out the file type that we are dealing with. If it is a pipe
|
||
// then we have not prewritten the immediate data.
|
||
//
|
||
// immediateLength is the length of the data sent with the write
|
||
// block raw request.
|
||
//
|
||
|
||
shareType = rfcb->ShareType;
|
||
immediateLength = WorkContext->Parameters.WriteRaw.ImmediateLength;
|
||
immediateWriteDone = WorkContext->Parameters.WriteRaw.ImmediateWriteDone;
|
||
|
||
//
|
||
// Deallocate the raw receive buffer. Note that we do not need to
|
||
// unlock the raw buffer, because it was allocated out of nonpaged
|
||
// pool and locked using MmBuildMdlForNonPagedPool, which doesn't
|
||
// increment reference counts and therefore has no inverse.
|
||
//
|
||
|
||
if ( !WorkContext->Parameters.WriteRaw.MdlWrite ) {
|
||
|
||
//
|
||
// If this is a named pipe the request buffer actually points
|
||
// "immediateLength" bytes into the write buffer.
|
||
//
|
||
|
||
if ( immediateWriteDone ) {
|
||
DEALLOCATE_NONPAGED_POOL( WorkContext->RequestBuffer->Buffer );
|
||
IF_SMB_DEBUG(RAW2) {
|
||
SrvPrint1( "raw buffer 0x%p deallocated\n",
|
||
WorkContext->RequestBuffer->Buffer );
|
||
}
|
||
} else {
|
||
DEALLOCATE_NONPAGED_POOL(
|
||
(PCHAR)WorkContext->RequestBuffer->Buffer - immediateLength );
|
||
IF_SMB_DEBUG(RAW2) {
|
||
SrvPrint1( "raw buffer 0x%p deallocated\n",
|
||
(PCHAR)WorkContext->RequestBuffer->Buffer - immediateLength );
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
status = WorkContext->Irp->IoStatus.Status;
|
||
|
||
//
|
||
// If this is not a pipe we have already successfully written the
|
||
// immediate pipe data, so return the total bytes written by the two
|
||
// write operations.
|
||
//
|
||
|
||
writeLength = (ULONG)WorkContext->Irp->IoStatus.Information;
|
||
|
||
if( NT_SUCCESS( status ) && writeLength == 0 ) {
|
||
|
||
writeLength = WorkContext->Parameters.WriteRaw.Length;
|
||
|
||
} else {
|
||
|
||
if ( immediateWriteDone ) {
|
||
writeLength += immediateLength;
|
||
}
|
||
}
|
||
|
||
UPDATE_WRITE_STATS( WorkContext, writeLength );
|
||
|
||
finalResponseBuffer = WorkContext->Parameters.WriteRaw.FinalResponseBuffer;
|
||
|
||
//
|
||
// Update the file position.
|
||
//
|
||
// !!! Need to worry about wraparound?
|
||
//
|
||
|
||
if ( shareType == ShareTypeDisk || shareType == ShareTypePrint ) {
|
||
|
||
rfcb->CurrentPosition =
|
||
WorkContext->Parameters.WriteRaw.Offset.LowPart + writeLength;
|
||
|
||
}
|
||
|
||
if ( finalResponseBuffer == NULL ) {
|
||
|
||
//
|
||
// Update server statistics.
|
||
//
|
||
|
||
UPDATE_STATISTICS( WorkContext, 0, SMB_COM_WRITE_RAW );
|
||
|
||
//
|
||
// Save the write behind error, if any.
|
||
//
|
||
|
||
if ( !NT_SUCCESS( status ) ) {
|
||
|
||
//
|
||
// because of our assumption that the cached rfcb does
|
||
// not have a write behind error stored. This saves us
|
||
// a compare on our critical path.
|
||
//
|
||
|
||
if ( WorkContext->Connection->CachedFid == (ULONG)rfcb->Fid ) {
|
||
WorkContext->Connection->CachedFid = (ULONG)-1;
|
||
}
|
||
rfcb->SavedError = status;
|
||
}
|
||
|
||
//
|
||
// Dereference control blocks, etc.
|
||
//
|
||
|
||
WorkContext->ResponseBuffer->Buffer = NULL;
|
||
|
||
RestartWriteCompleteResponse( WorkContext );
|
||
|
||
IF_DEBUG(TRACE2) SrvPrint0( "SrvFsdRestartWriteRaw complete\n" );
|
||
goto Cleanup;
|
||
|
||
}
|
||
|
||
//
|
||
// Writethrough mode. Send a response to the client. We have to
|
||
// get a little tricky here, to make the raw mode work item look
|
||
// enough like a normal one to be able to send using it. Note that
|
||
// the header from the original request SMB was copied into the
|
||
// final response buffer.
|
||
//
|
||
|
||
WorkContext->ResponseHeader = (PSMB_HEADER)finalResponseBuffer;
|
||
WorkContext->ResponseParameters = WorkContext->ResponseHeader + 1;
|
||
|
||
ASSERT( WorkContext->RequestBuffer == WorkContext->ResponseBuffer );
|
||
|
||
WorkContext->ResponseBuffer->Buffer = finalResponseBuffer;
|
||
sendLength = (ULONG)( (PCHAR)NEXT_LOCATION(
|
||
WorkContext->ResponseParameters,
|
||
RESP_WRITE_COMPLETE,
|
||
0
|
||
) - (PCHAR)finalResponseBuffer );
|
||
WorkContext->ResponseBuffer->DataLength = sendLength;
|
||
|
||
//
|
||
// Remap the MDL to describe the final response buffer.
|
||
//
|
||
|
||
mdl = WorkContext->ResponseBuffer->Mdl;
|
||
|
||
MmInitializeMdl( mdl, finalResponseBuffer, sendLength );
|
||
MmBuildMdlForNonPagedPool( mdl );
|
||
|
||
//
|
||
// Set the bit in the SMB that indicates this is a response from the
|
||
// server.
|
||
//
|
||
|
||
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
|
||
|
||
//
|
||
// Send the response. When the send completes, the restart routine
|
||
// RestartWriteCompleteResponse is called. We then dereference
|
||
// control blocks and put the raw mode work item back on the free
|
||
// list.
|
||
//
|
||
|
||
if ( (status != STATUS_SUCCESS) &&
|
||
(KeGetCurrentIrql() >= DISPATCH_LEVEL) ) {
|
||
WorkContext->Irp->IoStatus.Status = status;
|
||
WorkContext->Irp->IoStatus.Information = writeLength;
|
||
WorkContext->FspRestartRoutine = SrvBuildAndSendWriteCompleteResponse;
|
||
WorkContext->FsdRestartRoutine = RestartWriteCompleteResponse; // after response
|
||
QUEUE_WORK_TO_FSP( WorkContext );
|
||
} else {
|
||
SrvFsdBuildWriteCompleteResponse(
|
||
WorkContext,
|
||
status,
|
||
writeLength
|
||
);
|
||
SRV_START_SEND_2(
|
||
WorkContext,
|
||
SrvFsdSendCompletionRoutine,
|
||
RestartWriteCompleteResponse,
|
||
NULL
|
||
);
|
||
}
|
||
|
||
Cleanup:
|
||
SrvWmiEndContext(WorkContext);
|
||
return;
|
||
|
||
} // SrvFsdRestartWriteRaw
|
||
|