Add hack for ROS's weird behavior. Will investigate but this lets you boot for now
[reactos.git] / reactos / ntoskrnl / io / irp.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/io/irp.c
5 * PURPOSE: Handle IRPs
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * David Welch (welch@mcmail.com)
9 */
10
11 /* INCLUDES ****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 #define TAG_IRP TAG('I', 'R', 'P', ' ')
20 #define TAG_SYS_BUF TAG('I', 'o', ' ' , ' ')
21
22 /* FUNCTIONS *****************************************************************/
23
24 /*
25 * @implemented
26 *
27 * FUNCTION: Allocates an IRP
28 * ARGUMENTS:
29 * StackSize = the size of the stack required for the irp
30 * ChargeQuota = Charge allocation to current threads quota
31 * RETURNS: Irp allocated
32 */
33 PIRP
34 STDCALL
35 IoAllocateIrp(CCHAR StackSize,
36 BOOLEAN ChargeQuota)
37 {
38 PIRP Irp;
39 USHORT Size = IoSizeOfIrp(StackSize);
40
41 /* Check if we shoudl charge quota */
42 if (ChargeQuota)
43 {
44 /* Irp = ExAllocatePoolWithQuotaTag(NonPagedPool,IoSizeOfIrp(StackSize), TAG_IRP); */
45 /* FIXME */
46 Irp = ExAllocatePoolWithTag(NonPagedPool,
47 Size,
48 TAG_IRP);
49 }
50 else
51 {
52 /* Allocate the IRP With no Quota charge */
53 Irp = ExAllocatePoolWithTag(NonPagedPool,
54 Size,
55 TAG_IRP);
56 }
57
58 /* Make sure it was sucessful */
59 if (Irp==NULL) return(NULL);
60
61 /* Now Initialize it */
62 IoInitializeIrp(Irp, Size, StackSize);
63
64 /* Return it */
65 return Irp;
66 }
67
68 /*
69 * @implemented
70 *
71 * FUNCTION: Allocates and sets up an IRP to be sent to lower level drivers
72 * ARGUMENTS:
73 * MajorFunction = One of IRP_MJ_READ, IRP_MJ_WRITE,
74 * IRP_MJ_FLUSH_BUFFERS or IRP_MJ_SHUTDOWN
75 * DeviceObject = Device object to send the irp to
76 * Buffer = Buffer into which data will be read or written
77 * Length = Length in bytes of the irp to be allocated
78 * StartingOffset = Starting offset on the device
79 * IoStatusBlock (OUT) = Storage for the result of the operation
80 * RETURNS: The IRP allocated on success, or
81 * NULL on failure
82 */
83 PIRP
84 STDCALL
85 IoBuildAsynchronousFsdRequest(ULONG MajorFunction,
86 PDEVICE_OBJECT DeviceObject,
87 PVOID Buffer,
88 ULONG Length,
89 PLARGE_INTEGER StartingOffset,
90 PIO_STATUS_BLOCK IoStatusBlock)
91 {
92 PIRP Irp;
93 PIO_STACK_LOCATION StackPtr;
94 LOCK_OPERATION AccessType;
95
96 DPRINT("IoBuildAsynchronousFsdRequest(MajorFunction %x, DeviceObject %x, "
97 "Buffer %x, Length %x, StartingOffset %x, "
98 "IoStatusBlock %x\n",MajorFunction,DeviceObject,Buffer,Length,
99 StartingOffset,IoStatusBlock);
100
101 /* Allocate IRP */
102 if (!(Irp = IoAllocateIrp(DeviceObject->StackSize,TRUE))) return Irp;
103
104 /* Get the Stack */
105 StackPtr = IoGetNextIrpStackLocation(Irp);
106
107 /* Write the Major function and then deal with it */
108 StackPtr->MajorFunction = (UCHAR)MajorFunction;
109
110 /* Do not handle the following here */
111 if (MajorFunction != IRP_MJ_FLUSH_BUFFERS &&
112 MajorFunction != IRP_MJ_SHUTDOWN &&
113 MajorFunction != IRP_MJ_PNP)
114 {
115 /* Check if this is Buffered IO */
116 if (DeviceObject->Flags & DO_BUFFERED_IO)
117 {
118 /* Allocate the System Buffer */
119 Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool,
120 Length,
121 TAG_SYS_BUF);
122
123 /* Set flags */
124 Irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
125
126 /* Handle special IRP_MJ_WRITE Case */
127 if (MajorFunction == IRP_MJ_WRITE)
128 {
129 /* Copy the buffer data */
130 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, Buffer, Length);
131 }
132 else
133 {
134 /* Set the Input Operation flag and set this as a User Buffer */
135 Irp->Flags |= IRP_INPUT_OPERATION;
136 Irp->UserBuffer = Buffer;
137 }
138 }
139 else if (DeviceObject->Flags & DO_DIRECT_IO)
140 {
141 /* Use an MDL for Direct I/O */
142 Irp->MdlAddress = MmCreateMdl(NULL, Buffer, Length);
143
144 /* Use the right Access Type */
145 if (MajorFunction == IRP_MJ_READ)
146 {
147 AccessType = IoReadAccess;
148 }
149 else
150 {
151 AccessType = IoWriteAccess;
152 }
153
154 /* Probe and Lock */
155 _SEH_FILTER(FreeAndGoOn)
156 {
157 /* Free the IRP and its MDL */
158 IoFreeMdl(Irp->MdlAddress);
159 IoFreeIrp(Irp);
160 return EXCEPTION_CONTINUE_SEARCH;
161 }
162 _SEH_TRY_FILTER(FreeAndGoOn)
163 {
164 /* Do the probe */
165 MmProbeAndLockPages(Irp->MdlAddress, KernelMode, AccessType);
166 }
167 _SEH_HANDLE
168 {
169 return NULL;
170 }
171 _SEH_END;
172 }
173 else
174 {
175 /* Neither, use the buffer */
176 Irp->UserBuffer = Buffer;
177 }
178
179 if (MajorFunction == IRP_MJ_READ)
180 {
181 StackPtr->Parameters.Read.Length = Length;
182 StackPtr->Parameters.Read.ByteOffset = *StartingOffset;
183 }
184 else if (MajorFunction == IRP_MJ_WRITE)
185 {
186 StackPtr->Parameters.Write.Length = Length;
187 StackPtr->Parameters.Write.ByteOffset = *StartingOffset;
188 }
189 }
190
191 /* Set the Current Thread and IOSB */
192 if (!IoStatusBlock) KEBUGCHECK(0); /* Temporary to catch illegal ROS Drivers */
193 Irp->UserIosb = IoStatusBlock;
194 Irp->Tail.Overlay.Thread = PsGetCurrentThread();
195
196 /* Set the Status Block after all is done */
197 return Irp;
198 }
199
200 /*
201 * @implemented
202 *
203 * FUNCTION: Allocates and sets up an IRP to be sent to drivers
204 * ARGUMENTS:
205 * IoControlCode = Device io control code
206 * DeviceObject = Device object to send the irp to
207 * InputBuffer = Buffer from which data will be read by the driver
208 * InputBufferLength = Length in bytes of the input buffer
209 * OutputBuffer = Buffer into which data will be written by the driver
210 * OutputBufferLength = Length in bytes of the output buffer
211 * InternalDeviceIoControl = Determines weather
212 * IRP_MJ_INTERNAL_DEVICE_CONTROL or
213 * IRP_MJ_DEVICE_CONTROL will be used
214 * Event = Event used to notify the caller of completion
215 * IoStatusBlock (OUT) = Storage for the result of the operation
216 * RETURNS: The IRP allocated on success, or
217 * NULL on failure
218 */
219 PIRP
220 STDCALL
221 IoBuildDeviceIoControlRequest(ULONG IoControlCode,
222 PDEVICE_OBJECT DeviceObject,
223 PVOID InputBuffer,
224 ULONG InputBufferLength,
225 PVOID OutputBuffer,
226 ULONG OutputBufferLength,
227 BOOLEAN InternalDeviceIoControl,
228 PKEVENT Event,
229 PIO_STATUS_BLOCK IoStatusBlock)
230 {
231 PIRP Irp;
232 PIO_STACK_LOCATION StackPtr;
233 ULONG BufferLength;
234 LOCK_OPERATION AccessType;
235
236 DPRINT("IoBuildDeviceIoRequest(IoControlCode %x, DeviceObject %x, "
237 "InputBuffer %x, InputBufferLength %x, OutputBuffer %x, "
238 "OutputBufferLength %x, InternalDeviceIoControl %x "
239 "Event %x, IoStatusBlock %x\n",IoControlCode,DeviceObject,
240 InputBuffer,InputBufferLength,OutputBuffer,OutputBufferLength,
241 InternalDeviceIoControl,Event,IoStatusBlock);
242
243 /* Allocate IRP */
244 if (!(Irp = IoAllocateIrp(DeviceObject->StackSize,TRUE))) return Irp;
245
246 /* Get the Stack */
247 StackPtr = IoGetNextIrpStackLocation(Irp);
248
249 /* Set the DevCtl Type */
250 StackPtr->MajorFunction = InternalDeviceIoControl ?
251 IRP_MJ_INTERNAL_DEVICE_CONTROL : IRP_MJ_DEVICE_CONTROL;
252
253 /* Set the IOCTL Data */
254 StackPtr->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
255 StackPtr->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
256 StackPtr->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
257
258 /* Handle the Methods */
259 switch (IO_METHOD_FROM_CTL_CODE(IoControlCode))
260 {
261 case METHOD_BUFFERED:
262 DPRINT("Using METHOD_BUFFERED!\n");
263
264 /* Select the right Buffer Length */
265 BufferLength = InputBufferLength > OutputBufferLength ? InputBufferLength : OutputBufferLength;
266
267 /* Make sure there is one */
268 if (BufferLength)
269 {
270 /* Allocate the System Buffer */
271 Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool,
272 BufferLength,
273 TAG_SYS_BUF);
274
275 /* Fail if we couldn't */
276 if (Irp->AssociatedIrp.SystemBuffer == NULL)
277 {
278 IoFreeIrp(Irp);
279 return(NULL);
280 }
281
282 /* Check if we got a buffer */
283 if (InputBuffer)
284 {
285 /* Copy into the System Buffer */
286 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
287 InputBuffer,
288 InputBufferLength);
289 }
290
291 /* Write the flags */
292 Irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
293 if (OutputBuffer) Irp->Flags |= IRP_INPUT_OPERATION;
294
295 /* Save the Buffer */
296 Irp->UserBuffer = OutputBuffer;
297 }
298 else
299 {
300 /* Clear the Flags and Buffer */
301 Irp->Flags = 0;
302 Irp->UserBuffer = NULL;
303 }
304 break;
305
306 case METHOD_IN_DIRECT:
307 case METHOD_OUT_DIRECT:
308 DPRINT("Using METHOD_IN/OUT DIRECT!\n");
309
310 /* Check if we got an input buffer */
311 if (InputBuffer)
312 {
313 /* Allocate the System Buffer */
314 Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPool,
315 InputBufferLength,
316 TAG_SYS_BUF);
317
318 /* Fail if we couldn't */
319 if (Irp->AssociatedIrp.SystemBuffer == NULL)
320 {
321 IoFreeIrp(Irp);
322 return(NULL);
323 }
324
325 /* Copy into the System Buffer */
326 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
327 InputBuffer,
328 InputBufferLength);
329
330 /* Write the flags */
331 Irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
332 }
333 else
334 {
335 Irp->Flags = 0;
336 }
337
338 /* Check if we got an output buffer */
339 if (OutputBuffer)
340 {
341 /* Allocate the System Buffer */
342 Irp->MdlAddress = IoAllocateMdl(OutputBuffer,
343 OutputBufferLength,
344 FALSE,
345 FALSE,
346 Irp);
347
348 /* Fail if we couldn't */
349 if (Irp->MdlAddress == NULL)
350 {
351 IoFreeIrp(Irp);
352 return NULL;
353 }
354
355 /* Probe and Lock */
356 _SEH_FILTER(FreeAndGoOn)
357 {
358 /* Free the MDL and IRP */
359 IoFreeMdl(Irp->MdlAddress);
360 IoFreeIrp(Irp);
361 return EXCEPTION_CONTINUE_SEARCH;
362 }
363 _SEH_TRY_FILTER(FreeAndGoOn)
364 {
365 /* Use the right Access Type */
366 if (IO_METHOD_FROM_CTL_CODE(IoControlCode) == METHOD_IN_DIRECT)
367 {
368 AccessType = IoReadAccess;
369 }
370 else
371 {
372 AccessType = IoWriteAccess;
373 }
374
375 /* Do the probe */
376 MmProbeAndLockPages(Irp->MdlAddress, KernelMode, AccessType);
377 }
378 _SEH_HANDLE
379 {
380 return NULL;
381 }
382 _SEH_END;
383 }
384 break;
385
386 case METHOD_NEITHER:
387
388 /* Just save the Buffer */
389 Irp->UserBuffer = OutputBuffer;
390 StackPtr->Parameters.DeviceIoControl.Type3InputBuffer = InputBuffer;
391 break;
392 }
393
394 /* Now write the Event and IoSB */
395 if (!IoStatusBlock) KEBUGCHECK(0); /* Temporary to catch illegal ROS Drivers */
396 Irp->UserIosb = IoStatusBlock;
397 Irp->UserEvent = Event;
398
399 /* Sync IRPs are queued to requestor thread's irp cancel/cleanup list */
400 Irp->Tail.Overlay.Thread = PsGetCurrentThread();
401 IoQueueThreadIrp(Irp);
402
403 /* Return the IRP */
404 return Irp;
405 }
406
407 /*
408 * @implemented
409 *
410 * FUNCTION: Allocates and builds an IRP to be sent synchronously to lower
411 * level driver(s)
412 * ARGUMENTS:
413 * MajorFunction = Major function code, one of IRP_MJ_READ,
414 * IRP_MJ_WRITE, IRP_MJ_FLUSH_BUFFERS, IRP_MJ_SHUTDOWN
415 * DeviceObject = Target device object
416 * Buffer = Buffer containing data for a read or write
417 * Length = Length in bytes of the information to be transferred
418 * StartingOffset = Offset to begin the read/write from
419 * Event (OUT) = Will be set when the operation is complete
420 * IoStatusBlock (OUT) = Set to the status of the operation
421 * RETURNS: The IRP allocated on success, or
422 * NULL on failure
423 */
424 PIRP
425 STDCALL
426 IoBuildSynchronousFsdRequest(ULONG MajorFunction,
427 PDEVICE_OBJECT DeviceObject,
428 PVOID Buffer,
429 ULONG Length,
430 PLARGE_INTEGER StartingOffset,
431 PKEVENT Event,
432 PIO_STATUS_BLOCK IoStatusBlock)
433 {
434 PIRP Irp;
435
436 DPRINT("IoBuildSynchronousFsdRequest(MajorFunction %x, DeviceObject %x, "
437 "Buffer %x, Length %x, StartingOffset %x, Event %x, "
438 "IoStatusBlock %x\n",MajorFunction,DeviceObject,Buffer,Length,
439 StartingOffset,Event,IoStatusBlock);
440
441 /* Do the big work to set up the IRP */
442 Irp = IoBuildAsynchronousFsdRequest(MajorFunction,
443 DeviceObject,
444 Buffer,
445 Length,
446 StartingOffset,
447 IoStatusBlock );
448
449 /* Set the Event which makes it Syncronous */
450 Irp->UserEvent = Event;
451
452 /* Sync IRPs are queued to requestor thread's irp cancel/cleanup list */
453 IoQueueThreadIrp(Irp);
454 return(Irp);
455 }
456
457 /*
458 * @implemented
459 */
460 BOOLEAN
461 STDCALL
462 IoCancelIrp(PIRP Irp)
463 {
464 KIRQL oldlvl;
465 PDRIVER_CANCEL CancelRoutine;
466
467 DPRINT("IoCancelIrp(Irp %x)\n",Irp);
468
469 IoAcquireCancelSpinLock(&oldlvl);
470
471 Irp->Cancel = TRUE;
472
473 CancelRoutine = IoSetCancelRoutine(Irp, NULL);
474 if (CancelRoutine == NULL)
475 {
476 IoReleaseCancelSpinLock(oldlvl);
477 return(FALSE);
478 }
479
480 Irp->CancelIrql = oldlvl;
481 CancelRoutine(IoGetCurrentIrpStackLocation(Irp)->DeviceObject, Irp);
482 return(TRUE);
483 }
484
485 /**
486 * @name IoCancelThreadIo
487 *
488 * Cancel all pending I/O request associated with specified thread.
489 *
490 * @param Thread
491 * Thread to cancel requests for.
492 */
493
494 VOID
495 STDCALL
496 IoCancelThreadIo(PETHREAD Thread)
497 {
498 PLIST_ENTRY IrpEntry;
499 PIRP Irp;
500 KIRQL OldIrql;
501 ULONG Retries = 3000;
502 LARGE_INTEGER Interval;
503
504 OldIrql = KfRaiseIrql(APC_LEVEL);
505
506 /*
507 * Start by cancelling all the IRPs in the current thread queue.
508 */
509
510 for (IrpEntry = Thread->IrpList.Flink;
511 IrpEntry != &Thread->IrpList;
512 IrpEntry = IrpEntry->Flink)
513 {
514 Irp = CONTAINING_RECORD(IrpEntry, IRP, ThreadListEntry);
515 IoCancelIrp(Irp);
516 }
517
518 /*
519 * Wait till all the IRPs are completed or cancelled.
520 */
521
522 while (!IsListEmpty(&Thread->IrpList))
523 {
524 KfLowerIrql(OldIrql);
525
526 /* Wait a short while and then look if all our IRPs were completed. */
527 Interval.QuadPart = -1000000; /* 100 milliseconds */
528 KeDelayExecutionThread(KernelMode, FALSE, &Interval);
529
530 /*
531 * Don't stay here forever if some broken driver doesn't complete
532 * the IRP.
533 */
534
535 if (Retries-- == 0)
536 {
537 /* FIXME: Handle this gracefully. */
538 DPRINT1("Thread with dead IRPs!");
539 ASSERT(FALSE);
540 }
541
542 OldIrql = KfRaiseIrql(APC_LEVEL);
543 }
544
545 KfLowerIrql(OldIrql);
546 }
547
548 #ifdef IoCallDriver
549 #undef IoCallDriver
550 #endif
551 /*
552 * @implemented
553 */
554 NTSTATUS
555 STDCALL
556 IoCallDriver (PDEVICE_OBJECT DeviceObject, PIRP Irp)
557 {
558 /* Call fast call */
559 return IofCallDriver(DeviceObject, Irp);
560 }
561
562 /*
563 * @implemented
564 */
565 VOID
566 STDCALL
567 IoCompleteRequest(PIRP Irp,
568 CCHAR PriorityBoost)
569 {
570 /* Call the fastcall */
571 IofCompleteRequest(Irp, PriorityBoost);
572 }
573
574 /*
575 * @implemented
576 */
577 VOID
578 STDCALL
579 IoEnqueueIrp(IN PIRP Irp)
580 {
581 IoQueueThreadIrp(Irp);
582 }
583
584 /*
585 * @implemented
586 *
587 * FUNCTION: Sends an IRP to the next lower driver
588 */
589 NTSTATUS
590 FASTCALL
591 IofCallDriver(PDEVICE_OBJECT DeviceObject,
592 PIRP Irp)
593
594 {
595 PDRIVER_OBJECT DriverObject;
596 PIO_STACK_LOCATION Param;
597
598 DPRINT("IofCallDriver(DeviceObject %x, Irp %x)\n",DeviceObject,Irp);
599
600 /* Get the Driver Object */
601 DriverObject = DeviceObject->DriverObject;
602
603 /* Set the Stack Location */
604 IoSetNextIrpStackLocation(Irp);
605
606 /* Get the current one */
607 Param = IoGetCurrentIrpStackLocation(Irp);
608
609 DPRINT("IrpSp 0x%X\n", Param);
610
611 /* Get the Device Object */
612 Param->DeviceObject = DeviceObject;
613
614 /* Call it */
615 return DriverObject->MajorFunction[Param->MajorFunction](DeviceObject, Irp);
616 }
617
618 #ifdef IoCompleteRequest
619 #undef IoCompleteRequest
620 #endif
621 /*
622 * @implemented
623 */
624 VOID FASTCALL
625 IofCompleteRequest(PIRP Irp,
626 CCHAR PriorityBoost)
627 /*
628 * FUNCTION: Indicates the caller has finished all processing for a given
629 * I/O request and is returning the given IRP to the I/O manager
630 * ARGUMENTS:
631 * Irp = Irp to be cancelled
632 * PriorityBoost = Increment by which to boost the priority of the
633 * thread making the request
634 */
635 {
636 ULONG i;
637 NTSTATUS Status;
638 PFILE_OBJECT OriginalFileObject;
639 PDEVICE_OBJECT DeviceObject;
640 KIRQL oldIrql;
641 PMDL Mdl;
642 PIO_STACK_LOCATION Stack = (PIO_STACK_LOCATION)(Irp + 1);
643
644 DPRINT("IoCompleteRequest(Irp %x, PriorityBoost %d) Event %x THread %x\n",
645 Irp,PriorityBoost, Irp->UserEvent, PsGetCurrentThread());
646
647 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
648 ASSERT(Irp->CancelRoutine == NULL);
649 ASSERT(Irp->IoStatus.Status != STATUS_PENDING);
650
651 Irp->PendingReturned = IoGetCurrentIrpStackLocation(Irp)->Control & SL_PENDING_RETURNED;
652
653 /*
654 * Run the completion routines.
655 */
656
657 for (i=Irp->CurrentLocation;i<(ULONG)Irp->StackCount;i++)
658 {
659 /*
660 Completion routines expect the current irp stack location to be the same as when
661 IoSetCompletionRoutine was called to set them. A side effect is that completion
662 routines set by highest level drivers without their own stack location will receive
663 an invalid current stack location (at least it should be considered as invalid).
664 Since the DeviceObject argument passed is taken from the current stack, this value
665 is also invalid (NULL).
666 */
667 if (Irp->CurrentLocation < Irp->StackCount - 1)
668 {
669 IoSkipCurrentIrpStackLocation(Irp);
670 DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
671 }
672 else
673 {
674 DeviceObject = NULL;
675 }
676
677 if (Stack[i].CompletionRoutine != NULL &&
678 ((NT_SUCCESS(Irp->IoStatus.Status) && (Stack[i].Control & SL_INVOKE_ON_SUCCESS)) ||
679 (!NT_SUCCESS(Irp->IoStatus.Status) && (Stack[i].Control & SL_INVOKE_ON_ERROR)) ||
680 (Irp->Cancel && (Stack[i].Control & SL_INVOKE_ON_CANCEL))))
681 {
682 Status = Stack[i].CompletionRoutine(DeviceObject,
683 Irp,
684 Stack[i].Context);
685
686 if (Status == STATUS_MORE_PROCESSING_REQUIRED)
687 {
688 return;
689 }
690 }
691
692 if (IoGetCurrentIrpStackLocation(Irp)->Control & SL_PENDING_RETURNED)
693 {
694 Irp->PendingReturned = TRUE;
695 }
696 }
697
698 /* Windows NT File System Internals, page 165 */
699 if (Irp->Flags & IRP_ASSOCIATED_IRP)
700 {
701 ULONG MasterIrpCount;
702 PIRP MasterIrp = Irp->AssociatedIrp.MasterIrp;
703
704 /* This should never happen! */
705 ASSERT(IsListEmpty(&Irp->ThreadListEntry));
706
707 MasterIrpCount = InterlockedDecrement(&MasterIrp->AssociatedIrp.IrpCount);
708 while ((Mdl = Irp->MdlAddress))
709 {
710 Irp->MdlAddress = Mdl->Next;
711 IoFreeMdl(Mdl);
712 }
713 IoFreeIrp(Irp);
714 if (MasterIrpCount == 0)
715 {
716 IofCompleteRequest(MasterIrp, IO_NO_INCREMENT);
717 }
718 return;
719 }
720
721 /*
722 * Were done calling completion routines. Now do any cleanup that can be
723 * done in an arbitrarily context.
724 */
725
726 /* Windows NT File System Internals, page 165 */
727 if (Irp->Flags & (IRP_PAGING_IO|IRP_CLOSE_OPERATION))
728 {
729 /* This should never happen! */
730 ASSERT(IsListEmpty(&Irp->ThreadListEntry));
731
732 /*
733 * If MDL_IO_PAGE_READ is set, then the caller is responsible
734 * for deallocating of the mdl.
735 */
736 if (Irp->Flags & IRP_PAGING_IO &&
737 Irp->MdlAddress &&
738 !(Irp->MdlAddress->MdlFlags & MDL_IO_PAGE_READ))
739 {
740
741 if (Irp->MdlAddress->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
742 {
743 MmUnmapLockedPages(Irp->MdlAddress->MappedSystemVa, Irp->MdlAddress);
744 }
745
746 ExFreePool(Irp->MdlAddress);
747 }
748
749 if (Irp->UserIosb)
750 {
751 _SEH_TRY
752 {
753 *Irp->UserIosb = Irp->IoStatus;
754 }
755 _SEH_HANDLE
756 {
757 DPRINT1("Unable to set UserIosb (at 0x%x) to 0x%x, Error: 0x%x\n",
758 Irp->UserIosb, Irp->IoStatus, _SEH_GetExceptionCode());
759 }
760 _SEH_END;
761 }
762
763 if (Irp->UserEvent)
764 {
765 KeSetEvent(Irp->UserEvent, PriorityBoost, FALSE);
766 }
767
768 /* io manager frees the irp for close operations */
769 // if (Irp->Flags & IRP_PAGING_IO)
770 // {
771 IoFreeIrp(Irp);
772 // }
773
774 return;
775 }
776
777
778 /*
779 Hi Dave,
780
781 I went through my old notes. You are correct and in most cases
782 IoCompleteRequest() will issue an MmUnlockPages() for each MDL in the IRP
783 chain. There are however few exceptions: one is MDLs for associated IRPs,
784 it's expected that those MDLs have been initialized with
785 IoBuildPartialMdl(). Another exception is PAGING_IO irps, the i/o completion
786 code doesn't do anything to MDLs of those IRPs.
787
788 sara
789
790 */
791
792
793 for (Mdl = Irp->MdlAddress; Mdl; Mdl = Mdl->Next)
794 {
795 /*
796 * Undo the MmProbeAndLockPages. If MmGetSystemAddressForMdl was called
797 * on this mdl, this mapping (if any) is also undone by MmUnlockPages.
798 */
799 MmUnlockPages(Mdl);
800 }
801
802 //Windows NT File System Internals, page 154
803 OriginalFileObject = Irp->Tail.Overlay.OriginalFileObject;
804
805 if (Irp->PendingReturned || KeGetCurrentIrql() == DISPATCH_LEVEL)
806 {
807 BOOLEAN bStatus;
808
809 DPRINT("Dispatching APC\n");
810
811 KeInitializeApc( &Irp->Tail.Apc,
812 &Irp->Tail.Overlay.Thread->Tcb,
813 Irp->ApcEnvironment,
814 IoSecondStageCompletion,//kernel routine
815 NULL,
816 (PKNORMAL_ROUTINE) NULL,
817 KernelMode,
818 NULL);
819
820 bStatus = KeInsertQueueApc(&Irp->Tail.Apc,
821 (PVOID)OriginalFileObject,
822 NULL, // This is used for REPARSE stuff
823 PriorityBoost);
824
825 if (bStatus == FALSE)
826 {
827 DPRINT1("Error queueing APC for thread. Thread has probably exited.\n");
828 }
829
830 DPRINT("Finished dispatching APC\n");
831 }
832 else
833 {
834 DPRINT("Calling IoSecondStageCompletion routine directly\n");
835 KeRaiseIrql(APC_LEVEL, &oldIrql);
836 IoSecondStageCompletion(&Irp->Tail.Apc,NULL,NULL,(PVOID)&OriginalFileObject, NULL);
837 KeLowerIrql(oldIrql);
838 DPRINT("Finished completition routine\n");
839 }
840 }
841
842 /*
843 * @unimplemented
844 */
845 BOOLEAN
846 STDCALL
847 IoForwardIrpSynchronously(IN PDEVICE_OBJECT DeviceObject,
848 IN PIRP Irp)
849 {
850 UNIMPLEMENTED;
851 return FALSE;
852 }
853
854 /*
855 * @implemented
856 *
857 * FUNCTION: Releases a caller allocated irp
858 * ARGUMENTS:
859 * Irp = Irp to free
860 */
861 VOID
862 STDCALL
863 IoFreeIrp(PIRP Irp)
864 {
865 /* Free the pool memory associated with it */
866 ExFreePool(Irp);
867 }
868
869 /*
870 * @implemented
871 */
872 PEPROCESS STDCALL
873 IoGetRequestorProcess(IN PIRP Irp)
874 {
875 return(Irp->Tail.Overlay.Thread->ThreadsProcess);
876 }
877
878 /*
879 * @implemented
880 */
881 ULONG
882 STDCALL
883 IoGetRequestorProcessId(IN PIRP Irp)
884 {
885 return (ULONG)(IoGetRequestorProcess(Irp)->UniqueProcessId);
886 }
887
888 /*
889 * @implemented
890 */
891 NTSTATUS
892 STDCALL
893 IoGetRequestorSessionId(IN PIRP Irp,
894 OUT PULONG pSessionId)
895 {
896 *pSessionId = IoGetRequestorProcess(Irp)->SessionId;
897
898 return STATUS_SUCCESS;
899 }
900
901 /*
902 * @implemented
903 */
904 PIRP
905 STDCALL
906 IoGetTopLevelIrp(VOID)
907 {
908 return(PsGetCurrentThread()->TopLevelIrp);
909 }
910
911 /*
912 * @implemented
913 *
914 * FUNCTION: Initalizes an irp allocated by the caller
915 * ARGUMENTS:
916 * Irp = IRP to initalize
917 * PacketSize = Size in bytes of the IRP
918 * StackSize = Number of stack locations in the IRP
919 */
920 VOID
921 STDCALL
922 IoInitializeIrp(PIRP Irp,
923 USHORT PacketSize,
924 CCHAR StackSize)
925 {
926 ASSERT(Irp != NULL);
927
928 DPRINT("IoInitializeIrp(StackSize %x, Irp %x)\n",StackSize, Irp);
929
930 /* Clear it */
931 RtlZeroMemory(Irp, PacketSize);
932
933 /* Set the Header and other data */
934 Irp->Type = IO_TYPE_IRP;
935 Irp->Size = PacketSize;
936 Irp->StackCount = StackSize;
937 Irp->CurrentLocation = StackSize;
938 Irp->ApcEnvironment = KeGetCurrentThread()->ApcStateIndex;
939 Irp->Tail.Overlay.CurrentStackLocation = (PIO_STACK_LOCATION)(Irp + 1) + StackSize;
940
941 /* Initialize the Thread List */
942 InitializeListHead(&Irp->ThreadListEntry);
943
944 DPRINT("Irp->Tail.Overlay.CurrentStackLocation %x\n", Irp->Tail.Overlay.CurrentStackLocation);
945 }
946
947 /*
948 * NAME EXPORTED
949 * IoIsOperationSynchronous@4
950 *
951 * DESCRIPTION
952 * Check if the I/O operation associated with the given IRP
953 * is synchronous.
954 *
955 * ARGUMENTS
956 * Irp Packet to check.
957 *
958 * RETURN VALUE
959 * TRUE if Irp's operation is synchronous; otherwise FALSE.
960 *
961 * @implemented
962 */
963 BOOLEAN
964 STDCALL
965 IoIsOperationSynchronous(IN PIRP Irp)
966 {
967 /* Check the flags */
968 if ((Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO) ||
969 (Irp->Flags & IRP_SYNCHRONOUS_API) ||
970 (IoGetCurrentIrpStackLocation(Irp)->FileObject->Flags &
971 FO_SYNCHRONOUS_IO))
972 {
973 /* Synch API or Paging I/O is OK, as is Sync File I/O */
974 return TRUE;
975 }
976
977 /* Otherwise, it is an asynchronous operation. */
978 return FALSE;
979 }
980
981 /*
982 * @unimplemented
983 */
984 BOOLEAN
985 STDCALL
986 IoIsValidNameGraftingBuffer(IN PIRP Irp,
987 IN PREPARSE_DATA_BUFFER ReparseBuffer)
988 {
989 UNIMPLEMENTED;
990 return FALSE;
991 }
992
993 /*
994 * @implemented
995 *
996 * FUNCTION: Allocates and initializes an irp to associated with a master irp
997 * ARGUMENTS:
998 * Irp = Master irp
999 * StackSize = Number of stack locations to be allocated in the irp
1000 * RETURNS: The irp allocated
1001 * NOTE: The caller is responsible for incrementing
1002 * Irp->AssociatedIrp.IrpCount.
1003 */
1004 PIRP
1005 STDCALL
1006 IoMakeAssociatedIrp(PIRP Irp,
1007 CCHAR StackSize)
1008 {
1009 PIRP AssocIrp;
1010
1011 /* Allocate the IRP */
1012 AssocIrp = IoAllocateIrp(StackSize,FALSE);
1013 if (AssocIrp == NULL) return NULL;
1014
1015 /* Set the Flags */
1016 AssocIrp->Flags |= IRP_ASSOCIATED_IRP;
1017
1018 /* Set the Thread */
1019 AssocIrp->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
1020
1021 /* Associate them */
1022 AssocIrp->AssociatedIrp.MasterIrp = Irp;
1023
1024 return AssocIrp;
1025 }
1026
1027 /*
1028 * @implemented
1029 */
1030 NTSTATUS
1031 STDCALL
1032 IoPageRead(PFILE_OBJECT FileObject,
1033 PMDL Mdl,
1034 PLARGE_INTEGER Offset,
1035 PKEVENT Event,
1036 PIO_STATUS_BLOCK StatusBlock)
1037 {
1038 PIRP Irp;
1039 PIO_STACK_LOCATION StackPtr;
1040 PDEVICE_OBJECT DeviceObject;
1041
1042 DPRINT("IoPageRead(FileObject %x, Mdl %x)\n",
1043 FileObject, Mdl);
1044
1045 /* Get the Device Object */
1046 DeviceObject = IoGetRelatedDeviceObject(FileObject);
1047
1048 /* Allocate IRP */
1049 Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
1050
1051 /* Get the Stack */
1052 StackPtr = IoGetNextIrpStackLocation(Irp);
1053
1054 /* Create the IRP Settings */
1055 Irp->MdlAddress = Mdl;
1056 Irp->UserBuffer = MmGetMdlVirtualAddress(Mdl);
1057 Irp->UserIosb = StatusBlock;
1058 Irp->UserEvent = Event;
1059 Irp->RequestorMode = KernelMode;
1060 Irp->Flags = IRP_PAGING_IO | IRP_NOCACHE | IRP_SYNCHRONOUS_PAGING_IO | IRP_INPUT_OPERATION;
1061 Irp->Tail.Overlay.OriginalFileObject = FileObject;
1062 Irp->Tail.Overlay.Thread = PsGetCurrentThread();
1063
1064 /* Set the Stack Settings */
1065 StackPtr->Parameters.Read.Length = MmGetMdlByteCount(Mdl);
1066 StackPtr->Parameters.Read.ByteOffset = *Offset;
1067 StackPtr->MajorFunction = IRP_MJ_READ;
1068 StackPtr->FileObject = FileObject;
1069
1070 /* Call the Driver */
1071 return IofCallDriver(DeviceObject, Irp);
1072 }
1073
1074 /*
1075 * @implemented
1076 */
1077 VOID
1078 STDCALL
1079 IoQueueThreadIrp(IN PIRP Irp)
1080 {
1081 KIRQL OldIrql;
1082
1083 /* Raise to APC */
1084 OldIrql = KfRaiseIrql(APC_LEVEL);
1085
1086 /*
1087 * Synchronous irp's are queued to requestor thread. If they are not
1088 * completed when the thread exits, they are canceled (cleaned up).
1089 * - Gunnar
1090 */
1091 InsertTailList(&Irp->Tail.Overlay.Thread->IrpList, &Irp->ThreadListEntry);
1092
1093 /* Lower back */
1094 KfLowerIrql(OldIrql);
1095 }
1096
1097 /*
1098 * @implemented
1099 * Reference: Chris Cant's "Writing WDM Device Drivers"
1100 */
1101 VOID
1102 STDCALL
1103 IoReuseIrp(IN OUT PIRP Irp,
1104 IN NTSTATUS Status)
1105 {
1106 UCHAR AllocationFlags;
1107
1108 /* Get the old flags */
1109 AllocationFlags = Irp->AllocationFlags;
1110
1111 /* Reinitialize the IRP */
1112 IoInitializeIrp(Irp, Irp->Size, Irp->StackCount);
1113
1114 /* Duplicate the data */
1115 Irp->IoStatus.Status = Status;
1116 Irp->AllocationFlags = AllocationFlags;
1117 }
1118
1119 /*
1120 * @implemented
1121 */
1122 VOID
1123 STDCALL
1124 IoSetTopLevelIrp(IN PIRP Irp)
1125 {
1126 PsGetCurrentThread()->TopLevelIrp = Irp;
1127 }
1128
1129 /*
1130 * @implemented
1131 */
1132 NTSTATUS
1133 STDCALL
1134 IoSynchronousPageWrite(PFILE_OBJECT FileObject,
1135 PMDL Mdl,
1136 PLARGE_INTEGER Offset,
1137 PKEVENT Event,
1138 PIO_STATUS_BLOCK StatusBlock)
1139 {
1140 PIRP Irp;
1141 PIO_STACK_LOCATION StackPtr;
1142 PDEVICE_OBJECT DeviceObject;
1143
1144 DPRINT("IoSynchronousPageWrite(FileObject %x, Mdl %x)\n",
1145 FileObject, Mdl);
1146
1147 /* Get the Device Object */
1148 DeviceObject = IoGetRelatedDeviceObject(FileObject);
1149
1150 /* Allocate IRP */
1151 Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
1152
1153 /* Get the Stack */
1154 StackPtr = IoGetNextIrpStackLocation(Irp);
1155
1156 /* Create the IRP Settings */
1157 Irp->MdlAddress = Mdl;
1158 Irp->UserBuffer = MmGetMdlVirtualAddress(Mdl);
1159 Irp->UserIosb = StatusBlock;
1160 Irp->UserEvent = Event;
1161 Irp->RequestorMode = KernelMode;
1162 Irp->Flags = IRP_PAGING_IO | IRP_NOCACHE | IRP_SYNCHRONOUS_PAGING_IO;
1163 Irp->Tail.Overlay.OriginalFileObject = FileObject;
1164 Irp->Tail.Overlay.Thread = PsGetCurrentThread();
1165
1166 /* Set the Stack Settings */
1167 StackPtr->Parameters.Write.Length = MmGetMdlByteCount(Mdl);
1168 StackPtr->Parameters.Write.ByteOffset = *Offset;
1169 StackPtr->MajorFunction = IRP_MJ_WRITE;
1170 StackPtr->FileObject = FileObject;
1171
1172 /* Call the Driver */
1173 return IofCallDriver(DeviceObject, Irp);
1174 }
1175
1176 VOID
1177 STDCALL
1178 IoSecondStageCompletion_KernelApcRoutine(PKAPC Apc,
1179 PKNORMAL_ROUTINE *NormalRoutine,
1180 PVOID *NormalContext,
1181 PVOID *SystemArgument1,
1182 PVOID *SystemArgument2)
1183 {
1184 /* Free the IRP */
1185 IoFreeIrp(CONTAINING_RECORD(Apc, IRP, Tail.Apc));
1186 }
1187
1188 VOID
1189 STDCALL
1190 IoSecondStageCompletion_RundownApcRoutine(PKAPC Apc)
1191 {
1192 /* Free the IRP */
1193 IoFreeIrp(CONTAINING_RECORD(Apc, IRP, Tail.Apc));
1194 }
1195
1196 /*
1197 * FUNCTION: Performs the second stage of irp completion for read/write irps
1198 *
1199 * Called as a special kernel APC kernel-routine or directly from IofCompleteRequest()
1200 *
1201 * Note that we'll never see irp's flagged IRP_PAGING_IO (IRP_MOUNT_OPERATION)
1202 * or IRP_CLOSE_OPERATION (IRP_MJ_CLOSE and IRP_MJ_CLEANUP) here since their
1203 * cleanup/completion is fully taken care of in IoCompleteRequest.
1204 * -Gunnar
1205 */
1206 VOID
1207 STDCALL
1208 IoSecondStageCompletion(PKAPC Apc,
1209 PKNORMAL_ROUTINE* NormalRoutine,
1210 PVOID* NormalContext,
1211 PVOID* SystemArgument1,
1212 PVOID* SystemArgument2)
1213 {
1214 PFILE_OBJECT FileObject;
1215 PIRP Irp;
1216 PMDL Mdl, NextMdl;
1217
1218 if (Apc) DPRINT("IoSecondStageCompletition with APC: %x\n", Apc);
1219
1220 /* Get data from the APC */
1221 FileObject = (PFILE_OBJECT)(*SystemArgument1);
1222 Irp = CONTAINING_RECORD(Apc, IRP, Tail.Apc);
1223 DPRINT("IoSecondStageCompletition, %x\n", Irp);
1224
1225 /* Handle Buffered case first */
1226 if (Irp->Flags & IRP_BUFFERED_IO)
1227 {
1228 /* Check if we have an input buffer and if we suceeded */
1229 if (Irp->Flags & IRP_INPUT_OPERATION && NT_SUCCESS(Irp->IoStatus.Status))
1230 {
1231 /* Copy the buffer back to the user */
1232 RtlCopyMemory(Irp->UserBuffer,
1233 Irp->AssociatedIrp.SystemBuffer,
1234 Irp->IoStatus.Information);
1235 }
1236
1237 /* Also check if we should de-allocate it */
1238 if (Irp->Flags & IRP_DEALLOCATE_BUFFER)
1239 {
1240 ExFreePool(Irp->AssociatedIrp.SystemBuffer);
1241 }
1242 }
1243
1244 /* Now we got rid of these two... */
1245 Irp->Flags &= ~(IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER);
1246
1247 /* Check if there's an MDL */
1248 if ((Mdl = Irp->MdlAddress))
1249 {
1250 /* Clear all of them */
1251 do
1252 {
1253 NextMdl = Mdl->Next;
1254 IoFreeMdl(Mdl);
1255 Mdl = NextMdl;
1256 } while (Mdl);
1257 }
1258 Irp->MdlAddress = NULL;
1259
1260 /* Check for Success but allow failure for Async IRPs */
1261 if (NT_SUCCESS(Irp->IoStatus.Status) ||
1262 (Irp->PendingReturned &&
1263 !(Irp->Flags & IRP_SYNCHRONOUS_API) &&
1264 (FileObject == NULL || FileObject->Flags & FO_SYNCHRONOUS_IO)))
1265 {
1266 /* Save the IOSB Information */
1267 *Irp->UserIosb = Irp->IoStatus;
1268
1269 /* Check if there's an event */
1270 if (Irp->UserEvent)
1271 {
1272 /* Signal the Event */
1273 KeSetEvent(Irp->UserEvent, 0, FALSE);
1274
1275 }
1276 else if (FileObject)
1277 {
1278 /* Signal the File Object Instead */
1279 KeSetEvent(&FileObject->Event, 0, FALSE);
1280
1281 /* Set the Status */
1282 FileObject->FinalStatus = Irp->IoStatus.Status;
1283 }
1284
1285 /* Check if there's a File Object */
1286 if (FileObject)
1287 {
1288 /* Dereference the Event if this is an ASYNC IRP */
1289 if (!Irp->Flags & IRP_SYNCHRONOUS_API)
1290 {
1291 if (Irp->UserEvent != &FileObject->Event)
1292 {
1293 ObDereferenceObject(Irp->UserEvent);
1294 }
1295 }
1296
1297 /* If the File Object is SYNC, then we need to signal its event too */
1298 if (FileObject->Flags & FO_SYNCHRONOUS_IO)
1299 {
1300 /* Signal Event */
1301
1302 KeSetEvent(&FileObject->Event, 0, FALSE);
1303
1304 /* Set the Status */
1305 FileObject->FinalStatus = Irp->IoStatus.Status;
1306 }
1307 }
1308
1309 /* Remove the IRP from the list of Thread Pending IRPs */
1310 RemoveEntryList(&Irp->ThreadListEntry);
1311 InitializeListHead(&Irp->ThreadListEntry);
1312
1313 /* Now call the User APC if one was requested */
1314 if (Irp->Overlay.AsynchronousParameters.UserApcRoutine != NULL)
1315 {
1316 KeInitializeApc(&Irp->Tail.Apc,
1317 KeGetCurrentThread(),
1318 CurrentApcEnvironment,
1319 IoSecondStageCompletion_KernelApcRoutine,
1320 IoSecondStageCompletion_RundownApcRoutine,
1321 (PKNORMAL_ROUTINE)Irp->Overlay.AsynchronousParameters.UserApcRoutine,
1322 Irp->RequestorMode,
1323 Irp->Overlay.AsynchronousParameters.UserApcContext);
1324
1325 KeInsertQueueApc(&Irp->Tail.Apc,
1326 Irp->UserIosb,
1327 NULL,
1328 2);
1329 }
1330 else if (FileObject && FileObject->CompletionContext)
1331 {
1332 /* Call the IO Completion Port if we have one, instead */
1333 IoSetIoCompletion(FileObject->CompletionContext->Port,
1334 FileObject->CompletionContext->Key,
1335 Irp->Overlay.AsynchronousParameters.UserApcContext,
1336 Irp->IoStatus.Status,
1337 Irp->IoStatus.Information,
1338 FALSE);
1339 }
1340 else
1341 {
1342 /* Don't have anything, free it */
1343 IoFreeIrp(Irp);
1344 }
1345
1346 /* Dereference the File Object */
1347 if (FileObject) ObDereferenceObject(FileObject);
1348 }
1349 else
1350 {
1351 /* Remove the IRP from the list of Thread Pending IRPs */
1352 RemoveEntryList(&Irp->ThreadListEntry);
1353 InitializeListHead(&Irp->ThreadListEntry);
1354
1355 /* Signal the Events only if PendingReturned and we have a File Object */
1356 if (FileObject && Irp->PendingReturned)
1357 {
1358 /* Check for SYNC IRP */
1359 if (Irp->Flags & IRP_SYNCHRONOUS_API)
1360 {
1361 /* Set the status in this case only */
1362 *Irp->UserIosb = Irp->IoStatus;
1363
1364 /* Signal our event if we have one */
1365 if (Irp->UserEvent)
1366 {
1367 KeSetEvent(Irp->UserEvent, 0, FALSE);
1368 }
1369 else
1370 {
1371 /* Signal the File's Event instead */
1372 KeSetEvent(&FileObject->Event, 0, FALSE);
1373 }
1374 }
1375 else
1376 {
1377 /* We'll report the Status to the File Object, not the IRP */
1378 FileObject->FinalStatus = Irp->IoStatus.Status;
1379
1380 /* Signal the File Object ONLY if this was Async */
1381 KeSetEvent(&FileObject->Event, 0, FALSE);
1382 }
1383 }
1384
1385 /* Dereference the Event if it's an ASYNC IRP on a File Object */
1386 if (Irp->UserEvent && !(Irp->Flags & IRP_SYNCHRONOUS_API) && FileObject)
1387 {
1388 if (Irp->UserEvent != &FileObject->Event)
1389 {
1390 ObDereferenceObject(Irp->UserEvent);
1391 }
1392 }
1393
1394 /* Dereference the File Object */
1395 if (FileObject) ObDereferenceObject(FileObject);
1396
1397 /* Free the IRP */
1398 IoFreeIrp(Irp);
1399 }
1400 }
1401
1402 /* EOF */