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.

733 lines
17 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. lpcqueue.c
  5. Abstract:
  6. Local Inter-Process Communication (LPC) queue support routines.
  7. Author:
  8. Steve Wood (stevewo) 15-May-1989
  9. Revision History:
  10. --*/
  11. #include "lpcp.h"
  12. #ifdef ALLOC_PRAGMA
  13. #pragma alloc_text(INIT,LpcpInitializePortZone)
  14. #pragma alloc_text(PAGE,LpcpInitializePortQueue)
  15. #pragma alloc_text(PAGE,LpcpDestroyPortQueue)
  16. #pragma alloc_text(PAGE,LpcpExtendPortZone)
  17. #pragma alloc_text(PAGE,LpcpFreeToPortZone)
  18. #pragma alloc_text(PAGE,LpcpSaveDataInfoMessage)
  19. #pragma alloc_text(PAGE,LpcpFreeDataInfoMessage)
  20. #pragma alloc_text(PAGE,LpcpFindDataInfoMessage)
  21. #pragma alloc_text(PAGE,LpcDisconnectPort)
  22. #endif
  23. #ifdef ALLOC_DATA_PRAGMA
  24. #pragma data_seg("PAGEDATA")
  25. #endif // ALLOC_DATA_PRAGMA
  26. ULONG LpcpTotalNumberOfMessages = 0;
  27. ULONG LpcpMaxMessageSize = 0;
  28. PAGED_LOOKASIDE_LIST LpcpMessagesLookaside;
  29. #ifdef ALLOC_DATA_PRAGMA
  30. #pragma data_seg()
  31. #endif // ALLOC_DATA_PRAGMA
  32. NTSTATUS
  33. LpcpInitializePortQueue (
  34. IN PLPCP_PORT_OBJECT Port
  35. )
  36. /*++
  37. Routine Description:
  38. This routine is used to initialize the message queue for a port object.
  39. Arguments:
  40. Port - Supplies the port object being initialized
  41. Return Value:
  42. NTSTATUS - An appropriate status value
  43. --*/
  44. {
  45. PLPCP_NONPAGED_PORT_QUEUE NonPagedPortQueue;
  46. PAGED_CODE();
  47. //
  48. // Allocate space for the port queue
  49. //
  50. NonPagedPortQueue = ExAllocatePoolWithTag( NonPagedPool,
  51. sizeof(LPCP_NONPAGED_PORT_QUEUE),
  52. 'troP' );
  53. if (NonPagedPortQueue == NULL) {
  54. return STATUS_INSUFFICIENT_RESOURCES;
  55. }
  56. //
  57. // Initialize the fields in the non paged port queue
  58. //
  59. KeInitializeSemaphore( &NonPagedPortQueue->Semaphore, 0, 0x7FFFFFFF );
  60. NonPagedPortQueue->BackPointer = Port;
  61. //
  62. // Have the port msg queue point to the non nonpaged port queue
  63. //
  64. Port->MsgQueue.Semaphore = &NonPagedPortQueue->Semaphore;
  65. //
  66. // Initialize the port msg queue to be empty
  67. //
  68. InitializeListHead( &Port->MsgQueue.ReceiveHead );
  69. //
  70. // And return to our caller
  71. //
  72. return STATUS_SUCCESS;
  73. }
  74. VOID
  75. LpcpDestroyPortQueue (
  76. IN PLPCP_PORT_OBJECT Port,
  77. IN BOOLEAN CleanupAndDestroy
  78. )
  79. /*++
  80. Routine Description:
  81. This routine is used to teardown the message queue of a port object.
  82. After running this message will either be empty (like it was just
  83. initialized) or completely gone (needs to be initialized)
  84. Arguments:
  85. Port - Supplies the port containing the message queue being modified
  86. CleanupAndDestroy - Specifies if the message queue should be set back
  87. to the freshly initialized state (value of FALSE) or completely
  88. torn down (value of TRUE)
  89. Return Value:
  90. None.
  91. --*/
  92. {
  93. PLIST_ENTRY Next, Head;
  94. PETHREAD ThreadWaitingForReply;
  95. PLPCP_MESSAGE Msg;
  96. PLPCP_PORT_OBJECT ConnectionPort = NULL;
  97. PAGED_CODE();
  98. //
  99. // If this port is connected to another port, then disconnect it.
  100. // Protect this with a lock in case the other side is going away
  101. // at the same time.
  102. //
  103. LpcpAcquireLpcpLock();
  104. if ( ((Port->Flags & PORT_TYPE) != UNCONNECTED_COMMUNICATION_PORT)
  105. &&
  106. (Port->ConnectedPort != NULL) ) {
  107. Port->ConnectedPort->ConnectedPort = NULL;
  108. //
  109. // Disconnect the connection port
  110. //
  111. if (Port->ConnectedPort->ConnectionPort) {
  112. ConnectionPort = Port->ConnectedPort->ConnectionPort;
  113. Port->ConnectedPort->ConnectionPort = NULL;
  114. }
  115. }
  116. //
  117. // If connection port, then mark name as deleted
  118. //
  119. if ((Port->Flags & PORT_TYPE) == SERVER_CONNECTION_PORT) {
  120. Port->Flags |= PORT_NAME_DELETED;
  121. }
  122. //
  123. // Walk list of threads waiting for a reply to a message sent to this
  124. // port. Signal each thread's LpcReplySemaphore to wake them up. They
  125. // will notice that there was no reply and return
  126. // STATUS_PORT_DISCONNECTED
  127. //
  128. Head = &Port->LpcReplyChainHead;
  129. Next = Head->Flink;
  130. while ((Next != NULL) && (Next != Head)) {
  131. ThreadWaitingForReply = CONTAINING_RECORD( Next, ETHREAD, LpcReplyChain );
  132. //
  133. // If the thread is exiting, in the location of LpcReplyChain is stored the ExitTime
  134. // We'll stop to search through the list.
  135. if ( ThreadWaitingForReply->LpcExitThreadCalled ) {
  136. break;
  137. }
  138. Next = Next->Flink;
  139. RemoveEntryList( &ThreadWaitingForReply->LpcReplyChain );
  140. InitializeListHead( &ThreadWaitingForReply->LpcReplyChain );
  141. if (!KeReadStateSemaphore( &ThreadWaitingForReply->LpcReplySemaphore )) {
  142. //
  143. // Thread is waiting on a message. Signal the semaphore and free
  144. // the message
  145. //
  146. Msg = LpcpGetThreadMessage(ThreadWaitingForReply);
  147. if ( Msg ) {
  148. //
  149. // If the message is a connection request and has a section object
  150. // attached, then dereference that section object
  151. //
  152. if ((Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) == LPC_CONNECTION_REQUEST) {
  153. PLPCP_CONNECTION_MESSAGE ConnectMsg;
  154. ConnectMsg = (PLPCP_CONNECTION_MESSAGE)(Msg + 1);
  155. if ( ConnectMsg->SectionToMap != NULL ) {
  156. ObDereferenceObject( ConnectMsg->SectionToMap );
  157. }
  158. }
  159. ThreadWaitingForReply->LpcReplyMessage = NULL;
  160. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED );
  161. Next = Port->LpcReplyChainHead.Flink; // Lock has been dropped
  162. }
  163. ThreadWaitingForReply->LpcReplyMessageId = 0;
  164. KeReleaseSemaphore( &ThreadWaitingForReply->LpcReplySemaphore,
  165. 0,
  166. 1L,
  167. FALSE );
  168. }
  169. }
  170. InitializeListHead( &Port->LpcReplyChainHead );
  171. //
  172. // Walk list of messages queued to this port. Remove each message from
  173. // the list and free it.
  174. //
  175. while (Port->MsgQueue.ReceiveHead.Flink && !IsListEmpty (&Port->MsgQueue.ReceiveHead)) {
  176. Msg = CONTAINING_RECORD( Port->MsgQueue.ReceiveHead.Flink, LPCP_MESSAGE, Entry );
  177. RemoveEntryList (&Msg->Entry);
  178. InitializeListHead( &Msg->Entry );
  179. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED );
  180. }
  181. LpcpReleaseLpcpLock();
  182. if ( ConnectionPort ) {
  183. ObDereferenceObject( ConnectionPort );
  184. }
  185. //
  186. // Check if the caller wants it all to go away
  187. //
  188. if ( CleanupAndDestroy ) {
  189. //
  190. // Free semaphore associated with the queue.
  191. //
  192. if (Port->MsgQueue.Semaphore != NULL) {
  193. ExFreePool( CONTAINING_RECORD( Port->MsgQueue.Semaphore,
  194. LPCP_NONPAGED_PORT_QUEUE,
  195. Semaphore ));
  196. }
  197. }
  198. //
  199. // And return to our caller
  200. //
  201. return;
  202. }
  203. NTSTATUS
  204. LpcDisconnectPort (
  205. IN PVOID Port
  206. )
  207. /*++
  208. Routine Description:
  209. This routine is used to disconnect an LPC port so no more messages can be sent and anybody waiting for a message
  210. is woken up with an error.
  211. Arguments:
  212. Port - Supplies the port to be disconnected
  213. Return Value:
  214. NTSTATUS - Status of operation
  215. --*/
  216. {
  217. LpcpDestroyPortQueue (Port, FALSE);
  218. return STATUS_SUCCESS;
  219. }
  220. VOID
  221. LpcpInitializePortZone (
  222. IN ULONG MaxEntrySize
  223. )
  224. {
  225. LpcpMaxMessageSize = MaxEntrySize;
  226. ExInitializePagedLookasideList( &LpcpMessagesLookaside,
  227. NULL,
  228. NULL,
  229. 0,
  230. MaxEntrySize,
  231. 'McpL',
  232. 32
  233. );
  234. }
  235. VOID
  236. FASTCALL
  237. LpcpFreeToPortZone (
  238. IN PLPCP_MESSAGE Msg,
  239. IN ULONG MutexFlags
  240. )
  241. {
  242. PLPCP_CONNECTION_MESSAGE ConnectMsg;
  243. PETHREAD RepliedToThread = NULL;
  244. PLPCP_PORT_OBJECT ClientPort = NULL;
  245. PAGED_CODE();
  246. //
  247. // Acquire the global lock if necessary
  248. //
  249. if ((MutexFlags & LPCP_MUTEX_OWNED) == 0) {
  250. LpcpAcquireLpcpLock();
  251. }
  252. //
  253. // A entry field connects the message to the message queue of the
  254. // owning port object. If not already removed then remove this
  255. // message
  256. //
  257. if (!IsListEmpty( &Msg->Entry )) {
  258. RemoveEntryList( &Msg->Entry );
  259. InitializeListHead( &Msg->Entry );
  260. }
  261. //
  262. // If the replied to thread is not null then we have a reference
  263. // to the thread that we should now remove
  264. //
  265. if (Msg->RepliedToThread != NULL) {
  266. RepliedToThread = Msg->RepliedToThread;
  267. Msg->RepliedToThread = NULL;
  268. }
  269. //
  270. // If the msg was for a connection request then we know that
  271. // right after the lpcp message is a connection message whose
  272. // client port field might need to be dereferenced
  273. //
  274. if ((Msg->Request.u2.s2.Type & ~LPC_KERNELMODE_MESSAGE) == LPC_CONNECTION_REQUEST) {
  275. ConnectMsg = (PLPCP_CONNECTION_MESSAGE)(Msg + 1);
  276. if (ConnectMsg->ClientPort) {
  277. //
  278. // Capture a pointer to the client port then null it
  279. // out so that no one else can use it, then release
  280. // lpcp lock before we dereference the client port
  281. //
  282. ClientPort = ConnectMsg->ClientPort;
  283. ConnectMsg->ClientPort = NULL;
  284. }
  285. }
  286. LpcpReleaseLpcpLock();
  287. if ( ClientPort ) {
  288. ObDereferenceObject( ClientPort );
  289. }
  290. if ( RepliedToThread ) {
  291. ObDereferenceObject( RepliedToThread );
  292. }
  293. ExFreeToPagedLookasideList(&LpcpMessagesLookaside, Msg);
  294. if ((MutexFlags & LPCP_MUTEX_OWNED) &&
  295. ((MutexFlags & LPCP_MUTEX_RELEASE_ON_RETURN) == 0)) {
  296. LpcpAcquireLpcpLock();
  297. }
  298. }
  299. VOID
  300. LpcpSaveDataInfoMessage (
  301. IN PLPCP_PORT_OBJECT Port,
  302. IN PLPCP_MESSAGE Msg,
  303. IN ULONG MutexFlags
  304. )
  305. /*++
  306. Routine Description:
  307. This routine is used in place of freeing a message and instead saves the
  308. message off a separate queue from the port.
  309. Arguments:
  310. Port - Specifies the port object under which to save this message
  311. Msg - Supplies the message being saved
  312. MutexFlags - Supplies whether the mutex is owned.
  313. Return Value:
  314. None.
  315. --*/
  316. {
  317. PAGED_CODE();
  318. //
  319. // Take out the global lock if our caller didn't already.
  320. //
  321. if ((MutexFlags & LPCP_MUTEX_OWNED) == 0) {
  322. LpcpAcquireLpcpLock();
  323. }
  324. //
  325. // Make sure we get to the connection port object of this port
  326. //
  327. if ((Port->Flags & PORT_TYPE) > UNCONNECTED_COMMUNICATION_PORT) {
  328. Port = Port->ConnectionPort;
  329. if (Port == NULL) {
  330. if ((MutexFlags & LPCP_MUTEX_OWNED) == 0) {
  331. LpcpReleaseLpcpLock();
  332. }
  333. return;
  334. }
  335. }
  336. LpcpTrace(( "%s Saving DataInfo Message %lx (%u.%u) Port: %lx\n",
  337. PsGetCurrentProcess()->ImageFileName,
  338. Msg,
  339. Msg->Request.MessageId,
  340. Msg->Request.CallbackId,
  341. Port ));
  342. //
  343. // Enqueue this message onto the data info chain for the port
  344. //
  345. InsertTailList( &Port->LpcDataInfoChainHead, &Msg->Entry );
  346. //
  347. // Free the global lock
  348. //
  349. if ((MutexFlags & LPCP_MUTEX_OWNED) == 0) {
  350. LpcpReleaseLpcpLock();
  351. }
  352. //
  353. // And return to our caller
  354. //
  355. return;
  356. }
  357. VOID
  358. LpcpFreeDataInfoMessage (
  359. IN PLPCP_PORT_OBJECT Port,
  360. IN ULONG MessageId,
  361. IN ULONG CallbackId,
  362. IN LPC_CLIENT_ID ClientId
  363. )
  364. /*++
  365. Routine Description:
  366. This routine is used to free up a saved message in a port
  367. Arguments:
  368. Port - Supplies the port being manipulated
  369. MessageId - Supplies the id of the message being freed
  370. CallbackId - Supplies the callback id of the message being freed
  371. Return Value:
  372. None.
  373. --*/
  374. {
  375. PLPCP_MESSAGE Msg;
  376. PLIST_ENTRY Head, Next;
  377. PAGED_CODE();
  378. //
  379. // Make sure we get to the connection port object of this port
  380. //
  381. if ((Port->Flags & PORT_TYPE) > UNCONNECTED_COMMUNICATION_PORT) {
  382. Port = Port->ConnectionPort;
  383. if (Port == NULL) {
  384. return;
  385. }
  386. }
  387. //
  388. // Zoom down the data info chain for the connection port object
  389. //
  390. Head = &Port->LpcDataInfoChainHead;
  391. Next = Head->Flink;
  392. while (Next != Head) {
  393. Msg = CONTAINING_RECORD( Next, LPCP_MESSAGE, Entry );
  394. //
  395. // If this message matches the callers specification then remove
  396. // this message, free it back to the port zone, and return back
  397. // to our caller
  398. //
  399. if ((Msg->Request.MessageId == MessageId) &&
  400. (Msg->Request.ClientId.UniqueProcess == ClientId.UniqueProcess) &&
  401. (Msg->Request.ClientId.UniqueThread == ClientId.UniqueThread)) {
  402. LpcpTrace(( "%s Removing DataInfo Message %lx (%u.%u) Port: %lx\n",
  403. PsGetCurrentProcess()->ImageFileName,
  404. Msg,
  405. Msg->Request.MessageId,
  406. Msg->Request.CallbackId,
  407. Port ));
  408. RemoveEntryList( &Msg->Entry );
  409. InitializeListHead( &Msg->Entry );
  410. LpcpFreeToPortZone( Msg, LPCP_MUTEX_OWNED );
  411. return;
  412. } else {
  413. //
  414. // Keep on going down the data info chain
  415. //
  416. Next = Next->Flink;
  417. }
  418. }
  419. //
  420. // We didn't find a match so just return to our caller
  421. //
  422. LpcpTrace(( "%s Unable to find DataInfo Message (%u.%u) Port: %lx\n",
  423. PsGetCurrentProcess()->ImageFileName,
  424. MessageId,
  425. CallbackId,
  426. Port ));
  427. return;
  428. }
  429. PLPCP_MESSAGE
  430. LpcpFindDataInfoMessage (
  431. IN PLPCP_PORT_OBJECT Port,
  432. IN ULONG MessageId,
  433. IN ULONG CallbackId,
  434. IN LPC_CLIENT_ID ClientId
  435. )
  436. /*++
  437. Routine Description:
  438. This routine is used to locate a specific message stored off the
  439. data info chain of a port
  440. Arguments:
  441. Port - Supplies the port being examined
  442. MessageId - Supplies the ID of the message being searched for
  443. CallbackId - Supplies the callback ID being searched for
  444. Return Value:
  445. PLPCP_MESSAGE - returns a pointer to the message satisfying the
  446. search criteria or NULL of none was found
  447. --*/
  448. {
  449. PLPCP_MESSAGE Msg;
  450. PLIST_ENTRY Head, Next;
  451. PAGED_CODE();
  452. //
  453. // Make sure we get to the connection port object of this port
  454. //
  455. if ((Port->Flags & PORT_TYPE) > UNCONNECTED_COMMUNICATION_PORT) {
  456. Port = Port->ConnectionPort;
  457. if (Port == NULL) {
  458. return NULL;
  459. }
  460. }
  461. //
  462. // Zoom down the data info chain for the connection port object looking
  463. // for a match
  464. //
  465. Head = &Port->LpcDataInfoChainHead;
  466. Next = Head->Flink;
  467. while (Next != Head) {
  468. Msg = CONTAINING_RECORD( Next, LPCP_MESSAGE, Entry );
  469. if ((Msg->Request.MessageId == MessageId) &&
  470. (Msg->Request.ClientId.UniqueProcess == ClientId.UniqueProcess) &&
  471. (Msg->Request.ClientId.UniqueThread == ClientId.UniqueThread)) {
  472. LpcpTrace(( "%s Found DataInfo Message %lx (%u.%u) Port: %lx\n",
  473. PsGetCurrentProcess()->ImageFileName,
  474. Msg,
  475. Msg->Request.MessageId,
  476. Msg->Request.CallbackId,
  477. Port ));
  478. return Msg;
  479. } else {
  480. Next = Next->Flink;
  481. }
  482. }
  483. //
  484. // We did not find a match so return null to our caller
  485. //
  486. LpcpTrace(( "%s Unable to find DataInfo Message (%u.%u) Port: %lx\n",
  487. PsGetCurrentProcess()->ImageFileName,
  488. MessageId,
  489. CallbackId,
  490. Port ));
  491. return NULL;
  492. }