mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2445 lines
74 KiB
2445 lines
74 KiB
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
fsdsmb.c
|
|
|
|
Abstract:
|
|
|
|
This module implements SMB processing routines and their support
|
|
routines for the File System Driver of the LAN Manager server.
|
|
|
|
*** This module must be nonpageable.
|
|
|
|
Author:
|
|
|
|
Chuck Lenzmeier (chuckl) 19-Mar-1990
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
//
|
|
// This module is laid out as follows:
|
|
// Includes
|
|
// Local #defines
|
|
// Local type definitions
|
|
// Forward declarations of local functions
|
|
// SMB processing routines
|
|
// Restart routines and other support routines
|
|
//
|
|
|
|
#include "precomp.h"
|
|
#include "fsdsmb.tmh"
|
|
#pragma hdrstop
|
|
|
|
VOID SRVFASTCALL
|
|
SrvFspRestartLargeReadAndXComplete(
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
VOID SRVFASTCALL
|
|
RestartReadAndXCompressedSendComplete (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
);
|
|
|
|
#if SRVCATCH
|
|
VOID
|
|
SrvUpdateCatchBuffer (
|
|
IN PWORK_CONTEXT WorkContext,
|
|
IN OUT PBYTE Buffer,
|
|
IN DWORD BufferLength
|
|
);
|
|
#endif
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
//#pragma alloc_text( PAGE8FIL, SrvFsdRestartRead )
|
|
#pragma alloc_text( PAGE8FIL, SrvFsdRestartReadAndX )
|
|
#pragma alloc_text( PAGE8FIL, SrvFsdRestartReadAndXCompressed )
|
|
#pragma alloc_text( PAGE8FIL, SrvFsdRestartWrite )
|
|
#pragma alloc_text( PAGE8FIL, SrvFsdRestartWriteAndX )
|
|
#pragma alloc_text( PAGE, RestartReadAndXCompressedSendComplete )
|
|
#pragma alloc_text( PAGE, SrvFspRestartLargeReadAndXComplete )
|
|
|
|
#if DBG
|
|
#pragma alloc_text( PAGE, SrvValidateCompressedData )
|
|
#endif
|
|
|
|
#if SRVCATCH
|
|
#pragma alloc_text( PAGE, SrvUpdateCatchBuffer )
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
VOID SRVFASTCALL
|
|
SrvFsdRestartRead (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes file read completion for a Read SMB.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PREQ_READ request;
|
|
PRESP_READ response;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PRFCB rfcb;
|
|
SHARE_TYPE shareType;
|
|
KIRQL oldIrql;
|
|
USHORT readLength;
|
|
BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
|
|
|
|
UNLOCKABLE_CODE( 8FIL );
|
|
|
|
if (bNeedTrace) {
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_READ;
|
|
SrvWmiStartContext(WorkContext);
|
|
}
|
|
else
|
|
WorkContext->bAlreadyTrace = FALSE;
|
|
|
|
IF_DEBUG(FSD2) SrvPrint0( " - SrvFsdRestartRead\n" );
|
|
|
|
//
|
|
// Get the request and response parameter pointers.
|
|
//
|
|
|
|
request = (PREQ_READ)WorkContext->RequestParameters;
|
|
response = (PRESP_READ)WorkContext->ResponseParameters;
|
|
|
|
//
|
|
// Get the file pointer.
|
|
//
|
|
|
|
rfcb = WorkContext->Rfcb;
|
|
shareType = rfcb->ShareType;
|
|
IF_DEBUG(FSD2) {
|
|
SrvPrint2( " connection 0x%p, RFCB 0x%p\n",
|
|
WorkContext->Connection, rfcb );
|
|
}
|
|
|
|
//
|
|
// If the read failed, set an error status in the response header.
|
|
// (If we tried to read entirely beyond the end of file, we return a
|
|
// normal response indicating that nothing was read.)
|
|
//
|
|
|
|
status = WorkContext->Irp->IoStatus.Status;
|
|
readLength = (USHORT)WorkContext->Irp->IoStatus.Information;
|
|
|
|
if ( status == STATUS_BUFFER_OVERFLOW && shareType == ShareTypePipe ) {
|
|
|
|
//
|
|
// If this is an named pipe and the error is
|
|
// STATUS_BUFFER_OVERFLOW, set the error in the smb header, but
|
|
// return all the data to the client.
|
|
//
|
|
|
|
SrvSetBufferOverflowError( WorkContext );
|
|
|
|
} else if ( !NT_SUCCESS(status) ) {
|
|
|
|
if ( status != STATUS_END_OF_FILE ) {
|
|
|
|
IF_DEBUG(ERRORS) SrvPrint1( "Read failed: %X\n", status );
|
|
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
|
WorkContext->FspRestartRoutine = SrvFsdRestartRead;
|
|
QUEUE_WORK_TO_FSP( WorkContext );
|
|
} else {
|
|
SrvSetSmbError( WorkContext, status );
|
|
SrvFsdSendResponse( WorkContext );
|
|
}
|
|
IF_DEBUG(FSD2) SrvPrint0( "SrvFsdRestartRead complete\n" );
|
|
|
|
goto Cleanup;
|
|
|
|
} else {
|
|
readLength = 0;
|
|
}
|
|
}
|
|
|
|
#ifdef SLMDBG
|
|
{
|
|
PRFCB_TRACE entry;
|
|
PCHAR readAddress;
|
|
ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
|
|
rfcb->OperationCount++;
|
|
entry = &rfcb->Trace[rfcb->NextTrace];
|
|
if ( ++rfcb->NextTrace == SLMDBG_TRACE_COUNT ) {
|
|
rfcb->NextTrace = 0;
|
|
rfcb->TraceWrapped = TRUE;
|
|
}
|
|
RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
|
|
entry->Command = WorkContext->NextCommand;
|
|
KeQuerySystemTime( &entry->Time );
|
|
entry->Data.ReadWrite.Offset = SmbGetUlong( &request->Offset );
|
|
entry->Data.ReadWrite.Length = readLength;
|
|
readAddress = (PCHAR)response->Buffer;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// The read completed successfully. If this is a disk file, update
|
|
// the file position.
|
|
//
|
|
|
|
if (shareType == ShareTypeDisk) {
|
|
|
|
#if SRVCATCH
|
|
if( KeGetCurrentIrql() == 0 &&
|
|
rfcb->SrvCatch &&
|
|
SmbGetUlong( &request->Offset ) == 0 ) {
|
|
|
|
SrvUpdateCatchBuffer( WorkContext, (PCHAR)response->Buffer, readLength );
|
|
}
|
|
#endif
|
|
rfcb->CurrentPosition = SmbGetUlong( &request->Offset ) + readLength;
|
|
|
|
}
|
|
|
|
//
|
|
// Save the count of bytes read, to be used to update the server
|
|
// statistics database.
|
|
//
|
|
|
|
UPDATE_READ_STATS( WorkContext, readLength );
|
|
|
|
//
|
|
// Build the response message.
|
|
//
|
|
|
|
response->WordCount = 5;
|
|
SmbPutUshort( &response->Count, readLength );
|
|
RtlZeroMemory( (PVOID)&response->Reserved[0], sizeof(response->Reserved) );
|
|
SmbPutUshort(
|
|
&response->ByteCount,
|
|
(USHORT)(readLength + FIELD_OFFSET(RESP_READ,Buffer[0]) -
|
|
FIELD_OFFSET(RESP_READ,BufferFormat))
|
|
);
|
|
response->BufferFormat = SMB_FORMAT_DATA;
|
|
SmbPutUshort( &response->DataLength, readLength );
|
|
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_READ,
|
|
readLength
|
|
);
|
|
|
|
//
|
|
// Processing of the SMB is complete. Send the response.
|
|
//
|
|
|
|
SrvFsdSendResponse( WorkContext );
|
|
|
|
Cleanup:
|
|
IF_DEBUG(FSD2) SrvPrint0( "SrvFsdRestartRead complete\n" );
|
|
if (bNeedTrace) {
|
|
SrvWmiEndContext(WorkContext);
|
|
}
|
|
|
|
return;
|
|
|
|
} // SrvFsdRestartRead
|
|
|
|
VOID SRVFASTCALL
|
|
RestartReadAndXCompressedSendComplete (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes the TDI transport send complete for a compressed ReadAndX SMB
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
--*/
|
|
{
|
|
PRFCB rfcb = WorkContext->Rfcb;
|
|
PLFCB lfcb = WorkContext->Rfcb->Lfcb;
|
|
NTSTATUS status;
|
|
PMDL mdl = WorkContext->ResponseBuffer->Mdl->Next;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// If we have a cache mdl, give it back to the cache manager
|
|
//
|
|
|
|
if( mdl != NULL ) {
|
|
WorkContext->ResponseBuffer->Mdl->Next = NULL;
|
|
}
|
|
|
|
if( lfcb->MdlReadCompleteCompressed == NULL ||
|
|
!lfcb->MdlReadCompleteCompressed( lfcb->FileObject,
|
|
mdl,
|
|
lfcb->DeviceObject) ) {
|
|
|
|
status = SrvIssueMdlCompleteRequest( WorkContext, NULL, mdl, IRP_MJ_READ,
|
|
&WorkContext->Parameters.ReadAndXCompressed.ReadOffset,
|
|
WorkContext->Parameters.ReadAndXCompressed.ReadLength
|
|
);
|
|
|
|
if( !NT_SUCCESS( status ) ) {
|
|
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the work item
|
|
//
|
|
SrvDereferenceWorkItem( WorkContext );
|
|
}
|
|
|
|
#if DBG
|
|
VOID
|
|
SrvValidateCompressedData(
|
|
PWORK_CONTEXT WorkContext,
|
|
PMDL CompressedDataMdl,
|
|
PCOMPRESSED_DATA_INFO Cdi
|
|
)
|
|
|
|
/*++
|
|
|
|
Make sure the compressed buffer that we just got from the filesystem is correct
|
|
We do this by decompressing it!
|
|
|
|
--*/
|
|
{
|
|
ULONG i, compressedDataLength;
|
|
PMDL mdl;
|
|
NTSTATUS status;
|
|
PBYTE compressedBuffer;
|
|
PBYTE uncompressedBuffer;
|
|
ULONG count;
|
|
PBYTE pin;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// We need to copy the compressed data into a flat buffer
|
|
//
|
|
for ( i = compressedDataLength = 0; i < Cdi->NumberOfChunks; i++ ) {
|
|
compressedDataLength += Cdi->CompressedChunkSizes[i];
|
|
}
|
|
|
|
if( compressedDataLength && CompressedDataMdl == NULL ) {
|
|
DbgPrint( "SrvValidateCompressedData(wc %X, cdi %X, mdl %X ) -- no MDL!",
|
|
WorkContext, Cdi, WorkContext->Irp->MdlAddress );
|
|
// DbgBreakPoint();
|
|
return;
|
|
}
|
|
|
|
if( compressedDataLength > SrvMaxCompressedDataLength ) {
|
|
DbgPrint( "SrvValidateCompressedData(wc %X, cdi %X) compresseddatalength %u\n",
|
|
WorkContext, Cdi, compressedDataLength );
|
|
DbgBreakPoint();
|
|
return;
|
|
}
|
|
|
|
pin = compressedBuffer =
|
|
ALLOCATE_NONPAGED_POOL( compressedDataLength, BlockTypeDataBuffer );
|
|
if( compressedBuffer == NULL ) {
|
|
return;
|
|
}
|
|
|
|
for( mdl = CompressedDataMdl; mdl != NULL; mdl = mdl->Next ) {
|
|
i = MmGetMdlByteCount( mdl );
|
|
|
|
RtlCopyMemory( pin, MmGetSystemAddressForMdl( mdl ), i );
|
|
pin += i;
|
|
}
|
|
//
|
|
// The largest output chunk is SrvMaxCompressedDataLength
|
|
//
|
|
uncompressedBuffer = ALLOCATE_HEAP_COLD( SrvMaxCompressedDataLength, BlockTypeDataBuffer );
|
|
if( uncompressedBuffer == NULL ) {
|
|
DEALLOCATE_NONPAGED_POOL( compressedBuffer );
|
|
return;
|
|
}
|
|
|
|
status = RtlDecompressChunks( uncompressedBuffer, SrvMaxCompressedDataLength,
|
|
compressedBuffer, compressedDataLength,
|
|
NULL, 0, Cdi );
|
|
|
|
if( !NT_SUCCESS( status ) ) {
|
|
DbgPrint( "\nSrvValidateCompressedData - status %X\n", status );
|
|
DbgPrint( " WC %X, CDI %X, Irp %X, Mdl %X, InBuffer %X\n",
|
|
WorkContext, Cdi, WorkContext->Irp,
|
|
CompressedDataMdl, uncompressedBuffer
|
|
);
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
DEALLOCATE_NONPAGED_POOL( compressedBuffer );
|
|
FREE_HEAP( uncompressedBuffer );
|
|
}
|
|
#endif
|
|
|
|
VOID SRVFASTCALL
|
|
SrvFsdRestartReadAndXCompressed (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes file read completion for a compressed ReadAndX SMB
|
|
|
|
This routine may be called in the FSD or the FSP. There can be
|
|
no chained command.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRFCB rfcb = WorkContext->Rfcb;
|
|
PIRP irp = WorkContext->Irp;
|
|
NTSTATUS status = irp->IoStatus.Status;
|
|
ULONG readLength = (ULONG)irp->IoStatus.Information; // size of uncompressed data
|
|
ULONG dataLength; // size of compressed data
|
|
ULONG cdiLength; // size of compressedDataInfo
|
|
USHORT flags2;
|
|
PCOMPRESSED_DATA_INFO compressedDataInfo;
|
|
PRESP_READ_ANDX response;
|
|
ULONG i;
|
|
BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
|
|
|
|
UNLOCKABLE_CODE( 8FIL );
|
|
if (bNeedTrace) {
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_READ_AND_X;
|
|
SrvWmiStartContext(WorkContext);
|
|
}
|
|
else
|
|
WorkContext->bAlreadyTrace = FALSE;
|
|
|
|
IF_DEBUG( COMPRESSION ) {
|
|
KdPrint(( "SRV: SrvFsdRestartReadAndXCompressed status %X, uncompressed len %u, mdl %p\n",
|
|
status, readLength, irp->MdlAddress ));
|
|
}
|
|
|
|
if( NT_SUCCESS( status ) && readLength != 0 ) {
|
|
//
|
|
// Scan the Compression Info structure tallying the sizes of
|
|
// each chunk.
|
|
//
|
|
//
|
|
compressedDataInfo = WorkContext->Parameters.ReadAndXCompressed.Aux.Buffer;
|
|
|
|
#if DBG
|
|
SrvValidateCompressedData( WorkContext, WorkContext->Irp->MdlAddress,compressedDataInfo );
|
|
#endif
|
|
|
|
cdiLength = FIELD_OFFSET( COMPRESSED_DATA_INFO, CompressedChunkSizes ) +
|
|
(sizeof(ULONG) * compressedDataInfo->NumberOfChunks );
|
|
|
|
for ( i = dataLength = 0;
|
|
i < compressedDataInfo->NumberOfChunks;
|
|
i++ ) {
|
|
|
|
dataLength += compressedDataInfo->CompressedChunkSizes[i];
|
|
}
|
|
|
|
} else if( status == STATUS_END_OF_FILE || readLength == 0 ) {
|
|
|
|
readLength = dataLength = cdiLength = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Make sure we are at passive level
|
|
//
|
|
if( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
|
WorkContext->FspRestartRoutine = SrvFsdRestartReadAndXCompressed;
|
|
SrvQueueWorkToFsp( WorkContext );
|
|
goto Cleanup;
|
|
}
|
|
|
|
irp->Tail.Overlay.AuxiliaryBuffer = NULL;
|
|
|
|
IF_DEBUG( ERRORS ) {
|
|
KdPrint(("SRV: SrvFsdRestartReadAndXCompressed status %X\n", status ));
|
|
}
|
|
|
|
if( status != STATUS_INVALID_READ_MODE &&
|
|
status != STATUS_UNSUPPORTED_COMPRESSION &&
|
|
status != STATUS_USER_MAPPED_FILE &&
|
|
status != STATUS_NOT_SUPPORTED &&
|
|
status != STATUS_BAD_COMPRESSION_BUFFER ) {
|
|
|
|
//
|
|
// Set the error and get out!
|
|
//
|
|
SrvSetSmbError( WorkContext, status );
|
|
SrvFsdSendResponse( WorkContext );
|
|
goto Cleanup;
|
|
}
|
|
|
|
IF_DEBUG( COMPRESSION ) {
|
|
KdPrint(( " %X: rerouting to SrvSmbReadAndX\n", status ));
|
|
}
|
|
|
|
//
|
|
// No more compressed reads for this file!
|
|
//
|
|
rfcb->Mfcb->NonpagedMfcb->OpenFileAttributes &= ~FILE_ATTRIBUTE_COMPRESSED;
|
|
|
|
//
|
|
// We now need to turn around and do a regular non-compressed read. We
|
|
// can just reroute this back to the original Read&X processor because we
|
|
// know that we have not changed the original request received from the client.
|
|
// Also, we have turned off both the FILE_ATTRIBUTE_COMPRESSED flag in the
|
|
// Mfcb, as well as the SMB_FLAGS2_COMPRESSED bit in the header. Therefore
|
|
// we will not loop!
|
|
//
|
|
// We also know that SrvSmbReadAndX will return SmbStatusInProgress, because
|
|
// all of the error checks have previously passed!
|
|
//
|
|
// This is not a terribly efficient way to handle this error, but it should be
|
|
// extremely rare.
|
|
//
|
|
SrvStatistics.CompressedReadsFailed++;
|
|
|
|
(VOID)SrvSmbReadAndX( WorkContext );
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
irp->Tail.Overlay.AuxiliaryBuffer = NULL;
|
|
|
|
//
|
|
// Update the file position
|
|
//
|
|
WorkContext->Rfcb->CurrentPosition =
|
|
WorkContext->Parameters.ReadAndXCompressed.ReadOffset.LowPart + readLength;
|
|
|
|
//
|
|
// Build and send the response
|
|
//
|
|
response = (PRESP_READ_ANDX)WorkContext->ResponseParameters;
|
|
|
|
SmbPutUshort( &response->Remaining, (USHORT)-1 );
|
|
response->WordCount = 12;
|
|
response->AndXCommand = SMB_COM_NO_ANDX_COMMAND;
|
|
response->AndXReserved = 0;
|
|
SmbPutUshort( &response->AndXOffset, 0 );
|
|
SmbPutUshort( &response->DataCompactionMode, 0 );
|
|
SmbPutUshort( &response->CdiLength, (USHORT)cdiLength );
|
|
SmbPutUshort( &response->DataLength, (USHORT)readLength );
|
|
SmbPutUshort( &response->DataLengthHigh, (USHORT)(readLength >> 16) );
|
|
SmbPutUshort( &response->DataOffset, (USHORT)READX_BUFFER_OFFSET );
|
|
RtlZeroMemory( (PVOID)&response->Reserved3[0], sizeof(response->Reserved3) );
|
|
SmbPutUshort( &response->ByteCount, (USHORT)(cdiLength + dataLength) );
|
|
|
|
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
|
|
|
|
//
|
|
// If we got some data, indicate to the client that we are returning
|
|
// a compressed response
|
|
//
|
|
if( cdiLength ) {
|
|
//
|
|
// Copy the COMPRESSED_DATA_INFO structure to the Data[] portion of the
|
|
// response.
|
|
//
|
|
RtlCopyMemory( response->Buffer, compressedDataInfo, cdiLength );
|
|
|
|
flags2 = SmbGetAlignedUshort( &WorkContext->ResponseHeader->Flags2 );
|
|
flags2 |= SMB_FLAGS2_COMPRESSED;
|
|
|
|
SmbPutAlignedUshort( &WorkContext->ResponseHeader->Flags2, flags2 );
|
|
|
|
WorkContext->ResponseBuffer->Mdl->Next = irp->MdlAddress;
|
|
}
|
|
|
|
WorkContext->ResponseBuffer->Mdl->ByteCount = READX_BUFFER_OFFSET + cdiLength;
|
|
|
|
WorkContext->ResponseBuffer->DataLength =
|
|
READX_BUFFER_OFFSET + cdiLength + dataLength;
|
|
|
|
WorkContext->FspRestartRoutine = RestartReadAndXCompressedSendComplete;
|
|
|
|
irp->Cancel = FALSE;
|
|
|
|
SrvStartSend2( WorkContext, SrvQueueWorkToFspAtSendCompletion );
|
|
|
|
Cleanup:
|
|
if (bNeedTrace) {
|
|
SrvWmiEndContext(WorkContext);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
VOID SRVFASTCALL
|
|
SrvFsdRestartReadAndX (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes file read completion for a ReadAndX SMB.
|
|
|
|
This routine may be called in the FSD or the FSP. If the chained
|
|
command is Close, it will be called in the FSP.
|
|
|
|
*** This routine cannot look at the original ReadAndX request!
|
|
This is because the read data may have overlaid the request.
|
|
All necessary information from the request must be stored
|
|
in WorkContext->Parameters.ReadAndX.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRESP_READ_ANDX response;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PRFCB rfcb;
|
|
SHARE_TYPE shareType;
|
|
KIRQL oldIrql;
|
|
PCHAR readAddress;
|
|
CLONG bufferOffset;
|
|
ULONG readLength;
|
|
BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
|
|
|
|
UNLOCKABLE_CODE( 8FIL );
|
|
if (bNeedTrace) {
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_READ_AND_X;
|
|
SrvWmiStartContext(WorkContext);
|
|
}
|
|
else
|
|
WorkContext->bAlreadyTrace = FALSE;
|
|
|
|
IF_DEBUG(FSD2) SrvPrint0( " - SrvFsdRestartReadAndX\n" );
|
|
|
|
//
|
|
// Get the response parameter pointer.
|
|
//
|
|
|
|
response = (PRESP_READ_ANDX)WorkContext->ResponseParameters;
|
|
|
|
//
|
|
// Get the file pointer.
|
|
//
|
|
|
|
rfcb = WorkContext->Rfcb;
|
|
shareType = rfcb->ShareType;
|
|
IF_DEBUG(FSD2) {
|
|
SrvPrint2( " connection 0x%p, RFCB 0x%p\n",
|
|
WorkContext->Connection, rfcb );
|
|
}
|
|
|
|
//
|
|
// If the read failed, set an error status in the response header.
|
|
// (If we tried to read entirely beyond the end of file, we return a
|
|
// normal response indicating that nothing was read.)
|
|
//
|
|
|
|
status = WorkContext->Irp->IoStatus.Status;
|
|
readLength = (ULONG)WorkContext->Irp->IoStatus.Information;
|
|
|
|
if ( status == STATUS_BUFFER_OVERFLOW && shareType == ShareTypePipe ) {
|
|
|
|
//
|
|
// If this is an named pipe and the error is
|
|
// STATUS_BUFFER_OVERFLOW, set the error in the smb header, but
|
|
// return all the data to the client.
|
|
//
|
|
|
|
SrvSetBufferOverflowError( WorkContext );
|
|
|
|
} else if ( !NT_SUCCESS(status) ) {
|
|
|
|
if ( status != STATUS_END_OF_FILE ) {
|
|
|
|
IF_DEBUG(ERRORS) SrvPrint1( "Read failed: %X\n", status );
|
|
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
|
WorkContext->FspRestartRoutine = SrvFsdRestartReadAndX;
|
|
QUEUE_WORK_TO_FSP( WorkContext );
|
|
} else {
|
|
SrvSetSmbError( WorkContext, status );
|
|
SrvFsdSendResponse( WorkContext );
|
|
}
|
|
IF_DEBUG(FSD2) SrvPrint0("SrvFsdRestartReadAndX complete\n");
|
|
goto Cleanup;
|
|
} else {
|
|
readLength = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The read completed successfully. Generate information about the
|
|
// destination of the read data. Find out how much was actually
|
|
// read. If none was read, we don't have to worry about the offset.
|
|
//
|
|
|
|
if ( readLength != 0 ) {
|
|
|
|
readAddress = WorkContext->Parameters.ReadAndX.ReadAddress;
|
|
bufferOffset = (ULONG)(readAddress - (PCHAR)WorkContext->ResponseHeader);
|
|
|
|
//
|
|
// Save the count of bytes read, to be used to update the server
|
|
// statistics database.
|
|
//
|
|
|
|
UPDATE_READ_STATS( WorkContext, readLength );
|
|
|
|
} else {
|
|
|
|
readAddress = (PCHAR)response->Buffer;
|
|
bufferOffset = 0;
|
|
|
|
}
|
|
|
|
#ifdef SLMDBG
|
|
{
|
|
PRFCB_TRACE entry;
|
|
ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
|
|
rfcb->OperationCount++;
|
|
entry = &rfcb->Trace[rfcb->NextTrace];
|
|
if ( ++rfcb->NextTrace == SLMDBG_TRACE_COUNT ) {
|
|
rfcb->NextTrace = 0;
|
|
rfcb->TraceWrapped = TRUE;
|
|
}
|
|
RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
|
|
entry->Command = WorkContext->NextCommand;
|
|
KeQuerySystemTime( &entry->Time );
|
|
entry->Data.ReadWrite.Offset =
|
|
WorkContext->Parameters.ReadAndX.ReadOffset.LowPart;
|
|
ASSERT (WorkContext->Parameters.ReadAndX.ReadOffset.HighPart == 0);
|
|
entry->Data.ReadWrite.Length = readLength;
|
|
}
|
|
#endif
|
|
|
|
if (shareType == ShareTypePipe) {
|
|
|
|
//
|
|
// If this is NPFS then, Irp->Overlay.AllocationSize actually
|
|
// contains the number bytes left to read on this side of the named
|
|
// pipe. Return this information to the client.
|
|
//
|
|
|
|
if (WorkContext->Irp->Overlay.AllocationSize.LowPart != 0) {
|
|
SmbPutUshort(
|
|
&response->Remaining,
|
|
(USHORT)(WorkContext->Irp->Overlay.AllocationSize.LowPart - readLength)
|
|
);
|
|
} else {
|
|
SmbPutUshort(
|
|
&response->Remaining,
|
|
0
|
|
);
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( shareType == ShareTypeDisk ) {
|
|
|
|
#if SRVCATCH
|
|
if( KeGetCurrentIrql() == 0 &&
|
|
rfcb->SrvCatch &&
|
|
WorkContext->Parameters.ReadAndX.ReadOffset.QuadPart == 0 ) {
|
|
|
|
SrvUpdateCatchBuffer( WorkContext, readAddress, readLength );
|
|
}
|
|
#endif
|
|
//
|
|
// If this is a disk file, then update the file position.
|
|
//
|
|
|
|
rfcb->CurrentPosition =
|
|
WorkContext->Parameters.ReadAndX.ReadOffset.LowPart +
|
|
readLength;
|
|
}
|
|
|
|
SmbPutUshort( &response->Remaining, (USHORT)-1 );
|
|
}
|
|
|
|
//
|
|
// Build the response message. (Note that if no data was read, we
|
|
// return a byte count of 0 -- we don't add padding.)
|
|
//
|
|
// *** Note that even though there may have been a chained command,
|
|
// we make this the last response in the chain. This is what
|
|
// the OS/2 server does. (Sort of -- it doesn't bother to
|
|
// update the AndX fields of the response.) Since the only legal
|
|
// chained commands are Close and CloseAndTreeDisc, this seems
|
|
// like a reasonable thing to do. It does make life easier --
|
|
// we don't have to find the end of the read data and write
|
|
// another response there. Besides, the read data might have
|
|
// completely filled the SMB buffer.
|
|
//
|
|
|
|
response->WordCount = 12;
|
|
response->AndXCommand = SMB_COM_NO_ANDX_COMMAND;
|
|
response->AndXReserved = 0;
|
|
SmbPutUshort( &response->AndXOffset, 0 );
|
|
SmbPutUshort( &response->DataCompactionMode, 0 );
|
|
SmbPutUshort( &response->Reserved, 0 );
|
|
SmbPutUshort( &response->DataLength, (USHORT)readLength );
|
|
SmbPutUshort( &response->DataOffset, (USHORT)bufferOffset );
|
|
SmbPutUshort( &response->DataLengthHigh, (USHORT)(readLength >> 16) );
|
|
RtlZeroMemory( (PVOID)&response->Reserved3[0], sizeof(response->Reserved3) );
|
|
SmbPutUshort(
|
|
&response->ByteCount,
|
|
(USHORT)(readLength + (readAddress - response->Buffer))
|
|
);
|
|
|
|
WorkContext->ResponseParameters = NEXT_LOCATION(
|
|
response,
|
|
RESP_READ_ANDX,
|
|
readLength +
|
|
(readAddress - response->Buffer)
|
|
);
|
|
|
|
//
|
|
// Processing of the SMB is complete, except that the file may still
|
|
// need to be closed. If not, just send the response. If this is a
|
|
// ReadAndX and Close, we need to close the file first.
|
|
//
|
|
// *** Note that other chained commands are illegal, but are ignored
|
|
// -- no error is returned.
|
|
//
|
|
|
|
if ( WorkContext->NextCommand != SMB_COM_CLOSE ) {
|
|
|
|
//
|
|
// Not a chained Close. Just send the response.
|
|
//
|
|
|
|
SrvFsdSendResponse( WorkContext );
|
|
|
|
} else {
|
|
|
|
ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
|
|
|
|
//
|
|
// Remember the file last write time, to correctly set this on
|
|
// close.
|
|
//
|
|
|
|
WorkContext->Parameters.LastWriteTime =
|
|
WorkContext->Parameters.ReadAndX.LastWriteTimeInSeconds;
|
|
|
|
//
|
|
// This is a ReadAndX and Close. Call SrvRestartChainedClose to
|
|
// do the close and send the response.
|
|
//
|
|
|
|
SrvRestartChainedClose( WorkContext );
|
|
|
|
}
|
|
IF_DEBUG(FSD2) SrvPrint0( "SrvFsdRestartReadAndX complete\n" );
|
|
|
|
Cleanup:
|
|
if (bNeedTrace) {
|
|
SrvWmiEndContext(WorkContext);
|
|
}
|
|
return;
|
|
|
|
} // SrvFsdRestartReadAndX
|
|
|
|
/*
|
|
* This routine is called at final send completion
|
|
*/
|
|
VOID SRVFASTCALL
|
|
SrvFspRestartLargeReadAndXComplete(
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
if( WorkContext->Parameters.ReadAndX.SavedMdl != NULL ) {
|
|
|
|
WorkContext->ResponseBuffer->Mdl = WorkContext->Parameters.ReadAndX.SavedMdl;
|
|
|
|
MmPrepareMdlForReuse( WorkContext->ResponseBuffer->PartialMdl );
|
|
WorkContext->ResponseBuffer->PartialMdl->Next = NULL;
|
|
|
|
}
|
|
|
|
if ( WorkContext->Parameters.ReadAndX.MdlRead == TRUE ) {
|
|
|
|
//
|
|
// Call the Cache Manager to release the MDL chain.
|
|
//
|
|
if( WorkContext->Parameters.ReadAndX.CacheMdl ) {
|
|
//
|
|
// Try the fast path first..
|
|
//
|
|
if( WorkContext->Rfcb->Lfcb->MdlReadComplete == NULL ||
|
|
|
|
WorkContext->Rfcb->Lfcb->MdlReadComplete(
|
|
WorkContext->Rfcb->Lfcb->FileObject,
|
|
WorkContext->Parameters.ReadAndX.CacheMdl,
|
|
WorkContext->Rfcb->Lfcb->DeviceObject ) == FALSE ) {
|
|
|
|
//
|
|
// Fast path didn't work, try an IRP...
|
|
//
|
|
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
|
|
WorkContext->Parameters.ReadAndX.CacheMdl,
|
|
IRP_MJ_READ,
|
|
&WorkContext->Parameters.ReadAndX.ReadOffset,
|
|
WorkContext->Parameters.ReadAndX.ReadLength
|
|
);
|
|
|
|
if( !NT_SUCCESS( status ) ) {
|
|
//
|
|
// At this point, all we can do is complain!
|
|
//
|
|
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
|
|
}
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
PMDL mdl = (PMDL)(((ULONG_PTR)(WorkContext->Parameters.ReadAndX.ReadAddress) + sizeof(PVOID) - 1) & ~(sizeof(PVOID)-1));
|
|
|
|
//
|
|
// We shortened the byte count if the read returned less data than we asked for
|
|
//
|
|
mdl->ByteCount = WorkContext->Parameters.ReadAndX.ReadLength;
|
|
|
|
MmUnlockPages( mdl );
|
|
MmPrepareMdlForReuse( mdl );
|
|
|
|
FREE_HEAP( WorkContext->Parameters.ReadAndX.Buffer );
|
|
}
|
|
|
|
SrvDereferenceWorkItem( WorkContext );
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This routine is called when the read completes
|
|
*/
|
|
VOID SRVFASTCALL
|
|
SrvFsdRestartLargeReadAndX (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes file read completion for a ReadAndX SMB which
|
|
is larger than the negotiated buffer size, and is from
|
|
a disk file.
|
|
|
|
There is no follow on command.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRESP_READ_ANDX response = (PRESP_READ_ANDX)WorkContext->ResponseParameters;
|
|
|
|
USHORT readLength;
|
|
NTSTATUS status = WorkContext->Irp->IoStatus.Status;
|
|
PRFCB rfcb = WorkContext->Rfcb;
|
|
PIRP irp = WorkContext->Irp;
|
|
BOOLEAN mdlRead = WorkContext->Parameters.ReadAndX.MdlRead;
|
|
BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
|
|
|
|
#ifdef SRVCATCH
|
|
// For the Catch case, make sure we're PASSIVE
|
|
if( (KeGetCurrentIrql() != PASSIVE_LEVEL) && (WorkContext->Rfcb->SrvCatch != 0) ) {
|
|
//
|
|
// Requeue this routine to come back around at passive level.
|
|
// (inefficient, but should be very rare)
|
|
//
|
|
WorkContext->FspRestartRoutine = SrvFsdRestartLargeReadAndX;
|
|
SrvQueueWorkToFspAtDpcLevel( WorkContext );
|
|
goto Cleanup;
|
|
}
|
|
#endif
|
|
|
|
UNLOCKABLE_CODE( 8FIL );
|
|
if (bNeedTrace) {
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_READ_AND_X;
|
|
SrvWmiStartContext(WorkContext);
|
|
}
|
|
else
|
|
WorkContext->bAlreadyTrace = FALSE;
|
|
|
|
// Copy the MDL pointer back out of the IRP. This is because if the read
|
|
// failed, CC will free the MDL and NULL the pointer. Not retrieving it in the
|
|
// non-fast-io path will result in us holding (and possibly freeing) a dangling pointer
|
|
if( mdlRead )
|
|
{
|
|
WorkContext->Parameters.ReadAndX.CacheMdl = WorkContext->Irp->MdlAddress;
|
|
}
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
if( status != STATUS_END_OF_FILE ) {
|
|
IF_DEBUG(ERRORS) SrvPrint1( "Read failed: %X\n", status );
|
|
//
|
|
// We cannot call SrvSetSmbError() at elevated IRQL.
|
|
//
|
|
if( KeGetCurrentIrql() != 0 ) {
|
|
//
|
|
// Requeue this routine to come back around at passive level.
|
|
// (inefficient, but should be very rare)
|
|
//
|
|
WorkContext->FspRestartRoutine = SrvFsdRestartLargeReadAndX;
|
|
SrvQueueWorkToFspAtDpcLevel( WorkContext );
|
|
goto Cleanup;
|
|
}
|
|
SrvSetSmbError( WorkContext, status );
|
|
}
|
|
|
|
readLength = 0;
|
|
|
|
} else if( 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 steps, 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
|
|
//
|
|
|
|
PMDL mdl = WorkContext->Irp->MdlAddress;
|
|
readLength = 0;
|
|
|
|
while( mdl != NULL ) {
|
|
readLength += (USHORT)MmGetMdlByteCount( mdl );
|
|
mdl = mdl->Next;
|
|
}
|
|
} else {
|
|
//
|
|
// This was a copy read. The I/O status block has the length.
|
|
//
|
|
readLength = (USHORT)WorkContext->Irp->IoStatus.Information;
|
|
}
|
|
|
|
//
|
|
// Build the response message. (Note that if no data was read, we
|
|
// return a byte count of 0 -- we don't add padding.)
|
|
//
|
|
SmbPutUshort( &response->Remaining, (USHORT)-1 );
|
|
response->WordCount = 12;
|
|
response->AndXCommand = SMB_COM_NO_ANDX_COMMAND;
|
|
response->AndXReserved = 0;
|
|
SmbPutUshort( &response->AndXOffset, 0 );
|
|
SmbPutUshort( &response->DataCompactionMode, 0 );
|
|
SmbPutUshort( &response->Reserved, 0 );
|
|
SmbPutUshort( &response->Reserved2, 0 );
|
|
RtlZeroMemory( (PVOID)&response->Reserved3[0], sizeof(response->Reserved3) );
|
|
SmbPutUshort( &response->DataLength, readLength );
|
|
|
|
|
|
if( readLength == 0 ) {
|
|
|
|
SmbPutUshort( &response->DataOffset, 0 );
|
|
SmbPutUshort( &response->ByteCount, 0 );
|
|
WorkContext->Parameters.ReadAndX.PadCount = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Update the file position.
|
|
//
|
|
rfcb->CurrentPosition =
|
|
WorkContext->Parameters.ReadAndX.ReadOffset.LowPart +
|
|
readLength;
|
|
|
|
//
|
|
// Update statistics
|
|
//
|
|
UPDATE_READ_STATS( WorkContext, readLength );
|
|
|
|
SmbPutUshort( &response->DataOffset,
|
|
(USHORT)(READX_BUFFER_OFFSET + WorkContext->Parameters.ReadAndX.PadCount) );
|
|
|
|
SmbPutUshort( &response->ByteCount,
|
|
(USHORT)( readLength + WorkContext->Parameters.ReadAndX.PadCount ) );
|
|
|
|
}
|
|
|
|
//
|
|
// We will use two MDLs to describe the packet we're sending -- one
|
|
// for the header and parameters, the other for the data.
|
|
//
|
|
// Handling of the second MDL varies depending on whether we did a copy
|
|
// read or an MDL read.
|
|
//
|
|
|
|
//
|
|
// Set the first MDL for just the header + pad
|
|
//
|
|
IoBuildPartialMdl(
|
|
WorkContext->ResponseBuffer->Mdl,
|
|
WorkContext->ResponseBuffer->PartialMdl,
|
|
WorkContext->ResponseBuffer->Buffer,
|
|
READX_BUFFER_OFFSET + WorkContext->Parameters.ReadAndX.PadCount
|
|
);
|
|
|
|
WorkContext->ResponseBuffer->PartialMdl->MdlFlags |=
|
|
(WorkContext->ResponseBuffer->Mdl->MdlFlags & MDL_NETWORK_HEADER); // prop flag
|
|
|
|
//
|
|
// Set the overall data length to the header + pad + data
|
|
//
|
|
WorkContext->ResponseBuffer->DataLength = READX_BUFFER_OFFSET +
|
|
WorkContext->Parameters.ReadAndX.PadCount +
|
|
readLength;
|
|
|
|
irp->Cancel = FALSE;
|
|
|
|
//
|
|
// The second MDL depends on the kind of read which we did
|
|
//
|
|
if( readLength != 0 ) {
|
|
|
|
if( mdlRead ) {
|
|
|
|
WorkContext->ResponseBuffer->PartialMdl->Next =
|
|
WorkContext->Irp->MdlAddress;
|
|
|
|
} else {
|
|
|
|
//
|
|
// This was a copy read. The MDL describing the data buffer is in the SMB buffer
|
|
//
|
|
|
|
PMDL mdl = (PMDL)(((ULONG_PTR)(WorkContext->Parameters.ReadAndX.ReadAddress) + sizeof(PVOID) - 1) & ~(sizeof(PVOID)-1));
|
|
|
|
WorkContext->ResponseBuffer->PartialMdl->Next = mdl;
|
|
mdl->ByteCount = readLength;
|
|
|
|
}
|
|
|
|
#ifdef SRVCATCH
|
|
if( rfcb->SrvCatch && WorkContext->ResponseBuffer->PartialMdl->Next && (WorkContext->Parameters.ReadAndX.ReadOffset.QuadPart == 0) )
|
|
{
|
|
PVOID Buffer;
|
|
|
|
Buffer = MmGetSystemAddressForMdlSafe( WorkContext->ResponseBuffer->PartialMdl->Next, LowPagePriority );
|
|
if( Buffer )
|
|
{
|
|
SrvUpdateCatchBuffer( WorkContext, Buffer, WorkContext->ResponseBuffer->PartialMdl->Next->ByteCount );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
//
|
|
// SrvStartSend2 wants to use WorkContext->ResponseBuffer->Mdl, but
|
|
// we want it to use WorkContext->ResponseBuffer->PartialMdl. So switch
|
|
// it!
|
|
//
|
|
WorkContext->Parameters.ReadAndX.SavedMdl = WorkContext->ResponseBuffer->Mdl;
|
|
WorkContext->ResponseBuffer->Mdl = WorkContext->ResponseBuffer->PartialMdl;
|
|
|
|
//
|
|
// Send the response!
|
|
//
|
|
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
|
|
WorkContext->FspRestartRoutine = SrvFspRestartLargeReadAndXComplete;
|
|
SrvStartSend2( WorkContext, SrvQueueWorkToFspAtSendCompletion );
|
|
|
|
Cleanup:
|
|
if (bNeedTrace) {
|
|
SrvWmiEndContext(WorkContext);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
VOID SRVFASTCALL
|
|
SrvFsdRestartWrite (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes file write completion for a Write SMB.
|
|
|
|
This routine is called in the FSP for a write and close SMB so that
|
|
it can free the pageable MFCB and for a write and unlock SMB so that
|
|
it can do the unlock; for other SMBs, it is called in the FSD.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PREQ_WRITE request;
|
|
PRESP_WRITE response;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PRFCB rfcb;
|
|
KIRQL oldIrql;
|
|
USHORT writeLength;
|
|
BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
|
|
|
|
UNLOCKABLE_CODE( 8FIL );
|
|
if (bNeedTrace) {
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_WRITE;
|
|
SrvWmiStartContext(WorkContext);
|
|
}
|
|
else
|
|
WorkContext->bAlreadyTrace = FALSE;
|
|
|
|
IF_DEBUG(FSD2) SrvPrint0( " - SrvFsdRestartWrite\n" );
|
|
|
|
//
|
|
// Get the request and response parameter pointers.
|
|
//
|
|
|
|
request = (PREQ_WRITE)WorkContext->RequestParameters;
|
|
response = (PRESP_WRITE)WorkContext->ResponseParameters;
|
|
|
|
//
|
|
// Get the file pointer.
|
|
//
|
|
|
|
rfcb = WorkContext->Rfcb;
|
|
IF_DEBUG(FSD2) {
|
|
SrvPrint2( " connection 0x%p, RFCB 0x%p\n",
|
|
WorkContext->Connection, rfcb );
|
|
}
|
|
|
|
//
|
|
// If the write failed, set an error status in the response header.
|
|
//
|
|
|
|
status = WorkContext->Irp->IoStatus.Status;
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
IF_DEBUG(ERRORS) SrvPrint1( "Write failed: %X\n", status );
|
|
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
|
WorkContext->FspRestartRoutine = SrvFsdRestartWrite;
|
|
QUEUE_WORK_TO_FSP( WorkContext );
|
|
goto Cleanup;
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, status );
|
|
|
|
} else {
|
|
|
|
//
|
|
// The write succeeded.
|
|
//
|
|
|
|
writeLength = (USHORT)WorkContext->Irp->IoStatus.Information;
|
|
|
|
//
|
|
// Save the count of bytes written, to be used to update the
|
|
// server statistics database.
|
|
//
|
|
|
|
UPDATE_WRITE_STATS( WorkContext, writeLength );
|
|
|
|
if ( rfcb->ShareType == ShareTypeDisk ) {
|
|
|
|
//
|
|
// Update the file position.
|
|
//
|
|
|
|
rfcb->CurrentPosition = SmbGetUlong( &request->Offset ) + writeLength;
|
|
|
|
if ( WorkContext->NextCommand == SMB_COM_WRITE ) {
|
|
response->WordCount = 1;
|
|
SmbPutUshort( &response->Count, writeLength );
|
|
SmbPutUshort( &response->ByteCount, 0 );
|
|
|
|
WorkContext->ResponseParameters =
|
|
NEXT_LOCATION( response, RESP_WRITE, 0 );
|
|
|
|
//
|
|
// Processing of the SMB is complete. Send the response.
|
|
//
|
|
|
|
SrvFsdSendResponse( WorkContext );
|
|
IF_DEBUG(FSD2) SrvPrint0( "SrvFsdRestartWrite complete\n" );
|
|
goto Cleanup;
|
|
}
|
|
|
|
} else if ( rfcb->ShareType == ShareTypePrint ) {
|
|
|
|
//
|
|
// Update the file position.
|
|
//
|
|
|
|
if ( WorkContext->NextCommand == SMB_COM_WRITE_PRINT_FILE ) {
|
|
rfcb->CurrentPosition += writeLength;
|
|
} else {
|
|
rfcb->CurrentPosition =
|
|
SmbGetUlong( &request->Offset ) + writeLength;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this was a Write and Unlock request, do the unlock. This
|
|
// is safe because we are restarted in the FSP in this case.
|
|
//
|
|
// Note that if the write failed, the range remains locked.
|
|
//
|
|
|
|
if ( WorkContext->NextCommand == SMB_COM_WRITE_AND_UNLOCK ) {
|
|
|
|
IF_SMB_DEBUG(READ_WRITE1) {
|
|
SrvPrint0( "SrvFsdRestartWrite: unlock requested -- passing request to FSP\n" );
|
|
}
|
|
|
|
SrvRestartWriteAndUnlock( WorkContext );
|
|
goto Cleanup;
|
|
|
|
} else if ( WorkContext->NextCommand == SMB_COM_WRITE_AND_CLOSE ) {
|
|
|
|
WorkContext->Parameters.LastWriteTime = SmbGetUlong(
|
|
&((PREQ_WRITE_AND_CLOSE)request)->LastWriteTimeInSeconds );
|
|
|
|
}
|
|
|
|
//
|
|
// If everything worked, build a response message. (If something
|
|
// failed, an error indication has already been placed in the SMB.)
|
|
//
|
|
|
|
if ( WorkContext->NextCommand == SMB_COM_WRITE_PRINT_FILE ) {
|
|
|
|
//
|
|
// ByteCount has a different offset for WRITE_PRINT_FILE
|
|
//
|
|
|
|
PRESP_WRITE_PRINT_FILE response2;
|
|
|
|
response2 = (PRESP_WRITE_PRINT_FILE)WorkContext->ResponseParameters;
|
|
response2->WordCount = 0;
|
|
SmbPutUshort( &response2->ByteCount, 0 );
|
|
|
|
WorkContext->ResponseParameters =
|
|
NEXT_LOCATION( response2, RESP_WRITE_PRINT_FILE, 0 );
|
|
} else {
|
|
|
|
response->WordCount = 1;
|
|
SmbPutUshort( &response->Count, writeLength );
|
|
SmbPutUshort( &response->ByteCount, 0 );
|
|
|
|
WorkContext->ResponseParameters =
|
|
NEXT_LOCATION( response, RESP_WRITE, 0 );
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this was a Write and Close request, close the file. It is
|
|
// safe to close the RFCB here because if this is a Write and Close,
|
|
// we're actually in the FSP, not in the FSD.
|
|
//
|
|
|
|
if ( WorkContext->NextCommand == SMB_COM_WRITE_AND_CLOSE ) {
|
|
|
|
ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
|
|
|
|
SrvRestartChainedClose( WorkContext );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Processing of the SMB is complete. Send the response.
|
|
//
|
|
|
|
SrvFsdSendResponse( WorkContext );
|
|
IF_DEBUG(FSD2) SrvPrint0( "SrvFsdRestartWrite complete\n" );
|
|
|
|
Cleanup:
|
|
if (bNeedTrace) {
|
|
SrvWmiEndContext(WorkContext);
|
|
}
|
|
return;
|
|
|
|
} // SrvFsdRestartWrite
|
|
|
|
VOID SRVFASTCALL
|
|
SrvFsdRestartPrepareMdlWriteAndX (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Processes the MDL preparation completion for the large WriteAndX SMB.
|
|
|
|
This routine initiates the receipt of transport data into the file's MDL,
|
|
and then control resumes at SrvFsdRestartWriteAndX when the transfer of data
|
|
from the transport is complete.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
--*/
|
|
{
|
|
PIRP irp = WorkContext->Irp;
|
|
PIO_STACK_LOCATION irpSp;
|
|
PTDI_REQUEST_KERNEL_RECEIVE parameters;
|
|
|
|
//
|
|
// Make sure we call SrvFsdRestartWriteAndX at passive level when
|
|
// the TDI receive completes.
|
|
//
|
|
WorkContext->FsdRestartRoutine = SrvQueueWorkToFspAtDpcLevel;
|
|
WorkContext->FspRestartRoutine = SrvFsdRestartWriteAndX;
|
|
|
|
//
|
|
// Make sure that we record the MDL address we're using
|
|
//
|
|
ASSERT( WorkContext->Parameters.WriteAndX.MdlAddress == NULL );
|
|
WorkContext->Parameters.WriteAndX.MdlAddress = irp->MdlAddress;
|
|
|
|
if( !NT_SUCCESS( irp->IoStatus.Status ) ) {
|
|
|
|
//
|
|
// Something went wrong. Early-out to SrvFsdRestartWriteAndX.
|
|
//
|
|
if( KeGetCurrentIrql() < DISPATCH_LEVEL ) {
|
|
SrvFsdRestartWriteAndX( WorkContext );
|
|
} else {
|
|
QUEUE_WORK_TO_FSP( WorkContext );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
ASSERT( irp->MdlAddress != NULL );
|
|
|
|
//
|
|
// Fill in the IRP for the TDI receive. We want to receive the data into
|
|
// the buffer described by the MDL we've just gotten
|
|
//
|
|
|
|
irp->Tail.Overlay.OriginalFileObject = NULL;
|
|
irp->Tail.Overlay.Thread = WorkContext->CurrentWorkQueue->IrpThread;
|
|
|
|
irpSp = IoGetNextIrpStackLocation( irp );
|
|
|
|
//
|
|
// Set up the completion routine
|
|
//
|
|
IoSetCompletionRoutine(
|
|
irp,
|
|
SrvFsdIoCompletionRoutine,
|
|
WorkContext,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
SET_OPERATION_START_TIME( &WorkContext );
|
|
|
|
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
irpSp->MinorFunction = (UCHAR)TDI_RECEIVE;
|
|
irpSp->FileObject = WorkContext->Connection->FileObject;
|
|
irpSp->DeviceObject = WorkContext->Connection->DeviceObject;
|
|
irpSp->Flags = 0;
|
|
|
|
parameters = (PTDI_REQUEST_KERNEL_RECEIVE)&irpSp->Parameters;
|
|
parameters->ReceiveLength = WorkContext->Parameters.WriteAndX.CurrentWriteLength;
|
|
parameters->ReceiveFlags = 0;
|
|
|
|
//
|
|
// Account for the amount we are taking in
|
|
//
|
|
WorkContext->Parameters.WriteAndX.RemainingWriteLength -=
|
|
WorkContext->Parameters.WriteAndX.CurrentWriteLength;
|
|
|
|
irp->AssociatedIrp.SystemBuffer = NULL;
|
|
irp->Flags = (ULONG)IRP_BUFFERED_IO;
|
|
irp->IoStatus.Status = 0;
|
|
|
|
ASSERT( irp->MdlAddress != NULL );
|
|
|
|
(VOID)IoCallDriver( irpSp->DeviceObject, irp );
|
|
|
|
//
|
|
// Processing resumes at SrvFsdRestartWriteAndX() when we've received
|
|
// the data from the transport. We will be at passive level.
|
|
//
|
|
}
|
|
|
|
VOID SRVFASTCALL
|
|
RestartLargeWriteAndX (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the restart routine that's invoked when we have received more data from
|
|
the transport, and we are not using MDLs to transfer the data into the file.
|
|
|
|
This routine initiates the write to the file, and then control resumes at
|
|
SrvFsdRestartWriteAndX when the write to the file is complete.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
--*/
|
|
{
|
|
PIRP irp = WorkContext->Irp;
|
|
ULONG length;
|
|
PRFCB rfcb = WorkContext->Rfcb;
|
|
PLFCB lfcb = rfcb->Lfcb;
|
|
|
|
//
|
|
// Check if we successfully received more data from the transport
|
|
//
|
|
|
|
if( irp->Cancel ||
|
|
(!NT_SUCCESS( irp->IoStatus.Status )
|
|
&& irp->IoStatus.Status != STATUS_BUFFER_OVERFLOW) ){
|
|
|
|
SrvSetSmbError( WorkContext, irp->IoStatus.Status );
|
|
SrvEndSmbProcessing( WorkContext, SmbStatusSendResponse );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We got more data from the transport. We need to write it out to the file if we
|
|
// haven't encountered any errors yet. The irp at this point holds the results of
|
|
// reading more data from the transport.
|
|
//
|
|
length = (ULONG)irp->IoStatus.Information;
|
|
|
|
//
|
|
// Adjust the parameters in the WorkContext
|
|
//
|
|
WorkContext->Parameters.WriteAndX.RemainingWriteLength -= length;
|
|
WorkContext->Parameters.WriteAndX.CurrentWriteLength = length;
|
|
|
|
//
|
|
// If we have picked up an error, we just want to keep reading from
|
|
// the transport and not write to the file.
|
|
//
|
|
if( WorkContext->Parameters.WriteAndX.FinalStatus ) {
|
|
|
|
//
|
|
// Indicate that we didn't write any more data to the file
|
|
//
|
|
WorkContext->Irp->IoStatus.Information = 0;
|
|
WorkContext->Irp->IoStatus.Status = WorkContext->Parameters.WriteAndX.FinalStatus;
|
|
|
|
SrvFsdRestartWriteAndX( WorkContext );
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Write the data to the file
|
|
//
|
|
if( lfcb->FastIoWrite != NULL ) {
|
|
|
|
try {
|
|
if( lfcb->FastIoWrite(
|
|
lfcb->FileObject,
|
|
&WorkContext->Parameters.WriteAndX.Offset,
|
|
WorkContext->Parameters.WriteAndX.CurrentWriteLength,
|
|
TRUE,
|
|
WorkContext->Parameters.WriteAndX.Key,
|
|
WorkContext->Parameters.WriteAndX.WriteAddress,
|
|
&WorkContext->Irp->IoStatus,
|
|
lfcb->DeviceObject
|
|
) ) {
|
|
|
|
//
|
|
// The fast I/O path worked. Call the restart routine directly
|
|
// to do postprocessing
|
|
//
|
|
SrvFsdRestartWriteAndX( 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 ));
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// The fast path failed, use the IRP to write the data to the file
|
|
//
|
|
|
|
IoBuildPartialMdl(
|
|
WorkContext->RequestBuffer->Mdl,
|
|
WorkContext->RequestBuffer->PartialMdl,
|
|
WorkContext->Parameters.WriteAndX.WriteAddress,
|
|
WorkContext->Parameters.WriteAndX.CurrentWriteLength
|
|
);
|
|
|
|
//
|
|
// Build the IRP.
|
|
//
|
|
SrvBuildReadOrWriteRequest(
|
|
WorkContext->Irp, // input IRP address
|
|
lfcb->FileObject, // target file object address
|
|
WorkContext, // context
|
|
IRP_MJ_WRITE, // major function code
|
|
0, // minor function code
|
|
WorkContext->Parameters.WriteAndX.WriteAddress,
|
|
WorkContext->Parameters.WriteAndX.CurrentWriteLength,
|
|
WorkContext->RequestBuffer->PartialMdl,
|
|
WorkContext->Parameters.WriteAndX.Offset,
|
|
WorkContext->Parameters.WriteAndX.Key
|
|
);
|
|
|
|
//
|
|
// Ensure that processing resumes in SrvFsdRestartWriteAndX when the
|
|
// write has completed. If this is the first part of a large write,
|
|
// we want to ensure that SrvFsdRestartWriteAndX is called at passive
|
|
// level because it might decide to use the cache manager to handle the
|
|
// rest of the write.
|
|
//
|
|
if ( WorkContext->Parameters.WriteAndX.InitialComplete ) {
|
|
WorkContext->FsdRestartRoutine = SrvFsdRestartWriteAndX;
|
|
} else {
|
|
WorkContext->FsdRestartRoutine = SrvQueueWorkToFspAtDpcLevel;
|
|
WorkContext->FspRestartRoutine = SrvFsdRestartWriteAndX;
|
|
}
|
|
|
|
(VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
|
|
|
|
//
|
|
// Processing resumes at SrvFsdRestartWriteAndX() when the file write
|
|
// is complete.
|
|
//
|
|
}
|
|
|
|
|
|
VOID SRVFASTCALL
|
|
SrvFsdRestartWriteAndX (
|
|
IN OUT PWORK_CONTEXT WorkContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine may be called in the FSD or the FSP. If the chained
|
|
command is Close, it will be called in the FSP.
|
|
|
|
If WorkContext->LargeIndication is set, this means we are processing
|
|
the flavor of WriteAndX that exceeds our negotiated buffer size. There may
|
|
be more data that we need to fetch from the transport. We may or may not be
|
|
doing MDL writes to the file.
|
|
|
|
If there is no more data to be gotten from the transport, we send the response
|
|
to the client.
|
|
|
|
Arguments:
|
|
|
|
WorkContext - Supplies a pointer to the work context block
|
|
describing server-specific context for the request.
|
|
|
|
--*/
|
|
|
|
{
|
|
PREQ_WRITE_ANDX request;
|
|
PREQ_NT_WRITE_ANDX ntRequest;
|
|
PRESP_WRITE_ANDX response;
|
|
|
|
PRFCB rfcb = WorkContext->Rfcb;
|
|
PIRP irp = WorkContext->Irp;
|
|
NTSTATUS status = irp->IoStatus.Status;
|
|
ULONG writeLength = (ULONG)irp->IoStatus.Information;
|
|
|
|
ULONG requestedWriteLength;
|
|
UCHAR nextCommand;
|
|
USHORT nextOffset;
|
|
USHORT reqAndXOffset;
|
|
LARGE_INTEGER position;
|
|
KIRQL oldIrql;
|
|
BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
|
|
|
|
PREQ_CLOSE closeRequest;
|
|
|
|
UNLOCKABLE_CODE( 8FIL );
|
|
if (bNeedTrace) {
|
|
if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
|
|
WorkContext->PreviousSMB = EVENT_TYPE_SMB_WRITE_AND_X;
|
|
SrvWmiStartContext(WorkContext);
|
|
}
|
|
else
|
|
WorkContext->bAlreadyTrace = FALSE;
|
|
|
|
IF_DEBUG(FSD2) SrvPrint0( " - SrvFsdRestartWriteAndX\n" );
|
|
|
|
//
|
|
// Get the request and response parameter pointers.
|
|
//
|
|
request = (PREQ_WRITE_ANDX)WorkContext->RequestParameters;
|
|
ntRequest = (PREQ_NT_WRITE_ANDX)WorkContext->RequestParameters;
|
|
response = (PRESP_WRITE_ANDX)WorkContext->ResponseParameters;
|
|
|
|
IF_DEBUG(FSD2) {
|
|
SrvPrint2( " connection 0x%p, RFCB 0x%p\n",
|
|
WorkContext->Connection, rfcb );
|
|
}
|
|
|
|
//
|
|
// If we are using MDL transfers and we have more data to get from the client
|
|
// then STATUS_BUFFER_OVERFLOW is simply an indication from the transport that
|
|
// it has more data to give to us. We consider that a success case for the
|
|
// purposes of this routine.
|
|
//
|
|
if( status == STATUS_BUFFER_OVERFLOW &&
|
|
WorkContext->LargeIndication &&
|
|
WorkContext->Parameters.WriteAndX.MdlAddress &&
|
|
WorkContext->Parameters.WriteAndX.RemainingWriteLength ) {
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Remember where the follow-on request begins, and what the next
|
|
// command is, as we are about to overwrite this information.
|
|
//
|
|
|
|
reqAndXOffset = SmbGetUshort( &request->AndXOffset );
|
|
|
|
nextCommand = request->AndXCommand;
|
|
WorkContext->NextCommand = nextCommand;
|
|
nextOffset = SmbGetUshort( &request->AndXOffset );
|
|
|
|
//
|
|
// If the write failed, set an error status in the response header.
|
|
// We still return a valid parameter block, in case some bytes were
|
|
// written before the error occurred. Note that we do _not_ process
|
|
// the next command if the write failed.
|
|
//
|
|
// *** OS/2 server behavior. Note that this is _not_ done for core
|
|
// Write.
|
|
//
|
|
|
|
if ( !NT_SUCCESS(status) ) {
|
|
|
|
IF_DEBUG(ERRORS) SrvPrint1( "Write failed: %X\n", status );
|
|
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
|
WorkContext->FspRestartRoutine = SrvFsdRestartWriteAndX;
|
|
QUEUE_WORK_TO_FSP( WorkContext );
|
|
goto Cleanup;
|
|
}
|
|
|
|
if( WorkContext->LargeIndication ) {
|
|
//
|
|
// Once this error code is set, we cease writing to the file. But
|
|
// we still need to consume the rest of the data that was sent to us
|
|
// by the client.
|
|
//
|
|
WorkContext->Parameters.WriteAndX.FinalStatus = status;
|
|
}
|
|
|
|
SrvSetSmbError( WorkContext, status );
|
|
nextCommand = SMB_COM_NO_ANDX_COMMAND;
|
|
}
|
|
|
|
//
|
|
// Update the file position.
|
|
//
|
|
|
|
if ( rfcb->ShareType != ShareTypePipe ) {
|
|
|
|
//
|
|
// We will ignore the distinction between clients that supply 32-bit
|
|
// and 64-bit file offsets. The reason for doing this is because
|
|
// the only clients that will use CurrentPosition is a 32-bit file
|
|
// offset client. Therefore, the upper 32-bits will never be used
|
|
// anyway. In addition, the RFCB is per client, so there is no
|
|
// possibility of clients mixing 32-bit and 64-bit file offsets.
|
|
// Therefore, for the 64-bit client, we will only read 32-bits of file
|
|
// offset.
|
|
//
|
|
|
|
if ( request->ByteCount == 12 ) {
|
|
|
|
//
|
|
// The client supplied a 32-bit file offset.
|
|
//
|
|
|
|
rfcb->CurrentPosition = SmbGetUlong( &request->Offset ) + writeLength;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The client supplied a 64-bit file offset. Only use 32-bits of
|
|
// file offset.
|
|
//
|
|
|
|
rfcb->CurrentPosition = SmbGetUlong( &ntRequest->Offset ) + writeLength;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the count of bytes written, to be used to update the server
|
|
// statistics database.
|
|
//
|
|
|
|
UPDATE_WRITE_STATS( WorkContext, writeLength );
|
|
|
|
IF_SMB_DEBUG(READ_WRITE1) {
|
|
SrvPrint2( "SrvFsdRestartWriteAndX: Fid 0x%lx, wrote %ld bytes\n",
|
|
rfcb->Fid, writeLength );
|
|
}
|
|
|
|
//
|
|
// If we are doing large transfers, and there is still more to go, then we
|
|
// need to keep the cycle going.
|
|
//
|
|
if( WorkContext->LargeIndication &&
|
|
WorkContext->Parameters.WriteAndX.RemainingWriteLength ) {
|
|
|
|
PIO_STACK_LOCATION irpSp;
|
|
PTDI_REQUEST_KERNEL_RECEIVE parameters;
|
|
LARGE_INTEGER PreviousWriteOffset;
|
|
BOOLEAN fAppending = TRUE;
|
|
|
|
PreviousWriteOffset = WorkContext->Parameters.WriteAndX.Offset;
|
|
|
|
//
|
|
// If we are only appending, do not change the offset
|
|
//
|
|
if( PreviousWriteOffset.QuadPart != 0xFFFFFFFFFFFFFFFF ) {
|
|
|
|
WorkContext->Parameters.WriteAndX.Offset.QuadPart += writeLength;
|
|
fAppending = FALSE;
|
|
}
|
|
|
|
//
|
|
// If we haven't tried an MDL write yet, or if we are already using
|
|
// MDLs, then we want to keep using MDLs
|
|
//
|
|
if( NT_SUCCESS( status ) && fAppending == FALSE &&
|
|
( WorkContext->Parameters.WriteAndX.InitialComplete == FALSE ||
|
|
( WorkContext->Parameters.WriteAndX.MdlAddress &&
|
|
WorkContext->Parameters.WriteAndX.RemainingWriteLength != 0 )
|
|
) ) {
|
|
|
|
PLFCB lfcb = rfcb->Lfcb;
|
|
NTSTATUS mdlStatus;
|
|
|
|
ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
|
|
|
|
WorkContext->Parameters.WriteAndX.InitialComplete = TRUE;
|
|
|
|
//
|
|
// If we already have an MDL, complete it now since we've already asked
|
|
// TDI to fill the buffer.
|
|
//
|
|
if( WorkContext->Parameters.WriteAndX.MdlAddress ) {
|
|
|
|
irp->MdlAddress = WorkContext->Parameters.WriteAndX.MdlAddress;
|
|
irp->IoStatus.Information = writeLength;
|
|
|
|
if( lfcb->MdlWriteComplete == NULL ||
|
|
|
|
lfcb->MdlWriteComplete( lfcb->FileObject,
|
|
&PreviousWriteOffset,
|
|
WorkContext->Parameters.WriteAndX.MdlAddress,
|
|
lfcb->DeviceObject
|
|
) == FALSE ) {
|
|
|
|
mdlStatus = SrvIssueMdlCompleteRequest( WorkContext,
|
|
NULL,
|
|
WorkContext->Parameters.WriteAndX.MdlAddress,
|
|
IRP_MJ_WRITE,
|
|
&PreviousWriteOffset,
|
|
writeLength
|
|
);
|
|
|
|
if( !NT_SUCCESS( mdlStatus ) ) {
|
|
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, mdlStatus );
|
|
if( NT_SUCCESS( status ) ) {
|
|
WorkContext->Parameters.WriteAndX.FinalStatus = status = mdlStatus;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have disposed of this MDL, get it out of our structures!
|
|
//
|
|
WorkContext->Parameters.WriteAndX.MdlAddress = NULL;
|
|
irp->MdlAddress = NULL;
|
|
}
|
|
|
|
//
|
|
// If we have more than 1 buffer's worth remaing, and if the filesystem
|
|
// supports MDL writes, then let's do MDL writes
|
|
//
|
|
if( NT_SUCCESS( status ) &&
|
|
(WorkContext->Parameters.WriteAndX.RemainingWriteLength >
|
|
WorkContext->Parameters.WriteAndX.BufferLength) &&
|
|
(lfcb->FileObject->Flags & FO_CACHE_SUPPORTED) ) {
|
|
|
|
LARGE_INTEGER offset;
|
|
ULONG remainingLength;
|
|
|
|
irp->IoStatus.Information = 0;
|
|
irp->UserBuffer = NULL;
|
|
irp->MdlAddress = NULL;
|
|
|
|
//
|
|
// Figure out how big we want this MDL attempt to be. We could
|
|
// map the whole thing in, but we don't want any single client request
|
|
// to lock down too much of the cache.
|
|
//
|
|
WorkContext->Parameters.WriteAndX.CurrentWriteLength = MIN (
|
|
WorkContext->Parameters.WriteAndX.RemainingWriteLength,
|
|
SrvMaxWriteChunk
|
|
);
|
|
|
|
if( lfcb->PrepareMdlWrite(
|
|
lfcb->FileObject,
|
|
&WorkContext->Parameters.WriteAndX.Offset,
|
|
WorkContext->Parameters.WriteAndX.CurrentWriteLength,
|
|
WorkContext->Parameters.WriteAndX.Key,
|
|
&irp->MdlAddress,
|
|
&irp->IoStatus,
|
|
lfcb->DeviceObject
|
|
) && irp->MdlAddress != NULL ) {
|
|
|
|
//
|
|
// The fast path worked!
|
|
//
|
|
SrvFsdRestartPrepareMdlWriteAndX( WorkContext );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// The fast path failed, build the write request. The fast path
|
|
// may have partially succeeded, returning a partial MDL chain.
|
|
// We need to adjust our write request to account for that.
|
|
//
|
|
offset.QuadPart = WorkContext->Parameters.WriteAndX.Offset.QuadPart;
|
|
|
|
//
|
|
// If we are not just appending, adjust the offset
|
|
//
|
|
if( offset.QuadPart != 0xFFFFFFFFFFFFFFFF ) {
|
|
offset.QuadPart += irp->IoStatus.Information;
|
|
}
|
|
|
|
remainingLength = WorkContext->Parameters.WriteAndX.CurrentWriteLength -
|
|
(ULONG)irp->IoStatus.Information;
|
|
|
|
SrvBuildReadOrWriteRequest(
|
|
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)
|
|
remainingLength,
|
|
irp->MdlAddress,
|
|
offset,
|
|
WorkContext->Parameters.WriteAndX.Key
|
|
);
|
|
|
|
WorkContext->bAlreadyTrace = TRUE;
|
|
WorkContext->FsdRestartRoutine = SrvFsdRestartPrepareMdlWriteAndX;
|
|
|
|
(VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We aren't doing MDL operations, so read the data from the transport into
|
|
// the SMB buffer.
|
|
//
|
|
WorkContext->Parameters.WriteAndX.CurrentWriteLength = MIN(
|
|
WorkContext->Parameters.WriteAndX.RemainingWriteLength,
|
|
WorkContext->Parameters.WriteAndX.BufferLength
|
|
);
|
|
|
|
//
|
|
// Fill in the IRP for the receive
|
|
//
|
|
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,
|
|
WorkContext,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE
|
|
);
|
|
|
|
SET_OPERATION_START_TIME( &WorkContext );
|
|
|
|
WorkContext->FsdRestartRoutine = SrvQueueWorkToFspAtDpcLevel;
|
|
WorkContext->FspRestartRoutine = RestartLargeWriteAndX;
|
|
|
|
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
irpSp->MinorFunction = (UCHAR)TDI_RECEIVE;
|
|
irpSp->FileObject = WorkContext->Connection->FileObject;
|
|
irpSp->DeviceObject = WorkContext->Connection->DeviceObject;
|
|
irpSp->Flags = 0;
|
|
|
|
parameters = (PTDI_REQUEST_KERNEL_RECEIVE)&irpSp->Parameters;
|
|
parameters->ReceiveLength = WorkContext->Parameters.WriteAndX.CurrentWriteLength;
|
|
parameters->ReceiveFlags = 0;
|
|
|
|
//
|
|
// Set the buffer's partial mdl to point just after the header for this
|
|
// WriteAndX SMB. We need to preserve the header to make it easier to send
|
|
// back the response.
|
|
//
|
|
|
|
IoBuildPartialMdl(
|
|
WorkContext->RequestBuffer->Mdl,
|
|
WorkContext->RequestBuffer->PartialMdl,
|
|
WorkContext->Parameters.WriteAndX.WriteAddress,
|
|
WorkContext->Parameters.WriteAndX.CurrentWriteLength
|
|
);
|
|
|
|
irp->MdlAddress = WorkContext->RequestBuffer->PartialMdl;
|
|
irp->AssociatedIrp.SystemBuffer = NULL;
|
|
irp->Flags = (ULONG)IRP_BUFFERED_IO; // ???
|
|
|
|
(VOID)IoCallDriver( irpSp->DeviceObject, irp );
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// We have no more data to write to the file. Clean up
|
|
// and send a response to the client
|
|
//
|
|
|
|
//
|
|
// If we are working on a large write using MDLs,
|
|
// then we need to clean up the MDL
|
|
//
|
|
if( WorkContext->LargeIndication &&
|
|
WorkContext->Parameters.WriteAndX.MdlAddress ) {
|
|
|
|
PLFCB lfcb = rfcb->Lfcb;
|
|
NTSTATUS mdlStatus;
|
|
|
|
ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
|
|
|
|
irp->MdlAddress = WorkContext->Parameters.WriteAndX.MdlAddress;
|
|
irp->IoStatus.Information = writeLength;
|
|
|
|
//
|
|
// Tell the filesystem that we're done with it
|
|
//
|
|
if( lfcb->MdlWriteComplete == NULL ||
|
|
|
|
lfcb->MdlWriteComplete( lfcb->FileObject,
|
|
&WorkContext->Parameters.WriteAndX.Offset,
|
|
WorkContext->Parameters.WriteAndX.MdlAddress,
|
|
lfcb->DeviceObject
|
|
) == FALSE ) {
|
|
|
|
mdlStatus = SrvIssueMdlCompleteRequest( WorkContext, NULL,
|
|
WorkContext->Parameters.WriteAndX.MdlAddress,
|
|
IRP_MJ_WRITE,
|
|
&WorkContext->Parameters.WriteAndX.Offset,
|
|
writeLength
|
|
);
|
|
|
|
if( !NT_SUCCESS( mdlStatus ) ) {
|
|
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, mdlStatus );
|
|
if( NT_SUCCESS( status ) ) {
|
|
status = mdlStatus;
|
|
}
|
|
}
|
|
}
|
|
|
|
irp->MdlAddress = NULL;
|
|
}
|
|
|
|
//
|
|
// Build the response message.
|
|
//
|
|
requestedWriteLength = SmbGetUshort( &request->DataLength );
|
|
|
|
if( WorkContext->LargeIndication ) {
|
|
|
|
requestedWriteLength |= (SmbGetUshort( &ntRequest->DataLengthHigh ) << 16);
|
|
|
|
writeLength = requestedWriteLength -
|
|
WorkContext->Parameters.WriteAndX.RemainingWriteLength;
|
|
}
|
|
|
|
SmbPutUlong( &response->Reserved, 0 );
|
|
SmbPutUshort( &response->CountHigh, (USHORT)(writeLength >> 16) );
|
|
|
|
response->AndXCommand = nextCommand;
|
|
response->AndXReserved = 0;
|
|
SmbPutUshort(
|
|
&response->AndXOffset,
|
|
GET_ANDX_OFFSET(
|
|
WorkContext->ResponseHeader,
|
|
WorkContext->ResponseParameters,
|
|
RESP_WRITE_ANDX,
|
|
0
|
|
)
|
|
);
|
|
|
|
response->WordCount = 6;
|
|
|
|
if ( rfcb->ShareType == ShareTypeDisk ||
|
|
WorkContext->Parameters.Transaction == NULL ) {
|
|
|
|
SmbPutUshort( &response->Count, (USHORT)writeLength );
|
|
|
|
} else {
|
|
|
|
SmbPutUshort( &response->Count, (USHORT)requestedWriteLength );
|
|
}
|
|
|
|
SmbPutUshort( &response->Remaining, (USHORT)-1 );
|
|
SmbPutUshort( &response->ByteCount, 0 );
|
|
|
|
WorkContext->ResponseParameters = (PCHAR)WorkContext->ResponseHeader +
|
|
SmbGetUshort( &response->AndXOffset );
|
|
|
|
WorkContext->RequestParameters = (PUCHAR)WorkContext->RequestHeader + reqAndXOffset;
|
|
|
|
IF_STRESS() {
|
|
// If this was a paging write that failed, log an error
|
|
PNT_SMB_HEADER pHeader = (PNT_SMB_HEADER)WorkContext->RequestHeader;
|
|
if( !NT_SUCCESS(pHeader->Status.NtStatus) && (pHeader->Flags2 & SMB_FLAGS2_PAGING_IO) )
|
|
{
|
|
KdPrint(("Paging Write failure from %z (%x)\n", (PCSTRING)&WorkContext->Connection->OemClientMachineNameString, pHeader->Status.NtStatus ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this was a raw mode write, queue the work to the FSP for
|
|
// completion. The FSP routine will handling dispatching of the
|
|
// AndX command.
|
|
//
|
|
|
|
if ( rfcb->ShareType != ShareTypeDisk &&
|
|
WorkContext->Parameters.Transaction != NULL ) {
|
|
|
|
WorkContext->FspRestartRoutine = SrvRestartWriteAndXRaw;
|
|
SrvQueueWorkToFsp( WorkContext );
|
|
goto Cleanup;
|
|
}
|
|
|
|
if( nextCommand == SMB_COM_NO_ANDX_COMMAND ) {
|
|
//
|
|
// No more commands. Send the response.
|
|
//
|
|
|
|
SrvFsdSendResponse( WorkContext );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Make sure the AndX command is still within the received SMB
|
|
//
|
|
if( (PCHAR)WorkContext->RequestHeader + reqAndXOffset >= END_OF_REQUEST_SMB( WorkContext ) ) {
|
|
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
KdPrint(( "SrvFsdRestartWriteAndX: Illegal followon offset: %u\n", reqAndXOffset ));
|
|
}
|
|
|
|
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
|
WorkContext->Irp->IoStatus.Status = STATUS_INVALID_SMB;
|
|
WorkContext->FspRestartRoutine = SrvBuildAndSendErrorResponse;
|
|
WorkContext->FsdRestartRoutine = SrvFsdRestartSmbComplete; // after response
|
|
QUEUE_WORK_TO_FSP( WorkContext );
|
|
} else {
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|
SrvFsdSendResponse( WorkContext );
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Test for a legal followon command, and dispatch as appropriate.
|
|
// Close is handled specially.
|
|
//
|
|
|
|
switch ( nextCommand ) {
|
|
|
|
case SMB_COM_READ:
|
|
case SMB_COM_READ_ANDX:
|
|
case SMB_COM_LOCK_AND_READ:
|
|
case SMB_COM_WRITE_ANDX:
|
|
|
|
//
|
|
// Queue the work item back to the FSP for further processing.
|
|
//
|
|
|
|
WorkContext->FspRestartRoutine = SrvRestartSmbReceived;
|
|
SrvQueueWorkToFsp( WorkContext );
|
|
|
|
break;
|
|
|
|
case SMB_COM_CLOSE:
|
|
|
|
//
|
|
// Save the last write time, to correctly set it. Call
|
|
// SrvRestartChainedClose to close the file and send the response.
|
|
//
|
|
|
|
closeRequest = (PREQ_CLOSE)
|
|
((PUCHAR)WorkContext->RequestHeader + reqAndXOffset);
|
|
|
|
//
|
|
// Make sure we stay within the received SMB
|
|
//
|
|
if( (PCHAR)closeRequest + FIELD_OFFSET( REQ_CLOSE, ByteCount)
|
|
<= END_OF_REQUEST_SMB( WorkContext ) ) {
|
|
|
|
WorkContext->Parameters.LastWriteTime =
|
|
closeRequest->LastWriteTimeInSeconds;
|
|
|
|
SrvRestartChainedClose( WorkContext );
|
|
break;
|
|
}
|
|
|
|
/* Falls Through! */
|
|
|
|
default: // Illegal followon command
|
|
|
|
IF_DEBUG(SMB_ERRORS) {
|
|
SrvPrint1( "SrvFsdRestartWriteAndX: Illegal followon "
|
|
"command: 0x%lx\n", nextCommand );
|
|
}
|
|
|
|
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
|
WorkContext->Irp->IoStatus.Status = STATUS_INVALID_SMB;
|
|
WorkContext->FspRestartRoutine = SrvBuildAndSendErrorResponse;
|
|
WorkContext->FsdRestartRoutine = SrvFsdRestartSmbComplete; // after response
|
|
QUEUE_WORK_TO_FSP( WorkContext );
|
|
} else {
|
|
SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
|
|
SrvFsdSendResponse( WorkContext );
|
|
}
|
|
|
|
}
|
|
|
|
IF_DEBUG(TRACE2) SrvPrint0( "SrvFsdRestartWriteAndX complete\n" );
|
|
|
|
Cleanup:
|
|
if (bNeedTrace) {
|
|
SrvWmiEndContext(WorkContext);
|
|
}
|
|
return;
|
|
|
|
} // SrvFsdRestartWriteAndX
|
|
|
|
#if SRVCATCH
|
|
BYTE CatchPrototype[] = ";UUIDREF=";
|
|
VOID
|
|
SrvUpdateCatchBuffer (
|
|
IN PWORK_CONTEXT WorkContext,
|
|
IN OUT PBYTE Buffer,
|
|
IN DWORD BufferLength
|
|
)
|
|
{
|
|
BYTE idBuffer[ 100 ];
|
|
PBYTE p, ep = idBuffer;
|
|
USHORT bytesRemaining = sizeof( idBuffer );
|
|
UNICODE_STRING userName, domainName;
|
|
OEM_STRING oemString;
|
|
ULONG requiredLength;
|
|
|
|
if( BufferLength <= sizeof( CatchPrototype ) ) {
|
|
return;
|
|
}
|
|
|
|
if( WorkContext->Session == 0 ) {
|
|
SrvVerifyUid( WorkContext, SmbGetAlignedUshort( &WorkContext->RequestHeader->Uid ) );
|
|
}
|
|
|
|
if( WorkContext->Session &&
|
|
NT_SUCCESS( SrvGetUserAndDomainName( WorkContext->Session, &userName, &domainName ) ) ) {
|
|
|
|
if( userName.Length && NT_SUCCESS( RtlUnicodeStringToOemString( &oemString, &userName, TRUE ) ) ) {
|
|
if( bytesRemaining >= oemString.Length + 1 ) {
|
|
RtlCopyMemory( ep, oemString.Buffer, oemString.Length );
|
|
ep += oemString.Length;
|
|
*ep++ = '\\';
|
|
bytesRemaining -= (oemString.Length + 1);
|
|
RtlFreeOemString( &oemString );
|
|
}
|
|
}
|
|
|
|
if( domainName.Length && NT_SUCCESS( RtlUnicodeStringToOemString( &oemString, &domainName, TRUE ) ) ) {
|
|
if( bytesRemaining >= oemString.Length ) {
|
|
RtlCopyMemory( ep, oemString.Buffer, oemString.Length );
|
|
ep += oemString.Length;
|
|
bytesRemaining -= oemString.Length;
|
|
RtlFreeOemString( &oemString );
|
|
}
|
|
}
|
|
|
|
SrvReleaseUserAndDomainName( WorkContext->Session, &userName, &domainName );
|
|
}
|
|
|
|
if( WorkContext->Connection && bytesRemaining ) {
|
|
|
|
oemString = WorkContext->Connection->OemClientMachineNameString;
|
|
|
|
if( oemString.Length && oemString.Length < bytesRemaining + 1 ) {
|
|
*ep++ = ' ';
|
|
RtlCopyMemory( ep, oemString.Buffer, oemString.Length );
|
|
ep += oemString.Length;
|
|
bytesRemaining -= oemString.Length;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Insert the CatchPrototype into the output buffer
|
|
//
|
|
if( WorkContext->Rfcb->SrvCatch == 1 )
|
|
{
|
|
RtlCopyMemory( Buffer, CatchPrototype, sizeof( CatchPrototype )-1 );
|
|
Buffer += sizeof( CatchPrototype )-1;
|
|
BufferLength -= (sizeof( CatchPrototype ) - 1);
|
|
|
|
//
|
|
// Encode the information
|
|
//
|
|
for( p = idBuffer; BufferLength >= 3 && p < ep; p++, BufferLength =- 2 ) {
|
|
*Buffer++ = SrvHexChars[ ((*p) >> 4) & 0xf ];
|
|
*Buffer++ = SrvHexChars[ (*p) & 0xf ];
|
|
}
|
|
|
|
if( BufferLength >= 3 ) {
|
|
*Buffer++ = '\r';
|
|
*Buffer++ = '\n';
|
|
*Buffer++ = ';';
|
|
}
|
|
}
|
|
else if( WorkContext->Rfcb->SrvCatch == 2 )
|
|
{
|
|
PBYTE InnerBuffer;
|
|
ULONG Offset;
|
|
|
|
Offset = SrvFindCatchOffset( Buffer, BufferLength );
|
|
if( Offset )
|
|
{
|
|
InnerBuffer = Buffer + Offset;
|
|
BufferLength = 1020;
|
|
|
|
RtlCopyMemory( InnerBuffer, CatchPrototype, sizeof( CatchPrototype )-1 );
|
|
InnerBuffer += sizeof( CatchPrototype )-1;
|
|
BufferLength -= (sizeof( CatchPrototype ) - 1);
|
|
|
|
//
|
|
// Encode the information
|
|
//
|
|
for( p = idBuffer; BufferLength >= 3 && p < ep; p++, BufferLength =- 2 ) {
|
|
*InnerBuffer++ = SrvHexChars[ ((*p) >> 4) & 0xf ];
|
|
*InnerBuffer++ = SrvHexChars[ (*p) & 0xf ];
|
|
}
|
|
|
|
if( BufferLength >= 3 ) {
|
|
*InnerBuffer++ = '\r';
|
|
*InnerBuffer++ = '\n';
|
|
*InnerBuffer++ = ';';
|
|
}
|
|
|
|
SrvCorrectCatchBuffer( Buffer, Offset );
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|