Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

858 lines
21 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. DataSup.c
  5. Abstract:
  6. This module implements the mailslot data queue support functions.
  7. Author:
  8. Manny Weiser (mannyw) 9-Jan-1991
  9. Revision History:
  10. --*/
  11. #include "mailslot.h"
  12. //
  13. // The debug trace level
  14. //
  15. #define Dbg (DEBUG_TRACE_DATASUP)
  16. //
  17. // Local declarations
  18. //
  19. VOID
  20. MsSetCancelRoutine(
  21. IN PIRP Irp
  22. );
  23. //
  24. // The following macro is used to dump a data queue
  25. //
  26. #define DumpDataQueue(S,P) { \
  27. ULONG MsDumpDataQueue(IN ULONG Level, IN PDATA_QUEUE Ptr); \
  28. DebugTrace(0,Dbg,S,0); \
  29. DebugTrace(0,Dbg,"", MsDumpDataQueue(Dbg,P)); \
  30. }
  31. #ifdef ALLOC_PRAGMA
  32. #pragma alloc_text( PAGE, MsAddDataQueueEntry )
  33. #pragma alloc_text( PAGE, MsInitializeDataQueue )
  34. #pragma alloc_text( PAGE, MsRemoveDataQueueEntry )
  35. #pragma alloc_text( PAGE, MsUninitializeDataQueue )
  36. #pragma alloc_text( PAGE, MsSetCancelRoutine )
  37. #pragma alloc_text( PAGE, MsResetCancelRoutine )
  38. #pragma alloc_text( PAGE, MsRemoveDataQueueIrp )
  39. #endif
  40. #if 0
  41. NOT PAGEABLE -- MsCancelDataQueueIrp
  42. #endif
  43. NTSTATUS
  44. MsInitializeDataQueue (
  45. IN PDATA_QUEUE DataQueue,
  46. IN PEPROCESS Process,
  47. IN ULONG Quota,
  48. IN ULONG MaximumMessageSize
  49. )
  50. /*++
  51. Routine Description:
  52. This routine initializes a new data queue. The indicated quota is taken
  53. from the process and not returned until the data queue is uninitialized.
  54. Arguments:
  55. DataQueue - Supplies the data queue to be initialized.
  56. Process - Supplies a pointer to the process creating the mailslot.
  57. Quota - Supplies the quota to assign to the data queue.
  58. MaximumMessageSize - The size of the largest message that can be
  59. written to the data queue.
  60. Return Value:
  61. None.
  62. --*/
  63. {
  64. NTSTATUS Status;
  65. PAGED_CODE();
  66. DebugTrace(+1, Dbg, "MsInitializeDataQueue, DataQueue = %08lx\n", (ULONG)DataQueue);
  67. //
  68. // Get the process's quota, if we can't get it then this call will
  69. // raise status.
  70. //
  71. Status = PsChargeProcessPagedPoolQuota (Process, Quota);
  72. if (!NT_SUCCESS (Status)) {
  73. return Status;
  74. }
  75. ObReferenceObject( Process );
  76. //
  77. // Initialize the data queue structure.
  78. //
  79. DataQueue->BytesInQueue = 0;
  80. DataQueue->EntriesInQueue = 0;
  81. DataQueue->QueueState = Empty;
  82. DataQueue->MaximumMessageSize = MaximumMessageSize;
  83. DataQueue->Quota = Quota;
  84. DataQueue->QuotaUsed = 0;
  85. InitializeListHead( &DataQueue->DataEntryList );
  86. //
  87. // Return to the caller.
  88. //
  89. DebugTrace(-1, Dbg, "MsInitializeDataQueue -> VOID\n", 0);
  90. return STATUS_SUCCESS;
  91. }
  92. VOID
  93. MsUninitializeDataQueue (
  94. IN PDATA_QUEUE DataQueue,
  95. IN PEPROCESS Process
  96. )
  97. /*++
  98. Routine Description:
  99. This routine uninitializes a data queue. The previously debited quota
  100. is returned to the process.
  101. Arguments:
  102. DataQueue - Supplies the data queue being uninitialized
  103. Process - Supplies a pointer to the process who created the mailslot
  104. Return Value:
  105. None.
  106. --*/
  107. {
  108. PAGED_CODE();
  109. DebugTrace(+1, Dbg, "MsUninitializeDataQueue, DataQueue = %08lx\n", (ULONG)DataQueue);
  110. //
  111. // Assert that the queue is empty
  112. //
  113. ASSERT( IsListEmpty(&DataQueue->DataEntryList) );
  114. ASSERT( DataQueue->BytesInQueue == 0);
  115. ASSERT( DataQueue->EntriesInQueue == 0);
  116. ASSERT( DataQueue->QuotaUsed == 0);
  117. //
  118. // Return all of our quota back to the process
  119. //
  120. PsReturnProcessPagedPoolQuota (Process, DataQueue->Quota);
  121. ObDereferenceObject (Process);
  122. //
  123. // For safety's sake, zero out the data queue structure.
  124. //
  125. RtlZeroMemory (DataQueue, sizeof (DATA_QUEUE));
  126. //
  127. // Return to the caller.
  128. //
  129. DebugTrace(-1, Dbg, "MsUnininializeDataQueue -> VOID\n", 0);
  130. return;
  131. }
  132. NTSTATUS
  133. MsAddDataQueueEntry (
  134. IN PDATA_QUEUE DataQueue,
  135. IN QUEUE_STATE Who,
  136. IN ULONG DataSize,
  137. IN PIRP Irp,
  138. IN PWORK_CONTEXT WorkContext
  139. )
  140. /*++
  141. Routine Description:
  142. This function adds a new data entry to the of the data queue.
  143. Entries are always appended to the queue. If necessary this
  144. function will allocate a data entry buffer, or use space in
  145. the IRP.
  146. The different actions we are perform are based on the type and who
  147. parameters and quota requirements.
  148. Who == ReadEntries
  149. +----------+ - Allocate Data Entry from IRP
  150. |Irp | +-----------+
  151. |BufferedIo|<----|Buffered | - Allocate New System buffer
  152. |DeallBu...| |EitherQuota|
  153. +----------+ +-----------+ - Reference and modify Irp to
  154. | | | do Buffered I/O, Deallocate
  155. v | v buffer, and have I/O completion
  156. +------+ +------>+------+ copy the buffer (Input operation)
  157. |User | |System|
  158. |Buffer| |Buffer|
  159. +------+ +------+
  160. Who == WriteEntries && Quota Available
  161. +----------+ - Allocate Data Entry from Quota
  162. |Irp | +-----------+
  163. | | |Buffered | - Allocate New System buffer
  164. | | |Quota |
  165. +----------+ +-----------+ - Copy data from User buffer to
  166. | | system buffer
  167. v v
  168. +------+ +------+ - Complete IRP
  169. |User |..copy..>|System|
  170. |Buffer| |Buffer|
  171. +------+ +------+
  172. Who == WriteEntries && Quota Not Available
  173. +----------+ - Allocate Data Entry from Irp
  174. |Irp | +-----------+
  175. |BufferedIo|<----|Buffered | - Allocate New System buffer
  176. |DeallBuff | |UserQuota |
  177. +----------+ +-----------+ - Reference and modify Irp to use
  178. | | | the new system buffer, do Buffered
  179. v | v I/O, and Deallocate buffer
  180. +------+ +------>+------+
  181. |User | |System| - Copy data from User buffer to
  182. |Buffer|..copy..>|Buffer| system buffer
  183. +------+ +------+
  184. Arguments:
  185. DataQueue - Supplies the Data queue being modified.
  186. Who - Indicates if this is the reader or writer that is adding to the
  187. mailslot.
  188. DataSize - Indicates the size of the data buffer needed to represent
  189. this entry.
  190. Irp - Supplies a pointer to the IRP responsible for this entry.
  191. Return Value:
  192. PDATA_ENTRY - Returns a pointer to the newly added data entry.
  193. --*/
  194. {
  195. PDATA_ENTRY dataEntry;
  196. PLIST_ENTRY previousEntry;
  197. PFCB fcb;
  198. ULONG TotalSize;
  199. NTSTATUS status;
  200. PAGED_CODE( );
  201. DebugTrace(+1, Dbg, "MsAddDataQueueEntry, DataQueue = %08lx\n", (ULONG)DataQueue);
  202. ASSERT( DataQueue->QueueState != -1 );
  203. Irp->IoStatus.Information = 0;
  204. if (Who == ReadEntries) {
  205. //
  206. // Allocate a data entry from the IRP, and allocate a new
  207. // system buffer.
  208. //
  209. dataEntry = (PDATA_ENTRY)IoGetNextIrpStackLocation( Irp );
  210. dataEntry->DataPointer = NULL;
  211. dataEntry->Irp = Irp;
  212. dataEntry->DataSize = DataSize;
  213. dataEntry->TimeoutWorkContext = WorkContext;
  214. //
  215. // Check to see if the mailslot has enough quota left to
  216. // allocate the system buffer.
  217. //
  218. if ((DataQueue->Quota - DataQueue->QuotaUsed) >= DataSize) {
  219. //
  220. // Use the mailslot quota to allocate pool for the request.
  221. //
  222. if (DataSize) {
  223. dataEntry->DataPointer = MsAllocatePagedPoolCold( DataSize,
  224. 'rFsM' );
  225. if (dataEntry->DataPointer == NULL) {
  226. return STATUS_INSUFFICIENT_RESOURCES;
  227. }
  228. }
  229. DataQueue->QuotaUsed += DataSize;
  230. dataEntry->From = MailslotQuota;
  231. } else {
  232. //
  233. // Use the caller's quota to allocate pool for the request.
  234. //
  235. if (DataSize) {
  236. dataEntry->DataPointer = MsAllocatePagedPoolWithQuotaCold( DataSize,
  237. 'rFsM' );
  238. if (dataEntry->DataPointer == NULL) {
  239. return STATUS_INSUFFICIENT_RESOURCES;
  240. }
  241. }
  242. dataEntry->From = UserQuota;
  243. }
  244. //
  245. // Modify the IRP to be buffered I/O, deallocate the buffer, copy
  246. // the buffer on completion, and to reference the new system
  247. // buffer.
  248. //
  249. Irp->Flags |= IRP_BUFFERED_IO | IRP_INPUT_OPERATION;
  250. Irp->AssociatedIrp.SystemBuffer = dataEntry->DataPointer;
  251. if (Irp->AssociatedIrp.SystemBuffer) {
  252. Irp->Flags |= IRP_DEALLOCATE_BUFFER;
  253. }
  254. Irp->IoStatus.Pointer = DataQueue;
  255. status = STATUS_PENDING;
  256. } else {
  257. //
  258. // This is a writer entry.
  259. //
  260. //
  261. // If there is enough quota left in the mailslot then we will
  262. // allocate the data entry and data buffer from the mailslot
  263. // quota.
  264. //
  265. TotalSize = sizeof(DATA_ENTRY) + DataSize;
  266. if (TotalSize < sizeof(DATA_ENTRY)) {
  267. return STATUS_INVALID_PARAMETER;
  268. }
  269. if ((DataQueue->Quota - DataQueue->QuotaUsed) >= TotalSize) {
  270. //
  271. // Allocate the data buffer using the mailslot quota.
  272. //
  273. dataEntry = MsAllocatePagedPool( TotalSize, 'dFsM' );
  274. if (dataEntry == NULL) {
  275. return STATUS_INSUFFICIENT_RESOURCES;
  276. }
  277. dataEntry->DataPointer = (PVOID) (dataEntry + 1);
  278. DataQueue->QuotaUsed += TotalSize;
  279. dataEntry->From = MailslotQuota;
  280. } else {
  281. //
  282. // There isn't enough quota in the mailslot. Use the
  283. // caller's quota.
  284. //
  285. dataEntry = MsAllocatePagedPoolWithQuota( TotalSize, 'dFsM' );
  286. if (dataEntry == NULL) {
  287. return STATUS_INSUFFICIENT_RESOURCES;
  288. }
  289. dataEntry->DataPointer = (PVOID) (dataEntry + 1);
  290. dataEntry->From = UserQuota;
  291. }
  292. dataEntry->Irp = NULL;
  293. dataEntry->DataSize = DataSize;
  294. dataEntry->TimeoutWorkContext = NULL;
  295. //
  296. // Copy the user buffer to the new system buffer, update the FCB
  297. // timestamps and complete the IRP.
  298. //
  299. try {
  300. RtlCopyMemory (dataEntry->DataPointer, Irp->UserBuffer, DataSize);
  301. } except( EXCEPTION_EXECUTE_HANDLER ) {
  302. //
  303. // Only need to free the writers case as the readers get the buffer freed on I/O
  304. // completion.
  305. //
  306. if (Who == WriteEntries) {
  307. MsFreePool ( dataEntry );
  308. }
  309. return GetExceptionCode (); // Watch out. Could be a guard page violation thats a warning!
  310. }
  311. fcb = CONTAINING_RECORD( DataQueue, FCB, DataQueue );
  312. KeQuerySystemTime( &fcb->Specific.Fcb.LastModificationTime );
  313. Irp->IoStatus.Information = DataSize;
  314. status = STATUS_SUCCESS;
  315. } // else (writer entry)
  316. //
  317. // Now data entry points to a new data entry to add to the data queue
  318. // Check if the queue is empty otherwise we will add this entry to
  319. // the end of the queue.
  320. //
  321. #if DBG
  322. if ( IsListEmpty( &DataQueue->DataEntryList ) ) {
  323. ASSERT( DataQueue->QueueState == Empty );
  324. ASSERT( DataQueue->BytesInQueue == 0);
  325. ASSERT( DataQueue->EntriesInQueue == 0);
  326. } else {
  327. ASSERT( DataQueue->QueueState == Who );
  328. }
  329. #endif
  330. DataQueue->QueueState = Who;
  331. //
  332. // Only cound written bytes and messages in the queue. This makes sense because we return
  333. // this value as the end of file position. GetMailslotInfo needs EntriesInQueue to
  334. // ignore reads.
  335. //
  336. if (Who == WriteEntries) {
  337. DataQueue->BytesInQueue += dataEntry->DataSize;
  338. DataQueue->EntriesInQueue += 1;
  339. }
  340. //
  341. // Insert the new entry at the appropriate place in the data queue.
  342. //
  343. InsertTailList( &DataQueue->DataEntryList, &dataEntry->ListEntry );
  344. WorkContext = dataEntry->TimeoutWorkContext;
  345. if ( WorkContext) {
  346. KeSetTimer( &WorkContext->Timer,
  347. WorkContext->Fcb->Specific.Fcb.ReadTimeout,
  348. &WorkContext->Dpc );
  349. }
  350. if (Who == ReadEntries) {
  351. MsSetCancelRoutine( Irp ); // this fakes a call to cancel if we are already canceled
  352. }
  353. //
  354. // Return to the caller.
  355. //
  356. DumpDataQueue( "After AddDataQueueEntry\n", DataQueue );
  357. DebugTrace(-1, Dbg, "MsAddDataQueueEntry -> %08lx\n", (ULONG)dataEntry);
  358. return status;
  359. }
  360. PIRP
  361. MsRemoveDataQueueEntry (
  362. IN PDATA_QUEUE DataQueue,
  363. IN PDATA_ENTRY DataEntry
  364. )
  365. /*++
  366. Routine Description:
  367. This routine removes the specified data entry from the indicated
  368. data queue, and possibly returns the IRP associated with the entry if
  369. it wasn't already completed.
  370. If the data entry we are removing indicates buffered I/O then we also
  371. need to deallocate the data buffer besides the data entry but only
  372. if the IRP is null. Note that the data entry might be stored in an IRP.
  373. If it is then we are going to return the IRP it is stored in.
  374. Arguments:
  375. DataEntry - Supplies a pointer to the data entry to remove.
  376. Return Value:
  377. PIRP - Possibly returns a pointer to an IRP.
  378. --*/
  379. {
  380. FROM from;
  381. PIRP irp;
  382. ULONG dataSize;
  383. PVOID dataPointer;
  384. PAGED_CODE( );
  385. DebugTrace(+1, Dbg, "MsRemoveDataQueueEntry, DataEntry = %08lx\n", (ULONG)DataEntry);
  386. DebugTrace( 0, Dbg, "DataQueue = %08lx\n", (ULONG)DataQueue);
  387. //
  388. // Remove the data entry from the queue and update the count of
  389. // data entries in the queue.
  390. //
  391. RemoveEntryList( &DataEntry->ListEntry );
  392. //
  393. // If the queue is now empty then we need to fix the queue
  394. // state.
  395. //
  396. if (IsListEmpty( &DataQueue->DataEntryList ) ) {
  397. DataQueue->QueueState = Empty;
  398. }
  399. //
  400. // Capture some of the fields from the data entry to make our
  401. // other references a little easier.
  402. //
  403. from = DataEntry->From;
  404. dataSize = DataEntry->DataSize;
  405. if (from == MailslotQuota) {
  406. DataQueue->QuotaUsed -= dataSize;
  407. }
  408. //
  409. // Get the IRP for this block if there is one
  410. //
  411. irp = DataEntry->Irp;
  412. if (irp) {
  413. //
  414. // Cancel the timer associated with this if there is one
  415. //
  416. MsCancelTimer (DataEntry);
  417. irp = MsResetCancelRoutine( irp );
  418. if ( irp == NULL ) {
  419. //
  420. // cancel is active. Let it know that we already did partial cleanup.
  421. // It just has to complete the IRP.
  422. //
  423. DataEntry->ListEntry.Flink = NULL;
  424. }
  425. } else {
  426. DataQueue->BytesInQueue -= DataEntry->DataSize;
  427. //
  428. // Free the data entry for a write request. This is part of the IRP for a read request.
  429. //
  430. ExFreePool( DataEntry );
  431. if (from == MailslotQuota) {
  432. DataQueue->QuotaUsed -= sizeof(DATA_ENTRY);
  433. }
  434. DataQueue->EntriesInQueue--;
  435. #if DBG
  436. if (DataQueue->EntriesInQueue == 0) {
  437. ASSERT (DataQueue->QueueState == Empty);
  438. ASSERT (DataQueue->BytesInQueue == 0);
  439. ASSERT (IsListEmpty( &DataQueue->DataEntryList ));
  440. ASSERT (DataQueue->QuotaUsed == 0);
  441. }
  442. #endif
  443. }
  444. //
  445. // Return to the caller.
  446. //
  447. DumpDataQueue( "After RemoveDataQueueEntry\n", DataQueue );
  448. DebugTrace(-1, Dbg, "MsRemoveDataQueueEntry -> %08lx\n", (ULONG)irp);
  449. return irp;
  450. }
  451. VOID
  452. MsRemoveDataQueueIrp (
  453. IN PIRP Irp,
  454. IN PDATA_QUEUE DataQueue
  455. )
  456. /*++
  457. Routine Description:
  458. This routine removes an IRP from its data queue.
  459. Requirements:
  460. The FCB for this data queue MUST be exclusively locked.
  461. Arguments:
  462. Irp - Supplies the Irp being removed.
  463. DataQueue - A pointer to the data queue structure where we expect
  464. to find the IRP.
  465. Return Value:
  466. Returns whether or not we actually dequeued the IRP.
  467. --*/
  468. {
  469. PDATA_ENTRY dataEntry;
  470. PLIST_ENTRY listEntry, nextListEntry;
  471. PWORK_CONTEXT workContext;
  472. PKTIMER timer;
  473. BOOLEAN foundIrp = FALSE;
  474. dataEntry = (PDATA_ENTRY)IoGetNextIrpStackLocation( Irp );
  475. //
  476. // This is the cancel path. If a completion path has already removed this IRP then return now.
  477. // The timer will have been canceled, counts adjusted etc.
  478. //
  479. if (dataEntry->ListEntry.Flink == NULL) {
  480. return;
  481. }
  482. //
  483. // remove this entry from the list.
  484. //
  485. RemoveEntryList (&dataEntry->ListEntry);
  486. MsCancelTimer (dataEntry);
  487. //
  488. // If the queue is now empty then we need to fix the queue
  489. // state.
  490. //
  491. //
  492. // Check if we need to return mailslot quota. The DATA_ENTRY was part of the IRP so we didn't
  493. // get charged for that
  494. //
  495. if ( dataEntry->From == MailslotQuota ) {
  496. DataQueue->QuotaUsed -= dataEntry->DataSize;
  497. }
  498. if (IsListEmpty( &DataQueue->DataEntryList ) ) {
  499. DataQueue->QueueState = Empty;
  500. ASSERT (DataQueue->BytesInQueue == 0);
  501. ASSERT (DataQueue->QuotaUsed == 0);
  502. ASSERT (DataQueue->EntriesInQueue == 0);
  503. }
  504. //
  505. // And return to our caller
  506. //
  507. return;
  508. } // MsRemoveDataQueueIrp
  509. VOID
  510. MsCancelDataQueueIrp (
  511. IN PDEVICE_OBJECT DeviceObject,
  512. IN PIRP Irp
  513. )
  514. /*++
  515. Routine Description:
  516. This routine implements the cancel function for an IRP saved in a
  517. data queue
  518. Arguments:
  519. DeviceObject - Device object associated with IRP or NULL if called directly by this driver
  520. Irp - Supplies the Irp being cancelled.
  521. Return Value:
  522. None.
  523. --*/
  524. {
  525. PFCB fcb;
  526. PDATA_QUEUE dataQueue;
  527. PIO_STACK_LOCATION irpSp;
  528. PFILE_OBJECT fileObject;
  529. //
  530. // This isn't strictly correct. IoCancelIrp can be called at Irql <= DISPATCH_LEVEL but
  531. // this code is assuming that the IRQL of the caller is <= APC_LEVEL.
  532. // If we are called inline we don't hold the cancel spinlock and we already own the FCB lock.
  533. //
  534. if (DeviceObject != NULL) {
  535. IoReleaseCancelSpinLock( Irp->CancelIrql );
  536. }
  537. irpSp = IoGetCurrentIrpStackLocation(Irp);
  538. fileObject = irpSp->FileObject;
  539. dataQueue = (PDATA_QUEUE)Irp->IoStatus.Pointer;
  540. fcb = CONTAINING_RECORD( dataQueue, FCB, DataQueue );
  541. //
  542. // Get exclusive access to the mailslot FCB so we can now do our work.
  543. //
  544. if (DeviceObject != NULL) {
  545. FsRtlEnterFileSystem ();
  546. MsAcquireExclusiveFcb( fcb );
  547. }
  548. MsRemoveDataQueueIrp( Irp, dataQueue );
  549. if (DeviceObject != NULL) {
  550. MsReleaseFcb( fcb );
  551. FsRtlExitFileSystem ();
  552. }
  553. MsCompleteRequest( Irp, STATUS_CANCELLED );
  554. //
  555. // And return to our caller
  556. //
  557. return;
  558. } // MsCancelDataQueueIrp
  559. PIRP
  560. MsResetCancelRoutine(
  561. IN PIRP Irp
  562. )
  563. /*++
  564. Routine Description:
  565. Stub to null out the cancel routine.
  566. Arguments:
  567. Irp - Supplies the Irp whose cancel routine is to be nulled out.
  568. Return Value:
  569. None.
  570. --*/
  571. {
  572. if ( IoSetCancelRoutine( Irp, NULL ) != NULL ) {
  573. return Irp;
  574. } else {
  575. return NULL;
  576. }
  577. } // MsResetCancelRoutine
  578. VOID
  579. MsSetCancelRoutine(
  580. IN PIRP Irp
  581. )
  582. /*++
  583. Routine Description:
  584. Stub to set the cancel routine. If the irp has already been cancelled,
  585. the cancel routine is called.
  586. Arguments:
  587. Irp - Supplies the Irp whose cancel routine is to be set.
  588. Return Value:
  589. None.
  590. --*/
  591. {
  592. IoMarkIrpPending( Irp ); // top level always returns STATUS_PENDING if we get this far
  593. IoSetCancelRoutine( Irp, MsCancelDataQueueIrp );
  594. if ( Irp->Cancel && IoSetCancelRoutine( Irp, NULL ) != NULL ) {
  595. //
  596. // The IRP was canceled before we put our routine on. Fake a cancel call
  597. //
  598. MsCancelDataQueueIrp (NULL, Irp);
  599. }
  600. return;
  601. } // MsSetCancelRoutine
  602.