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