Leaked source code of windows server 2003
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.

1285 lines
43 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. bulkw.c - this file needs to get folded into write.c
  5. Abstract:
  6. This module implements the mini redirector call down routines pertaining to write
  7. of file system objects.
  8. Author:
  9. Balan Sethu Raman [SethuR] 7-March-1995
  10. Revision History:
  11. Notes:
  12. The WRITE_BULK is an example of a potential multi SMB exchange that uses the
  13. associated exchange infra structure in the connection engine in conjunction with
  14. the continuation capability in the ORDINARY_EXCHANGE.
  15. The WRITE_BULK processing involves the following steps ...
  16. 1) send a SMB_WRITE_BULK request to the server.
  17. 2) process the SMB_WRITE_BULK response from the server and if successful
  18. spin up SMB_WRITE_BULK_DATA requests to write the data to the server. There
  19. are no responses from the server for the various SMB_WRITE_BULK_DATA requests.
  20. 3) On completion of the SMB_WRITE_BULK_DATA requests wait for the final
  21. SMB_WRITE_BULK response from the server.
  22. This sequence of SMB exchanges is implemented in the following manner ...
  23. 1) An instance of ORDINARY_EXCHANGE is created and submitted to the connection
  24. engine spin up the initial request.
  25. 2) If the response indicated success the continuation routine in the ORDINARY_EXCHANGE
  26. is set to MRxSmbWriteBulkContinuation.
  27. 3) On finalization by the connection engine the processing is resumed in
  28. MRxSmbWriteBulkDataContinuation. Here the ORDINARY_EXCHANGE instance is reset,
  29. the preparation made for receiving the final response. The SMB_WRITE_BULK_DATA
  30. requests are spun up as associated exchanges. Currently the SMB_WRITE_BULK_DATA
  31. requests are spun up in batches of atmost MAXIMUM_CONCURRENT_WRITE_BULK_DATA_REQUESTS
  32. On completion ofone batch of requests the next batch is spun up. This is one place
  33. where the logic needs to be fine tuned based upon observed performance. The
  34. approaches can range from spinning one request at a time to the current implementation.
  35. A variation would be to spin them up in batches but have each completion trigger of
  36. further processing. This would involve changes in when the associated exchange
  37. completion handler routine in the connection engine is activated.
  38. One final note --- the ContinuationRoutine is changed on the fly by the bulk data
  39. processing to ensure that the same ordinary exchange continuation infra structure
  40. is used to deal with the end case.
  41. --*/
  42. #include "precomp.h"
  43. #pragma hdrstop
  44. #include "bulkw.h"
  45. //
  46. // The local debug trace level
  47. //
  48. #define Dbg (DEBUG_TRACE_WRITE)
  49. #define MIN(a,b) ((a) < (b) ? (a) : (b))
  50. #define MAX(a,b) ((a) > (b) ? (a) : (b))
  51. #define MIN_CHUNK_SIZE (0x1000)
  52. #define MAXIMUM_CONCURRENT_WRITE_BULK_DATA_REQUESTS (5)
  53. #ifdef ALLOC_PRAGMA
  54. #pragma alloc_text(PAGE, ProcessWriteBulkCompressed)
  55. #pragma alloc_text(PAGE, MRxSmbBuildWriteBulk)
  56. #pragma alloc_text(PAGE, MRxSmbFinishWriteBulkData)
  57. #pragma alloc_text(PAGE, MRxSmbWriteBulkContinuation)
  58. #pragma alloc_text(PAGE, MRxSmbBuildWriteBulkData)
  59. #pragma alloc_text(PAGE, MRxSmbInitializeWriteBulkDataExchange)
  60. #pragma alloc_text(PAGE, MRxSmbWriteBulkDataExchangeStart)
  61. #pragma alloc_text(PAGE, MRxSmbWriteBulkDataExchangeFinalize)
  62. #endif
  63. extern SMB_EXCHANGE_DISPATCH_VECTOR SmbPseDispatch_Write;
  64. //
  65. // Forward declarations
  66. //
  67. NTSTATUS
  68. MRxSmbBuildWriteBulkData (
  69. IN OUT PSMBSTUFFER_BUFFER_STATE StufferState,
  70. IN PLARGE_INTEGER ByteOffsetAsLI,
  71. IN UCHAR Sequence,
  72. IN ULONG ByteCount,
  73. IN ULONG Remaining
  74. );
  75. NTSTATUS
  76. MRxSmbInitializeWriteBulkDataExchange(
  77. PSMB_WRITE_BULK_DATA_EXCHANGE *pWriteBulkDataExchangePointer,
  78. PSMB_PSE_ORDINARY_EXCHANGE pWriteExchange,
  79. PSMB_HEADER pSmbHeader,
  80. PREQ_WRITE_BULK_DATA pWriteBulkDataRequest,
  81. PMDL pDataMdl,
  82. ULONG DataSizeInBytes,
  83. ULONG DataOffsetInBytes,
  84. ULONG RemainingDataInBytes);
  85. NTSTATUS
  86. MRxSmbWriteBulkDataExchangeFinalize(
  87. IN OUT struct _SMB_EXCHANGE *pExchange,
  88. OUT BOOLEAN *pPostRequest);
  89. VOID
  90. ProcessWriteBulkCompressed (
  91. IN PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange
  92. )
  93. /*++
  94. Routine Description:
  95. This routine attempts to perform a write bulk operation.
  96. Arguments:
  97. OrdinaryExchange - pointer to the current ordinary exchange request.
  98. Return Value:
  99. NONE.
  100. Notes:
  101. rw->CompressedRequest - TRUE we have succeeded in making the buffer
  102. compressed. FALSE otherwise.
  103. This is the initial routine that is called to do the necessary preprocessing
  104. of the only kind of compressed write requests that are handled on the client side
  105. These are write requests that are page aligned for an integral number of pages
  106. to a compressed server.
  107. --*/
  108. {
  109. PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState;
  110. PRX_CONTEXT RxContext = OrdinaryExchange->RxContext;
  111. PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
  112. PSMB_PSE_OE_READWRITE rw = &OrdinaryExchange->ReadWrite;
  113. NTSTATUS status;
  114. PVOID workSpaceBuffer;
  115. ULONG workSpaceBufferSize;
  116. ULONG workSpaceFragmentSize;
  117. ULONG compressedInfoLength;
  118. PCOMPRESSED_DATA_INFO compressedDataInfo;
  119. ULONG i;
  120. PMDL mdl;
  121. ULONG headerLength;
  122. //RNGFIX
  123. //
  124. // We can also use the call to RxGetCompressionWorkSpaceSize as a test to see
  125. // if the current system knows how to handle the CompressionFormat/Engine on a
  126. // read request.
  127. //
  128. // We also need a workspace buffer. We can get the size of this buffer from
  129. // the call to RxGetCompressionWorkspace. We can have 1 statically allocated
  130. // workspace buffer (per File! if we find we don't have one, then return
  131. // a failure and do uncompressed writes!) This is recommended per file, since
  132. // the size of the workspace is dependent on the compression type, which can
  133. // vary on a per file basis.
  134. //
  135. // We must then pass the CDI ptr to the build write bulk request routine.
  136. //
  137. // We can then start writing the compressed data to the server in the finish
  138. // routine.
  139. //
  140. //RNGFIX - remember to deallocate this buffer on the cleanup side!
  141. PAGED_CODE();
  142. rw->CompressedRequest = FALSE;
  143. rw->DataOffset = 0;
  144. rw->CompressedByteCount = 0;
  145. //
  146. // Calculate length of the needed CDI.
  147. //
  148. compressedInfoLength = (sizeof(COMPRESSED_DATA_INFO) + 7 +
  149. (((rw->ThisByteCount + MIN_CHUNK_SIZE - 1) / MIN_CHUNK_SIZE) * 4))
  150. &~7;
  151. ASSERT( compressedInfoLength <= 65535 );
  152. //
  153. // Allocate the buffer to compress into. We could get tricky here and
  154. // allocate a portion of the buffer, like 15/16ths if the compression unit
  155. // shift (this would be for 16 sectors per compression unit). We will
  156. // allocate the CDI along with this.
  157. //
  158. compressedDataInfo = (PCOMPRESSED_DATA_INFO)RxAllocatePoolWithTag(
  159. NonPagedPool,
  160. rw->ThisByteCount + compressedInfoLength,
  161. MRXSMB_RW_POOLTAG);
  162. //
  163. // If we fail, just return an error.
  164. //
  165. if ( compressedDataInfo == NULL ) {
  166. return;
  167. }
  168. //
  169. // Save buffer address (not we skip past the CDI). We need to back
  170. // up the buffer address on the free later.
  171. //
  172. rw->BulkBuffer = (PCHAR)compressedDataInfo + compressedInfoLength;
  173. rw->DataOffset = (USHORT)compressedInfoLength;
  174. //
  175. // Fill in the CDI. RNGFIX - we need to get this data from the open!
  176. // CODE.IMPROVEMENT
  177. //
  178. compressedDataInfo->CompressionFormatAndEngine = COMPRESSION_FORMAT_LZNT1;
  179. compressedDataInfo->ChunkShift = 0xC;
  180. compressedDataInfo->CompressionUnitShift = 0xD;
  181. compressedDataInfo->ClusterShift = 0x9;
  182. //
  183. // Allocate the workspace buffer. We will allocate this separately, since
  184. // it is only needed for the duration of the compression operation. We'll
  185. // free it when we're done. We could just do this once when the file is
  186. // is opened. We know all of the info at that time, including the fact that
  187. // it is compressed. However, we'd be holding onto pool for much longer.
  188. //
  189. //RNGFIX - COMRPRESSION_FORMAT_LZNT1 should be from OpenFile!
  190. //CODE.IMPROVEMENT
  191. status = RtlGetCompressionWorkSpaceSize(
  192. COMPRESSION_FORMAT_LZNT1,
  193. &workSpaceBufferSize,
  194. &workSpaceFragmentSize );
  195. workSpaceBuffer = RxAllocatePoolWithTag(
  196. NonPagedPool,
  197. workSpaceBufferSize,
  198. MRXSMB_RW_POOLTAG);
  199. if ( workSpaceBuffer == NULL ) {
  200. RxFreePool( compressedDataInfo );
  201. rw->BulkBuffer = NULL;
  202. return;
  203. }
  204. status = RtlCompressChunks(
  205. rw->UserBufferBase + rw->ThisBufferOffset,
  206. rw->ThisByteCount,
  207. rw->BulkBuffer,
  208. rw->ThisByteCount,
  209. compressedDataInfo,
  210. compressedInfoLength,
  211. workSpaceBuffer );
  212. RxFreePool( workSpaceBuffer );
  213. if ( status != RX_MAP_STATUS(SUCCESS) ) {
  214. RxFreePool( compressedDataInfo );
  215. return;
  216. }
  217. rw->CompressedRequest = TRUE;
  218. //
  219. // Calculate length of compressed data.
  220. //
  221. ASSERT( compressedDataInfo->NumberOfChunks < 256 );
  222. rw->CompressedByteCount = 0;
  223. for ( i = 0; i < compressedDataInfo->NumberOfChunks; i++ ) {
  224. rw->CompressedByteCount += compressedDataInfo->CompressedChunkSizes[i];
  225. }
  226. //
  227. // Build an mdl from the receive buffer - just after the SMB header
  228. //
  229. // Use the larger of the two headers we'll have to send.
  230. headerLength = MAX( FIELD_OFFSET(REQ_WRITE_BULK_DATA, Buffer),
  231. FIELD_OFFSET(REQ_WRITE_BULK, Buffer) );
  232. mdl = (PMDL)(((ULONG)StufferState->BufferBase + sizeof(SMB_HEADER)
  233. + 10 + headerLength) & ~7);
  234. //
  235. // We will use the same mdl for both sending the CDI and the actual
  236. // compressed data. This mdl is part of the receive buffer - just after
  237. // the header.
  238. //
  239. // ASSERT( rw->CompressedByteCount >= compressedInfoLength );
  240. MmInitializeMdl( mdl, (PCHAR)rw->BulkBuffer - compressedInfoLength, compressedInfoLength );
  241. MmBuildMdlForNonPagedPool( mdl );
  242. return;
  243. } // ProcessWriteBulkCompressed
  244. NTSTATUS
  245. MRxSmbBuildWriteBulk (
  246. IN OUT PSMBSTUFFER_BUFFER_STATE StufferState,
  247. IN PLARGE_INTEGER ByteOffsetAsLI,
  248. IN ULONG ByteCount,
  249. IN ULONG MaxMessageSize,
  250. IN PVOID CompressedDataInfo,
  251. IN ULONG CompressedInfoSize,
  252. IN ULONG CompressedBufferSize,
  253. IN PMDL CompressedInfoMdl
  254. )
  255. /*++
  256. Routine Description:
  257. This builds a WriteBulk SMB. We don't have to worry about login id and such
  258. since that is done by the connection engine....pretty neat huh? All we have
  259. to do is format up the bits.
  260. Arguments:
  261. StufferState - the state of the smbbuffer from the stuffer's point of view
  262. ByteOffsetAsLI - the byte offset in the file where we want to write
  263. ByteCount - the length of the data to be written
  264. MaxMessageSize - the maximum message size that we can send
  265. CompressedDataInfo - pointer to the COMPRESSED_DATA_INFO structure
  266. CompressedInfoSize - size of the COMPRESSED_DATA_INFO structure (or zero)
  267. CompressedBufferSize - size of the Compressed Data
  268. CompressedInfoMdl - pointer to the compressed data info mdl
  269. Return Value:
  270. RXSTATUS
  271. SUCCESS
  272. NOT_IMPLEMENTED something has appeared in the arguments that i can't handle
  273. Notes:
  274. --*/
  275. {
  276. NTSTATUS Status;
  277. PRX_CONTEXT RxContext = StufferState->RxContext;
  278. RxCaptureFcb;RxCaptureFobx;
  279. PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
  280. PNT_SMB_HEADER SmbHeader = (PNT_SMB_HEADER)(StufferState->BufferBase);
  281. PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
  282. PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
  283. ULONG OffsetLow,OffsetHigh;
  284. UCHAR WriteMode = 0;
  285. UCHAR CompressionTechnology;
  286. PAGED_CODE();
  287. RxDbgTrace(+1, Dbg, ("MRxSmbBuildWriteBulk\n", 0 ));
  288. ASSERT( NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN );
  289. OffsetLow = ByteOffsetAsLI->LowPart;
  290. OffsetHigh = ByteOffsetAsLI->HighPart;
  291. COVERED_CALL(MRxSmbStartSMBCommand( StufferState, SetInitialSMB_Never,
  292. SMB_COM_WRITE_BULK, SMB_REQUEST_SIZE(WRITE_BULK),
  293. NO_EXTRA_DATA,
  294. NO_SPECIAL_ALIGNMENT,
  295. RESPONSE_HEADER_SIZE_NOT_SPECIFIED,
  296. 0,0,0,0 STUFFERTRACE(Dbg,'FC'))
  297. );
  298. RxDbgTrace(0, Dbg,("First write bulk status = %lu\n",Status));
  299. // MRxSmbDumpStufferState (1000,"SMB w/WRITE BULK before stuffing",StufferState);
  300. if (FlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,LOWIO_READWRITEFLAG_PAGING_IO)) {
  301. SmbPutAlignedUshort(
  302. &SmbHeader->Flags2,
  303. SmbGetAlignedUshort(&SmbHeader->Flags2)|SMB_FLAGS2_PAGING_IO);
  304. }
  305. ASSERT( SMB_WMODE_WRITE_THROUGH == 1 );
  306. if ( FlagOn(RxContext->Flags,RX_CONTEXT_FLAG_WRITE_THROUGH) ) {
  307. WriteMode |= SMB_WMODE_WRITE_THROUGH;
  308. }
  309. if ( CompressedInfoSize ) {
  310. CompressionTechnology = CompressionTechnologyOne;
  311. } else {
  312. CompressionTechnology = CompressionTechnologyNone;
  313. }
  314. MRxSmbStuffSMB (StufferState,
  315. "0yywDddddB!",
  316. // 0 UCHAR WordCount; // Count of parameter words = 12
  317. WriteMode, // y UCHAR Flags; // Flags byte
  318. CompressionTechnology, // y UCHAR CompressionTechnology
  319. // CompressionTechnology
  320. smbSrvOpen->Fid, // w _USHORT( Fid ); // File handle
  321. SMB_OFFSET_CHECK(WRITE_BULK, Offset)
  322. OffsetLow, OffsetHigh, // Dd LARGE_INTEGER Offset; // Offset in file to begin write
  323. ByteCount, // d _ULONG( TotalCount ); // Total amount of data in this request (ie bytes covered)
  324. CompressedBufferSize, // d _ULONG( DataCount ); // Count of data bytes in this message, replaces ByteCount
  325. MaxMessageSize, // d _ULONG( MessageSize );
  326. // Maximum bytes we can send per message
  327. // B _USHORT( ByteCount ); // Count of data bytes = 0, not used
  328. SMB_WCT_CHECK(12) CompressedInfoSize
  329. // UCHAR Buffer[1];
  330. );
  331. SmbPutUshort( StufferState->CurrentBcc, (USHORT)CompressedInfoSize );
  332. if ( CompressedInfoSize ) {
  333. MRxSmbStuffAppendRawData( StufferState, CompressedInfoMdl );
  334. }
  335. //MRxSmbDumpStufferState (700,"SMB w/WRITE BULK after stuffing",StufferState);
  336. FINALLY:
  337. RxDbgTraceUnIndent(-1, Dbg);
  338. return Status;
  339. } // MRxSmbBuildWriteBulk
  340. NTSTATUS
  341. MRxSmbFinishWriteBulkData (
  342. IN OUT PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange
  343. )
  344. /*++
  345. Routine Description:
  346. This routine completes the write bulk request processing. This routine must always
  347. return STATUS_PENDING to follow the correct processing in the ordinary exchange
  348. logic for synchronous operations. This is because this continuation routine will
  349. be invoked in other thread contexts
  350. This routine is used to wrap up synchronous bulk operations
  351. Arguments:
  352. OrdinaryExchange - the exchange instance
  353. Return Value:
  354. NTSTATUS - The return status for the operation
  355. --*/
  356. {
  357. PRX_CONTEXT RxContext = OrdinaryExchange->RxContext;
  358. PAGED_CODE();
  359. ASSERT(!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION));
  360. RxDbgTrace(0,Dbg,("Invoking Bulk Write wrap up for ....%lx\n",OrdinaryExchange));
  361. RxSignalSynchronousWaiter(RxContext);
  362. return STATUS_PENDING;
  363. }
  364. NTSTATUS
  365. MRxSmbWriteBulkContinuation(
  366. IN OUT PSMB_PSE_ORDINARY_EXCHANGE OrdinaryExchange
  367. )
  368. /*++
  369. Routine Description:
  370. This routine continues the Write bulk data request processing on receipt of
  371. a valid SMB_WRITE_BULK response from the server.
  372. Arguments:
  373. OrdinaryExchange - the exchange instance
  374. Return Value:
  375. NTSTATUS - The return status for the operation
  376. --*/
  377. {
  378. NTSTATUS Status = STATUS_SUCCESS;
  379. PSMBSTUFFER_BUFFER_STATE StufferState = &OrdinaryExchange->AssociatedStufferState;
  380. PRX_CONTEXT RxContext = OrdinaryExchange->RxContext;
  381. PSMB_PSE_OE_READWRITE rw = &OrdinaryExchange->ReadWrite;
  382. PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
  383. PSMB_EXCHANGE pExchange = &OrdinaryExchange->Exchange;
  384. PMDL HeaderMdl = StufferState->HeaderMdl;
  385. PMDL SubmitMdl = StufferState->HeaderPartialMdl;
  386. ULONG MessageSize;
  387. ULONG SendBufferLength;
  388. ULONG RemainingByteCount,ThisBufferOffset;
  389. ULONG PartialBytes;
  390. LARGE_INTEGER ByteOffsetAsLI;
  391. PMDL DataMdl;
  392. PMDL SourceMdl;
  393. PREQ_WRITE_BULK_DATA WriteBulkHeader;
  394. PSMBCEDB_SERVER_ENTRY pServerEntry;
  395. PSMB_HEADER pWriteBulkDataRequestSmbHeader;
  396. ULONG headerLength;
  397. ULONG ActiveWriteBulkDataRequests = 0;
  398. PAGED_CODE();
  399. headerLength = MAX( FIELD_OFFSET(REQ_WRITE_BULK_DATA, Buffer),
  400. FIELD_OFFSET(REQ_WRITE_BULK, Buffer) );
  401. RxDbgTrace(+1, Dbg, ("MRxSmbWriteBulkContinuation\n"));
  402. ASSERT( NodeType(RxContext) == RDBSS_NTC_RX_CONTEXT );
  403. RxDbgTrace(0, Dbg, ("-->BytesReturned=%08lx\n", rw->BytesReturned));
  404. ASSERT( !RxShouldPostCompletion());
  405. //
  406. // Pick up our maximum message size
  407. //
  408. pServerEntry = SmbCeGetExchangeServerEntry(pExchange);
  409. MessageSize = MIN( rw->MaximumSendSize,
  410. pServerEntry->pTransport->MaximumSendSize);
  411. ASSERT( MessageSize != 0 );
  412. ASSERT( rw->ThisByteCount != 0);
  413. ByteOffsetAsLI.QuadPart = rw->ByteOffsetAsLI.QuadPart;
  414. if (!FlagOn(rw->Flags,OE_RW_FLAG_WRITE_BULK_DATA_INITIALIZATION_DONE)) {
  415. SetFlag(rw->Flags,OE_RW_FLAG_WRITE_BULK_DATA_INITIALIZATION_DONE);
  416. SmbCeResetExchange((PSMB_EXCHANGE)OrdinaryExchange);
  417. ClearFlag(
  418. OrdinaryExchange->Flags,
  419. (SMBPSE_OE_FLAG_HEADER_ALREADY_PARSED |
  420. SMBPSE_OE_FLAG_OE_ALREADY_RESUMED) );
  421. SmbCeIncrementPendingLocalOperations((PSMB_EXCHANGE)OrdinaryExchange);
  422. if (OrdinaryExchange->Status == STATUS_SUCCESS) {
  423. SmbCeReceive((PSMB_EXCHANGE)OrdinaryExchange);
  424. //
  425. // Okay... we're now going to transform the exchange packet into one
  426. // that we can use for the WRITE_BULK_DATA request.
  427. //
  428. MRxSmbSetInitialSMB(StufferState STUFFERTRACE(Dbg,0));
  429. //
  430. // Build a generic WriteBulkData request. We'll fill in the specifics
  431. // as we re-use this buffer.
  432. //
  433. pWriteBulkDataRequestSmbHeader = (PSMB_HEADER)StufferState->BufferBase;
  434. WriteBulkHeader = (PREQ_WRITE_BULK_DATA)((PCHAR)StufferState->BufferBase +
  435. sizeof(SMB_HEADER));
  436. MRxSmbBuildWriteBulkData(
  437. StufferState,
  438. &ByteOffsetAsLI,
  439. rw->Sequence,
  440. 0,
  441. 0);
  442. //
  443. // If we have compressed data, pick up the corresponding byte count and
  444. // Mdl for the data. If we partial, we'll need to pick up another Mdl too.
  445. //
  446. ASSERT( CompressionTechnologyNone == 0 );
  447. if ( rw->CompressedRequest &&
  448. rw->CompressionTechnology ) {
  449. // Eventhough we have sent compressed the entire buffer and sent the
  450. // compression meta data to the server it might choose to accept
  451. // less data. In such cases the client should be prepared to scale back
  452. // the data that needs to be sent. The server side will ensure that
  453. // the data that is accepted will correspond to an integral number of
  454. // chunks. This will ensure that the subsequent requests have a chance
  455. // of being compressed. If this is not true we have no way of restarting.
  456. // Based upon the compressed length that has been accepted we need to
  457. // determine the number of chunks. This can be translated to the
  458. // equivalent number of uncompressed bytes which will establish the
  459. // resumption point.
  460. //
  461. // Use the space in the receive buffer - after the header mdl - for
  462. // the data mdl.
  463. //
  464. if (rw->ThisByteCount < rw->CompressedByteCount) {
  465. // This is the case where the server was not able to accept all
  466. // of our compressed data in one shot.
  467. ULONG NumberOfChunks = 0;
  468. ULONG CumulativeChunkSize = 0;
  469. PCOMPRESSED_DATA_INFO pCompressedDataInfo;
  470. pCompressedDataInfo = (PCOMPRESSED_DATA_INFO)
  471. ((PCHAR)rw->BulkBuffer - rw->DataOffset);
  472. for (;;) {
  473. ULONG TempSize;
  474. TempSize = CumulativeChunkSize +
  475. pCompressedDataInfo->CompressedChunkSizes[NumberOfChunks];
  476. if (TempSize <= rw->ThisByteCount) {
  477. NumberOfChunks++;
  478. CumulativeChunkSize = TempSize;
  479. } else {
  480. break;
  481. }
  482. }
  483. ASSERT(CumulativeChunkSize == rw->ThisByteCount);
  484. pCompressedDataInfo->NumberOfChunks = (USHORT)NumberOfChunks;
  485. rw->CompressedByteCount = CumulativeChunkSize;
  486. }
  487. RemainingByteCount = rw->CompressedByteCount;
  488. SourceMdl = (PMDL)(((ULONG)StufferState->BufferBase +
  489. sizeof(SMB_HEADER) + 10 + headerLength) & ~7);
  490. //
  491. // Build an mdl for describing the compressed data.
  492. //
  493. MmInitializeMdl( SourceMdl, rw->BulkBuffer, rw->CompressedByteCount );
  494. MmBuildMdlForNonPagedPool( SourceMdl );
  495. ThisBufferOffset = 0;
  496. } else {
  497. // Pick up the rest of the data, and no need to partial.
  498. RemainingByteCount = rw->ThisByteCount;
  499. SourceMdl = LowIoContext->ParamsFor.ReadWrite.Buffer;
  500. ThisBufferOffset = rw->ThisBufferOffset;
  501. }
  502. rw->PartialBytes = 0;
  503. rw->BytesReturned = 0;
  504. if (!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
  505. KeInitializeEvent(
  506. &RxContext->SyncEvent,
  507. NotificationEvent,
  508. FALSE );
  509. }
  510. } else {
  511. Status = OrdinaryExchange->Status;
  512. RemainingByteCount = 0;
  513. }
  514. } else {
  515. pWriteBulkDataRequestSmbHeader = (PSMB_HEADER)StufferState->BufferBase;
  516. WriteBulkHeader = (PREQ_WRITE_BULK_DATA)((PCHAR)StufferState->BufferBase +
  517. sizeof(SMB_HEADER));
  518. ByteOffsetAsLI.QuadPart += rw->PartialBytes;
  519. ThisBufferOffset = rw->PartialBytes;
  520. if ( rw->CompressedRequest &&
  521. rw->CompressionTechnology ) {
  522. RemainingByteCount = rw->CompressedByteCount - rw->PartialBytes;
  523. SourceMdl = (PMDL)(((ULONG)StufferState->BufferBase +
  524. sizeof(SMB_HEADER) + 10 + headerLength) & ~7);
  525. } else {
  526. RemainingByteCount = rw->ThisByteCount - rw->PartialBytes;
  527. SourceMdl = LowIoContext->ParamsFor.ReadWrite.Buffer;
  528. }
  529. if ((OrdinaryExchange->Status != STATUS_SUCCESS) &&
  530. (OrdinaryExchange->Status != STATUS_MORE_PROCESSING_REQUIRED)) {
  531. RemainingByteCount = 0;
  532. Status = OrdinaryExchange->Status;
  533. }
  534. RxDbgTrace(
  535. 0,
  536. Dbg,
  537. ("ABWR: OE %lx TBC %lx RBC %lx TBO %lx\n",
  538. OrdinaryExchange,
  539. rw->ThisByteCount,
  540. rw->RemainingByteCount,
  541. ThisBufferOffset));
  542. }
  543. while (RemainingByteCount > 0) {
  544. BOOLEAN AssociatedExchangeCompletionHandlerActivated = FALSE;
  545. PSMB_WRITE_BULK_DATA_EXCHANGE pWriteBulkDataExchange;
  546. //
  547. // Check if we need to build a partial mdl...
  548. //
  549. SendBufferLength = MIN( MessageSize, RemainingByteCount );
  550. // Get our offset and length in prepartion to build and
  551. // send the message.
  552. //
  553. // We manually setup the fields that change in the WriteBulkData
  554. // message, rather than build a new header each time to save
  555. // time and effort. This will happen once per message that we
  556. // send.
  557. //
  558. RemainingByteCount -= SendBufferLength;
  559. SmbPutUlong( &WriteBulkHeader->Offset.LowPart, ByteOffsetAsLI.LowPart );
  560. SmbPutUlong( &WriteBulkHeader->Offset.HighPart, ByteOffsetAsLI.HighPart );
  561. SmbPutUlong( &WriteBulkHeader->DataCount, SendBufferLength );
  562. SmbPutUlong( &WriteBulkHeader->Remaining, RemainingByteCount );
  563. Status = MRxSmbInitializeWriteBulkDataExchange(
  564. &pWriteBulkDataExchange,
  565. OrdinaryExchange,
  566. pWriteBulkDataRequestSmbHeader,
  567. WriteBulkHeader,
  568. SourceMdl,
  569. SendBufferLength,
  570. ThisBufferOffset,
  571. RemainingByteCount);
  572. // Advance offset and reduce the number of bytes written.
  573. ByteOffsetAsLI.QuadPart += SendBufferLength;
  574. ThisBufferOffset += SendBufferLength;
  575. rw->PartialBytes += SendBufferLength;
  576. if (Status == STATUS_SUCCESS) {
  577. ActiveWriteBulkDataRequests++;
  578. AssociatedExchangeCompletionHandlerActivated =
  579. ((ActiveWriteBulkDataRequests == MAXIMUM_CONCURRENT_WRITE_BULK_DATA_REQUESTS) ||
  580. (RemainingByteCount == 0));
  581. if (AssociatedExchangeCompletionHandlerActivated &&
  582. (RemainingByteCount == 0)) {
  583. OrdinaryExchange->OpSpecificState = SmbPseOEInnerIoStates_OperationCompleted;
  584. if (!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
  585. OrdinaryExchange->ContinuationRoutine = MRxSmbFinishWriteBulkData;
  586. }
  587. }
  588. Status = SmbCeInitiateAssociatedExchange(
  589. (PSMB_EXCHANGE)pWriteBulkDataExchange,
  590. AssociatedExchangeCompletionHandlerActivated);
  591. }
  592. if (!NT_SUCCESS(Status)) {
  593. RxDbgTrace( 0, Dbg, ("SmbPseExchangeReceive_default: SmbCeSend returned %lx\n",Status));
  594. goto FINALLY;
  595. }
  596. if (AssociatedExchangeCompletionHandlerActivated) {
  597. if (!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
  598. RxWaitSync( RxContext );
  599. Status = STATUS_SUCCESS;
  600. if (RemainingByteCount == 0) {
  601. break;
  602. } else {
  603. // Reinitialize the event
  604. KeInitializeEvent(
  605. &RxContext->SyncEvent,
  606. NotificationEvent,
  607. FALSE );
  608. ActiveWriteBulkDataRequests = 0;
  609. }
  610. } else {
  611. // Map the status to delay cleanup operations.
  612. Status = STATUS_PENDING;
  613. break;
  614. }
  615. }
  616. }
  617. FINALLY:
  618. if (Status != STATUS_PENDING) {
  619. OrdinaryExchange->OpSpecificState = SmbPseOEInnerIoStates_OperationCompleted;
  620. if (Status == STATUS_SUCCESS) {
  621. if(rw->CompressedRequest &&
  622. rw->CompressionTechnology) {
  623. PCOMPRESSED_DATA_INFO pCompressedDataInfo;
  624. pCompressedDataInfo = (PCOMPRESSED_DATA_INFO)
  625. ((PCHAR)rw->BulkBuffer - rw->DataOffset);
  626. rw->BytesReturned = pCompressedDataInfo->NumberOfChunks * MIN_CHUNK_SIZE;
  627. } else {
  628. rw->BytesReturned = rw->ThisByteCount;
  629. }
  630. } else {
  631. rw->BytesReturned = 0;
  632. }
  633. if (rw->CompressedRequest &&
  634. rw->BulkBuffer != NULL) {
  635. // Free buffer from start of CDI
  636. RxFreePool( (PCHAR)rw->BulkBuffer - rw->DataOffset );
  637. rw->BulkBuffer = NULL;
  638. }
  639. if ( rw->CompressedRequest &&
  640. rw->CompressionTechnology ) {
  641. SourceMdl = (PMDL)(((ULONG)StufferState->BufferBase +
  642. sizeof(SMB_HEADER) + 10 + headerLength) & ~7);
  643. MmPrepareMdlForReuse(SourceMdl);
  644. }
  645. if (!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
  646. KeInitializeEvent(
  647. &RxContext->SyncEvent,
  648. NotificationEvent,
  649. FALSE );
  650. }
  651. OrdinaryExchange->ContinuationRoutine = NULL;
  652. RxDbgTrace(
  653. 0,
  654. Dbg,
  655. ("OE %lx TBC %lx RBC %lx BR %lx TBO %lx\n",
  656. OrdinaryExchange,rw->ThisByteCount,
  657. rw->RemainingByteCount,
  658. rw->BytesReturned,
  659. rw->ThisBufferOffset));
  660. SmbCeDecrementPendingLocalOperationsAndFinalize((PSMB_EXCHANGE)OrdinaryExchange);
  661. if (!BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION)) {
  662. RxWaitSync( RxContext );
  663. } else {
  664. RxDbgTrace(
  665. 0,
  666. Dbg,
  667. ("ABWC: OE: %lx Status %lx\n",
  668. OrdinaryExchange,
  669. Status));
  670. }
  671. }
  672. RxDbgTrace(-1, Dbg, ("MRxSmbWriteBulkContinuation returning %08lx\n", Status ));
  673. return Status;
  674. }
  675. NTSTATUS
  676. MRxSmbBuildWriteBulkData (
  677. IN OUT PSMBSTUFFER_BUFFER_STATE StufferState,
  678. IN PLARGE_INTEGER ByteOffsetAsLI,
  679. IN UCHAR Sequence,
  680. IN ULONG ByteCount,
  681. IN ULONG Remaining
  682. )
  683. /*++
  684. Routine Description:
  685. This builds a WriteBulk SMB. We don't have to worry about login id and such
  686. since that is done by the connection engine....pretty neat huh? All we have
  687. to do is format up the bits.
  688. Arguments:
  689. StufferState - the state of the smbbuffer from the stuffer's point of view
  690. ByteOffsetAsLI - the byte offset in the file where we want to read
  691. Sequence - this WriteBulkData exchange sequence
  692. ByteCount - the length of the data to be written
  693. Return Value:
  694. NTSTATUS
  695. STATUS_SUCCESS
  696. STATUS_NOT_IMPLEMENTED something has appeared in the arguments that i can't handle
  697. Notes:
  698. --*/
  699. {
  700. NTSTATUS Status;
  701. PRX_CONTEXT RxContext = StufferState->RxContext;
  702. RxCaptureFcb;RxCaptureFobx;
  703. PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
  704. PNT_SMB_HEADER SmbHeader = (PNT_SMB_HEADER)(StufferState->BufferBase);
  705. PMRX_SRV_OPEN SrvOpen = capFobx->pSrvOpen;
  706. PMRX_SMB_SRV_OPEN smbSrvOpen = MRxSmbGetSrvOpenExtension(SrvOpen);
  707. ULONG OffsetLow,OffsetHigh;
  708. PAGED_CODE();
  709. RxDbgTrace(+1, Dbg, ("MRxSmbBuildWriteBulk\n", 0 ));
  710. ASSERT( NodeType(SrvOpen) == RDBSS_NTC_SRVOPEN );
  711. OffsetLow = ByteOffsetAsLI->LowPart;
  712. OffsetHigh = ByteOffsetAsLI->HighPart;
  713. StufferState->CurrentPosition = (PCHAR)(SmbHeader + 1);
  714. SmbHeader->Command = SMB_COM_WRITE_BULK_DATA;
  715. COVERED_CALL(MRxSmbStartSMBCommand( StufferState, SetInitialSMB_Never,
  716. SMB_COM_WRITE_BULK_DATA,
  717. SMB_REQUEST_SIZE(WRITE_BULK),
  718. NO_EXTRA_DATA,
  719. NO_SPECIAL_ALIGNMENT,
  720. RESPONSE_HEADER_SIZE_NOT_SPECIFIED,
  721. 0,0,0,0 STUFFERTRACE(Dbg,'FC'));
  722. );
  723. RxDbgTrace(0, Dbg,("First write bulk data status = %lu\n",Status));
  724. MRxSmbDumpStufferState (1000,"SMB w/WRITE BULK DATA before stuffing",StufferState);
  725. if ( FlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,LOWIO_READWRITEFLAG_PAGING_IO)) {
  726. SmbPutAlignedUshort(
  727. &SmbHeader->Flags2,
  728. SmbGetAlignedUshort(&SmbHeader->Flags2)|SMB_FLAGS2_PAGING_IO);
  729. }
  730. MRxSmbStuffSMB (StufferState,
  731. "0yywdDddB!",
  732. // 0 UCHAR WordCount; // Count of parameter words = 10
  733. Sequence, // y UCHAR Sequence; // Exchange sequence handle
  734. 0, // y UCHAR Reserved;
  735. smbSrvOpen->Fid, // w _USHORT( Fid ); // File handle
  736. ByteCount, // d _ULONG( DataCount ); // Count of bytes, replaces ByteCount
  737. SMB_OFFSET_CHECK(WRITE_BULK_DATA, Offset)
  738. OffsetLow, OffsetHigh, // Dd LARGE_INTEGER Offset; // Offset in file to begin write
  739. Remaining, // d _ULONG( Remaining ); // Bytes remaining to be written
  740. // B _USHORT( ByteCount ); // Count of data bytes = 0, not used
  741. SMB_WCT_CHECK(10) 0
  742. // UCHAR Buffer[1];
  743. );
  744. MRxSmbDumpStufferState (700,"SMB w/WRITE BULK DATA after stuffing",StufferState);
  745. FINALLY:
  746. RxDbgTraceUnIndent(-1, Dbg);
  747. return(Status);
  748. } // MRxSmbBuildWriteBulkData
  749. extern SMB_EXCHANGE_DISPATCH_VECTOR WriteBulkDataExchangeDispatchVector;
  750. NTSTATUS
  751. MRxSmbInitializeWriteBulkDataExchange(
  752. PSMB_WRITE_BULK_DATA_EXCHANGE *pWriteBulkDataExchangePointer,
  753. PSMB_PSE_ORDINARY_EXCHANGE pWriteExchange,
  754. PSMB_HEADER pSmbHeader,
  755. PREQ_WRITE_BULK_DATA pWriteBulkDataRequest,
  756. PMDL pDataMdl,
  757. ULONG DataSizeInBytes,
  758. ULONG DataOffsetInBytes,
  759. ULONG RemainingDataInBytes)
  760. {
  761. NTSTATUS Status = STATUS_SUCCESS;
  762. ULONG HeaderMdlSize;
  763. ULONG DataMdlSize;
  764. ULONG WriteBulkDataExchangeSize;
  765. PSMB_WRITE_BULK_DATA_EXCHANGE pWriteBulkDataExchange;
  766. PAGED_CODE();
  767. HeaderMdlSize = MmSizeOfMdl(
  768. 0,
  769. sizeof(SMB_HEADER) + TRANSPORT_HEADER_SIZE + FIELD_OFFSET(REQ_WRITE_BULK_DATA,Buffer));
  770. DataMdlSize = MmSizeOfMdl(
  771. 0,
  772. DataSizeInBytes);
  773. WriteBulkDataExchangeSize = FIELD_OFFSET(SMB_WRITE_BULK_DATA_EXCHANGE,Buffer) +
  774. HeaderMdlSize +
  775. DataMdlSize +
  776. TRANSPORT_HEADER_SIZE +
  777. sizeof(SMB_HEADER) +
  778. FIELD_OFFSET(REQ_WRITE_BULK_DATA,Buffer);
  779. pWriteBulkDataExchange = (PSMB_WRITE_BULK_DATA_EXCHANGE)
  780. SmbMmAllocateVariableLengthExchange(
  781. WRITE_BULK_DATA_EXCHANGE,
  782. WriteBulkDataExchangeSize);
  783. if (pWriteBulkDataExchange != NULL) {
  784. pWriteBulkDataExchange->pHeaderMdl =
  785. (PMDL)((PBYTE)pWriteBulkDataExchange +
  786. FIELD_OFFSET(SMB_WRITE_BULK_DATA_EXCHANGE,Buffer));
  787. pWriteBulkDataExchange->pDataMdl =
  788. (PMDL)((PBYTE)pWriteBulkDataExchange->pHeaderMdl + HeaderMdlSize);
  789. pWriteBulkDataExchange->pHeader =
  790. (PSMB_HEADER)((PBYTE)pWriteBulkDataExchange->pDataMdl +
  791. DataMdlSize + TRANSPORT_HEADER_SIZE);
  792. pWriteBulkDataExchange->pWriteBulkDataRequest =
  793. (PREQ_WRITE_BULK_DATA)(pWriteBulkDataExchange->pHeader + 1);
  794. pWriteBulkDataExchange->WriteBulkDataRequestLength =
  795. sizeof(SMB_HEADER) +
  796. FIELD_OFFSET(REQ_WRITE_BULK_DATA,Buffer) +
  797. DataSizeInBytes;
  798. RtlCopyMemory(
  799. pWriteBulkDataExchange->pHeader,
  800. pSmbHeader,
  801. sizeof(SMB_HEADER));
  802. RtlCopyMemory(
  803. pWriteBulkDataExchange->pWriteBulkDataRequest,
  804. pWriteBulkDataRequest,
  805. FIELD_OFFSET(REQ_WRITE_BULK_DATA,Buffer));
  806. RxInitializeHeaderMdl(
  807. pWriteBulkDataExchange->pHeaderMdl,
  808. pWriteBulkDataExchange->pHeader,
  809. sizeof(SMB_HEADER) + FIELD_OFFSET(REQ_WRITE_BULK_DATA,Buffer));
  810. RxBuildHeaderMdlForNonPagedPool(pWriteBulkDataExchange->pHeaderMdl);
  811. IoBuildPartialMdl(
  812. pDataMdl,
  813. pWriteBulkDataExchange->pDataMdl,
  814. (PBYTE)MmGetMdlVirtualAddress(pDataMdl) + DataOffsetInBytes,
  815. DataSizeInBytes);
  816. RxDbgTrace(
  817. 0,
  818. Dbg,
  819. ("Bulk Data O: %lx, Partial %lx Offset %lx Size %lx\n",
  820. pDataMdl->MappedSystemVa,
  821. pWriteBulkDataExchange->pDataMdl->MappedSystemVa,
  822. DataOffsetInBytes,
  823. DataSizeInBytes));
  824. pWriteBulkDataExchange->pHeaderMdl->Next = pWriteBulkDataExchange->pDataMdl;
  825. pWriteBulkDataExchange->pDataMdl->Next = NULL;
  826. // Initialize the associated exchange.
  827. Status = SmbCeInitializeAssociatedExchange(
  828. (PSMB_EXCHANGE *)&pWriteBulkDataExchange,
  829. (PSMB_EXCHANGE)pWriteExchange,
  830. WRITE_BULK_DATA_EXCHANGE,
  831. &WriteBulkDataExchangeDispatchVector);
  832. if (Status == STATUS_SUCCESS) {
  833. pWriteBulkDataExchange->Mid = pWriteExchange->Mid;
  834. SetFlag(
  835. pWriteBulkDataExchange->SmbCeFlags,
  836. (SMBCE_EXCHANGE_MID_VALID | SMBCE_EXCHANGE_RETAIN_MID));
  837. *pWriteBulkDataExchangePointer = pWriteBulkDataExchange;
  838. } else {
  839. BOOLEAN PostRequest = FALSE;
  840. MRxSmbWriteBulkDataExchangeFinalize(
  841. (PSMB_EXCHANGE)pWriteBulkDataExchange,
  842. &PostRequest);
  843. }
  844. }
  845. return Status;
  846. }
  847. NTSTATUS
  848. MRxSmbWriteBulkDataExchangeStart(
  849. IN struct _SMB_EXCHANGE *pExchange)
  850. /*++
  851. Routine Description:
  852. This routine initiates the wriet bulk data exchange operation
  853. Arguments:
  854. pExchange - pointer to the bulk write data exchange instance.
  855. Return Value:
  856. STATUS_SUCCESS if successful.
  857. Notes:
  858. --*/
  859. {
  860. NTSTATUS Status;
  861. PSMB_WRITE_BULK_DATA_EXCHANGE pWriteBulkDataExchange;
  862. PAGED_CODE();
  863. pWriteBulkDataExchange = (PSMB_WRITE_BULK_DATA_EXCHANGE)pExchange;
  864. IF_DEBUG {
  865. ULONG Length = 0;
  866. PMDL pTempMdl;
  867. pTempMdl = pWriteBulkDataExchange->pHeaderMdl;
  868. while (pTempMdl != NULL) {
  869. Length += pTempMdl->ByteCount;
  870. pTempMdl = pTempMdl->Next;
  871. }
  872. ASSERT(Length == pWriteBulkDataExchange->WriteBulkDataRequestLength);
  873. }
  874. Status = SmbCeSend(
  875. pExchange,
  876. 0,
  877. pWriteBulkDataExchange->pHeaderMdl,
  878. pWriteBulkDataExchange->WriteBulkDataRequestLength);
  879. if ((Status != STATUS_PENDING) &&
  880. (Status != STATUS_SUCCESS)) {
  881. BOOLEAN PostRequest = FALSE;
  882. MRxSmbWriteBulkDataExchangeFinalize(
  883. (PSMB_EXCHANGE)pWriteBulkDataExchange,
  884. &PostRequest);
  885. }
  886. return Status;
  887. }
  888. NTSTATUS
  889. MRxSmbWriteBulkDataExchangeSendCompletionHandler(
  890. IN struct _SMB_EXCHANGE *pExchange, // The exchange instance
  891. IN PMDL pDataBuffer,
  892. IN NTSTATUS SendCompletionStatus
  893. )
  894. /*++
  895. Routine Description:
  896. This routine handles send completionsn for the write bulk data exchange
  897. operation
  898. Arguments:
  899. pExchange - pointer to the bulk write data exchange instance.
  900. pDataBuffer - the buffer which was transmitted
  901. SendCompletionStatus - the completion status
  902. Return Value:
  903. STATUS_SUCCESS if successful.
  904. Notes:
  905. --*/
  906. {
  907. RxDbgTrace(
  908. 0,
  909. Dbg,
  910. ("send completion Associated Write Data Exchange %lx\n",
  911. pExchange));
  912. return STATUS_SUCCESS;
  913. }
  914. NTSTATUS
  915. MRxSmbWriteBulkDataExchangeFinalize(
  916. IN OUT struct _SMB_EXCHANGE *pExchange,
  917. OUT BOOLEAN *pPostRequest)
  918. /*++
  919. Routine Description:
  920. This routine handles the finalization of the write bulk data exchange
  921. Arguments:
  922. pExchange - pointer to the bulk write data exchange instance.
  923. pPostRequest - set to TRUE if the request is to be posted to a worker thread
  924. Return Value:
  925. STATUS_SUCCESS if successful.
  926. Notes:
  927. --*/
  928. {
  929. PAGED_CODE();
  930. if (!RxShouldPostCompletion()) {
  931. PSMB_WRITE_BULK_DATA_EXCHANGE pWriteBulkDataExchange;
  932. pWriteBulkDataExchange = (PSMB_WRITE_BULK_DATA_EXCHANGE)pExchange;
  933. RxDbgTrace(
  934. 0,
  935. Dbg,
  936. ("Finalizing Associated Write Data Exchange %lx\n",
  937. pWriteBulkDataExchange));
  938. MmPrepareMdlForReuse(
  939. pWriteBulkDataExchange->pHeaderMdl);
  940. MmPrepareMdlForReuse(
  941. pWriteBulkDataExchange->pDataMdl);
  942. ClearFlag(
  943. pWriteBulkDataExchange->SmbCeFlags,
  944. (SMBCE_EXCHANGE_MID_VALID | SMBCE_EXCHANGE_RETAIN_MID));
  945. SmbCeDiscardExchange(pExchange);
  946. *pPostRequest = FALSE;
  947. } else {
  948. *pPostRequest = TRUE;
  949. }
  950. return STATUS_SUCCESS;
  951. }
  952. SMB_EXCHANGE_DISPATCH_VECTOR
  953. WriteBulkDataExchangeDispatchVector =
  954. {
  955. MRxSmbWriteBulkDataExchangeStart,
  956. NULL,
  957. NULL,
  958. MRxSmbWriteBulkDataExchangeSendCompletionHandler,
  959. MRxSmbWriteBulkDataExchangeFinalize,
  960. NULL
  961. };
  962.