windows-nt/Source/XPSP1/NT/base/fs/srv/smbraw.c

3292 lines
92 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1990 Microsoft Corporation
Module Name:
smbraw.c
Abstract:
This module contains routines for processing the following SMBs in
the server FSP:
Read Block Raw
Write Block Raw
The routines in this module generally work closely with the routines
in fsdraw.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 "smbraw.tmh"
#pragma hdrstop
#define BugCheckFileId SRV_FILE_SMBRAW
//
// Forward declarations
//
VOID SRVFASTCALL
AbortRawWrite(
IN OUT PWORK_CONTEXT WorkContext
);
VOID SRVFASTCALL
PrepareRawCopyWrite (
IN OUT PWORK_CONTEXT WorkContext
);
BOOLEAN SRVFASTCALL
ReadRawPipe (
IN PWORK_CONTEXT WorkContext
);
VOID SRVFASTCALL
RestartMdlReadRawResponse (
IN OUT PWORK_CONTEXT WorkContext
);
VOID SRVFASTCALL
RestartPipeReadRawPeek (
IN OUT PWORK_CONTEXT WorkContext
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, SrvSmbWriteRaw )
#pragma alloc_text( PAGE, AbortRawWrite )
#pragma alloc_text( PAGE, PrepareRawCopyWrite )
#pragma alloc_text( PAGE, ReadRawPipe )
#pragma alloc_text( PAGE, RestartMdlReadRawResponse )
#pragma alloc_text( PAGE, RestartPipeReadRawPeek )
#pragma alloc_text( PAGE, SrvRestartRawReceive )
#pragma alloc_text( PAGE, SrvRestartReadRawComplete )
#pragma alloc_text( PAGE, SrvRestartWriteCompleteResponse )
#pragma alloc_text( PAGE, SrvBuildAndSendWriteCompleteResponse )
#endif
#if 0
NOT PAGEABLE -- DumpMdlChain
NOT PAGEABLE -- SrvSmbReadRaw
NOT PAGEABLE -- SrvDecrementRawWriteCount
#endif
#if DBG
VOID
DumpMdlChain(
IN PMDL mdl
)
{
ULONG mdlCount = 0;
ULONG length = 0;
if ( mdl == NULL ) {
KdPrint(( " <empty MDL chain>\n" ));
return;
}
do {
KdPrint(( " mdl %p len %04x flags %04x sysva %p va %p offset %04x\n",
mdl, mdl->ByteCount, mdl->MdlFlags,
mdl->MappedSystemVa, mdl->StartVa, mdl->ByteOffset ));
length += mdl->ByteCount;
mdlCount++;
mdl = mdl->Next;
} while ( mdl != NULL );
KdPrint(( " total of %ld bytes in %ld MDLs\n", length, mdlCount ));
return;
}
#endif
SMB_PROCESSOR_RETURN_TYPE
SrvSmbReadRaw (
SMB_PROCESSOR_PARAMETERS
)
/*++
Routine Description:
Processes the Read Block Raw SMB.
Note that Read Block Raw cannot return an error response. When the
server is unable to process the request, for whatever reason, it
simply responds with a zero-length message. The client uses a
normal Read SMB to determine what happened.
Arguments:
SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description of the
parameters to SMB processor routines.
Return Value:
SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h
--*/
{
PREQ_READ_RAW request;
PREQ_NT_READ_RAW ntRequest;
NTSTATUS status = STATUS_SUCCESS;
SMB_STATUS SmbStatus = SmbStatusInProgress;
USHORT fid;
PRFCB rfcb;
PLFCB lfcb;
PCONNECTION connection;
UCHAR minorFunction = 0;
PVOID rawBuffer = NULL;
CLONG readLength;
PMDL mdl = NULL;
ULONG key;
LARGE_INTEGER offset;
SHARE_TYPE shareType;
KIRQL oldIrql;
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_READ_RAW;
SrvWmiStartContext(WorkContext);
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.RawReadsAttempted );
request = (PREQ_READ_RAW)WorkContext->RequestParameters;
fid = SmbGetUshort( &request->Fid );
IF_SMB_DEBUG(RAW1) {
KdPrint(( "Read Block Raw request; FID 0x%lx, count %ld, "
"offset %ld\n",
fid, SmbGetUshort( &request->MaxCount ),
SmbGetUlong( &request->Offset ) ));
}
//
// If raw mode has been disabled or if the connection is unreliable,
// reject the raw read. Ask the client to use standard read by
// sending a zero-length response. The client will react by issuing
// a normal Read SMB, which we will be able to process.
//
connection = WorkContext->Connection;
if ( !SrvEnableRawMode || !connection->EnableRawIo ) {
IF_SMB_DEBUG(RAW1) {
KdPrint(( "SrvSmbReadRaw: Raw mode is disabled\n" ));
}
goto error_exit_no_cleanup;
}
//
// Get the rfcb
//
//
// Acquire the spin lock that guards the connection's file table.
//
ACQUIRE_SPIN_LOCK( connection->EndpointSpinLock, &oldIrql );
ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
if ( connection->CachedFid == fid ) {
rfcb = connection->CachedRfcb;
} else {
PTABLE_HEADER tableHeader;
USHORT index;
USHORT sequence;
//
// Initialize local variables: obtain the connection block address
// and crack the FID into its components.
//
index = FID_INDEX( fid );
sequence = FID_SEQUENCE( fid );
//
// Verify that the FID is in range, is in use, and has the correct
// sequence number.
tableHeader = &connection->FileTable;
if ( (index >= (USHORT)tableHeader->TableSize) ||
(tableHeader->Table[index].Owner == NULL) ||
(tableHeader->Table[index].SequenceNumber != sequence) ) {
goto error_exit_no_cleanup_locked;
}
rfcb = tableHeader->Table[index].Owner;
if ( GET_BLOCK_STATE(rfcb) != BlockStateActive ) {
goto error_exit_no_cleanup_locked;
}
//
// If there is a write behind error, reject the raw read.
//
if ( !NT_SUCCESS(rfcb->SavedError) ) {
goto error_exit_no_cleanup_locked;
}
//
// Cache this rfcb
//
connection->CachedRfcb = rfcb;
connection->CachedFid = (ULONG)fid;
}
//
// The FID is valid within the context of this connection. Verify
// that the file is still active and that the owning tree connect's
// TID is correct.
//
// Do not verify the UID for clients that do not understand it.
//
if ( (rfcb->Tid !=
SmbGetAlignedUshort( &WorkContext->RequestHeader->Tid )) ||
((rfcb->Uid !=
SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid )) &&
DIALECT_HONORS_UID(connection->SmbDialect)) ) {
goto error_exit_no_cleanup_locked;
}
//
// Mark the rfcb as active
//
rfcb->IsActive = TRUE;
//
// If a raw write is active, queue this work item in the RFCB
// pending completion of the raw write.
//
if ( rfcb->RawWriteCount != 0 ) {
InsertTailList(
&rfcb->RawWriteSerializationList,
&WorkContext->ListEntry
);
//
// These 2 fields must be set with the connection spinlock held
// since the workitem might be picked up by another thread and
// a race condition will occur.
//
WorkContext->FspRestartRoutine = SrvRestartSmbReceived;
WorkContext->Rfcb = NULL;
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
SmbStatus = SmbStatusInProgress;
goto Cleanup;
}
//
// If this is a pipe read, we do things differently.
//
shareType = rfcb->ShareType;
if ( shareType == ShareTypePipe ) {
//
// Indicate that a raw read is in progress on the connection.
//
connection->RawReadsInProgress++;
//
// The raw read can be accepted. Reference the RFCB.
//
rfcb->BlockHeader.ReferenceCount++;
UPDATE_REFERENCE_HISTORY( rfcb, FALSE );
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
WorkContext->Rfcb = rfcb;
if ( !ReadRawPipe( WorkContext ) ) {
goto error_exit_cleanup;
}
SmbStatus = SmbStatusInProgress;
goto Cleanup;
}
//
// If there is an oplock break in progress, return 0 bytes read. We
// do this because our oplock break request SMB may have crossed on
// the wire with the read raw request and it may have been received
// in the client's raw read buffer. This would cause the raw data
// to complete in the client's regular receive buffer and possibly
// to overrun it.
//
// If this is not the case, the client will simply retry the read
// using a different read protocol. If it is the case, the client
// must detect this and break the oplock, then redo the read.
//
if ( connection->OplockBreaksInProgress > 0 ) {
goto error_exit_no_cleanup_locked;
}
//
// Check to see whether we got a round trip break/response. If so,
// reject read raw.
//
if ( (LONG)(connection->LatestOplockBreakResponse -
WorkContext->Timestamp) >= 0 ) {
goto error_exit_no_cleanup_locked;
}
//
// If this is the first SMB received after sending an oplock break
// II to none, reject this read. We need to do this because there
// is no response to such a break, so we don't know for sure if the
// break crossed with the read, which would mean that the break
// actually completed the client's read, which would mean that any
// raw data that we sent would be incorrectly received.
//
if ( connection->BreakIIToNoneJustSent ) {
connection->BreakIIToNoneJustSent = FALSE;
goto error_exit_no_cleanup_locked;
}
//
// Indicate that a raw read is in progress on the connection.
//
connection->RawReadsInProgress++;
//
// The raw read can be accepted. Reference the RFCB.
//
rfcb->BlockHeader.ReferenceCount++;
UPDATE_REFERENCE_HISTORY( rfcb, FALSE );
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
WorkContext->Rfcb = rfcb;
if( rfcb->Lfcb->Session->IsSessionExpired )
{
SrvSetSmbError( WorkContext, SESSION_EXPIRED_STATUS_CODE );
goto error_exit_cleanup;
}
//
// Verify that the client has read access to the file via the
// specified handle.
//
lfcb = rfcb->Lfcb;
#if SRVCATCH
if ( rfcb->SrvCatch ) {
//
// Force the client through the core read path for this file
//
goto error_exit_cleanup;
}
#endif
if ( !rfcb->ReadAccessGranted ) {
CHECK_PAGING_IO_ACCESS(
WorkContext,
rfcb->GrantedAccess,
&status );
if ( !NT_SUCCESS( status ) ) {
SrvStatistics.GrantedAccessErrors++;
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbReadRaw: Read access not granted.\n"));
}
goto error_exit_cleanup;
}
}
//
// Calculate and save the read offset.
//
if ( request->WordCount == 8 ) {
//
// The client supplied a 32-bit offset.
//
offset.QuadPart = SmbGetUlong( &request->Offset );
} else if ( request->WordCount == 10 ) {
//
// The client supplied a 64-bit offset.
//
ntRequest = (PREQ_NT_READ_RAW)WorkContext->RequestParameters;
offset.LowPart = SmbGetUlong( &ntRequest->Offset );
offset.HighPart = SmbGetUlong( &ntRequest->OffsetHigh );
//
// Reject negative offsets
//
if ( offset.QuadPart < 0 ) {
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbReadRaw: Negative offset rejected.\n"));
}
goto error_exit_cleanup;
}
} else {
//
// Invalid word count. Return 0 bytes.
//
goto error_exit_cleanup;
}
WorkContext->Parameters.ReadRaw.ReadRawOtherInfo.Offset = offset;
//
// If this operation may block, and we're running short of
// resources, or if the target is a paused comm device, reject the
// request.
//
//
// Form the lock key using the FID and the PID.
//
// *** The FID must be included in the key in order to account for
// the folding of multiple remote compatibility mode opens into
// a single local open.
//
key = rfcb->ShiftedFid |
SmbGetAlignedUshort( &WorkContext->RequestHeader->Pid );
//
// If the SMB buffer is large enough, use it to do the local read.
//
readLength = SmbGetUshort( &request->MaxCount );
WorkContext->Parameters.ReadRaw.ReadRawOtherInfo.Length = readLength;
if ( //0 &&
(readLength <= SrvMdlReadSwitchover) ) {
do_copy_read:
WorkContext->Parameters.ReadRaw.SavedResponseBuffer = NULL;
WorkContext->Parameters.ReadRaw.MdlRead = FALSE;
//
// Try the fast I/O path first.
//
if ( lfcb->FastIoRead != NULL ) {
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
try {
if ( lfcb->FastIoRead(
lfcb->FileObject,
&offset,
readLength,
TRUE,
key,
WorkContext->ResponseBuffer->Buffer,
&WorkContext->Irp->IoStatus,
lfcb->DeviceObject
) ) {
//
// The fast I/O path worked. Send the data.
//
WorkContext->bAlreadyTrace = TRUE;
SrvFsdRestartReadRaw( WorkContext );
SmbStatus = SmbStatusInProgress;
goto Cleanup;
}
}
except( EXCEPTION_EXECUTE_HANDLER ) {
// Fall through to the slow path on an exception
NTSTATUS status = GetExceptionCode();
IF_DEBUG(ERRORS) {
KdPrint(("FastIoRead threw exception %x\n", status ));
}
}
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
}
//
// The fast I/O path failed, so we need to use a regular copy
// I/O request. Build an MDL describing the read buffer.
//
// *** Note the assumption that the response buffer already has
// a valid full MDL from which a partial MDL can be built.
//
IoBuildPartialMdl(
WorkContext->ResponseBuffer->Mdl,
WorkContext->ResponseBuffer->PartialMdl,
WorkContext->ResponseBuffer->Buffer,
readLength
);
mdl = WorkContext->ResponseBuffer->PartialMdl;
rawBuffer = WorkContext->ResponseHeader;
ASSERT( minorFunction == 0 );
} else {
//
// The SMB buffer isn't big enough. Does the target file system
// support the cache manager routines?
//
if ( lfcb->FileObject->Flags & FO_CACHE_SUPPORTED ) {
WorkContext->Parameters.ReadRaw.MdlRead = TRUE;
//
// We can use an MDL read. Try the fast I/O path first.
//
WorkContext->Irp->MdlAddress = NULL;
WorkContext->Irp->IoStatus.Information = 0;
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
if ( lfcb->MdlRead(
lfcb->FileObject,
&offset,
readLength,
key,
&WorkContext->Irp->MdlAddress,
&WorkContext->Irp->IoStatus,
lfcb->DeviceObject
) && WorkContext->Irp->MdlAddress ) {
//
// The fast I/O path worked. Send the data.
//
WorkContext->bAlreadyTrace = TRUE;
SrvFsdRestartReadRaw( WorkContext );
SmbStatus = SmbStatusInProgress;
goto Cleanup;
}
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
//
// The fast I/O path failed. We need to issue a regular MDL
// read request.
//
// The fast path may have partially succeeded, returning a
// partial MDL chain. We need to adjust our read request
// to account for that.
//
offset.QuadPart += WorkContext->Irp->IoStatus.Information;
readLength -= (ULONG)WorkContext->Irp->IoStatus.Information;
mdl = WorkContext->Irp->MdlAddress;
minorFunction = IRP_MN_MDL;
} else if (readLength > WorkContext->ResponseBuffer->BufferLength) {
//
// We have to use a normal "copy" read. We need to allocate
// a separate raw buffer.
//
ASSERT( minorFunction == 0 );
WorkContext->Parameters.ReadRaw.MdlRead = FALSE;
rawBuffer = ALLOCATE_NONPAGED_POOL(
readLength,
BlockTypeDataBuffer
);
IF_SMB_DEBUG(RAW2) KdPrint(( "rawBuffer: 0x%p\n", rawBuffer ));
if ( rawBuffer == NULL ) {
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbReadRaw: Unable to allocate raw "
"buffer\n" ));
}
goto error_exit_cleanup;
}
//
// We also need a buffer descriptor.
//
// *** Note: Currently, ResponseBuffer == RequestBuffer in a
// WorkContext block, so we don't really have to save
// the ResponseBuffer field. But we do so just to be on
// the safe side.
//
WorkContext->Parameters.ReadRaw.SavedResponseBuffer =
WorkContext->ResponseBuffer;
WorkContext->ResponseBuffer = ALLOCATE_NONPAGED_POOL(
sizeof(BUFFER),
BlockTypeBuffer
);
if ( WorkContext->ResponseBuffer == NULL ) {
INTERNAL_ERROR(
ERROR_LEVEL_EXPECTED,
"SrvSmbReadRaw: Unable to allocate %d bytes from "
"nonpaged pool.",
sizeof(BUFFER),
NULL
);
DEALLOCATE_NONPAGED_POOL( rawBuffer );
WorkContext->ResponseBuffer =
WorkContext->Parameters.ReadRaw.SavedResponseBuffer;
goto error_exit_cleanup;
}
WorkContext->ResponseBuffer->Buffer = rawBuffer;
WorkContext->ResponseBuffer->BufferLength = readLength;
//
// Finally, we need an MDL to describe the raw buffer.
//
// *** We used to try to use the PartialMdl for the SMB
// buffer here, if it was big enough. But since we
// already decided that the buffer itself isn't big
// enough, it's extremely likely that the MDL isn't big
// enough either.
//
mdl = IoAllocateMdl( rawBuffer, readLength, FALSE, FALSE, NULL );
if ( mdl == NULL ) {
DEALLOCATE_NONPAGED_POOL( WorkContext->ResponseBuffer );
WorkContext->ResponseBuffer =
WorkContext->Parameters.ReadRaw.SavedResponseBuffer;
DEALLOCATE_NONPAGED_POOL( rawBuffer );
goto error_exit_cleanup;
}
WorkContext->ResponseBuffer->Mdl = mdl;
//
// Build the mdl
//
MmBuildMdlForNonPagedPool( mdl );
//
// Try the fast I/O path first.
//
if ( lfcb->FastIoRead != NULL ) {
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
try {
if ( lfcb->FastIoRead(
lfcb->FileObject,
&offset,
readLength,
TRUE,
key,
WorkContext->ResponseBuffer->Buffer,
&WorkContext->Irp->IoStatus,
lfcb->DeviceObject
) ) {
//
// The fast I/O path worked. Send the data.
//
WorkContext->bAlreadyTrace = TRUE;
SrvFsdRestartReadRaw( WorkContext );
SmbStatus = SmbStatusInProgress;
goto Cleanup;
}
}
except( EXCEPTION_EXECUTE_HANDLER ) {
// Fall through to the slow path on an exception
NTSTATUS status = GetExceptionCode();
IF_DEBUG(ERRORS) {
KdPrint(("FastIoRead threw exception %x\n", status ));
}
}
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
}
//
// The fast I/O path failed, so we need to use a regular copy
// I/O request.
//
} else {
goto do_copy_read;
}
} // read fits in SMB buffer?
//
// Build the read request, reusing the receive IRP.
//
SrvBuildReadOrWriteRequest(
WorkContext->Irp, // input IRP address
lfcb->FileObject, // target file object address
WorkContext, // context
IRP_MJ_READ, // major function code
minorFunction, // minor function code
rawBuffer, // buffer address
readLength, // buffer length
mdl, // MDL address
offset, // byte offset
key // lock key
);
//
// Pass the request to the file system.
//
WorkContext->bAlreadyTrace = TRUE;
WorkContext->FsdRestartRoutine = SrvFsdRestartReadRaw;
DEBUG WorkContext->FspRestartRoutine = NULL;
IF_SMB_DEBUG(RAW2) {
KdPrint(( "SrvSmbReadRaw: reading from file 0x%p, offset %ld, length %ld, destination 0x%p, ",
lfcb->FileObject, offset.LowPart, readLength,
rawBuffer ));
KdPrint(( "func 0x%lx\n", minorFunction ));
}
(VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
//
// The read has been started. When it completes, processing
// resumes in the FSD at SrvFsdRestartReadRaw.
//
IF_DEBUG(TRACE2) KdPrint(( "SrvSmbReadRaw complete\n" ));
SmbStatus = SmbStatusInProgress;
goto Cleanup;
error_exit_no_cleanup_locked:
RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
error_exit_no_cleanup:
WorkContext->ResponseParameters = WorkContext->ResponseHeader;
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.RawReadsRejected );
SmbStatus = SmbStatusSendResponse;
goto Cleanup;
error_exit_cleanup:
ACQUIRE_SPIN_LOCK( connection->EndpointSpinLock, &oldIrql );
//
// We are about to release the work context and possibly free the
// connection. Create a referenced pointer to the connection so
// that we can send delayed oplock breaks, if necessary.
//
SrvReferenceConnectionLocked( connection );
//
// Since we cannot return an error code, we return zero bytes of
// data.
//
WorkContext->ResponseParameters = WorkContext->ResponseHeader;
//
// 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.
//
if ( IsListEmpty( &connection->OplockWorkList ) ) {
connection->RawReadsInProgress--;
RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
SrvFsdSendResponse( WorkContext );
} else {
RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
SrvFsdSendResponse2( WorkContext, SrvRestartReadRawComplete );
}
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.RawReadsRejected );
//
// Release the connection reference.
//
SrvDereferenceConnection( connection );
SmbStatus = SmbStatusInProgress;
IF_DEBUG(TRACE2) KdPrint(( "SrvSmbReadRaw complete\n" ));
Cleanup:
SrvWmiEndContext(WorkContext);
return SmbStatus;
} // SrvSmbReadRaw
SMB_PROCESSOR_RETURN_TYPE
SrvSmbWriteRaw (
SMB_PROCESSOR_PARAMETERS
)
/*++
Routine Description:
Processes the Write Block Raw SMB.
Arguments:
SMB_PROCESSOR_PARAMETERS - See smbprocs.h for a description of the
parameters to SMB processor routines.
Return Value:
SMB_PROCESSOR_RETURN_TYPE - See smbprocs.h
--*/
{
PREQ_WRITE_RAW request;
PREQ_NT_WRITE_RAW ntRequest;
NTSTATUS status = STATUS_SUCCESS;
SMB_STATUS SmbStatus = SmbStatusInProgress;
PRFCB rfcb = NULL;
PLFCB lfcb;
ULONG immediateLength;
BOOLEAN immediateWriteDone = FALSE;
USHORT fid;
PCHAR writeAddress;
ULONG writeLength;
ULONG key;
SHARE_TYPE shareType;
LARGE_INTEGER offset;
PWORK_CONTEXT rawWorkContext;
PVOID finalResponseBuffer = NULL;
PCONNECTION connection;
PAGED_CODE( );
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
WorkContext->PreviousSMB = EVENT_TYPE_SMB_WRITE_RAW;
SrvWmiStartContext(WorkContext);
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.RawWritesAttempted );
request = (PREQ_WRITE_RAW)WorkContext->RequestParameters;
ntRequest = (PREQ_NT_WRITE_RAW)WorkContext->RequestParameters;
fid = SmbGetUshort( &request->Fid );
//
// If the client is MS-NET 1.03 or before, reject him. We don't
// support raw writes from these clients.
//
connection = WorkContext->Connection;
if ( connection->SmbDialect >= SmbDialectMsNet103 ) {
IF_DEBUG(SMB_ERRORS) {
KdPrint(( "Raw write request from MS-NET 1.03 client.\n" ));
}
status = STATUS_SMB_USE_STANDARD;
goto error_exit_no_rfcb;
}
IF_SMB_DEBUG(RAW1) {
KdPrint(( "Write Block Raw request; FID 0x%lx, count %ld, "
"offset %ld, immediate length %ld\n",
fid,
SmbGetUshort( &request->Count ),
SmbGetUlong( &request->Offset ),
SmbGetUshort( &request->DataLength ) ));
}
immediateLength = SmbGetUshort( &request->DataLength );
writeLength = SmbGetUshort( &request->Count );
//
// make sure the immediate length:
// is not greater than the total bytes, and
// does not go beyond what we are given
//
if ( ( immediateLength > writeLength ) ||
( immediateLength > ( WorkContext->ResponseBuffer->DataLength -
SmbGetUshort(&request->DataOffset) ) )
) {
status = STATUS_INVALID_SMB;
goto error_exit_no_rfcb;
}
//
// Verify the FID. If verified, the RFCB is referenced and its
// address is stored in the WorkContext block, and the RFCB address
// is returned. In addition, the active raw write count is
// incremented.
//
//
// See if the fid matches the cached fid.
//
rfcb = SrvVerifyFidForRawWrite(
WorkContext,
fid,
&status
);
if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
if ( !NT_SUCCESS( status ) ) {
//
// Invalid file ID or write behind error. Reject the request.
//
IF_DEBUG(ERRORS) {
KdPrint((
"SrvSmbWriteRaw: Status %X on FID: 0x%lx\n",
status,
SmbGetUshort( &request->Fid )
));
}
goto error_exit_no_rfcb;
}
//
// The work item has been queued because a raw write is in
// progress.
//
SmbStatus = SmbStatusInProgress;
goto Cleanup;
}
lfcb = rfcb->Lfcb;
shareType = rfcb->ShareType;
if( lfcb->Session->IsSessionExpired )
{
status = SESSION_EXPIRED_STATUS_CODE;
goto error_exit;
}
//
// Validate the word count.
//
if ( shareType == ShareTypePipe ) {
if ( (request->WordCount != 12) && (request->WordCount != 14) ) {
status = STATUS_INVALID_SMB;
goto error_exit;
}
} else {
if ( request->WordCount == 12 ) {
offset.QuadPart = SmbGetUlong( &request->Offset );
} else if ( request->WordCount == 14 ) {
offset.HighPart = SmbGetUlong( &ntRequest->OffsetHigh ) ;
offset.LowPart = SmbGetUlong( &ntRequest->Offset ) ;
//
// Reject negative offsets. Add the offset to the immediate
// length and make sure that the result is not negative. We do the
// first check ( highpart >= 0x7fffffff ) so that in most cases
// we do only one check.
//
if ( (ULONG)offset.HighPart >= (ULONG)0x7fffffff &&
( (offset.QuadPart < 0) ||
((offset.QuadPart + immediateLength) < 0) ) ) {
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbWriteRaw: Negative offset rejected.\n"));
}
status = STATUS_INVALID_SMB;
goto error_exit;
}
} else {
status = STATUS_INVALID_SMB;
goto error_exit;
}
}
//
// Verify that the client has write access to the file via the
// specified handle.
//
if ( !rfcb->WriteAccessGranted && !rfcb->AppendAccessGranted ) {
SrvStatistics.GrantedAccessErrors++;
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbWriteRaw: Read access not granted.\n"));
}
status = STATUS_ACCESS_DENIED;
goto error_exit;
}
//
// Ensure that the write is extending the file if the user only has append access
//
if( !rfcb->WriteAccessGranted &&
offset.QuadPart < rfcb->Mfcb->NonpagedMfcb->OpenFileSize.QuadPart ) {
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbWriteRaw: Only append access to file allowed!\n" ));
}
SrvStatistics.GrantedAccessErrors++;
status = STATUS_ACCESS_DENIED;
goto error_exit;
}
#ifdef INCLUDE_SMB_IFMODIFIED
rfcb->Lfcb->FileUpdated = TRUE;
#endif
rfcb->WrittenTo = TRUE;
//
// If this operation may block, and we're running short of
// resources, or if the target is a paused comm device, reject the
// request. Note that we do NOT write immediate data -- this is the
// same behavior as the OS/2 server.
//
// !!! Implement the pause comm device test.
//
if ( rfcb->BlockingModePipe ) {
if ( SrvReceiveBufferShortage( ) ) {
//
// Reject the request.
//
// !!! Consider routing the request to the FSP, instead.
//
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbWriteRaw: No resources for blocking "
"write\n" ));
}
SrvFailedBlockingIoCount++;
SrvStatistics.BlockingSmbsRejected++;
status = STATUS_SMB_USE_STANDARD;
goto error_exit;
} else {
//
// It is okay to start a blocking operation.
// SrvReceiveBufferShortage() has already incremented
// SrvBlockingOpsInProgress.
//
WorkContext->BlockingOperation = TRUE;
}
} else if ( shareType == ShareTypeDisk &&
( ((ULONG)request->WriteMode & SMB_WMODE_WRITE_THROUGH) << 1 !=
((ULONG)lfcb->FileMode & FILE_WRITE_THROUGH) ) ) {
//
// Change the write through mode of the file, if necessary.
//
ASSERT( SMB_WMODE_WRITE_THROUGH == 0x01 );
ASSERT( FILE_WRITE_THROUGH == 0x02 );
SrvSetFileWritethroughMode(
lfcb,
(BOOLEAN)( (SmbGetUshort( &request->WriteMode )
& SMB_WMODE_WRITE_THROUGH) != 0 )
);
}
#ifdef SLMDBG
{
PRFCB_TRACE entry;
ULONG immediateLength, rawLength;
KIRQL oldIrql;
ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
rfcb->WriteCount++;
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 = 0;
immediateLength = SmbGetUshort( &request->DataLength );
rawLength = SmbGetUshort( &request->Count ) - immediateLength;
if ( immediateLength != 0 ) {
entry->Flags = (UCHAR)(entry->Flags | 2);
}
if ( ((lfcb->FileObject->Flags & FO_CACHE_SUPPORTED) != 0)
) {
entry->Flags = (UCHAR)(entry->Flags | 1);
}
#if 0
if ( ((lfcb->FileObject->Flags & FO_CACHE_SUPPORTED) != 0) &&
(rawLength > SrvMaxCopyWriteLength) ) {
entry->Flags = (UCHAR)(entry->Flags | 1);
}
#endif
KeQuerySystemTime( &entry->Time );
entry->Data.ReadWrite.Offset = SmbGetUlong( &request->Offset );
entry->Data.ReadWrite.Length = SmbGetUshort( &request->Count );
}
#endif
//
// If immediate data was sent write it first.
//
// If this is a named pipe, do not write the data unless all of the
// write data was sent in the original request. We cannot do the
// write in 2 parts, in case this is a message mode pipe.
//
// *** Note that this is different from the OS/2 server. It turns
// out to be easier for us to write the immediate data first,
// rather than copying it into a staging buffer pending receipt
// of the raw write data. This is largely due to using MDL
// writes -- we don't allocate a staging buffer when we do an
// MDL write.
//
//
// Form the lock key using the FID and the PID.
//
// *** The FID must be included in the key in order to
// account for the folding of multiple remote
// compatibility mode opens into a single local
// open.
//
key = rfcb->ShiftedFid |
SmbGetAlignedUshort( &WorkContext->RequestHeader->Pid );
if ( immediateLength != 0 ) {
if ( (shareType != ShareTypePipe) ||
(SmbGetUshort( &request->Count ) == (USHORT)immediateLength) ) {
if ( lfcb->FastIoWrite != NULL ) {
writeAddress = (PCHAR)WorkContext->RequestHeader +
SmbGetUshort( &request->DataOffset );
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesAttempted );
try {
immediateWriteDone = lfcb->FastIoWrite(
lfcb->FileObject,
&offset,
immediateLength,
TRUE,
key,
writeAddress,
&WorkContext->Irp->IoStatus,
lfcb->DeviceObject
);
IF_SMB_DEBUG(RAW2) {
KdPrint(( "SrvSmbWriteRaw: fast immediate write %s\n",
immediateWriteDone ? "worked" : "failed" ));
}
if ( immediateWriteDone ) {
writeLength -= immediateLength;
offset.QuadPart += immediateLength;
} else {
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesFailed );
}
}
except( EXCEPTION_EXECUTE_HANDLER ) {
// Fall through to the slow path on an exception
NTSTATUS status = GetExceptionCode();
immediateWriteDone = FALSE;
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesFailed );
IF_DEBUG(ERRORS) {
KdPrint(("FastIoRead threw exception %x\n", status ));
}
}
}
}
} else {
immediateWriteDone = TRUE;
}
//
// If the remaining write length is 0 (strange but legal), send a
// success response.
//
if ( writeLength == 0 ) {
IF_SMB_DEBUG(RAW1) {
KdPrint(( "PrepareRawWrite: No raw data !?!\n" ));
}
status = STATUS_SUCCESS;
goto error_exit;
}
//
// Reject Raw write if:
// raw mode has been disabled, or
// connection is unreliable, or
// file is non-cached and it is a write behind and it is a disk
// file (this condition is necessary to prevent a client
// from getting old data back when it does a raw read
// immediately after a raw write. This can result in
// synchronization problems.
//
if ( !SrvEnableRawMode ||
!connection->EnableRawIo ) {
IF_SMB_DEBUG(RAW1) {
KdPrint(( "SrvSmbWriteRaw: Raw mode is disabled\n" ));
}
//
// Update server statistics.
//
status = STATUS_SMB_USE_STANDARD;
goto error_exit;
}
//
// Obtain a raw mode work item from the raw mode pool. If none are
// available, ask the client to use a standard write request.
//
rawWorkContext = SrvGetRawModeWorkItem( );
if ( rawWorkContext == NULL ) {
IF_DEBUG(ERRORS) {
KdPrint(( "SrvSmbWriteRaw: No raw mode work items "
"available\n" ));
}
SrvOutOfRawWorkItemCount++;
status = STATUS_SMB_USE_STANDARD;
goto error_exit;
}
IF_SMB_DEBUG(RAW2) KdPrint(( "rawWorkContext: 0x%p\n", rawWorkContext ));
//
// If writethough mode was specified, we'll need to send a final
// response SMB. Allocate a nonpaged buffer to contain the
// response. If this fails, ask the client to use a standard write
// request.
//
if ( (SmbGetUshort( &request->WriteMode ) &
SMB_WMODE_WRITE_THROUGH) != 0 ) {
finalResponseBuffer = ALLOCATE_NONPAGED_POOL(
sizeof(SMB_HEADER) +
SIZEOF_SMB_PARAMS(RESP_WRITE_COMPLETE,0),
BlockTypeDataBuffer
);
IF_SMB_DEBUG(RAW2) {
KdPrint(( "finalResponseBuffer: 0x%p\n", finalResponseBuffer ));
}
if ( finalResponseBuffer == NULL ) {
INTERNAL_ERROR(
ERROR_LEVEL_EXPECTED,
"SrvSmbWriteRaw: Unable to allocate %d bytes from "
"nonpaged pool",
sizeof(SMB_HEADER) + SIZEOF_SMB_PARAMS(RESP_WRITE_COMPLETE,0),
NULL
);
SrvRequeueRawModeWorkItem( rawWorkContext );
status = STATUS_SMB_USE_STANDARD;
goto error_exit;
}
}
//
// Save necessary context information in the additional work context
// block.
//
rawWorkContext->Parameters.WriteRaw.FinalResponseBuffer = finalResponseBuffer;
rawWorkContext->Parameters.WriteRaw.ImmediateWriteDone = immediateWriteDone;
rawWorkContext->Parameters.WriteRaw.ImmediateLength = immediateLength;
rawWorkContext->Parameters.WriteRaw.Offset = offset;
rawWorkContext->Parameters.WriteRaw.Pid =
SmbGetAlignedUshort( &WorkContext->RequestHeader->Pid );
WorkContext->Parameters.WriteRawPhase1.RawWorkContext = rawWorkContext;
//
// Copy the start time from the original work context. Indicate
// that no statistics should be saved from the original, and what
// kind of statistics should be saved from the raw work context.
//
rawWorkContext->StartTime = 0;
//
// Copy pointers from the original work context to the raw work
// context, referencing as necessary.
//
rawWorkContext->Endpoint = WorkContext->Endpoint; // not a referenced ptr
rawWorkContext->Connection = connection;
SrvReferenceConnection( connection );
rawWorkContext->Share = NULL;
rawWorkContext->Session = NULL;
rawWorkContext->TreeConnect = NULL;
rawWorkContext->Rfcb = rfcb;
SrvReferenceRfcb( rfcb );
//
// Prepare either a copy write or an MDL write, as appropriate.
//
if ( !(lfcb->FileObject->Flags & FO_CACHE_SUPPORTED) ) {
//
// The file system doesn't support MDL write. Prepare a copy
// write.
//
rawWorkContext->Parameters.WriteRaw.MdlWrite = FALSE;
PrepareRawCopyWrite( WorkContext );
IF_DEBUG(TRACE2) KdPrint(( "SrvSmbWriteRaw complete\n" ));
SmbStatus = SmbStatusInProgress;
goto Cleanup;
}
//
// The file system supports MDL write. Prepare an MDL write.
//
rawWorkContext->Parameters.WriteRaw.MdlWrite = TRUE;
rawWorkContext->Parameters.WriteRaw.Length = writeLength;
//
// Try the fast path first.
//
WorkContext->Irp->MdlAddress = NULL;
WorkContext->Irp->IoStatus.Information = 0;
IF_SMB_DEBUG(RAW2) {
KdPrint(( "SrvSmbWriteRaw: trying fast path for offset %ld, "
"length %ld\n", offset.LowPart, writeLength ));
}
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesAttempted );
if ( lfcb->PrepareMdlWrite(
lfcb->FileObject,
&offset,
writeLength,
key,
&WorkContext->Irp->MdlAddress,
&WorkContext->Irp->IoStatus,
lfcb->DeviceObject
) && WorkContext->Irp->MdlAddress != NULL ) {
//
// The fast I/O path worked. Send the go-ahead response.
//
#if DBG
IF_SMB_DEBUG(RAW2) {
KdPrint(( "SrvSmbWriteRaw: fast path worked; MDL %p, length 0x%p\n", WorkContext->Irp->MdlAddress,
(PVOID)WorkContext->Irp->IoStatus.Information ));
DumpMdlChain( WorkContext->Irp->MdlAddress );
}
#endif
WorkContext->bAlreadyTrace = TRUE;
SrvFsdRestartPrepareRawMdlWrite( WorkContext );
IF_DEBUG(TRACE2) KdPrint(( "SrvSmbWriteRaw complete\n" ));
SmbStatus = SmbStatusInProgress;
goto Cleanup;
}
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesFailed );
//
// The fast I/O path failed. Build the write request, reusing the
// receive IRP.
//
// The fast path may have partially succeeded, returning a partial
// MDL chain. We need to adjust our write request to account for
// that.
//
IF_SMB_DEBUG(RAW2) {
KdPrint(( "SrvSmbWriteRaw: fast path failed; MDL %p, length 0x%p\n", WorkContext->Irp->MdlAddress,
(PVOID)WorkContext->Irp->IoStatus.Information ));
}
offset.QuadPart += WorkContext->Irp->IoStatus.Information;
writeLength -= (ULONG)WorkContext->Irp->IoStatus.Information;
SrvBuildReadOrWriteRequest(
WorkContext->Irp, // input IRP address
lfcb->FileObject, // target file object address
WorkContext, // context
IRP_MJ_WRITE, // major function code
IRP_MN_MDL, // minor function code
NULL, // buffer address (ignored)
writeLength, // buffer length
WorkContext->Irp->MdlAddress, // MDL address
offset, // byte offset
key // lock key
);
//
// Pass the request to the file system.
//
IF_SMB_DEBUG(RAW2) {
KdPrint(( "SrvSmbWriteRaw: write to file 0x%p, offset %ld, length %ld\n",
lfcb->FileObject, offset.LowPart, writeLength ));
}
WorkContext->bAlreadyTrace = TRUE;
WorkContext->FsdRestartRoutine = SrvFsdRestartPrepareRawMdlWrite;
DEBUG WorkContext->FspRestartRoutine = NULL;
(VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
//
// The MDL write has been started. When it completes, processing
// resumes at SrvFsdRestartPrepareRawMdlWrite.
//
IF_DEBUG(TRACE2) KdPrint(( "SrvSmbWriteRaw complete\n" ));
SmbStatus = SmbStatusInProgress;
goto Cleanup;
error_exit:
SrvDecrementRawWriteCount( rfcb );
error_exit_no_rfcb:
SrvFsdBuildWriteCompleteResponse(
WorkContext,
status,
immediateWriteDone ? immediateLength : 0
);
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.RawWritesRejected );
IF_DEBUG(TRACE2) KdPrint(( "SrvSmbWriteRaw complete\n" ));
SmbStatus = SmbStatusSendResponse;
Cleanup:
SrvWmiEndContext(WorkContext);
return SmbStatus;
} // SrvSmbWriteRaw
VOID SRVFASTCALL
AbortRawWrite(
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Handles cleanup when a raw write is aborted after the interim
"go-ahead" response has been sent. This routine is only used
when a catastrophic errors occurs -- for example, the connection
is closing. It does not send a final response to the client.
This routine is called in the FSP.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
This must be a pointer to the raw mode work item -- not the
original work item that received the Write Raw SMB.
Return Value:
None.
--*/
{
PMDL cacheMdl;
PMDL partialMdl;
NTSTATUS status;
PAGED_CODE( );
//
// Deallocate the final response buffer, if any.
//
if ( WorkContext->Parameters.WriteRaw.FinalResponseBuffer != NULL ) {
DEALLOCATE_NONPAGED_POOL(
WorkContext->Parameters.WriteRaw.FinalResponseBuffer
);
WorkContext->Parameters.WriteRaw.FinalResponseBuffer = NULL;
}
if ( !WorkContext->Parameters.WriteRaw.MdlWrite ) {
//
// This was a copy write. 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.ImmediateWriteDone ) {
DEALLOCATE_NONPAGED_POOL( WorkContext->RequestBuffer->Buffer );
} else {
DEALLOCATE_NONPAGED_POOL(
(PCHAR)WorkContext->RequestBuffer->Buffer -
WorkContext->Parameters.WriteRaw.ImmediateLength );
}
//
// Dereference control blocks and requeue the raw mode work
// item.
//
WorkContext->ResponseBuffer->Buffer = NULL;
SrvRestartWriteCompleteResponse( WorkContext );
return;
}
//
// This was an MDL write. If a partial MDL was built (because of
// immediate data), unmap it. Then complete the MDL write,
// indicating that no data was actually written.
//
cacheMdl = WorkContext->Parameters.WriteRaw.FirstMdl;
partialMdl = WorkContext->Irp->MdlAddress;
if ( partialMdl != cacheMdl ) {
ASSERT( (partialMdl->MdlFlags & MDL_PARTIAL) != 0 );
MmPrepareMdlForReuse( partialMdl );
}
#if DBG
IF_SMB_DEBUG(RAW2) {
KdPrint(( "AbortRawWrite: Completing MDL write with length 0\n" ));
DumpMdlChain( cacheMdl );
}
#endif
if( WorkContext->Rfcb->Lfcb->MdlWriteComplete == NULL ||
WorkContext->Rfcb->Lfcb->MdlWriteComplete(
WorkContext->Rfcb->Lfcb->FileObject,
&WorkContext->Parameters.WriteRaw.Offset,
cacheMdl,
WorkContext->Rfcb->Lfcb->DeviceObject ) == FALSE ) {
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
cacheMdl,
IRP_MJ_WRITE,
&WorkContext->Parameters.WriteRaw.Offset,
WorkContext->Parameters.WriteRaw.Length
);
if( !NT_SUCCESS( status ) ) {
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
}
}
SrvRestartWriteCompleteResponse( WorkContext );
return;
} // AbortRawWrite
VOID SRVFASTCALL
PrepareRawCopyWrite (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Prepares for a raw "copy" write. Allocates a buffer to receive the
raw data, prepares a work context block and an IRP describing the
raw receive, queues it to the connection, and sends a "go-ahead"
response.
Any immediate data sent in the request SMB has already been written
when this routine is called, unless this is a named pipe write, in
which case the immediate data has not been written. In this case,
we alllocate a buffer big enough for the the immediate data plus the
raw data, then copy the immediate data to the raw buffer before
proceeding. The raw data will be received appended to the immediate
data. Then both peices can be written with a single write. This is
needed so that data written to a message mode pipe will not be
written in 2 pieces.
This routine is called in the FSP.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PWORK_CONTEXT rawWorkContext;
PREQ_WRITE_RAW request;
PRESP_WRITE_RAW_INTERIM response;
NTSTATUS status;
PVOID rawBuffer;
PCHAR writeAddress;
PMDL mdl;
SHARE_TYPE shareType;
PVOID finalResponseBuffer;
ULONG immediateLength;
BOOLEAN immediateWriteDone;
ULONG writeLength;
ULONG bufferLength;
BOOLEAN sendWriteComplete = TRUE;
PIO_STACK_LOCATION irpSp;
PAGED_CODE( );
IF_DEBUG(WORKER1) KdPrint(( " - PrepareRawCopyWrite\n" ));
//
// Set up local variables.
//
rawWorkContext = WorkContext->Parameters.WriteRawPhase1.RawWorkContext;
request = (PREQ_WRITE_RAW)WorkContext->RequestParameters;
shareType = rawWorkContext->Rfcb->ShareType;
immediateLength = rawWorkContext->Parameters.WriteRaw.ImmediateLength;
immediateWriteDone =
rawWorkContext->Parameters.WriteRaw.ImmediateWriteDone;
writeLength = SmbGetUshort( &request->Count ) - immediateLength;
finalResponseBuffer =
rawWorkContext->Parameters.WriteRaw.FinalResponseBuffer;
if ( !immediateWriteDone ) {
bufferLength = writeLength + immediateLength;
} else {
bufferLength = writeLength;
}
//
// Allocate a nonpaged buffer to contain the write data. If this
// fails, ask the client to use a standard write request.
//
rawBuffer = ALLOCATE_NONPAGED_POOL( bufferLength, BlockTypeDataBuffer );
IF_SMB_DEBUG(RAW2) KdPrint(( "rawBuffer: 0x%p\n", rawBuffer ));
if ( rawBuffer == NULL ) {
// !!! Should we log this error?
IF_DEBUG(ERRORS) {
KdPrint(( "PrepareRawCopyWrite: Unable to allocate "
"raw buffer\n" ));
}
status = STATUS_SMB_USE_STANDARD;
goto abort;
}
if ( !immediateWriteDone ) {
//
// Copy the immediate data to the raw buffer.
//
writeAddress = (PCHAR)WorkContext->RequestHeader +
SmbGetUshort( &request->DataOffset );
RtlCopyMemory( rawBuffer, writeAddress, immediateLength );
//
// Move the virtual start of the raw buffer to the end of the
// immediate data.
//
rawBuffer = (PCHAR)rawBuffer + immediateLength;
}
//
// 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)
);
}
//
// Set up the buffer descriptor for the raw buffer.
//
rawWorkContext->RequestBuffer->Buffer = rawBuffer;
rawWorkContext->RequestBuffer->BufferLength = writeLength;
//
// Initialize the MDL to describe the raw buffer.
// (SrvBuildIoControlRequest will lock the buffer for I/O.)
//
mdl = rawWorkContext->RequestBuffer->Mdl;
MmInitializeMdl( mdl, rawBuffer, writeLength );
//
// Build the mdl
//
MmBuildMdlForNonPagedPool( mdl );
//
// Set up the restart routines in the work context block.
//
rawWorkContext->FsdRestartRoutine = SrvQueueWorkToFspAtDpcLevel;
rawWorkContext->FspRestartRoutine = SrvRestartRawReceive;
//
// Build the I/O request packet.
//
(VOID)SrvBuildIoControlRequest(
rawWorkContext->Irp, // input IRP address
NULL, // target file object address
rawWorkContext, // context
IRP_MJ_INTERNAL_DEVICE_CONTROL, // major function
TDI_RECEIVE, // minor function
NULL, // input buffer address
0, // input buffer length
rawBuffer, // output buffer address
writeLength, // output buffer length
mdl, // MDL address
NULL
);
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 );
//
// Build 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
);
//
// Send off the interim response by ending processing of the SMB.
//
SrvEndSmbProcessing( WorkContext, SmbStatusSendResponse );
IF_DEBUG(TRACE2) KdPrint(( "PrepareRawCopyWrite complete\n" ));
return;
abort:
//
// For one reason or another, we are not going to receive any raw
// data, so clean up.
//
if ( finalResponseBuffer != NULL ) {
DEALLOCATE_NONPAGED_POOL( finalResponseBuffer );
}
SrvRestartWriteCompleteResponse( rawWorkContext );
//
// If a response is to be sent, send it now.
//
if ( sendWriteComplete ) {
SrvFsdBuildWriteCompleteResponse(
WorkContext,
status,
immediateWriteDone ? immediateLength : 0
);
SrvEndSmbProcessing( WorkContext, SmbStatusSendResponse );
} else {
SrvDereferenceWorkItem( WorkContext );
}
IF_DEBUG(TRACE2) KdPrint(( "PrepareRawCopyWrite complete\n" ));
return;
} // PrepareRawCopyWrite
BOOLEAN SRVFASTCALL
ReadRawPipe (
IN PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes the Read Block Raw SMB for pipes.
Note that Read Block Raw cannot return an error response. When the
server is unable to process the request, for whatever reason, it
simply responds with a zero-length message. The client uses a
normal Read SMB to determine what happened.
Arguments:
WorkContext - Pointer to the work context block.
Return Value:
TRUE, if operation succeeds.
FALSE, otherwise
--*/
{
PREQ_READ_RAW request;
USHORT fid;
PRFCB rfcb;
PLFCB lfcb;
PCONNECTION connection;
UCHAR minorFunction = 0;
BOOLEAN byteModePipe;
PVOID rawBuffer = NULL;
CLONG readLength;
PMDL mdl = NULL;
ULONG key;
LARGE_INTEGER offset;
PFILE_PIPE_PEEK_BUFFER pipePeekBuffer;
PAGED_CODE( );
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.RawReadsAttempted );
request = (PREQ_READ_RAW)WorkContext->RequestParameters;
fid = SmbGetUshort( &request->Fid );
readLength = SmbGetUshort( &request->MaxCount );
//
// If raw mode has been disabled or if the connection is unreliable,
// reject the raw read. Ask the client to use standard read by
// sending a zero-length response. The client will react by issuing
// a normal Read SMB, which we will be able to process.
//
connection = WorkContext->Connection;
rfcb = WorkContext->Rfcb;
lfcb = rfcb->Lfcb;
byteModePipe = rfcb->ByteModePipe;
//
// Verify that the client has read access to the file via the
// specified handle.
//
if ( !rfcb->ReadAccessGranted ) {
SrvStatistics.GrantedAccessErrors++;
IF_DEBUG(ERRORS) {
KdPrint(( "ReadRawPipe: Read access not granted.\n"));
}
return(FALSE);
}
//
// Verify the word counts
//
if ( (request->WordCount != 8) && (request->WordCount != 10) ) {
//
// Invalid word count. Return 0 bytes.
//
return(FALSE);
}
//
// If this operation may block, and we're running short of
// resources, or if the target is a paused comm device, reject the
// request.
//
if ( rfcb->BlockingModePipe ) {
if ( SrvReceiveBufferShortage( ) ) {
//
// Reject the request.
//
// !!! Consider routing the request to the FSP, instead.
//
IF_DEBUG(ERRORS) {
KdPrint(( "ReadRawPipe: No resources for blocking "
"read\n" ));
}
SrvFailedBlockingIoCount++;
SrvStatistics.BlockingSmbsRejected++;
return(FALSE);
}
//
// It is okay to start a blocking operation.
// SrvReceiveBufferShortage() has already incremented
// SrvBlockingOpsInProgress.
//
WorkContext->BlockingOperation = TRUE;
}
//
// Form the lock key using the FID and the PID.
//
// *** The FID must be included in the key in order to account for
// the folding of multiple remote compatibility mode opens into
// a single local open.
//
key = rfcb->ShiftedFid |
SmbGetAlignedUshort( &WorkContext->RequestHeader->Pid );
//
// If the SMB buffer is large enough, use it to do the local read.
//
if ( readLength <= WorkContext->ResponseBuffer->BufferLength ) {
WorkContext->Parameters.ReadRaw.SavedResponseBuffer = NULL;
WorkContext->Parameters.ReadRaw.MdlRead = FALSE;
//
// Try the fast I/O path first.
//
if ( byteModePipe && (lfcb->FastIoRead != NULL) ) {
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
try {
if ( lfcb->FastIoRead(
lfcb->FileObject,
&offset,
readLength,
TRUE,
key,
WorkContext->ResponseBuffer->Buffer,
&WorkContext->Irp->IoStatus,
lfcb->DeviceObject
) ) {
//
// The fast I/O path worked. Send the data.
//
WorkContext->bAlreadyTrace = TRUE;
SrvFsdRestartReadRaw( WorkContext );
return TRUE;
}
}
except( EXCEPTION_EXECUTE_HANDLER ) {
// Fall through to the slow path on an exception
NTSTATUS status = GetExceptionCode();
IF_DEBUG(ERRORS) {
KdPrint(("FastIoRead threw exception %x\n", status ));
}
}
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
}
//
// The fast I/O path failed, so we need to use a regular copy
// I/O request. Build an MDL describing the read buffer.
//
// *** Note the assumption that the response buffer already has
// a valid full MDL from which a partial MDL can be built.
//
IoBuildPartialMdl(
WorkContext->ResponseBuffer->Mdl,
WorkContext->ResponseBuffer->PartialMdl,
WorkContext->ResponseBuffer->Buffer,
readLength
);
mdl = WorkContext->ResponseBuffer->PartialMdl;
rawBuffer = WorkContext->ResponseHeader;
ASSERT( minorFunction == 0 );
} else {
//
// We have to use a normal "copy" read. We need to allocate
// a separate raw buffer.
//
ASSERT( minorFunction == 0 );
WorkContext->Parameters.ReadRaw.MdlRead = FALSE;
rawBuffer = ALLOCATE_NONPAGED_POOL(
readLength,
BlockTypeDataBuffer
);
IF_SMB_DEBUG(RAW2) KdPrint(( "rawBuffer: 0x%p\n", rawBuffer ));
if ( rawBuffer == NULL ) {
IF_DEBUG(ERRORS) {
KdPrint(( "ReadRawPipe: Unable to allocate raw buffer\n" ));
}
return(FALSE);
}
//
// We also need a buffer descriptor.
//
// *** Note: Currently, ResponseBuffer == RequestBuffer in a
// WorkContext block, so we don't really have to save
// the ResponseBuffer field. But we do so just to be on
// the safe side.
//
WorkContext->Parameters.ReadRaw.SavedResponseBuffer =
WorkContext->ResponseBuffer;
WorkContext->ResponseBuffer = ALLOCATE_NONPAGED_POOL(
sizeof(BUFFER),
BlockTypeBuffer
);
if ( WorkContext->ResponseBuffer == NULL ) {
INTERNAL_ERROR(
ERROR_LEVEL_EXPECTED,
"ReadRawPipe: Unable to allocate %d bytes from "
"nonpaged pool.",
sizeof(BUFFER),
NULL
);
DEALLOCATE_NONPAGED_POOL( rawBuffer );
WorkContext->ResponseBuffer =
WorkContext->Parameters.ReadRaw.SavedResponseBuffer;
return(FALSE);
}
WorkContext->ResponseBuffer->Buffer = rawBuffer;
WorkContext->ResponseBuffer->BufferLength = readLength;
//
// Finally, we need an MDL to describe the raw buffer.
//
// *** We used to try to use the PartialMdl for the SMB
// buffer here, if it was big enough. But since we
// already decided that the buffer itself isn't big
// enough, it's extremely likely that the MDL isn't big
// enough either.
//
mdl = IoAllocateMdl( rawBuffer, readLength, FALSE, FALSE, NULL );
if ( mdl == NULL ) {
DEALLOCATE_NONPAGED_POOL( WorkContext->ResponseBuffer );
WorkContext->ResponseBuffer =
WorkContext->Parameters.ReadRaw.SavedResponseBuffer;
DEALLOCATE_NONPAGED_POOL( rawBuffer );
return(FALSE);
}
WorkContext->ResponseBuffer->Mdl = mdl;
//
// Build the mdl
//
MmBuildMdlForNonPagedPool( mdl );
//
// Try the fast I/O path first.
//
if ( byteModePipe && (lfcb->FastIoRead != NULL) ) {
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
try {
if ( lfcb->FastIoRead(
lfcb->FileObject,
&offset,
readLength,
TRUE,
key,
WorkContext->ResponseBuffer->Buffer,
&WorkContext->Irp->IoStatus,
lfcb->DeviceObject
) ) {
//
// The fast I/O path worked. Send the data.
//
WorkContext->bAlreadyTrace = TRUE;
SrvFsdRestartReadRaw( WorkContext );
return TRUE;
}
}
except( EXCEPTION_EXECUTE_HANDLER ) {
// Fall through to the slow path on an exception
NTSTATUS status = GetExceptionCode();
IF_DEBUG(ERRORS) {
KdPrint(("FastIoRead threw exception %x\n", status ));
}
}
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
}
//
// The fast I/O path failed, so we need to use a regular copy
// I/O request.
//
} // read fits in SMB buffer?
//
// This is a read block raw on a named pipe. If this is a
// non-blocking read on a blocking mode pipe, we need to do a
// peek first. This is to ensure that we don't end up waiting
// when we're not supposed to.
//
// *** We used to have to peek on message mode pipes, in case
// the message was too large or zero-length. But now we
// have a special pipe FSCTL for this. See below.
//
if ( (SmbGetUshort( &request->MinCount ) == 0) &&
rfcb->BlockingModePipe ) {
//
// Allocate a buffer to peek the pipe. This buffer is freed
// in RestartPipeReadRawPeek().
//
pipePeekBuffer = ALLOCATE_NONPAGED_POOL(
FIELD_OFFSET( FILE_PIPE_PEEK_BUFFER, Data[0] ),
BlockTypeDataBuffer
);
if ( pipePeekBuffer == NULL ) {
//
// Can't allocate a buffer to do a peek. Nothing to do
// but return zero bytes to the client.
//
INTERNAL_ERROR(
ERROR_LEVEL_EXPECTED,
"ReadRawPipe: Unable to allocate %d bytes from"
"nonpaged pool.",
FIELD_OFFSET( FILE_PIPE_PEEK_BUFFER, Data[0] ),
NULL
);
if ( WorkContext->Parameters.ReadRaw.SavedResponseBuffer !=
NULL ) {
if ( mdl != WorkContext->Parameters.ReadRaw.
SavedResponseBuffer->PartialMdl ) {
IoFreeMdl( mdl );
}
DEALLOCATE_NONPAGED_POOL( WorkContext->ResponseBuffer );
WorkContext->ResponseBuffer =
WorkContext->Parameters.ReadRaw.SavedResponseBuffer;
DEALLOCATE_NONPAGED_POOL( rawBuffer );
}
return(FALSE);
}
//
// Save the address of the pipe peek buffer for the restart
// routine.
//
WorkContext->Parameters.ReadRaw.ReadRawOtherInfo.PipePeekBuffer =
pipePeekBuffer;
//
// Build the pipe peek request. We just want the header
// information. We do not need any data.
//
SrvBuildIoControlRequest(
WorkContext->Irp,
lfcb->FileObject,
WorkContext,
IRP_MJ_FILE_SYSTEM_CONTROL,
FSCTL_PIPE_PEEK,
pipePeekBuffer,
0,
NULL,
FIELD_OFFSET( FILE_PIPE_PEEK_BUFFER, Data[0] ),
NULL,
NULL
);
WorkContext->FsdRestartRoutine = SrvQueueWorkToFspAtDpcLevel;
WorkContext->FspRestartRoutine = RestartPipeReadRawPeek;
//
// Pass the request to NPFS.
//
(VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
} else {
//
// This is a blocking read, or a non-blocking read on a
// non-blocking pipe. Build and issue the read request. If
// this is a message-mode pipe, use the special ReadOverflow
// FSCTL. This FSCTL returns an error if the pipe is a
// message mode pipe and the first message in the pipe is
// either too big or has length 0. We need this because we
// can't return an error on a raw read -- the best we can do
// is return a zero-length message. So we need the
// operation to act like a Peek if the message is the wrong
// size. That's what the special FSCTL gives us.
//
SrvBuildIoControlRequest(
WorkContext->Irp,
lfcb->FileObject,
WorkContext,
IRP_MJ_FILE_SYSTEM_CONTROL,
byteModePipe ?
FSCTL_PIPE_INTERNAL_READ : FSCTL_PIPE_INTERNAL_READ_OVFLOW,
WorkContext->ResponseBuffer->Buffer,
0,
NULL,
SmbGetUshort( &request->MaxCount ),
NULL,
NULL
);
WorkContext->bAlreadyTrace = TRUE;
WorkContext->FsdRestartRoutine = SrvFsdRestartReadRaw;
DEBUG WorkContext->FspRestartRoutine = NULL;
IF_SMB_DEBUG(RAW2) {
KdPrint((
"ReadRawPipe: reading from file 0x%p, "
"length %ld, destination 0x%p\n",
lfcb->FileObject,
SmbGetUshort( &request->MaxCount ),
WorkContext->Parameters.ReadRaw.
SavedResponseBuffer->Buffer
));
}
(VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
//
// The read has been started. When it completes, processing
// resumes at SrvFsdRestartReadRaw.
//
}
IF_DEBUG(TRACE2) KdPrint(( "ReadRawPipe complete\n" ));
return TRUE;
} // ReadRawPipe
VOID SRVFASTCALL
RestartMdlReadRawResponse (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Processes response send completion for an MDL read. Releases the
MDL back to the file system.
This routine is called in the FSP.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
NTSTATUS status;
PAGED_CODE( );
IF_DEBUG(FSD2) KdPrint(( " - RestartMdlReadRawResponse\n" ));
//
// Call the Cache Manager to release the MDL chain. If the MDL read
// failed to return an MDL (for example, if the read was entirely
// beyond EOF), then we don't have to do this.
//
if ( WorkContext->Irp->MdlAddress ) {
if( WorkContext->Rfcb->Lfcb->MdlReadComplete == NULL ||
WorkContext->Rfcb->Lfcb->MdlReadComplete(
WorkContext->Rfcb->Lfcb->FileObject,
WorkContext->Irp->MdlAddress,
WorkContext->Rfcb->Lfcb->DeviceObject ) == FALSE ) {
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
WorkContext->Irp->MdlAddress,
IRP_MJ_READ,
&WorkContext->Parameters.ReadRaw.ReadRawOtherInfo.Offset,
WorkContext->Parameters.ReadRaw.ReadRawOtherInfo.Length
);
if( !NT_SUCCESS( status ) ) {
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
}
}
}
//
// Start oplock break notifications, if any are outstanding.
// SrvSendDelayedOplockBreak also sets read raw in progress to FALSE.
//
SrvSendDelayedOplockBreak( WorkContext->Connection );
//
// Finish postprocessing of the SMB.
//
SrvDereferenceWorkItem( WorkContext );
IF_DEBUG(FSD2) KdPrint(( "RestartMdlReadRawResponse complete.\n" ));
return;
} // RestartMdlReadRawResponse
VOID SRVFASTCALL
RestartPipeReadRawPeek (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
This function continues a read raw on a named pipe handle.
This routine is called in the FSP.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PFILE_PIPE_PEEK_BUFFER pipePeekBuffer;
PREQ_READ_RAW request;
ULONG readLength;
ULONG readDataAvailable;
ULONG messageLength;
ULONG numberOfMessages;
PLFCB lfcb;
PRFCB rfcb;
PAGED_CODE( );
pipePeekBuffer = WorkContext->Parameters.ReadRaw.ReadRawOtherInfo.
PipePeekBuffer;
request = (PREQ_READ_RAW)WorkContext->RequestParameters;
readLength = SmbGetUshort( &request->MaxCount );
rfcb = WorkContext->Rfcb;
lfcb = rfcb->Lfcb;
//
// The pipe peek has completed. Extract the critical information,
// then free the buffer.
//
readDataAvailable = pipePeekBuffer->ReadDataAvailable;
messageLength = pipePeekBuffer->MessageLength;
numberOfMessages = pipePeekBuffer->NumberOfMessages;
DEALLOCATE_NONPAGED_POOL( pipePeekBuffer );
//
// The read request is a non-blocking read on a blocking mode pipe.
// We react differently based on whether it is a byte mode pipe or
// a message mode pipe.
//
// Byte mode: If there is _any_ data in the pipe, we go get it. The
// read will complete immediately, copying either as much data
// as is available, or enough to fill the buffer, whichever is
// less, and there will be no error. If there is no data in
// the pipe, we have to return immediately, because we can't
// wait for data to arrive.
//
// Message mode: If there are no messages available, or if the
// first message is either zero-length or bigger than the
// client's buffer, we return immediately. We can't indicate
// underflow or overflow, so we can't allow the message to be
// pulled out of the queue. Otherwise, we can do the read.
//
if ( ( rfcb->ByteModePipe &&
(readDataAvailable == 0)
)
||
( !rfcb->ByteModePipe &&
( (numberOfMessages == 0)
||
(messageLength == 0)
||
(messageLength > readLength)
)
)
) {
if ( WorkContext->Parameters.ReadRaw.SavedResponseBuffer != NULL ) {
if ( WorkContext->ResponseBuffer->Mdl != WorkContext->Parameters.
ReadRaw.SavedResponseBuffer->PartialMdl ) {
IoFreeMdl( WorkContext->ResponseBuffer->Mdl );
}
DEALLOCATE_NONPAGED_POOL( WorkContext->ResponseBuffer->Buffer );
DEALLOCATE_NONPAGED_POOL( WorkContext->ResponseBuffer );
WorkContext->ResponseBuffer =
WorkContext->Parameters.ReadRaw.SavedResponseBuffer;
}
WorkContext->ResponseParameters = WorkContext->ResponseHeader;
SrvFsdSendResponse( WorkContext );
IF_DEBUG(TRACE2) KdPrint(( "RestartPipeReadRawPeek complete\n" ));
return;
}
//
// We have bypassed all of the pitfalls of doing a read block raw
// on a named pipe.
//
// Build and issue the read request.
//
// *** Note that we do not use the special ReadOverflow FSCTL here
// because we know from the above tests that we don't need to.
//
SrvBuildIoControlRequest(
WorkContext->Irp,
lfcb->FileObject,
WorkContext,
IRP_MJ_FILE_SYSTEM_CONTROL,
FSCTL_PIPE_INTERNAL_READ,
WorkContext->ResponseBuffer->Buffer,
0,
NULL,
readLength,
NULL,
NULL
);
WorkContext->bAlreadyTrace = TRUE;
WorkContext->FsdRestartRoutine = SrvFsdRestartReadRaw;
DEBUG WorkContext->FspRestartRoutine = NULL;
IF_SMB_DEBUG(RAW2) {
KdPrint(( "RestartPipeReadRawPeek: reading from file 0x%p, length %ld, destination 0x%p\n",
lfcb->FileObject, readLength,
WorkContext->ResponseBuffer->Buffer ));
}
(VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
//
// The read has been started. When it completes, processing resumes
// at SrvFsdRestartReadRaw.
//
IF_DEBUG(TRACE2) KdPrint(( "RestartPipeReadRawPeek complete\n" ));
return;
} // RestartPipeReadRawPeek
VOID SRVFASTCALL
SrvDecrementRawWriteCount (
IN PRFCB Rfcb
)
/*++
Routine Description:
This routine decrements the count of active raw writes for an RFCB.
If the count goes to zero, and there are work items queued pending
completion of the raw write, they are restarted. If the count goes
to zero, and the RFCB is closing, then the RFCB's cleanup is resumed
here.
This routine is called in both the FSP and the FSD.
Arguments:
Rfcb - Supplies a pointer to the RFCB.
Return Value:
None.
--*/
{
PLIST_ENTRY listEntry;
PWORK_CONTEXT workContext;
KIRQL oldIrql;
PCONNECTION connection = Rfcb->Connection;
//
// Acquire the spin lock that guards the count.
//
ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
//
// Decrement the active raw write count.
//
ASSERT( Rfcb->RawWriteCount > 0 );
Rfcb->RawWriteCount--;
if ( Rfcb->RawWriteCount == 0 ) {
//
// No raw writes are active. Flush the list of work items
// that were queued pending the completion of the raw write
// by restarting each of them.
//
while ( !IsListEmpty(&Rfcb->RawWriteSerializationList) ) {
listEntry = RemoveHeadList( &Rfcb->RawWriteSerializationList );
workContext = CONTAINING_RECORD(
listEntry,
WORK_CONTEXT,
ListEntry
);
QUEUE_WORK_TO_FSP( workContext );
}
if ( GET_BLOCK_STATE(Rfcb) == BlockStateClosing ) {
//
// The count is now zero and the RFCB is closing. The
// cleanup was deferred pending the completion of all raw
// writes. Do the cleanup now.
//
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
SrvCompleteRfcbClose( Rfcb );
} else {
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
}
} else {
RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
}
return;
} // SrvDecrementRawWriteCount
VOID SRVFASTCALL
SrvRestartRawReceive (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
This is the restart routine that is invoked when Write Block Raw
write data is received. It starts the local write operation.
This routine is called in the FSP.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PCONNECTION connection;
PIRP irp;
PRFCB rfcb;
PLFCB lfcb;
PMDL mdl;
PMDL partialMdl;
PCHAR writeAddress;
CLONG writeLength;
LARGE_INTEGER offset;
PVOID finalResponseBuffer;
CLONG immediateLength;
ULONG key;
NTSTATUS status;
PAGED_CODE( );
IF_DEBUG(FSD1) KdPrint(( " - SrvRestartRawReceive\n" ));
//
// If the I/O request failed or was canceled, or if the connection
// that received the message is no longer active, or if the server
// FSP isn't active, abort the request.
//
// *** Note that we don't send a final response in any of these
// cases. We assume that the failure is catastrophic, so
// there's no use sending a response.
//
connection = WorkContext->Connection;
irp = WorkContext->Irp;
WorkContext->CurrentWorkQueue->stats.BytesReceived += irp->IoStatus.Information;
if ( irp->Cancel ||
!NT_SUCCESS(irp->IoStatus.Status) ||
(GET_BLOCK_STATE(connection) != BlockStateActive) ||
SrvFspTransitioning ) {
IF_DEBUG(TRACE2) {
if ( irp->Cancel ) {
KdPrint(( " I/O canceled\n" ));
} else if ( !NT_SUCCESS(irp->IoStatus.Status) ) {
KdPrint(( " I/O failed: %X\n", irp->IoStatus.Status ));
} else if ( SrvFspTransitioning ) {
KdPrint(( " Server is shutting down.\n" ));
} else {
KdPrint(( " Connection no longer active\n" ));
}
}
//
// Abort the raw write.
//
AbortRawWrite( WorkContext );
IF_DEBUG(TRACE2) KdPrint(( "SrvRestartRawReceive complete\n" ));
return;
}
//
// Set up local variables.
//
rfcb = WorkContext->Rfcb;
lfcb = rfcb->Lfcb;
finalResponseBuffer = WorkContext->Parameters.WriteRaw.FinalResponseBuffer;
immediateLength = WorkContext->Parameters.WriteRaw.ImmediateLength;
//
// Determine whether we're doing "copy write" or "MDL write".
//
if ( WorkContext->Parameters.WriteRaw.MdlWrite ) {
#if DBG
IF_SMB_DEBUG(RAW2) {
KdPrint(( "SrvRestartRawReceive: Receive MDL chain after receive\n" ));
DumpMdlChain( irp->MdlAddress );
}
#endif
//
// This was an MDL write. If a partial MDL was built (because
// of immediate data), unmap it. Then complete the MDL write.
//
mdl = WorkContext->Parameters.WriteRaw.FirstMdl;
partialMdl = WorkContext->Irp->MdlAddress;
if ( partialMdl != mdl ) {
ASSERT( (partialMdl->MdlFlags & MDL_PARTIAL) != 0 );
MmPrepareMdlForReuse( partialMdl );
}
//
// Build the "complete MDL write" request. Note that
// irp->IoStatus.Information, which is where we're supposed to
// put the amount of data actually written, already contains the
// length of the received data.
//
irp->MdlAddress = mdl;
if ( !WorkContext->Parameters.WriteRaw.ImmediateWriteDone ) {
irp->IoStatus.Information += immediateLength;
}
#if DBG
IF_SMB_DEBUG(RAW2) {
KdPrint(( "SrvRestartRawReceive: Completing MDL write with length 0x%p\n",
(PVOID)irp->IoStatus.Information ));
DumpMdlChain( mdl );
}
#endif
if( lfcb->MdlWriteComplete == NULL ||
lfcb->MdlWriteComplete(
lfcb->FileObject,
&WorkContext->Parameters.WriteRaw.Offset,
mdl,
lfcb->DeviceObject ) == FALSE ) {
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
mdl,
IRP_MJ_WRITE,
&WorkContext->Parameters.WriteRaw.Offset,
WorkContext->Parameters.WriteRaw.Length
);
if( !NT_SUCCESS( status ) ) {
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
}
}
SrvFsdRestartWriteRaw( WorkContext );
return;
}
//
// We're doing a copy write. Get parameters for the request.
//
offset = WorkContext->Parameters.WriteRaw.Offset;
mdl = WorkContext->RequestBuffer->Mdl;
//
// Determine the amount of data to write. This is the actual amount
// of data sent by the client. (Note that this cannot be more than
// the client originally requested.)
//
writeAddress = (PCHAR)WorkContext->RequestBuffer->Buffer;
writeLength = (ULONG)irp->IoStatus.Information;
//
// If the immediate data was not written earlier, it immedidately
// precedes the data we just received. Adjust writeAddress and
// writeLength so that it too is written.
//
if ( !WorkContext->Parameters.WriteRaw.ImmediateWriteDone ) {
writeAddress -= immediateLength;
writeLength += immediateLength;
}
//
// Form the lock key using the FID and the PID.
//
// *** The FID must be included in the key in order to account for
// the folding of multiple remote compatibility mode opens into
// a single local open.
//
key = rfcb->ShiftedFid | WorkContext->Parameters.WriteRaw.Pid;
//
// Try the fast I/O path first.
//
if ( lfcb->FastIoWrite != NULL ) {
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesAttempted );
try {
if ( lfcb->FastIoWrite(
lfcb->FileObject,
&offset,
writeLength,
TRUE,
key,
writeAddress,
&WorkContext->Irp->IoStatus,
lfcb->DeviceObject
) ) {
//
// The fast I/O path worked. Call the restart routine directly.
//
SrvFsdRestartWriteRaw( WorkContext );
return;
}
}
except( EXCEPTION_EXECUTE_HANDLER ) {
// Fall through to the slow path on an exception
NTSTATUS status = GetExceptionCode();
IF_DEBUG(ERRORS) {
KdPrint(("FastIoRead threw exception %x\n", status ));
}
}
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesFailed );
}
//
// The fast I/O path failed, so we need to issue a real I/O request.
//
// Remap the MDL to describe only the received data, which may be
// less than was originally mapped.
//
// !!! Is this really necessary? Can't we just pass in the entire
// MDL, and count on the file system to know when to stop?
//
if ( writeLength != WorkContext->RequestBuffer->BufferLength ) {
MmInitializeMdl( mdl, writeAddress, writeLength );
MmBuildMdlForNonPagedPool( mdl );
}
if ( rfcb->ShareType != ShareTypePipe ) {
//
// Start the write request, reusing the receive IRP.
//
SrvBuildReadOrWriteRequest(
irp, // input IRP address
lfcb->FileObject, // target file object address
WorkContext, // context
IRP_MJ_WRITE, // major function code
0, // minor function code
writeAddress, // buffer address
writeLength, // buffer length
mdl, // MDL address
offset, // byte offset
key // lock key
);
} else {
//
// Build a write request for NPFS.
//
SrvBuildIoControlRequest(
WorkContext->Irp,
lfcb->FileObject,
WorkContext,
IRP_MJ_FILE_SYSTEM_CONTROL,
FSCTL_PIPE_INTERNAL_WRITE,
writeAddress,
writeLength,
NULL,
0,
NULL,
NULL
);
}
IF_SMB_DEBUG(RAW2) {
KdPrint(( "SrvRestartRawReceive: writing to file 0x%p, offset %ld, length %ld\n",
lfcb->FileObject, offset.LowPart, writeLength ));
}
//
// Pass the request to the file system.
//
WorkContext->FsdRestartRoutine = SrvFsdRestartWriteRaw;
DEBUG WorkContext->FspRestartRoutine = NULL;
(VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
//
// The write has been started. When it completes, processing
// continues at SrvFsdRestartWriteRaw.
//
return;
} // SrvRestartRawReceive
VOID SRVFASTCALL
SrvRestartReadRawComplete (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
This routine completes a read raw by starting any oplock breaks that
may have been deferred due to the read raw.
This routine is called in the FSP.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PAGED_CODE( );
//
// Start oplock break notifications, if any are outstanding.
// SrvSendDelayedOplockBreak also sets read raw in progress to FALSE.
//
SrvSendDelayedOplockBreak( WorkContext->Connection );
//
// Finish postprocessing of the SMB.
//
SrvDereferenceWorkItem( WorkContext );
return;
} // SrvRestartReadRawComplete
VOID SRVFASTCALL
SrvRestartWriteCompleteResponse (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
This is the restart routine that is invoked to clean up after a
Write Block Raw/Mpx completes, and the necessary clean up could not
be done in the FSD. See RestartWriteCompleteResponse in fsd.c.
This routine is called in the FSP.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PAGED_CODE( );
IF_DEBUG(WORKER1) KdPrint(( " - SrvRestartWriteCompleteResponse\n" ));
//
// Decrement the active raw write count for the RFCB, potentially
// allowing the RFCB to be closed. If WorkContext->Rfcb is NULL,
// then the count has been decremented in the fsd.
//
if ( WorkContext->Rfcb != NULL ) {
SrvDecrementRawWriteCount ( WorkContext->Rfcb );
}
//
// Dereference control blocks and the connection.
//
SrvReleaseContext( WorkContext );
SrvDereferenceConnection( WorkContext->Connection );
WorkContext->Connection = NULL;
WorkContext->Endpoint = NULL; // not a referenced pointer
//
// Put the work item back on the raw mode work item list.
//
SrvRequeueRawModeWorkItem( WorkContext );
IF_DEBUG(TRACE2) KdPrint(( "SrvRestartWriteCompleteResponse complete\n" ));
return;
} // SrvRestartWriteCompleteResponse
VOID SRVFASTCALL
SrvBuildAndSendWriteCompleteResponse (
IN OUT PWORK_CONTEXT WorkContext
)
/*++
Routine Description:
Sets up and sends a final response to a Write Block Raw/Mpx request.
This routine is called in the FSP. It is invoked as a restart routine
from the FSD when the status to be returned is not STATUS_SUCCESS.
Arguments:
WorkContext - Supplies a pointer to the work context block
describing server-specific context for the request.
Return Value:
None.
--*/
{
PAGED_CODE( );
SrvFsdBuildWriteCompleteResponse(
WorkContext,
WorkContext->Irp->IoStatus.Status,
(ULONG)WorkContext->Irp->IoStatus.Information
);
WorkContext->ResponseBuffer->DataLength =
(CLONG)( (PCHAR)WorkContext->ResponseParameters -
(PCHAR)WorkContext->ResponseHeader );
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
SRV_START_SEND_2(
WorkContext,
SrvFsdSendCompletionRoutine,
WorkContext->FsdRestartRoutine,
NULL
);
return;
} // SrvBuildAndSendWriteCompleteResponse