3 Copyright (c) 1997-2000 Microsoft Corporation
11 This module implements the Plug and Play routines for CDFS called by
20 // The Bug check file id for this module
23 #define BugCheckFileId (CDFS_BUG_CHECK_PNP)
25 _Requires_lock_held_(_Global_critical_region_
)
26 _Releases_nonreentrant_lock_(CdData
.DataResource
)
29 _Inout_ PIRP_CONTEXT IrpContext
,
34 _Requires_lock_held_(_Global_critical_region_
)
35 _Releases_nonreentrant_lock_(CdData
.DataResource
)
38 _Inout_ PIRP_CONTEXT IrpContext
,
43 _Requires_lock_held_(_Global_critical_region_
)
44 _Releases_nonreentrant_lock_(CdData
.DataResource
)
47 _Inout_ PIRP_CONTEXT IrpContext
,
52 _Requires_lock_held_(_Global_critical_region_
)
53 _Releases_nonreentrant_lock_(CdData
.DataResource
)
56 _Inout_ PIRP_CONTEXT IrpContext
,
61 // Tell prefast this is a completion routine.
62 IO_COMPLETION_ROUTINE CdPnpCompletionRoutine
;
65 CdPnpCompletionRoutine (
66 _In_ PDEVICE_OBJECT DeviceObject
,
68 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
72 #pragma alloc_text(PAGE, CdCommonPnp)
73 #pragma alloc_text(PAGE, CdPnpCancelRemove)
74 #pragma alloc_text(PAGE, CdPnpQueryRemove)
75 #pragma alloc_text(PAGE, CdPnpRemove)
76 #pragma alloc_text(PAGE, CdPnpSurpriseRemove)
79 _Requires_lock_held_(_Global_critical_region_
)
82 _Inout_ PIRP_CONTEXT IrpContext
,
90 This is the common routine for doing PnP operations called
91 by both the fsd and fsp threads
95 Irp - Supplies the Irp to process
99 NTSTATUS - The return status for the operation
104 NTSTATUS Status
= STATUS_SUCCESS
;
105 BOOLEAN PassThrough
= FALSE
;
107 PIO_STACK_LOCATION IrpSp
;
109 PVOLUME_DEVICE_OBJECT OurDeviceObject
;
114 // Global lock object is acquired based on internal book-keeping
115 _Analysis_suppress_lock_checking_(CdData
.DataResource
);
118 // Get the current Irp stack location.
121 IrpSp
= IoGetCurrentIrpStackLocation( Irp
);
124 // Find our Vcb. This is tricky since we have no file object in the Irp.
127 OurDeviceObject
= (PVOLUME_DEVICE_OBJECT
) IrpSp
->DeviceObject
;
130 // IO holds a handle reference on our VDO and holds the device lock, which
131 // syncs us against mounts/verifies. However we hold no reference on the
132 // volume, which may already have been torn down (and the Vpb freed), for
133 // example by a force dismount. Check for this condition. We must hold this
134 // lock until the pnp worker functions take additional locks/refs on the Vcb.
137 CdAcquireCdData( IrpContext
);
140 // Make sure this device object really is big enough to be a volume device
141 // object. If it isn't, we need to get out before we try to reference some
142 // field that takes us past the end of an ordinary device object.
145 #pragma prefast(suppress: 28175, "this is a filesystem driver, touching the size member is allowed")
146 if (OurDeviceObject
->DeviceObject
.Size
!= sizeof(VOLUME_DEVICE_OBJECT
) ||
147 NodeType( &OurDeviceObject
->Vcb
) != CDFS_NTC_VCB
) {
150 // We were called with something we don't understand.
153 Status
= STATUS_INVALID_PARAMETER
;
154 CdReleaseCdData( IrpContext
);
155 CdCompleteRequest( IrpContext
, Irp
, Status
);
160 // Force all PnP operations to be synchronous.
163 SetFlag( IrpContext
->Flags
, IRP_CONTEXT_FLAG_WAIT
);
165 Vcb
= &OurDeviceObject
->Vcb
;
168 // Check that the Vcb hasn't already been deleted. If so, just pass the
169 // request through to the driver below, we don't need to do anything.
172 if (NULL
== Vcb
->Vpb
) {
179 // Case on the minor code.
182 switch ( IrpSp
->MinorFunction
) {
184 case IRP_MN_QUERY_REMOVE_DEVICE
:
186 Status
= CdPnpQueryRemove( IrpContext
, Irp
, Vcb
);
189 case IRP_MN_SURPRISE_REMOVAL
:
191 Status
= CdPnpSurpriseRemove( IrpContext
, Irp
, Vcb
);
194 case IRP_MN_REMOVE_DEVICE
:
196 Status
= CdPnpRemove( IrpContext
, Irp
, Vcb
);
199 case IRP_MN_CANCEL_REMOVE_DEVICE
:
201 Status
= CdPnpCancelRemove( IrpContext
, Irp
, Vcb
);
213 CdReleaseCdData( IrpContext
);
216 // Just pass the IRP on. As we do not need to be in the
217 // way on return, ellide ourselves out of the stack.
220 IoSkipCurrentIrpStackLocation( Irp
);
222 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
225 // Cleanup our Irp Context. The driver has completed the Irp.
228 CdCompleteRequest( IrpContext
, NULL
, STATUS_SUCCESS
);
234 _Requires_lock_held_(_Global_critical_region_
)
235 _Releases_nonreentrant_lock_(CdData
.DataResource
)
238 _Inout_ PIRP_CONTEXT IrpContext
,
247 This routine handles the PnP query remove operation. The filesystem
248 is responsible for answering whether there are any reasons it sees
249 that the volume can not go away (and the device removed). Initiation
250 of the dismount begins when we answer yes to this question.
252 Query will be followed by a Cancel or Remove.
256 Irp - Supplies the Irp to process
258 Vcb - Supplies the volume being queried.
262 NTSTATUS - The return status for the operation
269 BOOLEAN VcbPresent
= TRUE
;
273 ASSERT_EXCLUSIVE_CDDATA
;
276 // Having said yes to a QUERY, any communication with the
277 // underlying storage stack is undefined (and may block)
278 // until the bounding CANCEL or REMOVE is sent.
280 // Acquire the global resource so that we can try to vaporize the volume,
281 // and the vcb resource itself.
284 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
287 // Drop a reference on the Vcb to keep it around after we drop the locks.
290 CdLockVcb( IrpContext
, Vcb
);
291 Vcb
->VcbReference
+= 1;
292 CdUnlockVcb( IrpContext
, Vcb
);
294 CdReleaseCdData( IrpContext
);
296 Status
= CdLockVolumeInternal( IrpContext
, Vcb
, NULL
);
299 // Reacquire the global lock, which means dropping the Vcb resource.
302 CdReleaseVcb( IrpContext
, Vcb
);
304 CdAcquireCdData( IrpContext
);
305 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
308 // Remove our extra reference.
311 CdLockVcb( IrpContext
, Vcb
);
312 Vcb
->VcbReference
-= 1;
313 CdUnlockVcb( IrpContext
, Vcb
);
315 if (NT_SUCCESS( Status
)) {
318 // We need to pass this down before starting the dismount, which
319 // could disconnect us immediately from the stack.
323 // Get the next stack location, and copy over the stack location
326 IoCopyCurrentIrpStackLocationToNext( Irp
);
329 // Set up the completion routine
332 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
333 IoSetCompletionRoutine( Irp
,
334 CdPnpCompletionRoutine
,
341 // Send the request and wait.
344 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
346 if (Status
== STATUS_PENDING
) {
348 (VOID
)KeWaitForSingleObject( &Event
,
354 Status
= Irp
->IoStatus
.Status
;
358 // Now if no one below us failed already, initiate the dismount
359 // on this volume, make it go away. PnP needs to see our internal
360 // streams close and drop their references to the target device.
362 // Since we were able to lock the volume, we are guaranteed to
363 // move this volume into dismount state and disconnect it from
364 // the underlying storage stack. The force on our part is actually
365 // unnecesary, though complete.
367 // What is not strictly guaranteed, though, is that the closes
368 // for the metadata streams take effect synchronously underneath
369 // of this call. This would leave references on the target device
370 // even though we are disconnected!
373 if (NT_SUCCESS( Status
)) {
375 VcbPresent
= CdCheckForDismount( IrpContext
, Vcb
, TRUE
);
377 NT_ASSERT( !VcbPresent
|| Vcb
->VcbCondition
== VcbDismountInProgress
);
381 // Note: Normally everything will complete and the internal streams will
382 // vaporise. However there is some code in the system which drops additional
383 // references on fileobjects, including our internal stream file objects,
384 // for (WMI) tracing purposes. If that happens to run concurrently with our
385 // teardown, our internal streams will not vaporise until those references
386 // are removed. So it's possible that the volume still remains at this
387 // point. The pnp query remove will fail due to our references on the device.
388 // To be cleaner we will return an error here. We could pend the pnp
389 // IRP until the volume goes away, but since we don't know when that will
390 // be, and this is a very rare case, we'll just fail the query.
392 // The reason this is the case is that handles/fileobjects place a reference
393 // on the device objects they overly. In the filesystem case, these references
394 // are on our target devices. PnP correcly thinks that if references remain
395 // on the device objects in the stack that someone has a handle, and that this
396 // counts as a reason to not succeed the query - even though every interrogated
397 // driver thinks that it is OK.
400 if (NT_SUCCESS( Status
) && VcbPresent
&& (Vcb
->VcbReference
!= 0)) {
402 Status
= STATUS_DEVICE_BUSY
;
407 // Release the Vcb if it could still remain.
412 CdReleaseVcb( IrpContext
, Vcb
);
415 _Analysis_assume_lock_not_held_(Vcb
->VcbResource
);
418 CdReleaseCdData( IrpContext
);
421 // Cleanup our IrpContext and complete the IRP if neccesary.
424 CdCompleteRequest( IrpContext
, Irp
, Status
);
429 _Requires_lock_held_(_Global_critical_region_
)
430 _Releases_nonreentrant_lock_(CdData
.DataResource
)
433 _Inout_ PIRP_CONTEXT IrpContext
,
442 This routine handles the PnP remove operation. This is our notification
443 that the underlying storage device for the volume we have is gone, and
444 an excellent indication that the volume will never reappear. The filesystem
445 is responsible for initiation or completion the dismount.
449 Irp - Supplies the Irp to process
451 Vcb - Supplies the volume being removed.
455 NTSTATUS - The return status for the operation
462 BOOLEAN VcbPresent
= TRUE
;
466 ASSERT_EXCLUSIVE_CDDATA
;
469 // REMOVE - a storage device is now gone. We either got
470 // QUERY'd and said yes OR got a SURPRISE OR a storage
471 // stack failed to spin back up from a sleep/stop state
472 // (the only case in which this will be the first warning).
474 // Note that it is entirely unlikely that we will be around
475 // for a REMOVE in the first two cases, as we try to intiate
480 // Acquire the global resource so that we can try to vaporize
481 // the volume, and the vcb resource itself.
484 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
487 // The device will be going away. Remove our lock and find
488 // out if we ever had one in the first place.
491 Status
= CdUnlockVolumeInternal( IrpContext
, Vcb
, NULL
);
494 // If the volume had not been locked, we must invalidate the
495 // volume to ensure it goes away properly. The remove will
499 if (!NT_SUCCESS( Status
)) {
501 CdLockVcb( IrpContext
, Vcb
);
503 if (Vcb
->VcbCondition
!= VcbDismountInProgress
) {
505 CdUpdateVcbCondition( Vcb
, VcbInvalid
);
508 CdUnlockVcb( IrpContext
, Vcb
);
510 Status
= STATUS_SUCCESS
;
514 // We need to pass this down before starting the dismount, which
515 // could disconnect us immediately from the stack.
519 // Get the next stack location, and copy over the stack location
522 IoCopyCurrentIrpStackLocationToNext( Irp
);
525 // Set up the completion routine
528 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
529 IoSetCompletionRoutine( Irp
,
530 CdPnpCompletionRoutine
,
537 // Send the request and wait.
540 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
542 if (Status
== STATUS_PENDING
) {
544 (VOID
)KeWaitForSingleObject( &Event
,
550 Status
= Irp
->IoStatus
.Status
;
554 // Now make our dismount happen. This may not vaporize the
555 // Vcb, of course, since there could be any number of handles
556 // outstanding if we were not preceeded by a QUERY.
558 // PnP will take care of disconnecting this stack if we
559 // couldn't get off of it immediately.
563 VcbPresent
= CdCheckForDismount( IrpContext
, Vcb
, TRUE
);
566 // Release the Vcb if it could still remain.
571 CdReleaseVcb( IrpContext
, Vcb
);
574 _Analysis_assume_lock_not_held_(Vcb
->VcbResource
);
577 CdReleaseCdData( IrpContext
);
580 // Cleanup our IrpContext and complete the IRP.
583 CdCompleteRequest( IrpContext
, Irp
, Status
);
588 _Requires_lock_held_(_Global_critical_region_
)
589 _Releases_nonreentrant_lock_(CdData
.DataResource
)
591 CdPnpSurpriseRemove (
592 _Inout_ PIRP_CONTEXT IrpContext
,
601 This routine handles the PnP surprise remove operation. This is another
602 type of notification that the underlying storage device for the volume we
603 have is gone, and is excellent indication that the volume will never reappear.
604 The filesystem is responsible for initiation or completion the dismount.
606 For the most part, only "real" drivers care about the distinction of a
607 surprise remove, which is a result of our noticing that a user (usually)
608 physically reached into the machine and pulled something out.
610 Surprise will be followed by a Remove when all references have been shut down.
614 Irp - Supplies the Irp to process
616 Vcb - Supplies the volume being removed.
620 NTSTATUS - The return status for the operation
627 BOOLEAN VcbPresent
= TRUE
;
631 ASSERT_EXCLUSIVE_CDDATA
;
634 // SURPRISE - a device was physically yanked away without
635 // any warning. This means external forces.
638 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
641 // Invalidate the volume right now.
643 // The intent here is to make every subsequent operation
644 // on the volume fail and grease the rails toward dismount.
645 // By definition there is no going back from a SURPRISE.
648 CdLockVcb( IrpContext
, Vcb
);
650 if (Vcb
->VcbCondition
!= VcbDismountInProgress
) {
652 CdUpdateVcbCondition( Vcb
, VcbInvalid
);
655 CdUnlockVcb( IrpContext
, Vcb
);
658 // We need to pass this down before starting the dismount, which
659 // could disconnect us immediately from the stack.
663 // Get the next stack location, and copy over the stack location
666 IoCopyCurrentIrpStackLocationToNext( Irp
);
669 // Set up the completion routine
672 KeInitializeEvent( &Event
, NotificationEvent
, FALSE
);
673 IoSetCompletionRoutine( Irp
,
674 CdPnpCompletionRoutine
,
681 // Send the request and wait.
684 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
686 if (Status
== STATUS_PENDING
) {
688 (VOID
)KeWaitForSingleObject( &Event
,
694 Status
= Irp
->IoStatus
.Status
;
698 // Now make our dismount happen. This may not vaporize the
699 // Vcb, of course, since there could be any number of handles
700 // outstanding since this is an out of band notification.
704 VcbPresent
= CdCheckForDismount( IrpContext
, Vcb
, TRUE
);
707 // Release the Vcb if it could still remain.
712 CdReleaseVcb( IrpContext
, Vcb
);
715 _Analysis_assume_lock_not_held_(Vcb
->VcbResource
);
718 CdReleaseCdData( IrpContext
);
721 // Cleanup our IrpContext and complete the IRP.
724 CdCompleteRequest( IrpContext
, Irp
, Status
);
729 _Requires_lock_held_(_Global_critical_region_
)
730 _Releases_nonreentrant_lock_(CdData
.DataResource
)
733 _Inout_ PIRP_CONTEXT IrpContext
,
742 This routine handles the PnP cancel remove operation. This is our
743 notification that a previously proposed remove (query) was eventually
744 vetoed by a component. The filesystem is responsible for cleaning up
745 and getting ready for more IO.
749 Irp - Supplies the Irp to process
751 Vcb - Supplies the volume being removed.
755 NTSTATUS - The return status for the operation
764 ASSERT_EXCLUSIVE_CDDATA
;
767 // CANCEL - a previous QUERY has been rescinded as a result
768 // of someone vetoing. Since PnP cannot figure out who may
769 // have gotten the QUERY (think about it: stacked drivers),
770 // we must expect to deal with getting a CANCEL without having
773 // For CDFS, this is quite easy. In fact, we can't get a
774 // CANCEL if the underlying drivers succeeded the QUERY since
775 // we disconnect the Vpb on our dismount initiation. This is
776 // actually pretty important because if PnP could get to us
777 // after the disconnect we'd be thoroughly unsynchronized
778 // with respect to the Vcb getting torn apart - merely referencing
779 // the volume device object is insufficient to keep us intact.
782 CdAcquireVcbExclusive( IrpContext
, Vcb
, FALSE
);
783 CdReleaseCdData( IrpContext
);
786 // Unlock the volume. This is benign if we never had seen
790 (VOID
) CdUnlockVolumeInternal( IrpContext
, Vcb
, NULL
);
792 CdReleaseVcb( IrpContext
, Vcb
);
795 // Send the request. The underlying driver will complete the
796 // IRP. Since we don't need to be in the way, simply ellide
797 // ourselves out of the IRP stack.
800 IoSkipCurrentIrpStackLocation( Irp
);
802 Status
= IoCallDriver(Vcb
->TargetDeviceObject
, Irp
);
804 CdCompleteRequest( IrpContext
, NULL
, STATUS_SUCCESS
);
811 // Local support routine
815 CdPnpCompletionRoutine (
816 _In_ PDEVICE_OBJECT DeviceObject
,
818 _In_reads_opt_(_Inexpressible_("varies")) PVOID Contxt
821 PKEVENT Event
= (PKEVENT
) Contxt
;
822 _Analysis_assume_(Contxt
!= NULL
);
824 KeSetEvent( Event
, 0, FALSE
);
826 return STATUS_MORE_PROCESSING_REQUIRED
;
828 UNREFERENCED_PARAMETER( DeviceObject
);
829 UNREFERENCED_PARAMETER( Irp
);
830 UNREFERENCED_PARAMETER( Contxt
);