3686 lines
105 KiB
C
3686 lines
105 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
smbbulk.c
|
||
|
||
Abstract:
|
||
|
||
This module contains routines for processing the following SMBs:
|
||
|
||
Read Bulk
|
||
Write Bulk
|
||
|
||
Note that core, raw mode and multiplexed mode SMB processors are not
|
||
contained in this module. Check smbrdwrt.c, smbraw.c and smbmpx.c
|
||
instead. SMB commands that pertain exclusively to locking (LockByteRange,
|
||
UnlockByteRange, and LockingAndX) are processed in smblock.c.
|
||
|
||
Author:
|
||
|
||
Rod Gamache (rodga) 15-Jun-1995
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
#define MIN_CHUNK_SIZE (4*1024) // 4KB minimum chunk size
|
||
|
||
#define BugCheckFileId SRV_FILE_SMBBULK
|
||
|
||
PVOID RngCompressedWC;
|
||
PVOID RngReadAddress;
|
||
PVOID RngBulkBuffer;
|
||
PVOID RngAuxBuffer;
|
||
BOOLEAN RngFastPath = FALSE;
|
||
|
||
|
||
#if DBG
|
||
VOID
|
||
DumpMdlChain(
|
||
IN PMDL mdl
|
||
);
|
||
#endif
|
||
|
||
|
||
//
|
||
// Forward declarations
|
||
//
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE, SrvSmbReadBulk )
|
||
#pragma alloc_text( PAGE8FIL, SrvFsdRestartReadBulk )
|
||
#endif
|
||
|
||
#define STACK_THRESHOLD 0xE00
|
||
|
||
VOID SRVFASTCALL
|
||
SrvFsdRestartReadBulkC (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
);
|
||
|
||
VOID SRVFASTCALL
|
||
RestartMdlReadBulkComplete (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
);
|
||
|
||
NTSTATUS
|
||
RestartCopyReadBulkComplete (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
);
|
||
|
||
NTSTATUS
|
||
SendCopyReadBulkFragment (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
);
|
||
|
||
NTSTATUS
|
||
SendMdlReadBulkFragment (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
);
|
||
|
||
VOID SRVFASTCALL
|
||
RestartPrepareBulkMdlWrite (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
);
|
||
|
||
NTSTATUS
|
||
RestartWriteBulkSendComplete (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
);
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE
|
||
SrvSmbWriteBulkData (
|
||
SMB_PROCESSOR_PARAMETERS
|
||
);
|
||
|
||
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE
|
||
SrvSmbReadBulk (
|
||
SMB_PROCESSOR_PARAMETERS
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes the Read Bulk SMB.
|
||
|
||
Arguments:
|
||
|
||
SMB_PROCESSOR_PARAMETERS - See smbtypes.h for a description
|
||
of the parameters to SMB processor routines.
|
||
|
||
Return Value:
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE - See smbtypes.h
|
||
|
||
--*/
|
||
|
||
{
|
||
PREQ_READ_BULK request;
|
||
PSMB_HEADER header;
|
||
|
||
NTSTATUS status;
|
||
USHORT fid;
|
||
PRFCB rfcb;
|
||
PLFCB lfcb;
|
||
LARGE_INTEGER offset;
|
||
ULONG bufferOffset;
|
||
PCHAR readAddress = NULL;
|
||
ULONG readLength;
|
||
ULONG key;
|
||
PVOID bulkBuffer;
|
||
UCHAR minorFunction;
|
||
PMDL mdl;
|
||
ULONG fragmentSize;
|
||
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// If we do not support bulk transfers, cut off this SMB now
|
||
//
|
||
if( SrvSupportsBulkTransfer == FALSE ) {
|
||
return SrvSmbIllegalCommand( WorkContext );
|
||
}
|
||
|
||
request = (PREQ_READ_BULK)WorkContext->RequestParameters;
|
||
header = WorkContext->RequestHeader;
|
||
|
||
//
|
||
// If we do not support compressed bulk transfers, make sure
|
||
// the client isn't asking for compressed data.
|
||
//
|
||
if( SrvSupportsCompression ) {
|
||
WorkContext->Parameters.ReadBulk.CompressionTechnology =
|
||
request->CompressionTechnology;
|
||
} else {
|
||
WorkContext->Parameters.ReadBulk.CompressionTechnology =
|
||
CompressionTechnologyNone;
|
||
}
|
||
|
||
fid = SmbGetUshort( &request->Fid );
|
||
|
||
IF_SMB_DEBUG(BULK1) {
|
||
KdPrint(( "Read Bulk request; FID 0x%lx, count %ld, offset %ld\n",
|
||
fid, SmbGetUshort( &request->MaxCount ),
|
||
SmbGetUlong( &request->Offset.LowPart ) ));
|
||
}
|
||
|
||
//
|
||
// First, verify the FID. If verified, the RFCB is referenced and
|
||
// its address is stored in the WorkContext block, and the RFCB
|
||
// address is returned.
|
||
//
|
||
|
||
rfcb = SrvVerifyFid(
|
||
WorkContext,
|
||
fid,
|
||
TRUE,
|
||
SrvRestartSmbReceived, // serialize with raw write
|
||
&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((
|
||
"SrvSmbReadBulk Status %X on FID: 0x%lx\n",
|
||
status,
|
||
fid
|
||
));
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
return SmbStatusSendResponse;
|
||
|
||
}
|
||
|
||
//
|
||
// The work item has been queued because a raw write is in
|
||
// progress.
|
||
//
|
||
|
||
return SmbStatusInProgress;
|
||
|
||
}
|
||
|
||
lfcb = rfcb->Lfcb;
|
||
|
||
//
|
||
// Verify that the client has read access to the file via the
|
||
// specified handle.
|
||
//
|
||
|
||
if ( !rfcb->ReadAccessGranted ) {
|
||
|
||
CHECK_PAGING_IO_ACCESS(
|
||
WorkContext,
|
||
rfcb->GrantedAccess,
|
||
&status );
|
||
if ( !NT_SUCCESS( status ) ) {
|
||
SrvStatistics.GrantedAccessErrors++;
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "SrvSmbReadBulk: Read access not granted.\n"));
|
||
}
|
||
SrvSetSmbError( WorkContext, status );
|
||
return SmbStatusSendResponse;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If this is not a disk file, then tell the client to use core read.
|
||
//
|
||
|
||
if ( rfcb->ShareType != ShareTypeDisk ) {
|
||
SrvSetSmbError( WorkContext, STATUS_SMB_USE_STANDARD );
|
||
return SmbStatusSendResponse;
|
||
}
|
||
|
||
//
|
||
// Form the lock key using the FID and the PID. (This is also
|
||
// irrelevant for pipes.)
|
||
//
|
||
// *** 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 );
|
||
|
||
//
|
||
// We need to save the key in case we fail on issuing an IRP compressed read
|
||
// request and we have to issue an uncompressed read from the completion
|
||
// routine.
|
||
//
|
||
|
||
WorkContext->Parameters.ReadBulk.Key = key;
|
||
|
||
//
|
||
// Get the file offset.
|
||
//
|
||
|
||
offset.LowPart = SmbGetUlong( &request->Offset.LowPart);
|
||
offset.HighPart = SmbGetUlong( &request->Offset.HighPart);
|
||
|
||
WorkContext->Parameters.ReadBulk.Offset.QuadPart = offset.QuadPart;
|
||
|
||
//
|
||
// Ensure the WorkContext is requeued to the head of the list to speed up
|
||
// the response.
|
||
//
|
||
|
||
WorkContext->QueueToHead = TRUE;
|
||
|
||
//
|
||
// Save info from SMB request
|
||
//
|
||
|
||
readLength = SmbGetUlong( &request->MaxCount );
|
||
|
||
//
|
||
// Get the Maximum Message size and validate it.
|
||
//
|
||
// 1. It must not be less than MIN_SEND_SIZE.
|
||
// 2. It must be minimized with our Maximum Message size, which has already
|
||
// been minimized with MAX_PARTIAL_BUFFER_SIZE.
|
||
// 3. It must not be greater than readLength.
|
||
//
|
||
|
||
fragmentSize = SmbGetUlong( &request->MessageSize );
|
||
if ( fragmentSize < MIN_SEND_SIZE ) {
|
||
fragmentSize = MIN_SEND_SIZE;
|
||
}
|
||
|
||
fragmentSize = MIN(fragmentSize, WorkContext->Connection->MaximumSendSize) -
|
||
READ_BULK_BUFFER_OFFSET;
|
||
|
||
WorkContext->Parameters.ReadBulk.FragmentSize =
|
||
MIN( fragmentSize, readLength );
|
||
|
||
WorkContext->Parameters.ReadBulk.RemainingCount = readLength;
|
||
|
||
//
|
||
// If the SMB buffer is large enough, use it to do the local read.
|
||
//
|
||
// This will only be done if we are not doing compressed reads, since
|
||
// the descriptors will have to come first and we don't know how big
|
||
// those will be.
|
||
//
|
||
|
||
if ( (WorkContext->Parameters.ReadBulk.CompressionTechnology ==
|
||
CompressionTechnologyNone) &&
|
||
(readLength <= SrvMpxMdlReadSwitchover) ) {
|
||
|
||
do_copy_read:
|
||
|
||
WorkContext->Parameters.ReadBulk.MdlRead = FALSE;
|
||
WorkContext->Parameters.ReadBulk.BulkBuffer = NULL;
|
||
WorkContext->Parameters.ReadBulk.BulkBufferMdl =
|
||
WorkContext->ResponseBuffer->Mdl;
|
||
|
||
readAddress = (PCHAR)WorkContext->ResponseHeader + READ_BULK_BUFFER_OFFSET;
|
||
WorkContext->Parameters.ReadBulk.NextFragmentAddress = readAddress;
|
||
|
||
//
|
||
// Try the fast I/O path first.
|
||
//
|
||
|
||
if ( lfcb->FastIoRead != NULL ) {
|
||
|
||
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
|
||
|
||
if ( lfcb->FastIoRead(
|
||
lfcb->FileObject,
|
||
&offset,
|
||
readLength,
|
||
TRUE,
|
||
key,
|
||
readAddress,
|
||
&WorkContext->Irp->IoStatus,
|
||
lfcb->DeviceObject
|
||
) ) {
|
||
|
||
//
|
||
// The fast I/O path worked. Send the data.
|
||
//
|
||
|
||
SrvFsdRestartReadBulk( WorkContext );
|
||
return SmbStatusInProgress;
|
||
|
||
}
|
||
|
||
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,
|
||
readAddress,
|
||
readLength
|
||
);
|
||
mdl = WorkContext->ResponseBuffer->PartialMdl;
|
||
minorFunction = 0;
|
||
WorkContext->FsdRestartRoutine = SrvFsdRestartReadBulk;
|
||
|
||
} else {
|
||
|
||
//
|
||
// The SMB buffer isn't big enough. Does the target file system
|
||
// support the cache manager routines?
|
||
//
|
||
// RNGFIX - Since Mdl Reads are broken, is it better to do COMPRESSED
|
||
// copy reads or do UNCOMPRESSED Mdl reads? For now do
|
||
// UNCOMPRESSED Mdl reads
|
||
if ( (lfcb->FileObject->Flags & FO_CACHE_SUPPORTED) ) {
|
||
|
||
WorkContext->Parameters.ReadBulk.MdlRead = TRUE;
|
||
WorkContext->Parameters.ReadBulk.ReadLength = readLength;
|
||
|
||
//
|
||
// 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 );
|
||
|
||
//
|
||
// Check if this is a compressed read request. If so, we'll try
|
||
// an Mdl Read Compressed request to the cache manager.
|
||
//
|
||
|
||
//RNGFIX
|
||
// If the file offset is aligned on a 4KB boundary (temp) and
|
||
// the readLength is a multiple of 4KB and the client can take
|
||
// data compressed, then let's go for compressed data!
|
||
// We also need to make sure that the file is compressed and the
|
||
// is at least 4KB big. It doesn't hurt if we read an uncompressed
|
||
// file or small file as compressed, but it will fail and we'll
|
||
// end up doing more work by coming back and reading the file
|
||
// uncompressed.
|
||
|
||
ASSERT( CompressionTechnologyNone == 0 );
|
||
//RNGFIX - Mdl Read Compressed is currently broken, until the
|
||
// NT resource package can be fix by TomM.
|
||
|
||
if ( 0 && //RNGFIX - temporarily disable compressed MDL reads
|
||
((offset.LowPart & 0xfff) == 0) &&
|
||
((readLength & 0xfff) == 0) &&
|
||
(WorkContext->Parameters.ReadBulk.CompressionTechnology) &&
|
||
((rfcb->Mfcb->NonpagedMfcb->OpenFileAttributes &
|
||
FILE_ATTRIBUTE_COMPRESSED) != 0 ) &&
|
||
(rfcb->Mfcb->NonpagedMfcb->OpenFileSize.QuadPart >= 0x1000) ) {
|
||
|
||
ULONG compressedInfoLength;
|
||
ULONG mdlLength;
|
||
|
||
compressedInfoLength = (sizeof(COMPRESSED_DATA_INFO) + 7 +
|
||
(((readLength + MIN_CHUNK_SIZE - 1) / MIN_CHUNK_SIZE) * 4)) &
|
||
~7;
|
||
|
||
//
|
||
// Setup for compressed Mdl read.
|
||
//
|
||
|
||
//
|
||
// The Compressed Data Info is pointed to by Aux.Buffer,
|
||
// which is carved out of the 4KB response buffer. We'll
|
||
// position this structure down a little, so that it is aligned
|
||
// for the cache manager on RISC platforms. We'll then move
|
||
// it so that it is contiguous with the response header for
|
||
// transmission over the wire.
|
||
//
|
||
|
||
WorkContext->Parameters.ReadBulk.Aux.Buffer =
|
||
(PVOID)((ULONG)((PCHAR)WorkContext->ResponseHeader +
|
||
compressedInfoLength + READ_BULK_BUFFER_OFFSET) & ~7);
|
||
WorkContext->Parameters.ReadBulk.Aux.Length = compressedInfoLength;
|
||
|
||
//
|
||
// Now let's try to do a Compressed MDL READ
|
||
//
|
||
|
||
// (Note: the only difference between the Mdl read and the
|
||
// Copy read is that one of buffer address and the mdl must
|
||
// be NULL. The one that is NULL is different between the
|
||
// Mdl read and the Copy read..
|
||
|
||
if ( lfcb->FastIoReadCompressed(
|
||
lfcb->FileObject,
|
||
&offset,
|
||
readLength,
|
||
key,
|
||
NULL,
|
||
&WorkContext->Irp->MdlAddress,
|
||
&WorkContext->Irp->IoStatus,
|
||
WorkContext->Parameters.ReadBulk.Aux.Buffer,
|
||
compressedInfoLength,
|
||
lfcb->DeviceObject
|
||
) ) {
|
||
|
||
//
|
||
// The fast I/O path worked. Send the data.
|
||
//
|
||
|
||
SrvFsdRestartReadBulkC( WorkContext );
|
||
return SmbStatusInProgress;
|
||
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
|
||
|
||
//
|
||
// The fast I/O path failed. We need to issue a regular MDL
|
||
// read request.
|
||
//
|
||
// This call should either entirely work or fail
|
||
// completely.
|
||
//
|
||
|
||
mdl = NULL;
|
||
minorFunction = IRP_MN_MDL | IRP_MN_COMPRESSED;
|
||
|
||
WorkContext->Parameters.ReadBulk.Aux.Flags = 0;
|
||
WorkContext->Irp->Tail.Overlay.AuxiliaryBuffer =
|
||
(PVOID)&WorkContext->Parameters.ReadBulk.Aux;
|
||
|
||
WorkContext->FsdRestartRoutine = SrvFsdRestartReadBulkC;
|
||
|
||
} else {
|
||
|
||
|
||
//
|
||
// This is an uncompressed Mdl read. Set CompressionTechnology
|
||
// in restart routine.
|
||
//
|
||
|
||
|
||
if ( lfcb->MdlRead(
|
||
lfcb->FileObject,
|
||
&offset,
|
||
readLength,
|
||
key,
|
||
&WorkContext->Irp->MdlAddress,
|
||
&WorkContext->Irp->IoStatus,
|
||
lfcb->DeviceObject
|
||
) ) {
|
||
|
||
//
|
||
// The fast I/O path worked. Send the data.
|
||
//
|
||
|
||
SrvFsdRestartReadBulk( WorkContext );
|
||
return SmbStatusInProgress;
|
||
|
||
}
|
||
|
||
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 -= WorkContext->Irp->IoStatus.Information;
|
||
|
||
mdl = WorkContext->Irp->MdlAddress;
|
||
minorFunction = IRP_MN_MDL;
|
||
WorkContext->FsdRestartRoutine = SrvFsdRestartReadBulk;
|
||
}
|
||
|
||
} else if (readLength > (WorkContext->ResponseBuffer->BufferLength -
|
||
READ_BULK_BUFFER_OFFSET)) {
|
||
|
||
ULONG mdlLength;
|
||
|
||
//
|
||
// We have to use a normal "copy" read. We need to allocate
|
||
// a separate buffer.
|
||
//
|
||
|
||
WorkContext->Parameters.ReadBulk.MdlRead = FALSE;
|
||
|
||
//RNGFIX
|
||
// If the file offset is aligned on a 4KB boundary (temp) and
|
||
// the readLength is a multiple of 4KB and the client can take
|
||
// data compressed, then let's go for compressed data!
|
||
// We also need to make sure that the file is compressed and the
|
||
// is at least 4KB big. It doesn't hurt if we read an uncompressed
|
||
// file or small file as compressed, but it will fail and we'll
|
||
// end up doing more work by coming back and reading the file
|
||
// uncompressed.
|
||
//
|
||
ASSERT( CompressionTechnologyNone == 0 );
|
||
if ( ((offset.LowPart & 0xfff) == 0) &&
|
||
((readLength & 0xfff) == 0) &&
|
||
(WorkContext->Parameters.ReadBulk.CompressionTechnology) &&
|
||
((rfcb->Mfcb->NonpagedMfcb->OpenFileAttributes &
|
||
FILE_ATTRIBUTE_COMPRESSED) != 0 ) &&
|
||
(rfcb->Mfcb->NonpagedMfcb->OpenFileSize.QuadPart >= 0x1000) ) {
|
||
|
||
ULONG compressedInfoLength;
|
||
|
||
compressedInfoLength = (sizeof(COMPRESSED_DATA_INFO) + 7 +
|
||
(((readLength + MIN_CHUNK_SIZE - 1) / MIN_CHUNK_SIZE) * 4)) &
|
||
~7;
|
||
|
||
//
|
||
// Calculate the size of an Mdl that would span the CDI plus
|
||
// the user data.
|
||
//
|
||
|
||
mdlLength = sizeof(MDL) + (sizeof(ULONG) *
|
||
COMPUTE_PAGES_SPANNED( PAGE_SIZE-1, readLength +
|
||
compressedInfoLength ) );
|
||
|
||
//
|
||
// We need to allocate a buffer that will hold the actual data,
|
||
// plus the CompressedDataInfo, plus 2 MDL's. The first mdl is
|
||
// used to map the buffer for the CDI plus data, the second mdl
|
||
// is used to post the read for the data in the non-fastio case.
|
||
//
|
||
|
||
bulkBuffer = ALLOCATE_NONPAGED_POOL(
|
||
readLength + compressedInfoLength +
|
||
(2 * mdlLength),
|
||
BlockTypeDataBuffer
|
||
);
|
||
|
||
if ( bulkBuffer == NULL ) {
|
||
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
|
||
return SmbStatusSendResponse;
|
||
}
|
||
|
||
WorkContext->Parameters.ReadBulk.BulkBuffer = bulkBuffer;
|
||
|
||
mdl = (PMDL)bulkBuffer;
|
||
bulkBuffer = (PCHAR)bulkBuffer + (2 * mdlLength);
|
||
|
||
//
|
||
// Initialize the first Mdl that describes CDI plus data.
|
||
//
|
||
|
||
MmInitializeMdl( mdl, bulkBuffer, readLength + compressedInfoLength );
|
||
WorkContext->Parameters.ReadBulk.BulkBufferMdl = mdl;
|
||
|
||
//
|
||
// Build the first mdl.
|
||
//
|
||
|
||
MmBuildMdlForNonPagedPool( mdl );
|
||
|
||
//
|
||
// Setup for compressed read.
|
||
//
|
||
|
||
WorkContext->Parameters.ReadBulk.Aux.Buffer = bulkBuffer;
|
||
|
||
WorkContext->Parameters.ReadBulk.Aux.Length = compressedInfoLength;
|
||
WorkContext->Parameters.ReadBulk.NextFragmentAddress = bulkBuffer;
|
||
|
||
readAddress = (PCHAR)bulkBuffer + compressedInfoLength;
|
||
|
||
//
|
||
// Initialize the second Mdl that describes the data only.
|
||
//
|
||
|
||
mdl = (PMDL)((PCHAR)mdl + mdlLength);
|
||
MmInitializeMdl( mdl, readAddress, readLength );
|
||
WorkContext->Parameters.ReadBulk.ReadBufferMdl = mdl;
|
||
|
||
//
|
||
// Build the second mdl.
|
||
//
|
||
|
||
MmBuildMdlForNonPagedPool( mdl );
|
||
|
||
//
|
||
// Try the fast I/O path first.
|
||
//
|
||
|
||
if ( lfcb->FastIoReadCompressed != NULL ) {
|
||
|
||
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
|
||
|
||
if ( lfcb->FastIoReadCompressed(
|
||
lfcb->FileObject,
|
||
&offset,
|
||
readLength,
|
||
key,
|
||
readAddress,
|
||
NULL,
|
||
&WorkContext->Irp->IoStatus,
|
||
WorkContext->Parameters.ReadBulk.Aux.Buffer,
|
||
compressedInfoLength,
|
||
lfcb->DeviceObject
|
||
) ) {
|
||
|
||
//
|
||
// The fast I/O path worked. Send the data.
|
||
//
|
||
|
||
SrvFsdRestartReadBulkC( WorkContext );
|
||
return SmbStatusInProgress;
|
||
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
|
||
|
||
}
|
||
|
||
//
|
||
// The fast I/O path failed, so we need to use a regular copy
|
||
// I/O request.
|
||
//
|
||
|
||
minorFunction = IRP_MN_COMPRESSED;
|
||
|
||
WorkContext->Parameters.ReadBulk.Aux.Flags = 0;
|
||
WorkContext->Irp->Tail.Overlay.AuxiliaryBuffer =
|
||
(PVOID)&WorkContext->Parameters.ReadBulk.Aux;
|
||
|
||
WorkContext->FsdRestartRoutine = SrvFsdRestartReadBulkC;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Read bulk uncompressed. Set CompressionTechnology in
|
||
// restart routine.
|
||
//
|
||
|
||
//
|
||
// Calculate the size of an mdl that would span the user data.
|
||
//
|
||
|
||
mdlLength = sizeof(MDL) +
|
||
(sizeof(ULONG) * COMPUTE_PAGES_SPANNED(PAGE_SIZE-1, readLength));
|
||
|
||
//
|
||
// Allocate a buffer for the data plus an mdl.
|
||
//
|
||
|
||
bulkBuffer = ALLOCATE_NONPAGED_POOL(
|
||
readLength + mdlLength,
|
||
BlockTypeDataBuffer
|
||
);
|
||
|
||
if ( bulkBuffer == NULL ) {
|
||
SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
|
||
return SmbStatusSendResponse;
|
||
}
|
||
|
||
WorkContext->Parameters.ReadBulk.BulkBuffer = bulkBuffer;
|
||
|
||
mdl = (PMDL)bulkBuffer;
|
||
bulkBuffer = (PCHAR)bulkBuffer + mdlLength;
|
||
|
||
WorkContext->Parameters.ReadBulk.NextFragmentAddress = bulkBuffer;
|
||
readAddress = bulkBuffer;
|
||
|
||
//
|
||
// Initialize the mdl.
|
||
//
|
||
|
||
MmInitializeMdl( mdl, readAddress, readLength );
|
||
WorkContext->Parameters.ReadBulk.BulkBufferMdl = mdl;
|
||
|
||
//
|
||
// Build the mdl.
|
||
//
|
||
|
||
MmBuildMdlForNonPagedPool( mdl );
|
||
|
||
WorkContext->Parameters.ReadBulk.ReadBufferMdl = NULL;
|
||
|
||
//
|
||
// Try the fast I/O path first.
|
||
//
|
||
|
||
if ( lfcb->FastIoRead != NULL ) {
|
||
|
||
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
|
||
|
||
if ( lfcb->FastIoRead(
|
||
lfcb->FileObject,
|
||
&offset,
|
||
readLength,
|
||
TRUE,
|
||
key,
|
||
bulkBuffer,
|
||
&WorkContext->Irp->IoStatus,
|
||
lfcb->DeviceObject
|
||
) ) {
|
||
|
||
//
|
||
// The fast I/O path worked. Send the data.
|
||
//
|
||
|
||
SrvFsdRestartReadBulk( WorkContext );
|
||
return SmbStatusInProgress;
|
||
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
|
||
|
||
}
|
||
|
||
//
|
||
// The fast I/O path failed, so we need to use a regular copy
|
||
// I/O request.
|
||
//
|
||
|
||
minorFunction = 0;
|
||
WorkContext->FsdRestartRoutine = SrvFsdRestartReadBulk;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
goto do_copy_read;
|
||
}
|
||
|
||
} // read fits in SMB buffer & not compressed?
|
||
|
||
//
|
||
// 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
|
||
readAddress, // buffer address
|
||
readLength, // buffer length
|
||
mdl, // MDL address
|
||
offset, // byte offset
|
||
key // lock key
|
||
);
|
||
|
||
//
|
||
// Pass the request to the file system.
|
||
//
|
||
|
||
DEBUG WorkContext->FspRestartRoutine = NULL;
|
||
|
||
(VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
|
||
|
||
//
|
||
// The read has been started. Control will return to the restart
|
||
// routine when the read completes.
|
||
//
|
||
|
||
IF_SMB_DEBUG(BULK2) KdPrint(( "SrvSmbReadBulk complete.\n" ));
|
||
return SmbStatusInProgress;
|
||
|
||
} // SrvSmbReadBulk
|
||
|
||
|
||
VOID SRVFASTCALL
|
||
SrvFsdRestartReadBulk (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes file read completion for a ReadBulk SMB.
|
||
|
||
This routine may be called in the FSD or the FSP.
|
||
|
||
*** This routine cannot look at the original ReadBulk request!
|
||
This is because the read data may have overlaid the request.
|
||
All necessary information from the request must be stored
|
||
in WorkContext->Parameters.ReadBulk
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
describing server-specific context for the request.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRESP_READ_BULK response;
|
||
|
||
BOOLEAN mdlRead;
|
||
NTSTATUS status;
|
||
PRFCB rfcb;
|
||
PCHAR readAddress;
|
||
ULONG readLength;
|
||
ULONG fragmentSize;
|
||
PIRP irp = WorkContext->Irp;
|
||
PMDL mdl;
|
||
|
||
UNLOCKABLE_CODE( 8FIL );
|
||
|
||
IF_SMB_DEBUG(BULK2) SrvPrint0( " - SrvFsdRestartReadBulk\n" );
|
||
|
||
WorkContext->Parameters.ReadBulk.CompressionTechnology =
|
||
CompressionTechnologyNone;
|
||
|
||
//
|
||
// If we just completed an MDL read, we need to remember the address
|
||
// of the first MDL so that we can give it back to the cache manager
|
||
// when we're done.
|
||
//
|
||
|
||
mdlRead = WorkContext->Parameters.ReadBulk.MdlRead;
|
||
|
||
if ( mdlRead ) {
|
||
WorkContext->Parameters.ReadBulk.CurrentMdl = NULL;
|
||
mdl = irp->MdlAddress;
|
||
WorkContext->Parameters.ReadBulk.FirstMdl = mdl;
|
||
}
|
||
|
||
//
|
||
// Get the file pointer.
|
||
//
|
||
|
||
rfcb = WorkContext->Rfcb;
|
||
IF_SMB_DEBUG(BULK2) {
|
||
SrvPrint2( " connection 0x%lx, RFCB 0x%lx\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;
|
||
|
||
if ( !NT_SUCCESS(status) && (status != STATUS_END_OF_FILE) ) {
|
||
|
||
IF_DEBUG(ERRORS) SrvPrint1( "Read failed: %X\n", status );
|
||
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
||
WorkContext->FspRestartRoutine = SrvFsdRestartReadBulk;
|
||
SrvQueueWorkToFsp( WorkContext );
|
||
return;
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
respond:
|
||
if ( mdlRead ) {
|
||
SrvFsdSendResponse2( WorkContext, RestartMdlReadBulkComplete );
|
||
} else {
|
||
WorkContext->ResponseBuffer->DataLength =
|
||
(ULONG)( (PCHAR)WorkContext->ResponseParameters -
|
||
(PCHAR)WorkContext->ResponseHeader );
|
||
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
|
||
SRV_START_SEND_2(
|
||
WorkContext,
|
||
RestartCopyReadBulkComplete,
|
||
NULL,
|
||
NULL );
|
||
}
|
||
IF_SMB_DEBUG(BULK2) SrvPrint0( "SrvFsdRestartReadBulk complete\n" );
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Get the amount of data actually read.
|
||
//
|
||
|
||
if ( status == STATUS_END_OF_FILE ) {
|
||
|
||
//
|
||
// The read started beyond the end of the file.
|
||
//
|
||
|
||
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.
|
||
//
|
||
|
||
readLength = 0;
|
||
while ( mdl != NULL ) {
|
||
readLength += MmGetMdlByteCount(mdl);
|
||
mdl = mdl->Next;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Copy read. The I/O status block has the length.
|
||
//
|
||
|
||
readLength = irp->IoStatus.Information;
|
||
|
||
}
|
||
|
||
//
|
||
// Update the file position.
|
||
//
|
||
|
||
WorkContext->Rfcb->CurrentPosition =
|
||
WorkContext->Parameters.ReadBulk.Offset.LowPart + readLength;
|
||
|
||
//
|
||
// Update statistics.
|
||
//
|
||
|
||
UPDATE_READ_STATS( WorkContext, readLength );
|
||
|
||
WorkContext->Parameters.ReadBulk.FirstMessage = TRUE;
|
||
|
||
response = (PRESP_READ_BULK)WorkContext->ResponseParameters;
|
||
|
||
//
|
||
// Build the response message. (Note that if no data was read, we
|
||
// return a byte count of 0 -- we don't add padding.)
|
||
//
|
||
|
||
response->WordCount = 12;
|
||
|
||
response->Flags = 0;
|
||
response->CompressionTechnology = CompressionTechnologyNone;
|
||
|
||
SmbPutUshort( &response->DataOffset, 0 );
|
||
SmbPutUlong( &response->Remaining, 0 );
|
||
|
||
if ( readLength == 0 ) {
|
||
SmbPutUlong( &response->Offset.LowPart, 0 );
|
||
SmbPutUlong( &response->Offset.HighPart, 0 );
|
||
SmbPutUlong( &response->DataCount, 0 );
|
||
SmbPutUlong( &response->Count, 0 );
|
||
|
||
WorkContext->ResponseParameters = NEXT_LOCATION(
|
||
response,
|
||
RESP_READ_BULK,
|
||
0 );
|
||
goto respond;
|
||
}
|
||
|
||
fragmentSize = WorkContext->Parameters.ReadBulk.FragmentSize;
|
||
|
||
WorkContext->Parameters.ReadBulk.RemainingLength = readLength;
|
||
|
||
//
|
||
// Build the first part of a successfull response - assume 1 response.
|
||
//
|
||
|
||
SmbPutUlong( &response->Offset.LowPart, WorkContext->Parameters.ReadBulk.Offset.LowPart );
|
||
SmbPutUlong( &response->Offset.HighPart, WorkContext->Parameters.ReadBulk.Offset.HighPart );
|
||
SmbPutUlong( &response->DataCount, readLength );
|
||
SmbPutUlong( &response->Count, readLength );
|
||
|
||
//
|
||
// We will use two MDLs to describe the packet we're sending -- one
|
||
// for the header and parameters, the other for the data. So we
|
||
// set the "response length" to not include the data. This is what
|
||
// SrvStartSend uses to set the first MDL's length.
|
||
//
|
||
// Handling of the second MDL varies depending on whether we did a
|
||
// copy read or an MDL read.
|
||
//
|
||
|
||
WorkContext->ResponseParameters = NEXT_LOCATION(
|
||
response,
|
||
RESP_READ_BULK,
|
||
0 // round up to longword address
|
||
);
|
||
|
||
WorkContext->ResponseBuffer->Mdl->ByteCount =
|
||
READ_BULK_BUFFER_OFFSET;
|
||
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
|
||
|
||
//
|
||
// Send response data.
|
||
//
|
||
|
||
ASSERT( WorkContext->ResponseBuffer->Mdl->Next == NULL );
|
||
|
||
irp->Cancel = FALSE;
|
||
|
||
if ( mdlRead ) {
|
||
|
||
//
|
||
// This was an MDL read.
|
||
//
|
||
|
||
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
||
|
||
//
|
||
// Check max message size
|
||
//
|
||
|
||
if ( fragmentSize < readLength ) {
|
||
|
||
//
|
||
// We'll have to send multiple fragments
|
||
//
|
||
|
||
WorkContext->ResponseBuffer->Mdl->Next =
|
||
WorkContext->ResponseBuffer->PartialMdl;
|
||
WorkContext->ResponseBuffer->PartialMdl->Next = NULL;
|
||
|
||
WorkContext->Parameters.ReadBulk.CurrentMdl =
|
||
WorkContext->Parameters.ReadBulk.FirstMdl;
|
||
WorkContext->Parameters.ReadBulk.CurrentMdlOffset = 0;
|
||
|
||
(VOID) SendMdlReadBulkFragment( NULL, irp, WorkContext );
|
||
|
||
} else {
|
||
|
||
//
|
||
// We only need to send 1 response
|
||
//
|
||
|
||
WorkContext->ResponseBuffer->Mdl->Next =
|
||
WorkContext->Parameters.ReadBulk.FirstMdl;
|
||
|
||
WorkContext->ResponseBuffer->DataLength =
|
||
readLength + READ_BULK_BUFFER_OFFSET;
|
||
|
||
WorkContext->FspRestartRoutine = RestartMdlReadBulkComplete;
|
||
SrvStartSend2( WorkContext, SrvQueueWorkToFspAtSendCompletion );
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// This was a copy read.
|
||
//
|
||
|
||
//
|
||
// Check max message size
|
||
//
|
||
|
||
if ( fragmentSize < readLength ) {
|
||
|
||
//
|
||
// We'll have to send multiple responses
|
||
//
|
||
|
||
WorkContext->ResponseBuffer->Mdl->Next =
|
||
WorkContext->ResponseBuffer->PartialMdl;
|
||
WorkContext->ResponseBuffer->PartialMdl->Next = NULL;
|
||
|
||
(VOID) SendCopyReadBulkFragment( NULL, irp, WorkContext );
|
||
|
||
} else {
|
||
|
||
//
|
||
// We only need to send 1 response
|
||
//
|
||
|
||
//
|
||
// Build a partial MDL describing the data.
|
||
//
|
||
|
||
IoBuildPartialMdl(
|
||
WorkContext->Parameters.ReadBulk.BulkBufferMdl,
|
||
WorkContext->ResponseBuffer->PartialMdl,
|
||
WorkContext->Parameters.ReadBulk.NextFragmentAddress,
|
||
readLength
|
||
);
|
||
|
||
WorkContext->ResponseBuffer->Mdl->Next =
|
||
WorkContext->ResponseBuffer->PartialMdl;
|
||
|
||
WorkContext->ResponseBuffer->DataLength =
|
||
readLength + READ_BULK_BUFFER_OFFSET;
|
||
|
||
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
||
DEBUG WorkContext->FspRestartRoutine = NULL;
|
||
|
||
SrvStartSend2( WorkContext, RestartCopyReadBulkComplete );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
} // SrvFsdRestartReadBulk
|
||
|
||
|
||
VOID SRVFASTCALL
|
||
SrvFsdRestartReadBulkC (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes file read completion for a ReadBulk SMB. This routine is
|
||
similar to SrvFsdRestartReadBulk, except is handles compressed version
|
||
of the read data. If this code ends up being as simple as I suspect,
|
||
then it should be merged directly into SrvFsdRestartReadBulk!
|
||
|
||
This routine may be called in the FSD or the FSP.
|
||
|
||
*** This routine cannot look at the original ReadBulk request!
|
||
This is because the read data may have overlaid the request.
|
||
All necessary information from the request must be stored
|
||
in WorkContext->Parameters.ReadBulk
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
describing server-specific context for the request.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRESP_READ_BULK response;
|
||
|
||
BOOLEAN mdlRead;
|
||
UCHAR minorFunction;
|
||
NTSTATUS status;
|
||
PRFCB rfcb;
|
||
PCHAR readAddress;
|
||
ULONG readLength;
|
||
ULONG fragmentSize;
|
||
PIRP irp = WorkContext->Irp;
|
||
PMDL mdl;
|
||
PCOMPRESSED_DATA_INFO compressedDataInfo;
|
||
ULONG i;
|
||
ULONG dataLength;
|
||
ULONG cdiLength;
|
||
|
||
UNLOCKABLE_CODE( 8FIL );
|
||
|
||
IF_SMB_DEBUG(BULK2) SrvPrint0( " - SrvFsdRestartReadBulk\n" );
|
||
|
||
irp->Tail.Overlay.AuxiliaryBuffer = NULL;
|
||
|
||
//
|
||
// If we just completed an MDL read, we need to remember the address
|
||
// of the first MDL so that we can give it back to the cache manager
|
||
// when we're done.
|
||
//
|
||
|
||
mdlRead = WorkContext->Parameters.ReadBulk.MdlRead;
|
||
|
||
if ( mdlRead ) {
|
||
RtlMoveMemory( (PCHAR)WorkContext->ResponseHeader +
|
||
READ_BULK_BUFFER_OFFSET,
|
||
WorkContext->Parameters.ReadBulk.Aux.Buffer,
|
||
WorkContext->Parameters.ReadBulk.Aux.Length );
|
||
WorkContext->Parameters.ReadBulk.CurrentMdl = NULL;
|
||
mdl = irp->MdlAddress;
|
||
WorkContext->Parameters.ReadBulk.FirstMdl = mdl;
|
||
minorFunction = IRP_MN_MDL;
|
||
} else {
|
||
mdl = WorkContext->Parameters.ReadBulk.BulkBufferMdl;
|
||
minorFunction = 0;
|
||
}
|
||
|
||
//
|
||
// Get the file pointer.
|
||
//
|
||
|
||
rfcb = WorkContext->Rfcb;
|
||
IF_SMB_DEBUG(BULK2) {
|
||
SrvPrint2( " connection 0x%lx, RFCB 0x%lx\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 = irp->IoStatus.Status;
|
||
|
||
if ( status == STATUS_INVALID_READ_MODE ) {
|
||
|
||
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
||
WorkContext->FspRestartRoutine = SrvFsdRestartReadBulkC;
|
||
SrvQueueWorkToFsp( WorkContext );
|
||
return;
|
||
}
|
||
|
||
//
|
||
// We failed! This should not happen very often. Do an uncompressed
|
||
// IRP read instead.
|
||
//
|
||
// This would happen when either the file size was truncated below
|
||
// 4KB or the file attribute changed from compressed to uncompressed.
|
||
//
|
||
//
|
||
// Build the uncompressed read request, reusing the receive IRP.
|
||
//
|
||
|
||
SrvBuildReadOrWriteRequest(
|
||
irp, // input IRP address
|
||
rfcb->Lfcb->FileObject, // target file object
|
||
WorkContext, // context
|
||
IRP_MJ_READ, // major function code
|
||
minorFunction, // minor function code
|
||
WorkContext->Parameters.ReadBulk.NextFragmentAddress, // address
|
||
WorkContext->Parameters.ReadBulk.RemainingCount, // buff length
|
||
mdl, // MDL address
|
||
WorkContext->Parameters.ReadBulk.Offset, // byte offset
|
||
WorkContext->Parameters.ReadBulk.Key // lock key
|
||
);
|
||
|
||
|
||
WorkContext->Parameters.ReadBulk.CompressionTechnology =
|
||
CompressionTechnologyNone;
|
||
WorkContext->Parameters.ReadBulk.ReadBufferMdl = NULL;
|
||
WorkContext->FsdRestartRoutine = SrvFsdRestartReadBulk;
|
||
WorkContext->FspRestartRoutine = NULL;
|
||
|
||
//
|
||
// We get really paranoid here. Reset the file attribute and file size.
|
||
//
|
||
// We could also do a query file info to get the current info instead
|
||
// of getting paranoid! However, it is expected that the return values
|
||
// would indicate that we can no longer do compressed reads. This works
|
||
// just as well.
|
||
//
|
||
//RNGFIX - add code to set the file attribute for the file to be uncompressed
|
||
// and file size to 1. Basically turning off uncompressed reads until
|
||
// the next open.
|
||
|
||
//
|
||
// Pass the request to the file system.
|
||
//
|
||
|
||
(VOID)IoCallDriver( rfcb->Lfcb->DeviceObject, irp );
|
||
|
||
return;
|
||
}
|
||
|
||
if ( !NT_SUCCESS(status) && (status != STATUS_END_OF_FILE) ) {
|
||
|
||
IF_DEBUG(ERRORS) SrvPrint1( "Read failed: %X\n", status );
|
||
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
||
WorkContext->FspRestartRoutine = SrvFsdRestartReadBulkC;
|
||
SrvQueueWorkToFsp( WorkContext );
|
||
return;
|
||
}
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
respond:
|
||
if ( mdlRead ) {
|
||
SrvFsdSendResponse2( WorkContext, RestartMdlReadBulkComplete );
|
||
} else {
|
||
WorkContext->ResponseBuffer->DataLength =
|
||
(ULONG)( (PCHAR)WorkContext->ResponseParameters -
|
||
(PCHAR)WorkContext->ResponseHeader );
|
||
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
|
||
SRV_START_SEND_2(
|
||
WorkContext,
|
||
RestartCopyReadBulkComplete,
|
||
NULL,
|
||
NULL );
|
||
}
|
||
IF_SMB_DEBUG(BULK2) SrvPrint0( "SrvFsdRestartReadBulkC complete\n" );
|
||
return;
|
||
}
|
||
|
||
response = (PRESP_READ_BULK)WorkContext->ResponseParameters;
|
||
|
||
//
|
||
// Get the amount of data actually read.
|
||
//
|
||
|
||
compressedDataInfo = WorkContext->Parameters.ReadBulk.Aux.Buffer;
|
||
|
||
if ( ( status == STATUS_END_OF_FILE ) ||
|
||
( WorkContext->Irp->IoStatus.Information == 0 ) ) {
|
||
|
||
//
|
||
// The read started beyond the end of the file.
|
||
//
|
||
|
||
readLength = 0;
|
||
dataLength = 0;
|
||
cdiLength = 0;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Scan the Compression Info structure tallying the sizes of
|
||
// each chunk.
|
||
//
|
||
// readLength is the size of the data uncompressed.
|
||
// dataLength is the size of the data compressed, plus the allocated
|
||
// CDI structure if this is a Copy Read.
|
||
// cdiLength is the size of the CDI. It is zero for Copy Reads.
|
||
//
|
||
|
||
ASSERT( compressedDataInfo->NumberOfChunks <= 256 );
|
||
|
||
readLength = irp->IoStatus.Information;
|
||
|
||
if ( mdlRead ) {
|
||
// The CDI accounts for 1 Chunk already, so subtract 1.
|
||
cdiLength = sizeof(COMPRESSED_DATA_INFO) +
|
||
(sizeof(ULONG) * (compressedDataInfo->NumberOfChunks - 1));
|
||
dataLength = 0;
|
||
SmbPutUshort( &response->DataOffset, (USHORT)cdiLength );
|
||
} else {
|
||
cdiLength = 0;
|
||
dataLength = WorkContext->Parameters.ReadBulk.Aux.Length;
|
||
SmbPutUshort( &response->DataOffset,
|
||
(USHORT)WorkContext->Parameters.ReadBulk.Aux.Length );
|
||
}
|
||
|
||
for ( i = 0; i < compressedDataInfo->NumberOfChunks; i++ ) {
|
||
dataLength += compressedDataInfo->CompressedChunkSizes[i];
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Update the file position.
|
||
//
|
||
|
||
WorkContext->Rfcb->CurrentPosition =
|
||
WorkContext->Parameters.ReadBulk.Offset.LowPart + readLength;
|
||
|
||
//
|
||
// Update statistics.
|
||
//
|
||
|
||
UPDATE_READ_STATS( WorkContext, readLength );
|
||
|
||
//
|
||
// Build the response message. (Note that if no data was read, we
|
||
// return a byte count of 0 -- we don't add padding.)
|
||
//
|
||
|
||
response->WordCount = 12;
|
||
|
||
response->CompressionTechnology =
|
||
WorkContext->Parameters.ReadBulk.CompressionTechnology;
|
||
|
||
SmbPutUlong( &response->Remaining, 0 );
|
||
|
||
if ( readLength == 0 ) {
|
||
response->Flags = 0;
|
||
SmbPutUlong( &response->Offset.LowPart, 0 );
|
||
SmbPutUlong( &response->Offset.HighPart, 0 );
|
||
SmbPutUshort( &response->DataOffset, 0 );
|
||
SmbPutUlong( &response->DataCount, 0 );
|
||
SmbPutUlong( &response->Count, 0 );
|
||
|
||
WorkContext->ResponseParameters = NEXT_LOCATION(
|
||
response,
|
||
RESP_READ_BULK,
|
||
0 );
|
||
goto respond;
|
||
}
|
||
|
||
fragmentSize = WorkContext->Parameters.ReadBulk.FragmentSize;
|
||
|
||
//
|
||
// Build the first part of a successfull response - assume 1 response.
|
||
//
|
||
|
||
response->Flags = READ_BULK_COMPRESSED_DATA_INFO;
|
||
|
||
SmbPutUlong( &response->Offset.LowPart, WorkContext->Parameters.ReadBulk.Offset.LowPart );
|
||
SmbPutUlong( &response->Offset.HighPart, WorkContext->Parameters.ReadBulk.Offset.HighPart );
|
||
SmbPutUlong( &response->DataCount, dataLength + cdiLength);
|
||
SmbPutUlong( &response->Count, readLength );
|
||
|
||
//
|
||
// We will use two MDLs to describe the packet we're sending -- one
|
||
// for the header and parameters, the other for the data. So we
|
||
// set the "response length" to not include the data. This is what
|
||
// SrvStartSend uses to set the first MDL's length.
|
||
//
|
||
// Handling of the second MDL varies depending on whether we did a
|
||
// copy read or an MDL read.
|
||
//
|
||
|
||
WorkContext->ResponseParameters = NEXT_LOCATION(
|
||
response,
|
||
RESP_READ_BULK,
|
||
0
|
||
);
|
||
|
||
WorkContext->ResponseBuffer->Mdl->ByteCount = READ_BULK_BUFFER_OFFSET +
|
||
cdiLength;
|
||
|
||
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
|
||
|
||
WorkContext->Parameters.ReadBulk.RemainingLength = dataLength;
|
||
|
||
//
|
||
// Send response data.
|
||
//
|
||
|
||
ASSERT( WorkContext->ResponseBuffer->Mdl->Next == NULL );
|
||
|
||
irp->Cancel = FALSE;
|
||
|
||
if ( mdlRead ) {
|
||
|
||
//
|
||
// This was an MDL read.
|
||
//
|
||
|
||
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
||
|
||
//
|
||
// Check max message size
|
||
//
|
||
|
||
if ( fragmentSize < dataLength ) {
|
||
|
||
//
|
||
// We'll have to send multiple fragments
|
||
//
|
||
|
||
WorkContext->ResponseBuffer->Mdl->Next =
|
||
WorkContext->ResponseBuffer->PartialMdl;
|
||
WorkContext->ResponseBuffer->PartialMdl->Next = NULL;
|
||
|
||
WorkContext->Parameters.ReadBulk.CurrentMdl =
|
||
WorkContext->Parameters.ReadBulk.FirstMdl;
|
||
WorkContext->Parameters.ReadBulk.CurrentMdlOffset = 0;
|
||
|
||
(VOID) SendMdlReadBulkFragment( NULL, irp, WorkContext );
|
||
|
||
} else {
|
||
|
||
//
|
||
// We only need to send 1 response
|
||
//
|
||
|
||
WorkContext->ResponseBuffer->Mdl->Next =
|
||
WorkContext->Parameters.ReadBulk.FirstMdl;
|
||
|
||
WorkContext->ResponseBuffer->DataLength =
|
||
dataLength + cdiLength + READ_BULK_BUFFER_OFFSET;
|
||
|
||
WorkContext->FspRestartRoutine = RestartMdlReadBulkComplete;
|
||
SrvStartSend2( WorkContext, SrvQueueWorkToFspAtSendCompletion );
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// This was a copy read.
|
||
//
|
||
|
||
//
|
||
// Check max message size
|
||
//
|
||
|
||
if ( fragmentSize < dataLength ) {
|
||
|
||
//
|
||
// We'll have to send multiple responses
|
||
//
|
||
|
||
WorkContext->Parameters.ReadBulk.RemainingLength = dataLength;
|
||
|
||
WorkContext->ResponseBuffer->Mdl->Next =
|
||
WorkContext->ResponseBuffer->PartialMdl;
|
||
WorkContext->ResponseBuffer->PartialMdl->Next = NULL;
|
||
|
||
(VOID) SendCopyReadBulkFragment( NULL, irp, WorkContext );
|
||
|
||
} else {
|
||
|
||
//
|
||
// We only need to send 1 response (COMPRESSED!)
|
||
//
|
||
|
||
//
|
||
// Build a partial MDL describing the data.
|
||
//
|
||
|
||
IoBuildPartialMdl(
|
||
WorkContext->Parameters.ReadBulk.BulkBufferMdl,
|
||
WorkContext->ResponseBuffer->PartialMdl,
|
||
WorkContext->Parameters.ReadBulk.NextFragmentAddress,
|
||
dataLength
|
||
);
|
||
|
||
WorkContext->ResponseBuffer->Mdl->Next =
|
||
WorkContext->ResponseBuffer->PartialMdl;
|
||
|
||
WorkContext->ResponseBuffer->DataLength =
|
||
dataLength + READ_BULK_BUFFER_OFFSET;
|
||
|
||
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
||
DEBUG WorkContext->FspRestartRoutine = NULL;
|
||
|
||
SrvStartSend2( WorkContext, RestartCopyReadBulkComplete );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
} // SrvFsdRestartReadBulkC
|
||
|
||
|
||
VOID SRVFASTCALL
|
||
SendMdlReadBulkFragment2(
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine description:
|
||
|
||
Stub to call real routine.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
representing the work item.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
(VOID) SendMdlReadBulkFragment( NULL, WorkContext->Irp, WorkContext );
|
||
|
||
} // SendMdlReadBulkFragment2
|
||
|
||
|
||
NTSTATUS
|
||
SendMdlReadBulkFragment (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine descritption:
|
||
|
||
Sends a Read Bulk response fragment when an MDL read was used.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to target device object for the request - not used.
|
||
|
||
Irp - Pointer to the I/O Request Packet.
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
representing the work item.
|
||
|
||
Return Value:
|
||
|
||
Status of this request.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRESP_READ_BULK response;
|
||
PIO_COMPLETION_ROUTINE sendCompletionRoutine;
|
||
|
||
ULONG fragmentSize;
|
||
ULONG remainingLength;
|
||
LARGE_INTEGER offset;
|
||
PCHAR fragmentAddress;
|
||
PMDL mdl;
|
||
ULONG mdlOffset;
|
||
ULONG partialLength;
|
||
ULONG lengthNeeded;
|
||
PCHAR startVa;
|
||
PCHAR systemVa;
|
||
|
||
UNLOCKABLE_CODE( 8FIL );
|
||
|
||
//
|
||
// Check the status of the send completion.
|
||
//
|
||
|
||
CHECK_SEND_COMPLETION_STATUS( Irp->IoStatus.Status );
|
||
|
||
//
|
||
// Turn off cancel boolean
|
||
//
|
||
|
||
Irp->Cancel = FALSE;
|
||
|
||
//
|
||
// Get context.
|
||
//
|
||
|
||
fragmentSize = WorkContext->Parameters.ReadBulk.FragmentSize,
|
||
remainingLength = WorkContext->Parameters.ReadBulk.RemainingLength;
|
||
offset.QuadPart = WorkContext->Parameters.ReadBulk.Offset.QuadPart;
|
||
|
||
//
|
||
// If the amount left to send is less than the fragment size, only
|
||
// send the remaining amount. Update the remaining amount.
|
||
//
|
||
|
||
if ( remainingLength < fragmentSize ) {
|
||
fragmentSize = remainingLength;
|
||
}
|
||
ASSERT( fragmentSize != 0 );
|
||
remainingLength -= fragmentSize;
|
||
|
||
//
|
||
// Build the response parameters.
|
||
//
|
||
|
||
response = (PRESP_READ_BULK)(WorkContext->ResponseHeader + 1);
|
||
SmbPutUlong( &response->Remaining, remainingLength );
|
||
SmbPutUlong( &response->Offset.LowPart, offset.LowPart );
|
||
SmbPutUlong( &response->Offset.HighPart, offset.HighPart );
|
||
SmbPutUlong( &response->DataCount, fragmentSize );
|
||
|
||
//
|
||
// If there is nothing left to send after this message, then indicate
|
||
// that this message has all of the remaining data (ie RemainingCount).
|
||
//
|
||
|
||
if ( remainingLength == 0 ) {
|
||
SmbPutUlong( &response->Count, WorkContext->Parameters.ReadBulk.RemainingCount );
|
||
} else {
|
||
SmbPutUlong( &response->Count, fragmentSize );
|
||
WorkContext->Parameters.ReadBulk.RemainingCount -= fragmentSize;
|
||
}
|
||
|
||
//
|
||
// First, clean up from previous mdl chain.
|
||
//
|
||
|
||
mdl = WorkContext->ResponseBuffer->PartialMdl;
|
||
while ( mdl != NULL ) {
|
||
MmPrepareMdlForReuse( mdl );
|
||
mdl = mdl->Next;
|
||
}
|
||
|
||
//
|
||
// If the current MDL doesn't describe all of the data we need to
|
||
// send, we need to play some games.
|
||
//
|
||
|
||
WorkContext->ResponseBuffer->PartialMdl->Next = NULL;
|
||
|
||
mdl = WorkContext->Parameters.ReadBulk.CurrentMdl;
|
||
startVa = MmGetMdlVirtualAddress( mdl );
|
||
mdlOffset = WorkContext->Parameters.ReadBulk.CurrentMdlOffset;
|
||
partialLength = MmGetMdlByteCount(mdl) - mdlOffset;
|
||
|
||
if ( partialLength >= fragmentSize ) {
|
||
|
||
//
|
||
// The current MDL has all of the data we need to send. Build
|
||
// a partial MDL describing that data.
|
||
//
|
||
|
||
IoBuildPartialMdl(
|
||
mdl,
|
||
WorkContext->ResponseBuffer->PartialMdl,
|
||
startVa + mdlOffset,
|
||
fragmentSize
|
||
);
|
||
|
||
//
|
||
// Indicate how much data we're taking out of the current MDL.
|
||
//
|
||
|
||
partialLength = fragmentSize;
|
||
|
||
} else {
|
||
|
||
|
||
//
|
||
// The data is spread over multiple Mdls. We'll carve up the
|
||
// WorkContext data buffer and use that for mdls (temporarily).
|
||
//
|
||
|
||
PMDL prevMdl;
|
||
PMDL curMdl;
|
||
#if DBG
|
||
ULONG mdlCount = 0;
|
||
#endif
|
||
|
||
ASSERT( WorkContext->ResponseBuffer->PartialMdl->Next == NULL );
|
||
|
||
//
|
||
// Calculate number of bytes needed beyond the first mdl.
|
||
//
|
||
|
||
lengthNeeded = fragmentSize - partialLength;
|
||
|
||
ASSERT( lengthNeeded != 0 );
|
||
ASSERT( mdl->Next != NULL );
|
||
|
||
ASSERT( WorkContext->ResponseBuffer->PartialMdl->Next == NULL );
|
||
|
||
fragmentAddress = startVa + mdlOffset;
|
||
|
||
IoBuildPartialMdl(
|
||
mdl,
|
||
WorkContext->ResponseBuffer->PartialMdl,
|
||
fragmentAddress,
|
||
partialLength
|
||
);
|
||
|
||
prevMdl = WorkContext->ResponseBuffer->PartialMdl;
|
||
curMdl = (PMDL)(((ULONG)WorkContext->ResponseBuffer->Buffer +
|
||
READ_BULK_BUFFER_OFFSET + 7) & ~7);
|
||
|
||
do {
|
||
//
|
||
// Link in next mdl.
|
||
//
|
||
|
||
prevMdl->Next = curMdl;
|
||
prevMdl = curMdl;
|
||
|
||
#if DBG
|
||
mdlCount++;
|
||
|
||
//
|
||
// We could probably build about 40 full size Mdl's in the
|
||
// receive buffer. But's let's be conservative here.
|
||
// Assuming worst case of only 1 page per Mdl, we could handle
|
||
// about 120KB worth of buffer size with this mdl, plus what is
|
||
// mapped by the regular partial mdl. So, conservatively we could
|
||
// handle a 240KB fragment size.
|
||
//
|
||
|
||
ASSERT( mdlCount < 30 );
|
||
#endif
|
||
|
||
//
|
||
// Move to next source mdl.
|
||
//
|
||
|
||
mdl = mdl->Next;
|
||
ASSERT( mdl != NULL );
|
||
|
||
//
|
||
// Calculate how much we can (and need to) get out of this MDL.
|
||
//
|
||
|
||
startVa = MmGetMdlVirtualAddress( mdl );
|
||
partialLength = MIN( MmGetMdlByteCount(mdl), lengthNeeded );
|
||
|
||
MmInitializeMdl( curMdl, startVa, partialLength );
|
||
|
||
IoBuildPartialMdl(
|
||
mdl,
|
||
curMdl,
|
||
startVa,
|
||
partialLength
|
||
);
|
||
|
||
lengthNeeded -= partialLength;
|
||
|
||
//
|
||
// Advance to next mdl...
|
||
//
|
||
|
||
curMdl = (PMDL)((PCHAR)curMdl + sizeof(MDL) + 7 + ( sizeof(ULONG) *
|
||
ADDRESS_AND_SIZE_TO_SPAN_PAGES(startVa, partialLength)));
|
||
|
||
//
|
||
// Round to next quadword boundary. The add of 7 was done above.
|
||
//
|
||
|
||
curMdl = (PMDL)((ULONG)curMdl & ~7);
|
||
|
||
} while ( lengthNeeded != 0 );
|
||
|
||
prevMdl->Next = NULL;
|
||
mdlOffset = 0;
|
||
}
|
||
|
||
//
|
||
// Final preparation for the send depends on whether this is the
|
||
// last fragment.
|
||
//
|
||
|
||
if ( remainingLength != 0 ) {
|
||
|
||
//
|
||
// Not done. Update the current MDL position. If we have
|
||
// finished off the current MDL, move to the next one.
|
||
//
|
||
|
||
mdlOffset += partialLength;
|
||
if ( mdlOffset >= MmGetMdlByteCount(mdl) ) {
|
||
mdl = mdl->Next;
|
||
ASSERT( mdl != NULL );
|
||
mdlOffset = 0;
|
||
}
|
||
|
||
//
|
||
// Update context. Set up to restart after the send in this
|
||
// routine. We want do this as an FSD restart routine. But
|
||
// this may recurse, if the send doesn't pend, so we may use up
|
||
// the stack. If we are running out of stack, restart here in
|
||
// the FSP.
|
||
//
|
||
|
||
WorkContext->Parameters.ReadBulk.CurrentMdl = mdl;
|
||
WorkContext->Parameters.ReadBulk.CurrentMdlOffset = (USHORT)mdlOffset;
|
||
WorkContext->Parameters.ReadBulk.RemainingLength = remainingLength;
|
||
WorkContext->Parameters.ReadBulk.Offset.QuadPart += fragmentSize;
|
||
|
||
if ( IoGetRemainingStackSize() >= STACK_THRESHOLD ) {
|
||
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
||
sendCompletionRoutine = SendMdlReadBulkFragment;
|
||
} else {
|
||
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
||
WorkContext->FspRestartRoutine = SendMdlReadBulkFragment2;
|
||
sendCompletionRoutine = SrvQueueWorkToFspAtSendCompletion;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is the last fragment. Restart in the cleanup routine.
|
||
//
|
||
|
||
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
||
WorkContext->FspRestartRoutine = RestartMdlReadBulkComplete;
|
||
sendCompletionRoutine = SrvQueueWorkToFspAtSendCompletion;
|
||
}
|
||
|
||
if ( !WorkContext->Parameters.ReadBulk.FirstMessage ) {
|
||
// Not first message - no more cdi
|
||
SmbPutUshort( &response->DataOffset, 0 );
|
||
WorkContext->ResponseBuffer->Mdl->ByteCount = READ_BULK_BUFFER_OFFSET;
|
||
response->Flags &= ~READ_BULK_COMPRESSED_DATA_INFO;
|
||
}
|
||
|
||
WorkContext->Parameters.ReadBulk.FirstMessage = FALSE;
|
||
|
||
//
|
||
// Send the fragment.
|
||
//
|
||
WorkContext->ResponseBuffer->DataLength =
|
||
fragmentSize + READ_BULK_BUFFER_OFFSET;
|
||
|
||
SrvStartSend2( WorkContext, sendCompletionRoutine );
|
||
|
||
return(STATUS_MORE_PROCESSING_REQUIRED);
|
||
|
||
} // SendMdlReadBulkFragment
|
||
|
||
|
||
VOID SRVFASTCALL
|
||
RestartMdlReadBulkComplete (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the final completion routine for Read Bulk when MDL read is
|
||
used. It is called after the send of the last fragment completes.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
describing server-specific context for the request.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PMDL mdl;
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE( );
|
||
|
||
ASSERT( WorkContext->Parameters.ReadBulk.MdlRead );
|
||
|
||
//
|
||
// If we had to partial the data, then free up any system mappings
|
||
//
|
||
|
||
mdl = WorkContext->ResponseBuffer->Mdl->Next;
|
||
|
||
if ( mdl == WorkContext->ResponseBuffer->PartialMdl ) {
|
||
while ( mdl != NULL ) {
|
||
MmPrepareMdlForReuse( mdl );
|
||
mdl = mdl->Next;
|
||
}
|
||
}
|
||
|
||
mdl = WorkContext->Parameters.ReadBulk.FirstMdl;
|
||
|
||
//
|
||
// Give the MDL back to the cache manager. (If the read failed or
|
||
// returned no data, there will be no MDL.)
|
||
//
|
||
|
||
if ( mdl != NULL ) {
|
||
//KdPrint(( "Freeing MDL chain:\n" ));
|
||
//DumpMdlChain( mdl );
|
||
if ( WorkContext->Parameters.ReadBulk.CompressionTechnology ) {
|
||
|
||
if( WorkContext->Rfcb->Lfcb->MdlReadCompleteCompressed == NULL ||
|
||
WorkContext->Rfcb->Lfcb->MdlReadCompleteCompressed(
|
||
WorkContext->Rfcb->Lfcb->FileObject,
|
||
mdl,
|
||
WorkContext->Rfcb->Lfcb->DeviceObject ) == FALSE ) {
|
||
|
||
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
|
||
mdl,
|
||
IRP_MJ_READ,
|
||
&WorkContext->Parameters.ReadBulk.Offset,
|
||
WorkContext->Parameters.ReadBulk.ReadLength
|
||
);
|
||
if( !NT_SUCCESS( status ) ) {
|
||
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
|
||
}
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
if( WorkContext->Rfcb->Lfcb->MdlReadComplete == NULL ||
|
||
WorkContext->Rfcb->Lfcb->MdlReadComplete(
|
||
WorkContext->Rfcb->Lfcb->FileObject,
|
||
mdl,
|
||
WorkContext->Rfcb->Lfcb->DeviceObject ) == FALSE ) {
|
||
|
||
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
|
||
mdl,
|
||
IRP_MJ_READ,
|
||
&WorkContext->Parameters.ReadBulk.Offset,
|
||
WorkContext->Parameters.ReadBulk.ReadLength
|
||
);
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
WorkContext->ResponseBuffer->Mdl->Next = NULL;
|
||
|
||
//
|
||
// Free the work item by dereferencing it.
|
||
//
|
||
|
||
SrvDereferenceWorkItem( WorkContext );
|
||
return;
|
||
|
||
} // RestartMdlReadBulkComplete
|
||
|
||
|
||
VOID SRVFASTCALL
|
||
SendCopyReadBulkFragment2 (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Stub to call actual routine.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
representing the work item
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE( );
|
||
|
||
(VOID) SendCopyReadBulkFragment( NULL, WorkContext->Irp, WorkContext );
|
||
|
||
} // SendCopyReadBulkFragment2
|
||
|
||
|
||
NTSTATUS
|
||
SendCopyReadBulkFragment (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
|
||
Sends a Read Bulk response fragment when copy read was used.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to target device object for the request - not used.
|
||
|
||
Irp - Pointer to the I/O Request Packet.
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
representing the work item.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRESP_READ_BULK response;
|
||
|
||
ULONG fragmentSize;
|
||
ULONG remainingLength;
|
||
LARGE_INTEGER offset;
|
||
PCHAR fragmentAddress;
|
||
|
||
PIO_COMPLETION_ROUTINE sendCompletionRoutine;
|
||
|
||
UNLOCKABLE_CODE( 8FIL );
|
||
|
||
//
|
||
// Check the status of the send completion.
|
||
//
|
||
|
||
CHECK_SEND_COMPLETION_STATUS( Irp->IoStatus.Status );
|
||
|
||
//
|
||
// Turn off cancel boolean
|
||
//
|
||
|
||
Irp->Cancel = FALSE;
|
||
|
||
//
|
||
//
|
||
// Get context.
|
||
//
|
||
|
||
fragmentSize = WorkContext->Parameters.ReadBulk.FragmentSize;
|
||
remainingLength = WorkContext->Parameters.ReadBulk.RemainingLength;
|
||
offset.QuadPart = WorkContext->Parameters.ReadBulk.Offset.QuadPart;
|
||
fragmentAddress = WorkContext->Parameters.ReadBulk.NextFragmentAddress;
|
||
|
||
//
|
||
// If the amount left to send is less than the fragment size, only
|
||
// send the remaining amount. Update the remaining amount.
|
||
//
|
||
|
||
if ( remainingLength < fragmentSize ) {
|
||
fragmentSize = remainingLength;
|
||
}
|
||
ASSERT( fragmentSize != 0 );
|
||
remainingLength -= fragmentSize;
|
||
|
||
//
|
||
// Build the response parameters.
|
||
//
|
||
|
||
response = (PRESP_READ_BULK)(WorkContext->ResponseHeader + 1);
|
||
SmbPutUlong( &response->Remaining, remainingLength );
|
||
SmbPutUlong( &response->Offset.LowPart, offset.LowPart );
|
||
SmbPutUlong( &response->Offset.HighPart, offset.HighPart );
|
||
SmbPutUlong( &response->DataCount, fragmentSize );
|
||
SmbPutUshort( &response->CompressionTechnology,
|
||
WorkContext->Parameters.ReadBulk.CompressionTechnology );
|
||
|
||
//
|
||
// Check if this was a compressed read.
|
||
//
|
||
|
||
if ( WorkContext->Parameters.ReadBulk.CompressionTechnology ) {
|
||
// Data is compressed.
|
||
//
|
||
// Check if this a secondary response. We'll need to adjust
|
||
// DataOffset, and put in the new Count field.
|
||
//
|
||
if ( !WorkContext->Parameters.ReadBulk.FirstMessage ) {
|
||
response->Flags &= ~READ_BULK_COMPRESSED_DATA_INFO; // No more CDI
|
||
SmbPutUshort( &response->DataOffset, 0 );
|
||
SmbPutUlong( &response->Count, fragmentSize );
|
||
} else {
|
||
// If this is the first message, adjust length for what remains.
|
||
SmbPutUlong( &response->Count, SmbGetUlong( &response->Count ) - remainingLength );
|
||
}
|
||
|
||
} else {
|
||
// Data is not compressed.
|
||
SmbPutUlong( &response->Count, fragmentSize );
|
||
}
|
||
|
||
//
|
||
// Set to secondary response next time through.
|
||
//
|
||
WorkContext->Parameters.ReadBulk.FirstMessage = FALSE;
|
||
|
||
MmPrepareMdlForReuse( WorkContext->ResponseBuffer->PartialMdl );
|
||
|
||
//
|
||
// Build a partial MDL describing the data.
|
||
//
|
||
|
||
IoBuildPartialMdl(
|
||
WorkContext->Parameters.ReadBulk.BulkBufferMdl,
|
||
WorkContext->ResponseBuffer->PartialMdl,
|
||
fragmentAddress,
|
||
fragmentSize
|
||
);
|
||
|
||
//
|
||
// Final preparation for the send depends on whether this is the
|
||
// last fragment.
|
||
//
|
||
|
||
if ( remainingLength != 0 ) {
|
||
|
||
//
|
||
// Not done. Update context. Set up to restart after the send
|
||
// in this routine. We want do this as an FSD restart routine.
|
||
// But this may recurse, if the send doesn't pend, so we may use
|
||
// up the stack. If we are running out of stack, restart here
|
||
// in the FSP.
|
||
//
|
||
|
||
WorkContext->Parameters.ReadBulk.RemainingLength = remainingLength;
|
||
WorkContext->Parameters.ReadBulk.Offset.QuadPart += fragmentSize;
|
||
WorkContext->Parameters.ReadBulk.NextFragmentAddress += fragmentSize;
|
||
|
||
if ( IoGetRemainingStackSize() >= STACK_THRESHOLD ) {
|
||
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
||
sendCompletionRoutine = SendCopyReadBulkFragment;
|
||
} else {
|
||
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
||
WorkContext->FspRestartRoutine = SendCopyReadBulkFragment2;
|
||
sendCompletionRoutine = SrvQueueWorkToFspAtSendCompletion;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is the last fragment. Restart in the cleanup routine.
|
||
//
|
||
//
|
||
|
||
DEBUG WorkContext->FsdRestartRoutine = NULL;
|
||
DEBUG WorkContext->FspRestartRoutine = NULL;
|
||
sendCompletionRoutine = RestartCopyReadBulkComplete;
|
||
}
|
||
|
||
//
|
||
// Send the fragment.
|
||
//
|
||
|
||
WorkContext->ResponseBuffer->DataLength =
|
||
fragmentSize + READ_BULK_BUFFER_OFFSET;
|
||
|
||
SrvStartSend2( WorkContext, sendCompletionRoutine );
|
||
|
||
return(STATUS_MORE_PROCESSING_REQUIRED);
|
||
|
||
} // SendCopyReadBulkFragment
|
||
|
||
|
||
NTSTATUS
|
||
RestartCopyReadBulkComplete (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the final completion routine for Read Bulk when copy read is
|
||
used. It is called after the send completes.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - Pointer to target device object for the request.
|
||
|
||
Irp - Pointer to I/O request packet
|
||
|
||
WorkContext - Caller-specified context parameter associated with IRP.
|
||
This is actually a pointer to a Work Context block.
|
||
|
||
Return Value:
|
||
|
||
STATUS_MORE_PROCESSING_REQUIRED.
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL oldIrql;
|
||
UNLOCKABLE_CODE( 8FIL );
|
||
|
||
//
|
||
// Check the status of the send completion.
|
||
//
|
||
|
||
CHECK_SEND_COMPLETION_STATUS( Irp->IoStatus.Status );
|
||
|
||
//
|
||
// Reset the IRP cancelled bit.
|
||
//
|
||
|
||
Irp->Cancel = FALSE;
|
||
|
||
ASSERT( !WorkContext->Parameters.ReadBulk.MdlRead );
|
||
|
||
MmPrepareMdlForReuse( WorkContext->ResponseBuffer->PartialMdl );
|
||
|
||
//
|
||
// If we allocated a separate buffer to do the read, free it and its
|
||
// release any mappings for the associated Mdl's now.
|
||
//
|
||
|
||
if ( WorkContext->Parameters.ReadBulk.BulkBuffer != NULL ) {
|
||
MmPrepareMdlForReuse( WorkContext->Parameters.ReadBulk.BulkBufferMdl );
|
||
if ( WorkContext->Parameters.ReadBulk.ReadBufferMdl ) {
|
||
MmPrepareMdlForReuse( WorkContext->Parameters.ReadBulk.ReadBufferMdl );
|
||
}
|
||
|
||
DEALLOCATE_NONPAGED_POOL( WorkContext->Parameters.ReadBulk.BulkBuffer );
|
||
}
|
||
|
||
WorkContext->ResponseBuffer->Mdl->Next = NULL;
|
||
|
||
//
|
||
// Complete and requeue the work item.
|
||
//
|
||
|
||
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
||
SrvFsdRestartSmbComplete( WorkContext );
|
||
KeLowerIrql( oldIrql );
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} // RestartCopyReadBulkComplete
|
||
|
||
|
||
|
||
|
||
|
||
//
|
||
// Write Bulk Processing Routines
|
||
//
|
||
|
||
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE
|
||
SrvSmbWriteBulk (
|
||
SMB_PROCESSOR_PARAMETERS
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes the Write Bulk SMB.
|
||
|
||
Arguments:
|
||
|
||
SMB_PROCESSOR_PARAMETERS:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
representing the work item
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSMB_HEADER header;
|
||
PREQ_WRITE_BULK request;
|
||
|
||
NTSTATUS status;
|
||
USHORT fid;
|
||
PRFCB rfcb;
|
||
PLFCB lfcb;
|
||
ULONG writeLength;
|
||
ULONG key;
|
||
LARGE_INTEGER offset;
|
||
UCHAR writeMode;
|
||
BOOLEAN writeThrough;
|
||
KIRQL oldIrql;
|
||
UCHAR sequence;
|
||
UCHAR minorFunction;
|
||
ULONG maxWriteLength;
|
||
ULONG compressedInfoLength;
|
||
ULONG dataLength;
|
||
|
||
//
|
||
// If we don't support bulk transfers, clip it off now
|
||
//
|
||
|
||
if( SrvSupportsBulkTransfer == FALSE ) {
|
||
return SrvSmbIllegalCommand( WorkContext );
|
||
}
|
||
|
||
header = WorkContext->RequestHeader;
|
||
request = (PREQ_WRITE_BULK)WorkContext->RequestParameters;
|
||
|
||
fid = SmbGetUshort( &request->Fid );
|
||
RngFastPath = FALSE; //RNGFIX - remove this later
|
||
IF_SMB_DEBUG(BULK1) {
|
||
KdPrint(( "Write Bulk request; FID 0x%lx, "
|
||
"count %ld, offset %ld\n",
|
||
fid, SmbGetUshort( &request->TotalCount ),
|
||
SmbGetUlong( &request->Offset ) ));
|
||
}
|
||
|
||
//
|
||
// Verify the FID. If verified, the RFCB is referenced and its
|
||
// address is stored in the WorkContext block, and the RFCB
|
||
// address is returned.
|
||
//
|
||
|
||
rfcb = SrvVerifyFid(
|
||
WorkContext,
|
||
fid,
|
||
TRUE,
|
||
SrvRestartSmbReceived,
|
||
&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((
|
||
"SrvSmbWriteBulk: Status %X on FID: 0x%lx\n",
|
||
status,
|
||
fid
|
||
));
|
||
}
|
||
|
||
goto error;
|
||
}
|
||
|
||
//
|
||
// The work item has been queued because a raw write is in
|
||
// progress.
|
||
//
|
||
|
||
return SmbStatusInProgress;
|
||
|
||
}
|
||
|
||
//
|
||
// Verify that the client has write access to the file via the
|
||
// specified handle.
|
||
//
|
||
|
||
if ( !rfcb->WriteAccessGranted ) {
|
||
SrvStatistics.GrantedAccessErrors++;
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint(( "SrvSmbWriteBulk: Write access not granted.\n"));
|
||
}
|
||
status = STATUS_ACCESS_DENIED;
|
||
goto error;
|
||
}
|
||
|
||
//
|
||
// If this is not a disk file, tell the client to use core write.
|
||
//
|
||
|
||
if ( (rfcb->ShareType != ShareTypeDisk) ) {
|
||
status = STATUS_SMB_USE_STANDARD;
|
||
goto error;
|
||
}
|
||
|
||
//
|
||
// Save this work context in the rfcb, if there is room. If not, return
|
||
// an error! Note: this is not zero based.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK( &rfcb->SpinLock, &oldIrql );
|
||
|
||
for ( sequence = 1; sequence <= MAX_CONCURRENT_WRITE_BULK; sequence++ ) {
|
||
|
||
if ( rfcb->WriteBulk[sequence-1] == NULL ) {
|
||
WorkContext->Parameters.WriteBulk.Sequence = sequence;
|
||
rfcb->WriteBulk[sequence-1] = WorkContext;
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK( &rfcb->SpinLock, oldIrql );
|
||
|
||
if ( sequence >= MAX_CONCURRENT_WRITE_BULK ) {
|
||
//
|
||
// We failed to find a free spot in the array. Display status and
|
||
// return failure.
|
||
//
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint((
|
||
"SrvSmbWriteBulk: Failed to find room in WriteBulk array, FID: 0x%lx\n",
|
||
fid
|
||
));
|
||
}
|
||
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto error;
|
||
}
|
||
|
||
rfcb->WrittenTo = TRUE;
|
||
|
||
//
|
||
// Get the file offset.
|
||
//
|
||
|
||
offset.LowPart = SmbGetUlong( &request->Offset.LowPart );
|
||
offset.HighPart = SmbGetUlong( &request->Offset.HighPart );
|
||
|
||
//
|
||
// Get the amount of data to write.
|
||
//
|
||
|
||
writeLength = SmbGetUlong( &request->TotalCount );
|
||
dataLength = SmbGetUlong( &request->DataCount );
|
||
|
||
//
|
||
// We'll use the receive buffer for our Irps's in a multi-packet data
|
||
// burst. We can fit about 14 Irp's into the standard receive buffer.
|
||
// We now calculate how many Irp's can fit into our receive buffer.
|
||
//
|
||
|
||
WorkContext->Parameters.WriteBulk.IrpIndex =
|
||
(UCHAR)((SrvReceiveBufferSize - sizeof(REQ_WRITE_BULK_DATA)) / SrvReceiveIrpSize);
|
||
|
||
//
|
||
// Now figure out how big of a write we can handle from the client.
|
||
//
|
||
|
||
maxWriteLength = WorkContext->Parameters.WriteBulk.IrpIndex *
|
||
MIN( WorkContext->Connection->MaximumSendSize,
|
||
SmbGetUlong( &request->MessageSize ) );
|
||
|
||
writeLength = MIN( maxWriteLength, writeLength );
|
||
|
||
dataLength = MIN( maxWriteLength, dataLength );
|
||
|
||
//
|
||
// Save context for the restart routine.
|
||
//
|
||
|
||
WorkContext->Parameters.WriteBulk.WriteLength = writeLength;
|
||
WorkContext->Parameters.WriteBulk.CompressedLength = dataLength;
|
||
WorkContext->Parameters.WriteBulk.Offset.QuadPart = offset.QuadPart;
|
||
|
||
//
|
||
// 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( &header->Pid );
|
||
|
||
WorkContext->Parameters.WriteBulk.Key = key;
|
||
|
||
lfcb = rfcb->Lfcb;
|
||
|
||
//
|
||
// Check the write through mode and set it appropriately.
|
||
//
|
||
|
||
writeMode = request->Flags;
|
||
|
||
writeThrough = (BOOLEAN)((writeMode & SMB_WMODE_WRITE_THROUGH) != 0);
|
||
|
||
if ( writeThrough && (lfcb->FileMode & FILE_WRITE_THROUGH) == 0
|
||
|| !writeThrough && (lfcb->FileMode & FILE_WRITE_THROUGH) != 0 ) {
|
||
|
||
SrvSetFileWritethroughMode( lfcb, writeThrough );
|
||
|
||
}
|
||
|
||
//
|
||
// Check if compressed write request.
|
||
//
|
||
|
||
if ( SrvSupportsCompression ) {
|
||
WorkContext->Parameters.WriteBulk.CompressionTechnology =
|
||
request->CompressionTechnology;
|
||
} else {
|
||
WorkContext->Parameters.WriteBulk.CompressionTechnology =
|
||
CompressionTechnologyNone;
|
||
}
|
||
|
||
ASSERT( CompressionTechnologyNone == 0 );
|
||
if ( WorkContext->Parameters.WriteBulk.CompressionTechnology ) {
|
||
|
||
//
|
||
// Allow for rounding the CompressedInfo data to quadword boundary.
|
||
//
|
||
|
||
compressedInfoLength = (ULONG)SmbGetUshort( &request->ByteCount );
|
||
|
||
//
|
||
// Get a buffer for our auxiliary buffer and CDI.
|
||
//
|
||
|
||
WorkContext->Parameters.WriteBulk.Aux = (PFSRTL_AUXILIARY_BUFFER)
|
||
ALLOCATE_NONPAGED_POOL(
|
||
sizeof(FSRTL_AUXILIARY_BUFFER) +
|
||
compressedInfoLength + 7,
|
||
BlockTypeDataBuffer
|
||
);
|
||
|
||
|
||
if ( WorkContext->Parameters.WriteBulk.Aux ) {
|
||
|
||
//
|
||
// We MUST copy the CDI. The CDI comes in on some arbitrary
|
||
// buffer address, but we need to make sure that it is aligned
|
||
// to at least a structure alignment (ULONG or UQUAD?).
|
||
//
|
||
//RNGFIX - we may want to round destination to quadword boundary
|
||
|
||
RtlCopyMemory( (PVOID)((PCHAR)WorkContext->Parameters.WriteBulk.Aux
|
||
+ sizeof(FSRTL_AUXILIARY_BUFFER)),
|
||
&request->Buffer,
|
||
compressedInfoLength
|
||
);
|
||
} else {
|
||
|
||
WorkContext->Parameters.WriteBulk.CompressionTechnology =
|
||
CompressionTechnologyNone;
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// Try the fast path first.
|
||
//
|
||
|
||
WorkContext->Irp->MdlAddress = NULL;
|
||
WorkContext->Irp->IoStatus.Information = 0;
|
||
|
||
//
|
||
// If the write request is positioned correctly and the file is compressed
|
||
// and the client wanted a compressed write, then let's do a compressed
|
||
// request.
|
||
//
|
||
// N.B. we can't use the 'normal' FastIo path, because we don't have the
|
||
// data yet. So if we fail on the prepare mdl write case, we'll have
|
||
// to build an Irp.
|
||
//
|
||
#if 0 // RNGFIX
|
||
DbgPrint("offset.Lp: %lx, writeLen: %lx, Compres?: %lx, OpenFileAttr: %lx\n",
|
||
offset.LowPart, writeLength, WorkContext->Parameters.WriteBulk.CompressionTechnology,
|
||
rfcb->Mfcb->NonpagedMfcb->OpenFileAttributes & FILE_ATTRIBUTE_COMPRESSED);
|
||
#endif // RNGFIX
|
||
ASSERT( CompressionTechnologyNone == 0 );
|
||
if ( SrvSupportsCompression &&
|
||
( (offset.LowPart & 0xfff) == 0 ) &&
|
||
( (writeLength & 0xfff) == 0 ) &&
|
||
( WorkContext->Parameters.WriteBulk.CompressionTechnology ) &&
|
||
( (rfcb->Mfcb->NonpagedMfcb->OpenFileAttributes & FILE_ATTRIBUTE_COMPRESSED) != 0 ) ) {
|
||
|
||
//
|
||
// If cache operations are supported on this file system, then do
|
||
// a Compressed Mdl write request!
|
||
//
|
||
|
||
if ( (lfcb->FileObject->Flags & FO_CACHE_SUPPORTED) ) {
|
||
|
||
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesAttempted );
|
||
|
||
if ( lfcb->FastIoWriteCompressed(
|
||
lfcb->FileObject,
|
||
&offset,
|
||
writeLength,
|
||
key,
|
||
NULL,
|
||
&WorkContext->Irp->MdlAddress,
|
||
&WorkContext->Irp->IoStatus,
|
||
(PVOID)(WorkContext->Parameters.WriteBulk.Aux + 1),
|
||
compressedInfoLength,
|
||
lfcb->DeviceObject
|
||
) ) {
|
||
|
||
//
|
||
// The fast I/O path worked.
|
||
//
|
||
|
||
RestartPrepareBulkMdlWrite( WorkContext );
|
||
return SmbStatusInProgress;
|
||
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesFailed );
|
||
|
||
}
|
||
|
||
minorFunction = IRP_MN_COMPRESSED | IRP_MN_MDL;
|
||
|
||
WorkContext->Parameters.WriteBulk.Aux->Flags =
|
||
FSRTL_AUXILIARY_FLAG_DEALLOCATE;
|
||
//RNGFIX ?? WorkContext->Parameters.WriteBulk.Aux->Flags = 0;
|
||
WorkContext->Parameters.WriteBulk.Aux->Buffer = (PVOID)(WorkContext->Parameters.WriteBulk.Aux + 1),
|
||
#if 0 //RNGFIX
|
||
DbgPrint("CDI: %lx, sizof(FSRTL_AUX_BUF): %lx\n",
|
||
WorkContext->Parameters.WriteBulk.Aux->Buffer, sizeof(FSRTL_AUXILIARY_BUFFER) );
|
||
#endif // RNGFIX
|
||
WorkContext->Parameters.WriteBulk.Aux->Length = compressedInfoLength;
|
||
|
||
WorkContext->Irp->Tail.Overlay.AuxiliaryBuffer =
|
||
(PVOID)WorkContext->Parameters.WriteBulk.Aux;
|
||
|
||
WorkContext->FsdRestartRoutine = SrvFsdRestartReadBulkC;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We can't do compressed, so do uncompressed write.
|
||
//
|
||
|
||
ASSERT( CompressionTechnologyNone == 0 );
|
||
if ( WorkContext->Parameters.WriteBulk.CompressionTechnology ) {
|
||
|
||
WorkContext->Parameters.WriteBulk.CompressionTechnology =
|
||
CompressionTechnologyNone;
|
||
DEALLOCATE_NONPAGED_POOL( WorkContext->Parameters.WriteBulk.Aux );
|
||
WorkContext->Parameters.WriteBulk.Aux = NULL;
|
||
|
||
}
|
||
|
||
//
|
||
// If cache operations are supported on this file system, then do it!
|
||
//
|
||
|
||
if ( lfcb->FileObject->Flags & FO_CACHE_SUPPORTED ) {
|
||
|
||
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesAttempted );
|
||
|
||
if ( lfcb->PrepareMdlWrite(
|
||
lfcb->FileObject,
|
||
&offset,
|
||
writeLength,
|
||
key,
|
||
&WorkContext->Irp->MdlAddress,
|
||
&WorkContext->Irp->IoStatus,
|
||
lfcb->DeviceObject
|
||
) ) {
|
||
|
||
//
|
||
// The fast I/O path worked.
|
||
//
|
||
|
||
RestartPrepareBulkMdlWrite( WorkContext );
|
||
return SmbStatusInProgress;
|
||
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesFailed );
|
||
|
||
}
|
||
|
||
minorFunction = IRP_MN_MDL;
|
||
|
||
}
|
||
|
||
//
|
||
// The fast I/O path failed or isn't supported. 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.
|
||
//
|
||
|
||
offset.QuadPart += WorkContext->Irp->IoStatus.Information;
|
||
writeLength -= (USHORT)WorkContext->Irp->IoStatus.Information;
|
||
|
||
SrvBuildReadOrWriteRequest(
|
||
WorkContext->Irp, // input IRP address
|
||
lfcb->FileObject, // target file object address
|
||
WorkContext, // context
|
||
IRP_MJ_WRITE, // major function code
|
||
minorFunction, // 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.
|
||
//
|
||
|
||
WorkContext->FsdRestartRoutine = RestartPrepareBulkMdlWrite;
|
||
DEBUG WorkContext->FspRestartRoutine = NULL;
|
||
|
||
(VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
|
||
|
||
//
|
||
// The MDL write has been started. When it completes, processing
|
||
// resumes at RestartPrepareBulkMdlWrite.
|
||
//
|
||
|
||
return SmbStatusInProgress;
|
||
|
||
error:
|
||
|
||
//
|
||
// There is an error of some sort. Return the error.
|
||
//
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
|
||
return SmbStatusSendResponse;
|
||
|
||
} // SrvSmbWriteBulk
|
||
|
||
|
||
VOID SRVFASTCALL
|
||
RestartPrepareBulkMdlWrite (
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes the completion of an Mdl Write Bulk request to the local File
|
||
System. On success, we should get back a pointer to an Mdl chain.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
representing the work item
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRESP_WRITE_BULK response;
|
||
PREQ_WRITE_BULK request;
|
||
|
||
PRFCB rfcb;
|
||
PLFCB lfcb;
|
||
LARGE_INTEGER offset;
|
||
ULONG writeLength;
|
||
ULONG sendSize;
|
||
NTSTATUS status;
|
||
UCHAR sequence;
|
||
|
||
//RNGFIX ?? WorkContext->Parameters.WriteBulk.Aux = NULL;
|
||
|
||
response = (PRESP_WRITE_BULK)WorkContext->ResponseParameters;
|
||
request = (PREQ_WRITE_BULK)WorkContext->RequestParameters;
|
||
|
||
sendSize = SmbGetUlong( &request->MessageSize );
|
||
|
||
rfcb = WorkContext->Rfcb;
|
||
|
||
sequence = WorkContext->Parameters.WriteBulk.Sequence;
|
||
|
||
//
|
||
// If the MDL write preparation succeeded, send a response to 'let it rip'.
|
||
// Otherwise, return failure and complete the work item.
|
||
//
|
||
|
||
status = WorkContext->Irp->IoStatus.Status;
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
#if 0 //RNGFIX
|
||
DbgPrint("WriteBulk: success!, writeLen: %lx, stat.Info: %lx, Compr?: %lx\n",
|
||
WorkContext->Parameters.WriteBulk.WriteLength,
|
||
WorkContext->Irp->IoStatus.Information,
|
||
WorkContext->Parameters.WriteBulk.CompressionTechnology );
|
||
DbgPrint("Mdl: %lx, Mdl->Nxt: %lx, addr: %lx\n",
|
||
WorkContext->Irp->MdlAddress, WorkContext->Irp->MdlAddress->Next,
|
||
MmGetMdlVirtualAddress( WorkContext->Irp->MdlAddress ) );
|
||
#endif //RNGFIX
|
||
ASSERT( WorkContext->Parameters.WriteBulk.WriteLength ==
|
||
WorkContext->Irp->IoStatus.Information );
|
||
|
||
//
|
||
// Setup the write length dependent upon whether the data is compressed
|
||
//
|
||
|
||
ASSERT( CompressionTechnologyNone == 0 );
|
||
if ( WorkContext->Parameters.WriteBulk.CompressionTechnology ) {
|
||
|
||
writeLength = WorkContext->Parameters.WriteBulk.CompressedLength;
|
||
|
||
} else {
|
||
|
||
writeLength = WorkContext->Parameters.WriteBulk.WriteLength;
|
||
|
||
}
|
||
|
||
//
|
||
// Finish initializing the WriteBulk portion of the WorkContext.
|
||
//
|
||
|
||
WorkContext->Parameters.WriteBulk.Mdl = WorkContext->Irp->MdlAddress;
|
||
WorkContext->Parameters.WriteBulk.RemainingCount = writeLength;
|
||
WorkContext->Parameters.WriteBulk.CurrentOffset = 0;
|
||
WorkContext->Parameters.WriteBulk.FileObject = rfcb->Lfcb->FileObject;
|
||
WorkContext->Parameters.WriteBulk.Complete = FALSE;
|
||
|
||
//
|
||
// Build a successful response
|
||
//
|
||
|
||
response->WordCount = 5;
|
||
response->Sequence = sequence;
|
||
SmbPutUlong( &response->Length, writeLength );
|
||
#if 0 // RNGFIX
|
||
DbgPrint("msgsize: %lx, compr: %lx\n",
|
||
WorkContext->Connection->MaximumSendSize,
|
||
WorkContext->Parameters.WriteBulk.CompressionTechnology );
|
||
#endif // RNGFIX
|
||
SmbPutUlong( &response->MessageSize,
|
||
MIN(sendSize, WorkContext->Connection->MaximumSendSize) );
|
||
response->CompressionTechnology =
|
||
WorkContext->Parameters.WriteBulk.CompressionTechnology;
|
||
|
||
WorkContext->ResponseParameters = NEXT_LOCATION(
|
||
response,
|
||
RESP_WRITE_BULK,
|
||
0 );
|
||
|
||
//SrvFsdSendResponse2( WorkContext, RestartWriteBulkSendComplete );
|
||
WorkContext->ResponseBuffer->DataLength =
|
||
(ULONG)( (PCHAR)WorkContext->ResponseParameters -
|
||
(PCHAR)WorkContext->ResponseHeader );
|
||
WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
|
||
SRV_START_SEND_2(
|
||
WorkContext,
|
||
RestartWriteBulkSendComplete,
|
||
SrvSmbWriteBulkData,
|
||
NULL );
|
||
|
||
} else {
|
||
|
||
//
|
||
// The write request failed. Clean up and send back error.
|
||
// We'll have to come back on the rebound if we're at DPC level.
|
||
//
|
||
|
||
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
||
WorkContext->FspRestartRoutine = RestartPrepareBulkMdlWrite;
|
||
SrvQueueWorkToFsp( WorkContext );
|
||
return;
|
||
}
|
||
|
||
if ( WorkContext->Parameters.WriteBulk.CompressionTechnology ) {
|
||
//
|
||
// We failed trying a compressed write - so now try uncompressed
|
||
//
|
||
|
||
lfcb = rfcb->Lfcb;
|
||
|
||
offset.QuadPart = WorkContext->Parameters.WriteBulk.Offset.QuadPart;
|
||
|
||
WorkContext->Parameters.WriteBulk.CompressionTechnology =
|
||
CompressionTechnologyNone;
|
||
|
||
//
|
||
// If cache operations supported on this file system, then do it!
|
||
//
|
||
|
||
if ( lfcb->FileObject->Flags & FO_CACHE_SUPPORTED ) {
|
||
|
||
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesAttempted );
|
||
|
||
if ( lfcb->PrepareMdlWrite(
|
||
lfcb->FileObject,
|
||
&offset,
|
||
WorkContext->Parameters.WriteBulk.WriteLength,
|
||
WorkContext->Parameters.WriteBulk.Key,
|
||
&WorkContext->Irp->MdlAddress,
|
||
&WorkContext->Irp->IoStatus,
|
||
lfcb->DeviceObject
|
||
) ) {
|
||
|
||
//
|
||
// The fast I/O path worked.
|
||
//
|
||
|
||
RestartPrepareBulkMdlWrite( WorkContext );
|
||
return;
|
||
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesFailed );
|
||
|
||
}
|
||
|
||
//
|
||
// The fast I/O path failed or isn't supported. 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.
|
||
//
|
||
|
||
offset.QuadPart += WorkContext->Irp->IoStatus.Information;
|
||
writeLength -= (USHORT)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)
|
||
WorkContext->Parameters.WriteBulk.WriteLength, // buffer len
|
||
WorkContext->Irp->MdlAddress, // MDL address
|
||
offset, // byte offset
|
||
WorkContext->Parameters.WriteBulk.Key // lock key
|
||
);
|
||
|
||
//
|
||
// Pass the request to the file system.
|
||
//
|
||
|
||
WorkContext->FsdRestartRoutine = RestartPrepareBulkMdlWrite;
|
||
DEBUG WorkContext->FspRestartRoutine = NULL;
|
||
|
||
(VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
|
||
|
||
//
|
||
// The MDL write has been started. When it completes, processing
|
||
// resumes at RestartPrepareBulkMdlWrite.
|
||
//
|
||
|
||
} else {
|
||
|
||
rfcb->WriteBulk[sequence-1] = NULL;
|
||
|
||
SrvSetSmbError( WorkContext, status );
|
||
|
||
SrvFsdSendResponse( WorkContext );
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return;
|
||
|
||
} // RestartPrepareBulkMdlWrite
|
||
|
||
|
||
|
||
NTSTATUS
|
||
RestartWriteBulkSendComplete (
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp,
|
||
IN OUT PWORK_CONTEXT WorkContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the final completion routine for Write Bulk after sending the
|
||
response back to the client redirector.
|
||
|
||
We don't do anything... just hang around waiting for those big writes...
|
||
|
||
Arguments:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
describing server-specific context for the request.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
Irp->Cancel = FALSE;
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
} // RestartWriteBulkSendComplete
|
||
|
||
|
||
|
||
SMB_PROCESSOR_RETURN_TYPE
|
||
SrvSmbWriteBulkData (
|
||
SMB_PROCESSOR_PARAMETERS
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Processes the completion of a Write Bulk SMB.
|
||
|
||
Arguments:
|
||
|
||
SMB_PROCESSOR_PARAMETERS:
|
||
|
||
WorkContext - Supplies a pointer to the work context block
|
||
representing the work item
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PRFCB rfcb;
|
||
BOOLEAN rfcbClosing;
|
||
KIRQL oldIrql;
|
||
PMDL mdl;
|
||
NTSTATUS status;
|
||
|
||
//
|
||
// Clean up mappings for mdl chain.
|
||
//
|
||
|
||
mdl = WorkContext->ResponseBuffer->PartialMdl;
|
||
do {
|
||
MmPrepareMdlForReuse( mdl );
|
||
mdl = mdl->Next;
|
||
} while ( mdl );
|
||
|
||
WorkContext->ResponseBuffer->PartialMdl->Next = NULL;
|
||
|
||
if ( !WorkContext->Parameters.WriteBulk.RemainingCount ) {
|
||
|
||
if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
|
||
WorkContext->FspRestartRoutine = SrvSmbWriteBulkData;
|
||
SrvQueueWorkToFsp( WorkContext );
|
||
return SmbStatusNoResponse;
|
||
}
|
||
#if 0 //RNGFIX
|
||
DbgPrint("MdlDone! WriteMdl: %lx, WriteMdl->Nxt: %lx, addr: %lx, Compr: %lx\n",
|
||
WorkContext->Parameters.WriteBulk.Mdl, WorkContext->Parameters.WriteBulk.Mdl->Next,
|
||
MmGetMdlVirtualAddress( WorkContext->Parameters.WriteBulk.Mdl ),
|
||
WorkContext->Parameters.WriteBulk.CompressionTechnology );
|
||
#endif // RNGFIX
|
||
//
|
||
// Tell the cache manager that we're done with this MDL write.
|
||
//
|
||
|
||
ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
|
||
|
||
rfcb = WorkContext->Rfcb;
|
||
|
||
ASSERT( CompressionTechnologyNone == 0 );
|
||
if ( WorkContext->Parameters.WriteBulk.CompressionTechnology ) {
|
||
|
||
if ( WorkContext->Parameters.WriteBulk.Mdl ) {
|
||
|
||
ASSERT( rfcb );
|
||
if( rfcb->Lfcb->MdlWriteCompleteCompressed == NULL ||
|
||
rfcb->Lfcb->MdlWriteCompleteCompressed(
|
||
WorkContext->Parameters.WriteBulk.FileObject,
|
||
&WorkContext->Parameters.WriteBulk.Offset,
|
||
WorkContext->Parameters.WriteBulk.Mdl,
|
||
rfcb->Lfcb->DeviceObject ) == FALSE ) {
|
||
|
||
|
||
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
|
||
WorkContext->Parameters.WriteBulk.Mdl,
|
||
IRP_MJ_WRITE,
|
||
&WorkContext->Parameters.WriteBulk.Offset,
|
||
WorkContext->Parameters.WriteBulk.WriteLength
|
||
);
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
|
||
}
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
if ( WorkContext->Parameters.WriteBulk.Mdl ) {
|
||
|
||
ASSERT( rfcb );
|
||
if( rfcb->Lfcb->MdlWriteComplete == NULL ||
|
||
rfcb->Lfcb->MdlWriteComplete(
|
||
WorkContext->Parameters.WriteBulk.FileObject,
|
||
&WorkContext->Parameters.WriteBulk.Offset,
|
||
WorkContext->Parameters.WriteBulk.Mdl,
|
||
rfcb->Lfcb->DeviceObject ) == FALSE ) {
|
||
|
||
status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
|
||
WorkContext->Parameters.WriteBulk.Mdl,
|
||
IRP_MJ_WRITE,
|
||
&WorkContext->Parameters.WriteBulk.Offset,
|
||
WorkContext->Parameters.WriteBulk.WriteLength
|
||
);
|
||
if( !NT_SUCCESS( status ) ) {
|
||
SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
WorkContext->Parameters.WriteBulk.Mdl = NULL;
|
||
|
||
if ( rfcb != NULL ) {
|
||
rfcb->WriteBulk[WorkContext->Parameters.WriteBulk.Sequence-1] = NULL;
|
||
|
||
rfcbClosing = (GET_BLOCK_STATE(rfcb) != BlockStateActive);
|
||
|
||
if ( rfcbClosing ) {
|
||
SrvCompleteRfcbClose( rfcb );
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// We're done... finally!
|
||
//
|
||
|
||
KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
|
||
SrvFsdRestartSmbComplete( WorkContext );
|
||
KeLowerIrql( oldIrql );
|
||
}
|
||
|
||
return SmbStatusNoResponse;
|
||
|
||
} // SrvSmbWriteBulkData
|
||
|
||
|
||
|
||
NTSTATUS
|
||
RestartWriteBulkData (
|
||
IN PCONNECTION Connection,
|
||
IN ULONG ReceiveFlags,
|
||
IN ULONG BytesIndicated,
|
||
IN ULONG BytesAvailable,
|
||
IN PVOID Tsdu,
|
||
OUT PWORK_CONTEXT *WorkContext,
|
||
OUT PMDL *Mdl,
|
||
OUT PULONG ReceiveLength,
|
||
OUT PULONG BytesTaken,
|
||
OUT PIRP *Irp
|
||
)
|
||
|
||
{
|
||
PRFCB rfcb = NULL;
|
||
PREQ_WRITE_BULK_DATA request;
|
||
PWORK_CONTEXT workContext;
|
||
USHORT fid;
|
||
USHORT index;
|
||
USHORT sequence;
|
||
PTABLE_HEADER tableHeader;
|
||
LARGE_INTEGER offset;
|
||
PCHAR startVa;
|
||
PMDL mdl;
|
||
PMDL writeMdl;
|
||
PMDL nextMdl;
|
||
KIRQL oldIrql;
|
||
ULONG receiveLength;
|
||
ULONG byteCount;
|
||
ULONG startOffset;
|
||
PIRP irp;
|
||
|
||
//
|
||
// Check if we received a WRITE_BULK_DATA command, and if so process
|
||
// it here.
|
||
//
|
||
|
||
request = (PREQ_WRITE_BULK_DATA)(((PSMB_HEADER)Tsdu) + 1);
|
||
|
||
fid = SmbGetUshort( &request->Fid );
|
||
|
||
//
|
||
// The following code was 'lifted' from SrvVerifyFid2...
|
||
//
|
||
|
||
//
|
||
// Acquire the spin lock that guards the connection's file table.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK( &Connection->SpinLock, &oldIrql );
|
||
|
||
//
|
||
// See if this is the cached rfcb
|
||
//
|
||
|
||
if ( Connection->CachedFid == (ULONG)fid ) {
|
||
|
||
rfcb = Connection->CachedRfcb;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Verify that the FID is in range, is in use, and has the correct
|
||
// sequence number.
|
||
|
||
index = FID_INDEX( fid );
|
||
sequence = FID_SEQUENCE( fid );
|
||
tableHeader = &Connection->FileTable;
|
||
|
||
if ( (index < (USHORT)tableHeader->TableSize) &&
|
||
(tableHeader->Table[index].Owner != NULL) &&
|
||
(tableHeader->Table[index].SequenceNumber == sequence) &&
|
||
(GET_BLOCK_STATE(tableHeader->Table[index].Owner) == BlockStateActive) ) {
|
||
|
||
rfcb = tableHeader->Table[index].Owner;
|
||
|
||
//
|
||
// Cache the fid.
|
||
//
|
||
|
||
Connection->CachedRfcb = rfcb;
|
||
Connection->CachedFid = (ULONG)fid;
|
||
|
||
}
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK( &Connection->SpinLock, oldIrql );
|
||
|
||
if ( rfcb == NULL ) {
|
||
IF_DEBUG(ERRORS) {
|
||
KdPrint((
|
||
"SrvFsdTdiReceiveHandler: No RFCB on FID: 0x%lx\n",
|
||
fid
|
||
));
|
||
}
|
||
|
||
return STATUS_DATA_NOT_ACCEPTED;
|
||
|
||
}
|
||
|
||
//
|
||
// We now have a valid RFCB, check workContext.
|
||
//
|
||
|
||
workContext = rfcb->WriteBulk[request->Sequence-1];
|
||
|
||
offset.LowPart = SmbGetUlong( &request->Offset.LowPart );
|
||
offset.HighPart = SmbGetUlong( &request->Offset.HighPart );
|
||
|
||
receiveLength = SmbGetUlong( &request->DataCount );
|
||
|
||
if ( receiveLength == 0 ) {
|
||
return STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
|
||
*ReceiveLength = receiveLength;
|
||
|
||
//
|
||
// Check if requested write is within bounds...
|
||
//
|
||
|
||
if ( (workContext != NULL) &&
|
||
!workContext->Parameters.WriteBulk.Complete &&
|
||
(offset.QuadPart >= workContext->Parameters.WriteBulk.Offset.QuadPart) &&
|
||
(offset.QuadPart + receiveLength) <=
|
||
(workContext->Parameters.WriteBulk.Offset.QuadPart +
|
||
workContext->Parameters.WriteBulk.WriteLength) ) {
|
||
|
||
//
|
||
// Okay, this packet looks good.
|
||
//
|
||
|
||
*WorkContext = workContext;
|
||
|
||
//
|
||
// Adjust remaining length
|
||
//
|
||
|
||
workContext->Parameters.WriteBulk.RemainingCount -= receiveLength;
|
||
|
||
ASSERT( workContext->Parameters.WriteBulk.RemainingCount ==
|
||
SmbGetUlong( &request->Remaining ) );
|
||
|
||
//
|
||
// Check if we have all of the data for copying.
|
||
//
|
||
|
||
if ( ((ReceiveFlags & TDI_RECEIVE_ENTIRE_MESSAGE) != 0) &&
|
||
(BytesIndicated == BytesAvailable) ) {
|
||
|
||
ASSERT( BytesAvailable == (receiveLength + sizeof(SMB_HEADER) +
|
||
FIELD_OFFSET(REQ_WRITE_BULK_DATA, Buffer)) );
|
||
|
||
//
|
||
// We have all of the data - just copy it
|
||
//
|
||
// We need to handle WriteBulk.Mdl as an MDL chain!
|
||
//
|
||
|
||
mdl = workContext->Parameters.WriteBulk.Mdl;
|
||
|
||
startOffset = workContext->Parameters.WriteBulk.CurrentOffset;
|
||
|
||
//
|
||
// Advance to the correct mdl... if needed.
|
||
//
|
||
|
||
while ( startOffset > MmGetMdlByteCount( mdl ) ) {
|
||
startOffset -= MmGetMdlByteCount( mdl );
|
||
mdl = mdl->Next;
|
||
ASSERT( mdl != NULL );
|
||
}
|
||
|
||
//
|
||
// Adjust current offset.
|
||
//
|
||
|
||
workContext->Parameters.WriteBulk.CurrentOffset += receiveLength;
|
||
|
||
do {
|
||
|
||
byteCount = MIN( receiveLength,
|
||
MmGetMdlByteCount( mdl ) - startOffset );
|
||
|
||
startVa = (PCHAR)MmGetSystemAddressForMdl( mdl ) +
|
||
startOffset;
|
||
|
||
Tsdu = (PVOID)((PCHAR)Tsdu + sizeof(SMB_HEADER) +
|
||
FIELD_OFFSET(REQ_WRITE_BULK_DATA, Buffer) );
|
||
|
||
TdiCopyLookaheadData(
|
||
startVa,
|
||
Tsdu,
|
||
byteCount,
|
||
ReceiveFlags
|
||
);
|
||
|
||
//
|
||
// Adjust remaining length
|
||
//
|
||
|
||
receiveLength -= byteCount;
|
||
|
||
//
|
||
// Move to next mdl, if needed
|
||
//
|
||
|
||
mdl = mdl->Next;
|
||
startOffset = 0;
|
||
|
||
} while ( receiveLength );
|
||
|
||
//
|
||
// Check if we're all done, and if so, queue the work
|
||
// item for completion.
|
||
//
|
||
|
||
if ( !workContext->Parameters.WriteBulk.RemainingCount &&
|
||
!workContext->Parameters.WriteBulk.Complete ) {
|
||
|
||
workContext->Parameters.WriteBulk.Complete = TRUE;
|
||
|
||
//
|
||
// *** THE FOLLOWING IS COPIED FROM
|
||
// SrvQueueWorkToFspAtDpcLevel.
|
||
//
|
||
// Increment the processing count.
|
||
//
|
||
|
||
workContext->ProcessingCount++;
|
||
|
||
//
|
||
// Insert the work item at the tail of the nonblocking
|
||
// work queue.
|
||
//
|
||
|
||
workContext->FspRestartRoutine = SrvSmbWriteBulkData;
|
||
SrvInsertWorkQueueTail(
|
||
workContext->CurrentWorkQueue,
|
||
(PQUEUEABLE_BLOCK_HEADER)workContext
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Tell the transport that we copied the data.
|
||
//
|
||
|
||
*BytesTaken = BytesIndicated;
|
||
|
||
return STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Just as we suspect - the data is not all here.
|
||
// Remember, these are supposed to be BIG writes.
|
||
//
|
||
|
||
if ( workContext->Parameters.WriteBulk.RemainingCount == 0 ) {
|
||
workContext->Parameters.WriteBulk.Complete = TRUE;
|
||
}
|
||
|
||
//
|
||
// Build an Irp
|
||
//
|
||
|
||
// Calculate next Irp address
|
||
|
||
irp = (PIRP)(((ULONG)workContext->ResponseBuffer->Buffer +
|
||
sizeof(REQ_WRITE_BULK_DATA) + 7) & ~7);
|
||
|
||
ASSERT( workContext->Parameters.WriteBulk.IrpIndex != 0 );
|
||
|
||
irp = (PIRP)((PCHAR)irp +
|
||
(--workContext->Parameters.WriteBulk.IrpIndex *
|
||
SrvReceiveIrpSize));
|
||
|
||
*Irp = irp;
|
||
|
||
IoInitializeIrp( irp,
|
||
(USHORT)SrvReceiveIrpSize,
|
||
SrvReceiveIrpStackSize
|
||
);
|
||
|
||
// Indicate that we took the header.
|
||
|
||
*BytesTaken = sizeof(SMB_HEADER) +
|
||
FIELD_OFFSET( REQ_WRITE_BULK_DATA, Buffer );
|
||
ASSERT( *BytesTaken <= BytesAvailable );
|
||
|
||
//
|
||
// Adjust current offset.
|
||
//
|
||
|
||
startOffset = workContext->Parameters.WriteBulk.CurrentOffset;
|
||
workContext->Parameters.WriteBulk.CurrentOffset += receiveLength;
|
||
|
||
//
|
||
// Build a partial mdl
|
||
//
|
||
|
||
writeMdl = workContext->Parameters.WriteBulk.Mdl;
|
||
|
||
//
|
||
// Skip to correct mdl in mdl chain...
|
||
//
|
||
|
||
while ( startOffset > MmGetMdlByteCount( writeMdl ) ) {
|
||
startOffset -= MmGetMdlByteCount( writeMdl );
|
||
writeMdl = writeMdl->Next;
|
||
ASSERT( writeMdl != NULL );
|
||
}
|
||
|
||
mdl = workContext->ResponseBuffer->PartialMdl;
|
||
MmPrepareMdlForReuse( mdl );
|
||
*Mdl = mdl;
|
||
|
||
// nextMdl points to free Mdl's in the WorkContext receive buffer
|
||
|
||
nextMdl = (PMDL)(((ULONG)workContext->ResponseBuffer->Buffer +
|
||
WRITE_BULK_BUFFER_OFFSET + 7) & ~7);
|
||
#if 0 // RNGFIX
|
||
DbgPrint("writeMdl: %lx, mdl: %lx, nextMdl: %lx\n", writeMdl, mdl, nextMdl );
|
||
#endif // RNGFIX
|
||
do {
|
||
|
||
ASSERT( mdl != NULL );
|
||
ASSERT( writeMdl != NULL );
|
||
|
||
//
|
||
// The most we can handle in a single MDL is the maximum of:
|
||
// 1. The receive buffer.
|
||
// 2. The size of the write MDL.
|
||
// 3. The size of our largest MDL.
|
||
//
|
||
|
||
byteCount = MIN( receiveLength,
|
||
MmGetMdlByteCount( writeMdl ) - startOffset );
|
||
|
||
byteCount = MIN( byteCount, MAX_PARTIAL_BUFFER_SIZE);
|
||
|
||
// Get current address within mdl
|
||
|
||
startVa = (PCHAR)MmGetMdlVirtualAddress( writeMdl ) + startOffset;
|
||
#if 0 // RNGFIX
|
||
DbgPrint("writeMdl: %lx, nextMdl: %lx, byteCount: %lx, receiveLength: %lx, startOff: %lx\n",
|
||
writeMdl, nextMdl, byteCount, receiveLength, startOffset);
|
||
#endif // RNGFIX
|
||
IoBuildPartialMdl(
|
||
writeMdl,
|
||
mdl,
|
||
startVa,
|
||
byteCount
|
||
);
|
||
|
||
//
|
||
// Adjust remaining length
|
||
//
|
||
|
||
receiveLength -= byteCount;
|
||
|
||
//
|
||
// If there is more data, set up for next time through.
|
||
//
|
||
|
||
if ( receiveLength ) {
|
||
if ( (startOffset + byteCount) >= MmGetMdlByteCount( writeMdl ) ) {
|
||
writeMdl = writeMdl->Next;
|
||
startOffset = 0;
|
||
} else {
|
||
startOffset += byteCount;
|
||
}
|
||
|
||
mdl->Next = nextMdl;
|
||
mdl = nextMdl;
|
||
nextMdl = (PMDL)(((ULONG)nextMdl + sizeof(MDL) + 7 +
|
||
( sizeof(ULONG) *
|
||
ADDRESS_AND_SIZE_TO_SPAN_PAGES(PAGE_SIZE-1, MAX_PARTIAL_BUFFER_SIZE))) & ~7);
|
||
|
||
//
|
||
// Initialize and build the next mdl.
|
||
//
|
||
|
||
MmInitializeMdl( mdl, PAGE_SIZE-1, MAX_PARTIAL_BUFFER_SIZE);
|
||
|
||
} else {
|
||
|
||
mdl->Next = NULL;
|
||
|
||
}
|
||
|
||
} while ( receiveLength );
|
||
|
||
//
|
||
// Finish building irp in normal code path
|
||
//
|
||
|
||
return STATUS_MORE_PROCESSING_REQUIRED;
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// This packet doesn't look right - dump it.
|
||
//
|
||
|
||
return STATUS_DATA_NOT_ACCEPTED;
|
||
|
||
} // RestartWriteBulkData
|
||
|
||
|
||
|