Use upper-case ASSERT macros.
[reactos.git] / reactos / ntoskrnl / io / irp.c
1 /* $Id: irp.c,v 1.68 2004/10/22 20:25:53 ekohl Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/io/irp.c
6 * PURPOSE: Handle IRPs
7 * PROGRAMMER: David Welch (welch@mcmail.com)
8 * UPDATE HISTORY:
9 * 24/05/98: Created
10 */
11
12 /* NOTES *******************************************************************
13 *
14 * Layout of an IRP
15 *
16 * ################
17 * # Headers #
18 * ################
19 * # #
20 * # Variable #
21 * # length list #
22 * # of io stack #
23 * # locations #
24 * # #
25 * ################
26 *
27 *
28 *
29 */
30
31 /* INCLUDES ****************************************************************/
32
33 #include <ntoskrnl.h>
34 #define NDEBUG
35 #include <internal/debug.h>
36
37 /* GLOBALS *******************************************************************/
38
39 #define TAG_IRP TAG('I', 'R', 'P', ' ')
40
41
42 /* FUNCTIONS ****************************************************************/
43
44 /*
45 * @unimplemented
46 */
47 BOOLEAN
48 STDCALL
49 IoForwardIrpSynchronously(
50 IN PDEVICE_OBJECT DeviceObject,
51 IN PIRP Irp
52 )
53 {
54 UNIMPLEMENTED;
55 return FALSE;
56 }
57
58 /*
59 * @implemented
60 */
61 VOID STDCALL
62 IoFreeIrp(PIRP Irp)
63 /*
64 * FUNCTION: Releases a caller allocated irp
65 * ARGUMENTS:
66 * Irp = Irp to free
67 */
68 {
69 ExFreePool(Irp);
70 }
71
72 /*
73 * @unimplemented
74 */
75 ULONG
76 STDCALL
77 IoGetRequestorProcessId(
78 IN PIRP Irp
79 )
80 {
81 UNIMPLEMENTED;
82 return 0;
83 }
84
85 /*
86 * @unimplemented
87 */
88 NTSTATUS
89 STDCALL
90 IoGetRequestorSessionId(
91 IN PIRP Irp,
92 OUT PULONG pSessionId
93 )
94 {
95 UNIMPLEMENTED;
96 return STATUS_NOT_IMPLEMENTED;
97 }
98
99
100 /*
101 * @unimplemented
102 */
103 BOOLEAN
104 STDCALL
105 IoIsValidNameGraftingBuffer(
106 IN PIRP Irp,
107 IN PREPARSE_DATA_BUFFER ReparseBuffer
108 )
109 {
110 UNIMPLEMENTED;
111 return FALSE;
112 }
113
114 /*
115 * @implemented
116 */
117 PIRP STDCALL
118 IoMakeAssociatedIrp(PIRP Irp,
119 CCHAR StackSize)
120 /*
121 * FUNCTION: Allocates and initializes an irp to associated with a master irp
122 * ARGUMENTS:
123 * Irp = Master irp
124 * StackSize = Number of stack locations to be allocated in the irp
125 * RETURNS: The irp allocated
126 * NOTE: The caller is responsible for incrementing
127 * Irp->AssociatedIrp.IrpCount.
128 */
129 {
130 PIRP AssocIrp;
131
132 /* Allocate the IRP */
133 AssocIrp = IoAllocateIrp(StackSize,FALSE);
134 if (AssocIrp == NULL)
135 return NULL;
136
137 /* Set the Flags */
138 AssocIrp->Flags |= IRP_ASSOCIATED_IRP;
139
140 /* Set the Thread */
141 AssocIrp->Tail.Overlay.Thread = Irp->Tail.Overlay.Thread;
142
143 /* Associate them */
144 AssocIrp->AssociatedIrp.MasterIrp = Irp;
145
146 return AssocIrp;
147 }
148
149
150 /*
151 * @implemented
152 */
153 VOID STDCALL
154 IoInitializeIrp(PIRP Irp,
155 USHORT PacketSize,
156 CCHAR StackSize)
157 /*
158 * FUNCTION: Initalizes an irp allocated by the caller
159 * ARGUMENTS:
160 * Irp = IRP to initalize
161 * PacketSize = Size in bytes of the IRP
162 * StackSize = Number of stack locations in the IRP
163 */
164 {
165 ASSERT(Irp != NULL);
166
167 memset(Irp, 0, PacketSize);
168 Irp->Size = PacketSize;
169 Irp->StackCount = StackSize;
170 Irp->CurrentLocation = StackSize;
171 InitializeListHead(&Irp->ThreadListEntry);
172 Irp->Tail.Overlay.CurrentStackLocation = &Irp->Stack[(ULONG)StackSize];
173 Irp->ApcEnvironment = KeGetCurrentThread()->ApcStateIndex;
174 }
175
176
177 /*
178 * @implemented
179 */
180 NTSTATUS FASTCALL
181 IofCallDriver(PDEVICE_OBJECT DeviceObject,
182 PIRP Irp)
183 /*
184 * FUNCTION: Sends an IRP to the next lower driver
185 */
186 {
187 PDRIVER_OBJECT DriverObject;
188 PIO_STACK_LOCATION Param;
189
190 DPRINT("IofCallDriver(DeviceObject %x, Irp %x)\n",DeviceObject,Irp);
191
192 ASSERT(Irp);
193 ASSERT(DeviceObject);
194
195 DriverObject = DeviceObject->DriverObject;
196
197 ASSERT(DriverObject);
198
199 IoSetNextIrpStackLocation(Irp);
200 Param = IoGetCurrentIrpStackLocation(Irp);
201
202 DPRINT("IrpSp 0x%X\n", Param);
203
204 Param->DeviceObject = DeviceObject;
205
206 DPRINT("MajorFunction %d\n", Param->MajorFunction);
207 DPRINT("DriverObject->MajorFunction[Param->MajorFunction] %x\n",
208 DriverObject->MajorFunction[Param->MajorFunction]);
209
210 return DriverObject->MajorFunction[Param->MajorFunction](DeviceObject, Irp);
211 }
212
213
214 /*
215 * @implemented
216 */
217 NTSTATUS
218 STDCALL
219 IoCallDriver (PDEVICE_OBJECT DeviceObject, PIRP Irp)
220 {
221 return(IofCallDriver(DeviceObject,
222 Irp));
223 }
224
225
226 /*
227 * @implemented
228 */
229 PIRP STDCALL
230 IoAllocateIrp(CCHAR StackSize,
231 BOOLEAN ChargeQuota)
232 /*
233 * FUNCTION: Allocates an IRP
234 * ARGUMENTS:
235 * StackSize = the size of the stack required for the irp
236 * ChargeQuota = Charge allocation to current threads quota
237 * RETURNS: Irp allocated
238 */
239 {
240 PIRP Irp;
241
242 #if 0
243 DbgPrint("IoAllocateIrp(StackSize %d ChargeQuota %d)\n",
244 StackSize,
245 ChargeQuota);
246 KeDumpStackFrames(0,8);
247 #endif
248
249 if (ChargeQuota)
250 {
251 // Irp = ExAllocatePoolWithQuota(NonPagedPool,IoSizeOfIrp(StackSize));
252 Irp = ExAllocatePoolWithTag(NonPagedPool,
253 IoSizeOfIrp(StackSize),
254 TAG_IRP);
255 }
256 else
257 {
258 Irp = ExAllocatePoolWithTag(NonPagedPool,
259 IoSizeOfIrp(StackSize),
260 TAG_IRP);
261 }
262
263 if (Irp==NULL)
264 {
265 return(NULL);
266 }
267
268 RtlZeroMemory(Irp, IoSizeOfIrp(StackSize));
269 IoInitializeIrp(Irp,
270 IoSizeOfIrp(StackSize),
271 StackSize);
272
273 // DPRINT("Irp %x Irp->StackPtr %d\n", Irp, Irp->CurrentLocation);
274
275 return(Irp);
276 }
277
278
279 /*
280 * @implemented
281 */
282 VOID FASTCALL
283 IofCompleteRequest(PIRP Irp,
284 CCHAR PriorityBoost)
285 /*
286 * FUNCTION: Indicates the caller has finished all processing for a given
287 * I/O request and is returning the given IRP to the I/O manager
288 * ARGUMENTS:
289 * Irp = Irp to be cancelled
290 * PriorityBoost = Increment by which to boost the priority of the
291 * thread making the request
292 */
293 {
294 ULONG i;
295 NTSTATUS Status;
296 PFILE_OBJECT OriginalFileObject;
297 PDEVICE_OBJECT DeviceObject;
298 KIRQL oldIrql;
299 PMDL Mdl;
300
301 DPRINT("IoCompleteRequest(Irp %x, PriorityBoost %d) Event %x THread %x\n",
302 Irp,PriorityBoost, Irp->UserEvent, PsGetCurrentThread());
303
304 ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
305 ASSERT(Irp->CancelRoutine == NULL);
306 ASSERT(Irp->IoStatus.Status != STATUS_PENDING);
307
308 if (IoGetCurrentIrpStackLocation(Irp)->Control & SL_PENDING_RETURNED)
309 {
310 Irp->PendingReturned = TRUE;
311 }
312
313 /*
314 * Run the completion routines.
315 */
316
317 for (i=Irp->CurrentLocation;i<(ULONG)Irp->StackCount;i++)
318 {
319 /*
320 Completion routines expect the current irp stack location to be the same as when
321 IoSetCompletionRoutine was called to set them. A side effect is that completion
322 routines set by highest level drivers without their own stack location will receive
323 an invalid current stack location (at least it should be considered as invalid).
324 Since the DeviceObject argument passed is taken from the current stack, this value
325 is also invalid (NULL).
326 */
327 if (Irp->CurrentLocation < Irp->StackCount - 1)
328 {
329 IoSetPreviousIrpStackLocation(Irp);
330 DeviceObject = IoGetCurrentIrpStackLocation(Irp)->DeviceObject;
331 }
332 else
333 {
334 DeviceObject = NULL;
335 }
336
337 if (Irp->Stack[i].CompletionRoutine != NULL &&
338 ((NT_SUCCESS(Irp->IoStatus.Status) && (Irp->Stack[i].Control & SL_INVOKE_ON_SUCCESS)) ||
339 (!NT_SUCCESS(Irp->IoStatus.Status) && (Irp->Stack[i].Control & SL_INVOKE_ON_ERROR)) ||
340 (Irp->Cancel && (Irp->Stack[i].Control & SL_INVOKE_ON_CANCEL))))
341 {
342 Status = Irp->Stack[i].CompletionRoutine(DeviceObject,
343 Irp,
344 Irp->Stack[i].Context);
345
346 if (Status == STATUS_MORE_PROCESSING_REQUIRED)
347 {
348 return;
349 }
350 }
351
352 if (IoGetCurrentIrpStackLocation(Irp)->Control & SL_PENDING_RETURNED)
353 {
354 Irp->PendingReturned = TRUE;
355 }
356 }
357
358 /* Windows NT File System Internals, page 165 */
359 if (Irp->Flags & IRP_ASSOCIATED_IRP)
360 {
361 ULONG MasterIrpCount;
362 PIRP MasterIrp = Irp->AssociatedIrp.MasterIrp;
363
364 MasterIrpCount = InterlockedDecrement(&MasterIrp->AssociatedIrp.IrpCount);
365 while ((Mdl = Irp->MdlAddress))
366 {
367 Irp->MdlAddress = Mdl->Next;
368 IoFreeMdl(Mdl);
369 }
370 IoFreeIrp(Irp);
371 if (MasterIrpCount == 0)
372 {
373 IofCompleteRequest(MasterIrp, IO_NO_INCREMENT);
374 }
375 return;
376 }
377
378 /*
379 * Were done calling completion routines. Now do any cleanup that can be
380 * done in an arbitrarily context.
381 */
382
383 /* Windows NT File System Internals, page 165 */
384 if (Irp->Flags & (IRP_PAGING_IO|IRP_CLOSE_OPERATION))
385 {
386 /*
387 * If MDL_IO_PAGE_READ is set, then the caller is responsible
388 * for deallocating of the mdl.
389 */
390 if (Irp->Flags & IRP_PAGING_IO &&
391 Irp->MdlAddress &&
392 !(Irp->MdlAddress->MdlFlags & MDL_IO_PAGE_READ))
393 {
394
395 if (Irp->MdlAddress->MdlFlags & MDL_MAPPED_TO_SYSTEM_VA)
396 {
397 MmUnmapLockedPages(Irp->MdlAddress->MappedSystemVa, Irp->MdlAddress);
398 }
399
400 ExFreePool(Irp->MdlAddress);
401 }
402
403 if (Irp->UserIosb)
404 {
405 *Irp->UserIosb = Irp->IoStatus;
406 }
407
408 if (Irp->UserEvent)
409 {
410 KeSetEvent(Irp->UserEvent, PriorityBoost, FALSE);
411 }
412
413 /* io manager frees the irp for close operations */
414 // if (Irp->Flags & IRP_PAGING_IO)
415 // {
416 IoFreeIrp(Irp);
417 // }
418
419 return;
420 }
421
422
423 /*
424 Hi Dave,
425
426 I went through my old notes. You are correct and in most cases
427 IoCompleteRequest() will issue an MmUnlockPages() for each MDL in the IRP
428 chain. There are however few exceptions: one is MDLs for associated IRPs,
429 it's expected that those MDLs have been initialized with
430 IoBuildPartialMdl(). Another exception is PAGING_IO irps, the i/o completion
431 code doesn't do anything to MDLs of those IRPs.
432
433 sara
434
435 */
436
437
438 for (Mdl = Irp->MdlAddress; Mdl; Mdl = Mdl->Next)
439 {
440 /*
441 * Undo the MmProbeAndLockPages. If MmGetSystemAddressForMdl was called
442 * on this mdl, this mapping (if any) is also undone by MmUnlockPages.
443 */
444 MmUnlockPages(Mdl);
445 }
446
447 //Windows NT File System Internals, page 154
448 OriginalFileObject = Irp->Tail.Overlay.OriginalFileObject;
449
450 if (Irp->PendingReturned || KeGetCurrentIrql() == DISPATCH_LEVEL)
451 {
452 BOOLEAN bStatus;
453
454 DPRINT("Dispatching APC\n");
455 KeInitializeApc( &Irp->Tail.Apc,
456 &Irp->Tail.Overlay.Thread->Tcb,
457 Irp->ApcEnvironment,
458 IoSecondStageCompletion,//kernel routine
459 NULL,
460 (PKNORMAL_ROUTINE) NULL,
461 KernelMode,
462 OriginalFileObject);
463
464 bStatus = KeInsertQueueApc(&Irp->Tail.Apc,
465 (PVOID)Irp,
466 (PVOID)(ULONG)PriorityBoost,
467 PriorityBoost);
468
469 if (bStatus == FALSE)
470 {
471 DPRINT1("Error queueing APC for thread. Thread has probably exited.\n");
472 }
473
474 DPRINT("Finished dispatching APC\n");
475 }
476 else
477 {
478 DPRINT("Calling IoSecondStageCompletion routine directly\n");
479 KeRaiseIrql(APC_LEVEL, &oldIrql);
480 IoSecondStageCompletion(NULL,NULL,(PVOID)&OriginalFileObject,(PVOID) &Irp,(PVOID) &PriorityBoost);
481 KeLowerIrql(oldIrql);
482 DPRINT("Finished completition routine\n");
483 }
484 }
485
486
487 /*
488 * @implemented
489 */
490 VOID STDCALL
491 IoCompleteRequest(PIRP Irp,
492 CCHAR PriorityBoost)
493 {
494 IofCompleteRequest(Irp, PriorityBoost);
495 }
496
497
498 /**********************************************************************
499 * NAME EXPORTED
500 * IoIsOperationSynchronous@4
501 *
502 * DESCRIPTION
503 * Check if the I/O operation associated with the given IRP
504 * is synchronous.
505 *
506 * ARGUMENTS
507 * Irp Packet to check.
508 *
509 * RETURN VALUE
510 * TRUE if Irp's operation is synchronous; otherwise FALSE.
511 *
512 * @implemented
513 */
514 BOOLEAN STDCALL
515 IoIsOperationSynchronous(IN PIRP Irp)
516 {
517 PFILE_OBJECT FileObject = NULL;
518
519 FileObject = IoGetCurrentIrpStackLocation(Irp)->FileObject;
520
521 if (Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO)
522 {
523 return TRUE;
524 }
525
526 if (Irp->Flags & IRP_PAGING_IO)
527 {
528 return FALSE;
529 }
530
531 //NOTE: Windows 2000 crash if IoStack->FileObject == NULL, so I guess we should too;-)
532 if (Irp->Flags & IRP_SYNCHRONOUS_API || FileObject->Flags & FO_SYNCHRONOUS_IO)
533 {
534 return TRUE;
535 }
536
537 /* Otherwise, it is an asynchronous operation. */
538 return FALSE;
539 }
540
541
542 /*
543 * @implemented
544 */
545 VOID STDCALL
546 IoEnqueueIrp(IN PIRP Irp)
547 {
548 IoQueueThreadIrp(Irp);
549 }
550
551
552 /*
553 * @implemented
554 */
555 VOID STDCALL
556 IoSetTopLevelIrp(IN PIRP Irp)
557 {
558 PETHREAD Thread;
559
560 Thread = PsGetCurrentThread();
561 Thread->TopLevelIrp = Irp;
562 }
563
564
565 /*
566 * @implemented
567 */
568 PIRP STDCALL
569 IoGetTopLevelIrp(VOID)
570 {
571 return(PsGetCurrentThread()->TopLevelIrp);
572 }
573
574
575 /*
576 * @implemented
577 */
578 VOID STDCALL
579 IoQueueThreadIrp(IN PIRP Irp)
580 {
581 /* undefine this when (if ever) implementing irp cancellation */
582 #if 0
583 KIRQL oldIrql;
584
585 oldIrql = KfRaiseIrql(APC_LEVEL);
586
587 /* Synchronous irp's are queued to requestor thread. If they are not completed
588 when the thread exits, they are canceled (cleaned up).
589 -Gunnar */
590 InsertTailList(&PsGetCurrentThread()->IrpList, &Irp->ThreadListEntry);
591
592 KfLowerIrql(oldIrql);
593 #endif
594 }
595
596
597 /*
598 * @implemented
599 */
600 VOID STDCALL
601 IoReuseIrp(
602 IN OUT PIRP Irp,
603 IN NTSTATUS Status)
604 {
605
606 UCHAR AllocationFlags;
607
608 /* Reference: Chris Cant's "Writing WDM Device Drivers" */
609 AllocationFlags = Irp->AllocationFlags;
610 IoInitializeIrp(Irp, Irp->Size, Irp->StackCount);
611 Irp->IoStatus.Status = Status;
612 Irp->AllocationFlags = AllocationFlags;
613 }
614
615 /* EOF */