xref: /reactos/drivers/filesystems/fastfat/pnp.c (revision 84344399)
1 /*++
2 
3 Copyright (c) 1997-2000 Microsoft Corporation
4 
5 Module Name:
6 
7     Pnp.c
8 
9 Abstract:
10 
11     This module implements the Plug and Play routines for FAT called by
12     the dispatch driver.
13 
14 
15 --*/
16 
17 #include "fatprocs.h"
18 
19 //
20 //  The Bug check file id for this module
21 //
22 
23 #define BugCheckFileId                   (FAT_BUG_CHECK_PNP)
24 
25 #define Dbg                              (DEBUG_TRACE_PNP)
26 
27 _Requires_lock_held_(_Global_critical_region_)
28 _Requires_lock_held_(FatData.Resource)
29 NTSTATUS
30 FatPnpQueryRemove (
31     PIRP_CONTEXT IrpContext,
32     PIRP Irp,
33     PVCB Vcb
34     );
35 
36 _Requires_lock_held_(_Global_critical_region_)
37 NTSTATUS
38 FatPnpRemove (
39     PIRP_CONTEXT IrpContext,
40     PIRP Irp,
41     PVCB Vcb
42     );
43 
44 _Requires_lock_held_(_Global_critical_region_)
45 NTSTATUS
46 FatPnpSurpriseRemove (
47     PIRP_CONTEXT IrpContext,
48     PIRP Irp,
49     PVCB Vcb
50     );
51 
52 _Requires_lock_held_(_Global_critical_region_)
53 NTSTATUS
54 FatPnpCancelRemove (
55     PIRP_CONTEXT IrpContext,
56     PIRP Irp,
57     PVCB Vcb
58     );
59 
60 IO_COMPLETION_ROUTINE FatPnpCompletionRoutine;
61 
62 NTSTATUS
63 NTAPI
64 FatPnpCompletionRoutine (
65     _In_ PDEVICE_OBJECT DeviceObject,
66     _In_ PIRP Irp,
67     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
68     );
69 
70 #ifdef ALLOC_PRAGMA
71 #pragma alloc_text(PAGE, FatCommonPnp)
72 #pragma alloc_text(PAGE, FatFsdPnp)
73 #pragma alloc_text(PAGE, FatPnpCancelRemove)
74 #pragma alloc_text(PAGE, FatPnpQueryRemove)
75 #pragma alloc_text(PAGE, FatPnpRemove)
76 #pragma alloc_text(PAGE, FatPnpSurpriseRemove)
77 #endif
78 
79 
80 _Function_class_(IRP_MJ_PNP)
81 _Function_class_(DRIVER_DISPATCH)
82 NTSTATUS
83 NTAPI
84 FatFsdPnp (
85     _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
86     _Inout_ PIRP Irp
87     )
88 
89 /*++
90 
91 Routine Description:
92 
93     This routine implements the FSD part of PnP operations
94 
95 Arguments:
96 
97     VolumeDeviceObject - Supplies the volume device object where the
98         file exists
99 
100     Irp - Supplies the Irp being processed
101 
102 Return Value:
103 
104     NTSTATUS - The FSD status for the IRP
105 
106 --*/
107 
108 {
109     NTSTATUS Status;
110     PIRP_CONTEXT IrpContext = NULL;
111 
112     BOOLEAN TopLevel;
113     BOOLEAN Wait;
114 
115     PAGED_CODE();
116 
117     DebugTrace(+1, Dbg, "FatFsdPnp\n", 0);
118 
119     FsRtlEnterFileSystem();
120 
121     TopLevel = FatIsIrpTopLevel( Irp );
122 
123     _SEH2_TRY {
124 
125         //
126         //  We expect there to never be a fileobject, in which case we will always
127         //  wait.  Since at the moment we don't have any concept of pending Pnp
128         //  operations, this is a bit nitpicky.
129         //
130 
131         if (IoGetCurrentIrpStackLocation( Irp )->FileObject == NULL) {
132 
133             Wait = TRUE;
134 
135         } else {
136 
137             Wait = CanFsdWait( Irp );
138         }
139 
140         IrpContext = FatCreateIrpContext( Irp, Wait );
141 
142         Status = FatCommonPnp( IrpContext, Irp );
143 
144     } _SEH2_EXCEPT(FatExceptionFilter( IrpContext, _SEH2_GetExceptionInformation() )) {
145 
146         //
147         //  We had some trouble trying to perform the requested
148         //  operation, so we'll abort the I/O request with
149         //  the error status that we get back from the
150         //  execption code
151         //
152 
153         Status = FatProcessException( IrpContext, Irp, _SEH2_GetExceptionCode() );
154     } _SEH2_END;
155 
156     if (TopLevel) { IoSetTopLevelIrp( NULL ); }
157 
158     FsRtlExitFileSystem();
159 
160     //
161     //  And return to our caller
162     //
163 
164     DebugTrace(-1, Dbg, "FatFsdPnp -> %08lx\n", Status);
165 
166     UNREFERENCED_PARAMETER( VolumeDeviceObject );
167 
168     return Status;
169 }
170 
171 
172 _Requires_lock_held_(_Global_critical_region_)
173 NTSTATUS
174 FatCommonPnp (
175     IN PIRP_CONTEXT IrpContext,
176     IN PIRP Irp
177     )
178 
179 /*++
180 
181 Routine Description:
182 
183     This is the common routine for doing PnP operations called
184     by both the fsd and fsp threads
185 
186 Arguments:
187 
188     Irp - Supplies the Irp to process
189 
190 Return Value:
191 
192     NTSTATUS - The return status for the operation
193 
194 --*/
195 
196 {
197     NTSTATUS Status;
198 
199     PIO_STACK_LOCATION IrpSp;
200 
201     PVOLUME_DEVICE_OBJECT OurDeviceObject;
202     PVCB Vcb;
203 
204     PAGED_CODE();
205 
206     //
207     //  Force everything to wait.
208     //
209 
210     SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
211 
212     //
213     //  Get the current Irp stack location.
214     //
215 
216     IrpSp = IoGetCurrentIrpStackLocation( Irp );
217 
218     //
219     //  Find our Vcb.  This is tricky since we have no file object in the Irp.
220     //
221 
222     OurDeviceObject = (PVOLUME_DEVICE_OBJECT) IrpSp->DeviceObject;
223 
224     //
225     //  Take the global lock to synchronise against volume teardown.
226     //
227 
228 #ifdef _MSC_VER
229 #pragma prefast( push )
230 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
231 #pragma prefast( disable: 28193, "this will always wait" )
232 #endif
233 
234     FatAcquireExclusiveGlobal( IrpContext );
235 
236 #ifdef _MSC_VER
237 #pragma prefast( pop )
238 #endif
239 
240     //
241     //  Make sure this device object really is big enough to be a volume device
242     //  object.  If it isn't, we need to get out before we try to reference some
243     //  field that takes us past the end of an ordinary device object.
244     //
245 
246 #ifdef _MSC_VER
247 #pragma prefast( suppress: 28175, "touching Size is ok for a filesystem" )
248 #endif
249     if (OurDeviceObject->DeviceObject.Size != sizeof(VOLUME_DEVICE_OBJECT) ||
250         NodeType( &OurDeviceObject->Vcb ) != FAT_NTC_VCB) {
251 
252         //
253         //  We were called with something we don't understand.
254         //
255 
256         FatReleaseGlobal( IrpContext );
257 
258         Status = STATUS_INVALID_PARAMETER;
259         FatCompleteRequest( IrpContext, Irp, Status );
260         return Status;
261     }
262 
263     Vcb = &OurDeviceObject->Vcb;
264 
265     //
266     //  Case on the minor code.
267     //
268 
269     switch ( IrpSp->MinorFunction ) {
270 
271         case IRP_MN_QUERY_REMOVE_DEVICE:
272 
273             Status = FatPnpQueryRemove( IrpContext, Irp, Vcb );
274             break;
275 
276         case IRP_MN_SURPRISE_REMOVAL:
277 
278             Status = FatPnpSurpriseRemove( IrpContext, Irp, Vcb );
279             break;
280 
281         case IRP_MN_REMOVE_DEVICE:
282 
283             Status = FatPnpRemove( IrpContext, Irp, Vcb );
284             break;
285 
286         case IRP_MN_CANCEL_REMOVE_DEVICE:
287 
288             Status = FatPnpCancelRemove( IrpContext, Irp, Vcb );
289             break;
290 
291         default:
292 
293             FatReleaseGlobal( IrpContext );
294 
295             //
296             //  Just pass the IRP on.  As we do not need to be in the
297             //  way on return, ellide ourselves out of the stack.
298             //
299 
300             IoSkipCurrentIrpStackLocation( Irp );
301 
302             Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
303 
304             //
305             //  Cleanup our Irp Context.  The driver has completed the Irp.
306             //
307 
308             FatCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
309 
310             break;
311     }
312 
313     return Status;
314 }
315 
316 
317 VOID
318 FatPnpAdjustVpbRefCount(
319     IN PVCB Vcb,
320     IN ULONG Delta
321     )
322 {
323     KIRQL OldIrql;
324 
325     IoAcquireVpbSpinLock( &OldIrql);
326     Vcb->Vpb->ReferenceCount += Delta;
327     IoReleaseVpbSpinLock( OldIrql);
328 }
329 
330 _Requires_lock_held_(_Global_critical_region_)
331 _Requires_lock_held_(FatData.Resource)
332 NTSTATUS
333 FatPnpQueryRemove (
334     PIRP_CONTEXT IrpContext,
335     PIRP Irp,
336     PVCB Vcb
337     )
338 
339 /*++
340 
341 Routine Description:
342 
343     This routine handles the PnP query remove operation.  The filesystem
344     is responsible for answering whether there are any reasons it sees
345     that the volume can not go away (and the device removed).  Initiation
346     of the dismount begins when we answer yes to this question.
347 
348     Query will be followed by a Cancel or Remove.
349 
350 Arguments:
351 
352     Irp - Supplies the Irp to process
353 
354     Vcb - Supplies the volume being queried.
355 
356 Return Value:
357 
358     NTSTATUS - The return status for the operation
359 
360 --*/
361 
362 {
363     NTSTATUS Status = STATUS_SUCCESS;
364     KEVENT Event;
365     BOOLEAN VcbDeleted = FALSE;
366     BOOLEAN GlobalHeld = TRUE;
367 
368     PAGED_CODE();
369 
370     //
371     //  Having said yes to a QUERY, any communication with the
372     //  underlying storage stack is undefined (and may block)
373     //  until the bounding CANCEL or REMOVE is sent.
374     //
375 
376     FatAcquireExclusiveVcb( IrpContext, Vcb );
377 
378     FatReleaseGlobal( IrpContext);
379     GlobalHeld = FALSE;
380 
381     _SEH2_TRY {
382 
383         Status = FatLockVolumeInternal( IrpContext, Vcb, NULL );
384 
385         //
386         //  Drop an additional reference on the Vpb so that the volume cannot be
387         //  torn down when we drop all the locks below.
388         //
389 
390         FatPnpAdjustVpbRefCount( Vcb, 1);
391 
392         //
393         //  Drop and reacquire the resources in the right order.
394         //
395 
396         FatReleaseVcb( IrpContext, Vcb );
397 
398         NT_ASSERT( FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) );
399 
400 #ifdef _MSC_VER
401 #pragma prefast( push )
402 #pragma prefast( disable: 28137, "prefast wants the wait to be a constant, but that isn't possible for the way fastfat is designed" )
403 #pragma prefast( disable: 28193, "this will always wait" )
404 #endif
405 
406         FatAcquireExclusiveGlobal( IrpContext );
407         GlobalHeld = TRUE;
408 
409 #ifdef _MSC_VER
410 #pragma prefast( pop )
411 #endif
412 
413         FatAcquireExclusiveVcb( IrpContext, Vcb );
414 
415         //
416         //  Drop the reference we added above.
417         //
418 
419         FatPnpAdjustVpbRefCount( Vcb, (ULONG)-1);
420 
421         if (NT_SUCCESS( Status )) {
422 
423             //
424             //  With the volume held locked, note that we must finalize as much
425             //  as possible right now.
426             //
427 
428             FatFlushAndCleanVolume( IrpContext, Irp, Vcb, Flush );
429 
430             //
431             //  We need to pass this down before starting the dismount, which
432             //  could disconnect us immediately from the stack.
433             //
434 
435             //
436             //  Get the next stack location, and copy over the stack location
437             //
438 
439             IoCopyCurrentIrpStackLocationToNext( Irp );
440 
441             //
442             //  Set up the completion routine
443             //
444 
445             KeInitializeEvent( &Event, NotificationEvent, FALSE );
446             IoSetCompletionRoutine( Irp,
447                                     FatPnpCompletionRoutine,
448                                     &Event,
449                                     TRUE,
450                                     TRUE,
451                                     TRUE );
452 
453             //
454             //  Send the request and wait.
455             //
456 
457             Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
458 
459             if (Status == STATUS_PENDING) {
460 
461                 KeWaitForSingleObject( &Event,
462                                        Executive,
463                                        KernelMode,
464                                        FALSE,
465                                        NULL );
466 
467                 Status = Irp->IoStatus.Status;
468             }
469 
470             //
471             //  Now if no one below us failed already, initiate the dismount
472             //  on this volume, make it go away.  PnP needs to see our internal
473             //  streams close and drop their references to the target device.
474             //
475             //  Since we were able to lock the volume, we are guaranteed to
476             //  move this volume into dismount state and disconnect it from
477             //  the underlying storage stack.  The force on our part is actually
478             //  unnecesary, though complete.
479             //
480             //  What is not strictly guaranteed, though, is that the closes
481             //  for the metadata streams take effect synchronously underneath
482             //  of this call.  This would leave references on the target device
483             //  even though we are disconnected!
484             //
485 
486             if (NT_SUCCESS( Status )) {
487 
488                 VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE );
489 
490                 NT_ASSERT( VcbDeleted || Vcb->VcbCondition == VcbBad );
491 
492             }
493         }
494 
495     } _SEH2_FINALLY {
496 
497         //
498         //  Release the Vcb if it could still remain.
499         //
500 
501         if (!VcbDeleted) {
502 
503             FatReleaseVcb( IrpContext, Vcb );
504         }
505 
506         if (GlobalHeld) {
507 
508             FatReleaseGlobal( IrpContext );
509         }
510     } _SEH2_END;
511 
512     //
513     //  Cleanup our IrpContext and complete the IRP if neccesary.
514     //
515 
516     FatCompleteRequest( IrpContext, Irp, Status );
517 
518     return Status;
519 }
520 
521 
522 
523 _Requires_lock_held_(_Global_critical_region_)
524 NTSTATUS
525 FatPnpRemove (
526     PIRP_CONTEXT IrpContext,
527     PIRP Irp,
528     PVCB Vcb
529     )
530 
531 /*++
532 
533 Routine Description:
534 
535     This routine handles the PnP remove operation.  This is our notification
536     that the underlying storage device for the volume we have is gone, and
537     an excellent indication that the volume will never reappear. The filesystem
538     is responsible for initiation or completion of the dismount.
539 
540 Arguments:
541 
542     Irp - Supplies the Irp to process
543 
544     Vcb - Supplies the volume being removed.
545 
546 Return Value:
547 
548     NTSTATUS - The return status for the operation
549 
550 --*/
551 
552 {
553     NTSTATUS Status;
554     KEVENT Event;
555     BOOLEAN VcbDeleted = FALSE;
556 
557     PAGED_CODE();
558 
559     //
560     //  REMOVE - a storage device is now gone.  We either got
561     //  QUERY'd and said yes OR got a SURPRISE OR a storage
562     //  stack failed to spin back up from a sleep/stop state
563     //  (the only case in which this will be the first warning).
564     //
565     //  Note that it is entirely unlikely that we will be around
566     //  for a REMOVE in the first two cases, as we try to intiate
567     //  dismount.
568     //
569 
570     //
571     //  Acquire the global resource so that we can try to vaporize
572     //  the volume, and the vcb resource itself.
573     //
574 
575     FatAcquireExclusiveVcb( IrpContext, Vcb );
576 
577     //
578     //  The device will be going away.  Remove our lock (benign
579     //  if we never had it).
580     //
581 
582     (VOID) FatUnlockVolumeInternal( IrpContext, Vcb, NULL );
583 
584     //
585     //  We need to pass this down before starting the dismount, which
586     //  could disconnect us immediately from the stack.
587     //
588 
589     //
590     //  Get the next stack location, and copy over the stack location
591     //
592 
593     IoCopyCurrentIrpStackLocationToNext( Irp );
594 
595     //
596     //  Set up the completion routine
597     //
598 
599     KeInitializeEvent( &Event, NotificationEvent, FALSE );
600     IoSetCompletionRoutine( Irp,
601                             FatPnpCompletionRoutine,
602                             &Event,
603                             TRUE,
604                             TRUE,
605                             TRUE );
606 
607     //
608     //  Send the request and wait.
609     //
610 
611     Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
612 
613     if (Status == STATUS_PENDING) {
614 
615         KeWaitForSingleObject( &Event,
616                                Executive,
617                                KernelMode,
618                                FALSE,
619                                NULL );
620 
621         Status = Irp->IoStatus.Status;
622     }
623 
624     _SEH2_TRY {
625 
626         //
627         //  Knock as many files down for this volume as we can.
628         //
629 
630         FatFlushAndCleanVolume( IrpContext, Irp, Vcb, NoFlush );
631 
632         //
633         //  Now make our dismount happen.  This may not vaporize the
634         //  Vcb, of course, since there could be any number of handles
635         //  outstanding if we were not preceeded by a QUERY.
636         //
637         //  PnP will take care of disconnecting this stack if we
638         //  couldn't get off of it immediately.
639         //
640 
641         VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE );
642 
643     } _SEH2_FINALLY {
644 
645         //
646         //  Release the Vcb if it could still remain.
647         //
648 
649         if (!VcbDeleted) {
650 
651             FatReleaseVcb( IrpContext, Vcb );
652         }
653 
654         FatReleaseGlobal( IrpContext );
655     } _SEH2_END;
656 
657     //
658     //  Cleanup our IrpContext and complete the IRP.
659     //
660 
661     FatCompleteRequest( IrpContext, Irp, Status );
662 
663     return Status;
664 }
665 
666 
667 _Requires_lock_held_(_Global_critical_region_)
668 NTSTATUS
669 FatPnpSurpriseRemove (
670     PIRP_CONTEXT IrpContext,
671     PIRP Irp,
672     PVCB Vcb
673     )
674 
675 /*++
676 
677 Routine Description:
678 
679     This routine handles the PnP surprise remove operation.  This is another
680     type of notification that the underlying storage device for the volume we
681     have is gone, and is excellent indication that the volume will never reappear.
682     The filesystem is responsible for initiation or completion the dismount.
683 
684     For the most part, only "real" drivers care about the distinction of a
685     surprise remove, which is a result of our noticing that a user (usually)
686     physically reached into the machine and pulled something out.
687 
688     Surprise will be followed by a Remove when all references have been shut down.
689 
690 Arguments:
691 
692     Irp - Supplies the Irp to process
693 
694     Vcb - Supplies the volume being removed.
695 
696 Return Value:
697 
698     NTSTATUS - The return status for the operation
699 
700 --*/
701 
702 {
703     NTSTATUS Status;
704     KEVENT Event;
705     BOOLEAN VcbDeleted = FALSE;
706 
707     PAGED_CODE();
708 
709     //
710     //  SURPRISE - a device was physically yanked away without
711     //  any warning.  This means external forces.
712     //
713 
714     FatAcquireExclusiveVcb( IrpContext, Vcb );
715 
716     //
717     //  We need to pass this down before starting the dismount, which
718     //  could disconnect us immediately from the stack.
719     //
720 
721     //
722     //  Get the next stack location, and copy over the stack location
723     //
724 
725     IoCopyCurrentIrpStackLocationToNext( Irp );
726 
727     //
728     //  Set up the completion routine
729     //
730 
731     KeInitializeEvent( &Event, NotificationEvent, FALSE );
732     IoSetCompletionRoutine( Irp,
733                             FatPnpCompletionRoutine,
734                             &Event,
735                             TRUE,
736                             TRUE,
737                             TRUE );
738 
739     //
740     //  Send the request and wait.
741     //
742 
743     Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
744 
745     if (Status == STATUS_PENDING) {
746 
747         KeWaitForSingleObject( &Event,
748                                Executive,
749                                KernelMode,
750                                FALSE,
751                                NULL );
752 
753         Status = Irp->IoStatus.Status;
754     }
755 
756     _SEH2_TRY {
757 
758         //
759         //  Knock as many files down for this volume as we can.
760         //
761 
762         FatFlushAndCleanVolume( IrpContext, Irp, Vcb, NoFlush );
763 
764         //
765         //  Now make our dismount happen.  This may not vaporize the
766         //  Vcb, of course, since there could be any number of handles
767         //  outstanding since this is an out of band notification.
768         //
769 
770         VcbDeleted = FatCheckForDismount( IrpContext, Vcb, TRUE );
771 
772     } _SEH2_FINALLY {
773 
774         //
775         //  Release the Vcb if it could still remain.
776         //
777 
778         if (!VcbDeleted) {
779 
780             FatReleaseVcb( IrpContext, Vcb );
781         }
782 
783         FatReleaseGlobal( IrpContext );
784     } _SEH2_END;
785 
786     //
787     //  Cleanup our IrpContext and complete the IRP.
788     //
789 
790     FatCompleteRequest( IrpContext, Irp, Status );
791 
792     return Status;
793 }
794 
795 
796 _Requires_lock_held_(_Global_critical_region_)
797 NTSTATUS
798 FatPnpCancelRemove (
799     PIRP_CONTEXT IrpContext,
800     PIRP Irp,
801     PVCB Vcb
802     )
803 
804 /*++
805 
806 Routine Description:
807 
808     This routine handles the PnP cancel remove operation.  This is our
809     notification that a previously proposed remove (query) was eventually
810     vetoed by a component.  The filesystem is responsible for cleaning up
811     and getting ready for more IO.
812 
813 Arguments:
814 
815     Irp - Supplies the Irp to process
816 
817     Vcb - Supplies the volume being removed.
818 
819 Return Value:
820 
821     NTSTATUS - The return status for the operation
822 
823 --*/
824 
825 {
826     NTSTATUS Status = STATUS_SUCCESS;
827 
828     PAGED_CODE();
829 
830     //
831     //  CANCEL - a previous QUERY has been rescinded as a result
832     //  of someone vetoing.  Since PnP cannot figure out who may
833     //  have gotten the QUERY (think about it: stacked drivers),
834     //  we must expect to deal with getting a CANCEL without having
835     //  seen the QUERY.
836     //
837     //  For FAT, this is quite easy.  In fact, we can't get a
838     //  CANCEL if the underlying drivers succeeded the QUERY since
839     //  we disconnect the Vpb on our dismount initiation.  This is
840     //  actually pretty important because if PnP could get to us
841     //  after the disconnect we'd be thoroughly unsynchronized
842     //  with respect to the Vcb getting torn apart - merely referencing
843     //  the volume device object is insufficient to keep us intact.
844     //
845 
846     FatAcquireExclusiveVcb( IrpContext, Vcb );
847     FatReleaseGlobal( IrpContext);
848 
849     //
850     //  Unlock the volume.  This is benign if we never had seen
851     //  a QUERY.
852     //
853 
854     (VOID)FatUnlockVolumeInternal( IrpContext, Vcb, NULL );
855 
856     _SEH2_TRY {
857 
858         //
859         //  Send the request.  The underlying driver will complete the
860         //  IRP.  Since we don't need to be in the way, simply ellide
861         //  ourselves out of the IRP stack.
862         //
863 
864         IoSkipCurrentIrpStackLocation( Irp );
865 
866         Status = IoCallDriver(Vcb->TargetDeviceObject, Irp);
867     }
868     _SEH2_FINALLY {
869 
870         FatReleaseVcb( IrpContext, Vcb );
871     } _SEH2_END;
872 
873     FatCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
874 
875     return Status;
876 }
877 
878 
879 //
880 //  Local support routine
881 //
882 
883 NTSTATUS
884 NTAPI
885 FatPnpCompletionRoutine (
886     _In_ PDEVICE_OBJECT DeviceObject,
887     _In_ PIRP Irp,
888     _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
889     )
890 {
891     PKEVENT Event = (PKEVENT) Contxt;
892 
893     KeSetEvent( Event, 0, FALSE );
894 
895     return STATUS_MORE_PROCESSING_REQUIRED;
896 
897     UNREFERENCED_PARAMETER( DeviceObject );
898     UNREFERENCED_PARAMETER( Contxt );
899     UNREFERENCED_PARAMETER( Irp );
900 }
901 
902 
903