1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/iomgr/iocomp.c
5 * PURPOSE: I/O Wrappers (called Completion Ports) for Kernel Queues
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Thomas Weidenmueller (w3seek@reactos.org)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 POBJECT_TYPE IoCompletionType;
17
18 GENERAL_LOOKASIDE IoCompletionPacketLookaside;
19
20 GENERIC_MAPPING IopCompletionMapping =
21 {
22 STANDARD_RIGHTS_READ | IO_COMPLETION_QUERY_STATE,
23 STANDARD_RIGHTS_WRITE | IO_COMPLETION_MODIFY_STATE,
24 STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
25 IO_COMPLETION_ALL_ACCESS
26 };
27
28 static const INFORMATION_CLASS_INFO IoCompletionInfoClass[] =
29 {
30 /* IoCompletionBasicInformation */
31 IQS_SAME(IO_COMPLETION_BASIC_INFORMATION, ULONG, ICIF_QUERY),
32 };
33
34 /* PRIVATE FUNCTIONS *********************************************************/
35
36 NTSTATUS
37 NTAPI
IopUnloadSafeCompletion(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PVOID Context)38 IopUnloadSafeCompletion(IN PDEVICE_OBJECT DeviceObject,
39 IN PIRP Irp,
40 IN PVOID Context)
41 {
42 NTSTATUS Status;
43 PIO_UNLOAD_SAFE_COMPLETION_CONTEXT UnsafeContext = Context;
44
45 /* Reference the device object */
46 ObReferenceObject(UnsafeContext->DeviceObject);
47
48 /* Call the completion routine */
49 Status= UnsafeContext->CompletionRoutine(DeviceObject,
50 Irp,
51 UnsafeContext->Context);
52
53 /* Dereference the device object */
54 ObDereferenceObject(UnsafeContext->DeviceObject);
55
56 /* Free our context */
57 ExFreePool(UnsafeContext);
58 return Status;
59 }
60
61 VOID
62 NTAPI
IopFreeMiniPacket(PIOP_MINI_COMPLETION_PACKET Packet)63 IopFreeMiniPacket(PIOP_MINI_COMPLETION_PACKET Packet)
64 {
65 PKPRCB Prcb = KeGetCurrentPrcb();
66 PNPAGED_LOOKASIDE_LIST List;
67
68 /* Use the P List */
69 List = (PNPAGED_LOOKASIDE_LIST)Prcb->
70 PPLookasideList[LookasideCompletionList].P;
71 List->L.TotalFrees++;
72
73 /* Check if the Free was within the Depth or not */
74 if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth)
75 {
76 /* Let the balancer know */
77 List->L.FreeMisses++;
78
79 /* Use the L List */
80 List = (PNPAGED_LOOKASIDE_LIST)Prcb->
81 PPLookasideList[LookasideCompletionList].L;
82 List->L.TotalFrees++;
83
84 /* Check if the Free was within the Depth or not */
85 if (ExQueryDepthSList(&List->L.ListHead) >= List->L.Depth)
86 {
87 /* All lists failed, use the pool */
88 List->L.FreeMisses++;
89 ExFreePool(Packet);
90 return;
91 }
92 }
93
94 /* The free was within dhe Depth */
95 InterlockedPushEntrySList(&List->L.ListHead, (PSLIST_ENTRY)Packet);
96 }
97
98 VOID
99 NTAPI
IopDeleteIoCompletion(PVOID ObjectBody)100 IopDeleteIoCompletion(PVOID ObjectBody)
101 {
102 PKQUEUE Queue = ObjectBody;
103 PLIST_ENTRY FirstEntry;
104 PLIST_ENTRY CurrentEntry;
105 PIRP Irp;
106 PIOP_MINI_COMPLETION_PACKET Packet;
107
108 /* Rundown the Queue */
109 FirstEntry = KeRundownQueue(Queue);
110 if (FirstEntry)
111 {
112 /* Loop the packets */
113 CurrentEntry = FirstEntry;
114 do
115 {
116 /* Get the Packet */
117 Packet = CONTAINING_RECORD(CurrentEntry,
118 IOP_MINI_COMPLETION_PACKET,
119 ListEntry);
120
121 /* Go to next Entry */
122 CurrentEntry = CurrentEntry->Flink;
123
124 /* Check if it's part of an IRP, or a separate packet */
125 if (Packet->PacketType == IopCompletionPacketIrp)
126 {
127 /* Get the IRP and free it */
128 Irp = CONTAINING_RECORD(Packet, IRP, Tail.Overlay.ListEntry);
129 IoFreeIrp(Irp);
130 }
131 else
132 {
133 /* Use common routine */
134 IopFreeMiniPacket(Packet);
135 }
136 } while (FirstEntry != CurrentEntry);
137 }
138 }
139
140 /* PUBLIC FUNCTIONS **********************************************************/
141
142 /*
143 * @implemented
144 */
145 NTSTATUS
146 NTAPI
IoSetIoCompletion(IN PVOID IoCompletion,IN PVOID KeyContext,IN PVOID ApcContext,IN NTSTATUS IoStatus,IN ULONG_PTR IoStatusInformation,IN BOOLEAN Quota)147 IoSetIoCompletion(IN PVOID IoCompletion,
148 IN PVOID KeyContext,
149 IN PVOID ApcContext,
150 IN NTSTATUS IoStatus,
151 IN ULONG_PTR IoStatusInformation,
152 IN BOOLEAN Quota)
153 {
154 PKQUEUE Queue = (PKQUEUE)IoCompletion;
155 PNPAGED_LOOKASIDE_LIST List;
156 PKPRCB Prcb = KeGetCurrentPrcb();
157 PIOP_MINI_COMPLETION_PACKET Packet;
158
159 /* Get the P List */
160 List = (PNPAGED_LOOKASIDE_LIST)Prcb->
161 PPLookasideList[LookasideCompletionList].P;
162
163 /* Try to allocate the Packet */
164 List->L.TotalAllocates++;
165 Packet = (PVOID)InterlockedPopEntrySList(&List->L.ListHead);
166
167 /* Check if that failed, use the L list if it did */
168 if (!Packet)
169 {
170 /* Let the balancer know */
171 List->L.AllocateMisses++;
172
173 /* Get L List */
174 List = (PNPAGED_LOOKASIDE_LIST)Prcb->
175 PPLookasideList[LookasideCompletionList].L;
176
177 /* Try to allocate the Packet */
178 List->L.TotalAllocates++;
179 Packet = (PVOID)InterlockedPopEntrySList(&List->L.ListHead);
180 }
181
182 /* Still failed, use pool */
183 if (!Packet)
184 {
185 /* Let the balancer know */
186 List->L.AllocateMisses++;
187
188 /* Allocate from Nonpaged Pool */
189 Packet = ExAllocatePoolWithTag(NonPagedPool, sizeof(*Packet), IOC_TAG);
190 }
191
192 /* Make sure we have one by now... */
193 if (Packet)
194 {
195 /* Set up the Packet */
196 Packet->PacketType = IopCompletionPacketMini;
197 Packet->KeyContext = KeyContext;
198 Packet->ApcContext = ApcContext;
199 Packet->IoStatus = IoStatus;
200 Packet->IoStatusInformation = IoStatusInformation;
201
202 /* Insert the Queue */
203 KeInsertQueue(Queue, &Packet->ListEntry);
204 }
205 else
206 {
207 /* Out of memory, fail */
208 return STATUS_INSUFFICIENT_RESOURCES;
209 }
210
211 /* Return Success */
212 return STATUS_SUCCESS;
213 }
214
215 /*
216 * @implemented
217 */
218 NTSTATUS
219 NTAPI
IoSetCompletionRoutineEx(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp,IN PIO_COMPLETION_ROUTINE CompletionRoutine,IN PVOID Context,IN BOOLEAN InvokeOnSuccess,IN BOOLEAN InvokeOnError,IN BOOLEAN InvokeOnCancel)220 IoSetCompletionRoutineEx(IN PDEVICE_OBJECT DeviceObject,
221 IN PIRP Irp,
222 IN PIO_COMPLETION_ROUTINE CompletionRoutine,
223 IN PVOID Context,
224 IN BOOLEAN InvokeOnSuccess,
225 IN BOOLEAN InvokeOnError,
226 IN BOOLEAN InvokeOnCancel)
227 {
228 PIO_UNLOAD_SAFE_COMPLETION_CONTEXT UnloadContext;
229
230 /* Allocate the context */
231 UnloadContext = ExAllocatePoolWithTag(NonPagedPool,
232 sizeof(*UnloadContext),
233 'sUoI');
234 if (!UnloadContext) return STATUS_INSUFFICIENT_RESOURCES;
235
236 /* Set up the context */
237 UnloadContext->DeviceObject = DeviceObject;
238 UnloadContext->Context = Context;
239 UnloadContext->CompletionRoutine = CompletionRoutine;
240
241 /* Now set the completion routine */
242 IoSetCompletionRoutine(Irp,
243 IopUnloadSafeCompletion,
244 UnloadContext,
245 InvokeOnSuccess,
246 InvokeOnError,
247 InvokeOnCancel);
248 return STATUS_SUCCESS;
249 }
250
251 NTSTATUS
252 NTAPI
NtCreateIoCompletion(OUT PHANDLE IoCompletionHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN ULONG NumberOfConcurrentThreads)253 NtCreateIoCompletion(OUT PHANDLE IoCompletionHandle,
254 IN ACCESS_MASK DesiredAccess,
255 IN POBJECT_ATTRIBUTES ObjectAttributes,
256 IN ULONG NumberOfConcurrentThreads)
257 {
258 PKQUEUE Queue;
259 HANDLE hIoCompletionHandle;
260 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
261 NTSTATUS Status;
262 PAGED_CODE();
263
264 /* Check if this was a user-mode call */
265 if (PreviousMode != KernelMode)
266 {
267 /* Wrap probing in SEH */
268 _SEH2_TRY
269 {
270 /* Probe the handle */
271 ProbeForWriteHandle(IoCompletionHandle);
272 }
273 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
274 {
275 /* Return the exception code */
276 _SEH2_YIELD(return _SEH2_GetExceptionCode());
277 }
278 _SEH2_END;
279 }
280
281 /* Create the Object */
282 Status = ObCreateObject(PreviousMode,
283 IoCompletionType,
284 ObjectAttributes,
285 PreviousMode,
286 NULL,
287 sizeof(KQUEUE),
288 0,
289 0,
290 (PVOID*)&Queue);
291 if (NT_SUCCESS(Status))
292 {
293 /* Initialize the Queue */
294 KeInitializeQueue(Queue, NumberOfConcurrentThreads);
295
296 /* Insert it */
297 Status = ObInsertObject(Queue,
298 NULL,
299 DesiredAccess,
300 0,
301 NULL,
302 &hIoCompletionHandle);
303 if (NT_SUCCESS(Status))
304 {
305 /* Protect writing the handle in SEH */
306 _SEH2_TRY
307 {
308 /* Write the handle back */
309 *IoCompletionHandle = hIoCompletionHandle;
310 }
311 _SEH2_EXCEPT(ExSystemExceptionFilter())
312 {
313 /* Get the exception code */
314 Status = _SEH2_GetExceptionCode();
315 }
316 _SEH2_END;
317 }
318 }
319
320 /* Return Status */
321 return Status;
322 }
323
324 NTSTATUS
325 NTAPI
NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes)326 NtOpenIoCompletion(OUT PHANDLE IoCompletionHandle,
327 IN ACCESS_MASK DesiredAccess,
328 IN POBJECT_ATTRIBUTES ObjectAttributes)
329 {
330 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
331 HANDLE hIoCompletionHandle;
332 NTSTATUS Status;
333 PAGED_CODE();
334
335 /* Check if this was a user-mode call */
336 if (PreviousMode != KernelMode)
337 {
338 /* Wrap probing in SEH */
339 _SEH2_TRY
340 {
341 /* Probe the handle */
342 ProbeForWriteHandle(IoCompletionHandle);
343 }
344 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
345 {
346 /* Return the exception code */
347 _SEH2_YIELD(return _SEH2_GetExceptionCode());
348 }
349 _SEH2_END;
350 }
351
352 /* Open the Object */
353 Status = ObOpenObjectByName(ObjectAttributes,
354 IoCompletionType,
355 PreviousMode,
356 NULL,
357 DesiredAccess,
358 NULL,
359 &hIoCompletionHandle);
360 if (NT_SUCCESS(Status))
361 {
362 /* Protect writing the handle in SEH */
363 _SEH2_TRY
364 {
365 /* Write the handle back */
366 *IoCompletionHandle = hIoCompletionHandle;
367 }
368 _SEH2_EXCEPT(ExSystemExceptionFilter())
369 {
370 /* Get the exception code */
371 Status = _SEH2_GetExceptionCode();
372 }
373 _SEH2_END;
374 }
375
376 /* Return Status */
377 return Status;
378 }
379
380 NTSTATUS
381 NTAPI
NtQueryIoCompletion(IN HANDLE IoCompletionHandle,IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass,OUT PVOID IoCompletionInformation,IN ULONG IoCompletionInformationLength,OUT PULONG ResultLength OPTIONAL)382 NtQueryIoCompletion(IN HANDLE IoCompletionHandle,
383 IN IO_COMPLETION_INFORMATION_CLASS IoCompletionInformationClass,
384 OUT PVOID IoCompletionInformation,
385 IN ULONG IoCompletionInformationLength,
386 OUT PULONG ResultLength OPTIONAL)
387 {
388 PKQUEUE Queue;
389 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
390 NTSTATUS Status;
391 PAGED_CODE();
392
393 /* Check buffers and parameters */
394 Status = DefaultQueryInfoBufferCheck(IoCompletionInformationClass,
395 IoCompletionInfoClass,
396 sizeof(IoCompletionInfoClass) /
397 sizeof(IoCompletionInfoClass[0]),
398 ICIF_PROBE_READ_WRITE,
399 IoCompletionInformation,
400 IoCompletionInformationLength,
401 ResultLength,
402 NULL,
403 PreviousMode);
404 if (!NT_SUCCESS(Status)) return Status;
405
406 /* Get the Object */
407 Status = ObReferenceObjectByHandle(IoCompletionHandle,
408 IO_COMPLETION_QUERY_STATE,
409 IoCompletionType,
410 PreviousMode,
411 (PVOID*)&Queue,
412 NULL);
413 if (NT_SUCCESS(Status))
414 {
415 /* Protect write in SEH */
416 _SEH2_TRY
417 {
418 /* Return Info */
419 ((PIO_COMPLETION_BASIC_INFORMATION)IoCompletionInformation)->
420 Depth = KeReadStateQueue(Queue);
421
422 /* Return Result Length if needed */
423 if (ResultLength)
424 {
425 *ResultLength = sizeof(IO_COMPLETION_BASIC_INFORMATION);
426 }
427 }
428 _SEH2_EXCEPT(ExSystemExceptionFilter())
429 {
430 /* Get exception code */
431 Status = _SEH2_GetExceptionCode();
432 }
433 _SEH2_END;
434
435 /* Dereference the queue */
436 ObDereferenceObject(Queue);
437 }
438
439 /* Return Status */
440 return Status;
441 }
442
443 NTSTATUS
444 NTAPI
NtRemoveIoCompletion(IN HANDLE IoCompletionHandle,OUT PVOID * KeyContext,OUT PVOID * ApcContext,OUT PIO_STATUS_BLOCK IoStatusBlock,IN PLARGE_INTEGER Timeout OPTIONAL)445 NtRemoveIoCompletion(IN HANDLE IoCompletionHandle,
446 OUT PVOID *KeyContext,
447 OUT PVOID *ApcContext,
448 OUT PIO_STATUS_BLOCK IoStatusBlock,
449 IN PLARGE_INTEGER Timeout OPTIONAL)
450 {
451 LARGE_INTEGER SafeTimeout;
452 PKQUEUE Queue;
453 PIOP_MINI_COMPLETION_PACKET Packet;
454 PLIST_ENTRY ListEntry;
455 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
456 NTSTATUS Status;
457 PIRP Irp;
458 PVOID Apc, Key;
459 IO_STATUS_BLOCK IoStatus;
460 PAGED_CODE();
461
462 /* Check if the call was from user mode */
463 if (PreviousMode != KernelMode)
464 {
465 /* Protect probes in SEH */
466 _SEH2_TRY
467 {
468 /* Probe the pointers */
469 ProbeForWritePointer(KeyContext);
470 ProbeForWritePointer(ApcContext);
471
472 /* Probe the I/O Status Block */
473 ProbeForWriteIoStatusBlock(IoStatusBlock);
474 if (Timeout)
475 {
476 /* Probe and capture the timeout */
477 SafeTimeout = ProbeForReadLargeInteger(Timeout);
478 Timeout = &SafeTimeout;
479 }
480 }
481 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
482 {
483 /* Return the exception code */
484 _SEH2_YIELD(return _SEH2_GetExceptionCode());
485 }
486 _SEH2_END;
487 }
488
489 /* Open the Object */
490 Status = ObReferenceObjectByHandle(IoCompletionHandle,
491 IO_COMPLETION_MODIFY_STATE,
492 IoCompletionType,
493 PreviousMode,
494 (PVOID*)&Queue,
495 NULL);
496 if (NT_SUCCESS(Status))
497 {
498 /* Remove queue */
499 ListEntry = KeRemoveQueue(Queue, PreviousMode, Timeout);
500
501 /* If we got a timeout or user_apc back, return the status */
502 if (((NTSTATUS)(ULONG_PTR)ListEntry == STATUS_TIMEOUT) ||
503 ((NTSTATUS)(ULONG_PTR)ListEntry == STATUS_USER_APC))
504 {
505 /* Set this as the status */
506 Status = (NTSTATUS)(ULONG_PTR)ListEntry;
507 }
508 else
509 {
510 /* Get the Packet Data */
511 Packet = CONTAINING_RECORD(ListEntry,
512 IOP_MINI_COMPLETION_PACKET,
513 ListEntry);
514
515 /* Check if this is piggybacked on an IRP */
516 if (Packet->PacketType == IopCompletionPacketIrp)
517 {
518 /* Get the IRP */
519 Irp = CONTAINING_RECORD(ListEntry,
520 IRP,
521 Tail.Overlay.ListEntry);
522
523 /* Save values */
524 Key = Irp->Tail.CompletionKey;
525 Apc = Irp->Overlay.AsynchronousParameters.UserApcContext;
526 IoStatus = Irp->IoStatus;
527
528 /* Free the IRP */
529 IoFreeIrp(Irp);
530 }
531 else
532 {
533 /* Save values */
534 Key = Packet->KeyContext;
535 Apc = Packet->ApcContext;
536 IoStatus.Status = Packet->IoStatus;
537 IoStatus.Information = Packet->IoStatusInformation;
538
539 /* Free the packet */
540 IopFreeMiniPacket(Packet);
541 }
542
543 /* Enter SEH to write back the values */
544 _SEH2_TRY
545 {
546 /* Write the values to caller */
547 *ApcContext = Apc;
548 *KeyContext = Key;
549 *IoStatusBlock = IoStatus;
550 }
551 _SEH2_EXCEPT(ExSystemExceptionFilter())
552 {
553 /* Get the exception code */
554 Status = _SEH2_GetExceptionCode();
555 }
556 _SEH2_END;
557 }
558
559 /* Dereference the Object */
560 ObDereferenceObject(Queue);
561 }
562
563 /* Return status */
564 return Status;
565 }
566
567 NTSTATUS
568 NTAPI
NtSetIoCompletion(IN HANDLE IoCompletionPortHandle,IN PVOID CompletionKey,IN PVOID CompletionContext,IN NTSTATUS CompletionStatus,IN ULONG CompletionInformation)569 NtSetIoCompletion(IN HANDLE IoCompletionPortHandle,
570 IN PVOID CompletionKey,
571 IN PVOID CompletionContext,
572 IN NTSTATUS CompletionStatus,
573 IN ULONG CompletionInformation)
574 {
575 NTSTATUS Status;
576 PKQUEUE Queue;
577 PAGED_CODE();
578
579 /* Get the Object */
580 Status = ObReferenceObjectByHandle(IoCompletionPortHandle,
581 IO_COMPLETION_MODIFY_STATE,
582 IoCompletionType,
583 ExGetPreviousMode(),
584 (PVOID*)&Queue,
585 NULL);
586 if (NT_SUCCESS(Status))
587 {
588 /* Set the Completion */
589 Status = IoSetIoCompletion(Queue,
590 CompletionKey,
591 CompletionContext,
592 CompletionStatus,
593 CompletionInformation,
594 TRUE);
595
596 /* Dereference the object */
597 ObDereferenceObject(Queue);
598 }
599
600 /* Return status */
601 return Status;
602 }
603