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.

621 lines
13 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. WaitSup.c
  5. Abstract:
  6. This module implements the Wait for Named Pipe support routines.
  7. Author:
  8. Gary Kimura [GaryKi] 30-Aug-1990
  9. Revision History:
  10. --*/
  11. #include "NpProcs.h"
  12. //
  13. // The debug trace level
  14. //
  15. #define Dbg (DEBUG_TRACE_WAITSUP)
  16. #ifdef ALLOC_PRAGMA
  17. #pragma alloc_text(PAGE, NpInitializeWaitQueue)
  18. #pragma alloc_text(PAGE, NpUninitializeWaitQueue)
  19. #endif
  20. //
  21. // Local procedures and structures
  22. //
  23. typedef struct _WAIT_CONTEXT {
  24. PIRP Irp;
  25. KDPC Dpc;
  26. KTIMER Timer;
  27. PWAIT_QUEUE WaitQueue;
  28. UNICODE_STRING TranslatedString;
  29. PFILE_OBJECT FileObject;
  30. } WAIT_CONTEXT;
  31. typedef WAIT_CONTEXT *PWAIT_CONTEXT;
  32. VOID
  33. NpInitializeWaitQueue (
  34. IN PWAIT_QUEUE WaitQueue
  35. )
  36. /*++
  37. Routine Description:
  38. This routine initializes the wait for named pipe queue.
  39. Arguments:
  40. WaitQueue - Supplies a pointer to the list head being initialized
  41. Return Value:
  42. None.
  43. --*/
  44. {
  45. PAGED_CODE();
  46. DebugTrace(+1, Dbg, "NpInitializeWaitQueue, WaitQueue = %08lx\n", WaitQueue);
  47. //
  48. // Initialize the List head
  49. //
  50. InitializeListHead( &WaitQueue->Queue );
  51. //
  52. // Initialize the Wait Queue's spinlock
  53. //
  54. KeInitializeSpinLock( &WaitQueue->SpinLock );
  55. //
  56. // and return to our caller
  57. //
  58. DebugTrace(-1, Dbg, "NpInitializeWaitQueue -> VOID\n", 0);
  59. return;
  60. }
  61. VOID
  62. NpUninitializeWaitQueue (
  63. IN PWAIT_QUEUE WaitQueue
  64. )
  65. /*++
  66. Routine Description:
  67. This routine uninitializes the wait for named pipe queue.
  68. Arguments:
  69. WaitQueue - Supplies a pointer to the list head being uninitialized
  70. Return Value:
  71. None.
  72. --*/
  73. {
  74. PAGED_CODE();
  75. DebugTrace(+1, Dbg, "NpInitializeWaitQueue, WaitQueue = %08lx\n", WaitQueue);
  76. //
  77. // And return to our caller
  78. //
  79. DebugTrace(-1, Dbg, "NpInitializeWaitQueue -> VOID\n", 0);
  80. return;
  81. }
  82. NTSTATUS
  83. NpAddWaiter (
  84. IN PWAIT_QUEUE WaitQueue,
  85. IN LARGE_INTEGER DefaultTimeOut,
  86. IN PIRP Irp,
  87. IN PUNICODE_STRING TranslatedString
  88. )
  89. /*++
  90. Routine Description:
  91. This routine adds a new "wait for named pipe" IRP to the wait queue.
  92. After calling this function the caller nolonger can access the IRP
  93. Arguments:
  94. WaitQueue - Supplies the wait queue being used
  95. DefaultTimeOut - Supplies the default time out to use if one is
  96. not supplied in the Irp
  97. Irp - Supplies a pointer to the wait Irp
  98. TranslatedString - If not NULL points to the translated string
  99. Return Value:
  100. None.
  101. --*/
  102. {
  103. KIRQL OldIrql;
  104. PWAIT_CONTEXT Context;
  105. PFILE_PIPE_WAIT_FOR_BUFFER WaitForBuffer;
  106. LARGE_INTEGER Timeout;
  107. ULONG i;
  108. NTSTATUS status;
  109. PIO_STACK_LOCATION IrpSp;
  110. DebugTrace(+1, Dbg, "NpAddWaiter, WaitQueue = %08lx\n", WaitQueue);
  111. IrpSp = IoGetCurrentIrpStackLocation (Irp);
  112. //
  113. // Allocate a dpc and timer structure and initialize them
  114. //
  115. Context = NpAllocateNonPagedPoolWithQuota( sizeof(WAIT_CONTEXT), 'wFpN' );
  116. if (Context == NULL) {
  117. return STATUS_INSUFFICIENT_RESOURCES;
  118. }
  119. KeInitializeDpc( &Context->Dpc, NpTimerDispatch, Context );
  120. KeInitializeTimer( &Context->Timer );
  121. if (TranslatedString) {
  122. Context->TranslatedString = (*TranslatedString);
  123. } else {
  124. Context->TranslatedString.Length = 0;
  125. Context->TranslatedString.Buffer = NULL;
  126. }
  127. Context->WaitQueue = WaitQueue;
  128. Context->Irp = Irp;
  129. //
  130. // Figure out our timeout value
  131. //
  132. WaitForBuffer = (PFILE_PIPE_WAIT_FOR_BUFFER)Irp->AssociatedIrp.SystemBuffer;
  133. if (WaitForBuffer->TimeoutSpecified) {
  134. Timeout = WaitForBuffer->Timeout;
  135. } else {
  136. Timeout = DefaultTimeOut;
  137. }
  138. //
  139. // Upcase the name of the pipe we are waiting for
  140. //
  141. for (i = 0; i < WaitForBuffer->NameLength/sizeof(WCHAR); i += 1) {
  142. WaitForBuffer->Name[i] = RtlUpcaseUnicodeChar(WaitForBuffer->Name[i]);
  143. }
  144. NpIrpWaitQueue(Irp) = WaitQueue;
  145. NpIrpWaitContext(Irp) = Context;
  146. //
  147. // Acquire the spinlock
  148. //
  149. KeAcquireSpinLock( &WaitQueue->SpinLock, &OldIrql );
  150. //
  151. // Now set the cancel routine for the irp and check if it has been cancelled.
  152. //
  153. IoSetCancelRoutine( Irp, NpCancelWaitQueueIrp );
  154. if (Irp->Cancel && IoSetCancelRoutine( Irp, NULL ) != NULL) {
  155. status = STATUS_CANCELLED;
  156. } else {
  157. //
  158. // Now insert this new entry into the Wait Queue
  159. //
  160. InsertTailList( &WaitQueue->Queue, &Irp->Tail.Overlay.ListEntry );
  161. IoMarkIrpPending (Irp);
  162. //
  163. // The DPC routine may run without an IRP if it gets completed before it runs. To keep the WaitQueue
  164. // valid we need a file object reference. This is an unload issue becuase the wait queue is in the VCB.
  165. //
  166. Context->FileObject = IrpSp->FileObject;
  167. ObReferenceObject (IrpSp->FileObject);
  168. //
  169. // And set the timer to go off
  170. //
  171. (VOID)KeSetTimer( &Context->Timer, Timeout, &Context->Dpc );
  172. Context = NULL;
  173. status = STATUS_PENDING;
  174. }
  175. //
  176. // Release the spinlock
  177. //
  178. KeReleaseSpinLock( &WaitQueue->SpinLock, OldIrql );
  179. if (Context != NULL) {
  180. NpFreePool (Context);
  181. }
  182. //
  183. // And now return to our caller
  184. //
  185. DebugTrace(-1, Dbg, "NpAddWaiter -> VOID\n", 0);
  186. return status;
  187. }
  188. NTSTATUS
  189. NpCancelWaiter (
  190. IN PWAIT_QUEUE WaitQueue,
  191. IN PUNICODE_STRING NameOfPipe,
  192. IN NTSTATUS Completionstatus,
  193. IN PLIST_ENTRY DeferredList
  194. )
  195. /*++
  196. Routine Description:
  197. This procedure cancels all waiters that are waiting for the named
  198. pipe to reach the listening state. The corresponding IRPs are completed
  199. with Completionstatus.
  200. Arguments:
  201. WaitQueue - Supplies the wait queue being modified
  202. NameOfPipe - Supplies the name of the named pipe (device relative)
  203. that has just reached the listening state.
  204. CompletionStatus - Status to complete IRPs with
  205. DeferredList - List or IRPs to complete once we drop locks
  206. Return Value:
  207. None.
  208. --*/
  209. {
  210. KIRQL OldIrql;
  211. PLIST_ENTRY Links;
  212. PIRP Irp;
  213. PFILE_PIPE_WAIT_FOR_BUFFER WaitForBuffer;
  214. PWAIT_CONTEXT Context, ContextList= NULL;
  215. ULONG i;
  216. BOOLEAN SuccessfullMatch = FALSE;
  217. UNICODE_STRING NonPagedNameOfPipe;
  218. DebugTrace(+1, Dbg, "NpCancelWaiter, WaitQueue = %08lx\n", WaitQueue);
  219. //
  220. // Capture the name of pipe before we grab the spinlock, and upcase it
  221. //
  222. NonPagedNameOfPipe.Buffer = NpAllocateNonPagedPool( NameOfPipe->Length, 'tFpN' );
  223. if (NonPagedNameOfPipe.Buffer == NULL) {
  224. return STATUS_INSUFFICIENT_RESOURCES;
  225. }
  226. NonPagedNameOfPipe.Length = 0;
  227. NonPagedNameOfPipe.MaximumLength = NameOfPipe->Length;
  228. (VOID) RtlUpcaseUnicodeString( &NonPagedNameOfPipe, NameOfPipe, FALSE );
  229. //
  230. // Acquire the spinlock
  231. //
  232. KeAcquireSpinLock( &WaitQueue->SpinLock, &OldIrql );
  233. //
  234. // For each waiting irp check if the name matches
  235. //
  236. for (Links = WaitQueue->Queue.Flink;
  237. Links != &WaitQueue->Queue;
  238. Links = Links->Flink) {
  239. Irp = CONTAINING_RECORD( Links, IRP, Tail.Overlay.ListEntry );
  240. WaitForBuffer = (PFILE_PIPE_WAIT_FOR_BUFFER)Irp->AssociatedIrp.SystemBuffer;
  241. Context = NpIrpWaitContext(Irp);
  242. //
  243. // Check if this Irp matches the one we've been waiting for
  244. // First check the lengths for equality, and then compare
  245. // the strings. They match if we exit the inner loop with
  246. // i >= name length.
  247. //
  248. SuccessfullMatch = FALSE;
  249. if (Context->TranslatedString.Length ) {
  250. if (NonPagedNameOfPipe.Length == Context->TranslatedString.Length) {
  251. if (RtlEqualMemory(Context->TranslatedString.Buffer, NonPagedNameOfPipe.Buffer, NonPagedNameOfPipe.Length)) {
  252. SuccessfullMatch = TRUE;
  253. }
  254. }
  255. } else {
  256. if (((USHORT)(WaitForBuffer->NameLength + sizeof(WCHAR))) == NonPagedNameOfPipe.Length) {
  257. for (i = 0; i < WaitForBuffer->NameLength/sizeof(WCHAR); i += 1) {
  258. if (WaitForBuffer->Name[i] != NonPagedNameOfPipe.Buffer[i+1]) {
  259. break;
  260. }
  261. }
  262. if (i >= WaitForBuffer->NameLength/sizeof(WCHAR)) {
  263. SuccessfullMatch = TRUE;
  264. }
  265. }
  266. }
  267. if (SuccessfullMatch) {
  268. Links = Links->Blink;
  269. RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
  270. //
  271. // Attempt to stop the timer. If its already running then it must be stalled before obtaining
  272. // this spinlock or it would have removed this item from the list. Break the link between the timer
  273. // context and the IRP in this case and let it run on.
  274. //
  275. if (KeCancelTimer( &Context->Timer )) {
  276. //
  277. // Time got stopped. The context gets freed below after we drop the lock.
  278. //
  279. Context->WaitQueue = (PWAIT_QUEUE) ContextList;
  280. ContextList = Context;
  281. } else {
  282. //
  283. // Break the link between the timer and the IRP
  284. //
  285. Context->Irp = NULL;
  286. NpIrpWaitContext(Irp) = NULL;
  287. }
  288. //
  289. // Remove cancelation. If its already running then let it complete the IRP.
  290. //
  291. if (IoSetCancelRoutine( Irp, NULL ) != NULL) {
  292. Irp->IoStatus.Information = 0;
  293. NpDeferredCompleteRequest (Irp, Completionstatus, DeferredList);
  294. } else {
  295. //
  296. // Cancel is already running. Let it complete this IRP but let it know its orphaned.
  297. //
  298. NpIrpWaitContext(Irp) = NULL;
  299. }
  300. }
  301. }
  302. //
  303. // Release the spinlock
  304. //
  305. KeReleaseSpinLock( &WaitQueue->SpinLock, OldIrql );
  306. NpFreePool (NonPagedNameOfPipe.Buffer);
  307. while (ContextList != NULL) {
  308. Context = ContextList;
  309. ContextList = (PWAIT_CONTEXT) Context->WaitQueue;
  310. ObDereferenceObject (Context->FileObject);
  311. NpFreePool( Context );
  312. }
  313. DebugTrace(-1, Dbg, "NpCancelWaiter -> VOID\n", 0);
  314. //
  315. // And now return to our caller
  316. //
  317. return STATUS_SUCCESS;
  318. }
  319. //
  320. // Local support routine
  321. //
  322. VOID
  323. NpTimerDispatch(
  324. IN PKDPC Dpc,
  325. IN PVOID Contxt,
  326. IN PVOID SystemArgument1,
  327. IN PVOID SystemArgument2
  328. )
  329. /*++
  330. Routine Description:
  331. This routine is called whenever a timer on a wait queue Irp goes off
  332. Arguments:
  333. Dpc - Ignored
  334. Contxt - Supplies a pointer to the context whose timer went off
  335. SystemArgument1 - Ignored
  336. SystemArgument2 - Ignored
  337. Return Value:
  338. none.
  339. --*/
  340. {
  341. PIRP Irp;
  342. KIRQL OldIrql;
  343. PLIST_ENTRY Links;
  344. PWAIT_CONTEXT Context;
  345. PWAIT_QUEUE WaitQueue;
  346. UNREFERENCED_PARAMETER( Dpc );
  347. UNREFERENCED_PARAMETER( SystemArgument1 );
  348. UNREFERENCED_PARAMETER( SystemArgument2 );
  349. Context = (PWAIT_CONTEXT)Contxt;
  350. WaitQueue = Context->WaitQueue;
  351. KeAcquireSpinLock( &WaitQueue->SpinLock, &OldIrql );
  352. Irp = Context->Irp;
  353. if (Irp != NULL) {
  354. RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
  355. if (IoSetCancelRoutine (Irp, NULL) == NULL) {
  356. //
  357. // Cancel started running. Let it complete the IRP but show its orphaned
  358. //
  359. NpIrpWaitContext(Irp) = NULL;
  360. Irp = NULL;
  361. }
  362. }
  363. KeReleaseSpinLock( &WaitQueue->SpinLock, OldIrql );
  364. if (Irp != NULL) {
  365. NpCompleteRequest( Irp, STATUS_IO_TIMEOUT );
  366. }
  367. //
  368. // Remove the file reference now we have finished with the wait queue.
  369. //
  370. ObDereferenceObject (Context->FileObject);
  371. //
  372. // Deallocate the context
  373. //
  374. NpFreePool (Context);
  375. //
  376. // And now return to our caller
  377. //
  378. return;
  379. }
  380. //
  381. // Local Support routine
  382. //
  383. VOID
  384. NpCancelWaitQueueIrp(
  385. IN PDEVICE_OBJECT DeviceObject,
  386. IN PIRP Irp
  387. )
  388. /*++
  389. Routine Description:
  390. This routine is called to cancel a wait queue irp
  391. Arguments:
  392. DeviceObject - Ignored
  393. Irp - Supplies the Irp being cancelled. The Iosb.Status field in the irp
  394. points to the wait queue
  395. Return Value:
  396. none.
  397. --*/
  398. {
  399. PWAIT_QUEUE WaitQueue;
  400. KIRQL OldIrql;
  401. PLIST_ENTRY Links;
  402. PWAIT_CONTEXT Context;
  403. UNREFERENCED_PARAMETER( DeviceObject );
  404. IoReleaseCancelSpinLock( Irp->CancelIrql );
  405. //
  406. // The status field is used to store a pointer to the wait queue
  407. // containing this irp
  408. //
  409. WaitQueue = NpIrpWaitQueue(Irp);
  410. //
  411. // Get the spinlock proctecting the wait queue
  412. //
  413. KeAcquireSpinLock( &WaitQueue->SpinLock, &OldIrql );
  414. Context = NpIrpWaitContext(Irp);
  415. if (Context != NULL) {
  416. RemoveEntryList (&Irp->Tail.Overlay.ListEntry);
  417. if (!KeCancelTimer( &Context->Timer )) {
  418. //
  419. // Timer is already running. Break the link between the timer and the IRP as this thread is going to complete it.
  420. //
  421. Context->Irp = NULL;
  422. Context = NULL;
  423. }
  424. }
  425. KeReleaseSpinLock( &WaitQueue->SpinLock, OldIrql );
  426. if (Context) {
  427. ObDereferenceObject (Context->FileObject);
  428. NpFreePool (Context);
  429. }
  430. Irp->IoStatus.Information = 0;
  431. NpCompleteRequest( Irp, STATUS_CANCELLED );
  432. //
  433. // And return to our caller
  434. //
  435. return;
  436. }