3 Copyright (c) 1997-2000 Microsoft Corporation
11 This module implements the Plug and Play routines for FAT called by
20 // The Bug check file id for this module
23 #define BugCheckFileId (FAT_BUG_CHECK_PNP)
25 #define Dbg (DEBUG_TRACE_PNP)
27 _Requires_lock_held_(_Global_critical_region_
)
28 _Requires_lock_held_(FatData
.Resource
)
31 PIRP_CONTEXT IrpContext
,
36 _Requires_lock_held_(_Global_critical_region_
)
39 PIRP_CONTEXT IrpContext
,
44 _Requires_lock_held_(_Global_critical_region_
)
46 FatPnpSurpriseRemove (
47 PIRP_CONTEXT IrpContext
,
52 _Requires_lock_held_(_Global_critical_region_
)
55 PIRP_CONTEXT IrpContext
,
60 IO_COMPLETION_ROUTINE FatPnpCompletionRoutine
;
64 FatPnpCompletionRoutine (
65 _In_ PDEVICE_OBJECT DeviceObject
,
67 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
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)
80 _Function_class_(IRP_MJ_PNP
)
81 _Function_class_(DRIVER_DISPATCH
)
85 _In_ PVOLUME_DEVICE_OBJECT VolumeDeviceObject
,
93 This routine implements the FSD part of PnP operations
97 VolumeDeviceObject - Supplies the volume device object where the
100 Irp - Supplies the Irp being processed
104 NTSTATUS - The FSD status for the IRP
110 PIRP_CONTEXT IrpContext
= NULL
;
117 DebugTrace(+1, Dbg
, "FatFsdPnp\n", 0);
119 FsRtlEnterFileSystem();
121 TopLevel
= FatIsIrpTopLevel( Irp
);
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.
131 if (IoGetCurrentIrpStackLocation( Irp
)->FileObject
== NULL
) {
137 Wait
= CanFsdWait( Irp
);
140 IrpContext
= FatCreateIrpContext( Irp
, Wait
);
142 Status
= FatCommonPnp( IrpContext
, Irp
);
144 } _SEH2_EXCEPT(FatExceptionFilter( IrpContext
, _SEH2_GetExceptionInformation() )) {
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
153 Status
= FatProcessException( IrpContext
, Irp
, _SEH2_GetExceptionCode() );
156 if (TopLevel
) { IoSetTopLevelIrp( NULL
); }
158 FsRtlExitFileSystem();
161 // And return to our caller
164 DebugTrace(-1, Dbg
, "FatFsdPnp -> %08lx\n", Status
);
166 UNREFERENCED_PARAMETER( VolumeDeviceObject
);
172 _Requires_lock_held_(_Global_critical_region_
)
175 IN PIRP_CONTEXT IrpContext
,
183 This is the common routine for doing PnP operations called
184 by both the fsd and fsp threads
188 Irp - Supplies the Irp to process
192 NTSTATUS - The return status for the operation
199 PIO_STACK_LOCATION IrpSp
;
201 PVOLUME_DEVICE_OBJECT OurDeviceObject
;
207 // Force everything to wait.
210 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
213 // Get the current Irp stack location.
216 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
219 // Find our Vcb. This is tricky since we have no file object in the Irp.
222 OurDeviceObject
= (PVOLUME_DEVICE_OBJECT
) IrpSp
->DeviceObject
;
225 // Take the global lock to synchronise against volume teardown.
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" )
234 FatAcquireExclusiveGlobal( IrpContext
);
237 #pragma prefast( pop )
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.
247 #pragma prefast( suppress: 28175, "touching Size is ok for a filesystem" )
249 if (OurDeviceObject
->DeviceObject
.Size
!= sizeof(VOLUME_DEVICE_OBJECT
) ||
250 NodeType( &OurDeviceObject
->Vcb
) != FAT_NTC_VCB
) {
253 // We were called with something we don't understand.
256 FatReleaseGlobal( IrpContext
);
258 Status
= STATUS_INVALID_PARAMETER
;
259 FatCompleteRequest( IrpContext
, Irp
, Status
);
263 Vcb
= &OurDeviceObject
->Vcb
;
266 // Case on the minor code.
269 switch ( IrpSp
->MinorFunction
) {
271 case IRP_MN_QUERY_REMOVE_DEVICE
:
273 Status
= FatPnpQueryRemove( IrpContext
, Irp
, Vcb
);
276 case IRP_MN_SURPRISE_REMOVAL
:
278 Status
= FatPnpSurpriseRemove( IrpContext
, Irp
, Vcb
);
281 case IRP_MN_REMOVE_DEVICE
:
283 Status
= FatPnpRemove( IrpContext
, Irp
, Vcb
);
286 case IRP_MN_CANCEL_REMOVE_DEVICE
:
288 Status
= FatPnpCancelRemove( IrpContext
, Irp
, Vcb
);
293 FatReleaseGlobal( IrpContext
);
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.
300 IoSkipCurrentIrpStackLocation( Irp
);
302 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
305 // Cleanup our Irp Context. The driver has completed the Irp.
308 FatCompleteRequest( IrpContext
, NULL
, STATUS_SUCCESS
);
318 FatPnpAdjustVpbRefCount(
325 IoAcquireVpbSpinLock( &OldIrql
);
326 Vcb
->Vpb
->ReferenceCount
+= Delta
;
327 IoReleaseVpbSpinLock( OldIrql
);
330 _Requires_lock_held_(_Global_critical_region_
)
331 _Requires_lock_held_(FatData
.Resource
)
334 PIRP_CONTEXT IrpContext
,
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.
348 Query will be followed by a Cancel or Remove.
352 Irp - Supplies the Irp to process
354 Vcb - Supplies the volume being queried.
358 NTSTATUS - The return status for the operation
363 NTSTATUS Status
= STATUS_SUCCESS
;
365 BOOLEAN VcbDeleted
= FALSE
;
366 BOOLEAN GlobalHeld
= TRUE
;
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.
376 FatAcquireExclusiveVcb( IrpContext
, Vcb
);
378 FatReleaseGlobal( IrpContext
);
383 Status
= FatLockVolumeInternal( IrpContext
, Vcb
, NULL
);
386 // Drop an additional reference on the Vpb so that the volume cannot be
387 // torn down when we drop all the locks below.
390 FatPnpAdjustVpbRefCount( Vcb
, 1);
393 // Drop and reacquire the resources in the right order.
396 FatReleaseVcb( IrpContext
, Vcb
);
398 NT_ASSERT( FlagOn(IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
) );
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" )
406 FatAcquireExclusiveGlobal( IrpContext
);
410 #pragma prefast( pop )
413 FatAcquireExclusiveVcb( IrpContext
, Vcb
);
416 // Drop the reference we added above.
419 FatPnpAdjustVpbRefCount( Vcb
, (ULONG
)-1);
421 if (NT_SUCCESS( Status
)) {
424 // With the volume held locked, note that we must finalize as much
425 // as possible right now.
428 FatFlushAndCleanVolume( IrpContext
, Irp
, Vcb
, Flush
);
431 // We need to pass this down before starting the dismount, which
432 // could disconnect us immediately from the stack.
436 // Get the next stack location, and copy over the stack location
439 IoCopyCurrentIrpStackLocationToNext( Irp
);
442 // Set up the completion routine
445 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
446 IoSetCompletionRoutine( Irp
,
447 FatPnpCompletionRoutine
,
454 // Send the request and wait.
457 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
459 if (Status
== STATUS_PENDING
) {
461 KeWaitForSingleObject( &Event
,
467 Status
= Irp
->IoStatus
.Status
;
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.
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.
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!
486 if (NT_SUCCESS( Status
)) {
488 VcbDeleted
= FatCheckForDismount( IrpContext
, Vcb
, TRUE
);
490 NT_ASSERT( VcbDeleted
|| Vcb
->VcbCondition
== VcbBad
);
498 // Release the Vcb if it could still remain.
503 FatReleaseVcb( IrpContext
, Vcb
);
508 FatReleaseGlobal( IrpContext
);
513 // Cleanup our IrpContext and complete the IRP if neccesary.
516 FatCompleteRequest( IrpContext
, Irp
, Status
);
523 _Requires_lock_held_(_Global_critical_region_
)
526 PIRP_CONTEXT IrpContext
,
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.
542 Irp - Supplies the Irp to process
544 Vcb - Supplies the volume being removed.
548 NTSTATUS - The return status for the operation
555 BOOLEAN VcbDeleted
= FALSE
;
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).
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
571 // Acquire the global resource so that we can try to vaporize
572 // the volume, and the vcb resource itself.
575 FatAcquireExclusiveVcb( IrpContext
, Vcb
);
578 // The device will be going away. Remove our lock (benign
579 // if we never had it).
582 (VOID
) FatUnlockVolumeInternal( IrpContext
, Vcb
, NULL
);
585 // We need to pass this down before starting the dismount, which
586 // could disconnect us immediately from the stack.
590 // Get the next stack location, and copy over the stack location
593 IoCopyCurrentIrpStackLocationToNext( Irp
);
596 // Set up the completion routine
599 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
600 IoSetCompletionRoutine( Irp
,
601 FatPnpCompletionRoutine
,
608 // Send the request and wait.
611 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
613 if (Status
== STATUS_PENDING
) {
615 KeWaitForSingleObject( &Event
,
621 Status
= Irp
->IoStatus
.Status
;
627 // Knock as many files down for this volume as we can.
630 FatFlushAndCleanVolume( IrpContext
, Irp
, Vcb
, NoFlush
);
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.
637 // PnP will take care of disconnecting this stack if we
638 // couldn't get off of it immediately.
641 VcbDeleted
= FatCheckForDismount( IrpContext
, Vcb
, TRUE
);
646 // Release the Vcb if it could still remain.
651 FatReleaseVcb( IrpContext
, Vcb
);
654 FatReleaseGlobal( IrpContext
);
658 // Cleanup our IrpContext and complete the IRP.
661 FatCompleteRequest( IrpContext
, Irp
, Status
);
667 _Requires_lock_held_(_Global_critical_region_
)
669 FatPnpSurpriseRemove (
670 PIRP_CONTEXT IrpContext
,
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.
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.
688 Surprise will be followed by a Remove when all references have been shut down.
692 Irp - Supplies the Irp to process
694 Vcb - Supplies the volume being removed.
698 NTSTATUS - The return status for the operation
705 BOOLEAN VcbDeleted
= FALSE
;
710 // SURPRISE - a device was physically yanked away without
711 // any warning. This means external forces.
714 FatAcquireExclusiveVcb( IrpContext
, Vcb
);
717 // We need to pass this down before starting the dismount, which
718 // could disconnect us immediately from the stack.
722 // Get the next stack location, and copy over the stack location
725 IoCopyCurrentIrpStackLocationToNext( Irp
);
728 // Set up the completion routine
731 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
732 IoSetCompletionRoutine( Irp
,
733 FatPnpCompletionRoutine
,
740 // Send the request and wait.
743 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
745 if (Status
== STATUS_PENDING
) {
747 KeWaitForSingleObject( &Event
,
753 Status
= Irp
->IoStatus
.Status
;
759 // Knock as many files down for this volume as we can.
762 FatFlushAndCleanVolume( IrpContext
, Irp
, Vcb
, NoFlush
);
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.
770 VcbDeleted
= FatCheckForDismount( IrpContext
, Vcb
, TRUE
);
775 // Release the Vcb if it could still remain.
780 FatReleaseVcb( IrpContext
, Vcb
);
783 FatReleaseGlobal( IrpContext
);
787 // Cleanup our IrpContext and complete the IRP.
790 FatCompleteRequest( IrpContext
, Irp
, Status
);
796 _Requires_lock_held_(_Global_critical_region_
)
799 PIRP_CONTEXT IrpContext
,
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.
815 Irp - Supplies the Irp to process
817 Vcb - Supplies the volume being removed.
821 NTSTATUS - The return status for the operation
826 NTSTATUS Status
= STATUS_SUCCESS
;
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
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.
846 FatAcquireExclusiveVcb( IrpContext
, Vcb
);
847 FatReleaseGlobal( IrpContext
);
850 // Unlock the volume. This is benign if we never had seen
854 (VOID
)FatUnlockVolumeInternal( IrpContext
, Vcb
, NULL
);
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.
864 IoSkipCurrentIrpStackLocation( Irp
);
866 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
870 FatReleaseVcb( IrpContext
, Vcb
);
873 FatCompleteRequest( IrpContext
, NULL
, STATUS_SUCCESS
);
880 // Local support routine
885 FatPnpCompletionRoutine (
886 _In_ PDEVICE_OBJECT DeviceObject
,
888 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
891 PKEVENT Event
= (PKEVENT
) Contxt
;
893 KeSetEvent( Event
, 0, FALSE
);
895 return STATUS_MORE_PROCESSING_REQUIRED
;
897 UNREFERENCED_PARAMETER( DeviceObject
);
898 UNREFERENCED_PARAMETER( Contxt
);
899 UNREFERENCED_PARAMETER( Irp
);