Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1286 lines
42 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
bulkw.c - this file needs to get folded into write.c
Abstract:
This module implements the mini redirector call down routines pertaining to write
of file system objects.
Author:
Balan Sethu Raman [SethuR] 7-March-1995
Revision History:
Notes:
The WRITE_BULK is an example of a potential multi SMB exchange that uses the
associated exchange infra structure in the connection engine in conjunction with
the continuation capability in the ORDINARY_EXCHANGE.
The WRITE_BULK processing involves the following steps ...
1) send a SMB_WRITE_BULK request to the server.
2) process the SMB_WRITE_BULK response from the server and if successful
spin up SMB_WRITE_BULK_DATA requests to write the data to the server. There
are no responses from the server for the various SMB_WRITE_BULK_DATA requests.
3) On completion of the SMB_WRITE_BULK_DATA requests wait for the final
SMB_WRITE_BULK response from the server.
This sequence of SMB exchanges is implemented in the following manner ...
1) An instance of ORDINARY_EXCHANGE is created and submitted to the connection
engine spin up the initial request.
2) If the response indicated success the continuation routine in the ORDINARY_EXCHANGE
is set to MRxSmbWriteBulkContinuation.
3) On finalization by the connection engine the processing is resumed in
MRxSmbWriteBulkDataContinuation. Here the ORDINARY_EXCHANGE instance is reset,
the preparation made for receiving the final response. The SMB_WRITE_BULK_DATA
requests are spun up as associated exchanges. Currently the SMB_WRITE_BULK_DATA
requests are spun up in batches of atmost MAXIMUM_CONCURRENT_WRITE_BULK_DATA_REQUESTS
On completion ofone batch of requests the next batch is spun up. This is one place
where the logic needs to be fine tuned based upon observed performance. The
approaches can range from spinning one request at a time to the current implementation.
A variation would be to spin them up in batches but have each completion trigger of
further processing. This would involve changes in when the associated exchange
completion handler routine in the connection engine is activated.
One final note --- the ContinuationRoutine is changed on the fly by the bulk data
processing to ensure that the same ordinary exchange continuation infra structure
is used to deal with the end case.
--*/
#include "precomp.h"
#pragma hdrstop
#include "bulkw.h"
//
// The local debug trace level
//
#define Dbg (DEBUG_TRACE_WRITE)
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN_CHUNK_SIZE (0x1000)
#define MAXIMUM_CONCURRENT_WRITE_BULK_DATA_REQUESTS (5)
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, ProcessWriteBulkCompressed)
#pragma alloc_text(PAGE, MRxSmbBuildWriteBulk)
#pragma alloc_text(PAGE, MRxSmbFinishWriteBulkData)
#pragma alloc_text(PAGE, MRxSmbWriteBulkContinuation)
#pragma alloc_text(PAGE, MRxSmbBuildWriteBulkData)
#pragma alloc_text(PAGE, MRxSmbInitializeWriteBulkDataExchange)
#pragma alloc_text(PAGE, MRxSmbWriteBulkDataExchangeStart)
#pragma alloc_text(PAGE, MRxSmbWriteBulkDataExchangeFinalize)
#endif
extern SMB_EXCHANGE_DISPATCH_VECTOR SmbPseDispatch_Write;
//
// Forward declarations
//
NTSTATUS
MRxSmbBuildWriteBulkData (
IN OUT PSMBSTUFFER_BUFFER_STATE StufferState,
IN PLARGE_INTEGER ByteOffsetAsLI,
IN UCHAR Sequence,
IN ULONG ByteCount,
IN ULONG Remaining
);
NTSTATUS
MRxSmbInitializeWriteBulkDataExchange(
PSMB_WRITE_BULK_DATA_EXCHANGE *pWriteBulkDataExchangePointer,
PSMB_PSE_ORDINARY_EXCHANGE pWriteExchange,
PSMB_HEADER pSmbHeader,
PREQ_WRITE_BULK_DATA pWriteBulkDataRequest,
PMDL pDataMdl,
ULONG DataSizeInBytes,
ULONG DataOffsetInBytes,
ULONG RemainingDataInBytes);
NTSTATUS
MRxSmbWriteBulkDataExchangeFinalize(
IN OUT struct _SMB_EXCHANGE *pExchange,
OUT BOOLEAN *pPostRequest);
VOID
ProcessWriteBulkCompressed (
IN PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange
)
/*++
Routine Description:
This routine attempts to perform a write bulk operation.
Arguments:
OrdinaryExchange - pointer to the current ordinary exchange request.
Return Value:
NONE.
Notes:
rw->CompressedRequest - TRUE we have succeeded in making the buffer
compressed. FALSE otherwise.
This is the initial routine that is called to do the necessary preprocessing
of the only kind of compressed write requests that are handled on the client side
These are write requests that are page aligned for an integral number of pages
to a compressed server.
--*/
{
PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState;
PRX_CONTEXT RxContext = OrdinaryExchange->RxContext;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
PSMB_PSE_OE_READWRITE rw = &OrdinaryExchange->ReadWrite;
NTSTATUS status;
PVOID workSpaceBuffer;
ULONG workSpaceBufferSize;
ULONG workSpaceFragmentSize;
ULONG compressedInfoLength;
PCOMPRESSED_DATA_INFO compressedDataInfo;
ULONG i;
PMDL mdl;
ULONG headerLength;
//RNGFIX
//
// We can also use the call to RxGetCompressionWorkSpaceSize as a test to see
// if the current system knows how to handle the CompressionFormat/Engine on a
// read request.
//
// We also need a workspace buffer. We can get the size of this buffer from
// the call to RxGetCompressionWorkspace. We can have 1 statically allocated
// workspace buffer (per File! if we find we don't have one, then return
// a failure and do uncompressed writes!) This is recommended per file, since
// the size of the workspace is dependent on the compression type, which can
// vary on a per file basis.
//
// We must then pass the CDI ptr to the build write bulk request routine.
//
// We can then start writing the compressed data to the server in the finish
// routine.
//
//RNGFIX - remember to deallocate this buffer on the cleanup side!
PAGED_CODE();
rw->CompressedRequest = FALSE;
rw->DataOffset = 0;
rw->CompressedByteCount = 0;
//
// Calculate length of the needed CDI.
//
compressedInfoLength = (sizeof(COMPRESSED_DATA_INFO) + 7 +
(((rw->ThisByteCount + MIN_CHUNK_SIZE - 1) / MIN_CHUNK_SIZE) * 4))
&~7;
ASSERT( compressedInfoLength <= 65535 );
//
// Allocate the buffer to compress into. We could get tricky here and
// allocate a portion of the buffer, like 15/16ths if the compression unit
// shift (this would be for 16 sectors per compression unit). We will
// allocate the CDI along with this.
//
compressedDataInfo = (PCOMPRESSED_DATA_INFO)RxAllocatePoolWithTag(
NonPagedPool,
rw->ThisByteCount + compressedInfoLength,
MRXSMB_RW_POOLTAG);
//
// If we fail, just return an error.
//
if ( compressedDataInfo == NULL ) {
return;
}
//
// Save buffer address (not we skip past the CDI). We need to back
// up the buffer address on the free later.
//
rw->BulkBuffer = (PCHAR)compressedDataInfo + compressedInfoLength;
rw->DataOffset = (USHORT)compressedInfoLength;
//
// Fill in the CDI. RNGFIX - we need to get this data from the open!
// CODE.IMPROVEMENT
//
compressedDataInfo->CompressionFormatAndEngine = COMPRESSION_FORMAT_LZNT1;
compressedDataInfo->ChunkShift = 0xC;
compressedDataInfo->CompressionUnitShift = 0xD;
compressedDataInfo->ClusterShift = 0x9;
//
// Allocate the workspace buffer. We will allocate this separately, since
// it is only needed for the duration of the compression operation. We'll
// free it when we're done. We could just do this once when the file is
// is opened. We know all of the info at that time, including the fact that
// it is compressed. However, we'd be holding onto pool for much longer.
//
//RNGFIX - COMRPRESSION_FORMAT_LZNT1 should be from OpenFile!
//CODE.IMPROVEMENT
status = RtlGetCompressionWorkSpaceSize(
COMPRESSION_FORMAT_LZNT1,
&workSpaceBufferSize,
&workSpaceFragmentSize );
workSpaceBuffer = RxAllocatePoolWithTag(
NonPagedPool,
workSpaceBufferSize,
MRXSMB_RW_POOLTAG);
if ( workSpaceBuffer == NULL ) {
RxFreePool( compressedDataInfo );
rw->BulkBuffer = NULL;
return;
}
status = RtlCompressChunks(
rw->UserBufferBase + rw->ThisBufferOffset,
rw->ThisByteCount,
rw->BulkBuffer,
rw->ThisByteCount,
compressedDataInfo,
compressedInfoLength,
workSpaceBuffer );
RxFreePool( workSpaceBuffer );
if ( status != RX_MAP_STATUS(SUCCESS) ) {
RxFreePool( compressedDataInfo );
return;
}
rw->CompressedRequest = TRUE;
//
// Calculate length of compressed data.
//
ASSERT( compressedDataInfo->NumberOfChunks < 256 );
rw->CompressedByteCount = 0;
for ( i = 0; i < compressedDataInfo->NumberOfChunks; i++ ) {
rw->CompressedByteCount += compressedDataInfo->CompressedChunkSizes[i];
}
//
// Build an mdl from the receive buffer - just after the SMB header
//
// Use the larger of the two headers we'll have to send.
headerLength = MAX( FIELD_OFFSET(REQ_WRITE_BULK_DATA, Buffer),
FIELD_OFFSET(REQ_WRITE_BULK, Buffer) );
mdl = (PMDL)(((ULONG)StufferState->BufferBase + sizeof(SMB_HEADER)
+ 10 + headerLength) & ~7);
//
// We will use the same mdl for both sending the CDI and the actual
// compressed data. This mdl is part of the receive buffer - just after
// the header.
//
// ASSERT( rw->CompressedByteCount >= compressedInfoLength );
MmInitializeMdl( mdl, (PCHAR)rw->BulkBuffer - compressedInfoLength, compressedInfoLength );
MmBuildMdlForNonPagedPool( mdl );
return;
} // ProcessWriteBulkCompressed
NTSTATUS
MRxSmbBuildWriteBulk (
IN OUT PSMBSTUFFER_BUFFER_STATE StufferState,
IN PLARGE_INTEGER ByteOffsetAsLI,
IN ULONG ByteCount,
IN ULONG MaxMessageSize,
IN PVOID CompressedDataInfo,
IN ULONG CompressedInfoSize,
IN ULONG CompressedBufferSize,
IN PMDL CompressedInfoMdl
)
/*++
Routine Description:
This builds a WriteBulk SMB. We don't have to worry about login id and such
since that is done by the connection engine....pretty neat huh? All we have
to do is format up the bits.
Arguments:
StufferState - the state of the smbbuffer from the stuffer's point of view
ByteOffsetAsLI - the byte offset in the file where we want to write
ByteCount - the length of the data to be written
MaxMessageSize - the maximum message size that we can send
CompressedDataInfo - pointer to the COMPRESSED_DATA_INFO structure
CompressedInfoSize - size of the COMPRESSED_DATA_INFO structure (or zero)
CompressedBufferSize - size of the Compressed Data
CompressedInfoMdl - pointer to the compressed data info mdl
Return Value:
RXSTATUS
SUCCESS
NOT_IMPLEMENTED something has appeared in the arguments that i can't handle
Notes:
--*/
{
NTSTATUS Status;
PRX_CONTEXT RxContext = StufferState->RxContext;
RxCaptureFcb;RxCaptureFobx;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
PNT_SMB_HEADER SmbHeader = (PNT_SMB_HEADER)(StufferState->BufferBase);
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
ULONG OffsetLow,OffsetHigh;
UCHAR WriteMode = 0;
UCHAR CompressionTechnology;
PAGED_CODE();
RxDbgTrace(+1, Dbg, ("MRxSmbBuildWriteBulk\n", 0 ));
ASSERT( NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN );
OffsetLow = ByteOffsetAsLI->LowPart;
OffsetHigh = ByteOffsetAsLI->HighPart;
COVERED_CALL(MRxSmbStartSMBCommand( StufferState, SetInitialSMB_Never,
SMB_COM_WRITE_BULK, SMB_REQUEST_SIZE(WRITE_BULK),
NO_EXTRA_DATA,
NO_SPECIAL_ALIGNMENT,
RESPONSE_HEADER_SIZE_NOT_SPECIFIED,
0,0,0,0 STUFFERTRACE(Dbg,'FC'))
);
RxDbgTrace(0, Dbg,("First write bulk status = %lu\n",Status));
// MRxSmbDumpStufferState (1000,"SMB w/WRITE BULK before stuffing",StufferState);
if (FlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,LOWIO_READWRITEFLAG_PAGING_IO)) {
SmbPutAlignedUshort(
&SmbHeader->Flags2,
SmbGetAlignedUshort(&SmbHeader->Flags2)|SMB_FLAGS2_PAGING_IO);
}
ASSERT( SMB_WMODE_WRITE_THROUGH == 1 );
if ( FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_WRITE_THROUGH) ) {
WriteMode |= SMB_WMODE_WRITE_THROUGH;
}
if ( CompressedInfoSize ) {
CompressionTechnology = CompressionTechnologyOne;
} else {
CompressionTechnology = CompressionTechnologyNone;
}
MRxSmbStuffSMB (StufferState,
"0yywDddddB!",
// 0 UCHAR WordCount; // Count of parameter words = 12
WriteMode, // y UCHAR Flags; // Flags byte
CompressionTechnology, // y UCHAR CompressionTechnology
// CompressionTechnology
smbSrvOpen->Fid, // w _USHORT( Fid ); // File handle
SMB_OFFSET_CHECK(WRITE_BULK, Offset)
OffsetLow, OffsetHigh, // Dd LARGE_INTEGER Offset; // Offset in file to begin write
ByteCount, // d _ULONG( TotalCount ); // Total amount of data in this request (ie bytes covered)
CompressedBufferSize, // d _ULONG( DataCount ); // Count of data bytes in this message, replaces ByteCount
MaxMessageSize, // d _ULONG( MessageSize );
// Maximum bytes we can send per message
// B _USHORT( ByteCount ); // Count of data bytes = 0, not used
SMB_WCT_CHECK(12) CompressedInfoSize
// UCHAR Buffer[1];
);
SmbPutUshort( StufferState->CurrentBcc, (USHORT)CompressedInfoSize );
if ( CompressedInfoSize ) {
MRxSmbStuffAppendRawData( StufferState, CompressedInfoMdl );
}
//MRxSmbDumpStufferState (700,"SMB w/WRITE BULK after stuffing",StufferState);
FINALLY:
RxDbgTraceUnIndent(-1, Dbg);
return Status;
} // MRxSmbBuildWriteBulk
NTSTATUS
MRxSmbFinishWriteBulkData (
IN OUT PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange
)
/*++
Routine Description:
This routine completes the write bulk request processing. This routine must always
return STATUS_PENDING to follow the correct processing in the ordinary exchange
logic for synchronous operations. This is because this continuation routine will
be invoked in other thread contexts
This routine is used to wrap up synchronous bulk operations
Arguments:
OrdinaryExchange - the exchange instance
Return Value:
NTSTATUS - The return status for the operation
--*/
{
PRX_CONTEXT RxContext = OrdinaryExchange->RxContext;
PAGED_CODE();
ASSERT(!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
RxDbgTrace(0,Dbg,("Invoking Bulk Write wrap up for ....%lx\n",OrdinaryExchange));
RxSignalSynchronousWaiter(RxContext);
return STATUS_PENDING;
}
NTSTATUS
MRxSmbWriteBulkContinuation(
IN OUT PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange
)
/*++
Routine Description:
This routine continues the Write bulk data request processing on receipt of
a valid SMB_WRITE_BULK response from the server.
Arguments:
OrdinaryExchange - the exchange instance
Return Value:
NTSTATUS - The return status for the operation
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState;
PRX_CONTEXT RxContext = OrdinaryExchange->RxContext;
PSMB_PSE_OE_READWRITE rw = &OrdinaryExchange->ReadWrite;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
PSMB_EXCHANGE pExchange = &OrdinaryExchange->Exchange;
PMDL HeaderMdl = StufferState->HeaderMdl;
PMDL SubmitMdl = StufferState->HeaderPartialMdl;
ULONG MessageSize;
ULONG SendBufferLength;
ULONG RemainingByteCount,ThisBufferOffset;
ULONG PartialBytes;
LARGE_INTEGER ByteOffsetAsLI;
PMDL DataMdl;
PMDL SourceMdl;
PREQ_WRITE_BULK_DATA WriteBulkHeader;
PSMBCEDB_SERVER_ENTRY pServerEntry;
PSMB_HEADER pWriteBulkDataRequestSmbHeader;
ULONG headerLength;
ULONG ActiveWriteBulkDataRequests = 0;
PAGED_CODE();
headerLength = MAX( FIELD_OFFSET(REQ_WRITE_BULK_DATA, Buffer),
FIELD_OFFSET(REQ_WRITE_BULK, Buffer) );
RxDbgTrace(+1, Dbg, ("MRxSmbWriteBulkContinuation\n"));
ASSERT( NodeType(RxContext) == RDBSS_NTC_RX_CONTEXT );
RxDbgTrace(0, Dbg, ("-->BytesReturned=%08lx\n", rw->BytesReturned));
ASSERT( !RxShouldPostCompletion());
//
// Pick up our maximum message size
//
pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
MessageSize = MIN( rw->MaximumSendSize,
pServerEntry->pTransport->MaximumSendSize);
ASSERT( MessageSize != 0 );
ASSERT( rw->ThisByteCount != 0);
ByteOffsetAsLI.QuadPart = rw->ByteOffsetAsLI.QuadPart;
if (!FlagOn(rw->Flags,OE_RW_FLAG_WRITE_BULK_DATA_INITIALIZATION_DONE)) {
SetFlag(rw->Flags,OE_RW_FLAG_WRITE_BULK_DATA_INITIALIZATION_DONE);
SmbCeResetExchange((PSMB_EXCHANGE)OrdinaryExchange);
ClearFlag(
OrdinaryExchange->Flags,
(SMBPSE_OE_FLAG_HEADER_ALREADY_PARSED |
SMBPSE_OE_FLAG_OE_ALREADY_RESUMED) );
SmbCeIncrementPendingLocalOperations((PSMB_EXCHANGE)OrdinaryExchange);
if (OrdinaryExchange->Status == STATUS_SUCCESS) {
SmbCeReceive((PSMB_EXCHANGE)OrdinaryExchange);
//
// Okay... we're now going to transform the exchange packet into one
// that we can use for the WRITE_BULK_DATA request.
//
MRxSmbSetInitialSMB(StufferState STUFFERTRACE(Dbg,0));
//
// Build a generic WriteBulkData request. We'll fill in the specifics
// as we re-use this buffer.
//
pWriteBulkDataRequestSmbHeader = (PSMB_HEADER)StufferState->BufferBase;
WriteBulkHeader = (PREQ_WRITE_BULK_DATA)((PCHAR)StufferState->BufferBase +
sizeof(SMB_HEADER));
MRxSmbBuildWriteBulkData(
StufferState,
&ByteOffsetAsLI,
rw->Sequence,
0,
0);
//
// If we have compressed data, pick up the corresponding byte count and
// Mdl for the data. If we partial, we'll need to pick up another Mdl too.
//
ASSERT( CompressionTechnologyNone == 0 );
if ( rw->CompressedRequest &&
rw->CompressionTechnology ) {
// Eventhough we have sent compressed the entire buffer and sent the
// compression meta data to the server it might choose to accept
// less data. In such cases the client should be prepared to scale back
// the data that needs to be sent. The server side will ensure that
// the data that is accepted will correspond to an integral number of
// chunks. This will ensure that the subsequent requests have a chance
// of being compressed. If this is not true we have no way of restarting.
// Based upon the compressed length that has been accepted we need to
// determine the number of chunks. This can be translated to the
// equivalent number of uncompressed bytes which will establish the
// resumption point.
//
// Use the space in the receive buffer - after the header mdl - for
// the data mdl.
//
if (rw->ThisByteCount < rw->CompressedByteCount) {
// This is the case where the server was not able to accept all
// of our compressed data in one shot.
ULONG NumberOfChunks = 0;
ULONG CumulativeChunkSize = 0;
PCOMPRESSED_DATA_INFO pCompressedDataInfo;
pCompressedDataInfo = (PCOMPRESSED_DATA_INFO)
((PCHAR)rw->BulkBuffer - rw->DataOffset);
for (;;) {
ULONG TempSize;
TempSize = CumulativeChunkSize +
pCompressedDataInfo->CompressedChunkSizes[NumberOfChunks];
if (TempSize <= rw->ThisByteCount) {
NumberOfChunks++;
CumulativeChunkSize = TempSize;
} else {
break;
}
}
ASSERT(CumulativeChunkSize == rw->ThisByteCount);
pCompressedDataInfo->NumberOfChunks = (USHORT)NumberOfChunks;
rw->CompressedByteCount = CumulativeChunkSize;
}
RemainingByteCount = rw->CompressedByteCount;
SourceMdl = (PMDL)(((ULONG)StufferState->BufferBase +
sizeof(SMB_HEADER) + 10 + headerLength) & ~7);
//
// Build an mdl for describing the compressed data.
//
MmInitializeMdl( SourceMdl, rw->BulkBuffer, rw->CompressedByteCount );
MmBuildMdlForNonPagedPool( SourceMdl );
ThisBufferOffset = 0;
} else {
// Pick up the rest of the data, and no need to partial.
RemainingByteCount = rw->ThisByteCount;
SourceMdl = LowIoContext->ParamsFor.ReadWrite.Buffer;
ThisBufferOffset = rw->ThisBufferOffset;
}
rw->PartialBytes = 0;
rw->BytesReturned = 0;
if (!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
KeInitializeEvent(
&RxContext->SyncEvent,
NotificationEvent,
FALSE );
}
} else {
Status = OrdinaryExchange->Status;
RemainingByteCount = 0;
}
} else {
pWriteBulkDataRequestSmbHeader = (PSMB_HEADER)StufferState->BufferBase;
WriteBulkHeader = (PREQ_WRITE_BULK_DATA)((PCHAR)StufferState->BufferBase +
sizeof(SMB_HEADER));
ByteOffsetAsLI.QuadPart += rw->PartialBytes;
ThisBufferOffset = rw->PartialBytes;
if ( rw->CompressedRequest &&
rw->CompressionTechnology ) {
RemainingByteCount = rw->CompressedByteCount - rw->PartialBytes;
SourceMdl = (PMDL)(((ULONG)StufferState->BufferBase +
sizeof(SMB_HEADER) + 10 + headerLength) & ~7);
} else {
RemainingByteCount = rw->ThisByteCount - rw->PartialBytes;
SourceMdl = LowIoContext->ParamsFor.ReadWrite.Buffer;
}
if ((OrdinaryExchange->Status != STATUS_SUCCESS) &&
(OrdinaryExchange->Status != STATUS_MORE_PROCESSING_REQUIRED)) {
RemainingByteCount = 0;
Status = OrdinaryExchange->Status;
}
RxDbgTrace(
0,
Dbg,
("ABWR: OE %lx TBC %lx RBC %lx TBO %lx\n",
OrdinaryExchange,
rw->ThisByteCount,
rw->RemainingByteCount,
ThisBufferOffset));
}
while (RemainingByteCount > 0) {
BOOLEAN AssociatedExchangeCompletionHandlerActivated = FALSE;
PSMB_WRITE_BULK_DATA_EXCHANGE pWriteBulkDataExchange;
//
// Check if we need to build a partial mdl...
//
SendBufferLength = MIN( MessageSize, RemainingByteCount );
// Get our offset and length in prepartion to build and
// send the message.
//
// We manually setup the fields that change in the WriteBulkData
// message, rather than build a new header each time to save
// time and effort. This will happen once per message that we
// send.
//
RemainingByteCount -= SendBufferLength;
SmbPutUlong( &WriteBulkHeader->Offset.LowPart, ByteOffsetAsLI.LowPart );
SmbPutUlong( &WriteBulkHeader->Offset.HighPart, ByteOffsetAsLI.HighPart );
SmbPutUlong( &WriteBulkHeader->DataCount, SendBufferLength );
SmbPutUlong( &WriteBulkHeader->Remaining, RemainingByteCount );
Status = MRxSmbInitializeWriteBulkDataExchange(
&pWriteBulkDataExchange,
OrdinaryExchange,
pWriteBulkDataRequestSmbHeader,
WriteBulkHeader,
SourceMdl,
SendBufferLength,
ThisBufferOffset,
RemainingByteCount);
// Advance offset and reduce the number of bytes written.
ByteOffsetAsLI.QuadPart += SendBufferLength;
ThisBufferOffset += SendBufferLength;
rw->PartialBytes += SendBufferLength;
if (Status == STATUS_SUCCESS) {
ActiveWriteBulkDataRequests++;
AssociatedExchangeCompletionHandlerActivated =
((ActiveWriteBulkDataRequests == MAXIMUM_CONCURRENT_WRITE_BULK_DATA_REQUESTS) ||
(RemainingByteCount == 0));
if (AssociatedExchangeCompletionHandlerActivated &&
(RemainingByteCount == 0)) {
OrdinaryExchange->OpSpecificState = SmbPseOEInnerIoStates_OperationCompleted;
if (!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
OrdinaryExchange->ContinuationRoutine = MRxSmbFinishWriteBulkData;
}
}
Status = SmbCeInitiateAssociatedExchange(
(PSMB_EXCHANGE)pWriteBulkDataExchange,
AssociatedExchangeCompletionHandlerActivated);
}
if (!NT_SUCCESS(Status)) {
RxDbgTrace( 0, Dbg, ("SmbPseExchangeReceive_default: SmbCeSend returned %lx\n",Status));
goto FINALLY;
}
if (AssociatedExchangeCompletionHandlerActivated) {
if (!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
RxWaitSync( RxContext );
Status = STATUS_SUCCESS;
if (RemainingByteCount == 0) {
break;
} else {
// Reinitialize the event
KeInitializeEvent(
&RxContext->SyncEvent,
NotificationEvent,
FALSE );
ActiveWriteBulkDataRequests = 0;
}
} else {
// Map the status to delay cleanup operations.
Status = STATUS_PENDING;
break;
}
}
}
FINALLY:
if (Status != STATUS_PENDING) {
OrdinaryExchange->OpSpecificState = SmbPseOEInnerIoStates_OperationCompleted;
if (Status == STATUS_SUCCESS) {
if(rw->CompressedRequest &&
rw->CompressionTechnology) {
PCOMPRESSED_DATA_INFO pCompressedDataInfo;
pCompressedDataInfo = (PCOMPRESSED_DATA_INFO)
((PCHAR)rw->BulkBuffer - rw->DataOffset);
rw->BytesReturned = pCompressedDataInfo->NumberOfChunks * MIN_CHUNK_SIZE;
} else {
rw->BytesReturned = rw->ThisByteCount;
}
} else {
rw->BytesReturned = 0;
}
if (rw->CompressedRequest &&
rw->BulkBuffer != NULL) {
// Free buffer from start of CDI
RxFreePool( (PCHAR)rw->BulkBuffer - rw->DataOffset );
rw->BulkBuffer = NULL;
}
if ( rw->CompressedRequest &&
rw->CompressionTechnology ) {
SourceMdl = (PMDL)(((ULONG)StufferState->BufferBase +
sizeof(SMB_HEADER) + 10 + headerLength) & ~7);
MmPrepareMdlForReuse(SourceMdl);
}
if (!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
KeInitializeEvent(
&RxContext->SyncEvent,
NotificationEvent,
FALSE );
}
OrdinaryExchange->ContinuationRoutine = NULL;
RxDbgTrace(
0,
Dbg,
("OE %lx TBC %lx RBC %lx BR %lx TBO %lx\n",
OrdinaryExchange,rw->ThisByteCount,
rw->RemainingByteCount,
rw->BytesReturned,
rw->ThisBufferOffset));
SmbCeDecrementPendingLocalOperationsAndFinalize((PSMB_EXCHANGE)OrdinaryExchange);
if (!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
RxWaitSync( RxContext );
} else {
RxDbgTrace(
0,
Dbg,
("ABWC: OE: %lx Status %lx\n",
OrdinaryExchange,
Status));
}
}
RxDbgTrace(-1, Dbg, ("MRxSmbWriteBulkContinuation returning %08lx\n", Status ));
return Status;
}
NTSTATUS
MRxSmbBuildWriteBulkData (
IN OUT PSMBSTUFFER_BUFFER_STATE StufferState,
IN PLARGE_INTEGER ByteOffsetAsLI,
IN UCHAR Sequence,
IN ULONG ByteCount,
IN ULONG Remaining
)
/*++
Routine Description:
This builds a WriteBulk SMB. We don't have to worry about login id and such
since that is done by the connection engine....pretty neat huh? All we have
to do is format up the bits.
Arguments:
StufferState - the state of the smbbuffer from the stuffer's point of view
ByteOffsetAsLI - the byte offset in the file where we want to read
Sequence - this WriteBulkData exchange sequence
ByteCount - the length of the data to be written
Return Value:
NTSTATUS
STATUS_SUCCESS
STATUS_NOT_IMPLEMENTED something has appeared in the arguments that i can't handle
Notes:
--*/
{
NTSTATUS Status;
PRX_CONTEXT RxContext = StufferState->RxContext;
RxCaptureFcb;RxCaptureFobx;
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
PNT_SMB_HEADER SmbHeader = (PNT_SMB_HEADER)(StufferState->BufferBase);
PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
ULONG OffsetLow,OffsetHigh;
PAGED_CODE();
RxDbgTrace(+1, Dbg, ("MRxSmbBuildWriteBulk\n", 0 ));
ASSERT( NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN );
OffsetLow = ByteOffsetAsLI->LowPart;
OffsetHigh = ByteOffsetAsLI->HighPart;
StufferState->CurrentPosition = (PCHAR)(SmbHeader + 1);
SmbHeader->Command = SMB_COM_WRITE_BULK_DATA;
COVERED_CALL(MRxSmbStartSMBCommand( StufferState, SetInitialSMB_Never,
SMB_COM_WRITE_BULK_DATA,
SMB_REQUEST_SIZE(WRITE_BULK),
NO_EXTRA_DATA,
NO_SPECIAL_ALIGNMENT,
RESPONSE_HEADER_SIZE_NOT_SPECIFIED,
0,0,0,0 STUFFERTRACE(Dbg,'FC'));
);
RxDbgTrace(0, Dbg,("First write bulk data status = %lu\n",Status));
MRxSmbDumpStufferState (1000,"SMB w/WRITE BULK DATA before stuffing",StufferState);
if ( FlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,LOWIO_READWRITEFLAG_PAGING_IO)) {
SmbPutAlignedUshort(
&SmbHeader->Flags2,
SmbGetAlignedUshort(&SmbHeader->Flags2)|SMB_FLAGS2_PAGING_IO);
}
MRxSmbStuffSMB (StufferState,
"0yywdDddB!",
// 0 UCHAR WordCount; // Count of parameter words = 10
Sequence, // y UCHAR Sequence; // Exchange sequence handle
0, // y UCHAR Reserved;
smbSrvOpen->Fid, // w _USHORT( Fid ); // File handle
ByteCount, // d _ULONG( DataCount ); // Count of bytes, replaces ByteCount
SMB_OFFSET_CHECK(WRITE_BULK_DATA, Offset)
OffsetLow, OffsetHigh, // Dd LARGE_INTEGER Offset; // Offset in file to begin write
Remaining, // d _ULONG( Remaining ); // Bytes remaining to be written
// B _USHORT( ByteCount ); // Count of data bytes = 0, not used
SMB_WCT_CHECK(10) 0
// UCHAR Buffer[1];
);
MRxSmbDumpStufferState (700,"SMB w/WRITE BULK DATA after stuffing",StufferState);
FINALLY:
RxDbgTraceUnIndent(-1, Dbg);
return(Status);
} // MRxSmbBuildWriteBulkData
extern SMB_EXCHANGE_DISPATCH_VECTOR WriteBulkDataExchangeDispatchVector;
NTSTATUS
MRxSmbInitializeWriteBulkDataExchange(
PSMB_WRITE_BULK_DATA_EXCHANGE *pWriteBulkDataExchangePointer,
PSMB_PSE_ORDINARY_EXCHANGE pWriteExchange,
PSMB_HEADER pSmbHeader,
PREQ_WRITE_BULK_DATA pWriteBulkDataRequest,
PMDL pDataMdl,
ULONG DataSizeInBytes,
ULONG DataOffsetInBytes,
ULONG RemainingDataInBytes)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG HeaderMdlSize;
ULONG DataMdlSize;
ULONG WriteBulkDataExchangeSize;
PSMB_WRITE_BULK_DATA_EXCHANGE pWriteBulkDataExchange;
PAGED_CODE();
HeaderMdlSize = MmSizeOfMdl(
0,
sizeof(SMB_HEADER) + TRANSPORT_HEADER_SIZE + FIELD_OFFSET(REQ_WRITE_BULK_DATA,Buffer));
DataMdlSize = MmSizeOfMdl(
0,
DataSizeInBytes);
WriteBulkDataExchangeSize = FIELD_OFFSET(SMB_WRITE_BULK_DATA_EXCHANGE,Buffer) +
HeaderMdlSize +
DataMdlSize +
TRANSPORT_HEADER_SIZE +
sizeof(SMB_HEADER) +
FIELD_OFFSET(REQ_WRITE_BULK_DATA,Buffer);
pWriteBulkDataExchange = (PSMB_WRITE_BULK_DATA_EXCHANGE)
SmbMmAllocateVariableLengthExchange(
WRITE_BULK_DATA_EXCHANGE,
WriteBulkDataExchangeSize);
if (pWriteBulkDataExchange != NULL) {
pWriteBulkDataExchange->pHeaderMdl =
(PMDL)((PBYTE)pWriteBulkDataExchange +
FIELD_OFFSET(SMB_WRITE_BULK_DATA_EXCHANGE,Buffer));
pWriteBulkDataExchange->pDataMdl =
(PMDL)((PBYTE)pWriteBulkDataExchange->pHeaderMdl + HeaderMdlSize);
pWriteBulkDataExchange->pHeader =
(PSMB_HEADER)((PBYTE)pWriteBulkDataExchange->pDataMdl +
DataMdlSize + TRANSPORT_HEADER_SIZE);
pWriteBulkDataExchange->pWriteBulkDataRequest =
(PREQ_WRITE_BULK_DATA)(pWriteBulkDataExchange->pHeader + 1);
pWriteBulkDataExchange->WriteBulkDataRequestLength =
sizeof(SMB_HEADER) +
FIELD_OFFSET(REQ_WRITE_BULK_DATA,Buffer) +
DataSizeInBytes;
RtlCopyMemory(
pWriteBulkDataExchange->pHeader,
pSmbHeader,
sizeof(SMB_HEADER));
RtlCopyMemory(
pWriteBulkDataExchange->pWriteBulkDataRequest,
pWriteBulkDataRequest,
FIELD_OFFSET(REQ_WRITE_BULK_DATA,Buffer));
RxInitializeHeaderMdl(
pWriteBulkDataExchange->pHeaderMdl,
pWriteBulkDataExchange->pHeader,
sizeof(SMB_HEADER) + FIELD_OFFSET(REQ_WRITE_BULK_DATA,Buffer));
RxBuildHeaderMdlForNonPagedPool(pWriteBulkDataExchange->pHeaderMdl);
IoBuildPartialMdl(
pDataMdl,
pWriteBulkDataExchange->pDataMdl,
(PBYTE)MmGetMdlVirtualAddress(pDataMdl) + DataOffsetInBytes,
DataSizeInBytes);
RxDbgTrace(
0,
Dbg,
("Bulk Data O: %lx, Partial %lx Offset %lx Size %lx\n",
pDataMdl->MappedSystemVa,
pWriteBulkDataExchange->pDataMdl->MappedSystemVa,
DataOffsetInBytes,
DataSizeInBytes));
pWriteBulkDataExchange->pHeaderMdl->Next = pWriteBulkDataExchange->pDataMdl;
pWriteBulkDataExchange->pDataMdl->Next = NULL;
// Initialize the associated exchange.
Status = SmbCeInitializeAssociatedExchange(
(PSMB_EXCHANGE *)&pWriteBulkDataExchange,
(PSMB_EXCHANGE)pWriteExchange,
WRITE_BULK_DATA_EXCHANGE,
&WriteBulkDataExchangeDispatchVector);
if (Status == STATUS_SUCCESS) {
pWriteBulkDataExchange->Mid = pWriteExchange->Mid;
SetFlag(
pWriteBulkDataExchange->SmbCeFlags,
(SMBCE_EXCHANGE_MID_VALID | SMBCE_EXCHANGE_RETAIN_MID));
*pWriteBulkDataExchangePointer = pWriteBulkDataExchange;
} else {
BOOLEAN PostRequest = FALSE;
MRxSmbWriteBulkDataExchangeFinalize(
(PSMB_EXCHANGE)pWriteBulkDataExchange,
&PostRequest);
}
}
return Status;
}
NTSTATUS
MRxSmbWriteBulkDataExchangeStart(
IN struct _SMB_EXCHANGE *pExchange)
/*++
Routine Description:
This routine initiates the wriet bulk data exchange operation
Arguments:
pExchange - pointer to the bulk write data exchange instance.
Return Value:
STATUS_SUCCESS if successful.
Notes:
--*/
{
NTSTATUS Status;
PSMB_WRITE_BULK_DATA_EXCHANGE pWriteBulkDataExchange;
PAGED_CODE();
pWriteBulkDataExchange = (PSMB_WRITE_BULK_DATA_EXCHANGE)pExchange;
IF_DEBUG {
ULONG Length = 0;
PMDL pTempMdl;
pTempMdl = pWriteBulkDataExchange->pHeaderMdl;
while (pTempMdl != NULL) {
Length += pTempMdl->ByteCount;
pTempMdl = pTempMdl->Next;
}
ASSERT(Length == pWriteBulkDataExchange->WriteBulkDataRequestLength);
}
Status = SmbCeSend(
pExchange,
0,
pWriteBulkDataExchange->pHeaderMdl,
pWriteBulkDataExchange->WriteBulkDataRequestLength);
if ((Status != STATUS_PENDING) &&
(Status != STATUS_SUCCESS)) {
BOOLEAN PostRequest = FALSE;
MRxSmbWriteBulkDataExchangeFinalize(
(PSMB_EXCHANGE)pWriteBulkDataExchange,
&PostRequest);
}
return Status;
}
NTSTATUS
MRxSmbWriteBulkDataExchangeSendCompletionHandler(
IN struct _SMB_EXCHANGE *pExchange, // The exchange instance
IN PMDL pDataBuffer,
IN NTSTATUS SendCompletionStatus
)
/*++
Routine Description:
This routine handles send completionsn for the write bulk data exchange
operation
Arguments:
pExchange - pointer to the bulk write data exchange instance.
pDataBuffer - the buffer which was transmitted
SendCompletionStatus - the completion status
Return Value:
STATUS_SUCCESS if successful.
Notes:
--*/
{
RxDbgTrace(
0,
Dbg,
("send completion Associated Write Data Exchange %lx\n",
pExchange));
return STATUS_SUCCESS;
}
NTSTATUS
MRxSmbWriteBulkDataExchangeFinalize(
IN OUT struct _SMB_EXCHANGE *pExchange,
OUT BOOLEAN *pPostRequest)
/*++
Routine Description:
This routine handles the finalization of the write bulk data exchange
Arguments:
pExchange - pointer to the bulk write data exchange instance.
pPostRequest - set to TRUE if the request is to be posted to a worker thread
Return Value:
STATUS_SUCCESS if successful.
Notes:
--*/
{
PAGED_CODE();
if (!RxShouldPostCompletion()) {
PSMB_WRITE_BULK_DATA_EXCHANGE pWriteBulkDataExchange;
pWriteBulkDataExchange = (PSMB_WRITE_BULK_DATA_EXCHANGE)pExchange;
RxDbgTrace(
0,
Dbg,
("Finalizing Associated Write Data Exchange %lx\n",
pWriteBulkDataExchange));
MmPrepareMdlForReuse(
pWriteBulkDataExchange->pHeaderMdl);
MmPrepareMdlForReuse(
pWriteBulkDataExchange->pDataMdl);
ClearFlag(
pWriteBulkDataExchange->SmbCeFlags,
(SMBCE_EXCHANGE_MID_VALID | SMBCE_EXCHANGE_RETAIN_MID));
SmbCeDiscardExchange(pExchange);
*pPostRequest = FALSE;
} else {
*pPostRequest = TRUE;
}
return STATUS_SUCCESS;
}
SMB_EXCHANGE_DISPATCH_VECTOR
WriteBulkDataExchangeDispatchVector =
{
MRxSmbWriteBulkDataExchangeStart,
NULL,
NULL,
MRxSmbWriteBulkDataExchangeSendCompletionHandler,
MRxSmbWriteBulkDataExchangeFinalize,
NULL
};