- Use correct, documented, LPC Message structure (PORT_MESSAGE). Fix all caller code...
[reactos.git] / reactos / ntoskrnl / lpc / reply.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/lpc/reply.c
6 * PURPOSE: Communication mechanism
7 *
8 * PROGRAMMERS: David Welch (welch@cwcom.net)
9 */
10
11 /* INCLUDES ******************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 /* FUNCTIONS *****************************************************************/
20
21 /**********************************************************************
22 * NAME
23 *
24 * DESCRIPTION
25 *
26 * ARGUMENTS
27 *
28 * RETURN VALUE
29 *
30 * REVISIONS
31 */
32 NTSTATUS STDCALL
33 EiReplyOrRequestPort (IN PEPORT Port,
34 IN PPORT_MESSAGE LpcReply,
35 IN ULONG MessageType,
36 IN PEPORT Sender)
37 {
38 KIRQL oldIrql;
39 PQUEUEDMESSAGE MessageReply;
40
41 if (Port == NULL)
42 {
43 KEBUGCHECK(0);
44 }
45
46 MessageReply = ExAllocatePoolWithTag(NonPagedPool, sizeof(QUEUEDMESSAGE),
47 TAG_LPC_MESSAGE);
48 MessageReply->Sender = Sender;
49
50 if (LpcReply != NULL)
51 {
52 memcpy(&MessageReply->Message, LpcReply, LpcReply->u1.s1.TotalLength);
53 }
54
55 MessageReply->Message.ClientId.UniqueProcess = PsGetCurrentProcessId();
56 MessageReply->Message.ClientId.UniqueThread = PsGetCurrentThreadId();
57 MessageReply->Message.u2.s2.Type = MessageType;
58 MessageReply->Message.MessageId = InterlockedIncrementUL(&LpcpNextMessageId);
59
60 KeAcquireSpinLock(&Port->Lock, &oldIrql);
61 EiEnqueueMessagePort(Port, MessageReply);
62 KeReleaseSpinLock(&Port->Lock, oldIrql);
63
64 return(STATUS_SUCCESS);
65 }
66
67
68 /**********************************************************************
69 * NAME EXPORTED
70 *
71 * DESCRIPTION
72 *
73 * ARGUMENTS
74 *
75 * RETURN VALUE
76 *
77 * REVISIONS
78 */
79 NTSTATUS STDCALL
80 NtReplyPort (IN HANDLE PortHandle,
81 IN PPORT_MESSAGE LpcReply)
82 {
83 NTSTATUS Status;
84 PEPORT Port;
85
86 DPRINT("NtReplyPort(PortHandle %x, LpcReply %x)\n", PortHandle, LpcReply);
87
88 Status = ObReferenceObjectByHandle(PortHandle,
89 PORT_ALL_ACCESS, /* AccessRequired */
90 LpcPortObjectType,
91 UserMode,
92 (PVOID*)&Port,
93 NULL);
94 if (!NT_SUCCESS(Status))
95 {
96 DPRINT("NtReplyPort() = %x\n", Status);
97 return(Status);
98 }
99
100 if (EPORT_DISCONNECTED == Port->State)
101 {
102 ObDereferenceObject(Port);
103 return STATUS_PORT_DISCONNECTED;
104 }
105
106 Status = EiReplyOrRequestPort(Port->OtherPort,
107 LpcReply,
108 LPC_REPLY,
109 Port);
110 KeReleaseSemaphore(&Port->OtherPort->Semaphore, IO_NO_INCREMENT, 1, FALSE);
111
112 ObDereferenceObject(Port);
113
114 return(Status);
115 }
116
117
118 /**********************************************************************
119 * NAME EXPORTED
120 * NtReplyWaitReceivePortEx
121 *
122 * DESCRIPTION
123 * Can be used with waitable ports.
124 * Present only in w2k+.
125 *
126 * ARGUMENTS
127 * PortHandle
128 * PortId
129 * LpcReply
130 * LpcMessage
131 * Timeout
132 *
133 * RETURN VALUE
134 *
135 * REVISIONS
136 */
137 NTSTATUS STDCALL
138 NtReplyWaitReceivePortEx(IN HANDLE PortHandle,
139 OUT PULONG PortId,
140 IN PPORT_MESSAGE LpcReply,
141 OUT PPORT_MESSAGE LpcMessage,
142 IN PLARGE_INTEGER Timeout)
143 {
144 PEPORT Port;
145 KIRQL oldIrql;
146 PQUEUEDMESSAGE Request;
147 BOOLEAN Disconnected;
148 LARGE_INTEGER to;
149 KPROCESSOR_MODE PreviousMode;
150 NTSTATUS Status = STATUS_SUCCESS;
151
152 PreviousMode = ExGetPreviousMode();
153
154 DPRINT("NtReplyWaitReceivePortEx(PortHandle %x, LpcReply %x, "
155 "LpcMessage %x)\n", PortHandle, LpcReply, LpcMessage);
156
157 if (PreviousMode != KernelMode)
158 {
159 _SEH_TRY
160 {
161 ProbeForWrite(LpcMessage,
162 sizeof(PORT_MESSAGE),
163 1);
164 }
165 _SEH_HANDLE
166 {
167 Status = _SEH_GetExceptionCode();
168 }
169 _SEH_END;
170
171 if (!NT_SUCCESS(Status))
172 {
173 return Status;
174 }
175 }
176
177 Status = ObReferenceObjectByHandle(PortHandle,
178 PORT_ALL_ACCESS,
179 LpcPortObjectType,
180 UserMode,
181 (PVOID*)&Port,
182 NULL);
183 if (!NT_SUCCESS(Status))
184 {
185 DPRINT1("NtReplyWaitReceivePortEx() = %x\n", Status);
186 return(Status);
187 }
188 if( Port->State == EPORT_DISCONNECTED )
189 {
190 /* If the port is disconnected, force the timeout to be 0
191 * so we don't wait for new messages, because there won't be
192 * any, only try to remove any existing messages
193 */
194 Disconnected = TRUE;
195 to.QuadPart = 0;
196 Timeout = &to;
197 }
198 else Disconnected = FALSE;
199
200 /*
201 * Send the reply, only if port is connected
202 */
203 if (LpcReply != NULL && !Disconnected)
204 {
205 Status = EiReplyOrRequestPort(Port->OtherPort,
206 LpcReply,
207 LPC_REPLY,
208 Port);
209 KeReleaseSemaphore(&Port->OtherPort->Semaphore, IO_NO_INCREMENT, 1,
210 FALSE);
211
212 if (!NT_SUCCESS(Status))
213 {
214 ObDereferenceObject(Port);
215 DPRINT1("NtReplyWaitReceivePortEx() = %x\n", Status);
216 return(Status);
217 }
218 }
219
220 /*
221 * Want for a message to be received
222 */
223 Status = KeWaitForSingleObject(&Port->Semaphore,
224 UserRequest,
225 UserMode,
226 FALSE,
227 Timeout);
228 if( Status == STATUS_TIMEOUT )
229 {
230 /*
231 * if the port is disconnected, and there are no remaining messages,
232 * return STATUS_PORT_DISCONNECTED
233 */
234 ObDereferenceObject(Port);
235 return(Disconnected ? STATUS_PORT_DISCONNECTED : STATUS_TIMEOUT);
236 }
237
238 if (!NT_SUCCESS(Status))
239 {
240 if (STATUS_THREAD_IS_TERMINATING != Status)
241 {
242 DPRINT1("NtReplyWaitReceivePortEx() = %x\n", Status);
243 }
244 ObDereferenceObject(Port);
245 return(Status);
246 }
247
248 /*
249 * Dequeue the message
250 */
251 KeAcquireSpinLock(&Port->Lock, &oldIrql);
252 Request = EiDequeueMessagePort(Port);
253 KeReleaseSpinLock(&Port->Lock, oldIrql);
254
255 if (Request->Message.u2.s2.Type == LPC_CONNECTION_REQUEST)
256 {
257 PORT_MESSAGE Header;
258 PEPORT_CONNECT_REQUEST_MESSAGE CRequest;
259
260 CRequest = (PEPORT_CONNECT_REQUEST_MESSAGE)&Request->Message;
261 memcpy(&Header, &Request->Message, sizeof(PORT_MESSAGE));
262 Header.u1.s1.DataLength = CRequest->ConnectDataLength;
263 Header.u1.s1.TotalLength = Header.u1.s1.DataLength + sizeof(PORT_MESSAGE);
264
265 if (PreviousMode != KernelMode)
266 {
267 _SEH_TRY
268 {
269 ProbeForWrite((PVOID)(LpcMessage + 1),
270 CRequest->ConnectDataLength,
271 1);
272
273 RtlCopyMemory(LpcMessage,
274 &Header,
275 sizeof(PORT_MESSAGE));
276 RtlCopyMemory((PVOID)(LpcMessage + 1),
277 CRequest->ConnectData,
278 CRequest->ConnectDataLength);
279 }
280 _SEH_HANDLE
281 {
282 Status = _SEH_GetExceptionCode();
283 }
284 _SEH_END;
285 }
286 else
287 {
288 RtlCopyMemory(LpcMessage,
289 &Header,
290 sizeof(PORT_MESSAGE));
291 RtlCopyMemory((PVOID)(LpcMessage + 1),
292 CRequest->ConnectData,
293 CRequest->ConnectDataLength);
294 }
295 }
296 else
297 {
298 if (PreviousMode != KernelMode)
299 {
300 _SEH_TRY
301 {
302 ProbeForWrite(LpcMessage,
303 Request->Message.u1.s1.TotalLength,
304 1);
305
306 RtlCopyMemory(LpcMessage,
307 &Request->Message,
308 Request->Message.u1.s1.TotalLength);
309 }
310 _SEH_HANDLE
311 {
312 Status = _SEH_GetExceptionCode();
313 }
314 _SEH_END;
315 }
316 else
317 {
318 RtlCopyMemory(LpcMessage,
319 &Request->Message,
320 Request->Message.u1.s1.TotalLength);
321 }
322 }
323 if (!NT_SUCCESS(Status))
324 {
325 /*
326 * Copying the message to the caller's buffer failed so
327 * undo what we did and return.
328 * FIXME: Also increment semaphore.
329 */
330 KeAcquireSpinLock(&Port->Lock, &oldIrql);
331 EiEnqueueMessageAtHeadPort(Port, Request);
332 KeReleaseSpinLock(&Port->Lock, oldIrql);
333 ObDereferenceObject(Port);
334 return(Status);
335 }
336 if (Request->Message.u2.s2.Type == LPC_CONNECTION_REQUEST)
337 {
338 KeAcquireSpinLock(&Port->Lock, &oldIrql);
339 EiEnqueueConnectMessagePort(Port, Request);
340 KeReleaseSpinLock(&Port->Lock, oldIrql);
341 }
342 else
343 {
344 ExFreePool(Request);
345 }
346
347 /*
348 * Dereference the port
349 */
350 ObDereferenceObject(Port);
351 return(STATUS_SUCCESS);
352 }
353
354
355 /**********************************************************************
356 * NAME EXPORTED
357 * NtReplyWaitReceivePort
358 *
359 * DESCRIPTION
360 * Can be used with waitable ports.
361 *
362 * ARGUMENTS
363 * PortHandle
364 * PortId
365 * LpcReply
366 * LpcMessage
367 *
368 * RETURN VALUE
369 *
370 * REVISIONS
371 */
372 NTSTATUS STDCALL
373 NtReplyWaitReceivePort (IN HANDLE PortHandle,
374 OUT PULONG PortId,
375 IN PPORT_MESSAGE LpcReply,
376 OUT PPORT_MESSAGE LpcMessage)
377 {
378 return(NtReplyWaitReceivePortEx (PortHandle,
379 PortId,
380 LpcReply,
381 LpcMessage,
382 NULL));
383 }
384
385 /**********************************************************************
386 * NAME
387 *
388 * DESCRIPTION
389 *
390 * ARGUMENTS
391 *
392 * RETURN VALUE
393 *
394 * REVISIONS
395 */
396 NTSTATUS STDCALL
397 NtReplyWaitReplyPort (HANDLE PortHandle,
398 PPORT_MESSAGE ReplyMessage)
399 {
400 UNIMPLEMENTED;
401 return(STATUS_NOT_IMPLEMENTED);
402 }
403
404 /*
405 * @unimplemented
406 */
407 NTSTATUS
408 STDCALL
409 LpcRequestWaitReplyPort (
410 IN PEPORT Port,
411 IN PPORT_MESSAGE LpcMessageRequest,
412 OUT PPORT_MESSAGE LpcMessageReply
413 )
414 {
415 UNIMPLEMENTED;
416 return STATUS_NOT_IMPLEMENTED;
417 }
418
419 /* EOF */