You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1248 lines
34 KiB
1248 lines
34 KiB
/*++
|
|
|
|
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;
|
|
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 );
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// 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
|
|
|