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
43 KiB
1286 lines
43 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
|
|
};
|
|
|
|
|
|
|
|
|