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.

4013 lines
107 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. smbmpx.c
  5. Abstract:
  6. This module contains routines for processing the following SMBs:
  7. Read Block Multiplexed
  8. Write Block Multiplexed
  9. Note that core and raw mode SMB processors are not contained in this
  10. module. Check smbrdwrt.c and smbraw.c instead. SMB commands that
  11. pertain exclusively to locking (LockByteRange, UnlockByteRange, and
  12. LockingAndX) are processed in smblock.c.
  13. Author:
  14. Chuck Lenzmeier (chuckl) 4-Nov-1993
  15. Revision History:
  16. --*/
  17. #include "precomp.h"
  18. #include "smbmpx.tmh"
  19. #pragma hdrstop
  20. #define BugCheckFileId SRV_FILE_SMBMPX
  21. #if 0
  22. BOOLEAN MpxDelay = TRUE;
  23. #endif
  24. //
  25. // Stack overflow threshold. This is used to determine when we are
  26. // getting close to the end of our stack and need to stop recursing
  27. // in SendCopy/MdlReadMpxFragment.
  28. //
  29. #define STACK_THRESHOLD 0xE00
  30. //
  31. // Forward declarations
  32. //
  33. VOID SRVFASTCALL
  34. RestartReadMpx (
  35. IN OUT PWORK_CONTEXT WorkContext
  36. );
  37. NTSTATUS
  38. SendCopyReadMpxFragment (
  39. IN PDEVICE_OBJECT DeviceObject,
  40. IN PIRP Irp,
  41. IN OUT PWORK_CONTEXT WorkContext
  42. );
  43. VOID SRVFASTCALL
  44. SendCopyReadMpxFragment2 (
  45. IN OUT PWORK_CONTEXT
  46. );
  47. NTSTATUS
  48. SendMdlReadMpxFragment (
  49. IN PDEVICE_OBJECT DeviceObject,
  50. IN PIRP Irp,
  51. IN OUT PWORK_CONTEXT WorkContext
  52. );
  53. VOID SRVFASTCALL
  54. SendMdlReadMpxFragment2 (
  55. IN OUT PWORK_CONTEXT WorkContext
  56. );
  57. VOID SRVFASTCALL
  58. RestartMdlReadMpxComplete (
  59. IN OUT PWORK_CONTEXT WorkContext
  60. );
  61. VOID SRVFASTCALL
  62. RestartWriteMpx (
  63. IN OUT PWORK_CONTEXT WorkContext
  64. );
  65. BOOLEAN
  66. CheckForWriteMpxComplete (
  67. IN OUT PWORK_CONTEXT WorkContext
  68. );
  69. VOID SRVFASTCALL
  70. RestartPrepareMpxMdlWrite (
  71. IN OUT PWORK_CONTEXT WorkContext
  72. );
  73. VOID SRVFASTCALL
  74. AddPacketToGlom (
  75. IN OUT PWORK_CONTEXT WorkContext
  76. );
  77. VOID SRVFASTCALL
  78. RestartAfterGlomDelay (
  79. IN OUT PWORK_CONTEXT WorkContext
  80. );
  81. VOID SRVFASTCALL
  82. RestartCompleteGlommingInIndication(
  83. IN OUT PWORK_CONTEXT WorkContext
  84. );
  85. VOID SRVFASTCALL
  86. RestartWriteMpxCompleteRfcbClose (
  87. IN OUT PWORK_CONTEXT WorkContext
  88. );
  89. VOID SRVFASTCALL
  90. WriteMpxMdlWriteComplete (
  91. IN OUT PWORK_CONTEXT WorkContext
  92. );
  93. #ifdef ALLOC_PRAGMA
  94. #pragma alloc_text( PAGE, SrvSmbReadMpx )
  95. #pragma alloc_text( PAGE, RestartMdlReadMpxComplete )
  96. #pragma alloc_text( PAGE, SrvRestartReceiveWriteMpx )
  97. #pragma alloc_text( PAGE, SrvSmbWriteMpxSecondary )
  98. #pragma alloc_text( PAGE, SendCopyReadMpxFragment2 )
  99. #pragma alloc_text( PAGE, SendMdlReadMpxFragment2 )
  100. #pragma alloc_text( PAGE8FIL, RestartReadMpx )
  101. #pragma alloc_text( PAGE8FIL, SendCopyReadMpxFragment )
  102. #pragma alloc_text( PAGE8FIL, RestartCopyReadMpxComplete )
  103. #pragma alloc_text( PAGE8FIL, SendMdlReadMpxFragment )
  104. #endif
  105. #if 0
  106. NOT PAGEABLE -- SrvSmbWriteMpx
  107. NOT PAGEABLE -- RestartWriteMpx
  108. NOT PAGEABLE -- CheckForWriteMpxComplete
  109. NOT PAGEABLE -- RestartCompleteGlommingInIndication
  110. NOT PAGEABLE -- RestartWriteMpxCompleteRfcbClose
  111. NOT PAGEABLE -- WriteMpxMdlWriteComplete
  112. #endif
  113. #if DBG
  114. VOID
  115. DumpMdlChain(
  116. IN PMDL mdl
  117. );
  118. #endif
  119. SMB_PROCESSOR_RETURN_TYPE
  120. SrvSmbReadMpx (
  121. SMB_PROCESSOR_PARAMETERS
  122. )
  123. /*++
  124. Routine Description:
  125. Processes the Read Mpx SMB.
  126. Arguments:
  127. WorkContext - Supplies a pointer to the work context block
  128. representing the work item
  129. Return Value:
  130. None.
  131. --*/
  132. {
  133. PSMB_HEADER header;
  134. PREQ_READ_MPX request;
  135. NTSTATUS status = STATUS_SUCCESS;
  136. SMB_STATUS SmbStatus = SmbStatusInProgress;
  137. USHORT fid;
  138. PRFCB rfcb;
  139. PLFCB lfcb;
  140. ULONG bufferOffset;
  141. PCHAR readAddress;
  142. CLONG readLength;
  143. ULONG key;
  144. LARGE_INTEGER offset;
  145. PMDL mdl;
  146. PVOID mpxBuffer;
  147. UCHAR minorFunction;
  148. PAGED_CODE( );
  149. if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
  150. WorkContext->PreviousSMB = EVENT_TYPE_SMB_READ_MPX;
  151. SrvWmiStartContext(WorkContext);
  152. header = WorkContext->RequestHeader;
  153. request = (PREQ_READ_MPX)WorkContext->RequestParameters;
  154. fid = SmbGetUshort( &request->Fid );
  155. IF_SMB_DEBUG(MPX1) {
  156. KdPrint(( "Read Block Multiplexed request; FID 0x%lx, "
  157. "count %ld, offset %ld\n",
  158. fid, SmbGetUshort( &request->MaxCount ),
  159. SmbGetUlong( &request->Offset ) ));
  160. }
  161. //
  162. // Verify the FID. If verified, the RFCB is referenced and its
  163. // address is stored in the WorkContext block, and the RFCB address
  164. // is returned.
  165. //
  166. rfcb = SrvVerifyFid(
  167. WorkContext,
  168. fid,
  169. TRUE,
  170. SrvRestartSmbReceived, // serialize with raw write
  171. &status
  172. );
  173. if ( rfcb == SRV_INVALID_RFCB_POINTER ) {
  174. if ( !NT_SUCCESS( status )) {
  175. //
  176. // Invalid file ID or write behind error. Reject the request.
  177. //
  178. IF_DEBUG(ERRORS) {
  179. KdPrint((
  180. "SrvSmbReadMpx: Status %X on FID: 0x%lx\n",
  181. status,
  182. fid
  183. ));
  184. }
  185. SrvSetSmbError( WorkContext, status );
  186. SmbStatus = SmbStatusSendResponse;
  187. goto Cleanup;
  188. }
  189. //
  190. // The work item has been queued because a raw write is in
  191. // progress.
  192. //
  193. SmbStatus = SmbStatusInProgress;
  194. goto Cleanup;
  195. }
  196. lfcb = rfcb->Lfcb;
  197. if( lfcb->Session->IsSessionExpired )
  198. {
  199. status = SESSION_EXPIRED_STATUS_CODE;
  200. SrvSetSmbError( WorkContext, status );
  201. SmbStatus = SmbStatusSendResponse;
  202. goto Cleanup;
  203. }
  204. //
  205. // Verify that the client has read access to the file via the
  206. // specified handle.
  207. //
  208. if( rfcb->MpxReadsOk == FALSE ) {
  209. if ( !rfcb->ReadAccessGranted ) {
  210. CHECK_PAGING_IO_ACCESS(
  211. WorkContext,
  212. rfcb->GrantedAccess,
  213. &status );
  214. if ( !NT_SUCCESS( status ) ) {
  215. SrvStatistics.GrantedAccessErrors++;
  216. IF_DEBUG(ERRORS) {
  217. KdPrint(( "SrvSmbReadMpx: Read access not granted.\n"));
  218. }
  219. SrvSetSmbError( WorkContext, status );
  220. SmbStatus = SmbStatusSendResponse;
  221. goto Cleanup;
  222. }
  223. }
  224. //
  225. // If this is not a disk file, tell the client to use core read.
  226. //
  227. if ( rfcb->ShareType != ShareTypeDisk ) {
  228. SrvSetSmbError( WorkContext, STATUS_SMB_USE_STANDARD );
  229. status = STATUS_SMB_USE_STANDARD;
  230. SmbStatus = SmbStatusSendResponse;
  231. goto Cleanup;
  232. }
  233. rfcb->MpxReadsOk = TRUE;
  234. }
  235. //
  236. // Form the lock key using the FID and the PID.
  237. //
  238. // *** The FID must be included in the key in order to account for
  239. // the folding of multiple remote compatibility mode opens into
  240. // a single local open.
  241. //
  242. key = rfcb->ShiftedFid | SmbGetAlignedUshort( &header->Pid );
  243. //
  244. // See if the direct host IPX smart card can handle this read. If so,
  245. // return immediately, and the card will call our restart routine at
  246. // SrvIpxSmartCardReadComplete
  247. //
  248. if( rfcb->PagedRfcb->IpxSmartCardContext ) {
  249. IF_DEBUG( SIPX ) {
  250. KdPrint(( "SrvSmbReadMpx: calling SmartCard Read for context %p\n",
  251. WorkContext ));
  252. }
  253. WorkContext->Parameters.SmartCardRead.MdlReadComplete = lfcb->MdlReadComplete;
  254. WorkContext->Parameters.SmartCardRead.DeviceObject = lfcb->DeviceObject;
  255. if( SrvIpxSmartCard.Read( WorkContext->RequestBuffer->Buffer,
  256. rfcb->PagedRfcb->IpxSmartCardContext,
  257. key,
  258. WorkContext ) == TRUE ) {
  259. IF_DEBUG( SIPX ) {
  260. KdPrint(( " SrvSmbReadMpx: SmartCard Read returns TRUE\n" ));
  261. }
  262. SmbStatus = SmbStatusInProgress;
  263. goto Cleanup;
  264. }
  265. IF_DEBUG( SIPX ) {
  266. KdPrint(( " SrvSmbReadMpx: SmartCard Read returns FALSE\n" ));
  267. }
  268. }
  269. //
  270. // Get the file offset.
  271. //
  272. WorkContext->Parameters.ReadMpx.Offset = SmbGetUlong( &request->Offset );
  273. offset.QuadPart = WorkContext->Parameters.ReadMpx.Offset;
  274. //
  275. // Calculate the address in the buffer at which to put the data.
  276. // This must be rounded up to a dword boundary. (The -1 below is
  277. // because sizeof(RESP_READ_MPX) includes one byte of Buffer.)
  278. //
  279. bufferOffset = (sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX, Buffer) + 3) & ~3;
  280. //
  281. // Calculate how much data we can send back in each fragment. This
  282. // is the size of the client's buffer, rounded down to a dword multiple.
  283. //
  284. // *** Because we use the SMB buffer's partial MDL to describe the
  285. // data fragments that we return, we need to limit the fragment
  286. // size to the SMB buffer size. Normally the client's buffer
  287. // size is <= ours, so this shouldn't be a factor.
  288. //
  289. WorkContext->Parameters.ReadMpx.FragmentSize =
  290. (USHORT)((MIN( lfcb->Session->MaxBufferSize,
  291. SrvReceiveBufferLength ) - bufferOffset) & ~3);
  292. //
  293. // If the SMB buffer is large enough, use it to do the local read.
  294. //
  295. readLength = SmbGetUshort( &request->MaxCount );
  296. if ( //0 &&
  297. (readLength <= SrvMpxMdlReadSwitchover) ) {
  298. do_copy_read:
  299. WorkContext->Parameters.ReadMpx.MdlRead = FALSE;
  300. WorkContext->Parameters.ReadMpx.MpxBuffer = NULL;
  301. WorkContext->Parameters.ReadMpx.MpxBufferMdl =
  302. WorkContext->ResponseBuffer->Mdl;
  303. readAddress = (PCHAR)WorkContext->ResponseHeader + bufferOffset;
  304. WorkContext->Parameters.ReadMpx.NextFragmentAddress = readAddress;
  305. //
  306. // Try the fast I/O path first.
  307. //
  308. if ( lfcb->FastIoRead != NULL ) {
  309. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
  310. try {
  311. if ( lfcb->FastIoRead(
  312. lfcb->FileObject,
  313. &offset,
  314. readLength,
  315. TRUE,
  316. key,
  317. readAddress,
  318. &WorkContext->Irp->IoStatus,
  319. lfcb->DeviceObject
  320. ) ) {
  321. //
  322. // The fast I/O path worked. Send the data.
  323. //
  324. WorkContext->bAlreadyTrace = TRUE;
  325. RestartReadMpx( WorkContext );
  326. SmbStatus = SmbStatusInProgress;
  327. goto Cleanup;
  328. }
  329. }
  330. except( EXCEPTION_EXECUTE_HANDLER ) {
  331. // Fall through to the slow path on an exception
  332. NTSTATUS status = GetExceptionCode();
  333. IF_DEBUG(ERRORS) {
  334. KdPrint(("FastIoRead threw exception %x\n", status ));
  335. }
  336. }
  337. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
  338. }
  339. //
  340. // The fast I/O path failed, so we need to use a regular copy
  341. // I/O request. Build an MDL describing the read buffer.
  342. //
  343. // *** Note the assumption that the response buffer already has
  344. // a valid full MDL from which a partial MDL can be built.
  345. //
  346. IoBuildPartialMdl(
  347. WorkContext->ResponseBuffer->Mdl,
  348. WorkContext->ResponseBuffer->PartialMdl,
  349. readAddress,
  350. readLength
  351. );
  352. mdl = WorkContext->ResponseBuffer->PartialMdl;
  353. minorFunction = 0;
  354. } else {
  355. //
  356. // The SMB buffer isn't big enough. Does the target file system
  357. // support the cache manager routines?
  358. //
  359. if ( //0 &&
  360. (lfcb->FileObject->Flags & FO_CACHE_SUPPORTED) ) {
  361. WorkContext->Parameters.ReadMpx.MdlRead = TRUE;
  362. //
  363. // We can use an MDL read. Try the fast I/O path first.
  364. //
  365. WorkContext->Irp->MdlAddress = NULL;
  366. WorkContext->Irp->IoStatus.Information = 0;
  367. WorkContext->Parameters.ReadMpx.ReadLength = readLength;
  368. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
  369. if ( lfcb->MdlRead(
  370. lfcb->FileObject,
  371. &offset,
  372. readLength,
  373. key,
  374. &WorkContext->Irp->MdlAddress,
  375. &WorkContext->Irp->IoStatus,
  376. lfcb->DeviceObject
  377. ) && WorkContext->Irp->MdlAddress != NULL ) {
  378. //
  379. // The fast I/O path worked. Send the data.
  380. //
  381. WorkContext->bAlreadyTrace = TRUE;
  382. RestartReadMpx( WorkContext );
  383. SmbStatus = SmbStatusInProgress;
  384. goto Cleanup;
  385. }
  386. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
  387. //
  388. // The fast I/O path failed. We need to issue a regular MDL
  389. // read request.
  390. //
  391. // The fast path may have partially succeeded, returning a
  392. // partial MDL chain. We need to adjust our read request
  393. // to account for that.
  394. //
  395. offset.QuadPart += WorkContext->Irp->IoStatus.Information;
  396. readLength -= (ULONG)WorkContext->Irp->IoStatus.Information;
  397. mdl = WorkContext->Irp->MdlAddress;
  398. minorFunction = IRP_MN_MDL;
  399. } else if (readLength > (WorkContext->ResponseBuffer->BufferLength -
  400. bufferOffset)) {
  401. //
  402. // We have to use a normal "copy" read. We need to allocate
  403. // a separate buffer.
  404. //
  405. WorkContext->Parameters.ReadMpx.MdlRead = FALSE;
  406. mpxBuffer = ALLOCATE_NONPAGED_POOL(
  407. readLength,
  408. BlockTypeDataBuffer
  409. );
  410. if ( mpxBuffer == NULL ) {
  411. SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
  412. status = STATUS_INSUFF_SERVER_RESOURCES;
  413. SmbStatus = SmbStatusSendResponse;
  414. goto Cleanup;
  415. }
  416. WorkContext->Parameters.ReadMpx.MpxBuffer = mpxBuffer;
  417. WorkContext->Parameters.ReadMpx.NextFragmentAddress = mpxBuffer;
  418. readAddress = mpxBuffer;
  419. //
  420. // We also need an MDL to describe the buffer.
  421. //
  422. mdl = IoAllocateMdl( mpxBuffer, readLength, FALSE, FALSE, NULL );
  423. if ( mdl == NULL ) {
  424. DEALLOCATE_NONPAGED_POOL( mpxBuffer );
  425. SrvSetSmbError( WorkContext, STATUS_INSUFF_SERVER_RESOURCES );
  426. status = STATUS_INSUFF_SERVER_RESOURCES;
  427. SmbStatus = SmbStatusSendResponse;
  428. goto Cleanup;
  429. }
  430. WorkContext->Parameters.ReadMpx.MpxBufferMdl = mdl;
  431. //
  432. // Build the mdl.
  433. //
  434. MmBuildMdlForNonPagedPool( mdl );
  435. //
  436. // Try the fast I/O path first.
  437. //
  438. if ( lfcb->FastIoRead != NULL ) {
  439. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsAttempted );
  440. try {
  441. if ( lfcb->FastIoRead(
  442. lfcb->FileObject,
  443. &offset,
  444. readLength,
  445. TRUE,
  446. key,
  447. mpxBuffer,
  448. &WorkContext->Irp->IoStatus,
  449. lfcb->DeviceObject
  450. ) ) {
  451. //
  452. // The fast I/O path worked. Send the data.
  453. //
  454. WorkContext->bAlreadyTrace = TRUE;
  455. RestartReadMpx( WorkContext );
  456. SmbStatus = SmbStatusInProgress;
  457. goto Cleanup;
  458. }
  459. }
  460. except( EXCEPTION_EXECUTE_HANDLER ) {
  461. // Fall through to the slow path on an exception
  462. NTSTATUS status = GetExceptionCode();
  463. IF_DEBUG(ERRORS) {
  464. KdPrint(("FastIoRead threw exception %x\n", status ));
  465. }
  466. }
  467. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastReadsFailed );
  468. }
  469. //
  470. // The fast I/O path failed, so we need to use a regular copy
  471. // I/O request.
  472. //
  473. minorFunction = 0;
  474. } else {
  475. goto do_copy_read;
  476. }
  477. } // read fits in SMB buffer?
  478. //
  479. // Build the read request, reusing the receive IRP.
  480. //
  481. SrvBuildReadOrWriteRequest(
  482. WorkContext->Irp, // input IRP address
  483. lfcb->FileObject, // target file object address
  484. WorkContext, // context
  485. IRP_MJ_READ, // major function code
  486. minorFunction, // minor function code
  487. readAddress, // buffer address
  488. readLength, // buffer length
  489. mdl, // MDL address
  490. offset, // byte offset
  491. key // lock key
  492. );
  493. //
  494. // Pass the request to the file system.
  495. //
  496. WorkContext->bAlreadyTrace = TRUE;
  497. WorkContext->FsdRestartRoutine = RestartReadMpx;
  498. DEBUG WorkContext->FspRestartRoutine = NULL;
  499. (VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
  500. //
  501. // The read has been started. Control will return to the restart
  502. // routine when the read completes.
  503. //
  504. SmbStatus = SmbStatusInProgress;
  505. Cleanup:
  506. SrvWmiEndContext(WorkContext);
  507. return SmbStatus;
  508. } // SrvSmbReadMpx
  509. VOID SRVFASTCALL
  510. RestartReadMpx (
  511. IN OUT PWORK_CONTEXT WorkContext
  512. )
  513. /*++
  514. Routine Description:
  515. Processes file read completion for a Read MPX SMB.
  516. Arguments:
  517. WorkContext - Supplies a pointer to the work context block
  518. describing server-specific context for the request.
  519. Return Value:
  520. None.
  521. --*/
  522. {
  523. PRESP_READ_MPX response;
  524. NTSTATUS status = STATUS_SUCCESS;
  525. SMB_STATUS SmbStatus = SmbStatusInProgress;
  526. LARGE_INTEGER position;
  527. KIRQL oldIrql;
  528. USHORT readLength;
  529. ULONG offset;
  530. PMDL mdl;
  531. BOOLEAN mdlRead;
  532. PIRP irp = WorkContext->Irp;
  533. BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
  534. UNLOCKABLE_CODE( 8FIL );
  535. if (bNeedTrace) {
  536. if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
  537. WorkContext->PreviousSMB = EVENT_TYPE_SMB_READ_MPX;
  538. SrvWmiStartContext(WorkContext);
  539. }
  540. else
  541. WorkContext->bAlreadyTrace = FALSE;
  542. IF_DEBUG(FSD2) KdPrint(( " - RestartReadMpx\n" ));
  543. //
  544. // If we just completed an MDL read, we need to remember the address
  545. // of the first MDL in the returned chain, so that we can give it
  546. // back to the cache manager when we're done.
  547. //
  548. mdlRead = WorkContext->Parameters.ReadMpx.MdlRead;
  549. if ( mdlRead ) {
  550. mdl = irp->MdlAddress;
  551. //KdPrint(( "Read MDL chain:\n" ));
  552. //DumpMdlChain( mdl );
  553. WorkContext->Parameters.ReadMpx.FirstMdl = mdl;
  554. }
  555. //
  556. // If the read failed, set an error status in the response header.
  557. // (If we tried to read entirely beyond the end of file, we return a
  558. // normal response indicating that nothing was read.)
  559. //
  560. status = irp->IoStatus.Status;
  561. if ( !NT_SUCCESS(status) && (status != STATUS_END_OF_FILE) ) {
  562. IF_DEBUG(ERRORS) KdPrint(( "Read failed: %X\n", status ));
  563. if ( KeGetCurrentIrql() >= DISPATCH_LEVEL ) {
  564. WorkContext->FspRestartRoutine = RestartReadMpx;
  565. SrvQueueWorkToFsp( WorkContext );
  566. goto Cleanup;
  567. }
  568. SrvSetSmbError( WorkContext, status );
  569. respond:
  570. if ( mdlRead ) {
  571. SrvFsdSendResponse2( WorkContext, RestartMdlReadMpxComplete );
  572. } else {
  573. WorkContext->ResponseBuffer->DataLength =
  574. (CLONG)( (PCHAR)WorkContext->ResponseParameters -
  575. (PCHAR)WorkContext->ResponseHeader );
  576. WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
  577. SRV_START_SEND_2(
  578. WorkContext,
  579. RestartCopyReadMpxComplete,
  580. NULL,
  581. NULL );
  582. }
  583. goto Cleanup;
  584. }
  585. //
  586. // Get the amount of data actually read.
  587. //
  588. if ( status == STATUS_END_OF_FILE ) {
  589. //
  590. // The read started beyond the end of the file.
  591. //
  592. readLength = 0;
  593. } else if ( mdlRead ) {
  594. //
  595. // For an MDL read, we have to walk the MDL chain in order to
  596. // determine how much data was read. This is because the
  597. // operation may have happened in multiple steps, with the MDLs
  598. // being chained together. For example, part of the read may
  599. // have been satisfied by the fast path, while the rest was
  600. // satisfied using an IRP.
  601. //
  602. readLength = 0;
  603. while ( mdl != NULL ) {
  604. readLength += (USHORT)MmGetMdlByteCount(mdl);
  605. mdl = mdl->Next;
  606. }
  607. } else {
  608. //
  609. // Copy read. The I/O status block has the length.
  610. //
  611. readLength = (USHORT)irp->IoStatus.Information;
  612. }
  613. //
  614. // Update the file position.
  615. //
  616. offset = WorkContext->Parameters.ReadMpx.Offset;
  617. WorkContext->Rfcb->CurrentPosition = offset + readLength;
  618. //
  619. // Update statistics.
  620. //
  621. UPDATE_READ_STATS( WorkContext, readLength );
  622. //
  623. // Special-case 0 bytes read.
  624. //
  625. response = (PRESP_READ_MPX)WorkContext->ResponseParameters;
  626. response->WordCount = 8;
  627. SmbPutUshort( &response->DataCompactionMode, 0 );
  628. SmbPutUshort( &response->Reserved, 0 );
  629. if ( readLength == 0 ) {
  630. SmbPutUlong( &response->Offset, offset );
  631. SmbPutUshort( &response->Count, 0 );
  632. SmbPutUshort( &response->Remaining, 0 );
  633. SmbPutUshort( &response->DataLength, 0 );
  634. SmbPutUshort( &response->DataOffset, 0 );
  635. SmbPutUshort( &response->ByteCount, 0 );
  636. WorkContext->ResponseParameters = NEXT_LOCATION(
  637. response,
  638. RESP_READ_MPX,
  639. 0
  640. );
  641. goto respond;
  642. }
  643. //
  644. // Build the static response header/parameters.
  645. //
  646. SmbPutUshort( &response->Count, readLength );
  647. SmbPutUshort(
  648. &response->DataOffset,
  649. (sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX, Buffer) + 3) & ~3
  650. );
  651. //
  652. // We will use two MDLs to describe the packet we're sending -- one
  653. // for the header and parameters, and another for the data. So we
  654. // set the "response length" to not include the data. This is what
  655. // SrvStartSend uses to set the first MDL's length.
  656. //
  657. // Handling of the second MDL varies depending on whether we did a
  658. // copy read or an MDL read.
  659. //
  660. ASSERT( ((sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX,Buffer)) & 3) == 3 );
  661. WorkContext->ResponseParameters = NEXT_LOCATION(
  662. response,
  663. RESP_READ_MPX,
  664. 1 // pad byte
  665. );
  666. WorkContext->ResponseBuffer->Mdl->ByteCount =
  667. (CLONG)( (PCHAR)WorkContext->ResponseParameters -
  668. (PCHAR)WorkContext->ResponseHeader );
  669. WorkContext->ResponseHeader->Flags |= SMB_FLAGS_SERVER_TO_REDIR;
  670. //
  671. // Start sending fragments.
  672. //
  673. WorkContext->Parameters.ReadMpx.RemainingLength = readLength;
  674. ASSERT( WorkContext->ResponseBuffer->Mdl->Next == NULL );
  675. WorkContext->ResponseBuffer->Mdl->Next =
  676. WorkContext->ResponseBuffer->PartialMdl;
  677. WorkContext->ResponseBuffer->PartialMdl->Next = NULL;
  678. if ( mdlRead ) {
  679. WorkContext->Parameters.ReadMpx.CurrentMdl =
  680. WorkContext->Parameters.ReadMpx.FirstMdl;
  681. WorkContext->Parameters.ReadMpx.CurrentMdlOffset = 0;
  682. (VOID)SendMdlReadMpxFragment( NULL, irp, WorkContext );
  683. } else {
  684. (VOID)SendCopyReadMpxFragment( NULL, irp, WorkContext );
  685. }
  686. Cleanup:
  687. if (bNeedTrace) {
  688. SrvWmiEndContext(WorkContext);
  689. }
  690. return;
  691. } // RestartReadMpx
  692. VOID SRVFASTCALL
  693. SendCopyReadMpxFragment2 (
  694. IN OUT PWORK_CONTEXT WorkContext
  695. )
  696. /*++
  697. Routine Description:
  698. Stub to call actual routine.
  699. Arguments:
  700. WorkContext - Supplies a pointer to the work context block
  701. representing the work item
  702. Return Value:
  703. None.
  704. --*/
  705. {
  706. PAGED_CODE( );
  707. (VOID) SendCopyReadMpxFragment( NULL, WorkContext->Irp, WorkContext );
  708. } // SendCopyReadMpxFragment2
  709. NTSTATUS
  710. SendCopyReadMpxFragment (
  711. IN PDEVICE_OBJECT DeviceObject,
  712. IN PIRP Irp,
  713. IN OUT PWORK_CONTEXT WorkContext
  714. )
  715. /*++
  716. Routine Description:
  717. Sends a Read Mpx response fragment when copy read was used.
  718. Arguments:
  719. WorkContext - Supplies a pointer to the work context block
  720. describing server-specific context for the request.
  721. Return Value:
  722. None.
  723. --*/
  724. {
  725. PRESP_READ_MPX response;
  726. USHORT fragmentSize;
  727. USHORT remainingLength;
  728. ULONG offset;
  729. PCHAR fragmentAddress;
  730. PIO_COMPLETION_ROUTINE sendCompletionRoutine;
  731. UNLOCKABLE_CODE( 8FIL );
  732. //
  733. // Check the status of the send completion.
  734. //
  735. CHECK_SEND_COMPLETION_STATUS( Irp->IoStatus.Status );
  736. //
  737. // Turn off cancel boolean
  738. //
  739. Irp->Cancel = FALSE;
  740. //
  741. // Get context.
  742. //
  743. fragmentSize = WorkContext->Parameters.ReadMpx.FragmentSize;
  744. remainingLength = WorkContext->Parameters.ReadMpx.RemainingLength;
  745. offset = WorkContext->Parameters.ReadMpx.Offset;
  746. fragmentAddress = WorkContext->Parameters.ReadMpx.NextFragmentAddress;
  747. //
  748. // If the amount left to send is less than the fragment size, only
  749. // send the remaining amount. Update the remaining amount.
  750. //
  751. if ( remainingLength < fragmentSize ) {
  752. fragmentSize = remainingLength;
  753. }
  754. ASSERT( fragmentSize != 0 );
  755. remainingLength -= fragmentSize;
  756. //
  757. // Build the response parameters.
  758. //
  759. response = (PRESP_READ_MPX)(WorkContext->ResponseHeader + 1);
  760. SmbPutUshort( &response->Remaining, remainingLength );
  761. SmbPutUlong( &response->Offset, offset );
  762. SmbPutUshort( &response->DataLength, fragmentSize );
  763. ASSERT( ((sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX, Buffer)) & 3) == 3 );
  764. SmbPutUshort( &response->ByteCount, fragmentSize + 1 ); // account for padding
  765. //
  766. // Build a partial MDL describing the data.
  767. //
  768. IoBuildPartialMdl(
  769. WorkContext->Parameters.ReadMpx.MpxBufferMdl,
  770. WorkContext->ResponseBuffer->PartialMdl,
  771. fragmentAddress,
  772. fragmentSize
  773. );
  774. //
  775. // Final preparation for the send depends on whether this is the
  776. // last fragment.
  777. //
  778. if ( remainingLength != 0 ) {
  779. //
  780. // Not done. Update context. Set up to restart after the send
  781. // in this routine. We want do this as an FSD restart routine.
  782. // But this may recurse, if the send doesn't pend, so we may use
  783. // up the stack. If we are running out of stack, restart here
  784. // in the FSP.
  785. //
  786. WorkContext->Parameters.ReadMpx.RemainingLength = remainingLength;
  787. WorkContext->Parameters.ReadMpx.Offset += fragmentSize;
  788. WorkContext->Parameters.ReadMpx.NextFragmentAddress += fragmentSize;
  789. if ( IoGetRemainingStackSize() >= STACK_THRESHOLD ) {
  790. DEBUG WorkContext->FsdRestartRoutine = NULL;
  791. sendCompletionRoutine = SendCopyReadMpxFragment;
  792. } else {
  793. DEBUG WorkContext->FsdRestartRoutine = NULL;
  794. WorkContext->FspRestartRoutine = SendCopyReadMpxFragment2;
  795. sendCompletionRoutine = SrvQueueWorkToFspAtSendCompletion;
  796. }
  797. } else {
  798. //
  799. // This is the last fragment. Restart in the cleanup routine.
  800. //
  801. DEBUG WorkContext->FsdRestartRoutine = NULL;
  802. DEBUG WorkContext->FspRestartRoutine = NULL;
  803. sendCompletionRoutine = RestartCopyReadMpxComplete;
  804. }
  805. //
  806. // Send the fragment.
  807. //
  808. WorkContext->ResponseBuffer->DataLength = // +1 for pad
  809. sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX,Buffer) + 1 + fragmentSize;
  810. if ( WorkContext->Endpoint->IsConnectionless ) {
  811. SrvIpxStartSend( WorkContext, sendCompletionRoutine );
  812. } else {
  813. SrvStartSend2( WorkContext, sendCompletionRoutine );
  814. }
  815. return(STATUS_MORE_PROCESSING_REQUIRED);
  816. } // SendCopyReadMpxFragment
  817. VOID SRVFASTCALL
  818. SendMdlReadMpxFragment2 (
  819. IN OUT PWORK_CONTEXT WorkContext
  820. )
  821. /*++
  822. Routine Description:
  823. Stub to call actual routine.
  824. Arguments:
  825. WorkContext - Supplies a pointer to the work context block
  826. representing the work item
  827. Return Value:
  828. None.
  829. --*/
  830. {
  831. PAGED_CODE( );
  832. (VOID) SendMdlReadMpxFragment( NULL, WorkContext->Irp, WorkContext );
  833. } // SendMdlReadMpxFragment2
  834. NTSTATUS
  835. SendMdlReadMpxFragment (
  836. IN PDEVICE_OBJECT DeviceObject,
  837. IN PIRP Irp,
  838. IN OUT PWORK_CONTEXT WorkContext
  839. )
  840. /*++
  841. Routine Description:
  842. Sends a Read Mpx response fragment when MDL read was used.
  843. Arguments:
  844. WorkContext - Supplies a pointer to the work context block
  845. describing server-specific context for the request.
  846. Return Value:
  847. None.
  848. --*/
  849. {
  850. PRESP_READ_MPX response;
  851. PIO_COMPLETION_ROUTINE sendCompletionRoutine;
  852. USHORT fragmentSize;
  853. USHORT remainingLength;
  854. ULONG offset;
  855. PCHAR fragmentAddress;
  856. PMDL mdl;
  857. ULONG mdlOffset;
  858. ULONG partialLength;
  859. ULONG lengthNeeded;
  860. PCHAR startVa;
  861. PCHAR systemVa;
  862. UNLOCKABLE_CODE( 8FIL );
  863. //
  864. // Check the status of the send completion.
  865. //
  866. CHECK_SEND_COMPLETION_STATUS( Irp->IoStatus.Status );
  867. //
  868. // Turn off cancel boolean
  869. //
  870. Irp->Cancel = FALSE;
  871. //
  872. // Get context.
  873. //
  874. fragmentSize = WorkContext->Parameters.ReadMpx.FragmentSize,
  875. remainingLength = WorkContext->Parameters.ReadMpx.RemainingLength;
  876. offset = WorkContext->Parameters.ReadMpx.Offset;
  877. //
  878. // If the amount left to send is less than the fragment size, only
  879. // send the remaining amount. Update the remaining amount.
  880. //
  881. if ( remainingLength < fragmentSize ) {
  882. fragmentSize = remainingLength;
  883. }
  884. ASSERT( fragmentSize != 0 );
  885. remainingLength -= fragmentSize;
  886. //
  887. // Build the response parameters.
  888. //
  889. response = (PRESP_READ_MPX)(WorkContext->ResponseHeader + 1);
  890. SmbPutUshort( &response->Remaining, remainingLength );
  891. SmbPutUlong( &response->Offset, offset );
  892. SmbPutUshort( &response->DataLength, fragmentSize );
  893. ASSERT( ((sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX,Buffer)) & 3) == 3 );
  894. SmbPutUshort( &response->ByteCount, fragmentSize + 1 ); // account for padding
  895. //
  896. // If the current MDL doesn't describe all of the data we need to
  897. // send, we need to play some games.
  898. //
  899. MmPrepareMdlForReuse( WorkContext->ResponseBuffer->PartialMdl );
  900. mdl = WorkContext->Parameters.ReadMpx.CurrentMdl;
  901. startVa = MmGetMdlVirtualAddress( mdl );
  902. mdlOffset = WorkContext->Parameters.ReadMpx.CurrentMdlOffset;
  903. partialLength = MmGetMdlByteCount(mdl) - mdlOffset;
  904. if ( partialLength >= fragmentSize ) {
  905. //
  906. // The current MDL has all of the data we need to send. Build
  907. // a partial MDL describing that data.
  908. //
  909. IoBuildPartialMdl(
  910. mdl,
  911. WorkContext->ResponseBuffer->PartialMdl,
  912. startVa + mdlOffset,
  913. fragmentSize
  914. );
  915. //
  916. // Indicate how much data we're taking out of the current MDL.
  917. //
  918. partialLength = fragmentSize;
  919. } else {
  920. //
  921. // The data we need is spread across more than one MDL. Painful
  922. // as this seems, we need to copy the data into the standard
  923. // response buffer. It's possible that we could play some games
  924. // with the MDLs and avoid the copy, but it doesn't seem worth it.
  925. // There is, after all, additional cost in the NDIS driver for
  926. // chaining MDLs together.
  927. //
  928. // *** Note that we still send a second MDL, even though the data
  929. // for this send will abut the response parameters.
  930. //
  931. // Calculate the address of the buffer. Build a partial MDL
  932. // describing it.
  933. //
  934. fragmentAddress = (PCHAR)WorkContext->ResponseBuffer->Buffer +
  935. sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX,Buffer) + 1;
  936. IoBuildPartialMdl(
  937. WorkContext->ResponseBuffer->Mdl,
  938. WorkContext->ResponseBuffer->PartialMdl,
  939. fragmentAddress,
  940. fragmentSize
  941. );
  942. ASSERT( WorkContext->ResponseBuffer->PartialMdl->Next == NULL );
  943. //
  944. // Copy from the current MDL into the buffer.
  945. //
  946. systemVa = MmGetSystemAddressForMdl( mdl );
  947. RtlCopyMemory( fragmentAddress, systemVa + mdlOffset, partialLength );
  948. //
  949. // Update the destination address and set the remaining copy
  950. // amount.
  951. //
  952. fragmentAddress += partialLength;
  953. lengthNeeded = fragmentSize - partialLength;
  954. ASSERT( lengthNeeded != 0 );
  955. do {
  956. //
  957. // Move to the next MDL.
  958. //
  959. mdl = mdl->Next;
  960. ASSERT( mdl != NULL );
  961. //
  962. // Calculate how much we can (and need to) copy out of this
  963. // MDL, and do the copy.
  964. //
  965. startVa = MmGetMdlVirtualAddress( mdl );
  966. partialLength = MIN( MmGetMdlByteCount(mdl), lengthNeeded );
  967. systemVa = MmGetSystemAddressForMdl( mdl );
  968. RtlCopyMemory( fragmentAddress, systemVa, partialLength );
  969. //
  970. // Update the destination address and the remaining copy
  971. // amount. We may be done.
  972. //
  973. fragmentAddress += partialLength;
  974. lengthNeeded -= partialLength;
  975. } while ( lengthNeeded != 0 );
  976. //
  977. // We just copied from the beginning of the current MDL.
  978. //
  979. mdlOffset = 0;
  980. }
  981. //
  982. // Final preparation for the send depends on whether this is the
  983. // last fragment.
  984. //
  985. if ( remainingLength != 0 ) {
  986. //
  987. // Not done. Update the current MDL position. If we have
  988. // finished off the current MDL, move to the next one.
  989. //
  990. mdlOffset += partialLength;
  991. if ( mdlOffset >= MmGetMdlByteCount(mdl) ) {
  992. mdl = mdl->Next;
  993. ASSERT( mdl != NULL );
  994. mdlOffset = 0;
  995. }
  996. //
  997. // Update context. Set up to restart after the send in this
  998. // routine. We want do this as an FSD restart routine. But
  999. // this may recurse, if the send doesn't pend, so we may use up
  1000. // the stack. If we are running out of stack, restart here in
  1001. // the FSP.
  1002. //
  1003. WorkContext->Parameters.ReadMpx.CurrentMdl = mdl;
  1004. WorkContext->Parameters.ReadMpx.CurrentMdlOffset = (USHORT)mdlOffset;
  1005. WorkContext->Parameters.ReadMpx.RemainingLength = remainingLength;
  1006. WorkContext->Parameters.ReadMpx.Offset += fragmentSize;
  1007. if ( IoGetRemainingStackSize() >= STACK_THRESHOLD ) {
  1008. DEBUG WorkContext->FsdRestartRoutine = NULL;
  1009. sendCompletionRoutine = SendMdlReadMpxFragment;
  1010. } else {
  1011. DEBUG WorkContext->FsdRestartRoutine = NULL;
  1012. WorkContext->FspRestartRoutine = SendMdlReadMpxFragment2;
  1013. sendCompletionRoutine = SrvQueueWorkToFspAtSendCompletion;
  1014. }
  1015. } else {
  1016. //
  1017. // This is the last fragment. Restart in the cleanup routine.
  1018. //
  1019. DEBUG WorkContext->FsdRestartRoutine = NULL;
  1020. WorkContext->FspRestartRoutine = RestartMdlReadMpxComplete;
  1021. sendCompletionRoutine = SrvQueueWorkToFspAtSendCompletion;
  1022. }
  1023. //
  1024. // Send the fragment.
  1025. //
  1026. WorkContext->ResponseBuffer->DataLength = // +1 for pad
  1027. sizeof(SMB_HEADER) + FIELD_OFFSET(RESP_READ_MPX,Buffer) + 1 + fragmentSize;
  1028. if ( WorkContext->Endpoint->IsConnectionless ) {
  1029. SrvIpxStartSend( WorkContext, sendCompletionRoutine );
  1030. } else {
  1031. SrvStartSend2( WorkContext, sendCompletionRoutine );
  1032. }
  1033. return(STATUS_MORE_PROCESSING_REQUIRED);
  1034. } // SendMdlReadMpxFragment
  1035. NTSTATUS
  1036. RestartCopyReadMpxComplete (
  1037. IN PDEVICE_OBJECT DeviceObject,
  1038. IN PIRP Irp,
  1039. IN OUT PWORK_CONTEXT WorkContext
  1040. )
  1041. /*++
  1042. Routine Description:
  1043. This is the final completion routine for Read Mpx when copy read is
  1044. used. It is called after the send of the last fragment completes.
  1045. Arguments:
  1046. DeviceObject - Pointer to target device object for the request.
  1047. Irp - Pointer to I/O request packet
  1048. WorkContext - Caller-specified context parameter associated with IRP.
  1049. This is actually a pointer to a Work Context block.
  1050. Return Value:
  1051. STATUS_MORE_PROCESSING_REQUIRED.
  1052. --*/
  1053. {
  1054. KIRQL oldIrql;
  1055. UNLOCKABLE_CODE( 8FIL );
  1056. //
  1057. // Check the status of the send completion.
  1058. //
  1059. CHECK_SEND_COMPLETION_STATUS( Irp->IoStatus.Status );
  1060. //
  1061. // Reset the IRP cancelled bit.
  1062. //
  1063. Irp->Cancel = FALSE;
  1064. ASSERT( !WorkContext->Parameters.ReadMpx.MdlRead );
  1065. //
  1066. // If we allocated a separate buffer to do the read, free it and its
  1067. // MDL now.
  1068. //
  1069. if ( WorkContext->Parameters.ReadMpx.MpxBuffer != NULL ) {
  1070. DEALLOCATE_NONPAGED_POOL( WorkContext->Parameters.ReadMpx.MpxBuffer );
  1071. IoFreeMdl( WorkContext->Parameters.ReadMpx.MpxBufferMdl );
  1072. }
  1073. WorkContext->ResponseBuffer->Mdl->Next = NULL;
  1074. //
  1075. // Complete and requeue the work item.
  1076. //
  1077. KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
  1078. SrvFsdRestartSmbComplete( WorkContext );
  1079. KeLowerIrql( oldIrql );
  1080. return STATUS_MORE_PROCESSING_REQUIRED;
  1081. } // RestartCopyReadMpxComplete
  1082. VOID SRVFASTCALL
  1083. RestartMdlReadMpxComplete (
  1084. IN OUT PWORK_CONTEXT WorkContext
  1085. )
  1086. /*++
  1087. Routine Description:
  1088. This is the final completion routine for Read Mpx when MDL read is
  1089. used. It is called after the send of the last fragment completes.
  1090. Arguments:
  1091. WorkContext - Supplies a pointer to the work context block
  1092. describing server-specific context for the request.
  1093. Return Value:
  1094. None.
  1095. --*/
  1096. {
  1097. NTSTATUS status;
  1098. LARGE_INTEGER offset;
  1099. PAGED_CODE( );
  1100. ASSERT( WorkContext->Parameters.ReadMpx.MdlRead );
  1101. //
  1102. // Give the MDL back to the cache manager. (If the read failed or
  1103. // returned no data, there will be no MDL.)
  1104. //
  1105. MmPrepareMdlForReuse( WorkContext->ResponseBuffer->PartialMdl );
  1106. if ( WorkContext->Parameters.ReadMpx.FirstMdl != NULL ) {
  1107. //KdPrint(( "Freeing MDL chain:\n" ));
  1108. //DumpMdlChain( WorkContext->Parameters.ReadMpx.FirstMdl );
  1109. if( WorkContext->Rfcb->Lfcb->MdlReadComplete == NULL ||
  1110. WorkContext->Rfcb->Lfcb->MdlReadComplete(
  1111. WorkContext->Rfcb->Lfcb->FileObject,
  1112. WorkContext->Parameters.ReadMpx.FirstMdl,
  1113. WorkContext->Rfcb->Lfcb->DeviceObject ) == FALSE ) {
  1114. offset.QuadPart = WorkContext->Parameters.ReadMpx.Offset;
  1115. //
  1116. // Fast path didn't work, try an IRP...
  1117. //
  1118. status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
  1119. WorkContext->Parameters.ReadMpx.FirstMdl,
  1120. IRP_MJ_READ,
  1121. &offset,
  1122. WorkContext->Parameters.ReadMpx.ReadLength
  1123. );
  1124. if( !NT_SUCCESS( status ) ) {
  1125. //
  1126. // All we can do is complain now!
  1127. //
  1128. SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
  1129. }
  1130. }
  1131. }
  1132. WorkContext->ResponseBuffer->Mdl->Next = NULL;
  1133. //
  1134. // Free the work item by dereferencing it.
  1135. //
  1136. SrvDereferenceWorkItem( WorkContext );
  1137. return;
  1138. } // RestartMdlReadMpxComplete
  1139. VOID SRVFASTCALL
  1140. SrvRestartReceiveWriteMpx (
  1141. IN OUT PWORK_CONTEXT WorkContext
  1142. )
  1143. /*++
  1144. Routine Description:
  1145. This routine replaces the normal restart routine for TDI Receive
  1146. completion when a Write Mpx SMB is received over IPX. If a receive
  1147. error occurs, or if the SMB is invalid, it cleans up the active
  1148. write mpx state that was set up in SrvIpxServerDatagramHandler.
  1149. Arguments:
  1150. WorkContext - Supplies a pointer to the work context block
  1151. describing server-specific context for the request.
  1152. Return Value:
  1153. None.
  1154. --*/
  1155. {
  1156. NTSTATUS status;
  1157. SMB_STATUS smbStatus;
  1158. PCONNECTION connection;
  1159. PIRP irp;
  1160. PSMB_HEADER header;
  1161. ULONG length;
  1162. PAGED_CODE( );
  1163. connection = WorkContext->Connection;
  1164. irp = WorkContext->Irp;
  1165. //
  1166. // Save the length of the received message. Store the length
  1167. // in the work context block for statistics gathering.
  1168. //
  1169. length = (ULONG)irp->IoStatus.Information;
  1170. WorkContext->RequestBuffer->DataLength = length;
  1171. WorkContext->CurrentWorkQueue->stats.BytesReceived += length;
  1172. //
  1173. // Store in the work context block the time at which processing
  1174. // of the request began. Use the time that the work item was
  1175. // queued to the FSP for this purpose.
  1176. //
  1177. WorkContext->StartTime = WorkContext->Timestamp;
  1178. //
  1179. // Update the server network error count. If the TDI receive
  1180. // failed or was canceled, don't try to process an SMB.
  1181. //
  1182. status = irp->IoStatus.Status;
  1183. if ( irp->Cancel || !NT_SUCCESS(status) ) {
  1184. IF_DEBUG(NETWORK_ERRORS) {
  1185. KdPrint(( "SrvRestartReceiveWriteMpx: status = %X for IRP %p\n",
  1186. irp->IoStatus.Status, irp ));
  1187. }
  1188. SrvUpdateErrorCount( &SrvNetworkErrorRecord, TRUE );
  1189. if ( NT_SUCCESS(status) ) status = STATUS_CANCELLED;
  1190. goto cleanup;
  1191. }
  1192. SrvUpdateErrorCount( &SrvNetworkErrorRecord, FALSE );
  1193. //
  1194. // We (should) have received an SMB.
  1195. //
  1196. SMBTRACE_SRV2(
  1197. WorkContext->ResponseBuffer->Buffer,
  1198. WorkContext->ResponseBuffer->DataLength
  1199. );
  1200. //
  1201. // Initialize the error class and code fields in the header to
  1202. // indicate success.
  1203. //
  1204. header = WorkContext->ResponseHeader;
  1205. SmbPutUlong( &header->ErrorClass, STATUS_SUCCESS );
  1206. //
  1207. // If the connection is closing or the server is shutting down,
  1208. // ignore this SMB.
  1209. //
  1210. if ( (GET_BLOCK_STATE(connection) != BlockStateActive) ||
  1211. SrvFspTransitioning ) {
  1212. goto cleanup;
  1213. }
  1214. //
  1215. // Verify the SMB to make sure that it has a valid header, and that
  1216. // the word count and byte count are within range.
  1217. //
  1218. WorkContext->NextCommand = header->Command;
  1219. if ( !SrvValidateSmb( WorkContext ) ) {
  1220. IF_DEBUG(SMB_ERRORS) {
  1221. KdPrint(( "SrvRestartReceiveWriteMpx: Invalid SMB.\n" ));
  1222. KdPrint(( " SMB received from %z\n",
  1223. (PCSTRING)&WorkContext->Connection->OemClientMachineNameString ));
  1224. }
  1225. //
  1226. // The SMB is invalid. We send back an INVALID_SMB status.
  1227. //
  1228. status = STATUS_INVALID_SMB;
  1229. goto cleanup;
  1230. }
  1231. //
  1232. // Clear the flag that indicates the we just sent an oplock break II
  1233. // to none. This allows subsequent raw reads to be processed.
  1234. //
  1235. //not needed on IPX//connection->BreakIIToNoneJustSent = FALSE;
  1236. //
  1237. // Process the received SMB. The called routine is responsible
  1238. // for sending any response(s) that are needed and for getting
  1239. // the receive buffer back onto the receive queue as soon as
  1240. // possible.
  1241. //
  1242. smbStatus = SrvSmbWriteMpx( WorkContext );
  1243. ASSERT( smbStatus != SmbStatusMoreCommands );
  1244. if ( smbStatus != SmbStatusInProgress ) {
  1245. //
  1246. // Return the TransportContext
  1247. //
  1248. if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
  1249. TdiReturnChainedReceives( &WorkContext->Parameters.WriteMpx.TransportContext,
  1250. 1
  1251. );
  1252. }
  1253. SrvEndSmbProcessing( WorkContext, smbStatus );
  1254. }
  1255. return;
  1256. cleanup:
  1257. //
  1258. // We will not be processing this write. We still need to check
  1259. // for whether this is the last Write Mpx active on the RFCB, and
  1260. // if so, send the response to the write.
  1261. //
  1262. // *** Note that if we are here because we received an invalid
  1263. // SMB, the completion of the Write Mpx overrides the sending
  1264. // of an error response.
  1265. //
  1266. //
  1267. // Return the TransportContext
  1268. //
  1269. if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
  1270. TdiReturnChainedReceives( &WorkContext->Parameters.WriteMpx.TransportContext,
  1271. 1
  1272. );
  1273. }
  1274. if ( CheckForWriteMpxComplete( WorkContext ) ) {
  1275. SrvFsdSendResponse( WorkContext );
  1276. } else if ( status == STATUS_INVALID_SMB ) {
  1277. SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
  1278. SrvFsdSendResponse( WorkContext );
  1279. } else {
  1280. SrvDereferenceWorkItem( WorkContext );
  1281. }
  1282. return;
  1283. } // SrvRestartReceiveWriteMpx
  1284. SMB_PROCESSOR_RETURN_TYPE
  1285. SrvSmbWriteMpx (
  1286. SMB_PROCESSOR_PARAMETERS
  1287. )
  1288. /*++
  1289. Routine Description:
  1290. Processes the Write Mpx SMB.
  1291. *** The server currently does not support multiplexed mode reads and
  1292. writes on connection-based transports. When such requests are
  1293. received, the error "use standard mode" is returned.
  1294. Multiplexed mode turns out not to be the performance win it was
  1295. thought to be (on local nets), so we haven't implemented it,
  1296. except over IPX.
  1297. Arguments:
  1298. WorkContext - Supplies a pointer to the work context block
  1299. representing the work item
  1300. Return Value:
  1301. None.
  1302. --*/
  1303. {
  1304. PSMB_HEADER header;
  1305. PREQ_WRITE_MPX request;
  1306. PRESP_WRITE_MPX_DATAGRAM response;
  1307. NTSTATUS status = STATUS_SUCCESS;
  1308. SMB_STATUS SmbStatus = SmbStatusInProgress;
  1309. USHORT fid;
  1310. USHORT mid;
  1311. PRFCB rfcb;
  1312. PLFCB lfcb;
  1313. PWRITE_MPX_CONTEXT writeMpx;
  1314. CLONG bufferOffset;
  1315. PCHAR writeAddress;
  1316. USHORT writeLength;
  1317. ULONG key;
  1318. LARGE_INTEGER offset;
  1319. USHORT writeMode;
  1320. BOOLEAN writeThrough;
  1321. KIRQL oldIrql;
  1322. PMDL mdl;
  1323. if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
  1324. WorkContext->PreviousSMB = EVENT_TYPE_SMB_WRITE_MPX;
  1325. SrvWmiStartContext(WorkContext);
  1326. header = WorkContext->RequestHeader;
  1327. request = (PREQ_WRITE_MPX)WorkContext->RequestParameters;
  1328. fid = SmbGetUshort( &request->Fid );
  1329. IF_SMB_DEBUG(MPX1) {
  1330. KdPrint(( "Write Block Multipliexed request; FID 0x%lx, "
  1331. "count %ld, offset %ld\n",
  1332. fid, SmbGetUshort( &request->Count ),
  1333. SmbGetUlong( &request->Offset ) ));
  1334. }
  1335. //
  1336. // Verify the FID. If verified, the RFCB is referenced and its
  1337. // address is stored in the WorkContext block, and the RFCB
  1338. // address is returned.
  1339. //
  1340. writeMode = SmbGetUshort( &request->WriteMode );
  1341. if( (writeMode & SMB_WMODE_DATAGRAM) == 0 ||
  1342. !WorkContext->Endpoint->IsConnectionless ) {
  1343. SrvFsdBuildWriteCompleteResponse( WorkContext, STATUS_SMB_USE_STANDARD, 0 );
  1344. SmbStatus = SmbStatusSendResponse;
  1345. goto Cleanup;
  1346. }
  1347. rfcb = SrvVerifyFid(
  1348. WorkContext,
  1349. fid,
  1350. TRUE,
  1351. SrvRestartSmbReceived, // serialize with raw write
  1352. &status
  1353. );
  1354. if ( rfcb == SRV_INVALID_RFCB_POINTER) {
  1355. if ( !NT_SUCCESS(status) ) {
  1356. //
  1357. // Invalid file ID or write behind error. Reject the request.
  1358. //
  1359. IF_DEBUG(ERRORS) {
  1360. KdPrint((
  1361. "SrvSmbWriteMpx: Status %X on FID: 0x%lx\n",
  1362. status,
  1363. fid
  1364. ));
  1365. }
  1366. goto error;
  1367. }
  1368. //
  1369. // The work item has been queued because a raw write is in
  1370. // progress.
  1371. //
  1372. SmbStatus = SmbStatusInProgress;
  1373. goto Cleanup;
  1374. }
  1375. if( rfcb->Lfcb->Session->IsSessionExpired )
  1376. {
  1377. status = SESSION_EXPIRED_STATUS_CODE;
  1378. SrvFsdBuildWriteCompleteResponse( WorkContext, status, 0 );
  1379. SmbStatus = SmbStatusSendResponse;
  1380. goto Cleanup;
  1381. }
  1382. //
  1383. // Verify that the client has write access to the file via the
  1384. // specified handle.
  1385. //
  1386. if( !rfcb->MpxWritesOk ) {
  1387. if ( !rfcb->WriteAccessGranted && !rfcb->AppendAccessGranted ) {
  1388. SrvStatistics.GrantedAccessErrors++;
  1389. IF_DEBUG(ERRORS) {
  1390. KdPrint(( "SrvSmbWriteMpx: Write access not granted.\n"));
  1391. }
  1392. status = STATUS_ACCESS_DENIED;
  1393. goto error;
  1394. }
  1395. //
  1396. // If this is not a disk or a print file tell the client to use core write.
  1397. //
  1398. if ( rfcb->ShareType != ShareTypeDisk &&
  1399. rfcb->ShareType != ShareTypePrint ) {
  1400. status = STATUS_SMB_USE_STANDARD;
  1401. goto error;
  1402. }
  1403. rfcb->MpxWritesOk = TRUE;
  1404. }
  1405. rfcb->WrittenTo = TRUE;
  1406. #ifdef INCLUDE_SMB_IFMODIFIED
  1407. rfcb->Lfcb->FileUpdated = TRUE;
  1408. #endif
  1409. //
  1410. // If this a stale packet, ignore it. Stale here means that the MID
  1411. // of the packet is not equal to the MID of the current write mux.
  1412. // Such a packet can be received if a duplicate packet from a
  1413. // previous write mux is delivered after a new write mux starts.
  1414. //
  1415. writeMpx = &rfcb->WriteMpx;
  1416. mid = SmbGetAlignedUshort( &header->Mid );
  1417. if ( mid != writeMpx->Mid ) {
  1418. //
  1419. // Set the sequence number to 0 so that we don't send a response
  1420. // unless we have to because the Write Mpx refcount drops to 0.
  1421. //
  1422. SmbPutAlignedUshort( &header->SequenceNumber, 0 );
  1423. goto error;
  1424. }
  1425. //
  1426. // Get the file offset.
  1427. //
  1428. offset.QuadPart = SmbGetUlong( &request->Offset );
  1429. //
  1430. // Determine the amount of data to write. This is the minimum of
  1431. // the amount requested by the client and the amount of data
  1432. // actually sent in the request buffer.
  1433. //
  1434. bufferOffset = SmbGetUshort( &request->DataOffset );
  1435. //
  1436. // If we have the transport context, then setup WriteAddress accordingly.
  1437. //
  1438. WorkContext->Parameters.WriteMpx.DataMdl = NULL;
  1439. if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
  1440. writeAddress = (PCHAR)WorkContext->Parameters.WriteMpx.Buffer + bufferOffset;
  1441. } else {
  1442. writeAddress = (PCHAR)header + bufferOffset;
  1443. }
  1444. writeLength =
  1445. (USHORT)(MIN( (CLONG)SmbGetUshort( &request->DataLength ),
  1446. WorkContext->ResponseBuffer->DataLength - bufferOffset ));
  1447. //
  1448. // Save context for the restart routine.
  1449. //
  1450. WorkContext->Parameters.WriteMpx.WriteLength = writeLength;
  1451. //
  1452. // Form the lock key using the FID and the PID.
  1453. //
  1454. // *** The FID must be included in the key in order to account for
  1455. // the folding of multiple remote compatibility mode opens into
  1456. // a single local open.
  1457. //
  1458. key = rfcb->ShiftedFid | SmbGetAlignedUshort( &header->Pid );
  1459. //
  1460. // If this is the first packet of a new MID, set up to glom the
  1461. // packets into one big write.
  1462. //
  1463. lfcb = rfcb->Lfcb;
  1464. if ( WorkContext->Parameters.WriteMpx.FirstPacketOfGlom ) {
  1465. //
  1466. // Try the fast path first.
  1467. //
  1468. WorkContext->Irp->MdlAddress = NULL;
  1469. WorkContext->Irp->IoStatus.Information = 0;
  1470. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesAttempted );
  1471. ASSERT( lfcb->FileObject->Flags & FO_CACHE_SUPPORTED );
  1472. writeLength = SmbGetUshort( &request->Count );
  1473. writeMpx->StartOffset = offset.LowPart;
  1474. writeMpx->Length = writeLength;
  1475. if ( lfcb->PrepareMdlWrite(
  1476. lfcb->FileObject,
  1477. &offset,
  1478. writeLength,
  1479. key,
  1480. &WorkContext->Irp->MdlAddress,
  1481. &WorkContext->Irp->IoStatus,
  1482. lfcb->DeviceObject
  1483. ) && WorkContext->Irp->MdlAddress != NULL ) {
  1484. //
  1485. // The fast I/O path worked.
  1486. //
  1487. WorkContext->bAlreadyTrace = TRUE;
  1488. RestartPrepareMpxMdlWrite( WorkContext );
  1489. SmbStatus = SmbStatusInProgress;
  1490. goto Cleanup;
  1491. }
  1492. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesFailed );
  1493. //
  1494. // The fast I/O path failed. Build the write request, reusing the
  1495. // receive IRP.
  1496. //
  1497. // The fast path may have partially succeeded, returning a partial
  1498. // MDL chain. We need to adjust our write request to account for
  1499. // that.
  1500. //
  1501. offset.QuadPart += WorkContext->Irp->IoStatus.Information;
  1502. writeLength -= (USHORT)WorkContext->Irp->IoStatus.Information;
  1503. SrvBuildReadOrWriteRequest(
  1504. WorkContext->Irp, // input IRP address
  1505. lfcb->FileObject, // target file object address
  1506. WorkContext, // context
  1507. IRP_MJ_WRITE, // major function code
  1508. IRP_MN_MDL, // minor function code
  1509. NULL, // buffer address (ignored)
  1510. writeLength, // buffer length
  1511. WorkContext->Irp->MdlAddress, // MDL address
  1512. offset, // byte offset
  1513. key // lock key
  1514. );
  1515. //
  1516. // Pass the request to the file system.
  1517. //
  1518. WorkContext->bAlreadyTrace = TRUE;
  1519. WorkContext->FsdRestartRoutine = RestartPrepareMpxMdlWrite;
  1520. DEBUG WorkContext->FspRestartRoutine = NULL;
  1521. (VOID)IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
  1522. //
  1523. // The MDL write has been started. When it completes, processing
  1524. // resumes at RestartPrepareMpxMdlWrite.
  1525. //
  1526. SmbStatus = SmbStatusInProgress;
  1527. goto Cleanup;
  1528. }
  1529. //
  1530. // Save context for the restart routine.
  1531. //
  1532. WorkContext->Parameters.WriteMpx.Offset = offset.LowPart;
  1533. WorkContext->Parameters.WriteMpx.Mid = mid;
  1534. if ( writeMpx->GlomPending ) {
  1535. //
  1536. // A glom setup is pending. Wait for that to complete.
  1537. //
  1538. ACQUIRE_SPIN_LOCK( &rfcb->Connection->SpinLock, &oldIrql );
  1539. if ( writeMpx->GlomPending ) {
  1540. InsertTailList(
  1541. &writeMpx->GlomDelayList,
  1542. &WorkContext->ListEntry
  1543. );
  1544. RELEASE_SPIN_LOCK( &rfcb->Connection->SpinLock, oldIrql );
  1545. SmbStatus = SmbStatusInProgress;
  1546. goto Cleanup;
  1547. }
  1548. RELEASE_SPIN_LOCK( &rfcb->Connection->SpinLock, oldIrql );
  1549. }
  1550. if ( writeMpx->Glomming ) {
  1551. //
  1552. // We're glomming this into one big write. Add the data from
  1553. // this packet.
  1554. //
  1555. AddPacketToGlom( WorkContext );
  1556. SmbStatus = SmbStatusInProgress;
  1557. goto Cleanup;
  1558. }
  1559. //
  1560. // We are not glomming this write, because we missed the first
  1561. // packet of the write. So we write each block as it arrives.
  1562. //
  1563. // If the file's writethrough mode needs to be changed, do so now.
  1564. //
  1565. writeThrough = (BOOLEAN)((writeMode & SMB_WMODE_WRITE_THROUGH) != 0);
  1566. if ( writeThrough && (lfcb->FileMode & FILE_WRITE_THROUGH) == 0
  1567. || !writeThrough && (lfcb->FileMode & FILE_WRITE_THROUGH) != 0 ) {
  1568. SrvSetFileWritethroughMode( lfcb, writeThrough );
  1569. }
  1570. //
  1571. // Try the fast I/O path first. If that fails, fall through to the
  1572. // normal build-an-IRP path.
  1573. //
  1574. if ( lfcb->FastIoWrite != NULL ) {
  1575. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesAttempted );
  1576. try {
  1577. if ( lfcb->FastIoWrite(
  1578. lfcb->FileObject,
  1579. &offset,
  1580. writeLength,
  1581. TRUE,
  1582. key,
  1583. writeAddress,
  1584. &WorkContext->Irp->IoStatus,
  1585. lfcb->DeviceObject
  1586. ) ) {
  1587. //
  1588. // The fast I/O path worked. Call the restart routine directly
  1589. // to do postprocessing (including sending the response).
  1590. //
  1591. WorkContext->bAlreadyTrace = TRUE;
  1592. RestartWriteMpx( WorkContext );
  1593. SmbStatus = SmbStatusInProgress;
  1594. goto Cleanup;
  1595. }
  1596. }
  1597. except( EXCEPTION_EXECUTE_HANDLER ) {
  1598. // Fall through to the slow path on an exception
  1599. NTSTATUS status = GetExceptionCode();
  1600. IF_DEBUG(ERRORS) {
  1601. KdPrint(("FastIoRead threw exception %x\n", status ));
  1602. }
  1603. }
  1604. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.FastWritesFailed );
  1605. }
  1606. //
  1607. // The turbo path failed. Build the write request, reusing the
  1608. // receive IRP.
  1609. //
  1610. // Build an MDL describing the write buffer. Note that if the file
  1611. // system can complete the write immediately, the MDL isn't really
  1612. // needed, but if the file system must send the request to its FSP,
  1613. // the MDL _is_ needed.
  1614. //
  1615. // *** Note the assumption that the request buffer already has a
  1616. // valid full MDL from which a partial MDL can be built.
  1617. //
  1618. if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
  1619. mdl = IoAllocateMdl(
  1620. writeAddress,
  1621. writeLength,
  1622. FALSE,
  1623. FALSE,
  1624. NULL
  1625. );
  1626. if ( mdl == NULL ) {
  1627. status = STATUS_INSUFF_SERVER_RESOURCES;
  1628. goto error;
  1629. }
  1630. //
  1631. // Build the mdl.
  1632. //
  1633. MmBuildMdlForNonPagedPool( mdl );
  1634. WorkContext->Parameters.WriteMpx.DataMdl = mdl;
  1635. } else {
  1636. mdl = WorkContext->RequestBuffer->PartialMdl;
  1637. IoBuildPartialMdl(
  1638. WorkContext->RequestBuffer->Mdl,
  1639. mdl,
  1640. writeAddress,
  1641. writeLength
  1642. );
  1643. }
  1644. //
  1645. // Build the IRP.
  1646. //
  1647. SrvBuildReadOrWriteRequest(
  1648. WorkContext->Irp, // input IRP address
  1649. lfcb->FileObject, // target file object address
  1650. WorkContext, // context
  1651. IRP_MJ_WRITE, // major function code
  1652. 0, // minor function code
  1653. writeAddress, // buffer address
  1654. writeLength, // buffer length
  1655. mdl, // MDL address
  1656. offset, // byte offset
  1657. key // lock key
  1658. );
  1659. //
  1660. // Pass the request to the file system.
  1661. //
  1662. WorkContext->bAlreadyTrace = TRUE;
  1663. WorkContext->FsdRestartRoutine = RestartWriteMpx;
  1664. DEBUG WorkContext->FspRestartRoutine = NULL;
  1665. IoCallDriver( lfcb->DeviceObject, WorkContext->Irp );
  1666. //
  1667. // The write has been started. Control will return to
  1668. // RestartWriteMpx when the write completes.
  1669. //
  1670. SmbStatus = SmbStatusInProgress;
  1671. goto Cleanup;
  1672. error:
  1673. //
  1674. // There is an error of some sort. We still need to check for
  1675. // whether this is the last Write Mpx active on the RFCB, and if so,
  1676. // send the response to the write instead of the error. If this is
  1677. // not the last active mux request, then we either send an error
  1678. // response (non-datagram write mux or sequenced write mux) or
  1679. // ignore this request (unsequenced datagram). Note that if this is
  1680. // a non-datagram write mux, then we didn't come in over IPX, and we
  1681. // didn't bump the Write Mpx refcount.
  1682. //
  1683. //
  1684. // Return the TransportContext
  1685. //
  1686. if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
  1687. TdiReturnChainedReceives( &WorkContext->Parameters.WriteMpx.TransportContext,
  1688. 1
  1689. );
  1690. }
  1691. if ( WorkContext->Rfcb && CheckForWriteMpxComplete( WorkContext ) ) {
  1692. SmbStatus = SmbStatusSendResponse;
  1693. goto Cleanup;
  1694. }
  1695. if ( SmbGetAlignedUshort(&header->SequenceNumber) != 0 ) {
  1696. SrvSetSmbError2( WorkContext, status, TRUE );
  1697. response = (PRESP_WRITE_MPX_DATAGRAM)WorkContext->ResponseParameters;
  1698. response->WordCount = 2;
  1699. SmbPutUlong( &response->Mask, 0 );
  1700. SmbPutUshort( &response->ByteCount, 0 );
  1701. WorkContext->ResponseParameters = NEXT_LOCATION(
  1702. response,
  1703. RESP_WRITE_MPX_DATAGRAM,
  1704. 0
  1705. );
  1706. SmbStatus = SmbStatusSendResponse;
  1707. goto Cleanup;
  1708. }
  1709. SmbStatus = SmbStatusNoResponse;
  1710. Cleanup:
  1711. SrvWmiEndContext(WorkContext);
  1712. return SmbStatus;
  1713. } // SrvSmbWriteMpx
  1714. VOID SRVFASTCALL
  1715. RestartWriteMpx (
  1716. IN OUT PWORK_CONTEXT WorkContext
  1717. )
  1718. /*++
  1719. Routine Description:
  1720. Processes file write completion for a Write MPX SMB.
  1721. Arguments:
  1722. WorkContext - Supplies a pointer to the work context block
  1723. describing server-specific context for the request.
  1724. Return Value:
  1725. None.
  1726. --*/
  1727. {
  1728. PSMB_HEADER header;
  1729. PREQ_WRITE_MPX request;
  1730. BOOLEAN rfcbClosing;
  1731. PRESP_WRITE_MPX_DATAGRAM response;
  1732. NTSTATUS status = STATUS_SUCCESS;
  1733. SMB_STATUS SmbStatus = SmbStatusInProgress;
  1734. PRFCB rfcb;
  1735. PWRITE_MPX_CONTEXT writeMpx;
  1736. PCONNECTION connection;
  1737. KIRQL oldIrql;
  1738. USHORT writeLength;
  1739. LARGE_INTEGER position;
  1740. USHORT sequenceNumber;
  1741. BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
  1742. IF_DEBUG(FSD2) KdPrint(( " - RestartWriteMpx\n" ));
  1743. if (bNeedTrace) {
  1744. if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
  1745. WorkContext->PreviousSMB = EVENT_TYPE_SMB_WRITE_MPX;
  1746. SrvWmiStartContext(WorkContext);
  1747. }
  1748. else
  1749. WorkContext->bAlreadyTrace = FALSE;
  1750. header = WorkContext->RequestHeader;
  1751. request = (PREQ_WRITE_MPX)WorkContext->RequestParameters;
  1752. response = (PRESP_WRITE_MPX_DATAGRAM)WorkContext->ResponseParameters;
  1753. rfcb = WorkContext->Rfcb;
  1754. connection = WorkContext->Connection;
  1755. status = WorkContext->Irp->IoStatus.Status;
  1756. //
  1757. // Return the TransportContext
  1758. //
  1759. if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
  1760. TdiReturnChainedReceives( &WorkContext->Parameters.WriteMpx.TransportContext,
  1761. 1
  1762. );
  1763. WorkContext->Parameters.WriteMpx.TransportContext = NULL;
  1764. }
  1765. //
  1766. // Free the data Mdl.
  1767. //
  1768. if ( WorkContext->Parameters.WriteMpx.DataMdl ) {
  1769. IoFreeMdl( WorkContext->Parameters.WriteMpx.DataMdl );
  1770. WorkContext->Parameters.WriteMpx.DataMdl = NULL;
  1771. }
  1772. KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
  1773. ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
  1774. //
  1775. // If we're entered at dispatch level, and the write failed,
  1776. // or there is a saved error, or the rfcb is closing, then
  1777. // we need to have a worker thread call this routine.
  1778. //
  1779. if ( ((status != STATUS_SUCCESS) ||
  1780. (rfcb->SavedError != STATUS_SUCCESS) ||
  1781. (GET_BLOCK_STATE(rfcb) != BlockStateActive)) &&
  1782. (oldIrql >= DISPATCH_LEVEL) ) {
  1783. RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
  1784. WorkContext->FspRestartRoutine = RestartWriteMpx;
  1785. QUEUE_WORK_TO_FSP( WorkContext );
  1786. KeLowerIrql( oldIrql );
  1787. goto Cleanup;
  1788. }
  1789. //
  1790. // If this write is from a previous mux (meaning that a new one was
  1791. // started while we were doing this write), toss this request.
  1792. //
  1793. writeMpx = &rfcb->WriteMpx;
  1794. if ( WorkContext->Parameters.WriteMpx.Mid != writeMpx->Mid ) {
  1795. goto check_for_mux_end;
  1796. }
  1797. if ( !NT_SUCCESS(status) ) {
  1798. //
  1799. // The write failed. Remember the failure in the RFCB.
  1800. //
  1801. IF_DEBUG(ERRORS) KdPrint(( "Write failed: %X\n", status ));
  1802. if ( rfcb->SavedError == STATUS_SUCCESS ) {
  1803. rfcb->SavedError = status;
  1804. }
  1805. } else {
  1806. //
  1807. // The write succeeded. Update the information in the write mpx
  1808. // context block.
  1809. //
  1810. // !!! Need to deal with mask shifting by the redir and delayed
  1811. // packets.
  1812. //
  1813. #if 0
  1814. MpxDelay = !MpxDelay;
  1815. if ( MpxDelay ) {
  1816. LARGE_INTEGER interval;
  1817. interval.QuadPart = -10*1000*100;
  1818. KeDelayExecutionThread( KernelMode, FALSE, &interval );
  1819. }
  1820. #endif
  1821. writeMpx->Mask |= SmbGetUlong( &request->Mask );
  1822. }
  1823. //
  1824. // Save the count of bytes written, to be used to update the server
  1825. // statistics database.
  1826. //
  1827. writeLength = (USHORT)WorkContext->Irp->IoStatus.Information;
  1828. UPDATE_WRITE_STATS( WorkContext, writeLength );
  1829. IF_SMB_DEBUG(MPX1) {
  1830. KdPrint(( "RestartWriteMpx: Fid 0x%lx, wrote %ld bytes\n",
  1831. rfcb->Fid, writeLength ));
  1832. }
  1833. //
  1834. // If this is an unsequenced request, we're done. We don't respond
  1835. // until we get a sequenced request.
  1836. //
  1837. sequenceNumber = SmbGetAlignedUshort( &header->SequenceNumber );
  1838. if ( sequenceNumber == 0 ) {
  1839. goto check_for_mux_end;
  1840. }
  1841. //
  1842. // This is the last request in this mux sent by the client. Save
  1843. // the sequence number and update the file position.
  1844. //
  1845. writeMpx->SequenceNumber = sequenceNumber;
  1846. rfcb->CurrentPosition = WorkContext->Parameters.WriteMpx.Offset + writeLength;
  1847. check_for_mux_end:
  1848. //
  1849. // If we have received the sequenced command for this write mux,
  1850. // and this is the last active command, then it's time to send
  1851. // the response. Otherwise, we are done with this SMB.
  1852. //
  1853. if ( --writeMpx->ReferenceCount != 0 ) {
  1854. RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
  1855. SrvFsdRestartSmbComplete( WorkContext );
  1856. KeLowerIrql( oldIrql );
  1857. goto Cleanup;
  1858. }
  1859. //
  1860. // WriteMpx refcount is 0.
  1861. //
  1862. rfcbClosing = (GET_BLOCK_STATE(rfcb) != BlockStateActive);
  1863. if ( writeMpx->SequenceNumber == 0 ) {
  1864. //
  1865. // If the rfcb is closing, complete the cleanup.
  1866. //
  1867. RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
  1868. KeLowerIrql( oldIrql );
  1869. if ( rfcbClosing ) {
  1870. RestartWriteMpxCompleteRfcbClose( WorkContext );
  1871. }
  1872. if( oldIrql >= DISPATCH_LEVEL ) {
  1873. SrvFsdRestartSmbComplete( WorkContext );
  1874. } else {
  1875. SrvRestartFsdComplete( WorkContext );
  1876. }
  1877. goto Cleanup;
  1878. }
  1879. //
  1880. // We are done with this write mux. Save the accumulated mask, the
  1881. // sequence number, and the original MID, then clear the mask and
  1882. // sequence number to indicate that we no longer are in the middle
  1883. // of a write mux.
  1884. //
  1885. SmbPutUlong( &response->Mask, writeMpx->Mask );
  1886. writeMpx->Mask = 0;
  1887. SmbPutAlignedUshort( &header->SequenceNumber, writeMpx->SequenceNumber );
  1888. writeMpx->SequenceNumber = 0;
  1889. SmbPutAlignedUshort( &header->Mid, writeMpx->Mid );
  1890. //
  1891. // Save the status.
  1892. //
  1893. status = rfcb->SavedError;
  1894. rfcb->SavedError = STATUS_SUCCESS;
  1895. //
  1896. // Now we can release the lock.
  1897. //
  1898. RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
  1899. KeLowerIrql( oldIrql );
  1900. //
  1901. // Complete the rfcb close.
  1902. //
  1903. if ( rfcbClosing ) {
  1904. RestartWriteMpxCompleteRfcbClose( WorkContext );
  1905. }
  1906. //
  1907. // Build the response message.
  1908. //
  1909. if ( !NT_SUCCESS(status) ) {
  1910. SrvSetSmbError2( WorkContext, status, TRUE );
  1911. }
  1912. response->WordCount = 2;
  1913. SmbPutUshort( &response->ByteCount, 0 );
  1914. WorkContext->ResponseParameters = NEXT_LOCATION(
  1915. response,
  1916. RESP_WRITE_MPX_DATAGRAM,
  1917. 0
  1918. );
  1919. //
  1920. // Send the response.
  1921. //
  1922. SrvFsdSendResponse( WorkContext );
  1923. Cleanup:
  1924. if (bNeedTrace) {
  1925. SrvWmiEndContext(WorkContext);
  1926. }
  1927. return;
  1928. } // RestartWriteMpx
  1929. BOOLEAN
  1930. CheckForWriteMpxComplete (
  1931. IN OUT PWORK_CONTEXT WorkContext
  1932. )
  1933. {
  1934. PSMB_HEADER header;
  1935. PRESP_WRITE_MPX_DATAGRAM response;
  1936. NTSTATUS status;
  1937. PRFCB rfcb = WorkContext->Rfcb;
  1938. PWRITE_MPX_CONTEXT writeMpx = &rfcb->WriteMpx;
  1939. PCONNECTION connection = WorkContext->Connection;
  1940. KIRQL oldIrql;
  1941. //
  1942. // If we have not received the sequenced command for this write mux,
  1943. // or this is not the last active command, then return FALSE.
  1944. // Otherwise, it's time to send the response, so build it and return
  1945. // TRUE.
  1946. //
  1947. ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
  1948. if ( --writeMpx->ReferenceCount != 0 ) {
  1949. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  1950. return(FALSE);
  1951. }
  1952. //
  1953. // WriteMpx refcount is 0.
  1954. //
  1955. if ( writeMpx->SequenceNumber == 0 ) {
  1956. //
  1957. // If the rfcb is closing, complete the cleanup.
  1958. //
  1959. if ( GET_BLOCK_STATE(rfcb) != BlockStateActive ) {
  1960. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  1961. RestartWriteMpxCompleteRfcbClose( WorkContext );
  1962. } else {
  1963. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  1964. }
  1965. return FALSE;
  1966. }
  1967. //
  1968. // We are done with this write mux. Save the accumulated mask, the
  1969. // sequence number, and the original MID, then clear the mask and
  1970. // sequence number to indicate that we no longer are in the middle
  1971. // of a write mux.
  1972. //
  1973. header = WorkContext->ResponseHeader;
  1974. response = (PRESP_WRITE_MPX_DATAGRAM)WorkContext->ResponseParameters;
  1975. SmbPutUlong( &response->Mask, writeMpx->Mask );
  1976. writeMpx->Mask = 0;
  1977. SmbPutAlignedUshort( &header->SequenceNumber, writeMpx->SequenceNumber );
  1978. writeMpx->SequenceNumber = 0;
  1979. SmbPutAlignedUshort( &header->Mid, writeMpx->Mid );
  1980. //
  1981. // Save the status.
  1982. //
  1983. status = rfcb->SavedError;
  1984. rfcb->SavedError = STATUS_SUCCESS;
  1985. //
  1986. // Now we can release the lock.
  1987. //
  1988. if ( GET_BLOCK_STATE(rfcb) != BlockStateActive ) {
  1989. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  1990. RestartWriteMpxCompleteRfcbClose( WorkContext );
  1991. } else {
  1992. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  1993. }
  1994. //
  1995. // Build the response message.
  1996. //
  1997. if ( !NT_SUCCESS(status) ) {
  1998. SrvSetSmbError2( WorkContext, status, TRUE );
  1999. }
  2000. response->WordCount = 2;
  2001. SmbPutUshort( &response->ByteCount, 0 );
  2002. WorkContext->ResponseParameters = NEXT_LOCATION(
  2003. response,
  2004. RESP_WRITE_MPX_DATAGRAM,
  2005. 0
  2006. );
  2007. return TRUE;
  2008. } // CheckForWriteMpxComplete
  2009. VOID SRVFASTCALL
  2010. RestartPrepareMpxMdlWrite (
  2011. IN OUT PWORK_CONTEXT WorkContext
  2012. )
  2013. {
  2014. PSMB_HEADER header;
  2015. PREQ_WRITE_MPX request;
  2016. PRFCB rfcb;
  2017. PWRITE_MPX_CONTEXT writeMpx;
  2018. PCONNECTION connection;
  2019. PLIST_ENTRY listEntry;
  2020. PWORK_CONTEXT workContext;
  2021. USHORT writeLength;
  2022. PCHAR writeAddress;
  2023. KIRQL oldIrql;
  2024. ULONG bytesCopied;
  2025. NTSTATUS status = STATUS_SUCCESS;
  2026. SMB_STATUS SmbStatus = SmbStatusInProgress;
  2027. PMDL mdl;
  2028. BOOLEAN bNeedTrace = (WorkContext->bAlreadyTrace == FALSE);
  2029. if (bNeedTrace) {
  2030. if (WorkContext->PreviousSMB == EVENT_TYPE_SMB_LAST_EVENT)
  2031. WorkContext->PreviousSMB = EVENT_TYPE_SMB_WRITE_MPX;
  2032. SrvWmiStartContext(WorkContext);
  2033. }
  2034. else
  2035. WorkContext->bAlreadyTrace = FALSE;
  2036. header = WorkContext->RequestHeader;
  2037. request = (PREQ_WRITE_MPX)WorkContext->RequestParameters;
  2038. rfcb = WorkContext->Rfcb;
  2039. writeMpx = &rfcb->WriteMpx;
  2040. connection = WorkContext->Connection;
  2041. //
  2042. // If the MDL write preparation succeeded, copy the data from this
  2043. // packet into the cache. If it failed, toss this packet.
  2044. //
  2045. if( NT_SUCCESS(WorkContext->Irp->IoStatus.Status) ) {
  2046. mdl = WorkContext->Irp->MdlAddress;
  2047. #if DBG
  2048. IF_SMB_DEBUG(MPX2) {
  2049. KdPrint(( "RestartPrepareMpxMdlWrite: rfcb %p, input chain:\n", rfcb ));
  2050. DumpMdlChain( mdl );
  2051. }
  2052. #endif
  2053. writeMpx->MdlChain = mdl;
  2054. writeMpx->NumberOfRuns = 1;
  2055. writeMpx->RunList[0].Offset = 0;
  2056. writeLength = WorkContext->Parameters.WriteMpx.WriteLength;
  2057. writeMpx->RunList[0].Length = writeLength;
  2058. //
  2059. // If we have the transport context, setup writeAddress accordingly.
  2060. //
  2061. if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
  2062. writeAddress = (PCHAR)WorkContext->Parameters.WriteMpx.Buffer +
  2063. SmbGetUshort( &request->DataOffset );
  2064. } else {
  2065. writeAddress = (PCHAR)WorkContext->ResponseHeader +
  2066. SmbGetUshort( &request->DataOffset );
  2067. }
  2068. status = TdiCopyBufferToMdl(
  2069. writeAddress,
  2070. 0,
  2071. writeLength,
  2072. mdl,
  2073. 0,
  2074. &bytesCopied
  2075. );
  2076. ASSERT( status == STATUS_SUCCESS );
  2077. ASSERT( bytesCopied == writeLength );
  2078. KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
  2079. ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
  2080. writeMpx->Glomming = TRUE;
  2081. ASSERT( writeMpx->Mask == 0 );
  2082. writeMpx->Mask = SmbGetUlong( &request->Mask );
  2083. --writeMpx->ReferenceCount;
  2084. ASSERT( writeMpx->SequenceNumber == 0 );
  2085. } else {
  2086. KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
  2087. ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
  2088. if ( rfcb->SavedError == STATUS_SUCCESS ) {
  2089. rfcb->SavedError = WorkContext->Irp->IoStatus.Status;
  2090. }
  2091. --writeMpx->ReferenceCount;
  2092. writeMpx->Glomming = FALSE;
  2093. }
  2094. //
  2095. // Return the TransportContext
  2096. //
  2097. if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
  2098. TdiReturnChainedReceives( &WorkContext->Parameters.WriteMpx.TransportContext,
  2099. 1
  2100. );
  2101. }
  2102. writeMpx->GlomPending = FALSE;
  2103. while ( !IsListEmpty( &writeMpx->GlomDelayList ) ) {
  2104. listEntry = RemoveHeadList( &writeMpx->GlomDelayList );
  2105. workContext = CONTAINING_RECORD( listEntry, WORK_CONTEXT, ListEntry );
  2106. workContext->FspRestartRoutine = AddPacketToGlom;
  2107. QUEUE_WORK_TO_FSP( workContext );
  2108. }
  2109. //
  2110. // If the rfcb is closing and the write mpx ref count == 0,
  2111. // then we must complete the close.
  2112. //
  2113. if ( (GET_BLOCK_STATE(rfcb) != BlockStateActive) &&
  2114. (writeMpx->ReferenceCount == 0) ) {
  2115. RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
  2116. WorkContext->FspRestartRoutine = RestartWriteMpxCompleteRfcbClose;
  2117. QUEUE_WORK_TO_FSP( WorkContext );
  2118. KeLowerIrql( oldIrql );
  2119. goto Cleanup;
  2120. }
  2121. RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
  2122. SrvFsdRestartSmbComplete( WorkContext );
  2123. KeLowerIrql( oldIrql );
  2124. Cleanup:
  2125. if (bNeedTrace) {
  2126. SrvWmiEndContext(WorkContext);
  2127. }
  2128. return;
  2129. } // RestartPrepareMpxMdlWrite
  2130. VOID SRVFASTCALL
  2131. AddPacketToGlom (
  2132. IN OUT PWORK_CONTEXT WorkContext
  2133. )
  2134. {
  2135. PSMB_HEADER header;
  2136. PREQ_WRITE_MPX request;
  2137. PRESP_WRITE_MPX_DATAGRAM response;
  2138. PRFCB rfcb;
  2139. PWRITE_MPX_CONTEXT writeMpx;
  2140. PCONNECTION connection;
  2141. ULONG fileOffset;
  2142. USHORT glomOffset;
  2143. CLONG bufferOffset;
  2144. PCHAR writeAddress;
  2145. USHORT writeLength;
  2146. ULONG bytesCopied;
  2147. KIRQL oldIrql;
  2148. NTSTATUS status = STATUS_UNSUCCESSFUL;
  2149. USHORT sequenceNumber;
  2150. BOOLEAN rfcbClosing;
  2151. PWRITE_MPX_RUN run, nextRun;
  2152. ULONG runIndex, runCount;
  2153. USHORT runOffset;
  2154. USHORT runLength;
  2155. PMDL cacheMdl;
  2156. LARGE_INTEGER cacheOffset;
  2157. header = WorkContext->RequestHeader;
  2158. request = (PREQ_WRITE_MPX)WorkContext->RequestParameters;
  2159. rfcb = WorkContext->Rfcb;
  2160. connection = WorkContext->Connection;
  2161. writeMpx = &rfcb->WriteMpx;
  2162. cacheMdl = writeMpx->MdlChain;
  2163. if( writeMpx->Glomming == FALSE ) {
  2164. //
  2165. // We must have encountered an error in RestartPrepareMpxMdlWrite(), but
  2166. // we call through this routine to ensure we send a response back to the
  2167. // client.
  2168. //
  2169. KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
  2170. ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
  2171. goto check;
  2172. }
  2173. ASSERT( writeMpx->Glomming );
  2174. ASSERT( !writeMpx->GlomPending );
  2175. ASSERT( WorkContext->Parameters.WriteMpx.Mid == writeMpx->Mid );
  2176. //
  2177. // Get the file offset of this packet's data.
  2178. //
  2179. fileOffset = WorkContext->Parameters.WriteMpx.Offset;
  2180. //
  2181. // Determine the amount of data to write. This is the minimum of
  2182. // the amount requested by the client and the amount of data
  2183. // actually sent in the request buffer.
  2184. //
  2185. bufferOffset = SmbGetUshort( &request->DataOffset );
  2186. //
  2187. // If we have the transport context, setup writeAddress accordingly.
  2188. //
  2189. if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
  2190. writeAddress = (PCHAR)WorkContext->Parameters.WriteMpx.Buffer +
  2191. bufferOffset;
  2192. } else {
  2193. writeAddress = (PCHAR)header + bufferOffset;
  2194. }
  2195. writeLength = WorkContext->Parameters.WriteMpx.WriteLength;
  2196. ASSERT( writeLength <= 0xffff );
  2197. //
  2198. // If the data doesn't fall within the bounds of the glommed write,
  2199. // discard the packet.
  2200. //
  2201. // We always know that we've copied at least the first part of the
  2202. // glom.
  2203. //
  2204. ASSERT( writeMpx->NumberOfRuns > 0 );
  2205. KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
  2206. if ( fileOffset <= writeMpx->StartOffset ) {
  2207. ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
  2208. goto discard;
  2209. }
  2210. fileOffset -= writeMpx->StartOffset;
  2211. if ( (fileOffset + writeLength) > writeMpx->Length ) {
  2212. ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
  2213. goto discard;
  2214. }
  2215. ASSERT( fileOffset <= 0xffff );
  2216. ASSERT( fileOffset + writeLength <= 0xffff );
  2217. glomOffset = (USHORT)fileOffset;
  2218. //
  2219. // Copy the packet data into the glom.
  2220. //
  2221. status = TdiCopyBufferToMdl(
  2222. writeAddress,
  2223. 0,
  2224. writeLength,
  2225. cacheMdl,
  2226. glomOffset,
  2227. &bytesCopied
  2228. );
  2229. ASSERT( status == STATUS_SUCCESS );
  2230. ASSERT( bytesCopied == writeLength );
  2231. //
  2232. // Return the TransportContext
  2233. //
  2234. if ( WorkContext->Parameters.WriteMpx.TransportContext ) {
  2235. TdiReturnChainedReceives( &WorkContext->Parameters.WriteMpx.TransportContext,
  2236. 1
  2237. );
  2238. }
  2239. ACQUIRE_DPC_SPIN_LOCK( &connection->SpinLock );
  2240. //
  2241. // Update the glom run information. Note that this packet may have
  2242. // been received multiple times, so it may already be marked in the
  2243. // run information.
  2244. //
  2245. if (0) IF_SMB_DEBUG(MPX2) {
  2246. KdPrint(( "rfcb %p, offset %lx, length %lx\n", rfcb, glomOffset, writeLength ));
  2247. }
  2248. runCount = writeMpx->NumberOfRuns;
  2249. for ( runIndex = 1, nextRun = &writeMpx->RunList[1];
  2250. runIndex < runCount;
  2251. runIndex++, nextRun++ ) {
  2252. if ( nextRun->Offset > glomOffset ) {
  2253. break;
  2254. }
  2255. }
  2256. run = nextRun - 1;
  2257. runOffset = run->Offset;
  2258. runLength = run->Length;
  2259. ASSERT( runOffset <= glomOffset );
  2260. if ( (runOffset + runLength) == glomOffset ) {
  2261. //
  2262. // This packet abuts the end of the previous run. Add the
  2263. // length of this packet to the run length and attempt to
  2264. // coalesce with the next run.
  2265. //
  2266. runLength += writeLength;
  2267. goto coalesce;
  2268. }
  2269. if ( (runOffset + runLength) > glomOffset ) {
  2270. //
  2271. // This packet overlaps the previous run. If it lies completely
  2272. // within the previous run, ignore it.
  2273. //
  2274. if ( (USHORT)(runOffset + runLength) >= (glomOffset + writeLength) ) {
  2275. goto discard;
  2276. }
  2277. //
  2278. // This packet overlaps and extends the previous run. Calculate
  2279. // the new run length and attempt to coalesce with the next run.
  2280. //
  2281. runLength = (glomOffset - runOffset + writeLength);
  2282. goto coalesce;
  2283. }
  2284. //
  2285. // This packet's data is disjoint from the previous run.
  2286. //
  2287. if ( runIndex < runCount ) {
  2288. //
  2289. // There is a next run. Does this packet overlap with that run?
  2290. //
  2291. runOffset = nextRun->Offset;
  2292. runLength = nextRun->Length;
  2293. if ( (glomOffset + writeLength) >= runOffset ) {
  2294. //
  2295. // This packet overlaps the next run. Calculate the new run
  2296. // length.
  2297. //
  2298. nextRun->Offset = glomOffset;
  2299. nextRun->Length = runOffset - glomOffset + runLength;
  2300. goto check;
  2301. }
  2302. }
  2303. //
  2304. // Either this packet is disjoint from the next run, or there is no
  2305. // next run. Is there room in the run array for another run? If
  2306. // not, discard this packet. (Note that we discard it even though
  2307. // we have already copied the packet data. That's OK -- it will
  2308. // just be resent.)
  2309. //
  2310. if ( runCount == MAX_GLOM_RUN_COUNT ) {
  2311. goto discard;
  2312. }
  2313. //
  2314. // Add a new run. Since we know the new run is disjoint from the
  2315. // previous run, we know that the glom is not complete.
  2316. //
  2317. RtlMoveMemory( // NOT RtlCopyMemory -- buffers overlap
  2318. nextRun + 1,
  2319. nextRun,
  2320. (runCount - runIndex) * sizeof(WRITE_MPX_RUN)
  2321. );
  2322. writeMpx->NumberOfRuns++;
  2323. nextRun->Offset = glomOffset;
  2324. nextRun->Length = writeLength;
  2325. goto check;
  2326. coalesce:
  2327. if ( runIndex == runCount ) {
  2328. run->Length = runLength;
  2329. } else if ( (runOffset + runLength) >= nextRun->Offset ) {
  2330. run->Length = nextRun->Length + nextRun->Offset - runOffset;
  2331. writeMpx->NumberOfRuns--;
  2332. RtlMoveMemory( // NOT RtlCopyMemory -- buffers overlap
  2333. nextRun,
  2334. nextRun + 1,
  2335. (runCount - runIndex) * sizeof(WRITE_MPX_RUN)
  2336. );
  2337. } else {
  2338. run->Length += writeLength;
  2339. ASSERT( (runOffset + run->Length) < nextRun->Offset );
  2340. }
  2341. if ( (writeMpx->NumberOfRuns == 1) &&
  2342. (writeMpx->RunList[0].Length == writeMpx->Length) ) {
  2343. //
  2344. // The glom is complete.
  2345. //
  2346. writeMpx->GlomComplete = TRUE;
  2347. }
  2348. check:
  2349. if (0) IF_SMB_DEBUG(MPX2) {
  2350. if( writeMpx->Glomming ) {
  2351. ULONG i;
  2352. PWRITE_MPX_RUN runi;
  2353. for ( i = 0, runi = &writeMpx->RunList[0];
  2354. i < writeMpx->NumberOfRuns;
  2355. i++, runi++ ) {
  2356. KdPrint(( " run %d: offset %lx, length %lx\n", i, runi->Offset, runi->Length ));
  2357. }
  2358. }
  2359. }
  2360. writeMpx->Mask |= SmbGetUlong( &request->Mask );
  2361. //
  2362. // If this is an unsequenced request, we're done. We don't respond
  2363. // until we get a sequenced request.
  2364. //
  2365. sequenceNumber = SmbGetAlignedUshort( &header->SequenceNumber );
  2366. if ( sequenceNumber == 0 ) {
  2367. goto discard;
  2368. }
  2369. //
  2370. // This is the last request in this mux sent by the client. Save
  2371. // the sequence number.
  2372. //
  2373. writeMpx->SequenceNumber = sequenceNumber;
  2374. discard:
  2375. //
  2376. // If we have received the sequenced command for this write mux,
  2377. // and this is the last active command, then it's time to send
  2378. // the response. Otherwise, we are done with this SMB.
  2379. //
  2380. if ( --writeMpx->ReferenceCount != 0 ) {
  2381. RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
  2382. SrvFsdRestartSmbComplete( WorkContext );
  2383. KeLowerIrql( oldIrql );
  2384. return;
  2385. }
  2386. //
  2387. // WriteMpx refcount is 0.
  2388. //
  2389. rfcbClosing = (GET_BLOCK_STATE(rfcb) != BlockStateActive);
  2390. if ( writeMpx->SequenceNumber == 0 ) {
  2391. //
  2392. // If the rfcb is closing, complete the cleanup.
  2393. //
  2394. RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
  2395. KeLowerIrql( oldIrql );
  2396. if ( rfcbClosing ) {
  2397. RestartWriteMpxCompleteRfcbClose( WorkContext );
  2398. }
  2399. SrvRestartFsdComplete( WorkContext );
  2400. return;
  2401. }
  2402. //
  2403. // We are done with this write mux. Save the accumulated mask, the
  2404. // sequence number, and the original MID, then clear the mask and
  2405. // sequence number to indicate that we no longer are in the middle
  2406. // of a write mux.
  2407. //
  2408. response = (PRESP_WRITE_MPX_DATAGRAM)WorkContext->ResponseParameters;
  2409. SmbPutUlong( &response->Mask, writeMpx->Mask );
  2410. writeMpx->Mask = 0;
  2411. SmbPutAlignedUshort( &header->SequenceNumber, writeMpx->SequenceNumber );
  2412. writeMpx->SequenceNumber = 0;
  2413. SmbPutAlignedUshort( &header->Mid, writeMpx->Mid );
  2414. //
  2415. // If the glom is complete, we need to complete the MDL write. But
  2416. // we can't do that with the lock held, so we need to clear out all
  2417. // information related to the glom first.
  2418. //
  2419. if ( writeMpx->Glomming && writeMpx->GlomComplete ) {
  2420. PWORK_CONTEXT newContext;
  2421. //
  2422. // Save and clear information about the active glom.
  2423. //
  2424. writeMpx->Glomming = FALSE;
  2425. writeMpx->GlomComplete = FALSE;
  2426. cacheOffset.QuadPart = writeMpx->StartOffset;
  2427. writeLength = writeMpx->Length;
  2428. DEBUG writeMpx->MdlChain = NULL;
  2429. DEBUG writeMpx->StartOffset = 0;
  2430. DEBUG writeMpx->Length = 0;
  2431. //
  2432. // Save the status.
  2433. //
  2434. status = rfcb->SavedError;
  2435. rfcb->SavedError = STATUS_SUCCESS;
  2436. //
  2437. // Now we can release the lock.
  2438. //
  2439. RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
  2440. KeLowerIrql( oldIrql );
  2441. ALLOCATE_WORK_CONTEXT( WorkContext->CurrentWorkQueue, &newContext );
  2442. #if DBG
  2443. IF_SMB_DEBUG(MPX2) {
  2444. KdPrint(( "AddPacketToGlom: rfcb %p, completed chain:\n", rfcb ));
  2445. DumpMdlChain( cacheMdl );
  2446. }
  2447. #endif
  2448. if( newContext == NULL ) {
  2449. //
  2450. // Tell the cache manager that we're done with this MDL write.
  2451. //
  2452. if( rfcb->Lfcb->MdlWriteComplete == NULL ||
  2453. rfcb->Lfcb->MdlWriteComplete(
  2454. rfcb->Lfcb->FileObject,
  2455. &cacheOffset,
  2456. cacheMdl,
  2457. rfcb->Lfcb->DeviceObject ) == FALSE ) {
  2458. status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
  2459. cacheMdl,
  2460. IRP_MJ_WRITE,
  2461. &cacheOffset,
  2462. writeLength
  2463. );
  2464. if( !NT_SUCCESS( status ) ) {
  2465. SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
  2466. }
  2467. }
  2468. } else {
  2469. //
  2470. // Send the FsRtlMdlWriteComplete off on its way, and go ahead and send
  2471. // the response to the client now.
  2472. //
  2473. newContext->Rfcb = WorkContext->Rfcb;
  2474. SrvReferenceRfcb( newContext->Rfcb );
  2475. newContext->Parameters.WriteMpxMdlWriteComplete.CacheOffset = cacheOffset;
  2476. newContext->Parameters.WriteMpxMdlWriteComplete.WriteLength = writeLength;
  2477. newContext->Parameters.WriteMpxMdlWriteComplete.CacheMdl = cacheMdl;
  2478. newContext->FspRestartRoutine = WriteMpxMdlWriteComplete;
  2479. SrvQueueWorkToFsp( newContext );
  2480. }
  2481. } else {
  2482. if( writeMpx->Glomming == FALSE ) {
  2483. status = rfcb->SavedError;
  2484. rfcb->SavedError = STATUS_SUCCESS;
  2485. }
  2486. //
  2487. // Now we can release the lock.
  2488. //
  2489. RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
  2490. KeLowerIrql( oldIrql );
  2491. }
  2492. //
  2493. // Complete the rfcb close.
  2494. //
  2495. if ( rfcbClosing ) {
  2496. RestartWriteMpxCompleteRfcbClose( WorkContext );
  2497. }
  2498. //
  2499. // Build the response message.
  2500. //
  2501. if ( !NT_SUCCESS(status) ) {
  2502. SrvSetSmbError2( WorkContext, status, TRUE );
  2503. }
  2504. response->WordCount = 2;
  2505. SmbPutUshort( &response->ByteCount, 0 );
  2506. WorkContext->ResponseParameters = NEXT_LOCATION(
  2507. response,
  2508. RESP_WRITE_MPX_DATAGRAM,
  2509. 0
  2510. );
  2511. //
  2512. // Send the response.
  2513. //
  2514. SrvFsdSendResponse( WorkContext );
  2515. return;
  2516. } // AddPacketToGlom
  2517. BOOLEAN
  2518. AddPacketToGlomInIndication (
  2519. IN PWORK_CONTEXT WorkContext,
  2520. IN OUT PRFCB Rfcb,
  2521. IN PVOID Tsdu,
  2522. IN ULONG BytesAvailable,
  2523. IN ULONG ReceiveDatagramFlags,
  2524. IN PVOID SourceAddress,
  2525. IN PVOID Options
  2526. )
  2527. /*++
  2528. Routine Description:
  2529. Do Write glomming at indication.
  2530. *** connection spinlock assumed held. Released on exit ***
  2531. Arguments:
  2532. Return Value:
  2533. TRUE if the caller has to clean up the connection block.
  2534. --*/
  2535. {
  2536. PREQ_WRITE_MPX request;
  2537. PRESP_WRITE_MPX_DATAGRAM response;
  2538. PWRITE_MPX_CONTEXT writeMpx = &Rfcb->WriteMpx;
  2539. PCONNECTION connection = WorkContext->Connection;
  2540. ULONG fileOffset;
  2541. USHORT glomOffset;
  2542. CLONG bufferOffset;
  2543. PCHAR writeAddress;
  2544. USHORT writeLength;
  2545. ULONG bytesCopied;
  2546. NTSTATUS status;
  2547. USHORT sequenceNumber;
  2548. PSMB_HEADER header = (PSMB_HEADER)Tsdu;
  2549. PWRITE_MPX_RUN run, nextRun;
  2550. ULONG runIndex, runCount;
  2551. USHORT runOffset;
  2552. USHORT runLength;
  2553. ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
  2554. //
  2555. // copied from SrvRestartReceive.
  2556. //
  2557. WorkContext->CurrentWorkQueue->stats.BytesReceived += BytesAvailable;
  2558. connection->BreakIIToNoneJustSent = FALSE;
  2559. SrvUpdateErrorCount( &SrvNetworkErrorRecord, FALSE );
  2560. //
  2561. // Set up locals.
  2562. //
  2563. request = (PREQ_WRITE_MPX)(header + 1);
  2564. ASSERT( writeMpx->Glomming );
  2565. ASSERT( !writeMpx->GlomPending );
  2566. ASSERT( header->Mid == writeMpx->Mid );
  2567. //
  2568. // Get the file offset of this packet's data.
  2569. //
  2570. fileOffset = SmbGetUlong( &request->Offset );
  2571. //
  2572. // Determine the amount of data to write. This is the minimum of
  2573. // the amount requested by the client and the amount of data
  2574. // actually sent in the request buffer.
  2575. //
  2576. bufferOffset = SmbGetUshort( &request->DataOffset );
  2577. writeAddress = (PCHAR)header + bufferOffset;
  2578. writeLength =
  2579. (USHORT)(MIN( (CLONG)SmbGetUshort( &request->DataLength ),
  2580. BytesAvailable - bufferOffset ));
  2581. ASSERT( writeLength <= 0xffff );
  2582. //
  2583. // If the data doesn't fall within the bounds of the glommed write,
  2584. // discard the packet.
  2585. //
  2586. // We always know that we've copied at least the first part of the
  2587. // glom.
  2588. //
  2589. ASSERT( writeMpx->NumberOfRuns > 0 );
  2590. if ( fileOffset <= writeMpx->StartOffset ) {
  2591. goto discard;
  2592. }
  2593. fileOffset -= writeMpx->StartOffset;
  2594. if ( (fileOffset + writeLength) > writeMpx->Length ) {
  2595. goto discard;
  2596. }
  2597. ASSERT( fileOffset <= 0xffff );
  2598. ASSERT( fileOffset + writeLength <= 0xffff );
  2599. glomOffset = (USHORT)fileOffset;
  2600. //
  2601. // Copy the packet data into the glom.
  2602. //
  2603. status = TdiCopyBufferToMdl(
  2604. writeAddress,
  2605. 0,
  2606. writeLength,
  2607. writeMpx->MdlChain,
  2608. glomOffset,
  2609. &bytesCopied
  2610. );
  2611. ASSERT( status == STATUS_SUCCESS );
  2612. ASSERT( bytesCopied == writeLength );
  2613. //
  2614. // Update the glom run information. Note that this packet may have
  2615. // been received multiple times, so it may already be marked in the
  2616. // run information.
  2617. //
  2618. if (0) IF_SMB_DEBUG(MPX2) {
  2619. KdPrint(( "rfcb %p, offset %lx, length %lx\n", Rfcb, glomOffset, writeLength ));
  2620. }
  2621. runCount = writeMpx->NumberOfRuns;
  2622. for ( runIndex = 1, nextRun = &writeMpx->RunList[1];
  2623. runIndex < runCount;
  2624. runIndex++, nextRun++ ) {
  2625. if ( nextRun->Offset > glomOffset ) {
  2626. break;
  2627. }
  2628. }
  2629. run = nextRun - 1;
  2630. runOffset = run->Offset;
  2631. runLength = run->Length;
  2632. ASSERT( runOffset <= glomOffset );
  2633. if ( (runOffset + runLength) == glomOffset ) {
  2634. //
  2635. // This packet abuts the end of the previous run. Add the
  2636. // length of this packet to the run length and attempt to
  2637. // coalesce with the next run.
  2638. //
  2639. runLength += writeLength;
  2640. goto coalesce;
  2641. }
  2642. if ( (runOffset + runLength) > glomOffset ) {
  2643. //
  2644. // This packet overlaps the previous run. If it lies completely
  2645. // within the previous run, ignore it.
  2646. //
  2647. if ( (USHORT)(runOffset + runLength) >= (glomOffset + writeLength) ) {
  2648. goto discard;
  2649. }
  2650. //
  2651. // This packet overlaps and extends the previous run. Calculate
  2652. // the new run length and attempt to coalesce with the next run.
  2653. //
  2654. runLength = (glomOffset - runOffset + writeLength);
  2655. goto coalesce;
  2656. }
  2657. //
  2658. // This packet's data is disjoint from the previous run.
  2659. //
  2660. if ( runIndex < runCount ) {
  2661. //
  2662. // There is a next run. Does this packet overlap with that run?
  2663. //
  2664. runOffset = nextRun->Offset;
  2665. runLength = nextRun->Length;
  2666. if ( (glomOffset + writeLength) >= runOffset ) {
  2667. //
  2668. // This packet overlaps the next run. Calculate the new run
  2669. // length.
  2670. //
  2671. nextRun->Offset = glomOffset;
  2672. nextRun->Length = runOffset - glomOffset + runLength;
  2673. goto check;
  2674. }
  2675. }
  2676. //
  2677. // Either this packet is disjoint from the next run, or there is no
  2678. // next run. Is there room in the run array for another run? If
  2679. // not, discard this packet. (Note that we discard it even though
  2680. // we have already copied the packet data. That's OK -- it will
  2681. // just be resent.)
  2682. //
  2683. if ( runCount == MAX_GLOM_RUN_COUNT ) {
  2684. goto discard;
  2685. }
  2686. //
  2687. // Add a new run. Since we know the new run is disjoint from the
  2688. // previous run, we know that the glom is not complete.
  2689. //
  2690. RtlMoveMemory( // NOT RtlCopyMemory -- buffers overlap
  2691. nextRun + 1,
  2692. nextRun,
  2693. (runCount - runIndex) * sizeof(WRITE_MPX_RUN)
  2694. );
  2695. writeMpx->NumberOfRuns++;
  2696. nextRun->Offset = glomOffset;
  2697. nextRun->Length = writeLength;
  2698. goto check;
  2699. coalesce:
  2700. if ( runIndex == runCount ) {
  2701. run->Length = runLength;
  2702. } else if ( (runOffset + runLength) >= nextRun->Offset ) {
  2703. run->Length = nextRun->Length + nextRun->Offset - runOffset;
  2704. writeMpx->NumberOfRuns--;
  2705. RtlMoveMemory( // NOT RtlCopyMemory -- buffers overlap
  2706. nextRun,
  2707. nextRun + 1,
  2708. (runCount - runIndex) * sizeof(WRITE_MPX_RUN)
  2709. );
  2710. } else {
  2711. run->Length += writeLength;
  2712. ASSERT( (runOffset + run->Length) < nextRun->Offset );
  2713. }
  2714. if ( (writeMpx->NumberOfRuns == 1) &&
  2715. (writeMpx->RunList[0].Length == writeMpx->Length) ) {
  2716. //
  2717. // The glom is complete.
  2718. //
  2719. writeMpx->GlomComplete = TRUE;
  2720. }
  2721. check:
  2722. if (0) IF_SMB_DEBUG(MPX2) {
  2723. ULONG i;
  2724. PWRITE_MPX_RUN runi;
  2725. for ( i = 0, runi = &writeMpx->RunList[0];
  2726. i < writeMpx->NumberOfRuns;
  2727. i++, runi++ ) {
  2728. KdPrint(( " run %d: offset %lx, length %lx\n", i, runi->Offset, runi->Length ));
  2729. }
  2730. }
  2731. writeMpx->Mask |= SmbGetUlong( &request->Mask );
  2732. //
  2733. // If this is an unsequenced request, we're done. We don't respond
  2734. // until we get a sequenced request.
  2735. //
  2736. sequenceNumber = SmbGetAlignedUshort( &header->SequenceNumber );
  2737. if ( sequenceNumber == 0 ) {
  2738. goto discard;
  2739. }
  2740. //
  2741. // This is the last request in this mux sent by the client. Save
  2742. // the sequence number.
  2743. //
  2744. writeMpx->SequenceNumber = sequenceNumber;
  2745. discard:
  2746. //
  2747. // If we have received the sequenced command for this write mux,
  2748. // and this is the last active command, then it's time to send
  2749. // the response. Otherwise, we are done with this SMB.
  2750. //
  2751. if ( (--writeMpx->ReferenceCount != 0) ||
  2752. (writeMpx->SequenceNumber == 0) ) {
  2753. RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
  2754. return TRUE;
  2755. }
  2756. //
  2757. // Copy the header portion for the response.
  2758. //
  2759. TdiCopyLookaheadData(
  2760. WorkContext->RequestBuffer->Buffer,
  2761. Tsdu,
  2762. sizeof(SMB_HEADER),
  2763. ReceiveDatagramFlags
  2764. );
  2765. // WorkContext->RequestBuffer->DataLength = BytesAvailable;
  2766. //
  2767. // We are done with this write mux. Save the accumulated mask, the
  2768. // sequence number, and the original MID, then clear the mask and
  2769. // sequence number to indicate that we no longer are in the middle
  2770. // of a write mux.
  2771. //
  2772. response = (PRESP_WRITE_MPX_DATAGRAM)WorkContext->ResponseParameters;
  2773. SmbPutUlong( &response->Mask, writeMpx->Mask );
  2774. writeMpx->Mask = 0;
  2775. SmbPutAlignedUshort( &header->SequenceNumber, writeMpx->SequenceNumber );
  2776. writeMpx->SequenceNumber = 0;
  2777. SmbPutAlignedUshort( &header->Mid, writeMpx->Mid );
  2778. //
  2779. // If the glom is complete, we need to complete the MDL write. But
  2780. // we can't do that with the lock held, so we need to clear out all
  2781. // information related to the glom first.
  2782. //
  2783. if ( writeMpx->GlomComplete ) {
  2784. //
  2785. // The file is active and the TID is valid. Reference the
  2786. // RFCB.
  2787. //
  2788. Rfcb->BlockHeader.ReferenceCount++;
  2789. UPDATE_REFERENCE_HISTORY( Rfcb, FALSE );
  2790. //
  2791. // Now we can release the lock.
  2792. //
  2793. RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
  2794. WorkContext->Rfcb = Rfcb;
  2795. //
  2796. // Build the response message.
  2797. //
  2798. response->WordCount = 2;
  2799. SmbPutUshort( &response->ByteCount, 0 );
  2800. WorkContext->ResponseParameters = NEXT_LOCATION(
  2801. response,
  2802. RESP_WRITE_MPX_DATAGRAM,
  2803. 0
  2804. );
  2805. //
  2806. // Send this off to the fsp for final processing. We need to do
  2807. // this since we cannot call the cache manager at dpc level.
  2808. //
  2809. WorkContext->FspRestartRoutine = RestartCompleteGlommingInIndication;
  2810. SrvQueueWorkToFsp( WorkContext );
  2811. return FALSE;
  2812. } else {
  2813. //
  2814. // Now we can release the lock.
  2815. //
  2816. RELEASE_DPC_SPIN_LOCK( &connection->SpinLock );
  2817. }
  2818. //
  2819. // Build the response message.
  2820. //
  2821. ASSERT( status == STATUS_SUCCESS );
  2822. response->WordCount = 2;
  2823. SmbPutUshort( &response->ByteCount, 0 );
  2824. WorkContext->ResponseParameters = NEXT_LOCATION(
  2825. response,
  2826. RESP_WRITE_MPX_DATAGRAM,
  2827. 0
  2828. );
  2829. //
  2830. // Send the response.
  2831. //
  2832. SrvFsdSendResponse( WorkContext );
  2833. return FALSE;
  2834. } // AddPacketToGlomInIndication
  2835. SMB_PROCESSOR_RETURN_TYPE
  2836. SrvSmbWriteMpxSecondary (
  2837. SMB_PROCESSOR_PARAMETERS
  2838. )
  2839. /*++
  2840. Routine Description:
  2841. Processes the Write Mpx Secondary SMB.
  2842. *** The server should never see this SMB, since it returns the "use
  2843. standard read" error to the main Write Mpx SMB, except over IPX,
  2844. which doesn't use Write Mpx Secondary.
  2845. Arguments:
  2846. WorkContext - Supplies a pointer to the work context block
  2847. representing the work item
  2848. Return Value:
  2849. None.
  2850. --*/
  2851. {
  2852. PAGED_CODE( );
  2853. //
  2854. // Send a response that tells the client that this SMB is not
  2855. // valid.
  2856. //
  2857. INTERNAL_ERROR(
  2858. ERROR_LEVEL_UNEXPECTED,
  2859. "SrvSmbWriteMpxSecondary: unexpected SMB",
  2860. NULL,
  2861. NULL
  2862. );
  2863. SrvSetSmbError( WorkContext, STATUS_INVALID_SMB );
  2864. return SmbStatusSendResponse;
  2865. } // SrvSmbWriteMpxSecondary
  2866. VOID SRVFASTCALL
  2867. RestartCompleteGlommingInIndication(
  2868. IN OUT PWORK_CONTEXT WorkContext
  2869. )
  2870. {
  2871. LARGE_INTEGER cacheOffset;
  2872. KIRQL oldIrql;
  2873. PMDL cacheMdl;
  2874. NTSTATUS status;
  2875. PRFCB rfcb = WorkContext->Rfcb;
  2876. PWRITE_MPX_CONTEXT writeMpx = &rfcb->WriteMpx;
  2877. PCONNECTION connection = WorkContext->Connection;
  2878. ULONG writeLength;
  2879. ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
  2880. //
  2881. // Save the status.
  2882. //
  2883. status = rfcb->SavedError;
  2884. rfcb->SavedError = STATUS_SUCCESS;
  2885. //
  2886. // If the rfcb has closed, then the mdl write was completed.
  2887. //
  2888. if ( GET_BLOCK_STATE(rfcb) == BlockStateActive ) {
  2889. PWORK_CONTEXT newContext;
  2890. writeMpx->GlomComplete = FALSE;
  2891. writeMpx->Glomming = FALSE;
  2892. cacheOffset.QuadPart = writeMpx->StartOffset;
  2893. cacheMdl = writeMpx->MdlChain;
  2894. writeLength = writeMpx->Length;
  2895. DEBUG writeMpx->MdlChain = NULL;
  2896. DEBUG writeMpx->StartOffset = 0;
  2897. DEBUG writeMpx->Length = 0;
  2898. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  2899. KeRaiseIrql( DISPATCH_LEVEL, &oldIrql );
  2900. ALLOCATE_WORK_CONTEXT( WorkContext->CurrentWorkQueue, &newContext );
  2901. KeLowerIrql( oldIrql );
  2902. if( newContext == NULL ) {
  2903. //
  2904. // Tell the cache manager that we're done with this MDL write.
  2905. //
  2906. if( rfcb->Lfcb->MdlWriteComplete == NULL ||
  2907. rfcb->Lfcb->MdlWriteComplete(
  2908. rfcb->Lfcb->FileObject,
  2909. &cacheOffset,
  2910. cacheMdl,
  2911. rfcb->Lfcb->DeviceObject ) == FALSE ) {
  2912. status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
  2913. cacheMdl,
  2914. IRP_MJ_WRITE,
  2915. &cacheOffset,
  2916. writeLength
  2917. );
  2918. if( !NT_SUCCESS( status ) ) {
  2919. SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
  2920. }
  2921. }
  2922. } else {
  2923. //
  2924. // Send the FsRtlMdlWriteComplete off on its way, and go ahead and send
  2925. // the response to the client now.
  2926. //
  2927. newContext->Rfcb = WorkContext->Rfcb;
  2928. WorkContext->Rfcb = NULL;
  2929. newContext->Parameters.WriteMpxMdlWriteComplete.CacheOffset = cacheOffset;
  2930. newContext->Parameters.WriteMpxMdlWriteComplete.WriteLength = writeLength;
  2931. newContext->Parameters.WriteMpxMdlWriteComplete.CacheMdl = cacheMdl;
  2932. newContext->FspRestartRoutine = WriteMpxMdlWriteComplete;
  2933. SrvQueueWorkToFsp( newContext );
  2934. }
  2935. } else {
  2936. ASSERT( !writeMpx->Glomming );
  2937. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  2938. }
  2939. //
  2940. // Send the response.
  2941. //
  2942. if ( !NT_SUCCESS(status) ) {
  2943. SrvSetSmbError2( WorkContext, status, TRUE );
  2944. }
  2945. SrvFsdSendResponse( WorkContext );
  2946. return;
  2947. } // RestartCompleteGlommingInIndication
  2948. VOID SRVFASTCALL
  2949. WriteMpxMdlWriteComplete (
  2950. IN OUT PWORK_CONTEXT WorkContext
  2951. )
  2952. {
  2953. NTSTATUS status;
  2954. if( WorkContext->Rfcb->Lfcb->MdlWriteComplete == NULL ||
  2955. WorkContext->Rfcb->Lfcb->MdlWriteComplete(
  2956. WorkContext->Rfcb->Lfcb->FileObject,
  2957. &WorkContext->Parameters.WriteMpxMdlWriteComplete.CacheOffset,
  2958. WorkContext->Parameters.WriteMpxMdlWriteComplete.CacheMdl,
  2959. WorkContext->Rfcb->Lfcb->DeviceObject ) == FALSE ) {
  2960. status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
  2961. WorkContext->Parameters.WriteMpxMdlWriteComplete.CacheMdl,
  2962. IRP_MJ_WRITE,
  2963. &WorkContext->Parameters.WriteMpxMdlWriteComplete.CacheOffset,
  2964. WorkContext->Parameters.WriteMpxMdlWriteComplete.WriteLength );
  2965. if( !NT_SUCCESS( status ) ) {
  2966. SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
  2967. }
  2968. }
  2969. SrvDereferenceRfcb( WorkContext->Rfcb );
  2970. WorkContext->Rfcb = NULL;
  2971. WorkContext->FspRestartRoutine = SrvRestartReceive;
  2972. ASSERT( WorkContext->BlockHeader.ReferenceCount == 1 );
  2973. #if DBG
  2974. WorkContext->BlockHeader.ReferenceCount = 0;
  2975. #endif
  2976. RETURN_FREE_WORKITEM( WorkContext );
  2977. }
  2978. VOID SRVFASTCALL
  2979. RestartWriteMpxCompleteRfcbClose (
  2980. IN OUT PWORK_CONTEXT WorkContext
  2981. )
  2982. /*++
  2983. Routine Description:
  2984. Completes the rfcb close after last active writempx is finished.
  2985. Arguments:
  2986. WorkContext - Supplies a pointer to the work context block
  2987. representing the work item
  2988. Return Value:
  2989. None.
  2990. --*/
  2991. {
  2992. PCONNECTION connection = WorkContext->Connection;
  2993. PRFCB rfcb = WorkContext->Rfcb;
  2994. PWRITE_MPX_CONTEXT writeMpx = &rfcb->WriteMpx;
  2995. LARGE_INTEGER cacheOffset;
  2996. PMDL mdlChain;
  2997. KIRQL oldIrql;
  2998. ULONG writeLength;
  2999. NTSTATUS status;
  3000. //
  3001. // This rfcb is closing.
  3002. //
  3003. ACQUIRE_SPIN_LOCK( &connection->SpinLock, &oldIrql );
  3004. ASSERT ( GET_BLOCK_STATE(rfcb) != BlockStateActive );
  3005. writeMpx = &rfcb->WriteMpx;
  3006. if ( writeMpx->Glomming ) {
  3007. //
  3008. // We need to complete this write mdl
  3009. //
  3010. writeMpx->Glomming = FALSE;
  3011. writeMpx->GlomComplete = FALSE;
  3012. //
  3013. // Save the offset and MDL address.
  3014. //
  3015. cacheOffset.QuadPart = writeMpx->StartOffset;
  3016. mdlChain = writeMpx->MdlChain;
  3017. writeLength = writeMpx->Length;
  3018. DEBUG writeMpx->MdlChain = NULL;
  3019. DEBUG writeMpx->StartOffset = 0;
  3020. DEBUG writeMpx->Length = 0;
  3021. //
  3022. // Now we can release the lock.
  3023. //
  3024. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  3025. //
  3026. // Tell the cache manager that we're done with this MDL write.
  3027. //
  3028. if( rfcb->Lfcb->MdlWriteComplete == NULL ||
  3029. rfcb->Lfcb->MdlWriteComplete(
  3030. writeMpx->FileObject,
  3031. &cacheOffset,
  3032. mdlChain,
  3033. rfcb->Lfcb->DeviceObject ) == FALSE ) {
  3034. status = SrvIssueMdlCompleteRequest( WorkContext, NULL,
  3035. mdlChain,
  3036. IRP_MJ_WRITE,
  3037. &cacheOffset,
  3038. writeLength );
  3039. if( !NT_SUCCESS( status ) ) {
  3040. SrvLogServiceFailure( SRV_SVC_MDL_COMPLETE, status );
  3041. }
  3042. }
  3043. } else {
  3044. //
  3045. // Now we can release the lock.
  3046. //
  3047. RELEASE_SPIN_LOCK( &connection->SpinLock, oldIrql );
  3048. }
  3049. //
  3050. // Do the actual close
  3051. //
  3052. SrvCompleteRfcbClose( rfcb );
  3053. return;
  3054. } // RestartWriteMpxCompleteRfcbClose